Add combat action sprite sheets
197
scripts/generate-action-sprites.py
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PIL import Image, ImageChops, ImageDraw, ImageEnhance, ImageFilter
|
||||||
|
|
||||||
|
|
||||||
|
FRAME_SIZE = 313
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
UNIT_DIR = ROOT / "src" / "assets" / "images" / "units"
|
||||||
|
|
||||||
|
DIRECTIONS = ("south", "east", "north", "west")
|
||||||
|
FRAME_BY_ACTION = {
|
||||||
|
"attack": 2,
|
||||||
|
"strategy": 0,
|
||||||
|
"item": 1,
|
||||||
|
"hurt": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
FORWARD = {
|
||||||
|
"south": (0, 15),
|
||||||
|
"east": (18, 0),
|
||||||
|
"north": (0, -10),
|
||||||
|
"west": (-18, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
BACKWARD = {
|
||||||
|
"south": (0, -12),
|
||||||
|
"east": (-14, 0),
|
||||||
|
"north": (0, 12),
|
||||||
|
"west": (14, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
ITEM_POS = {
|
||||||
|
"south": (184, 142),
|
||||||
|
"east": (222, 143),
|
||||||
|
"north": (134, 136),
|
||||||
|
"west": (88, 143),
|
||||||
|
}
|
||||||
|
|
||||||
|
ACTION_ORDER = ("attack", "strategy", "item", "hurt")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
for source in sorted(UNIT_DIR.glob("unit-*.png")):
|
||||||
|
if source.name.endswith("-actions.png"):
|
||||||
|
continue
|
||||||
|
sheet = Image.open(source).convert("RGBA")
|
||||||
|
output = Image.new("RGBA", (FRAME_SIZE * len(ACTION_ORDER), FRAME_SIZE * len(DIRECTIONS)), (0, 0, 0, 0))
|
||||||
|
|
||||||
|
for row, direction in enumerate(DIRECTIONS):
|
||||||
|
for col, action in enumerate(ACTION_ORDER):
|
||||||
|
frame = crop_frame(sheet, row, FRAME_BY_ACTION[action])
|
||||||
|
if action == "attack":
|
||||||
|
action_frame = make_attack(frame, direction)
|
||||||
|
elif action == "strategy":
|
||||||
|
action_frame = make_strategy(frame, direction)
|
||||||
|
elif action == "item":
|
||||||
|
action_frame = make_item(frame, direction)
|
||||||
|
else:
|
||||||
|
action_frame = make_hurt(frame, direction)
|
||||||
|
output.alpha_composite(action_frame, (col * FRAME_SIZE, row * FRAME_SIZE))
|
||||||
|
|
||||||
|
output.save(source.with_name(f"{source.stem}-actions.png"))
|
||||||
|
|
||||||
|
|
||||||
|
def crop_frame(sheet: Image.Image, row: int, col: int) -> Image.Image:
|
||||||
|
return sheet.crop((col * FRAME_SIZE, row * FRAME_SIZE, (col + 1) * FRAME_SIZE, (row + 1) * FRAME_SIZE)).copy()
|
||||||
|
|
||||||
|
|
||||||
|
def offset_subject(frame: Image.Image, dx: int, dy: int) -> Image.Image:
|
||||||
|
canvas = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
bbox = frame.getbbox()
|
||||||
|
if not bbox:
|
||||||
|
return frame
|
||||||
|
subject = frame.crop(bbox)
|
||||||
|
canvas.alpha_composite(subject, (bbox[0] + dx, bbox[1] + dy))
|
||||||
|
return canvas
|
||||||
|
|
||||||
|
|
||||||
|
def underlay(frame: Image.Image, color: tuple[int, int, int, int], blur: int, expand: int = 0) -> Image.Image:
|
||||||
|
alpha = frame.getchannel("A")
|
||||||
|
if expand > 0:
|
||||||
|
alpha = alpha.filter(ImageFilter.MaxFilter(expand * 2 + 1))
|
||||||
|
alpha = alpha.filter(ImageFilter.GaussianBlur(blur))
|
||||||
|
glow = Image.new("RGBA", frame.size, color)
|
||||||
|
glow.putalpha(alpha.point(lambda value: min(255, int(value * color[3] / 255))))
|
||||||
|
result = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
result.alpha_composite(glow)
|
||||||
|
result.alpha_composite(frame)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_attack(frame: Image.Image, direction: str) -> Image.Image:
|
||||||
|
dx, dy = FORWARD[direction]
|
||||||
|
body = offset_subject(frame, dx, dy)
|
||||||
|
body = underlay(body, (255, 224, 120, 92), 4, 1)
|
||||||
|
slash = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(slash)
|
||||||
|
|
||||||
|
if direction == "east":
|
||||||
|
points = [(166, 76), (264, 126), (234, 220)]
|
||||||
|
elif direction == "west":
|
||||||
|
points = [(147, 76), (49, 126), (79, 220)]
|
||||||
|
elif direction == "south":
|
||||||
|
points = [(74, 104), (166, 184), (248, 112)]
|
||||||
|
else:
|
||||||
|
points = [(74, 202), (166, 112), (248, 198)]
|
||||||
|
|
||||||
|
draw.line(points, fill=(255, 247, 214, 218), width=15, joint="curve")
|
||||||
|
draw.line(points, fill=(98, 210, 255, 178), width=6, joint="curve")
|
||||||
|
draw.line(points, fill=(255, 178, 86, 158), width=2, joint="curve")
|
||||||
|
slash = slash.filter(ImageFilter.GaussianBlur(0.45))
|
||||||
|
|
||||||
|
result = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
result.alpha_composite(slash)
|
||||||
|
result.alpha_composite(body)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_strategy(frame: Image.Image, direction: str) -> Image.Image:
|
||||||
|
aura = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(aura)
|
||||||
|
center = (156, 160)
|
||||||
|
|
||||||
|
for radius, alpha in ((92, 42), (66, 66), (38, 92)):
|
||||||
|
draw.ellipse(
|
||||||
|
(center[0] - radius, center[1] - radius, center[0] + radius, center[1] + radius),
|
||||||
|
outline=(98, 195, 255, alpha),
|
||||||
|
width=4,
|
||||||
|
)
|
||||||
|
|
||||||
|
for index in range(10):
|
||||||
|
x = 72 + (index * 23) % 168
|
||||||
|
y = 58 + (index * 47) % 188
|
||||||
|
draw.polygon(
|
||||||
|
[(x, y - 7), (x + 4, y), (x, y + 7), (x - 4, y)],
|
||||||
|
fill=(255, 230, 132, 140),
|
||||||
|
)
|
||||||
|
|
||||||
|
aura = aura.filter(ImageFilter.GaussianBlur(0.35))
|
||||||
|
body = underlay(frame, (60, 160, 255, 92), 6, 2)
|
||||||
|
result = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
result.alpha_composite(aura)
|
||||||
|
result.alpha_composite(body)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_item(frame: Image.Image, direction: str) -> Image.Image:
|
||||||
|
body = underlay(frame, (95, 230, 150, 70), 4, 1)
|
||||||
|
effect = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(effect)
|
||||||
|
x, y = ITEM_POS[direction]
|
||||||
|
|
||||||
|
draw.rounded_rectangle((x - 18, y - 15, x + 18, y + 17), radius=7, fill=(137, 91, 45, 235), outline=(245, 207, 122, 230), width=3)
|
||||||
|
draw.ellipse((x - 10, y - 28, x + 10, y - 9), fill=(95, 221, 142, 210), outline=(224, 255, 219, 230), width=2)
|
||||||
|
draw.line((x - 26, y + 2, x + 26, y + 2), fill=(224, 255, 219, 184), width=4)
|
||||||
|
draw.line((x, y - 24, x, y + 27), fill=(224, 255, 219, 184), width=4)
|
||||||
|
|
||||||
|
for px, py in ((x - 38, y - 30), (x + 36, y - 26), (x - 30, y + 36), (x + 28, y + 34)):
|
||||||
|
draw.ellipse((px - 4, py - 4, px + 4, py + 4), fill=(159, 255, 200, 160))
|
||||||
|
|
||||||
|
result = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
result.alpha_composite(body)
|
||||||
|
result.alpha_composite(effect)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_hurt(frame: Image.Image, direction: str) -> Image.Image:
|
||||||
|
dx, dy = BACKWARD[direction]
|
||||||
|
body = offset_subject(frame, dx, dy)
|
||||||
|
body = ImageEnhance.Color(body).enhance(0.62)
|
||||||
|
|
||||||
|
alpha = body.getchannel("A")
|
||||||
|
red = Image.new("RGBA", body.size, (255, 78, 60, 70))
|
||||||
|
red.putalpha(alpha.point(lambda value: min(88, value // 3)))
|
||||||
|
body = Image.alpha_composite(body, red)
|
||||||
|
|
||||||
|
impact = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(impact)
|
||||||
|
if direction in ("west", "north"):
|
||||||
|
cx, cy = 190, 118
|
||||||
|
else:
|
||||||
|
cx, cy = 124, 118
|
||||||
|
spikes = [(cx, cy - 28), (cx + 9, cy - 8), (cx + 31, cy - 6), (cx + 12, cy + 7), (cx + 20, cy + 28), (cx, cy + 13), (cx - 22, cy + 27), (cx - 12, cy + 6), (cx - 31, cy - 7), (cx - 8, cy - 9)]
|
||||||
|
draw.polygon(spikes, fill=(255, 222, 118, 194), outline=(255, 84, 57, 224))
|
||||||
|
draw.line((cx - 38, cy - 35, cx + 38, cy + 35), fill=(255, 245, 205, 186), width=5)
|
||||||
|
draw.line((cx + 34, cy - 32, cx - 32, cy + 34), fill=(255, 245, 205, 154), width=3)
|
||||||
|
|
||||||
|
result = Image.new("RGBA", frame.size, (0, 0, 0, 0))
|
||||||
|
result.alpha_composite(body)
|
||||||
|
result.alpha_composite(impact.filter(ImageFilter.GaussianBlur(0.2)))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -74,6 +74,22 @@ try {
|
|||||||
throw new Error(`Debug battle state was not available: ${JSON.stringify(result.battleState)}`);
|
throw new Error(`Debug battle state was not available: ${JSON.stringify(result.battleState)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actionTexturesLoaded = await page.evaluate(() => {
|
||||||
|
const textures = window.__HEROS_GAME__?.textures;
|
||||||
|
return [
|
||||||
|
'unit-liu-bei-actions',
|
||||||
|
'unit-guan-yu-actions',
|
||||||
|
'unit-zhang-fei-actions',
|
||||||
|
'unit-rebel-actions',
|
||||||
|
'unit-rebel-archer-actions',
|
||||||
|
'unit-rebel-cavalry-actions',
|
||||||
|
'unit-rebel-leader-actions'
|
||||||
|
].every((key) => textures?.exists(key));
|
||||||
|
});
|
||||||
|
if (!actionTexturesLoaded) {
|
||||||
|
throw new Error('Expected all unit action textures to be loaded.');
|
||||||
|
}
|
||||||
|
|
||||||
const cameraBeforeScroll = result.battleState.camera;
|
const cameraBeforeScroll = result.battleState.camera;
|
||||||
if (
|
if (
|
||||||
!cameraBeforeScroll ||
|
!cameraBeforeScroll ||
|
||||||
|
|||||||
BIN
src/assets/images/units/unit-guan-yu-actions.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
src/assets/images/units/unit-liu-bei-actions.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/images/units/unit-rebel-actions.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/images/units/unit-rebel-archer-actions.png
Normal file
|
After Width: | Height: | Size: 913 KiB |
BIN
src/assets/images/units/unit-rebel-cavalry-actions.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
src/assets/images/units/unit-rebel-leader-actions.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
src/assets/images/units/unit-zhang-fei-actions.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
@@ -42,6 +42,13 @@ const unitFrameRows: Record<UnitDirection, number> = {
|
|||||||
west: 3
|
west: 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unitActionColumns: Record<UnitActionPose, number> = {
|
||||||
|
attack: 0,
|
||||||
|
strategy: 1,
|
||||||
|
item: 2,
|
||||||
|
hurt: 3
|
||||||
|
};
|
||||||
|
|
||||||
type BattleLayout = {
|
type BattleLayout = {
|
||||||
mapX: number;
|
mapX: number;
|
||||||
mapY: number;
|
mapY: number;
|
||||||
@@ -83,6 +90,7 @@ type UnitView = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type UnitDirection = 'south' | 'east' | 'north' | 'west';
|
type UnitDirection = 'south' | 'east' | 'north' | 'west';
|
||||||
|
type UnitActionPose = 'attack' | 'strategy' | 'item' | 'hurt';
|
||||||
|
|
||||||
type BattlePhase = 'idle' | 'moving' | 'command' | 'targeting' | 'animating' | 'resolved';
|
type BattlePhase = 'idle' | 'moving' | 'command' | 'targeting' | 'animating' | 'resolved';
|
||||||
type BattleCommand = 'attack' | 'strategy' | 'item' | 'wait';
|
type BattleCommand = 'attack' | 'strategy' | 'item' | 'wait';
|
||||||
@@ -2805,7 +2813,12 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
const attackerDirection = 'east';
|
const attackerDirection = 'east';
|
||||||
const defenderDirection = 'west';
|
const defenderDirection = 'west';
|
||||||
const attackerSprite = this.trackCombatObject(
|
const attackerSprite = this.trackCombatObject(
|
||||||
this.add.sprite(attackerX, groundY, unitTexture[result.attacker.id] ?? 'unit-rebel', this.unitFrameIndex(attackerDirection))
|
this.add.sprite(
|
||||||
|
attackerX,
|
||||||
|
groundY,
|
||||||
|
this.unitActionTexture(result.attacker),
|
||||||
|
this.unitActionFrameIndex(attackerDirection, this.actionPoseForCommand(result.action))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
attackerSprite.setDepth(depth + 6);
|
attackerSprite.setDepth(depth + 6);
|
||||||
attackerSprite.setDisplaySize(126, 126);
|
attackerSprite.setDisplaySize(126, 126);
|
||||||
@@ -2824,7 +2837,7 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
attackerStatus.expText.setText(`${result.characterGrowth.previousExp} / ${result.characterGrowth.next}`);
|
attackerStatus.expText.setText(`${result.characterGrowth.previousExp} / ${result.characterGrowth.next}`);
|
||||||
|
|
||||||
await this.delay(180);
|
await this.delay(180);
|
||||||
await this.playCombatActionMotion(result, attackerSprite, defenderSprite, attackerX, defenderX, groundY, depth + 10);
|
await this.playCombatActionMotion(result, attackerSprite, defenderSprite, attackerX, defenderX, groundY, depth + 10, defenderDirection);
|
||||||
if (result.hit) {
|
if (result.hit) {
|
||||||
this.showCombatImpact(result, defenderX, groundY - 46, depth + 14);
|
this.showCombatImpact(result, defenderX, groundY - 46, depth + 14);
|
||||||
} else {
|
} else {
|
||||||
@@ -2902,7 +2915,10 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
title.setOrigin(0.5, 0);
|
title.setOrigin(0.5, 0);
|
||||||
title.setDepth(depth + 2);
|
title.setDepth(depth + 2);
|
||||||
|
|
||||||
const userSprite = this.trackCombatObject(this.add.sprite(left + 160, top + 146, unitTexture[result.user.id] ?? 'unit-rebel', this.unitFrameIndex('east')));
|
const supportPose: UnitActionPose = result.usable.command === 'strategy' ? 'strategy' : 'item';
|
||||||
|
const userSprite = this.trackCombatObject(
|
||||||
|
this.add.sprite(left + 160, top + 146, this.unitActionTexture(result.user), this.unitActionFrameIndex('east', supportPose))
|
||||||
|
);
|
||||||
userSprite.setDepth(depth + 3);
|
userSprite.setDepth(depth + 3);
|
||||||
userSprite.setDisplaySize(112, 112);
|
userSprite.setDisplaySize(112, 112);
|
||||||
|
|
||||||
@@ -3053,7 +3069,8 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
attackerX: number,
|
attackerX: number,
|
||||||
defenderX: number,
|
defenderX: number,
|
||||||
groundY: number,
|
groundY: number,
|
||||||
depth: number
|
depth: number,
|
||||||
|
defenderDirection: UnitDirection
|
||||||
) {
|
) {
|
||||||
const direction = result.attacker.faction === 'ally' ? 1 : -1;
|
const direction = result.attacker.faction === 'ally' ? 1 : -1;
|
||||||
if (result.action === 'attack') {
|
if (result.action === 'attack') {
|
||||||
@@ -3066,6 +3083,9 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
ease: 'Sine.easeInOut'
|
ease: 'Sine.easeInOut'
|
||||||
});
|
});
|
||||||
await this.delay(180);
|
await this.delay(180);
|
||||||
|
if (result.hit) {
|
||||||
|
defenderSprite.setTexture(this.unitActionTexture(result.defender), this.unitActionFrameIndex(defenderDirection, 'hurt'));
|
||||||
|
}
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
targets: defenderSprite,
|
targets: defenderSprite,
|
||||||
x: defenderX + direction * (result.hit ? 12 : 28),
|
x: defenderX + direction * (result.hit ? 12 : 28),
|
||||||
@@ -3096,6 +3116,9 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
ease: 'Cubic.easeIn'
|
ease: 'Cubic.easeIn'
|
||||||
});
|
});
|
||||||
await this.delay(450);
|
await this.delay(450);
|
||||||
|
if (result.hit) {
|
||||||
|
defenderSprite.setTexture(this.unitActionTexture(result.defender), this.unitActionFrameIndex(defenderDirection, 'hurt'));
|
||||||
|
}
|
||||||
projectile.destroy();
|
projectile.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3746,6 +3769,26 @@ export class BattleScene extends Phaser.Scene {
|
|||||||
return unitFrameRows[direction] * 4 + frame;
|
return unitFrameRows[direction] * 4 + frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unitActionTexture(unit: UnitData) {
|
||||||
|
const base = unitTexture[unit.id] ?? 'unit-rebel';
|
||||||
|
const actionKey = `${base}-actions`;
|
||||||
|
return this.textures.exists(actionKey) ? actionKey : base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unitActionFrameIndex(direction: UnitDirection, pose: UnitActionPose) {
|
||||||
|
return unitFrameRows[direction] * 4 + unitActionColumns[pose];
|
||||||
|
}
|
||||||
|
|
||||||
|
private actionPoseForCommand(action: DamageCommand): UnitActionPose {
|
||||||
|
if (action === 'strategy') {
|
||||||
|
return 'strategy';
|
||||||
|
}
|
||||||
|
if (action === 'item') {
|
||||||
|
return 'item';
|
||||||
|
}
|
||||||
|
return 'attack';
|
||||||
|
}
|
||||||
|
|
||||||
private applyActedStyle(unit: UnitData) {
|
private applyActedStyle(unit: UnitData) {
|
||||||
const view = this.unitViews.get(unit.id);
|
const view = this.unitViews.get(unit.id);
|
||||||
if (!view) {
|
if (!view) {
|
||||||
|
|||||||
@@ -9,12 +9,19 @@ import storyThreeHeroesUrl from '../../assets/images/story/03-three-heroes-meet.
|
|||||||
import storyMilitiaUrl from '../../assets/images/story/04-volunteer-militia.png';
|
import storyMilitiaUrl from '../../assets/images/story/04-volunteer-militia.png';
|
||||||
import storySortieUrl from '../../assets/images/story/05-first-sortie.png';
|
import storySortieUrl from '../../assets/images/story/05-first-sortie.png';
|
||||||
import titleBackgroundUrl from '../../assets/images/taoyuan-oath-title.png';
|
import titleBackgroundUrl from '../../assets/images/taoyuan-oath-title.png';
|
||||||
|
import guanYuActionSheetUrl from '../../assets/images/units/unit-guan-yu-actions.png';
|
||||||
import guanYuUnitSheetUrl from '../../assets/images/units/unit-guan-yu.png';
|
import guanYuUnitSheetUrl from '../../assets/images/units/unit-guan-yu.png';
|
||||||
|
import liuBeiActionSheetUrl from '../../assets/images/units/unit-liu-bei-actions.png';
|
||||||
import liuBeiUnitSheetUrl from '../../assets/images/units/unit-liu-bei.png';
|
import liuBeiUnitSheetUrl from '../../assets/images/units/unit-liu-bei.png';
|
||||||
|
import rebelArcherActionSheetUrl from '../../assets/images/units/unit-rebel-archer-actions.png';
|
||||||
import rebelArcherUnitSheetUrl from '../../assets/images/units/unit-rebel-archer.png';
|
import rebelArcherUnitSheetUrl from '../../assets/images/units/unit-rebel-archer.png';
|
||||||
|
import rebelCavalryActionSheetUrl from '../../assets/images/units/unit-rebel-cavalry-actions.png';
|
||||||
import rebelCavalryUnitSheetUrl from '../../assets/images/units/unit-rebel-cavalry.png';
|
import rebelCavalryUnitSheetUrl from '../../assets/images/units/unit-rebel-cavalry.png';
|
||||||
|
import rebelLeaderActionSheetUrl from '../../assets/images/units/unit-rebel-leader-actions.png';
|
||||||
import rebelLeaderUnitSheetUrl from '../../assets/images/units/unit-rebel-leader.png';
|
import rebelLeaderUnitSheetUrl from '../../assets/images/units/unit-rebel-leader.png';
|
||||||
|
import rebelActionSheetUrl from '../../assets/images/units/unit-rebel-actions.png';
|
||||||
import rebelUnitSheetUrl from '../../assets/images/units/unit-rebel.png';
|
import rebelUnitSheetUrl from '../../assets/images/units/unit-rebel.png';
|
||||||
|
import zhangFeiActionSheetUrl from '../../assets/images/units/unit-zhang-fei-actions.png';
|
||||||
import zhangFeiUnitSheetUrl from '../../assets/images/units/unit-zhang-fei.png';
|
import zhangFeiUnitSheetUrl from '../../assets/images/units/unit-zhang-fei.png';
|
||||||
|
|
||||||
type UnitDirection = 'south' | 'east' | 'north' | 'west';
|
type UnitDirection = 'south' | 'east' | 'north' | 'west';
|
||||||
@@ -31,13 +38,13 @@ const unitSheetRows: Record<UnitDirection, number> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const unitSheets = [
|
const unitSheets = [
|
||||||
{ key: 'unit-liu-bei', url: liuBeiUnitSheetUrl },
|
{ key: 'unit-liu-bei', url: liuBeiUnitSheetUrl, actionKey: 'unit-liu-bei-actions', actionUrl: liuBeiActionSheetUrl },
|
||||||
{ key: 'unit-guan-yu', url: guanYuUnitSheetUrl },
|
{ key: 'unit-guan-yu', url: guanYuUnitSheetUrl, actionKey: 'unit-guan-yu-actions', actionUrl: guanYuActionSheetUrl },
|
||||||
{ key: 'unit-zhang-fei', url: zhangFeiUnitSheetUrl },
|
{ key: 'unit-zhang-fei', url: zhangFeiUnitSheetUrl, actionKey: 'unit-zhang-fei-actions', actionUrl: zhangFeiActionSheetUrl },
|
||||||
{ key: 'unit-rebel', url: rebelUnitSheetUrl },
|
{ key: 'unit-rebel', url: rebelUnitSheetUrl, actionKey: 'unit-rebel-actions', actionUrl: rebelActionSheetUrl },
|
||||||
{ key: 'unit-rebel-archer', url: rebelArcherUnitSheetUrl },
|
{ key: 'unit-rebel-archer', url: rebelArcherUnitSheetUrl, actionKey: 'unit-rebel-archer-actions', actionUrl: rebelArcherActionSheetUrl },
|
||||||
{ key: 'unit-rebel-cavalry', url: rebelCavalryUnitSheetUrl },
|
{ key: 'unit-rebel-cavalry', url: rebelCavalryUnitSheetUrl, actionKey: 'unit-rebel-cavalry-actions', actionUrl: rebelCavalryActionSheetUrl },
|
||||||
{ key: 'unit-rebel-leader', url: rebelLeaderUnitSheetUrl }
|
{ key: 'unit-rebel-leader', url: rebelLeaderUnitSheetUrl, actionKey: 'unit-rebel-leader-actions', actionUrl: rebelLeaderActionSheetUrl }
|
||||||
];
|
];
|
||||||
|
|
||||||
export class BootScene extends Phaser.Scene {
|
export class BootScene extends Phaser.Scene {
|
||||||
@@ -57,11 +64,15 @@ export class BootScene extends Phaser.Scene {
|
|||||||
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
||||||
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
||||||
|
|
||||||
unitSheets.forEach(({ key, url }) => {
|
unitSheets.forEach(({ key, url, actionKey, actionUrl }) => {
|
||||||
this.load.spritesheet(key, url, {
|
this.load.spritesheet(key, url, {
|
||||||
frameWidth: unitSheetFrameSize,
|
frameWidth: unitSheetFrameSize,
|
||||||
frameHeight: unitSheetFrameSize
|
frameHeight: unitSheetFrameSize
|
||||||
});
|
});
|
||||||
|
this.load.spritesheet(actionKey, actionUrl, {
|
||||||
|
frameWidth: unitSheetFrameSize,
|
||||||
|
frameHeight: unitSheetFrameSize
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||