#  Simcity threejs version

> by:hexianWeb

# Threejs 游戏基建结构

| 要素         | 作用             | 类比现实                       |
| ------------ | ---------------- | ------------------------------ |
| **Scene**    | 游戏世界的3D环境 | 就像游乐场的场地               |
| **Game UI**  | 用户界面和交互层 | 相当于游乐场的指示牌和售票处   |
| **Metadata** | 游戏数据和逻辑   | 类似游乐场的运营规则和游客数据 |

# Metadata 设计

## BUILDING_DATA 数据结构说明

`BUILDING_DATA` 是一个包含所有建筑类型及其属性的常量对象，定义于 `src/constants/constants.js`。其结构如下：

```js
export const BUILDING_DATA = {
  [buildingType]: {
    name: { zh: '中文名', en: 'English Name' }, // 建筑名称（多语言）
    type: 'buildingType',                      // 建筑类型唯一标识
    icon: '🏠',                                // 图标（emoji或字符串）
    buildingType: { zh: '建筑类型', en: 'Type' }, // 建筑大类（多语言）
    category: 'residential' | 'industrial' | 'commercial' | 'environment' | 'governance' | 'social' | 'infrastructure', // 分类
    levels: {
      [level]: {
        displayName: { zh: '中文名', en: 'English Name' }, // 等级显示名
        cost: number,                // 建造消耗金币
        maxPopulation?: number,      // 最大人口（住宅类）
        powerUsage?: number,         // 用电量
        powerOutput?: number,        // 发电量（发电建筑）
        pollution: number,           // 污染值（负数为减污）
        coinOutput?: number,         // 金币产出（商业/工业）
        population?: number,         // 提供就业/人口（商业/工业/设施）
        upgradeCost?: number,        // 升级消耗金币
        nextLevel?: number,          // 下一级编号（无则为null）
        visible: boolean,            // 是否在UI可见
      },
      // ...更多等级
    }
  },
  // ...更多建筑类型
}
```

### 字段说明

- **name**：建筑的多语言名称，`zh` 为中文，`en` 为英文。
- **type**：建筑类型唯一标识（如 `house`, `factory`）。
- **icon**：建筑图标，通常为 emoji。
- **buildingType**：建筑大类（如“住宅建筑”、“工业建筑”），多语言。
- **category**：建筑所属分类，用于功能分组（如 `residential`、`industrial`）。
- **levels**：建筑的多级属性，key 为等级（1, 2, 3...），value 为该等级的详细属性对象。

#### levels 下的属性

- **displayName**：该等级的多语言显示名。
- **cost**：建造该等级建筑所需金币。
- **maxPopulation**：最大人口容量，仅住宅类有。
- **powerUsage**：用电量，部分建筑有。
- **powerOutput**：发电量，仅发电建筑有。
- **pollution**：污染值，负数表示减污（如公园）。
- **coinOutput**：金币产出，商业/工业建筑有。
- **population**：提供就业/人口，部分建筑有。
- **upgradeCost**：升级到下一级所需金币。
- **nextLevel**：下一级编号，无则为 null。
- **visible**：该等级是否在UI中可见。

### 示例

以住宅（house）为例：

```js
house: {
  name: { zh: '住宅', en: 'Residential' },
  type: 'house',
  icon: '🏠',
  buildingType: { zh: '住宅建筑', en: 'Residential Building' },
  category: 'residential',
  levels: {
    1: {
      displayName: { zh: '普通住宅', en: 'Basic Residential' },
      cost: 300,
      maxPopulation: 50,
      powerUsage: 10,
      pollution: 2,
      upgradeCost: 600,
      nextLevel: 2,
      visible: true,
    },
    2: {
      displayName: { zh: '高级住宅', en: 'Advanced Residential' },
      cost: 600,
      maxPopulation: 100,
      powerUsage: 15,
      pollution: 3,
      upgradeCost: 1200,
      nextLevel: 3,
      visible: false,
    },
    3: {
      displayName: { zh: '豪华住宅', en: 'Luxury Residential' },
      cost: 1200,
      maxPopulation: 200,
      powerUsage: 20,
      pollution: 5,
      upgradeCost: null,
      nextLevel: null,
      visible: false,
    },
  },
}
```

---

### 设计原则

- **多语言支持**：所有显示相关字段均为 `{ zh, en }` 结构，便于国际化。
- **多级建筑**：通过 `levels` 字段支持建筑升级，每级有独立属性。
- **灵活扩展**：可轻松添加新建筑类型或扩展属性。
- **UI 可控**：`visible` 字段控制各等级在UI的显示与否。

---

如需进一步细化字段含义或扩展，请补充在此结构说明下方。

# SimObject  互动基类

> 提供了 mesh 管理、选中高亮、HTML 信息展示等通用交互能力。

- SimObject 提供了 mesh 管理、选中高亮、HTML 信息展示等通用交互能力。

- 只要是场景中可交互的对象（如 Tile、Building），都应继承 SimObject。

# building 类组件

## 🧠 分析与设计思路

1. 单一职责原则

- 0多态与扩展性

- 不同类型建筑继承自 Building，重写各自的功能方法（如 getPopulation、getPower、getEconomy）。

- 便于后续扩展新建筑类型或功能。

3. 解耦与协作

- Tile 只持有 Building 的实例（如 this.buildingInstance），通过接口与其交互。

- Building 需要能访问 Experience、scene、resources 等核心实例。

------

## 推荐实现步骤

### 1. 新建 building.js 基础类

- 负责加载建筑模型、通用属性（如 position、direction）、升级等。

- 提供通用接口（如 update、upgrade、get功能值等）。

### 2. 新建具体建筑子类（如 house.js、factory.js、shop.js）

- 继承 Building，重写/扩展功能方法。

### 3. 修改 Tile 类

- Tile 只负责地皮表现，持有 Building 实例。

- 通过接口与 Building 交互（如升级、获取功能值等）。

```mermaid
classDiagram
  SimObject <|-- Tile
  Tile <|-- Building
  SimObject <|-- Building
  Building <|-- House
  Building <|-- Factory
  Building <|-- Shop
```

# Tile 地皮交互

## 1. 方案梳理

### 方案一：Tile 负责建筑实例

- 流程：射线检测命中 tile（地皮），直接调用 tile.userData.setBuilding('house', 0) 在 tile 内部生成建筑实例（如 House），并作为 tile 的子对象（mesh.add(buildingInstance)）。

- 特点：

- 建筑和地皮是父子关系，建筑始终附着在 tile 上。

- 交互、管理、拾取都通过 tile 进行。

- 删除/移动建筑时，直接操作 tile 实例。

### 方案二：Tile 和 Building 分离

- 流程：射线检测命中 tile，获取其 position.x/z，随后在 buildingsGroup（独立 group）中创建建筑实例，建筑与 tile 仅通过坐标关联。

- 特点：

- 地皮和建筑完全分离，建筑统一管理在 buildingsGroup。

- 需要额外的数据结构维护 tile 与 building 的映射关系。

- 移动/删除建筑时，需要先查找对应 tile，再操作 buildingsGroup。

------

## 2. 需求与扩展性分析

### PRD 需求

- 建筑与地皮一一对应，每个 tile 最多一个建筑。

- 需要支持建筑的放置、移动、删除。

- 未来可能有 tile 升级、建筑升级、地皮扩展等需求。

### 技术实现对比

| 维度       | 方案一（父子）             | 方案二（分离）               |
| :--------- | :------------------------- | :--------------------------- |
| 实现难度   | 简单，直接操作 tile        | 复杂，需维护映射关系         |
| 性能       | 高效，遍历 tile 即可       | 需遍历 buildingsGroup 或查表 |
| 扩展性     | 易于扩展（如 tile 升级）   | 灵活，但管理复杂             |
| 交互逻辑   | 直观，所有交互聚焦 tile    | 需同步 tile 与 building 状态 |
| 数据一致性 | 易保证（父子结构天然一致） | 需手动同步，易出错           |
| 未来扩展   | 支持 tile/建筑联动、升级等 | 支持建筑独立动画、批量操作等 |

### 代码风格与维护

- 你的框架强调通过 Experience 单例获取依赖，tile 作为地皮的核心单元，建筑作为 tile 的"内容"更符合直觉。

- 方案一更贴合"组合"思想，tile 作为容器，建筑作为内容，便于后续扩展 tile 的属性（如地皮类型、状态等）。

------

## 3. 推荐与理由

强烈推荐采用方案一（tile 负责建筑实例，建筑作为 tile 的子对象），理由如下：

1. 符合 PRD 需求：每个 tile 只允许一个建筑，tile 作为地皮的唯一管理者，建筑作为其内容，逻辑清晰。

1. 易于维护：所有操作（放置、删除、移动、升级）都只需操作 tile 实例，无需额外维护映射关系。

1. 高扩展性：未来如地皮扩展、建筑升级、tile 状态变化等，都可以在 tile 类中集中管理，便于统一调试和维护。

1. 性能优越：遍历 tile 即可获取所有建筑，无需遍历全场景或查找映射表。

1. 代码风格统一：符合你当前框架的单例与组件化设计，便于团队协作和后续开发。

------

## 4. 方案一的最佳实践建议

- tile.js 中的 setBuilding(type, direction) 方法负责创建/替换建筑实例，并将其作为 tile 的子对象。

- interactor.js 通过射线检测命中 tile 后，直接调用 tile.userData.setBuilding(...) 实现建筑放置。

- 建筑删除/移动：可在 tile 上实现 removeBuilding()、moveBuilding() 等方法，保持 tile 的唯一性和一致性。

- 建筑信息面板：通过 tile 统一获取建筑信息，便于展示和交互。

------

## 5. 方案二的适用场景

- 如果未来有建筑与 tile 多对多、建筑可跨 tile、建筑批量动画等需求，可以考虑方案二。但目前 PRD 明确是一一对应，方案一更优

# UI 交互

- UI 交互（如选择建筑、切换模式、点击地皮）目前只在前端 JS 层（index.js）和 HTML 层（index.html）生效。

- Three.js 场景中的建筑实际放置、删除、移动等操作由 tile/building 组件（如 tile.js、house.js）负责。

- 需要桥接：UI 事件 → Three.js 场景 & Three.js 场景 → UI 事件操作。

  - 用户在左侧面板选择建筑类型（如"FACTORY"）。

  - 用户切换到"BUILD"模式。

  - 用户点击画布（canvas）上的某个 tile，期望在该 tile 上放置所选建筑。

  - 用户切换到"DEMOLISH"模式，点击建筑，期望删除该建筑。

  - 用户点击建筑，右侧面板显示详细信息。

### 方案：mitt js 事件驱动（推荐）

- 全局状态：用  Experience 单例存储当前选中的建筑类型、操作模式。多用于 UI 事件 → Three.js 场景

  ```js
  export default class Experience extends EventEmitter {
    constructor(canvas) {
      super()
      // ...
      this.currentMode = 'build'
      this.selectedBuilding = null
      this.credits = 12345
      // ...
    }
  }
  ```

随后在 Ray 射线检测相关逻辑中读取全局变量相应状态并做不同逻辑操作

```js
// interactor.js
_onClick(_event) {
  if (this.focused) {
    if (this.experience.currentMode === 'build' && window.selectedBuilding) {
      this.focused.setBuilding(window.selectedBuilding)

    } else if (this.experience.currentMode === 'demolish') {
      this.focused.removeBuilding()
      // 可选：window.showToast('建筑已拆除')
    } else {
      // 显示信息面板
      const html = this.focused.toHTML()
      document.getElementById('info-panel').innerHTML = html
    }
  }
}
```

- 事件驱动：UI 事件只负责更新全局状态，Three.js 交互（如 Interactor）在射线命中 tile/building 时，读取全局状态并执行相应操作。Three.js 场景 → UI 事件

  ```js
  // experience.js / interactor.js
  import { eventBus } from './event-bus'
  eventBus.emit('building:placed', { tile, type })

  // index.js
  import { eventBus } from './event-bus'
  eventBus.on('building:placed', ({ tile, type }) => {
    showToast(`${type} 已放置在 (${tile.x}, ${tile.y})`, 'success')
    // ...刷新 UI
  })
  ```

## 典型需求场景

- 用户在左侧面板选择建筑类型（如"FACTORY"）。

- 用户切换到"BUILD"模式。

- 用户点击画布（canvas）上的某个 tile，期望在该 tile 上放置所选建筑。

- 用户切换到"DEMOLISH"模式，点击建筑，期望删除该建筑。

- 用户点击建筑，右侧面板显示详细信息

## 典型事件设计

### ① 建筑相关

| 事件名            | 触发时机               | 事件参数结构                     |
| :---------------- | :--------------------- | :------------------------------- |
| building:placed   | 成功放置建筑后         | { tile, type, buildingInstance } |
| building:removed  | 拆除建筑后             | { tile, type }                   |
| building:selected | 选中建筑（点击/hover） | { tile, type, buildingInstance } |
| building:upgraded | 建筑升级后             | { tile, type, level }            |

### ② 地皮/格子相关

| 事件名        | 触发时机   | 事件参数结构 |
| :------------ | :--------- | :----------- |
| tile:selected | 选中地皮   | { tile }     |
| tile:expanded | 地皮扩展后 | { newSize }  |

### ③ 模式/状态相关

| 事件名           | 触发时机                   | 事件参数结构 |
| :--------------- | :------------------------- | :----------- |
| mode:changed     | 操作模式切换（build/move） | { mode }     |
| building:choosed | 选择建筑卡片               | { type }     |

### ④ 资源/经济相关

| 事件名             | 触发时机 | 事件参数结构          |
| :----------------- | :------- | :-------------------- |
| credits:changed    | 金币变化 | { credits, delta }    |
| population:changed | 人口变化 | { population, delta } |

### ⑤ UI 相关

| 事件名        | 触发时机       | 事件参数结构      |
| :------------ | :------------- | :---------------- |
| ui:toast      | 需要弹出提示时 | { message, type } |
| ui:panel:show | 显示信息面板   | { panel, data }   |

# 建筑四大模式

## 1. SELECT（选择）模式
**核心功能**：信息查看与建筑升级
```mermaid
graph TD
    A[SELECT模式] --> B[鼠标悬停效果]
    A --> C[点击交互]
    A --> D[信息面板]
    A --> E[建筑升级]

    B --> B1[地皮：浅蓝色边框]
    B --> B2[建筑：轻微浮动动画]

    C --> C1[点击地皮：显示空地信息]
    C --> C2[点击建筑：显示建筑详情]

    D --> D1[右侧面板显示]
    D1 --> D11[建筑名称/类型]
    D1 --> D12[居民人数/状态]
    D1 --> D13[当前产出]
    D1 --> D14[升级选项]

    E --> E1[显示升级按钮]
    E1 --> E11[升级条件检查]
    E1 --> E12[扣除金币]
    E1 --> E13[更新建筑模型]
```

**实现要点**：
- 视觉反馈：
  - 悬停地皮：浅蓝色半透明边框
  - 悬停建筑：Y轴轻微浮动(0.2单位 gsap ease )+发光效果
- 信息面板内容：
  ```js
  // 建筑信息示例
  {
    name: "高级公寓",
    type: "住宅",
    residents: "12/15",
    status: "正常", // 状态标签颜色：正常-绿色，拥挤-橙色，空置-灰色
    output: "+5.2金币/秒",
    nextLevel: {
      cost: 1500,
      benefits: "+2居民容量"
    }
  }
  ```
- 交互限制：
  - 禁用放置/删除操作
  - 升级按钮仅在满足条件时可用

  ```mermaid
  graph TD
    A[用户鼠标点击/移动] --> B[Interactor 监听事件]
    B --> C[获取当前模式: SELECT]
    C --> D[获取射线焦点 Tile]
    D --> E[高亮/选中 Tile]
    E --> F[调用 _handleSelectMode]
    F --> G[更新 Pinia: selectBuilding/selectPosition]
    G --> H[UI 组件监听 Pinia 状态]
    H --> I[App.vue 及子组件响应, 展示详情/高亮]
  ```

### upgrade, demolish 升级功能

```mermaid
sequenceDiagram
  participant User as 用户
  participant Interactor as Interactor(射线交互器)
  participant Pinia as Pinia(GameState)
  participant App as App.vue/子组件
  participant Building as buildingInstance

  User->>Interactor: 鼠标点击建筑
  Interactor->>Interactor: 获取当前模式: SELECT
  Interactor->>Interactor: 获取射线焦点 Tile
  Interactor->>Interactor: 高亮/选中 Tile
  Interactor->>Interactor: 调用 _handleSelectMode
  Interactor->>Pinia: setSelectedBuilding(building.type)
  Interactor->>Pinia: setSelectedPosition(tile.position)
  Pinia-->>App: 状态变更通知
  App->>App: 响应状态, 展示建筑详情/高亮

  User->>App: 点击升级/拆除按钮
  App->>App: 弹出 ConfirmDialog
  User->>App: 确认操作
  App->>Interactor: eventBus.emit('ui:action-confirmed', action)
  Interactor->>Interactor: _onActionConfirmed(action)
  alt 升级
    Interactor->>Interactor: _confirmUpgrade
    Interactor->>Building: building.upgrade()
    alt 可升级
      Building-->>Interactor: 返回新 building 数据
      Interactor->>Building: selected.setBuilding(newType, newDir, options)
      Interactor->>App: eventBus.emit('toast:add', '升级成功')
    else 已最高级
      Interactor->>App: eventBus.emit('toast:add', '已达最高等级')
    end
  else 拆除
    Interactor->>Interactor: _confirmDemolish
    Interactor->>Pinia: setTile, removeBuilding
    Interactor->>App: eventBus.emit('toast:add', '建筑移除')
  end
  App->>App: 响应状态, 刷新地图
```

## 2. BUILD（建造）模式

**核心流程**：

```mermaid
sequenceDiagram
    participant UI as 左侧面板
    participant Camera as 摄像机
    participant Tile as 地皮系统
    participant Economy as 经济系统

    UI->>Camera: 选择建筑类型
    Camera->>Tile: 显示放置预览(半透明模型)
    loop 地皮悬停检测
        Tile-->>Camera: 可用地皮(绿色高亮)<br/>不可用地皮(红色高亮)
    end
    Tile->>Economy: 点击放置时检查金币
    alt 金币充足
        Economy->>Tile: 扣除金币，生成建筑
        Tile->>Camera: 播放放置特效
    else 金币不足
        Economy->>UI: 显示"金币不足"提示
    end
```

**关键实现**：
```js
// 在Interactor.js中的实现
_onClick() {
  if (this.experience.currentMode === 'build') {
    const cost = Building.getCost(selectedBuildingType);

    if (this.experience.credits >= cost) {
      this.focusedTile.setBuilding(selectedBuildingType);
      this.experience.credits -= cost;
      eventBus.emit('credits:changed', {
        credits: this.experience.credits,
        delta: -cost
      });
    } else {
      eventBus.emit('ui:toast', {
        message: `金币不足！需要 ${cost} 金币`,
        type: 'error'
      });
    }
  }
}
```

**UI提示要素**：
1. 左侧面板：
   - 当前选中建筑卡片：金色边框+放大效果
   - 建筑价格显示（红色标注不足金额）
2. 场景内：
   - 可用地皮：绿色网格高亮
   - 不可用地皮：红色网格闪烁
   - 建筑预览：50%透明度的3D模型
3. 状态栏：
   - 实时金币计数（放置时跳动减少）

## 3. RELOCATE（移动）模式
**状态机实现**：
```mermaid
stateDiagram-v2
    [*] --> 选择阶段
    选择阶段 --> 放置阶段: 点击选中建筑
    放置阶段 --> 选择阶段: 按ESC取消

    放置阶段 --> 旋转调整: 按R键
    旋转调整 --> 放置阶段: 自动返回

    放置阶段 --> 确认放置: 点击空地
    确认放置 --> [*]: 完成移动

    state 放置阶段 {
        原建筑： 半透明显示
        目标位置： 绿色高亮框
        建筑预览： 跟随鼠标位置
    }

    state 旋转调整 {
        方向指示器： 4方向箭头UI
        当前朝向： 0°/90°/180°/270°
    }
```

**技术要点**：
```js
// 在Experience.js中
startRelocation(building) {
  this.relocatingBuilding = building;
  this.originalTile = building.parentTile;
  this.originalTile.setBuilding(null, true); // 临时移除

  // 创建预览模型
  this.previewModel = building.clone();
  this.previewModel.material.transparent = true;
  this.previewModel.material.opacity = 0.7;
}

// 旋转处理
rotatePreview(angle = 90) {
  this.previewRotation = (this.previewRotation + angle) % 360;
  this.previewModel.rotation.y = THREE.MathUtils.degToRad(this.previewRotation);
}

// 确认放置
confirmRelocation(targetTile) {
  targetTile.setBuildingInstance(this.relocatingBuilding);
  this.relocatingBuilding.setRotation(this.previewRotation);
  this.cleanupPreview();
}
```

**视觉反馈**：
1. 选中建筑：半透明化（opacity: 0.5）
2. 预览模型：70%透明度+发光轮廓
3. 有效目标地皮：脉动绿色光圈
4. 无效目标地皮：静态红色边框
5. 方向指示器：底部罗盘UI（显示当前朝向）

```mermaid
graph TD
  A[用户鼠标点击/移动] --> B[Interactor 监听事件]
  B --> C[获取当前模式: RELOCATE]
  C --> D[获取射线焦点 Tile]
  D --> E[高亮/选中 Tile]
  E --> F{第几次点击?}
  F -- 第一次 --> G[记录 relocateFirst]
  F -- 第二次 --> H{canPlaceBuilding 合法性判断}
  H -- 合法且空地 --> I[事件总线 ui:confirm-action]
  I --> J[App.vue 监听, 弹出 ConfirmDialog]
  J --> K[用户确认]
  K --> L[事件总线 'ui:action-confirmed']
  L --> M[Interactor._confirmRelocate]
  M --> N[Pinia: setTile, 交换建筑]
  N --> O[UI Toast: 搬迁成功]
  H -- 不合法/已占用 --> P[UI Toast: 无法搬迁]
  O & P --> Q[UI 组件监听 Pinia 状态]
  Q --> R[App.vue 及子组件响应, 刷新地图]
```

## 4. DEMOLISH（拆除）模式

**安全交互流程**：
```mermaid
graph LR
    A[点击建筑] --> B{确认对话框}
    B -->|确认| C[执行拆除]
    B -->|取消| D[返回DEMOLISH模式]

    C --> E[播放拆除动画]
    E --> F[返还部分资源]
    F --> G[更新经济系统]

    style A stroke:#f00
    style C stroke:#090
```

**视觉提示**：
1. 场景内：
   - 所有建筑：显示红色边框
   - 悬停建筑：脉动红色警示效果
2. 光标变化：
   - 默认：红色禁止图标
   - 悬停建筑：锤子图标
3. 确认对话框：
   - 半透明黑色蒙层
   - 居中红色边框面板
   - 拆除图标动画

```mermaid
graph TD
  A[用户鼠标点击/移动] --> B[Interactor 监听事件]
  B --> C[获取当前模式: DEMOLISH]
  C --> D[获取射线焦点 Tile]
  D --> E[高亮/选中 Tile]
  E --> F{Tile 是否有建筑}
  F -- 有建筑 --> G[事件总线 ui:confirm-action]
  G --> H[App.vue 监听, 弹出 ConfirmDialog]
  H --> I[用户确认]
  I --> J[事件总线 ui:action-confirmed]
  J --> K[已确认销毁 执行销毁逻辑]
  K --> L[Metadata 更新数据，Scene 删除地皮]
  L --> M[UI Toast: 建筑移除]
  F -- 无建筑 --> N[设置地皮为草地]
  M & N --> O[UI 组件监听 Pinia 状态]
  O --> P[App.vue 及子组件响应, 刷新地图]
```

# 产出系统

## 1.人口资源

### 1. 机制说明
- **住宅建筑**（如🏠、🏡）只决定"人口容量"上限（maxPopulation 字段）。
- **商业/工业建筑**（如🏭、🧪、🏬、🏢）提供就业岗位（population 字段），吸纳实际人口。
- **实际人口** = 所有商业/工业建筑的岗位总和，不能超过人口容量。
- **人口利用率** = 实际人口 / 人口容量。
- 当人口利用率 > 1.5 或 < 0.5 时，满意度下降。
- 满意度过低会导致人口流失、经济产出下降。

### 2. 资源关系矩阵（新版）
| 资源类型   | 来源建筑           | 吸纳/限制建筑     | 影响机制                                   |
| ---------- | ------------------ | ---------------- | ------------------------------------------ |
| 人口容量   | 住宅建筑           | -                | 限制实际人口上限                           |
| 实际人口   | -                  | 商业/工业建筑    | 由商业/工业建筑需求决定，不能超过人口容量   |
| 满意度     | 公园、医院、学校等 | 工厂、发电厂等   | 受人口利用率影响，过高/过低均会下降         |
| 资金       | 所有建筑           | -                | 受实际人口和满意度共同影响                 |

### 3. 资源流向与满意度影响（Mermaid 图）
```mermaid
graph TD
    A[住宅建筑🏠] -- 提供 --> B[人口容量]
    C[商业/工业建筑🏭/🏬] -- 吸纳 --> D[实际人口]
    B -- 限制 --> D
    D & B -- 计算比例 --> E[人口利用率]
    E -- 影响 --> F[满意度]
    F -- 影响 --> G[人口增长/流失]
    F -- 影响 --> H[经济产出]
```

### 4. 满意度影响机制
- **人口利用率 = 实际人口 / 人口容量**
- 当人口利用率 > 1.5 或 < 0.5 时，满意度每分钟下降X点
- 满意度过低时，人口增长变为负数（流失），经济产出下降

### 5. 公式示例
```
实际人口 = min(人口容量, 商业/工业总岗位需求)
人口利用率 = 实际人口 / 人口容量
满意度变化 =
  if (人口利用率 > 1.5 || 人口利用率 < 0.5)
    则满意度 -= Δ
  else
    满意度 += 正常增长
```

> 详见 @src/constants/constants.js 中 BUILDING_DATA 字段定义。

## 2. 金币的产出 & 获取 💰

### 机制说明
- 金币是城市建设与运营的核心资源。
- 金币主要通过商业（🏬、🏢）和工业（🏭、🧪、☢️）建筑按时间周期自动产出。
- 金币消耗主要用于新建筑的建造、升级、搬迁等。
- 产出与消耗数值详见下表。

### 产出与消耗一览表

| 建筑类型 | 建筑名称 | 产出金币/周期 | 建造消耗金币 |
| :--: | :--: | :--: | :--: |
| 工业 | 工厂 🏭 | 20 | 500 |
| 工业 | 化学工厂 🧪 | 30/45/60 | 1000/1500/2000 |
| 工业 | 核电站 ☢️ | 50 | 5000 |
| 商业 | 商店 🏬 | 25/40/60 | 400/800/1600 |
| 商业 | 办公室 🏢 | 35/55/80 | 500/1000/2000 |

> 说明：
> - "产出金币/周期"为该建筑每个时间周期（如每分钟）自动产出的金币数量。
> - "建造消耗金币"为新建该建筑时需要消耗的金币数量。
> - 部分建筑有多级，表格中以"/"分隔不同等级。
> - 详细数据请参考 @src/constants/constants.js 中 BUILDING_DATA。

### 机制细节
- 金币产出受市场需求、满意度等因素影响，详见"动态经济系统"章节。
- 拆除建筑可返还部分金币。
- 随着游戏进程推进，金币产出与消耗会动态调整。

# 未来功能

## 1. 挑战与失败机制

### 1..1 动态经济系统
```js
class Economy {
  constructor() {
    this.marketDemand = {
      residential: 1.0,
      commercial: 1.0,
      industrial: 1.0
    }

    // 每10分钟重新计算市场需求
    setInterval(() => this.calculateMarketDemand(), 600000)
  }

  calculateMarketDemand() {
    // 基于建筑比例调整需求
    const totalBuildings = this.experience.buildings.length
    const resRatio = this.getBuildingRatio('residential')
    const comRatio = this.getBuildingRatio('commercial')

    // 住宅需求公式：商业比例越高，住宅需求越大
    this.marketDemand.residential = 0.5 + comRatio * 1.5

    // 商业需求公式：住宅比例越高，商业需求越大
    this.marketDemand.commercial = 0.3 + resRatio * 2.0

    // 工业需求随机波动
    this.marketDemand.industrial = 0.8 + Math.random() * 0.4
  }

  getBuildingOutput(building) {
    const baseOutput = building.baseOutput
    const demandFactor = this.marketDemand[building.type]
    const efficiency = this.getEfficiency(building)

    return baseOutput * demandFactor * efficiency
  }
}
```

### 1.2 失败条件
```js
class GameState {
  checkFailureConditions() {
    // 条件1: 连续负债超过5分钟
    if (this.credits < 0) {
      this.debtTimer += delta
      if (this.debtTimer > 300) {
        this.triggerFailure('经济崩溃')
      }
    }
    else {
      this.debtTimer = 0
    }

    // 条件2: 人口归零
    if (this.population <= 0) {
      this.triggerFailure('城市荒废')
    }

    // 条件3: 污染爆表
    if (this.pollution >= 100) {
      this.triggerFailure('生态灾难')
    }
  }

  triggerFailure(reason) {
    eventBus.emit('game:over', { reason })
    // 保存分数到排行榜
    this.saveScore()
    // 显示失败界面
    this.showGameOverScreen(reason)
  }
}
```

## 2. 策略性建筑系统

### 2.1 建筑相互依存关系
```mermaid
graph LR
    A[住宅] -->|提供| B[人口]
    B -->|需要| C[就业]
    C -->|由| D[提供 by 商业/工业]
    D -->|消耗| E[电力]
    E -->|由| F[提供 by 发电厂]
    F -->|产生| G[污染]
    G -->|降低| H[满意度]
    H -->|影响| A[住宅人口增长]

    I[公园] -->|减少| G[污染]
    I -->|提升| H[满意度]

    J[医院] -->|提升| H[满意度]
    J -->|减少| K[人口流失]

    L[消防站] -->|提升| M[工业安全性]
    N[警察局] -->|提升| O[商业安全性]
    P[垃圾站] -->|减少| G[污染]
```

### 2.2 建筑状态指示器系统

#### 2.2.1 状态类型定义
基于 `statusConfig` 配置，建筑可显示以下状态：

| 状态类型 | 触发条件 | 显示图标 | 效果类型 |
|---------|---------|---------|---------|
| `UPGRADE_AVAILABLE` | 建筑可升级且金币充足 | ⬆️ | upgrade |
| `POWER_SHORTAGE` | 城市电力不足 | ⚡ | missPower |
| `POPULATION_OVERLOAD` | 人口超负荷 | 👥 | missPopulation |
| `POPULATION_SHORTAGE` | 人口不足 | 👥 | missPopulation |
| `MISSING_ROAD` | 缺少道路连接 | 🛣️ | missRoad |
| `HIGH_POLLUTION` | 污染过高 | 🌫️ | missPollution |
| `PROVIDING_BUFF` | 为相邻建筑提供增益 | 🌟 | upgrade |

#### 2.2.2 建筑互动配置

**住宅类建筑（house, house2）**
```js
statusConfig: [
  {
    statusType: 'UPGRADE_AVAILABLE',
    condition: (building, gs) => {
      const upgradeInfo = building.upgrade()
      return upgradeInfo && gs.credits >= building.getCost()
    },
    effect: { type: 'upgrade' }
  },
  {
    statusType: 'POPULATION_OVERLOAD',
    condition: (building, gs) => {
      return gs.population >= gs.maxPopulation
    },
    effect: { type: 'humanBuff' }
  }
]
```

**工业类建筑（factory, chemistry-factory, nuke-factory）**
```js
statusConfig: [
  {
    statusType: 'POWER_SHORTAGE',
    condition: (building, gs) => {
      return gs.powerUsage > gs.powerOutput
    },
    effect: { type: 'missPower' }
  },
  {
    statusType: 'MISSING_ROAD',
    condition: (building, _gs) => {
      return !building.hasRoadConnection()
    },
    effect: { type: 'missRoad' }
  },
  {
    statusType: 'HIGH_POLLUTION',
    condition: (building, gs) => {
      return gs.pollution > 80
    },
    effect: { type: 'missPollution' }
  }
]
```

**基础设施类建筑**
```js
// 公园 - 为住宅提供满意度加成
{
  statusType: 'PROVIDING_BUFF',
  condition: (building, gs) => {
    building.buffConfig = { targets: ['house', 'house2'], range: 2 }
    return building.checkForBuffTargets(gs)
  },
  effect: { type: 'upgrade' }
}

// 医院 - 为住宅提供满意度加成
{
  statusType: 'PROVIDING_BUFF',
  condition: (building, gs) => {
    building.buffConfig = { targets: ['house', 'house2'], range: 3 }
    return building.checkForBuffTargets(gs)
  },
  effect: { type: 'upgrade' }
}

// 消防站 - 为工业建筑提供安全性
{
  statusType: 'PROVIDING_BUFF',
  condition: (building, gs) => {
    building.buffConfig = { targets: ['factory', 'chemistry-factory', 'nuke-factory'], range: 2 }
    return building.checkForBuffTargets(gs)
  },
  effect: { type: 'upgrade' }
}

// 警察局 - 为商业建筑提供安全性
{
  statusType: 'PROVIDING_BUFF',
  condition: (building, gs) => {
    building.buffConfig = { targets: ['shop', 'office'], range: 2 }
    return building.checkForBuffTargets(gs)
  },
  effect: { type: 'upgrade' }
}

// 垃圾站 - 为工业建筑减少污染
{
  statusType: 'PROVIDING_BUFF',
  condition: (building, gs) => {
    building.buffConfig = { targets: ['factory', 'chemistry-factory', 'nuke-factory'], range: 2 }
    return building.checkForBuffTargets(gs)
  },
  effect: { type: 'missPollution' }
}
```

### 2.3 建筑升级系统

#### 2.3.1 升级条件
- 建筑达到当前等级上限
- 玩家金币充足
- 城市发展水平满足要求

#### 2.3.2 升级效果
- 人口容量提升（住宅类）
- 金币产出增加（商业/工业类）
- 电力产出提升（发电类）
- 污染减少（基础设施类）

### 2.4 实现规范

#### 2.4.1 状态检测机制
```js
// 在建筑类中实现状态检测
checkStatus(gs) {
  for (const config of this.statusConfig) {
    if (config.condition(this, gs)) {
      return config
    }
  }
  return null
}

// 检查相邻建筑
checkForBuffTargets(gs) {
  const neighbors = this.getNeighborTiles(this.buffConfig.range)
  return neighbors.some(tile =>
    tile.building && this.buffConfig.targets.includes(tile.building.type)
  )
}
```

#### 2.4.2 视觉效果实现
- 使用 `effects.js` 中的广告牌系统
- 状态图标位置：建筑顶部 0.5 单位高度
- 动画效果：缓入缓出 + 浮动动画
- 颜色编码：绿色（正常）、黄色（警告）、红色（危险）

## 3. 动态事件系统

**事件类型表**：
| 事件类型 | 频率       | 影响                | 玩家应对策略           |
| -------- | ---------- | ------------------- | ---------------------- |
| 经济危机 | 10-15分钟  | 所有商业收入减少40% | 转向工业或缩减开支     |
| 移民潮   | 随机       | 人口+20%            | 快速建造住宅提供住所   |
| 能源短缺 | 电力>90%时 | 电力产出-30%        | 建造备用能源或减少消耗 |
| 环保抗议 | 污染>60%时 | 满意度-25%          | 建造公园或升级清洁技术 |
| 技术突破 | 学校>3座时 | 所有升级费用-20%    | 趁机升级关键建筑       |

**事件实现代码**：
```js
class EventSystem {
  constructor() {
    this.events = [
      {
        id: 'economic_crisis',
        name: '全球经济危机',
        probability: 0.01, // 每分钟1%概率
        condition: () => this.experience.playTime > 600, // 10分钟后可能发生
        apply: () => {
          this.marketDemand.commercial *= 0.6
          eventBus.emit('ui:toast', {
            message: '经济危机！商业收入减少40%',
            type: 'warning'
          })
        },
        duration: 120 // 持续2分钟
      }
    ]
  }

  update(delta) {
    this.events.forEach((event) => {
      if (!event.active && Math.random() < event.probability * delta) {
        if (!event.condition || event.condition()) {
          this.activateEvent(event)
        }
      }

      if (event.active) {
        event.timeRemaining -= delta
        if (event.timeRemaining <= 0) {
          this.deactivateEvent(event)
        }
      }
    })
  }
}
```

## 4. 进阶游戏机制

### 5.1 区域规划加成
```js
class ZoneSystem {
  getEfficiencyBonus(building) {
    let bonus = 1.0

    // 检查相邻格子
    const neighbors = this.getNeighborTiles(building.tile)

    // 住宅相邻公园：满意度+10%
    if (building.type === 'residential') {
      const parkCount = neighbors.filter(t => t.building?.type === 'park').length
      bonus += parkCount * 0.1
    }

    // 工厂远离住宅：污染-15%
    if (building.type === 'industrial') {
      const residentialCount = neighbors.filter(t => t.building?.type === 'residential').length
      bonus -= residentialCount * 0.15
    }

    return Math.max(0.5, bonus) // 最低50%效率
  }
}
```

### 5.2 科技树系统
```mermaid
graph TD
    A[基础技术] --> B[清洁能源]
    A --> C[智能电网]
    A --> D[绿色建筑]

    B --> E[太阳能电池板<br>+20%电力 -30%污染]
    B --> F[核聚变发电<br>+50%电力 高风险]

    C --> G[电力传输优化<br>-10%电力损耗]
    C --> H[智能负载均衡<br>+15%电力效率]

    D --> I[垂直农场<br>+人口 +满意度]
    D --> J[生态住宅<br>-维护费 +满意度]
```

### 5.3 政策系统（二选一机制）
```js
class PolicySystem {
  policies = [
    {
      id: 'tax_policy',
      name: '税收政策',
      options: [
        {
          label: '低税率吸引投资',
          effect: '+20%人口增长，-15%建筑收入'
        },
        {
          label: '高税率增加收入',
          effect: '-10%人口增长，+25%建筑收入'
        }
      ]
    },
    {
      id: 'environment_policy',
      name: '环境政策',
      options: [
        {
          label: '工业优先',
          effect: '+30%工业产出，+0.2污染/分钟'
        },
        {
          label: '环保优先',
          effect: '-50%污染，-20%工业产出'
        }
      ]
    }
  ]

  applyPolicy(policyId, optionIndex) {
    const policy = this.policies.find(p => p.id === policyId)
    const option = policy.options[optionIndex]

    // 应用效果
    this.parseEffect(option.effect)

    // 锁定选择30分钟
    this.policyCooldowns[policyId] = 1800

    eventBus.emit('policy:enacted', { policy, option })
  }
}
```

## 5. 平衡性调整

### 5.1 资源转换公式
```
人口增长 = (基础增长率 + 满意度/100) × (1 - 污染/200)
        × (就业率^0.5) × 住宅容量使用率

电力需求 = Σ(工业建筑×1.2 + 商业建筑×0.8 + 住宅×0.2)
污染产生 = Σ(燃煤发电×0.8 + 工业建筑×0.5 - 公园×0.3)
```

### 5.2 动态难度曲线
```js
class DifficultySystem {
  getDifficultyMultiplier() {
    const timeFactor = Math.min(1.0, this.playTime / 3600) // 1小时后达最大难度
    const buildingFactor = Math.min(1.5, this.buildings.length / 50) // 50建筑后达上限

    return 0.8 + (timeFactor * 0.5) + (buildingFactor * 0.7)
  }

  adjustGameParameters() {
    const multiplier = this.getDifficultyMultiplier()

    // 增加维护成本
    this.maintenanceMultiplier = multiplier

    // 增加事件频率
    this.eventProbabilityMultiplier = 0.5 + multiplier * 0.5

    // 降低资源产出
    this.resourceOutputMultiplier = 1.2 - multiplier * 0.4
  }
}
```

## 6. 玩家进度与成就

### 6.1 里程碑系统
```js
const milestones = [
  { id: 'first_city', name: '初具规模', condition: state => state.population >= 100 },
  { id: 'power_king', name: '电力大亨', condition: state => state.powerOutput >= 500 },
  { id: 'eco_champion', name: '环保先锋', condition: state => state.pollution < 20 && state.playTime > 1800 },
  { id: 'metropolis', name: '大都市', condition: state => state.population >= 5000 }
]
```

### 6.2 保存进度设计
```js
class SaveSystem {
  saveGame() {
    const saveData = {
      version: 1.1,
      timestamp: Date.now(),
      resources: this.experience.resources,
      buildings: this.experience.buildings.map(b => ({
        type: b.type,
        level: b.level,
        x: b.tile.x,
        z: b.tile.z,
        rotation: b.rotation
      })),
      stats: {
        playTime: this.playTime,
        totalEarnings: this.totalEarnings,
        buildingsConstructed: this.buildingsConstructed
      },
      upgrades: this.unlockedUpgrades,
      policies: this.activePolicies
    }

    localStorage.setItem('city_save', JSON.stringify(saveData))
  }

  loadGame() {
    const saveData = JSON.parse(localStorage.getItem('city_save'))
    // 重建游戏状态...
  }
}
```

## 实施路线图

1. **第一阶段：核心经济系统重构（1-2周）**
   - 实现电力、人口、满意度资源
   - 添加建筑维护费
   - 创建基本失败条件

2. **第二阶段：策略深度扩展（2-3周）**
   - 实现区域规划加成
   - 添加基础事件系统
   - 设计初始科技树

3. **第三阶段：内容丰富化（3-4周）**
   - 添加10+新建筑类型
   - 实现政策系统
   - 创建成就和里程碑

4. **第四阶段：平衡与优化（1-2周）**
   - 调整资源公式
   - 优化新玩家引导
   - 添加难度选项

这些改进将游戏从简单的"放置-等待"循环转变为需要持续决策和战略规划的城市模拟游戏。玩家现在需要：
- 平衡多种相互冲突的资源
- 应对随机事件和危机
- 规划长期科技发展
- 在政策决策中权衡取舍
- 避免多个失败条件

同时保持了游戏的休闲本质，所有复杂系统都通过清晰的UI和渐进式引导呈现给玩家。

---

# useGameState.js 变量引用表

> 本文档记录了 `src/stores/useGameState.js` 中所有可用的变量、getters 和 actions，供开发时查询引用。

## State 状态变量

| 变量名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `currentMode` | string | `'build'` | 当前操作模式：`'build'`、`'demolish'`、`'relocate'`、`'select'` |
| `selectedBuilding` | object/null | `null` | 当前选中建筑：`{type, level}` |
| `selectedPosition` | object/null | `null` | 当前选中位置：`{x, z}` |
| `toastQueue` | array | `[]` | Toast 消息队列 |
| `gameDay` | number | `1` | 游戏天数 |
| `credits` | number | `10000` | 金币数量 |
| `territory` | number | `16` | 地皮数量 |
| `cityLevel` | number | `1` | 城市等级 |
| `cityName` | string | `'HeXian City'` | 城市名称 |
| `citySize` | number | `16` | 城市大小 |
| `language` | string | `'en'` | 语言设置：`'en'`、`'zh'` |
| `metadata` | array | `17x17` 数组 | 地图元数据，每个元素包含：`{type, building, direction, level}` |
| `showMapOverview` | boolean | `false` | 地图总览显示状态 |
| `stability` | number | `100` | 城市稳定度 (0-100) |
| `stabilityChangeRate` | number | `0` | 稳定度每秒变化率 |

## Getters 计算属性

| Getter名 | 返回类型 | 说明 | 计算公式 |
|----------|----------|------|----------|
| `dailyIncome` | number | 每日总收入 | 所有建筑的 `coinOutput` 总和，受相邻公园影响 |
| `maxPopulation` | number | 总人口容量 | 住宅建筑的 `maxPopulation` 总和，受相邻公园影响 |
| `totalJobs` | number | 总就业岗位 | 所有建筑的 `population` 总和 |
| `population` | number | 实际人口 | `Math.min(maxPopulation * 1.5, totalJobs)` |
| `maxPower` | number | 最大发电量 | 所有建筑的 `powerOutput` 总和 |
| `power` | number | 总耗电量 | 所有建筑的 `powerUsage` 总和 |
| `buildingCount` | number | 建筑总数 | 排除道路的建筑数量 |
| `pollution` | number | 总污染值 | 所有建筑的 `pollution` 总和，工业建筑受相邻公园影响 |
| `hospitalCount` | number | 医院数量 | 统计 `building === 'hospital'` 的数量 |
| `policeStationCount` | number | 警察局数量 | 统计 `building === 'police'` 的数量 |
| `fireStationCount` | number | 消防站数量 | 统计 `building === 'fire_station'` 的数量 |

## Actions 操作方法

| Action名 | 参数 | 说明 |
|----------|------|------|
| `updateStability()` | 无 | 更新稳定度变化率 |
| `applyStabilityChange()` | 无 | 应用稳定度变化 |
| `setMode(mode)` | string | 设置操作模式 |
| `setSelectedBuilding(payload)` | object | 设置选中建筑 |
| `setSelectedPosition(position)` | object | 设置选中位置 |
| `setCredits(credits)` | number | 设置金币数量 |
| `updateCredits(credits)` | number | 更新金币数量（增减） |
| `setTerritory(territory)` | number | 设置地皮数量 |
| `setCityLevel(cityLevel)` | number | 设置城市等级 |
| `setCityName(cityName)` | string | 设置城市名称 |
| `setCitySize(citySize)` | number | 设置城市大小 |
| `addToast(message, type)` | string, string | 添加 Toast 消息 |
| `setLanguage(lang)` | string | 设置语言 |
| `removeToast(id)` | number | 移除 Toast 消息 |
| `clearSelection()` | 无 | 清除选中状态 |
| `setTile(x, y, patch)` | number, number, object | 设置地图瓦片 |
| `updateTile(x, y, patch)` | number, number, object | 更新地图瓦片（同 setTile） |
| `getTile(x, y)` | number, number | 获取地图瓦片 |
| `setShowMapOverview(val)` | boolean | 设置地图总览显示 |
| `nextDay()` | 无 | 进入下一天，更新金币 |
| `resetAll()` | 无 | 重置所有状态 |

## 建筑状态检测中的变量引用

在建筑类的 `statusConfig` 中，`gs` 参数代表 `useGameState` 实例，可访问以下属性：

### 常用变量引用示例

```js
// 电力相关
gs.power // 总耗电量 (getter)
gs.maxPower // 最大发电量 (getter)

// 人口相关
gs.population // 实际人口 (getter)
gs.maxPopulation // 总人口容量 (getter)
gs.totalJobs // 总就业岗位 (getter)

// 其他资源
gs.credits // 金币数量 (state)
gs.pollution // 总污染值 (getter)
gs.stability // 城市稳定度 (state)

// 建筑统计
gs.buildingCount // 建筑总数 (getter)
gs.hospitalCount // 医院数量 (getter)
gs.policeStationCount // 警察局数量 (getter)
gs.fireStationCount // 消防站数量 (getter)
```

### 建筑状态配置示例

```js
// 电力短缺检测
{
  statusType: 'POWER_SHORTAGE',
  condition: (building, gs) => {
    return gs.power > gs.maxPower  // 耗电量 > 发电量
  },
  effect: { type: 'missPower' }
}

// 人口超负荷检测
{
  statusType: 'POPULATION_OVERLOAD',
  condition: (building, gs) => {
    return gs.population >= gs.maxPopulation  // 实际人口 >= 人口容量
  },
  effect: { type: 'missPopulation' }
}

// 可升级检测
{
  statusType: 'UPGRADE_AVAILABLE',
  condition: (building, gs) => {
    const upgradeInfo = building.upgrade()
    return upgradeInfo && gs.credits >= building.getCost()  // 有下一级且金币充足
  },
  effect: { type: 'upgrade' }
}
```

## 注意事项

1. **State vs Getter**:
   - `state` 中的变量是直接存储的值
   - `getter` 中的变量是计算得出的值，会实时更新

2. **变量命名一致性**:
   - 电力相关：`power` (耗电量) vs `maxPower` (发电量)
   - 人口相关：`population` (实际人口) vs `maxPopulation` (人口容量)

3. **地图访问**:
   - 使用 `gs.getTile(x, y)` 安全访问地图数据
   - 使用 `gs.setTile(x, y, patch)` 更新地图数据

4. **持久化**:
   - 所有 state 变量都会自动持久化到 localStorage
   - 页面刷新后会自动恢复状态
