From 40a0e4343f84d25b7f82b3cb6f1dde57710391b2 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Mon, 22 Jun 2026 02:49:56 +0900 Subject: [PATCH] Add directional walking unit sprites --- src/game/scenes/BattleScene.ts | 47 ++++- src/game/scenes/BootScene.ts | 376 ++++++++++++++++++++------------- 2 files changed, 277 insertions(+), 146 deletions(-) diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index 7c4e40f..8cdc68a 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -44,10 +44,14 @@ type BattleLayout = { }; type UnitView = { - sprite: Phaser.GameObjects.Image; + sprite: Phaser.GameObjects.Sprite; label: Phaser.GameObjects.Text; + textureBase: string; + direction: UnitDirection; }; +type UnitDirection = 'south' | 'east' | 'north' | 'west'; + type BattlePhase = 'idle' | 'moving' | 'command'; type BattleCommand = 'attack' | 'strategy' | 'item' | 'wait'; type RosterTab = 'ally' | 'enemy'; @@ -245,7 +249,8 @@ export class BattleScene extends Phaser.Scene { private drawUnits() { firstBattleUnits.forEach((unit) => { const sizeRatio = unit.faction === 'ally' ? 1.08 : 1.02; - const sprite = this.add.image(this.tileCenterX(unit.x), this.tileCenterY(unit.y), unitTexture[unit.id] ?? 'unit-rebel'); + const textureBase = unitTexture[unit.id] ?? 'unit-rebel'; + const sprite = this.add.sprite(this.tileCenterX(unit.x), this.tileCenterY(unit.y), `${textureBase}-south-0`); sprite.setName(`unit-${unit.id}`); sprite.setDisplaySize(this.layout.tileSize * sizeRatio, this.layout.tileSize * sizeRatio); sprite.setDepth(6); @@ -261,7 +266,7 @@ export class BattleScene extends Phaser.Scene { }); label.setOrigin(0.5, 0); label.setDepth(7); - this.unitViews.set(unit.id, { sprite, label }); + this.unitViews.set(unit.id, { sprite, label, textureBase, direction: 'south' }); }); } @@ -769,6 +774,8 @@ export class BattleScene extends Phaser.Scene { view.sprite.postFX.clear(); view.sprite.clearTint(); view.sprite.setAlpha(1); + view.sprite.stop(); + this.setUnitDirectionFrame(view, view.direction); view.label.setAlpha(1); view.label.setColor(unit?.faction === 'ally' ? '#e7edf7' : '#ffebe7'); view.label.setBackgroundColor(''); @@ -939,29 +946,59 @@ export class BattleScene extends Phaser.Scene { const targetX = this.tileCenterX(x); const targetY = this.tileCenterY(y); + const direction = this.directionFromDelta(targetX - view.sprite.x, targetY - view.sprite.y); this.tweens.killTweensOf([view.sprite, view.label]); + this.playUnitWalk(view, direction); this.tweens.add({ targets: view.sprite, x: targetX, y: targetY, duration, - ease: 'Sine.easeInOut' + ease: 'Sine.easeInOut', + onComplete: () => this.stopUnitWalk(view, direction) }); this.tweens.add({ targets: view.label, x: targetX, - y: targetY + this.layout.tileSize * 0.36, + y: targetY + this.layout.tileSize * 0.47, duration, ease: 'Sine.easeInOut' }); } + private directionFromDelta(deltaX: number, deltaY: number): UnitDirection { + if (Math.abs(deltaX) > Math.abs(deltaY)) { + return deltaX >= 0 ? 'east' : 'west'; + } + return deltaY >= 0 ? 'south' : 'north'; + } + + private playUnitWalk(view: UnitView, direction: UnitDirection) { + view.direction = direction; + view.sprite.setFlipX(direction === 'west'); + view.sprite.play(`${view.textureBase}-walk-${direction}`, true); + } + + private stopUnitWalk(view: UnitView, direction: UnitDirection) { + view.direction = direction; + view.sprite.stop(); + this.setUnitDirectionFrame(view, direction); + } + + private setUnitDirectionFrame(view: UnitView, direction: UnitDirection) { + const sourceDirection = direction === 'west' ? 'east' : direction; + view.sprite.setTexture(`${view.textureBase}-${sourceDirection}-0`); + view.sprite.setFlipX(direction === 'west'); + } + private applyActedStyle(unit: UnitData) { const view = this.unitViews.get(unit.id); if (!view) { return; } + view.sprite.stop(); + this.setUnitDirectionFrame(view, view.direction); view.sprite.postFX.addColorMatrix().grayscale(1); view.sprite.setAlpha(0.46); view.sprite.setTint(0xb7b7b7); diff --git a/src/game/scenes/BootScene.ts b/src/game/scenes/BootScene.ts index 5c6b126..17a6502 100644 --- a/src/game/scenes/BootScene.ts +++ b/src/game/scenes/BootScene.ts @@ -32,6 +32,24 @@ type UnitPixelPalette = { cape?: boolean; }; +type UnitDirection = 'south' | 'east' | 'north' | 'west'; +type DrawDirection = Exclude; + +type UnitPixelPose = { + direction: DrawDirection; + frame: number; + bob: number; + step: number; + arm: number; +}; + +const unitWalkFrames = [ + { bob: 0, step: 0, arm: 0 }, + { bob: -1, step: -2, arm: 2 }, + { bob: 0, step: 0, arm: 0 }, + { bob: -1, step: 2, arm: -2 } +]; + export class BootScene extends Phaser.Scene { constructor() { super('BootScene'); @@ -194,21 +212,53 @@ export class BootScene extends Phaser.Scene { } private createPixelUnitTexture(key: string, colors: UnitPixelPalette) { + (['south', 'east', 'north'] as DrawDirection[]).forEach((direction) => { + unitWalkFrames.forEach((frame, frameIndex) => { + this.createPixelUnitFrameTexture(`${key}-${direction}-${frameIndex}`, colors, { + direction, + frame: frameIndex, + ...frame + }); + }); + }); + this.createPixelUnitAnimations(key); + } + + private createPixelUnitAnimations(key: string) { + (['south', 'east', 'north', 'west'] as UnitDirection[]).forEach((direction) => { + const animationKey = `${key}-walk-${direction}`; + if (this.anims.exists(animationKey)) { + return; + } + + const sourceDirection = direction === 'west' ? 'east' : direction; + this.anims.create({ + key: animationKey, + frames: unitWalkFrames.map((_, frameIndex) => ({ + key: `${key}-${sourceDirection}-${frameIndex}` + })), + frameRate: 8, + repeat: -1 + }); + }); + } + + private createPixelUnitFrameTexture(key: string, colors: UnitPixelPalette, pose: UnitPixelPose) { const graphics = this.make.graphics({ x: 0, y: 0 }); - this.drawPixelShadow(graphics, colors); + this.drawPixelShadow(graphics, colors, pose); if (colors.mounted) { - this.drawHorse(graphics, colors); + this.drawHorse(graphics, colors, pose); } if (colors.cape) { - this.drawCape(graphics, colors); + this.drawCape(graphics, colors, pose); } - this.drawWeapon(graphics, colors, 'back'); - this.drawBody(graphics, colors); - this.drawHead(graphics, colors); - this.drawWeapon(graphics, colors, 'front'); + this.drawWeapon(graphics, colors, pose, 'back'); + this.drawBody(graphics, colors, pose); + this.drawHead(graphics, colors, pose); + this.drawWeapon(graphics, colors, pose, 'front'); graphics.generateTexture(key, 64, 64); graphics.destroy(); @@ -220,25 +270,27 @@ export class BootScene extends Phaser.Scene { graphics.fillRect(x, y, width, height); } - private drawPixelShadow(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette) { + private drawPixelShadow(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, pose: UnitPixelPose) { const left = colors.mounted ? 7 : 15; const width = colors.mounted ? 48 : 36; - this.px(graphics, 0x050609, left, 58, width, 3, 0.5); - this.px(graphics, 0x050609, left + 5, 56, width - 10, 2, 0.38); - this.px(graphics, 0x050609, left + 12, 54, width - 24, 2, 0.28); + const shift = pose.direction === 'east' ? 2 : 0; + this.px(graphics, 0x050609, left + shift, 58, width, 3, 0.5); + this.px(graphics, 0x050609, left + 5 + shift, 56, width - 10, 2, 0.38); + this.px(graphics, 0x050609, left + 12 + shift, 54, width - 24, 2, 0.28); } - private drawHorse(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette) { + private drawHorse(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, pose: UnitPixelPose) { const outline = colors.outline ?? 0x15100d; - this.px(graphics, outline, 9, 38, 40, 13); - this.px(graphics, outline, 44, 30, 9, 13); + const y = pose.bob; + this.px(graphics, outline, 9, 38 + y, 40, 13); + this.px(graphics, outline, 44, 30 + y, 9, 13); this.px(graphics, outline, 6, 43, 8, 6); - this.px(graphics, 0x5b3824, 11, 39, 36, 10); - this.px(graphics, 0x70472c, 13, 37, 25, 4); - this.px(graphics, 0x8c5a36, 17, 38, 19, 2); - this.px(graphics, 0x3a2418, 13, 49, 6, 9); + this.px(graphics, 0x5b3824, 11, 39 + y, 36, 10); + this.px(graphics, 0x70472c, 13, 37 + y, 25, 4); + this.px(graphics, 0x8c5a36, 17, 38 + y, 19, 2); + this.px(graphics, 0x3a2418, 13 + Math.max(0, -pose.step), 49, 6, 9); this.px(graphics, 0x3a2418, 25, 48, 5, 10); - this.px(graphics, 0x3a2418, 39, 48, 6, 10); + this.px(graphics, 0x3a2418, 39 + Math.max(0, pose.step), 48, 6, 10); this.px(graphics, outline, 12, 57, 8, 2); this.px(graphics, outline, 24, 57, 8, 2); this.px(graphics, outline, 38, 57, 9, 2); @@ -251,110 +303,139 @@ export class BootScene extends Phaser.Scene { this.px(graphics, 0x0b0b0b, 49, 32, 1, 1); } - private drawCape(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette) { + private drawCape(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, pose: UnitPixelPose) { const outline = colors.outline ?? 0x17100c; - this.px(graphics, outline, 16, 20, 22, 24); - this.px(graphics, outline, 13, 28, 9, 13); - this.px(graphics, 0x6c2722, 17, 21, 20, 21); - this.px(graphics, 0x8f392c, 18, 23, 8, 16); - this.px(graphics, 0x3f1715, 14, 35, 18, 4); - this.px(graphics, 0xc46146, 20, 24, 3, 12); + const y = pose.bob; + const side = pose.direction === 'east' ? 4 : 0; + this.px(graphics, outline, 16 + side, 20 + y, 22, 24); + this.px(graphics, outline, 13 + side, 28 + y, 9, 13); + this.px(graphics, 0x6c2722, 17 + side, 21 + y, 20, 21); + this.px(graphics, 0x8f392c, 18 + side, 23 + y, 8, 16); + this.px(graphics, 0x3f1715, 14 + side, 35 + y, 18, 4); + this.px(graphics, 0xc46146, 20 + side, 24 + y, 3, 12); } - private drawWeapon(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, layer: 'back' | 'front') { + private drawWeapon(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, pose: UnitPixelPose, layer: 'back' | 'front') { const kind = colors.weaponKind ?? 'sword'; const metal = colors.metal ?? colors.weapon; const outline = colors.outline ?? 0x101010; + const y = pose.bob; if (layer === 'back' && kind === 'spear') { - this.px(graphics, outline, 50, 8, 4, 47); - this.px(graphics, colors.weapon, 51, 10, 2, 44); - this.px(graphics, outline, 48, 5, 7, 5); - this.px(graphics, metal, 49, 3, 5, 6); - this.px(graphics, metal, 46, 9, 10, 2); - this.px(graphics, 0xffffff, 50, 4, 2, 3, 0.55); + const x = pose.direction === 'east' ? 44 : pose.direction === 'north' ? 13 : 50; + this.px(graphics, outline, x, 8 + y, 4, 47); + this.px(graphics, colors.weapon, x + 1, 10 + y, 2, 44); + this.px(graphics, outline, x - 2, 5 + y, 7, 5); + this.px(graphics, metal, x - 1, 3 + y, 5, 6); + this.px(graphics, metal, x - 4, 9 + y, 10, 2); + this.px(graphics, 0xffffff, x, 4 + y, 2, 3, 0.55); return; } if (layer === 'back' && kind === 'glaive') { - this.px(graphics, outline, 50, 5, 4, 50); - this.px(graphics, colors.weapon, 51, 8, 2, 46); - this.px(graphics, outline, 44, 6, 13, 5); - this.px(graphics, metal, 45, 5, 10, 5); - this.px(graphics, metal, 54, 9, 5, 5); - this.px(graphics, metal, 47, 12, 4, 6); - this.px(graphics, 0xffffff, 47, 6, 6, 1, 0.48); + const x = pose.direction === 'north' ? 12 : 50; + this.px(graphics, outline, x, 5 + y, 4, 50); + this.px(graphics, colors.weapon, x + 1, 8 + y, 2, 46); + this.px(graphics, outline, x - 6, 6 + y, 13, 5); + this.px(graphics, metal, x - 5, 5 + y, 10, 5); + this.px(graphics, metal, x + 4, 9 + y, 5, 5); + this.px(graphics, metal, x - 3, 12 + y, 4, 6); + this.px(graphics, 0xffffff, x - 3, 6 + y, 6, 1, 0.48); return; } if (layer === 'back' && kind === 'bow') { - this.px(graphics, outline, 15, 17, 3, 27); - this.px(graphics, outline, 18, 14, 3, 6); - this.px(graphics, outline, 18, 41, 3, 6); - this.px(graphics, colors.weapon, 17, 18, 2, 25); - this.px(graphics, colors.weapon, 19, 15, 2, 5); - this.px(graphics, colors.weapon, 19, 41, 2, 5); - this.px(graphics, metal, 22, 19, 1, 24, 0.8); - this.px(graphics, metal, 11, 32, 12, 1); - this.px(graphics, metal, 11, 30, 3, 5); + const x = pose.direction === 'east' ? 47 : 15; + this.px(graphics, outline, x, 17 + y, 3, 27); + this.px(graphics, outline, x + 3, 14 + y, 3, 6); + this.px(graphics, outline, x + 3, 41 + y, 3, 6); + this.px(graphics, colors.weapon, x + 2, 18 + y, 2, 25); + this.px(graphics, colors.weapon, x + 4, 15 + y, 2, 5); + this.px(graphics, colors.weapon, x + 4, 41 + y, 2, 5); + this.px(graphics, metal, x + 7, 19 + y, 1, 24, 0.8); + this.px(graphics, metal, x - 4, 32 + y, 12, 1); + this.px(graphics, metal, x - 4, 30 + y, 3, 5); return; } if (layer === 'back' && kind === 'axe') { - this.px(graphics, outline, 50, 13, 4, 43); - this.px(graphics, colors.weapon, 51, 15, 2, 40); - this.px(graphics, outline, 43, 11, 16, 9); - this.px(graphics, metal, 44, 10, 9, 8); - this.px(graphics, metal, 52, 13, 7, 6); - this.px(graphics, 0xffffff, 45, 11, 5, 1, 0.5); + const x = pose.direction === 'north' ? 12 : 50; + this.px(graphics, outline, x, 13 + y, 4, 43); + this.px(graphics, colors.weapon, x + 1, 15 + y, 2, 40); + this.px(graphics, outline, x - 7, 11 + y, 16, 9); + this.px(graphics, metal, x - 6, 10 + y, 9, 8); + this.px(graphics, metal, x + 2, 13 + y, 7, 6); + this.px(graphics, 0xffffff, x - 5, 11 + y, 5, 1, 0.5); return; } if (layer === 'front' && kind === 'dual') { - this.px(graphics, outline, 46, 26, 3, 20); - this.px(graphics, metal, 47, 22, 2, 20); - this.px(graphics, metal, 49, 20, 3, 4); - this.px(graphics, colors.accent, 44, 39, 6, 2); - this.px(graphics, outline, 17, 31, 3, 17); - this.px(graphics, metal, 16, 28, 2, 18); - this.px(graphics, metal, 14, 27, 3, 4); - this.px(graphics, colors.accent, 16, 42, 6, 2); + if (pose.direction === 'north') { + this.px(graphics, outline, 18, 30 + y, 3, 15); + this.px(graphics, metal, 17, 27 + y, 2, 16); + this.px(graphics, outline, 45, 30 + y, 3, 15); + this.px(graphics, metal, 46, 27 + y, 2, 16); + return; + } + const side = pose.direction === 'east' ? 3 : 0; + this.px(graphics, outline, 46 + side, 26 + y, 3, 20); + this.px(graphics, metal, 47 + side, 22 + y, 2, 20); + this.px(graphics, metal, 49 + side, 20 + y, 3, 4); + this.px(graphics, colors.accent, 44 + side, 39 + y, 6, 2); + this.px(graphics, outline, 17 + pose.arm, 31 + y, 3, 17); + this.px(graphics, metal, 16 + pose.arm, 28 + y, 2, 18); + this.px(graphics, metal, 14 + pose.arm, 27 + y, 3, 4); + this.px(graphics, colors.accent, 16 + pose.arm, 42 + y, 6, 2); return; } if (layer === 'front' && kind === 'sword') { - this.px(graphics, outline, 48, 22, 4, 28); - this.px(graphics, metal, 49, 18, 2, 29); - this.px(graphics, metal, 47, 20, 6, 3); - this.px(graphics, colors.accent, 46, 42, 7, 2); + this.px(graphics, outline, 48, 22 + y, 4, 28); + this.px(graphics, metal, 49, 18 + y, 2, 29); + this.px(graphics, metal, 47, 20 + y, 6, 3); + this.px(graphics, colors.accent, 46, 42 + y, 7, 2); } } - private drawBody(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette) { + private drawBody(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, pose: UnitPixelPose) { const outline = colors.outline ?? 0x111318; const highlight = colors.highlight ?? colors.robe; const boot = colors.boot ?? 0x16120e; - const bodyY = colors.mounted ? 21 : 26; + const bodyY = (colors.mounted ? 21 : 26) + pose.bob; const legY = colors.mounted ? 40 : 46; + const side = pose.direction === 'east' ? 3 : 0; - this.px(graphics, outline, 23, legY, 10, 12); - this.px(graphics, outline, 35, legY - 2, 10, 14); - this.px(graphics, colors.shade, 25, legY, 7, 10); - this.px(graphics, colors.robe, 36, legY - 1, 7, 11); + this.px(graphics, outline, 23 + side + Math.min(0, pose.step), legY, 10, 12); + this.px(graphics, outline, 35 + side + Math.max(0, pose.step), legY - 2, 10, 14); + this.px(graphics, colors.shade, 25 + side + Math.min(0, pose.step), legY, 7, 10); + this.px(graphics, colors.robe, 36 + side + Math.max(0, pose.step), legY - 1, 7, 11); this.px(graphics, boot, 22, legY + 10, 12, 4); - this.px(graphics, boot, 35, legY + 10, 12, 4); - this.px(graphics, 0xffffff, 27, legY + 1, 3, 1, 0.3); + this.px(graphics, boot, 35 + side, legY + 10, 12, 4); + this.px(graphics, 0xffffff, 27 + side, legY + 1, 3, 1, 0.3); - this.px(graphics, outline, 21, bodyY + 2, 25, 23); - this.px(graphics, outline, 18, bodyY + 8, 8, 16); - this.px(graphics, outline, 42, bodyY + 6, 8, 16); - this.px(graphics, colors.shade, 23, bodyY + 4, 21, 21); - this.px(graphics, colors.robe, 24, bodyY + 3, 18, 20); - this.px(graphics, highlight, 26, bodyY + 5, 5, 15); - this.px(graphics, colors.shade, 37, bodyY + 5, 5, 17); - this.px(graphics, colors.robe, 19, bodyY + 9, 7, 13); - this.px(graphics, colors.robe, 42, bodyY + 8, 7, 12); - this.px(graphics, highlight, 20, bodyY + 10, 3, 9); + if (pose.direction === 'east') { + this.px(graphics, outline, 25, bodyY + 2, 21, 23); + this.px(graphics, outline, 22 + pose.arm, bodyY + 9, 8, 14); + this.px(graphics, outline, 42 - pose.arm, bodyY + 8, 8, 14); + this.px(graphics, colors.shade, 27, bodyY + 4, 17, 20); + this.px(graphics, colors.robe, 28, bodyY + 3, 14, 20); + this.px(graphics, highlight, 30, bodyY + 5, 4, 14); + this.px(graphics, colors.shade, 39, bodyY + 6, 4, 16); + this.px(graphics, colors.robe, 23 + pose.arm, bodyY + 10, 7, 12); + this.px(graphics, colors.robe, 42 - pose.arm, bodyY + 9, 7, 11); + } else { + const backShade = pose.direction === 'north' ? colors.robe : colors.shade; + this.px(graphics, outline, 21, bodyY + 2, 25, 23); + this.px(graphics, outline, 18, bodyY + 8, 8, 16); + this.px(graphics, outline, 42, bodyY + 6, 8, 16); + this.px(graphics, backShade, 23, bodyY + 4, 21, 21); + this.px(graphics, colors.robe, 24, bodyY + 3, 18, 20); + this.px(graphics, highlight, 26, bodyY + 5, 5, 15); + this.px(graphics, colors.shade, 37, bodyY + 5, 5, 17); + this.px(graphics, colors.robe, 19 + pose.arm, bodyY + 9, 7, 13); + this.px(graphics, colors.robe, 42 - pose.arm, bodyY + 8, 7, 12); + this.px(graphics, highlight, 20 + pose.arm, bodyY + 10, 3, 9); + } this.px(graphics, colors.trim, 24, bodyY + 4, 18, 2); this.px(graphics, colors.trim, 25, bodyY + 12, 17, 2); @@ -366,10 +447,12 @@ export class BootScene extends Phaser.Scene { this.px(graphics, 0xf8e6a8, 38, bodyY + 7, 3, 2, 0.78); this.px(graphics, 0xf8e6a8, 27, bodyY + 18, 13, 1, 0.55); - this.px(graphics, colors.skin, 17, bodyY + 15, 4, 6); - this.px(graphics, colors.skin, 48, bodyY + 13, 4, 6); - this.px(graphics, colors.skinShade ?? 0x9a6747, 18, bodyY + 20, 3, 2); - this.px(graphics, colors.skinShade ?? 0x9a6747, 49, bodyY + 18, 3, 2); + if (pose.direction !== 'north') { + this.px(graphics, colors.skin, 17 + pose.arm, bodyY + 15, 4, 6); + this.px(graphics, colors.skin, 48 - pose.arm, bodyY + 13, 4, 6); + this.px(graphics, colors.skinShade ?? 0x9a6747, 18 + pose.arm, bodyY + 20, 3, 2); + this.px(graphics, colors.skinShade ?? 0x9a6747, 49 - pose.arm, bodyY + 18, 3, 2); + } if (colors.weaponKind === 'bow') { this.px(graphics, colors.accent, 39, bodyY + 14, 7, 3); @@ -377,68 +460,79 @@ export class BootScene extends Phaser.Scene { } } - private drawHead(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette) { + private drawHead(graphics: Phaser.GameObjects.Graphics, colors: UnitPixelPalette, pose: UnitPixelPose) { const outline = colors.outline ?? 0x111318; const skinShade = colors.skinShade ?? 0xb17450; - const headY = colors.mounted ? 7 : 10; + const headY = (colors.mounted ? 7 : 10) + pose.bob; + const side = pose.direction === 'east' ? 3 : 0; - this.px(graphics, outline, 24, headY + 1, 18, 16); - this.px(graphics, outline, 21, headY + 6, 5, 7); - this.px(graphics, outline, 40, headY + 6, 5, 7); - this.px(graphics, colors.skin, 25, headY + 2, 16, 14); - this.px(graphics, colors.skin, 22, headY + 7, 4, 5); - this.px(graphics, colors.skin, 40, headY + 7, 4, 5); - this.px(graphics, skinShade, 34, headY + 9, 5, 5); - this.px(graphics, skinShade, 26, headY + 14, 14, 2); - - if (colors.headgear === 'turban') { - this.px(graphics, colors.accent, 23, headY - 1, 20, 6); - this.px(graphics, colors.trim, 25, headY, 15, 2); - this.px(graphics, colors.accent, 31, headY - 4, 8, 4); - this.px(graphics, colors.shade, 40, headY + 1, 5, 5); - } else if (colors.headgear === 'helmet') { - this.px(graphics, outline, 23, headY - 2, 20, 8); - this.px(graphics, colors.trim, 25, headY - 3, 16, 6); - this.px(graphics, colors.accent, 29, headY - 7, 8, 5); - this.px(graphics, colors.metal ?? colors.trim, 26, headY + 3, 15, 2); - } else if (colors.headgear === 'cap') { - this.px(graphics, colors.hair, 23, headY - 1, 20, 6); - this.px(graphics, colors.accent, 25, headY - 2, 15, 3); - this.px(graphics, colors.accent, 30, headY - 5, 8, 4); - } else { - this.px(graphics, colors.hair, 23, headY - 2, 20, 6); - this.px(graphics, colors.hair, 22, headY + 2, 6, 9); - this.px(graphics, colors.hair, 39, headY + 2, 5, 9); - this.px(graphics, colors.accent, 26, headY - 4, 13, 3); - this.px(graphics, colors.accent, 29, headY - 7, 8, 4); - this.px(graphics, colors.hair, 31, headY - 9, 5, 3); + this.px(graphics, outline, 24 + side, headY + 1, pose.direction === 'east' ? 16 : 18, 16); + this.px(graphics, outline, 21 + side, headY + 6, 5, 7); + this.px(graphics, outline, 40 + side, headY + 6, 5, 7); + this.px(graphics, colors.skin, 25 + side, headY + 2, pose.direction === 'east' ? 14 : 16, 14); + if (pose.direction !== 'north') { + this.px(graphics, colors.skin, 22 + side, headY + 7, 4, 5); + this.px(graphics, colors.skin, 40 + side, headY + 7, 4, 5); + this.px(graphics, skinShade, 34 + side, headY + 9, 5, 5); + this.px(graphics, skinShade, 26 + side, headY + 14, 14, 2); } - this.px(graphics, 0x050609, 27, headY + 8, 2, 1); - this.px(graphics, 0x050609, 36, headY + 8, 2, 1); - this.px(graphics, 0x3a1d18, 31, headY + 13, 5, 1); - this.px(graphics, 0xffffff, 29, headY + 4, 5, 1, 0.5); - this.px(graphics, 0xffffff, 36, headY + 4, 4, 1, 0.42); + if (colors.headgear === 'turban') { + this.px(graphics, colors.accent, 23 + side, headY - 1, 20, 6); + this.px(graphics, colors.trim, 25 + side, headY, 15, 2); + this.px(graphics, colors.accent, 31 + side, headY - 4, 8, 4); + this.px(graphics, colors.shade, 40 + side, headY + 1, 5, 5); + } else if (colors.headgear === 'helmet') { + this.px(graphics, outline, 23 + side, headY - 2, 20, 8); + this.px(graphics, colors.trim, 25 + side, headY - 3, 16, 6); + this.px(graphics, colors.accent, 29 + side, headY - 7, 8, 5); + this.px(graphics, colors.metal ?? colors.trim, 26 + side, headY + 3, 15, 2); + } else if (colors.headgear === 'cap') { + this.px(graphics, colors.hair, 23 + side, headY - 1, 20, 6); + this.px(graphics, colors.accent, 25 + side, headY - 2, 15, 3); + this.px(graphics, colors.accent, 30 + side, headY - 5, 8, 4); + } else { + this.px(graphics, colors.hair, 23 + side, headY - 2, 20, 6); + this.px(graphics, colors.hair, 22 + side, headY + 2, 6, 9); + this.px(graphics, colors.hair, 39 + side, headY + 2, 5, 9); + this.px(graphics, colors.accent, 26 + side, headY - 4, 13, 3); + this.px(graphics, colors.accent, 29 + side, headY - 7, 8, 4); + this.px(graphics, colors.hair, 31 + side, headY - 9, 5, 3); + } - if (colors.beard) { + if (pose.direction !== 'north') { + this.px(graphics, 0x050609, 27 + side, headY + 8, 2, 1); + this.px(graphics, 0x050609, 36 + side, headY + 8, 2, 1); + this.px(graphics, 0x3a1d18, 31 + side, headY + 13, 5, 1); + this.px(graphics, 0xffffff, 29 + side, headY + 4, 5, 1, 0.5); + this.px(graphics, 0xffffff, 36 + side, headY + 4, 4, 1, 0.42); + if (pose.direction === 'east') { + this.px(graphics, skinShade, 41 + side, headY + 10, 3, 2); + } + } else { + this.px(graphics, colors.shade, 26, headY + 7, 14, 6, 0.55); + this.px(graphics, colors.trim, 28, headY + 12, 10, 2, 0.75); + } + + if (colors.beard && pose.direction !== 'north') { const beardY = headY + 15; - this.px(graphics, outline, 26, beardY - 1, 15, 8); - this.px(graphics, colors.hair, 27, beardY, 13, 7); - this.px(graphics, 0x4b281d, 28, beardY + 1, 3, 5); - this.px(graphics, 0x4b281d, 36, beardY + 1, 2, 5); + this.px(graphics, outline, 26 + side, beardY - 1, 15, 8); + this.px(graphics, colors.hair, 27 + side, beardY, 13, 7); + this.px(graphics, 0x4b281d, 28 + side, beardY + 1, 3, 5); + this.px(graphics, 0x4b281d, 36 + side, beardY + 1, 2, 5); if (colors.beardStyle === 'long') { - this.px(graphics, outline, 28, beardY + 6, 11, 12); - this.px(graphics, colors.hair, 29, beardY + 6, 9, 11); - this.px(graphics, 0x160d0a, 31, beardY + 14, 5, 5); - this.px(graphics, 0x6a3a2a, 31, beardY + 7, 3, 9); + this.px(graphics, outline, 28 + side, beardY + 6, 11, 12); + this.px(graphics, colors.hair, 29 + side, beardY + 6, 9, 11); + this.px(graphics, 0x160d0a, 31 + side, beardY + 14, 5, 5); + this.px(graphics, 0x6a3a2a, 31 + side, beardY + 7, 3, 9); } else if (colors.beardStyle === 'wild') { - this.px(graphics, outline, 23, beardY + 2, 20, 10); - this.px(graphics, colors.hair, 24, beardY + 2, 18, 9); - this.px(graphics, outline, 27, beardY + 10, 13, 8); - this.px(graphics, colors.hair, 28, beardY + 10, 11, 7); - this.px(graphics, 0x4b281d, 26, beardY + 5, 4, 4); - this.px(graphics, 0x4b281d, 37, beardY + 5, 4, 4); + this.px(graphics, outline, 23 + side, beardY + 2, 20, 10); + this.px(graphics, colors.hair, 24 + side, beardY + 2, 18, 9); + this.px(graphics, outline, 27 + side, beardY + 10, 13, 8); + this.px(graphics, colors.hair, 28 + side, beardY + 10, 11, 7); + this.px(graphics, 0x4b281d, 26 + side, beardY + 5, 4, 4); + this.px(graphics, 0x4b281d, 37 + side, beardY + 5, 4, 4); } } }