三国群英传游戏构建器

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

Build a Three Kingdoms strategy game (三国群英传) in a single HTML file with SVG map, 3D CSS cities, battle system, hero recruitment, diplomacy, and custom factions. Invoke when user wants to create, modify, or extend a Three Kingdoms style strategy game.

一句话选型结论

按资料字段整理

三国群英传游戏构建器是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_170e8f72/sg-heroes-builder
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_170e8f72仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791621958
正文体量42.6 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
三国群英传游戏构建器当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

42.6 KB
模型实际读到的说明书原文,来自技能仓库

三国群英传游戏构建技能 (SG Heroes Builder)

This skill builds a complete turn-based Three Kingdoms strategy game in a single HTML file. It covers all systems: map rendering, 3D cities, factions, heroes, battles, internal affairs, diplomacy, wild general recruitment, custom factions, and AI behavior.

Architecture Overview

Single-file approach: All HTML, CSS, and JavaScript live in one sg-heroes.html file. No external dependencies, no build tools, no frameworks. Pure vanilla JS + CSS 3D transforms + SVG.

File Structure

sg-heroes.html
├── <style>        — All CSS (theme variables, 3D city styles, animations, layout)
├── <body>         — Game screens (menu, game, battle, modal)
├── <script>       — All game logic (data, systems, rendering, AI, events)
└── manifest.json  — PWA manifest (optional)

Step 1: HTML Structure & Game Screens

Required DOM Elements

<!-- Main menu screen -->
<div id="menu-screen" class="screen active">
  <h1>三国群英传 - 群雄逐鹿</h1>
  <button id="start-game-btn" onclick="showFactionSelect()">开始游戏</button>
  <button onclick="loadGame()">读取存档</button>
</div>

<!-- Game screen with map + panels + action bar -->
<div id="game-screen" class="screen">
  <div id="top-bar">
    <span id="turn-info"></span>
    <span id="faction-info"></span>
    <span id="resource-info"></span>
  </div>
  <div id="map-container">
    <svg id="map-svg"></svg>
    <div id="cities-layer"></div>
    <!-- Zoom controls -->
    <div style="position:absolute;right:10px;top:10px;">
      <button onclick="zoomMap(0.2)">+</button>
      <button onclick="zoomMap(-0.2)">−</button>
    </div>
    <!-- Faction legend -->
    <div id="map-legend">...</div>
  </div>
  <div id="city-panel"></div>
  <div id="action-bar">
    <button onclick="centerOnCapital()">🏛 回主城</button>
    <button onclick="resetMapView()">全图</button>
    <button onclick="showGeneralsList()">武将名册</button>
    <button onclick="showTreasures()">宝物图鉴</button>
    <button onclick="showDiplomacy()">外交谋略</button>
    <button onclick="showFactionInfo()">天下大势</button>
    <button onclick="saveGame()">保存进度</button>
    <button onclick="confirmEndTurn()">结束回合</button>
  </div>
</div>

<!-- Battle screen -->
<div id="battle-screen" class="screen">...</div>

<!-- Modal overlay -->
<div id="modal-overlay"><div id="modal-content"></div></div>

Step 2: CSS Theme & 3D City Styles

Theme Variables

:root {
  --bg-dark: #0a0a0a;
  --bg-panel: #1a1410;
  --gold: #d4af37;
  --gold-dim: #b8860b;
  --text-main: #e8d5b7;
  --text-dim: #a0896a;
  --border: #5a3a10;
  --wei-color: #4169e1;
  --shu-color: #228b22;
  --wu-color: #dc143c;
  --qun-color: #daa520;
  --custom-color: #9b59b6;
  --neutral-color: #666;
}

3D City CSS (Isometric Projection)

.city-node {
  position: absolute;
  transform: translate(-50%, -80%);
  width: 40px; height: 40px;
  cursor: pointer;
  transition: transform 0.3s;
}
.city-node:hover { transform: translate(-50%, -80%) scale(1.15); z-index: 100; }

.city-3d {
  position: relative;
  width: 100%; height: 100%;
  transform-style: preserve-3d;
  transform: rotateX(55deg) rotateZ(-45deg);
}

/* City walls (two visible faces) */
.city-wall-left, .city-wall-right {
  position: absolute;
  bottom: 0;
  width: 50%; height: 60%;
  border: 1px solid #5a4520;
}
.city-wall-left { left: 0; }
.city-wall-right { right: 0; border-left: none; }

/* Corner towers */
.corner-tower {
  position: absolute;
  width: 35%; height: 50%;
  transform-style: preserve-3d;
}
.ct-tl { top: 0; left: 0; }
.ct-tr { top: 0; right: 0; }
.ct-bl { bottom: 0; left: 0; }
.ct-br { bottom: 0; right: 0; }

/* Tower roofs (Chinese style curved eaves using border triangles) */
.ct-roof, .tower-roof {
  position: absolute;
  top: -4px;
  left: 50%;
  transform: translateX(-50%);
}

/* Central tower */
.city-tower {
  position: absolute;
  top: 15%; left: 25%;
  width: 50%; height: 70%;
  transform-style: preserve-3d;
}

/* Flag */
.city-flag {
  position: absolute;
  top: -12px; left: 50%;
  transform: translateX(-50%) rotateZ(45deg);
}
.flag-pole { width: 2px; height: 16px; background: #444; }
.flag-cloth {
  width: 10px; height: 7px;
  animation: flagWave 1.5s ease-in-out infinite;
}
@keyframes flagWave { 0%,100%{transform:skewX(0);} 50%{transform:skewX(-8deg);} }

City Size Classes

.size-s { width: 32px; height: 32px; }
.size-m { width: 40px; height: 40px; }
.size-l { width: 48px; height: 48px; }
.capital { width: 56px; height: 56px; }

Special City Types

.water-city .city-base { background: #4a6a8a; }
.border-city .city-wall-left { border-color: #8b0000; }
.sky-city { animation: skyFloat 4s ease-in-out infinite; }
@keyframes skyFloat { 0%,100%{transform:translate(-50%,-80%);} 50%{transform:translate(-50%,-84%);} }

Map SVG & Layer

#map-container {
  position: relative;
  width: 100%; flex: 1;
  overflow: hidden;
  background: #f5e6c8;
  background-image: 
    radial-gradient(ellipse at center, rgba(245,230,200,1) 0%, rgba(230,210,170,0.9) 100%);
}
#map-svg {
  position: absolute; top:0; left:0; width:100%; height:100%;
  transition: transform 0.5s cubic-bezier(0.22,1,0.36,1);
  transform-origin: center center;
}
#cities-layer {
  position: absolute; top:0; left:0; width:100%; height:100%;
  pointer-events: none;
  transition: transform 0.5s cubic-bezier(0.22,1,0.36,1);
  transform-origin: center center;
}

Step 3: Game Data Structures

Factions

const FACTIONS = {
  wei: { name:'曹魏', color:'#4169e1', leader:'曹操', capital:'xuchang', personality:'cautious' },
  shu: { name:'蜀汉', color:'#228b22', leader:'刘备', capital:'xinye', personality:'righteous' },
  wu: { name:'东吴', color:'#dc143c', leader:'孙权', capital:'jianye', personality:'defensive' },
  qun: { name:'群雄', color:'#daa520', leader:'董卓', capital:'luoyang', personality:'aggressive' },
  custom: { name:'自立', color:'#9b59b6', leader:'自定义', capital:'longcheng', personality:'balanced' }
};
const COLORS = { wei:'#4169e1', shu:'#228b22', wu:'#dc143c', qun:'#daa520', custom:'#9b59b6', neutral:'#666' };

Cities (50+ cities based on real Three Kingdoms geography)

const CITY_DATA = [
  // Central plains
  { id:'luoyang', name:'洛阳', x:48, y:40, faction:'qun', pop:80000, gold:200, food:200, defense:9, garrison:5000, isCapital:true, desc:'东都洛阳,天下之中' },
  { id:'xuchang', name:'许昌', x:52, y:45, faction:'wei', pop:60000, gold:150, food:150, defense:7, garrison:4000, isCapital:true, desc:'曹魏都城' },
  { id:'xinye', name:'新野', x:45, y:55, faction:'shu', pop:30000, gold:80, food:80, defense:5, garrison:2000, isCapital:true, desc:'刘备驻扎之地' },
  { id:'jianye', name:'建业', x:72, y:65, faction:'wu', pop:70000, gold:180, food:160, defense:8, garrison:4500, isCapital:true, desc:'吴国都城' },
  // ... 50+ more cities covering all of China
  { id:'longcheng', name:'悬空城', x:90, y:88, faction:'custom', pop:35000, gold:100, food:80, defense:7, garrison:2000, isCapital:true, desc:'云海之上的悬空之城' }
];

City Adjacency (for roads and movement)

const ADJACENCY = {
  luoyang: ['xuchang','tongguan','hulaoguan'],
  xuchang: ['luoyang','xinye','runan'],
  xinye: ['xuchang','xiangyang','wancheng'],
  // ... all connections
  longcheng: ['kuaiji'] // 悬空城通过海路连接会稽
};

Generals (60+ famous characters from Romance of the Three Kingdoms)

const GENERAL_DATA = [
  // Wei
  { id:'g_caocao', name:'曹操', faction:'wei', star:5, force:72, int:92, command:95, skill:'奸雄', skillDesc:'战斗胜率+15%', troop:'cavalry', bio:'治世之能臣,乱世之奸雄' },
  { id:'g_xiahou', name:'夏侯惇', faction:'wei', star:4, force:88, int:55, command:78, skill:'刚烈', skillDesc:'受伤后攻击+20%', troop:'spearman', bio:'独眼猛将' },
  // Shu
  { id:'g_liubei', name:'刘备', faction:'shu', star:5, force:65, int:85, command:88, skill:'仁德', skillDesc:'招募成功率+15%', troop:'spearman', bio:'中山靖王之后' },
  { id:'g_guanyu', name:'关羽', faction:'shu', star:5, force:98, int:78, command:85, skill:'武圣', skillDesc:'攻击+25%,必杀率+15%', troop:'cavalry', bio:'武财神,义薄云天' },
  { id:'g_zhangfei', name:'张飞', faction:'shu', star:5, force:97, int:50, command:72, skill:'咆哮', skillDesc:'敌军士气-20%', troop:'spearman', bio:'燕人张翼德' },
  // Wu
  { id:'g_sunquan', name:'孙权', faction:'wu', star:4, force:60, int:88, command:82, skill:'制衡', skillDesc:'防御+20%', troop:'archer', bio:'生子当如孙仲谋' },
  { id:'g_zhouyu', name:'周瑜', faction:'wu', star:5, force:72, int:95, command:90, skill:'火攻', skillDesc:'火攻伤害+50%', troop:'navy', bio:'美周郎' },
  // Qun
  { id:'g_dongzhuo', name:'董卓', faction:'qun', star:4, force:85, int:50, command:70, skill:'暴虐', skillDesc:'掠夺资源+30%', troop:'cavalry', bio:'权倾朝野的暴君' },
  // ... 60+ more
];

Wild Generals ( recruitable hidden characters)

const WILD_GENERALS = [
  // Legend tier (rare, powerful, expensive)
  { id:'w_lvbu', name:'吕布(野)', star:5, force:100, int:42, command:78, skill:'无双', skillDesc:'攻击+30%,必杀率+20%', troop:'cavalry', cost:3000, bio:'人中吕布', rarity:'legend' },
  { id:'w_zhaoyun', name:'赵云(野)', star:5, force:96, int:82, command:90, skill:'龙胆', skillDesc:'攻防各+15%', troop:'spearman', cost:2500, bio:'常山赵子龙', rarity:'legend' },
  // Epic tier
  { id:'w_zhugeliang', name:'诸葛亮(野)', star:5, force:40, int:100, command:88, skill:'卧龙', skillDesc:'计谋成功率+30%', troop:'archer', cost:3000, bio:'卧龙先生', rarity:'legend' },
  // Female generals
  { id:'w_diaochan', name:'貂蝉(野)', star:4, force:35, int:88, command:50, skill:'闭月', skillDesc:'敌将倒戈概率+25%', troop:'archer', cost:1500, bio:'四大美人之一', rarity:'epic', isFemale:true },
  // ... 40+ more wild generals
];

Treasures

const TREASURES = [
  { id:'t_qinglong', name:'青龙偃月刀', owner:'guanyu', forceBonus:8, desc:'重八十二斤,刀中之王' },
  { id:'t_zhangba', name:'丈八蛇矛', owner:'zhangfei', forceBonus:7, desc:'全用镔铁点钢' },
  { id:'t_chitu', name:'赤兔马', owner:null, speedBonus:5, desc:'日行千里,夜走八百' },
  // ... more treasures
];

Historical Events

const HISTORICAL_EVENTS = [
  {
    id:'e_taoyuan', name:'桃园结义', turn:1,
    condition: (g) => g.generals['g_liubei'] && g.generals['g_guanyu'] && g.generals['g_zhangfei'],
    effect: (g) => {
      // Add sworn brother relationship
      addRelationship('g_liubei','g_guanyu','sworn_brother');
      addRelationship('g_liubei','g_zhangfei','sworn_brother');
      addLog('【桃园结义】刘关张桃园三结义,兄弟同心!');
    }
  },
  {
    id:'e_sangu', name:'三顾茅庐', turn:10,
    condition: (g) => g.factions.shu.cities.length >= 2 && !g.generals['g_zhugeliang'],
    effect: (g) => {
      // Add Zhuge Liang to Shu faction
      addGeneralToFaction('g_zhugeliang','shu','xinye');
      addLog('【三顾茅庐】刘备三顾茅庐,诸葛亮出山!');
    }
  },
  // ... more events
];

Step 4: Map Rendering System

Coordinate Transformation (City spacing scale)

const MAP_SCALE = 1.75;
const MAP_CX = 50, MAP_CY = 50;

function mapX(x) { return MAP_CX + (x - MAP_CX) * MAP_SCALE; }
function mapY(y) { return MAP_CY + (y - MAP_CY) * MAP_SCALE; }

Map View Control (Pan/Zoom)

let mapView = { offsetX: 0, offsetY: 0, zoom: 1 };

function centerOnCapital() {
  const capitalId = game.factions[game.playerFaction].capital;
  const capital = game.cities[capitalId];
  const cx = mapX(capital.x), cy = mapY(capital.y);
  mapView.offsetX = 50 - cx;
  mapView.offsetY = 50 - cy;
  mapView.zoom = 1.6;
  renderMap();
  game.selectedCity = capitalId;
  showCityPanel(capitalId);
  showToast(`已回到主城:${capital.name}`);
}

function resetMapView() {
  mapView = { offsetX: 0, offsetY: 0, zoom: 1 };
  renderMap();
}

function zoomMap(delta) {
  mapView.zoom = Math.max(0.5, Math.min(2.5, mapView.zoom + delta));
  renderMap();
}

renderMap() Function (Complete)

function renderMap() {
  const svg = document.getElementById('map-svg');
  const layer = document.getElementById('cities-layer');
  svg.innerHTML = '';
  layer.innerHTML = '';

  svg.setAttribute('viewBox', '-40 -40 180 180');
  svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');

  // 1. Draw terrain decorations (mountains, forests)
  // Mountains (small hill symbols)
  const mountains = [[20,18],[25,22],[30,15],/*...*/];
  for (const [mx,my] of mountains) {
    const m = document.createElementNS('http://www.w3.org/2000/svg','path');
    const tx = mapX(mx), ty = mapY(my);
    m.setAttribute('d',`M ${tx-2} ${ty+1} L ${tx} ${ty-2} L ${tx+2} ${ty+1} Z`);
    m.setAttribute('fill','rgba(120,100,70,0.25)');
    svg.appendChild(m);
  }

  // 2. Draw faction territory color blocks
  // 2a. Territory bands between same-faction adjacent cities
  const territoryDrawn = new Set();
  for (const cityId in ADJACENCY) {
    const city = game.cities[cityId];
    if (!city || city.faction === 'neutral') continue;
    for (const adjId of ADJACENCY[cityId]) {
      const adj = game.cities[adjId];
      if (!adj || adj.faction !== city.faction) continue;
      const key = [cityId, adjId].sort().join('-');
      if (territoryDrawn.has(key)) continue;
      territoryDrawn.add(key);
      // Draw thick translucent line in faction color
      const x1=mapX(city.x), y1=mapY(city.y), x2=mapX(adj.x), y2=mapY(adj.y);
      const terrLine = document.createElementNS('http://www.w3.org/2000/svg','line');
      terrLine.setAttribute('x1',x1); terrLine.setAttribute('y1',y1);
      terrLine.setAttribute('x2',x2); terrLine.setAttribute('y2',y2);
      terrLine.setAttribute('stroke', COLORS[city.faction]);
      terrLine.setAttribute('stroke-width', '8');
      terrLine.setAttribute('stroke-linecap', 'round');
      terrLine.setAttribute('opacity', city.faction === game.playerFaction ? '0.18' : '0.12');
      svg.appendChild(terrLine);
    }
  }
  // 2b. Faction glow circles around each city
  for (const cityId in game.cities) {
    const city = game.cities[cityId];
    if (city.faction === 'neutral') continue;
    const cx = mapX(city.x), cy = mapY(city.y);
    const color = COLORS[city.faction];
    const isPlayer = city.faction === game.playerFaction;
    const isCapital = city.isCapital;
    const radius = isCapital ? 7 : 5;
    // Outer glow
    const glow = document.createElementNS('http://www.w3.org/2000/svg','circle');
    glow.setAttribute('cx',cx); glow.setAttribute('cy',cy);
    glow.setAttribute('r',radius+3);
    glow.setAttribute('fill',color);
    glow.setAttribute('opacity', isPlayer ? '0.08' : '0.05');
    svg.appendChild(glow);
    // Core circle
    const core = document.createElementNS('http://www.w3.org/2000/svg','circle');
    core.setAttribute('cx',cx); core.setAttribute('cy',cy);
    core.setAttribute('r',radius);
    core.setAttribute('fill',color);
    core.setAttribute('opacity', isPlayer ? '0.15' : '0.1');
    svg.appendChild(core);
    // Capital ring
    if (isCapital) {
      const ring = document.createElementNS('http://www.w3.org/2000/svg','circle');
      ring.setAttribute('cx',cx); ring.setAttribute('cy',cy);
      ring.setAttribute('r',radius+1.5);
      ring.setAttribute('fill','none');
      ring.setAttribute('stroke',color);
      ring.setAttribute('stroke-width','0.5');
      ring.setAttribute('opacity','0.5');
      ring.setAttribute('stroke-dasharray','1,0.5');
      svg.appendChild(ring);
    }
  }

  // 3. Draw roads (passable paths between cities)
  const drawn = new Set();
  for (const cityId in ADJACENCY) {
    const city = game.cities[cityId];
    if (!city) continue;
    for (const adjId of ADJACENCY[cityId]) {
      const adj = game.cities[adjId];
      if (!adj) continue;
      const key = [cityId, adjId].sort().join('-');
      if (drawn.has(key)) continue;
      drawn.add(key);
      const x1=mapX(city.x), y1=mapY(city.y), x2=mapX(adj.x), y2=mapY(adj.y);
      const sameFaction = city.faction === adj.faction && city.faction !== 'neutral';
      // Road shadow (dark)
      const shadow = document.createElementNS('http://www.w3.org/2000/svg','line');
      shadow.setAttribute('x1',x1); shadow.setAttribute('y1',y1);
      shadow.setAttribute('x2',x2); shadow.setAttribute('y2',y2);
      shadow.setAttribute('stroke','rgba(120,90,50,0.4)');
      shadow.setAttribute('stroke-width','1.2');
      svg.appendChild(shadow);
      // Road main (earthen yellow)
      const road = document.createElementNS('http://www.w3.org/2000/svg','line');
      road.setAttribute('x1',x1); road.setAttribute('y1',y1);
      road.setAttribute('x2',x2); road.setAttribute('y2',y2);
      road.setAttribute('stroke', sameFaction ? 'rgba(180,140,80,0.7)' : 'rgba(160,130,80,0.5)');
      road.setAttribute('stroke-width','0.7');
      svg.appendChild(road);
      // Dashed line for same-faction official roads
      if (sameFaction) {
        const dash = document.createElementNS('http://www.w3.org/2000/svg','line');
        dash.setAttribute('x1',x1); dash.setAttribute('y1',y1);
        dash.setAttribute('x2',x2); dash.setAttribute('y2',y2);
        dash.setAttribute('stroke','rgba(200,170,100,0.4)');
        dash.setAttribute('stroke-width','0.2');
        dash.setAttribute('stroke-dasharray','1.5,1.5');
        svg.appendChild(dash);
      }
    }
  }

  // 4. Draw rivers (Yangtze and Yellow River)
  // Draw as SVG paths with blue translucent strokes

  // 5. Draw cities as 3D DOM elements
  for (const cityId in game.cities) {
    const city = game.cities[cityId];
    const color = city.faction === 'neutral' ? COLORS.neutral : COLORS[city.faction];
    const node = document.createElement('div');
    node.className = `city-node ${sizeClass}${specialClass}`;
    node.style.left = mapX(city.x) + '%';
    node.style.top = mapY(city.y) + '%';
    node.onclick = () => onCityClick(cityId);

    // Build 3D city HTML with faction-specific roof colors
    const city3d = document.createElement('div');
    city3d.className = 'city-3d';
    city3d.innerHTML = buildCity3DHTML(color, roofColor, wallColors);
    node.appendChild(city3d);

    // Wild general indicator badge
    const wildGens = getWildGeneralsInCity(cityId);
    if (wildGens.length > 0) {
      const badge = document.createElement('div');
      badge.style.cssText = 'position:absolute;top:-2px;right:-8px;width:14px;height:14px;background:linear-gradient(135deg,#ff6b35,#ff8c42);color:#fff;font-size:0.6em;font-weight:bold;border-radius:50%;display:flex;align-items:center;justify-content:center;animation:wildPulse 1.5s infinite;z-index:10;';
      badge.textContent = '贤';
      city3d.appendChild(badge);
    }

    // City name label
    const nameLabel = document.createElement('div');
    nameLabel.className = 'city-name';
    nameLabel.textContent = city.name + (city.isCapital ? '★' : '');
    node.appendChild(nameLabel);

    // Garrison count
    const garLabel = document.createElement('div');
    garLabel.className = 'city-gar';
    garLabel.textContent = city.garrison;
    node.appendChild(garLabel);

    layer.appendChild(node);
  }

  // 6. Apply map view transform (SVG and layer sync)
  const tx = mapView.offsetX, ty = mapView.offsetY, s = mapView.zoom;
  const cssTransform = `translate(${tx}%, ${ty}%) scale(${s})`;
  svg.style.transform = cssTransform;
  layer.style.transform = cssTransform;
}

3D City HTML Builder

function buildCity3DHTML(color, roofColor, wallColors) {
  const { wallBase, wallLight, wallDark } = wallColors;
  return `
    <div class="city-base" style="background:${wallBase};"></div>
    <div class="city-wall-left" style="background:linear-gradient(180deg,${wallLight} 0%,${wallBase} 100%);">
      <div class="crenellations"></div>
    </div>
    <div class="city-wall-right" style="background:linear-gradient(180deg,${wallDark} 0%,${wallDark} 100%);">
      <div class="crenellations"></div>
    </div>
    <div class="city-gate"></div>
    <!-- 4 corner towers with roofs -->
    <div class="corner-tower ct-tl">...</div>
    <div class="corner-tower ct-tr">...</div>
    <div class="corner-tower ct-bl">...</div>
    <div class="corner-tower ct-br">...</div>
    <!-- Central tower -->
    <div class="city-tower">...</div>
    <!-- Flag -->
    <div class="city-flag">
      <div class="flag-pole"></div>
      <div class="flag-cloth" style="background:${color};"></div>
    </div>
  `;
}

Step 5: Game Initialization

function initGame(playerFactionId) {
  mapView = { offsetX: 0, offsetY: 0, zoom: 1 };
  game = {
    turn: 1,
    playerFaction: playerFactionId,
    cities: {},
    factions: {},
    generals: {},
    wildGenerals: {}, // cityId -> [generalId, ...]
    treasures: {},
    relationships: {},
    log: [],
    selectedCity: null,
    pendingAction: null
  };

  // Initialize cities from CITY_DATA
  for (const cd of CITY_DATA) {
    game.cities[cd.id] = { ...cd, garrison: cd.garrison, originalFaction: cd.faction };
  }

  // Initialize factions
  for (const f in FACTIONS) {
    const fdata = FACTIONS[f];
    let capitalCity = null;
    for (const cid in game.cities) {
      if (game.cities[cid].faction === f && game.cities[cid].isCapital) {
        capitalCity = cid; break;
      }
    }
    game.factions[f] = {
      name: fdata.name, color: fdata.color, leader: fdata.leader,
      capital: capitalCity, gold: 2000, food: 2000,
      personality: fdata.personality, atWar: {}, allies: []
    };
  }

  // Initialize generals from GENERAL_DATA
  for (const gd of GENERAL_DATA) {
    game.generals[gd.id] = {
      ...gd, troops: 1000+gd.star*200, maxTroops: 800+gd.star*200,
      level: 1, exp: 0, loyalty: 90, wounded: false, treasures: []
    };
    // Assign to city
    for (const cid in game.cities) {
      if (game.cities[cid].faction === gd.faction) {
        game.generals[gd.id].cityId = cid; break;
      }
    }
  }

  // Initialize wild generals (random distribution)
  distributeWildGenerals();

  // Initialize treasures
  for (const t of TREASURES) {
    game.treasures[t.id] = { ...t };
  }

  // Initialize relationships
  initRelationships();

  // Trigger turn-1 events
  checkHistoricalEvents();

  renderAll();
}

Step 6: Custom Faction System

Custom Lord Setup

let customLordName = '新君主';
let customFemaleName = '红颜';

function showCustomLordDialog() {
  let html = `<div class="modal-header"><span class="modal-title">自立势力 - 君主设定</span></div>`;
  html += `<div class="modal-body">
    <div style="margin:20px 0;">
      <label>君主姓名:</label>
      <input id="custom_lord_name" type="text" value="${customLordName}" maxlength="6"
             style="background:var(--bg-dark);border:2px solid var(--gold);color:var(--gold);padding:8px;font-size:1.2em;">
    </div>
    <div style="margin:20px 0;">
      <label>女武将姓名:</label>
      <input id="custom_female_name" type="text" value="${customFemaleName}" maxlength="6"
             style="background:var(--bg-dark);border:2px solid var(--gold);color:var(--gold);padding:8px;font-size:1.2em;">
    </div>
    <div style="color:var(--text-dim);font-size:0.85em;">
      ◆ 起始城池:悬空城(云海之上,右下角秘境)<br>
      ◆ 配备:自定义女武将(弓将)+ 天威将军(猛将)<br>
      ◆ 初始资源:金币3500、粮草3500
    </div>
  </div>`;
  html += `<div class="modal-footer">
    <button class="btn btn-success" onclick="confirmCustomLord()">确认起兵</button>
    <button class="btn" onclick="closeModal()">取消</button>
  </div>`;
  showModal(html);
}

function confirmCustomLord() {
  const ln = document.getElementById('custom_lord_name').value.trim() || '新君主';
  const fn = document.getElementById('custom_female_name').value.trim() || '红颜';
  customLordName = ln;
  customFemaleName = fn;
  closeModal();
  selectFactionMenu('custom');
}

function initCustomFaction() {
  game.factions.custom = {
    name: '自立', color: COLORS.custom, leader: customLordName,
    capital: 'longcheng', gold: 3500, food: 3500,
    personality: 'balanced', atWar: {}, allies: []
  };

  // Custom lord general
  game.generals['g_custom_lord'] = {
    id: 'g_custom_lord', name: customLordName, faction: 'custom',
    star: 5, force: 88, int: 85, command: 90,
    skill: '雄主', skillDesc: '攻防+15%,招募率+20%',
    troop: 'cavalry', troops: 2000, maxTroops: 2500,
    loyalty: 100, level: 1, exp: 0, wounded: false, treasures: [],
    isLord: true, cityId: 'longcheng',
    biography: customLordName + ',于悬空城起兵,志在天下。'
  };

  // Custom female general
  game.generals['g_custom_female'] = {
    id: 'g_custom_female', name: customFemaleName, faction: 'custom',
    star: 5, force: 85, int: 92, command: 86,
    skill: '巾帼', skillDesc: '弓术+20%,士气+15%',
    troop: 'archer', troops: 1500, maxTroops: 2000,
    loyalty: 95, level: 1, exp: 0, wounded: false, treasures: [],
    isFemale: true, cityId: 'longcheng',
    biography: customFemaleName + ',文武双全的巾帼英雄。'
  };

  // Third powerful general: 天威将军
  game.generals['g_tianwei'] = {
    id: 'g_tianwei', name: '天威将军', faction: 'custom',
    star: 5, force: 96, int: 70, command: 88,
    skill: '天威', skillDesc: '攻击+25%,冲锋+20%',
    troop: 'cavalry', troops: 1800, maxTroops: 2300,
    loyalty: 95, level: 1, exp: 0, wounded: false, treasures: [],
    isLordGeneral: true, cityId: 'longcheng',
    biography: '天生神力,万夫不当之勇,随主公起兵悬空城。'
  };

  // Assign to 悬空城
  game.cities['longcheng'].garrison = 2000;
  addLog(`【悬空起兵】${customLordName}于悬空城起兵,${customFemaleName}与天威将军相随,霸业自此始!`);
}

Step 7: Wild General Recruitment System

Distribution

function distributeWildGenerals() {
  game.wildGenerals = {};
  const shuffled = [...WILD_GENERALS].sort(() => Math.random() - 0.5);
  const cities = Object.keys(game.cities);
  // Distribute to ~20 cities, 1-2 generals each
  let idx = 0;
  for (const wg of shuffled) {
    if (idx >= 25) break;
    const cityId = cities[Math.floor(Math.random() * cities.length)];
    if (!game.wildGenerals[cityId]) game.wildGenerals[cityId] = [];
    if (game.wildGenerals[cityId].length < 2) {
      game.wildGenerals[cityId].push(wg.id);
      game.generals[wg.id] = {
        ...wg, faction: null, cityId: cityId, troops: 0,
        maxTroops: 800+wg.star*200, level: 1, exp: 0,
        loyalty: 0, wounded: false, treasures: [], isWild: true
      };
      idx++;
    }
  }
}

function getWildGeneralsInCity(cityId) {
  return game.wildGenerals[cityId] || [];
}

Search & Recruit

function searchWildGeneral(cityId) {
  const city = game.cities[cityId];
  if (city.faction !== game.playerFaction) { showToast('只能在己方城池寻访!'); return; }
  if (game.factions[game.playerFaction].gold < 200) { showToast('寻访需要200金!'); return; }

  const wildGens = getWildGeneralsInCity(cityId);
  if (wildGens.length === 0) {
    // Search with 25% chance to discover new general
    game.factions[game.playerFaction].gold -= 200;
    if (Math.random() < 0.25) {
      const remaining = WILD_GENERALS.filter(w => !game.generals[w.id]);
      if (remaining.length > 0) {
        const newWild = remaining[Math.floor(Math.random()*remaining.length)];
        const wildGen = { ...newWild, faction:null, cityId:cityId, troops:0,
          maxTroops: 800+newWild.star*200, level:1, exp:0, loyalty:0,
          wounded:false, treasures:[], isWild:true };
        game.generals[newWild.id] = wildGen;
        if (!game.wildGenerals[cityId]) game.wildGenerals[cityId] = [];
        game.wildGenerals[cityId].push(newWild.id);
        addLog(`【寻访】在${city.name}发现了在野武将${newWild.name}!`);
        showToast(`发现武将:${newWild.name}!`);
      } else { showToast('未发现贤才'); }
    } else { showToast('未发现贤才'); }
    renderAll();
    return;
  }

  // Show recruitment UI
  let html = `<div class="modal-header"><span class="modal-title">在野武将 - ${city.name}</span></div>`;
  html += `<div class="modal-body">`;
  for (const wid of wildGens) {
    const g = game.generals[wid];
    if (!g) continue;
    // Success rate by rarity + faction leader bonuses
    let successRate = g.rarity==='legend' ? 0.35 : g.rarity==='epic' ? 0.5 : g.rarity==='rare' ? 0.7 : 0.85;
    const lord = game.generals['g_custom_lord'] || game.generals['g_liubei'];
    if (lord && lord.skill === '雄主') successRate += 0.2;
    if (lord && lord.skill === '仁德') successRate += 0.15;
    successRate = Math.min(0.95, successRate);
    // Build recruitment card with stats, cost, success rate, recruit button
    html += buildRecruitCard(g, successRate, cityId);
  }
  html += `</div>`;
  showModal(html);
}

function recruitGeneral(wid, cityId) {
  const g = game.generals[wid];
  const city = game.cities[cityId];
  const faction = game.factions[game.playerFaction];
  if (faction.gold < g.cost) { showToast('金币不足!'); return; }

  let successRate = g.rarity==='legend' ? 0.35 : g.rarity==='epic' ? 0.5 : g.rarity==='rare' ? 0.7 : 0.85;
  const lord = game.generals['g_custom_lord'] || game.generals['g_liubei'];
  if (lord && lord.skill === '雄主') successRate += 0.2;
  if (lord && lord.skill === '仁德') successRate += 0.15;
  successRate = Math.min(0.95, successRate);

  faction.gold -= g.cost;

  if (Math.random() < successRate) {
    // Success
    g.faction = game.playerFaction;
    g.cityId = cityId;
    g.troops = 500;
    g.maxTroops = 800 + g.star * 200;
    g.loyalty = 70;
    g.isWild = false;
    game.wildGenerals[cityId] = game.wildGenerals[cityId].filter(id => id !== wid);
    addLog(`【招募】成功招募${g.name}!`);
    showToast(`${g.name}已加入麾下!`);
  } else {
    addLog(`【招募】${g.name}拒绝了招募...`);
    showToast(`${g.name}拒绝了招募`);
  }
  renderAll();
  // Reopen panel
  searchWildGeneral(cityId);
}

Wild General AI Movement (End of Turn)

function moveWildGenerals() {
  for (const cityId in game.wildGenerals) {
    const gens = [...game.wildGenerals[cityId]];
    for (const wid of gens) {
      if (Math.random() < 0.15) { // 15% chance to move
        const neighbors = ADJACENCY[cityId] || [];
        if (neighbors.length > 0) {
          const newCity = neighbors[Math.floor(Math.random()*neighbors.length)];
          // Remove from old city
          game.wildGenerals[cityId] = game.wildGenerals[cityId].filter(id => id !== wid);
          // Add to new city
          if (!game.wildGenerals[newCity]) game.wildGenerals[newCity] = [];
          game.wildGenerals[newCity].push(wid);
          if (game.generals[wid]) game.generals[wid].cityId = newCity;
        }
      }
    }
  }
}

Step 8: Battle System

Battle Calculation

function calculateBattle(attacker, defender, attackerCity, defenderCity) {
  let atkPower = attacker.force + attacker.troops * 0.1;
  let defPower = defender.force + defender.troops * 0.1;

  // Formation bonuses
  atkPower *= getFormationBonus(currentFormation, 'attack');
  defPower *= getFormationBonus(defenderFormation, 'defense');

  // Unit counter system (cavalry > archer > spearman > cavalry)
  atkPower *= getUnitCounterMultiplier(attacker.troop, defender.troop);

  // Skill effects
  atkPower *= getSkillMultiplier(attacker, 'attack');
  defPower *= getSkillMultiplier(defender, 'defense');

  // Relationship bonuses (sworn brothers fight together: +10%)
  if (hasRelationship(attacker.id, defender.id, 'sworn_brother')) {
    atkPower *= 1.1;
  }

  // Treasure bonuses
  atkPower += getTreasureBonus(attacker, 'force');
  defPower += getTreasureBonus(defender, 'force');

  // Random factor
  atkPower *= (0.8 + Math.random() * 0.4);
  defPower *= (0.8 + Math.random() * 0.4);

  const attackerWins = atkPower > defPower;
  const casualtyRate = Math.abs(atkPower - defPower) / Math.max(atkPower, defPower);

  return {
    winner: attackerWins ? 'attacker' : 'defender',
    attackerLosses: Math.floor(attacker.troops * casualtyRate * 0.5),
    defenderLosses: Math.floor(defender.troops * casualtyRate * 0.5),
    attackerPower: atkPower,
    defenderPower: defPower
  };
}

Unit Counter System

const UNIT_COUNTERS = {
  cavalry: 'archer',   // Cavalry beats Archer
  archer: 'spearman',  // Archer beats Spearman
  spearman: 'cavalry', // Spearman beats Cavalry
  navy: 'none'         // Navy is neutral on water
};

function getUnitCounterMultiplier(atkTroop, defTroop) {
  if (UNIT_COUNTERS[atkTroop] === defTroop) return 1.3; // Counter advantage
  if (UNIT_COUNTERS[defTroop] === atkTroop) return 0.7; // Counter disadvantage
  return 1.0;
}

Step 9: Internal Affairs System

function developCity(cityId, action) {
  const city = game.cities[cityId];
  const faction = game.factions[city.faction];
  switch(action) {
    case 'farm':      // +Food
      if (faction.gold < 100) { showToast('金币不足'); return; }
      faction.gold -= 100;
      city.food += 50 + Math.floor(Math.random()*50);
      addLog(`【内政】${city.name}农业发展,粮食+${50}`);
      break;
    case 'commerce':  // +Gold
      if (faction.gold < 100) { showToast('金币不足'); return; }
      faction.gold -= 100;
      faction.gold += 200 + Math.floor(Math.random()*100);
      addLog(`【内政】${city.name}商业发展,金币+200`);
      break;
    case 'fortify':   // +Defense
      if (faction.gold < 150) { showToast('金币不足'); return; }
      faction.gold -= 150;
      city.defense += 1;
      addLog(`【内政】${city.name}城防加固,防御+1`);
      break;
    case 'recruit':   // +Garrison
      if (faction.gold < 100 || faction.food < 100) { showToast('资源不足'); return; }
      faction.gold -= 100; faction.food -= 100;
      city.garrison += 500;
      addLog(`【内政】${city.name}招募士兵+500`);
      break;
  }
  renderAll();
}

Step 10: Diplomacy System

function diplomacyAction(targetFaction, action) {
  const player = game.factions[game.playerFaction];
  const target = game.factions[targetFaction];
  switch(action) {
    case 'alliance':
      if (player.gold < 500) { showToast('金币不足'); return; }
      player.gold -= 500;
      if (Math.random() < 0.5) {
        player.allies.push(targetFaction);
        target.allies.push(game.playerFaction);
        addLog(`【外交】与${target.name}结成同盟!`);
      } else { addLog(`【外交】${target.name}拒绝了同盟提议`); }
      break;
    case 'sowDiscord':
      if (player.gold < 800) { showToast('金币不足'); return; }
      player.gold -= 800;
      // Reduce target's relationship with its allies
      addLog(`【外交】对${target.name}实施离间计`);
      break;
    case 'surrender':
      // Persuade target city to surrender
      addLog(`【外交】劝降${target.name}`);
      break;
  }
  renderAll();
}

Step 11: AI Behavior System

function runAITurn(factionId) {
  const faction = game.factions[factionId];
  const personality = faction.personality;

  // 1. Internal affairs
  for (const cityId in game.cities) {
    const city = game.cities[cityId];
    if (city.faction !== factionId) continue;
    // AI develops based on personality
    if (personality === 'cautious' && city.defense < 8) developCity(cityId, 'fortify');
    else if (personality === 'aggressive' && city.garrison < 3000) developCity(cityId, 'recruit');
    else developCity(cityId, Math.random() < 0.5 ? 'farm' : 'commerce');
  }

  // 2. Military decisions
  const myCities = Object.values(game.cities).filter(c => c.faction === factionId);
  const enemyCities = Object.values(game.cities).filter(c => c.faction !== factionId && c.faction !== 'neutral');

  // Aggression level by personality and turn
  const baseAggression = personality === 'aggressive' ? 0.7 : personality === 'cautious' ? 0.2 : 0.4;
  const turnBonus = Math.min(0.3, game.turn * 0.01);
  const aggression = baseAggression + turnBonus;

  if (Math.random() < aggression) {
    // Find weakest adjacent enemy city
    for (const myCity of myCities) {
      const adjacent = ADJACENCY[myCity.id] || [];
      for (const adjId of adjacent) {
        const enemy = game.cities[adjId];
        if (!enemy || enemy.faction === factionId || enemy.faction === 'neutral') continue;
        if (myCity.garrison > enemy.garrison * 1.5) {
          // Attack!
          aiAttackCity(factionId, myCity.id, adjId);
          break;
        }
      }
    }
  }

  // 3. Wild general movement
  moveWildGenerals();
}

Step 12: Turn Management

function confirmEndTurn() {
  let html = `<div class="modal-header"><span class="modal-title">结束回合?</span></div>`;
  html += `<div class="modal-body"><p>当前回合:第${game.turn}回合</p></div>`;
  html += `<div class="modal-footer"><button class="btn btn-success" onclick="endTurn()">确认结束</button><button class="btn" onclick="closeModal()">取消</button></div>`;
  showModal(html);
}

function endTurn() {
  closeModal();
  // AI turns
  for (const f in game.factions) {
    if (f !== game.playerFaction) runAITurn(f);
  }
  game.turn++;
  // Income
  for (const fid in game.factions) {
    const faction = game.factions[fid];
    for (const cityId in game.cities) {
      const city = game.cities[cityId];
      if (city.faction === fid) {
        faction.gold += 50 + Math.floor(city.pop / 1000);
        faction.food += 30 + Math.floor(city.pop / 2000);
      }
    }
  }
  // Check events
  checkHistoricalEvents();
  // Check victory/defeat
  checkGameOver();
  addLog(`--- 第${game.turn}回合 ---`);
  renderAll();
}

Step 13: Save/Load System

function saveGame() {
  const data = JSON.stringify(game);
  localStorage.setItem('sgHeroesSave', data);
  showToast('进度已保存');
}

function loadGame() {
  const data = localStorage.getItem('sgHeroesSave');
  if (!data) { showToast('无存档'); return; }
  game = JSON.parse(data);
  showScreen('game-screen');
  renderAll();
  showToast('存档已读取');
}

Step 14: Visual Effects System (FX Class)

class FX {
  static flash(element, color = 'gold', duration = 500) {
    const colors = { gold: '#d4af37', red: '#dc143c', green: '#228b22', blue: '#4169e1' };
    const c = colors[color] || color;
    element.style.transition = 'box-shadow 0.3s';
    element.style.boxShadow = `0 0 20px ${c}, 0 0 40px ${c}`;
    setTimeout(() => { element.style.boxShadow = ''; }, duration);
  }

  static particleBurst(x, y, count = 12) {
    for (let i = 0; i < count; i++) {
      const p = document.createElement('div');
      p.style.cssText = `position:fixed;left:${x}px;top:${y}px;width:6px;height:6px;background:#d4af37;border-radius:50%;pointer-events:none;z-index:9999;`;
      const angle = (i / count) * Math.PI * 2;
      const dist = 50 + Math.random() * 50;
      p.animate([
        { transform: 'translate(0,0) scale(1)', opacity: 1 },
        { transform: `translate(${Math.cos(angle)*dist}px,${Math.sin(angle)*dist}px) scale(0)`, opacity: 0 }
      ], { duration: 600, easing: 'cubic-bezier(0.22,1,0.36,1)' });
      document.body.appendChild(p);
      setTimeout(() => p.remove(), 600);
    }
  }

  static screenShake(intensity = 5, duration = 300) {
    const container = document.body;
    const startTime = Date.now();
    function shake() {
      const elapsed = Date.now() - startTime;
      if (elapsed > duration) { container.style.transform = ''; return; }
      const x = (Math.random() - 0.5) * intensity;
      const y = (Math.random() - 0.5) * intensity;
      container.style.transform = `translate(${x}px, ${y}px)`;
      requestAnimationFrame(shake);
    }
    shake();
  }
}

Step 15: Key CSS Animations

@keyframes wildPulse { 0%,100%{transform:scale(1);} 50%{transform:scale(1.15);} }
@keyframes pulse { 0%,100%{box-shadow:0 0 5px rgba(34,139,34,0.5);} 50%{box-shadow:0 0 15px rgba(34,139,34,0.8);} }
@keyframes flagWave { 0%,100%{transform:skewX(0);} 50%{transform:skewX(-8deg);} }
@keyframes skyFloat { 0%,100%{transform:translate(-50%,-80%) translateY(0px);} 50%{transform:translate(-50%,-80%) translateY(-4px);} }
@keyframes cloudFloat { 0%,100%{opacity:0.6;transform:scaleX(1);} 50%{opacity:0.9;transform:scaleX(1.1);} }
@keyframes skyGlow { 0%{opacity:0.4;transform:scale(1);} 100%{opacity:0.8;transform:scale(1.3);} }

Step 16: Helper Functions

function showToast(msg) {
  const toast = document.createElement('div');
  toast.className = 'toast';
  toast.textContent = msg;
  document.body.appendChild(toast);
  setTimeout(() => toast.remove(), 2500);
}

function showModal(html) {
  document.getElementById('modal-content').innerHTML = html;
  document.getElementById('modal-overlay').classList.add('active');
}

function closeModal() {
  document.getElementById('modal-overlay').classList.remove('active');
}

function addLog(msg) {
  game.log.push({ turn: game.turn, msg });
}

function showScreen(id) {
  document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
  document.getElementById(id).classList.add('active');
}

function renderAll() {
  renderMap();
  renderTopBar();
  if (game.selectedCity) showCityPanel(game.selectedCity);
}

Build Order (Implementation Sequence)

  1. HTML structure — game screens, panels, modal
  2. CSS theme — variables, layout, city styles, animations
  3. Data definitions — CITY_DATA, GENERAL_DATA, WILD_GENERALS, TREASURES, ADJACENCY
  4. initGame() — initialize all game state
  5. renderMap() — SVG terrain + territory + roads + 3D city nodes
  6. City panel — show city info, generals, internal affairs, recruit, attack
  7. Battle system — calculateBattle, formation, unit counters, FX
  8. Internal affairs — developCity (farm, commerce, fortify, recruit)
  9. Diplomacy — alliance, sow discord, surrender
  10. AI behavior — runAITurn with personality-based decisions
  11. Turn management — endTurn, income, events, victory check
  12. Wild generals — distributeWildGenerals, searchWildGeneral, recruitGeneral, moveWildGenerals
  13. Custom faction — showCustomLordDialog, initCustomFaction, 悬空城 setup
  14. Map features — centerOnCapital, resetMapView, zoomMap, wheel zoom
  15. Save/Load — localStorage persistence
  16. Polish — FX effects, toast, modal, log, legend

常见问题

5 问
答案由本站依据条目字段整理
三国群英传游戏构建器怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_170e8f72/sg-heroes-builder。安装方式为未标注。装完刷新技能列表或重开客户端。

三国群英传游戏构建器支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

三国群英传游戏构建器权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

三国群英传游戏构建器收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

三国群英传游戏构建器用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。