Add class and terrain battle rules
This commit is contained in:
@@ -16,7 +16,7 @@ Build a small complete tactical RPG loop:
|
||||
- Cinematic title scene with an original Taoyuan Oath background, subtle motion, menu UI, and first-input audio
|
||||
- Cinematic prologue pages with high-resolution story backgrounds, character portraits, non-black scene transitions, and scenario data
|
||||
- File-based original soft stereo orchestral BGM loops for title, prologue, oath, militia, and battle-prep scenes
|
||||
- First battle scene with a high-resolution battlefield background, large 12x12 tactical grid, high-detail pixel-style unit sprites, ally/enemy roster tabs, unit detail stat panels, movement range preview, click-to-move unit movement, cursor-adjacent post-move command popup, right-click move cancel, right-click tactical menu, turn ending/reset, resonance bond tracking, and grayscale acted-unit feedback
|
||||
- First battle scene with a high-resolution battlefield background, large 12x12 tactical grid, high-detail pixel-style unit sprites, class and terrain affinity rules, ally/enemy roster tabs, unit detail stat panels, terrain-cost movement range preview, click-to-move unit movement, cursor-adjacent post-move command popup, right-click move cancel, right-click tactical menu, turn ending/reset, resonance bond tracking, and grayscale acted-unit feedback
|
||||
- Flow verification script from title to first battle
|
||||
|
||||
## Next Steps
|
||||
|
||||
195
src/game/data/battleRules.ts
Normal file
195
src/game/data/battleRules.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
export type TerrainType = 'plain' | 'road' | 'forest' | 'hill' | 'village' | 'fort' | 'camp';
|
||||
|
||||
export type UnitClassKey = 'lord' | 'infantry' | 'cavalry' | 'spearman' | 'archer' | 'bandit' | 'yellowTurban' | 'rebelLeader';
|
||||
|
||||
export type TerrainRule = {
|
||||
label: string;
|
||||
color: number;
|
||||
alpha: number;
|
||||
moveCost: number;
|
||||
defenseBonus: number;
|
||||
};
|
||||
|
||||
export type UnitClassDefinition = {
|
||||
key: UnitClassKey;
|
||||
name: string;
|
||||
family: string;
|
||||
role: string;
|
||||
description: string;
|
||||
terrainRatings: Record<TerrainType, number>;
|
||||
movementCosts?: Partial<Record<TerrainType, number>>;
|
||||
};
|
||||
|
||||
export const terrainRules: Record<TerrainType, TerrainRule> = {
|
||||
plain: { label: '평지', color: 0x6d8056, alpha: 0.04, moveCost: 1, defenseBonus: 0 },
|
||||
road: { label: '길', color: 0xb08b55, alpha: 0.1, moveCost: 1, defenseBonus: 0 },
|
||||
forest: { label: '숲', color: 0x355f42, alpha: 0.17, moveCost: 2, defenseBonus: 10 },
|
||||
hill: { label: '언덕', color: 0x82724e, alpha: 0.15, moveCost: 2, defenseBonus: 8 },
|
||||
village: { label: '마을', color: 0x8d6f4b, alpha: 0.2, moveCost: 1, defenseBonus: 12 },
|
||||
fort: { label: '요새', color: 0x735c46, alpha: 0.22, moveCost: 1, defenseBonus: 18 },
|
||||
camp: { label: '진영', color: 0x4f6a73, alpha: 0.17, moveCost: 1, defenseBonus: 15 }
|
||||
};
|
||||
|
||||
export const unitClasses: Record<UnitClassKey, UnitClassDefinition> = {
|
||||
lord: {
|
||||
key: 'lord',
|
||||
name: '군웅',
|
||||
family: '군주계',
|
||||
role: '지휘 / 균형',
|
||||
description: '지형을 크게 타지 않고 아군 전선을 안정시키는 지휘관 병과.',
|
||||
terrainRatings: {
|
||||
plain: 100,
|
||||
road: 105,
|
||||
forest: 95,
|
||||
hill: 95,
|
||||
village: 110,
|
||||
fort: 115,
|
||||
camp: 115
|
||||
}
|
||||
},
|
||||
infantry: {
|
||||
key: 'infantry',
|
||||
name: '의용 보병',
|
||||
family: '보병계',
|
||||
role: '전열 / 방어',
|
||||
description: '숲과 진영에서 버티는 힘이 좋고 기병의 돌격을 받아내기 좋다.',
|
||||
terrainRatings: {
|
||||
plain: 100,
|
||||
road: 100,
|
||||
forest: 110,
|
||||
hill: 105,
|
||||
village: 115,
|
||||
fort: 120,
|
||||
camp: 115
|
||||
},
|
||||
movementCosts: {
|
||||
forest: 1,
|
||||
village: 1,
|
||||
fort: 1,
|
||||
camp: 1
|
||||
}
|
||||
},
|
||||
cavalry: {
|
||||
key: 'cavalry',
|
||||
name: '경기병',
|
||||
family: '기병계',
|
||||
role: '기동 / 돌파',
|
||||
description: '길과 평지에서 빠르게 압박하지만 숲과 언덕에서는 힘이 떨어진다.',
|
||||
terrainRatings: {
|
||||
plain: 110,
|
||||
road: 120,
|
||||
forest: 75,
|
||||
hill: 80,
|
||||
village: 95,
|
||||
fort: 90,
|
||||
camp: 100
|
||||
},
|
||||
movementCosts: {
|
||||
road: 1,
|
||||
forest: 3,
|
||||
hill: 3
|
||||
}
|
||||
},
|
||||
spearman: {
|
||||
key: 'spearman',
|
||||
name: '창병',
|
||||
family: '창병계',
|
||||
role: '대기병 / 압박',
|
||||
description: '기병 견제와 전선 고정에 특화된 근접 병과.',
|
||||
terrainRatings: {
|
||||
plain: 105,
|
||||
road: 100,
|
||||
forest: 95,
|
||||
hill: 105,
|
||||
village: 105,
|
||||
fort: 110,
|
||||
camp: 110
|
||||
}
|
||||
},
|
||||
archer: {
|
||||
key: 'archer',
|
||||
name: '궁병',
|
||||
family: '궁병계',
|
||||
role: '원거리 / 견제',
|
||||
description: '언덕과 요새에서 유리하고 근접 압박에는 약한 원거리 병과.',
|
||||
terrainRatings: {
|
||||
plain: 95,
|
||||
road: 95,
|
||||
forest: 90,
|
||||
hill: 115,
|
||||
village: 100,
|
||||
fort: 120,
|
||||
camp: 105
|
||||
},
|
||||
movementCosts: {
|
||||
hill: 1
|
||||
}
|
||||
},
|
||||
bandit: {
|
||||
key: 'bandit',
|
||||
name: '도적',
|
||||
family: '적병계',
|
||||
role: '습격 / 산악',
|
||||
description: '숲과 언덕에서 강하고 정규전 방어 지형에서는 약하다.',
|
||||
terrainRatings: {
|
||||
plain: 95,
|
||||
road: 95,
|
||||
forest: 115,
|
||||
hill: 115,
|
||||
village: 100,
|
||||
fort: 90,
|
||||
camp: 95
|
||||
},
|
||||
movementCosts: {
|
||||
forest: 1,
|
||||
hill: 1
|
||||
}
|
||||
},
|
||||
yellowTurban: {
|
||||
key: 'yellowTurban',
|
||||
name: '황건병',
|
||||
family: '황건적계',
|
||||
role: '민병 / 물량',
|
||||
description: '능력은 낮지만 험지에서 성가시게 버티는 반란군 병과.',
|
||||
terrainRatings: {
|
||||
plain: 95,
|
||||
road: 95,
|
||||
forest: 105,
|
||||
hill: 105,
|
||||
village: 100,
|
||||
fort: 90,
|
||||
camp: 95
|
||||
},
|
||||
movementCosts: {
|
||||
forest: 1
|
||||
}
|
||||
},
|
||||
rebelLeader: {
|
||||
key: 'rebelLeader',
|
||||
name: '두령',
|
||||
family: '두령계',
|
||||
role: '지휘 / 근접',
|
||||
description: '황건적을 이끄는 지휘관형 적병. 진영과 험지에서 버티는 힘이 있다.',
|
||||
terrainRatings: {
|
||||
plain: 100,
|
||||
road: 100,
|
||||
forest: 110,
|
||||
hill: 110,
|
||||
village: 105,
|
||||
fort: 105,
|
||||
camp: 110
|
||||
},
|
||||
movementCosts: {
|
||||
forest: 1,
|
||||
hill: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function getUnitClass(classKey: UnitClassKey) {
|
||||
return unitClasses[classKey];
|
||||
}
|
||||
|
||||
export function getTerrainRule(terrain: TerrainType) {
|
||||
return terrainRules[terrain];
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { TerrainType, UnitClassKey } from './battleRules';
|
||||
|
||||
export type PortraitKey = 'liuBei' | 'guanYu' | 'zhangFei';
|
||||
|
||||
export type StoryPage = {
|
||||
@@ -15,6 +17,7 @@ export type UnitData = {
|
||||
name: string;
|
||||
faction: 'ally' | 'enemy';
|
||||
className: string;
|
||||
classKey: UnitClassKey;
|
||||
hp: number;
|
||||
maxHp: number;
|
||||
attack: number;
|
||||
@@ -41,8 +44,6 @@ export type BattleBond = {
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type TerrainType = 'plain' | 'forest' | 'hill' | 'fort';
|
||||
|
||||
export type BattleMap = {
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -140,18 +141,18 @@ export const firstBattleMap: BattleMap = {
|
||||
width: 12,
|
||||
height: 12,
|
||||
terrain: [
|
||||
['plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
|
||||
['plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain'],
|
||||
['plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'fort', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain'],
|
||||
['forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
|
||||
['forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain']
|
||||
['plain', 'plain', 'forest', 'forest', 'plain', 'road', 'road', 'hill', 'hill', 'plain', 'plain', 'plain'],
|
||||
['plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'hill', 'plain', 'plain', 'plain'],
|
||||
['plain', 'forest', 'plain', 'plain', 'plain', 'road', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'hill', 'hill', 'road', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'hill', 'plain', 'road', 'road', 'forest', 'plain', 'plain', 'fort', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'village', 'road', 'plain', 'plain', 'plain', 'forest', 'forest'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain'],
|
||||
['forest', 'forest', 'plain', 'plain', 'plain', 'road', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
|
||||
['forest', 'plain', 'plain', 'plain', 'plain', 'road', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'camp', 'plain', 'forest', 'forest', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'camp', 'camp', 'plain', 'forest', 'plain', 'road', 'plain', 'plain', 'forest', 'forest', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain']
|
||||
]
|
||||
};
|
||||
|
||||
@@ -160,7 +161,8 @@ export const firstBattleUnits: UnitData[] = [
|
||||
id: 'liu-bei',
|
||||
name: '유비',
|
||||
faction: 'ally',
|
||||
className: '의병장',
|
||||
className: '군웅',
|
||||
classKey: 'lord',
|
||||
hp: 32,
|
||||
maxHp: 32,
|
||||
attack: 9,
|
||||
@@ -173,7 +175,8 @@ export const firstBattleUnits: UnitData[] = [
|
||||
id: 'guan-yu',
|
||||
name: '관우',
|
||||
faction: 'ally',
|
||||
className: '보병',
|
||||
className: '의용 보병',
|
||||
classKey: 'infantry',
|
||||
hp: 30,
|
||||
maxHp: 30,
|
||||
attack: 10,
|
||||
@@ -187,6 +190,7 @@ export const firstBattleUnits: UnitData[] = [
|
||||
name: '장비',
|
||||
faction: 'ally',
|
||||
className: '창병',
|
||||
classKey: 'spearman',
|
||||
hp: 30,
|
||||
maxHp: 30,
|
||||
attack: 10,
|
||||
@@ -199,7 +203,8 @@ export const firstBattleUnits: UnitData[] = [
|
||||
id: 'rebel-a',
|
||||
name: '황건병',
|
||||
faction: 'enemy',
|
||||
className: '도적',
|
||||
className: '황건병',
|
||||
classKey: 'yellowTurban',
|
||||
hp: 20,
|
||||
maxHp: 20,
|
||||
attack: 6,
|
||||
@@ -212,7 +217,8 @@ export const firstBattleUnits: UnitData[] = [
|
||||
id: 'rebel-b',
|
||||
name: '황건병',
|
||||
faction: 'enemy',
|
||||
className: '도적',
|
||||
className: '황건병',
|
||||
classKey: 'yellowTurban',
|
||||
hp: 20,
|
||||
maxHp: 20,
|
||||
attack: 6,
|
||||
@@ -226,6 +232,7 @@ export const firstBattleUnits: UnitData[] = [
|
||||
name: '황건궁병',
|
||||
faction: 'enemy',
|
||||
className: '궁병',
|
||||
classKey: 'archer',
|
||||
hp: 18,
|
||||
maxHp: 18,
|
||||
attack: 7,
|
||||
@@ -239,6 +246,7 @@ export const firstBattleUnits: UnitData[] = [
|
||||
name: '두령 한석',
|
||||
faction: 'enemy',
|
||||
className: '두령',
|
||||
classKey: 'rebelLeader',
|
||||
hp: 30,
|
||||
maxHp: 30,
|
||||
attack: 9,
|
||||
|
||||
@@ -5,19 +5,12 @@ import {
|
||||
firstBattleMap,
|
||||
firstBattleUnits,
|
||||
type BattleBond,
|
||||
type TerrainType,
|
||||
type UnitData,
|
||||
type UnitStats
|
||||
} from '../data/scenario';
|
||||
import { getTerrainRule, getUnitClass, type TerrainType } from '../data/battleRules';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
const terrainColor: Record<TerrainType, number> = {
|
||||
plain: 0x6d8056,
|
||||
forest: 0x355f42,
|
||||
hill: 0x82724e,
|
||||
fort: 0x8b6f48
|
||||
};
|
||||
|
||||
const unitTexture: Record<string, string> = {
|
||||
'liu-bei': 'unit-liu-bei',
|
||||
'guan-yu': 'unit-guan-yu',
|
||||
@@ -208,13 +201,14 @@ export class BattleScene extends Phaser.Scene {
|
||||
|
||||
firstBattleMap.terrain.forEach((row, y) => {
|
||||
row.forEach((terrain, x) => {
|
||||
const terrainRule = getTerrainRule(terrain);
|
||||
const tile = this.add.rectangle(
|
||||
layout.gridX + x * layout.tileSize,
|
||||
layout.gridY + y * layout.tileSize,
|
||||
layout.tileSize,
|
||||
layout.tileSize,
|
||||
terrainColor[terrain],
|
||||
this.terrainAlpha(terrain)
|
||||
terrainRule.color,
|
||||
terrainRule.alpha
|
||||
);
|
||||
tile.setOrigin(0);
|
||||
tile.setStrokeStyle(1, 0x0a1014, 0.5);
|
||||
@@ -328,34 +322,28 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private showMoveRange(unit: UnitData) {
|
||||
for (let y = 0; y < firstBattleMap.height; y += 1) {
|
||||
for (let x = 0; x < firstBattleMap.width; x += 1) {
|
||||
if (!this.canMoveTo(unit, x, y)) {
|
||||
continue;
|
||||
this.reachableTiles(unit).forEach(({ x, y }) => {
|
||||
const marker = this.add.rectangle(
|
||||
this.layout.gridX + x * this.layout.tileSize,
|
||||
this.layout.gridY + y * this.layout.tileSize,
|
||||
this.layout.tileSize,
|
||||
this.layout.tileSize,
|
||||
palette.blue,
|
||||
0.24
|
||||
);
|
||||
marker.setOrigin(0);
|
||||
marker.setStrokeStyle(1, palette.blue, 0.68);
|
||||
marker.setDepth(4);
|
||||
marker.setInteractive({ useHandCursor: true });
|
||||
marker.on('pointerover', () => marker.setFillStyle(palette.blue, 0.36));
|
||||
marker.on('pointerout', () => marker.setFillStyle(palette.blue, 0.24));
|
||||
marker.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
|
||||
if (pointer.leftButtonDown()) {
|
||||
this.moveSelectedUnit(x, y, pointer);
|
||||
}
|
||||
|
||||
const marker = this.add.rectangle(
|
||||
this.layout.gridX + x * this.layout.tileSize,
|
||||
this.layout.gridY + y * this.layout.tileSize,
|
||||
this.layout.tileSize,
|
||||
this.layout.tileSize,
|
||||
palette.blue,
|
||||
0.24
|
||||
);
|
||||
marker.setOrigin(0);
|
||||
marker.setStrokeStyle(1, palette.blue, 0.68);
|
||||
marker.setDepth(4);
|
||||
marker.setInteractive({ useHandCursor: true });
|
||||
marker.on('pointerover', () => marker.setFillStyle(palette.blue, 0.36));
|
||||
marker.on('pointerout', () => marker.setFillStyle(palette.blue, 0.24));
|
||||
marker.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
|
||||
if (pointer.leftButtonDown()) {
|
||||
this.moveSelectedUnit(x, y, pointer);
|
||||
}
|
||||
});
|
||||
this.markers.push(marker);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.markers.push(marker);
|
||||
});
|
||||
}
|
||||
|
||||
private moveSelectedUnit(x: number, y: number, pointer?: Phaser.Input.Pointer) {
|
||||
@@ -386,35 +374,69 @@ export class BattleScene extends Phaser.Scene {
|
||||
}
|
||||
|
||||
private canMoveTo(unit: UnitData, x: number, y: number) {
|
||||
const distance = Math.abs(unit.x - x) + Math.abs(unit.y - y);
|
||||
return (
|
||||
distance > 0 &&
|
||||
distance <= unit.move &&
|
||||
x >= 0 &&
|
||||
y >= 0 &&
|
||||
x < firstBattleMap.width &&
|
||||
y < firstBattleMap.height &&
|
||||
!this.isOccupied(x, y, unit.id)
|
||||
);
|
||||
return this.reachableTiles(unit).some((tile) => tile.x === x && tile.y === y);
|
||||
}
|
||||
|
||||
private reachableTiles(unit: UnitData) {
|
||||
const directions = [
|
||||
{ x: 1, y: 0 },
|
||||
{ x: -1, y: 0 },
|
||||
{ x: 0, y: 1 },
|
||||
{ x: 0, y: -1 }
|
||||
];
|
||||
const costs = new Map<string, number>([[this.tileKey(unit.x, unit.y), 0]]);
|
||||
const queue = [{ x: unit.x, y: unit.y, cost: 0 }];
|
||||
const reachable = new Map<string, { x: number; y: number; cost: number }>();
|
||||
|
||||
while (queue.length > 0) {
|
||||
queue.sort((a, b) => a.cost - b.cost);
|
||||
const current = queue.shift();
|
||||
if (!current) {
|
||||
break;
|
||||
}
|
||||
|
||||
directions.forEach((direction) => {
|
||||
const nextX = current.x + direction.x;
|
||||
const nextY = current.y + direction.y;
|
||||
if (!this.isInBounds(nextX, nextY) || this.isOccupied(nextX, nextY, unit.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const terrain = firstBattleMap.terrain[nextY][nextX];
|
||||
const stepCost = this.movementCost(unit, terrain);
|
||||
const nextCost = current.cost + stepCost;
|
||||
const key = this.tileKey(nextX, nextY);
|
||||
if (nextCost > unit.move || nextCost >= (costs.get(key) ?? Number.POSITIVE_INFINITY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
costs.set(key, nextCost);
|
||||
const tile = { x: nextX, y: nextY, cost: nextCost };
|
||||
reachable.set(key, tile);
|
||||
queue.push(tile);
|
||||
});
|
||||
}
|
||||
|
||||
return Array.from(reachable.values());
|
||||
}
|
||||
|
||||
private movementCost(unit: UnitData, terrain: TerrainType) {
|
||||
const unitClass = getUnitClass(unit.classKey);
|
||||
return unitClass.movementCosts?.[terrain] ?? getTerrainRule(terrain).moveCost;
|
||||
}
|
||||
|
||||
private isInBounds(x: number, y: number) {
|
||||
return x >= 0 && y >= 0 && x < firstBattleMap.width && y < firstBattleMap.height;
|
||||
}
|
||||
|
||||
private tileKey(x: number, y: number) {
|
||||
return `${x},${y}`;
|
||||
}
|
||||
|
||||
private isOccupied(x: number, y: number, exceptUnitId?: string) {
|
||||
return firstBattleUnits.some((unit) => unit.id !== exceptUnitId && unit.x === x && unit.y === y);
|
||||
}
|
||||
|
||||
private terrainAlpha(terrain: TerrainType) {
|
||||
if (terrain === 'forest') {
|
||||
return 0.16;
|
||||
}
|
||||
if (terrain === 'hill') {
|
||||
return 0.13;
|
||||
}
|
||||
if (terrain === 'fort') {
|
||||
return 0.18;
|
||||
}
|
||||
return 0.03;
|
||||
}
|
||||
|
||||
private tileCenterX(x: number) {
|
||||
return this.layout.gridX + x * this.layout.tileSize + this.layout.tileSize / 2;
|
||||
}
|
||||
@@ -1028,6 +1050,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
private renderRosterRow(unit: UnitData, x: number, y: number, width: number) {
|
||||
const acted = this.actedUnitIds.has(unit.id);
|
||||
const active = this.selectedUnit?.id === unit.id;
|
||||
const unitClass = getUnitClass(unit.classKey);
|
||||
const rowBg = this.trackSideObject(this.add.rectangle(x, y, width, 42, active ? 0x2f4050 : 0x101820, acted ? 0.54 : 0.9));
|
||||
rowBg.setOrigin(0);
|
||||
rowBg.setStrokeStyle(1, active ? palette.gold : unit.faction === 'ally' ? palette.blue : 0xb86b55, active ? 0.88 : 0.52);
|
||||
@@ -1041,7 +1064,8 @@ export class BattleScene extends Phaser.Scene {
|
||||
color: nameColor,
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(x + 88, y + 9, unit.className, {
|
||||
this.trackSideObject(this.add.text(x + 88, y + 9, unitClass.name,
|
||||
{
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: acted ? '#8c8c8c' : '#9fb0bf'
|
||||
@@ -1076,18 +1100,22 @@ export class BattleScene extends Phaser.Scene {
|
||||
const top = panelY + 124;
|
||||
const acted = this.actedUnitIds.has(unit.id);
|
||||
const factionColor = unit.faction === 'ally' ? palette.blue : 0xb86b55;
|
||||
const unitClass = getUnitClass(unit.classKey);
|
||||
const terrain = firstBattleMap.terrain[unit.y][unit.x];
|
||||
const terrainRule = getTerrainRule(terrain);
|
||||
const terrainRating = unitClass.terrainRatings[terrain];
|
||||
|
||||
const header = this.trackSideObject(this.add.rectangle(left, top, width, 60, 0x101820, 0.94));
|
||||
header.setOrigin(0);
|
||||
header.setStrokeStyle(1, factionColor, 0.76);
|
||||
|
||||
this.trackSideObject(this.add.text(left + 14, top + 9, rosterLabels[unit.faction], {
|
||||
this.trackSideObject(this.add.text(left + 14, top + 9, `${rosterLabels[unit.faction]} / ${unitClass.family}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '14px',
|
||||
color: unit.faction === 'ally' ? '#9fd0ff' : '#ffb2a4',
|
||||
fontStyle: '700'
|
||||
}));
|
||||
this.trackSideObject(this.add.text(left + 14, top + 29, `${unit.name} ${unit.className}`, {
|
||||
this.trackSideObject(this.add.text(left + 14, top + 29, `${unit.name} ${unitClass.name}`, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '22px',
|
||||
color: acted ? '#bdbdbd' : '#f2e3bf',
|
||||
@@ -1133,7 +1161,7 @@ export class BattleScene extends Phaser.Scene {
|
||||
this.renderStatRow(stat.label, unit.stats[stat.key], left, statTop + index * 32, width);
|
||||
});
|
||||
|
||||
const positionText = `위치 ${unit.x + 1}, ${unit.y + 1}${acted ? ' / 행동완료' : ''}`;
|
||||
const positionText = `위치 ${unit.x + 1}, ${unit.y + 1} / ${terrainRule.label} ${terrainRating}%${acted ? ' / 행동완료' : ''}`;
|
||||
this.trackSideObject(this.add.text(left, top + 328, positionText, {
|
||||
fontFamily: '"Malgun Gothic", "Noto Sans KR", sans-serif',
|
||||
fontSize: '15px',
|
||||
|
||||
Reference in New Issue
Block a user