359 lines
15 KiB
TypeScript
359 lines
15 KiB
TypeScript
import Phaser from 'phaser';
|
|
import fifthBattleMapUrl from '../../assets/images/battle/fifth-battle-map.svg';
|
|
import firstBattleMapUrl from '../../assets/images/battle/first-battle-map.png';
|
|
import fourthBattleMapUrl from '../../assets/images/battle/fourth-battle-map.svg';
|
|
import secondBattleMapUrl from '../../assets/images/battle/second-battle-map.svg';
|
|
import thirdBattleMapUrl from '../../assets/images/battle/third-battle-map.svg';
|
|
import guanYuPortraitUrl from '../../assets/images/portraits/guan-yu.png';
|
|
import liuBeiPortraitUrl from '../../assets/images/portraits/liu-bei.png';
|
|
import zhangFeiPortraitUrl from '../../assets/images/portraits/zhang-fei.png';
|
|
import storyChaosUrl from '../../assets/images/story/01-yellow-turban-chaos.png';
|
|
import storyLiuBeiUrl from '../../assets/images/story/02-liu-bei-resolve.png';
|
|
import storyThreeHeroesUrl from '../../assets/images/story/03-three-heroes-meet.png';
|
|
import storyMilitiaUrl from '../../assets/images/story/04-volunteer-militia.png';
|
|
import storySortieUrl from '../../assets/images/story/05-first-sortie.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 liuBeiActionSheetUrl from '../../assets/images/units/unit-liu-bei-actions.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 rebelCavalryActionSheetUrl from '../../assets/images/units/unit-rebel-cavalry-actions.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 rebelActionSheetUrl from '../../assets/images/units/unit-rebel-actions.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';
|
|
|
|
type UnitDirection = 'south' | 'east' | 'north' | 'west';
|
|
|
|
const unitWalkFrameCount = 4;
|
|
const unitSheetFrameSize = 313;
|
|
const unitWalkFrameRate = 8;
|
|
const unitIdleFrameRate = 4;
|
|
const unitSheetRows: Record<UnitDirection, number> = {
|
|
south: 0,
|
|
east: 1,
|
|
north: 2,
|
|
west: 3
|
|
};
|
|
|
|
const unitSheets = [
|
|
{ key: 'unit-liu-bei', url: liuBeiUnitSheetUrl, actionKey: 'unit-liu-bei-actions', actionUrl: liuBeiActionSheetUrl },
|
|
{ key: 'unit-guan-yu', url: guanYuUnitSheetUrl, actionKey: 'unit-guan-yu-actions', actionUrl: guanYuActionSheetUrl },
|
|
{ key: 'unit-zhang-fei', url: zhangFeiUnitSheetUrl, actionKey: 'unit-zhang-fei-actions', actionUrl: zhangFeiActionSheetUrl },
|
|
{ key: 'unit-rebel', url: rebelUnitSheetUrl, actionKey: 'unit-rebel-actions', actionUrl: rebelActionSheetUrl },
|
|
{ key: 'unit-rebel-archer', url: rebelArcherUnitSheetUrl, actionKey: 'unit-rebel-archer-actions', actionUrl: rebelArcherActionSheetUrl },
|
|
{ key: 'unit-rebel-cavalry', url: rebelCavalryUnitSheetUrl, actionKey: 'unit-rebel-cavalry-actions', actionUrl: rebelCavalryActionSheetUrl },
|
|
{ key: 'unit-rebel-leader', url: rebelLeaderUnitSheetUrl, actionKey: 'unit-rebel-leader-actions', actionUrl: rebelLeaderActionSheetUrl }
|
|
];
|
|
|
|
export class BootScene extends Phaser.Scene {
|
|
constructor() {
|
|
super('BootScene');
|
|
}
|
|
|
|
preload() {
|
|
this.load.image('title-taoyuan', titleBackgroundUrl);
|
|
this.load.image('story-chaos', storyChaosUrl);
|
|
this.load.image('story-liu-bei', storyLiuBeiUrl);
|
|
this.load.image('story-three-heroes', storyThreeHeroesUrl);
|
|
this.load.image('story-militia', storyMilitiaUrl);
|
|
this.load.image('story-sortie', storySortieUrl);
|
|
this.load.image('battle-map-first', firstBattleMapUrl);
|
|
this.load.image('battle-map-second', secondBattleMapUrl);
|
|
this.load.image('battle-map-third', thirdBattleMapUrl);
|
|
this.load.image('battle-map-fourth', fourthBattleMapUrl);
|
|
this.load.image('battle-map-fifth', fifthBattleMapUrl);
|
|
this.load.image('portrait-liu-bei', liuBeiPortraitUrl);
|
|
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
|
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
|
|
|
unitSheets.forEach(({ key, url, actionKey, actionUrl }) => {
|
|
this.load.spritesheet(key, url, {
|
|
frameWidth: unitSheetFrameSize,
|
|
frameHeight: unitSheetFrameSize
|
|
});
|
|
this.load.spritesheet(actionKey, actionUrl, {
|
|
frameWidth: unitSheetFrameSize,
|
|
frameHeight: unitSheetFrameSize
|
|
});
|
|
});
|
|
}
|
|
|
|
create() {
|
|
this.createPetalTexture();
|
|
this.createEquipmentIconTextures();
|
|
this.createUnitAnimations();
|
|
this.scene.start('TitleScene');
|
|
}
|
|
|
|
private createPetalTexture() {
|
|
const graphics = this.make.graphics({ x: 0, y: 0 });
|
|
graphics.fillStyle(0xf0a6a4, 1);
|
|
graphics.fillEllipse(12, 10, 20, 9);
|
|
graphics.fillStyle(0xf7c7bd, 0.75);
|
|
graphics.fillEllipse(15, 8, 10, 4);
|
|
graphics.generateTexture('petal', 24, 18);
|
|
graphics.destroy();
|
|
}
|
|
|
|
private createEquipmentIconTextures() {
|
|
this.createItemIcon('item-training-sword', (graphics) => this.drawSwordIcon(graphics, 0xbfd6e6, 0x6b4a2c));
|
|
this.createItemIcon('item-twin-oath-blades', (graphics) => {
|
|
this.drawSwordIcon(graphics, 0xe8edf7, 0x8a5a32, -4);
|
|
this.drawSwordIcon(graphics, 0xd8b15f, 0x8a5a32, 4);
|
|
});
|
|
this.createItemIcon('item-green-dragon-glaive', (graphics) => this.drawPolearmIcon(graphics, 0x5fbf8f, 0xd8b15f));
|
|
this.createItemIcon('item-serpent-spear', (graphics) => this.drawSpearIcon(graphics, 0xe0e8ef, 0xb86b55));
|
|
this.createItemIcon('item-yellow-turban-saber', (graphics) => this.drawSaberIcon(graphics, 0xd8b15f, 0x9c6b2f));
|
|
this.createItemIcon('item-short-bow', (graphics) => this.drawBowIcon(graphics, 0xc58a4c, 0xe8dfca));
|
|
this.createItemIcon('item-leader-axe', (graphics) => this.drawAxeIcon(graphics, 0xd8dfe6, 0x8d5a3a));
|
|
|
|
this.createItemIcon('item-cloth-armor', (graphics) => this.drawRobeIcon(graphics, 0x91a8bc, 0xe8dfca));
|
|
this.createItemIcon('item-lamellar-armor', (graphics) => this.drawArmorIcon(graphics, 0x7d8792, 0xd8b15f));
|
|
this.createItemIcon('item-oath-robe', (graphics) => this.drawRobeIcon(graphics, 0x4e7aa8, 0xf0c0a8));
|
|
this.createItemIcon('item-reinforced-lamellar', (graphics) => this.drawArmorIcon(graphics, 0xb8c4ce, 0x59a6d6));
|
|
this.createItemIcon('item-rebel-vest', (graphics) => this.drawVestIcon(graphics, 0xb88b4a, 0xd8b15f));
|
|
|
|
this.createItemIcon('item-peach-charm', (graphics) => this.drawCharmIcon(graphics, 0xf0a6a4, 0xd8b15f));
|
|
this.createItemIcon('item-war-manual', (graphics) => this.drawBookIcon(graphics, 0x5b78a0, 0xe8dfca));
|
|
this.createItemIcon('item-bravery-token', (graphics) => this.drawTokenIcon(graphics, 0xd8b15f, 0x9b4c3a));
|
|
this.createItemIcon('item-grain-pouch', (graphics) => this.drawPouchIcon(graphics, 0xa7794a, 0xe8dfca));
|
|
this.createItemIcon('item-yellow-scarf-charm', (graphics) => this.drawCharmIcon(graphics, 0xd8b15f, 0x8b6f2a));
|
|
this.createItemIcon('item-wind-quiver', (graphics) => this.drawQuiverIcon(graphics, 0x8bb3c7, 0xe8dfca));
|
|
}
|
|
|
|
private createItemIcon(key: string, draw: (graphics: Phaser.GameObjects.Graphics) => void) {
|
|
if (this.textures.exists(key)) {
|
|
return;
|
|
}
|
|
|
|
const graphics = this.make.graphics({ x: 0, y: 0 });
|
|
graphics.fillStyle(0x101820, 0);
|
|
graphics.fillRect(0, 0, 28, 28);
|
|
graphics.lineStyle(1, 0x05070a, 0.7);
|
|
draw(graphics);
|
|
graphics.generateTexture(key, 28, 28);
|
|
graphics.destroy();
|
|
}
|
|
|
|
private drawSwordIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number, offsetX = 0) {
|
|
graphics.lineStyle(4, bladeColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(8 + offsetX, 22);
|
|
graphics.lineTo(20 + offsetX, 6);
|
|
graphics.strokePath();
|
|
graphics.lineStyle(2, 0xeff7ff, 0.8);
|
|
graphics.beginPath();
|
|
graphics.moveTo(11 + offsetX, 18);
|
|
graphics.lineTo(18 + offsetX, 8);
|
|
graphics.strokePath();
|
|
graphics.lineStyle(3, 0xd8b15f, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(6 + offsetX, 18);
|
|
graphics.lineTo(12 + offsetX, 23);
|
|
graphics.strokePath();
|
|
graphics.fillStyle(gripColor, 1);
|
|
graphics.fillCircle(8 + offsetX, 23, 2);
|
|
}
|
|
|
|
private drawPolearmIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, accentColor: number) {
|
|
graphics.lineStyle(3, 0x8d5a3a, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(8, 24);
|
|
graphics.lineTo(18, 5);
|
|
graphics.strokePath();
|
|
graphics.fillStyle(bladeColor, 1);
|
|
graphics.fillTriangle(17, 4, 25, 8, 18, 15);
|
|
graphics.lineStyle(2, accentColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(16, 12);
|
|
graphics.lineTo(23, 15);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawSpearIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, accentColor: number) {
|
|
graphics.lineStyle(3, 0x8d5a3a, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(7, 24);
|
|
graphics.lineTo(20, 4);
|
|
graphics.strokePath();
|
|
graphics.fillStyle(bladeColor, 1);
|
|
graphics.fillTriangle(18, 4, 24, 2, 22, 9);
|
|
graphics.fillStyle(accentColor, 1);
|
|
graphics.fillTriangle(15, 11, 20, 9, 17, 15);
|
|
}
|
|
|
|
private drawSaberIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number) {
|
|
graphics.lineStyle(4, bladeColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(8, 22);
|
|
graphics.lineTo(13, 15);
|
|
graphics.lineTo(19, 8);
|
|
graphics.lineTo(23, 6);
|
|
graphics.strokePath();
|
|
graphics.lineStyle(3, gripColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(6, 21);
|
|
graphics.lineTo(12, 23);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawBowIcon(graphics: Phaser.GameObjects.Graphics, bowColor: number, stringColor: number) {
|
|
graphics.lineStyle(3, bowColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(20, 4);
|
|
graphics.lineTo(14, 9);
|
|
graphics.lineTo(11, 14);
|
|
graphics.lineTo(14, 19);
|
|
graphics.lineTo(20, 24);
|
|
graphics.strokePath();
|
|
graphics.lineStyle(1, stringColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(20, 4);
|
|
graphics.lineTo(20, 24);
|
|
graphics.strokePath();
|
|
graphics.lineStyle(2, 0xd8dfe6, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(7, 14);
|
|
graphics.lineTo(22, 14);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawAxeIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number) {
|
|
graphics.lineStyle(3, gripColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(9, 24);
|
|
graphics.lineTo(18, 5);
|
|
graphics.strokePath();
|
|
graphics.fillStyle(bladeColor, 1);
|
|
graphics.fillTriangle(16, 5, 25, 8, 16, 15);
|
|
graphics.fillTriangle(16, 5, 9, 9, 15, 14);
|
|
}
|
|
|
|
private drawArmorIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillTriangle(14, 4, 24, 11, 20, 24);
|
|
graphics.fillTriangle(14, 4, 4, 11, 8, 24);
|
|
graphics.fillRect(8, 10, 12, 14);
|
|
graphics.lineStyle(2, accentColor, 0.9);
|
|
graphics.strokeRect(9, 11, 10, 12);
|
|
graphics.lineStyle(1, 0x05070a, 0.6);
|
|
graphics.beginPath();
|
|
graphics.moveTo(10, 16);
|
|
graphics.lineTo(18, 16);
|
|
graphics.moveTo(11, 20);
|
|
graphics.lineTo(17, 20);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawRobeIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillTriangle(14, 4, 24, 24, 4, 24);
|
|
graphics.fillStyle(0x101820, 0.45);
|
|
graphics.fillTriangle(14, 8, 18, 24, 10, 24);
|
|
graphics.lineStyle(2, accentColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(14, 6);
|
|
graphics.lineTo(14, 23);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawVestIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillRect(7, 7, 14, 18);
|
|
graphics.fillStyle(0x101820, 0.7);
|
|
graphics.fillTriangle(11, 7, 17, 7, 14, 14);
|
|
graphics.lineStyle(2, accentColor, 0.9);
|
|
graphics.strokeRect(8, 8, 12, 16);
|
|
}
|
|
|
|
private drawCharmIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillRoundedRect(8, 7, 12, 16, 2);
|
|
graphics.lineStyle(2, accentColor, 1);
|
|
graphics.strokeRoundedRect(8, 7, 12, 16, 2);
|
|
graphics.fillStyle(accentColor, 1);
|
|
graphics.fillCircle(14, 15, 3);
|
|
}
|
|
|
|
private drawBookIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, pageColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillRect(6, 6, 16, 18);
|
|
graphics.fillStyle(pageColor, 1);
|
|
graphics.fillRect(9, 8, 10, 14);
|
|
graphics.lineStyle(1, 0x101820, 0.65);
|
|
graphics.beginPath();
|
|
graphics.moveTo(14, 8);
|
|
graphics.lineTo(14, 22);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawTokenIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillCircle(14, 14, 9);
|
|
graphics.lineStyle(2, accentColor, 0.9);
|
|
graphics.strokeCircle(14, 14, 7);
|
|
graphics.fillStyle(accentColor, 1);
|
|
graphics.fillTriangle(14, 8, 18, 18, 10, 18);
|
|
}
|
|
|
|
private drawPouchIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, accentColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillRoundedRect(7, 10, 14, 13, 4);
|
|
graphics.fillTriangle(10, 11, 18, 11, 14, 5);
|
|
graphics.lineStyle(2, accentColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(8, 13);
|
|
graphics.lineTo(20, 13);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private drawQuiverIcon(graphics: Phaser.GameObjects.Graphics, baseColor: number, arrowColor: number) {
|
|
graphics.fillStyle(baseColor, 1);
|
|
graphics.fillRoundedRect(10, 8, 9, 16, 2);
|
|
graphics.lineStyle(2, arrowColor, 1);
|
|
graphics.beginPath();
|
|
graphics.moveTo(7, 6);
|
|
graphics.lineTo(18, 20);
|
|
graphics.moveTo(13, 5);
|
|
graphics.lineTo(20, 19);
|
|
graphics.strokePath();
|
|
}
|
|
|
|
private createUnitAnimations() {
|
|
unitSheets.forEach(({ key }) => {
|
|
(['south', 'east', 'north', 'west'] as UnitDirection[]).forEach((direction) => {
|
|
const frames = Array.from({ length: unitWalkFrameCount }, (_, frameIndex) => ({
|
|
key,
|
|
frame: unitSheetRows[direction] * unitWalkFrameCount + frameIndex
|
|
}));
|
|
const walkAnimationKey = `${key}-walk-${direction}`;
|
|
const idleAnimationKey = `${key}-idle-${direction}`;
|
|
|
|
if (!this.anims.exists(walkAnimationKey)) {
|
|
this.anims.create({
|
|
key: walkAnimationKey,
|
|
frames,
|
|
frameRate: unitWalkFrameRate,
|
|
repeat: -1
|
|
});
|
|
}
|
|
|
|
if (!this.anims.exists(idleAnimationKey)) {
|
|
this.anims.create({
|
|
key: idleAnimationKey,
|
|
frames,
|
|
frameRate: unitIdleFrameRate,
|
|
repeat: -1
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|