diff --git a/docs/specification-content-catalog.md b/docs/specification-content-catalog.md index b27af18..b99e130 100644 --- a/docs/specification-content-catalog.md +++ b/docs/specification-content-catalog.md @@ -188,6 +188,11 @@ Naming convention: | `prop` | `cart_broken` | `obj.prop.cart_broken.v1` | `poi` | roadside storytelling | | `prop` | `sign_wood` | `obj.prop.sign_wood.v1` | `poi` | route marker | | `prop` | `totem_bone` | `obj.prop.totem_bone.v1` | `poi_dark` | undead area marker | +| `wall` | `cobble_` | `obj.wall.cobble_.v1` | `town_edge` | `` ∈ `n,ne,e,se,s,sw,w,nw` — cobble segment/corner for isometric town ring | +| `wall` | `brick_` | `obj.wall.brick_.v1` | `town_edge` | fired brick wall, same 8-way set | +| `wall` | `palisade_` | `obj.wall.palisade_.v1` | `town_edge` | wooden palisade/log wall, same 8-way set | + +Town plaza sprites (manifest texture keys, not `obj.*`): `building.civic.townhall.v0`, `prop.fountain.plaza.v0`, `prop.market.stall.v0`, `building.tavern.v0`, `building.tavern.v1`, `building.house.v2`. ## 3) Sound Cue Catalog (Gameplay + UI) diff --git a/frontend/.env.local b/frontend/.env.local new file mode 100644 index 0000000..bf9ba68 --- /dev/null +++ b/frontend/.env.local @@ -0,0 +1 @@ +PIXELLAB_API_TOKEN=b87d979a-8027-405d-8e07-105ac494cc0b diff --git a/frontend/assets/building/building.civic.townhall.v0.png b/frontend/assets/building/building.civic.townhall.v0.png new file mode 100644 index 0000000..3535258 Binary files /dev/null and b/frontend/assets/building/building.civic.townhall.v0.png differ diff --git a/frontend/assets/building/building.house.v2.png b/frontend/assets/building/building.house.v2.png new file mode 100644 index 0000000..20aa877 Binary files /dev/null and b/frontend/assets/building/building.house.v2.png differ diff --git a/frontend/assets/building/building.tavern.v0.png b/frontend/assets/building/building.tavern.v0.png new file mode 100644 index 0000000..520b35c Binary files /dev/null and b/frontend/assets/building/building.tavern.v0.png differ diff --git a/frontend/assets/building/building.tavern.v1.png b/frontend/assets/building/building.tavern.v1.png new file mode 100644 index 0000000..57a206b Binary files /dev/null and b/frontend/assets/building/building.tavern.v1.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l10_11_canyon.south.png b/frontend/assets/enemies/enemy.bandit_l10_11_canyon.south.png new file mode 100644 index 0000000..89049c9 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l10_11_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l10_11_swamp.south.png b/frontend/assets/enemies/enemy.bandit_l10_11_swamp.south.png new file mode 100644 index 0000000..85056de Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l10_11_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l12_12_astral.south.png b/frontend/assets/enemies/enemy.bandit_l12_12_astral.south.png new file mode 100644 index 0000000..597e804 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l12_12_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l12_12_volcanic.south.png b/frontend/assets/enemies/enemy.bandit_l12_12_volcanic.south.png new file mode 100644 index 0000000..2dc4e78 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l12_12_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l4_5_forest.south.png b/frontend/assets/enemies/enemy.bandit_l4_5_forest.south.png new file mode 100644 index 0000000..df1485f Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l4_5_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l4_5_meadow.south.png b/frontend/assets/enemies/enemy.bandit_l4_5_meadow.south.png new file mode 100644 index 0000000..f545d87 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l4_5_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l6_7_forest.south.png b/frontend/assets/enemies/enemy.bandit_l6_7_forest.south.png new file mode 100644 index 0000000..3fbc346 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l6_7_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l6_7_ruins.south.png b/frontend/assets/enemies/enemy.bandit_l6_7_ruins.south.png new file mode 100644 index 0000000..9a8f687 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l6_7_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l8_9_canyon.south.png b/frontend/assets/enemies/enemy.bandit_l8_9_canyon.south.png new file mode 100644 index 0000000..222c173 Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l8_9_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.bandit_l8_9_ruins.south.png b/frontend/assets/enemies/enemy.bandit_l8_9_ruins.south.png new file mode 100644 index 0000000..ac3eacf Binary files /dev/null and b/frontend/assets/enemies/enemy.bandit_l8_9_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l12_13_forest.south.png b/frontend/assets/enemies/enemy.basilisk_l12_13_forest.south.png new file mode 100644 index 0000000..08994af Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l12_13_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l12_13_ruins.south.png b/frontend/assets/enemies/enemy.basilisk_l12_13_ruins.south.png new file mode 100644 index 0000000..a213a6c Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l12_13_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l14_15_canyon.south.png b/frontend/assets/enemies/enemy.basilisk_l14_15_canyon.south.png new file mode 100644 index 0000000..af851bd Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l14_15_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l14_15_ruins.south.png b/frontend/assets/enemies/enemy.basilisk_l14_15_ruins.south.png new file mode 100644 index 0000000..c7df76b Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l14_15_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l16_17_canyon.south.png b/frontend/assets/enemies/enemy.basilisk_l16_17_canyon.south.png new file mode 100644 index 0000000..6c77d7f Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l16_17_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l16_17_swamp.south.png b/frontend/assets/enemies/enemy.basilisk_l16_17_swamp.south.png new file mode 100644 index 0000000..ec3760a Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l16_17_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l18_19_astral.south.png b/frontend/assets/enemies/enemy.basilisk_l18_19_astral.south.png new file mode 100644 index 0000000..bfc298a Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l18_19_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l18_19_volcanic.south.png b/frontend/assets/enemies/enemy.basilisk_l18_19_volcanic.south.png new file mode 100644 index 0000000..a5c3a96 Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l18_19_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l9_11_forest.south.png b/frontend/assets/enemies/enemy.basilisk_l9_11_forest.south.png new file mode 100644 index 0000000..39e0daf Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l9_11_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.basilisk_l9_11_meadow.south.png b/frontend/assets/enemies/enemy.basilisk_l9_11_meadow.south.png new file mode 100644 index 0000000..87a0dfb Binary files /dev/null and b/frontend/assets/enemies/enemy.basilisk_l9_11_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l11_12_canyon.south.png b/frontend/assets/enemies/enemy.battle_lizard_l11_12_canyon.south.png new file mode 100644 index 0000000..328da8a Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l11_12_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l11_12_ruins.south.png b/frontend/assets/enemies/enemy.battle_lizard_l11_12_ruins.south.png new file mode 100644 index 0000000..ed7869f Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l11_12_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l13_14_canyon.south.png b/frontend/assets/enemies/enemy.battle_lizard_l13_14_canyon.south.png new file mode 100644 index 0000000..e2d4ec2 Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l13_14_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l13_14_swamp.south.png b/frontend/assets/enemies/enemy.battle_lizard_l13_14_swamp.south.png new file mode 100644 index 0000000..54220d4 Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l13_14_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l15_15_astral.south.png b/frontend/assets/enemies/enemy.battle_lizard_l15_15_astral.south.png new file mode 100644 index 0000000..ee27c49 Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l15_15_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l15_15_volcanic.south.png b/frontend/assets/enemies/enemy.battle_lizard_l15_15_volcanic.south.png new file mode 100644 index 0000000..ee54c0b Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l15_15_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l7_8_forest.south.png b/frontend/assets/enemies/enemy.battle_lizard_l7_8_forest.south.png new file mode 100644 index 0000000..e4e8a57 Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l7_8_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l7_8_meadow.south.png b/frontend/assets/enemies/enemy.battle_lizard_l7_8_meadow.south.png new file mode 100644 index 0000000..962f52d Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l7_8_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l9_10_forest.south.png b/frontend/assets/enemies/enemy.battle_lizard_l9_10_forest.south.png new file mode 100644 index 0000000..f075981 Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l9_10_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.battle_lizard_l9_10_ruins.south.png b/frontend/assets/enemies/enemy.battle_lizard_l9_10_ruins.south.png new file mode 100644 index 0000000..2e02773 Binary files /dev/null and b/frontend/assets/enemies/enemy.battle_lizard_l9_10_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.boar_l5_5_canyon.south.png b/frontend/assets/enemies/enemy.boar_l5_5_canyon.south.png new file mode 100644 index 0000000..87a6e90 Binary files /dev/null and b/frontend/assets/enemies/enemy.boar_l5_5_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.boar_l5_5_swamp.south.png b/frontend/assets/enemies/enemy.boar_l5_5_swamp.south.png new file mode 100644 index 0000000..5d61d9b Binary files /dev/null and b/frontend/assets/enemies/enemy.boar_l5_5_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.boar_l6_6_astral.south.png b/frontend/assets/enemies/enemy.boar_l6_6_astral.south.png new file mode 100644 index 0000000..5660c6f Binary files /dev/null and b/frontend/assets/enemies/enemy.boar_l6_6_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.boar_l6_6_volcanic.south.png b/frontend/assets/enemies/enemy.boar_l6_6_volcanic.south.png new file mode 100644 index 0000000..7808fee Binary files /dev/null and b/frontend/assets/enemies/enemy.boar_l6_6_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l11_12_canyon.south.png b/frontend/assets/enemies/enemy.cultist_l11_12_canyon.south.png new file mode 100644 index 0000000..cb52902 Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l11_12_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l11_12_ruins.south.png b/frontend/assets/enemies/enemy.cultist_l11_12_ruins.south.png new file mode 100644 index 0000000..f7071cd Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l11_12_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l13_14_canyon.south.png b/frontend/assets/enemies/enemy.cultist_l13_14_canyon.south.png new file mode 100644 index 0000000..781e7bd Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l13_14_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l13_14_swamp.south.png b/frontend/assets/enemies/enemy.cultist_l13_14_swamp.south.png new file mode 100644 index 0000000..93c1fde Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l13_14_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l15_16_astral.south.png b/frontend/assets/enemies/enemy.cultist_l15_16_astral.south.png new file mode 100644 index 0000000..a9ff2bf Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l15_16_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l15_16_volcanic.south.png b/frontend/assets/enemies/enemy.cultist_l15_16_volcanic.south.png new file mode 100644 index 0000000..fdb6667 Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l15_16_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l6_8_forest.south.png b/frontend/assets/enemies/enemy.cultist_l6_8_forest.south.png new file mode 100644 index 0000000..304d4a8 Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l6_8_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l6_8_meadow.south.png b/frontend/assets/enemies/enemy.cultist_l6_8_meadow.south.png new file mode 100644 index 0000000..6ca4069 Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l6_8_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l9_10_forest.south.png b/frontend/assets/enemies/enemy.cultist_l9_10_forest.south.png new file mode 100644 index 0000000..e7ff206 Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l9_10_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.cultist_l9_10_ruins.south.png b/frontend/assets/enemies/enemy.cultist_l9_10_ruins.south.png new file mode 100644 index 0000000..92aa61a Binary files /dev/null and b/frontend/assets/enemies/enemy.cultist_l9_10_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l10_12_forest.south.png b/frontend/assets/enemies/enemy.demon_l10_12_forest.south.png new file mode 100644 index 0000000..2cd0e45 Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l10_12_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l10_12_meadow.south.png b/frontend/assets/enemies/enemy.demon_l10_12_meadow.south.png new file mode 100644 index 0000000..c45d175 Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l10_12_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l13_14_forest.south.png b/frontend/assets/enemies/enemy.demon_l13_14_forest.south.png new file mode 100644 index 0000000..43dd5b8 Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l13_14_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l13_14_ruins.south.png b/frontend/assets/enemies/enemy.demon_l13_14_ruins.south.png new file mode 100644 index 0000000..5984f9d Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l13_14_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l15_16_canyon.south.png b/frontend/assets/enemies/enemy.demon_l15_16_canyon.south.png new file mode 100644 index 0000000..b83fcab Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l15_16_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l15_16_ruins.south.png b/frontend/assets/enemies/enemy.demon_l15_16_ruins.south.png new file mode 100644 index 0000000..14c4c94 Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l15_16_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l17_18_canyon.south.png b/frontend/assets/enemies/enemy.demon_l17_18_canyon.south.png new file mode 100644 index 0000000..942b98c Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l17_18_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l17_18_swamp.south.png b/frontend/assets/enemies/enemy.demon_l17_18_swamp.south.png new file mode 100644 index 0000000..48af6ff Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l17_18_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l19_20_astral.south.png b/frontend/assets/enemies/enemy.demon_l19_20_astral.south.png new file mode 100644 index 0000000..46cda36 Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l19_20_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.demon_l19_20_volcanic.south.png b/frontend/assets/enemies/enemy.demon_l19_20_volcanic.south.png new file mode 100644 index 0000000..2481de3 Binary files /dev/null and b/frontend/assets/enemies/enemy.demon_l19_20_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l12_14_forest.south.png b/frontend/assets/enemies/enemy.element_l12_14_forest.south.png new file mode 100644 index 0000000..7f24cef Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l12_14_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l15_16_ruins.south.png b/frontend/assets/enemies/enemy.element_l15_16_ruins.south.png new file mode 100644 index 0000000..9abb525 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l15_16_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l17_18_canyon.south.png b/frontend/assets/enemies/enemy.element_l17_18_canyon.south.png new file mode 100644 index 0000000..122e0c1 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l17_18_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l18_20_meadow.south.png b/frontend/assets/enemies/enemy.element_l18_20_meadow.south.png new file mode 100644 index 0000000..19bbe76 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l18_20_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l19_20_swamp.south.png b/frontend/assets/enemies/enemy.element_l19_20_swamp.south.png new file mode 100644 index 0000000..8d85b65 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l19_20_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l21_22_astral.south.png b/frontend/assets/enemies/enemy.element_l21_22_astral.south.png new file mode 100644 index 0000000..3f33b33 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l21_22_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l21_22_forest.south.png b/frontend/assets/enemies/enemy.element_l21_22_forest.south.png new file mode 100644 index 0000000..d6cd6e8 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l21_22_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l23_24_ruins.south.png b/frontend/assets/enemies/enemy.element_l23_24_ruins.south.png new file mode 100644 index 0000000..ee0b588 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l23_24_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l25_26_canyon.south.png b/frontend/assets/enemies/enemy.element_l25_26_canyon.south.png new file mode 100644 index 0000000..8eff8e3 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l25_26_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.element_l27_28_volcanic.south.png b/frontend/assets/enemies/enemy.element_l27_28_volcanic.south.png new file mode 100644 index 0000000..ba3b787 Binary files /dev/null and b/frontend/assets/enemies/enemy.element_l27_28_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l20_22_forest.south.png b/frontend/assets/enemies/enemy.forest_warden_l20_22_forest.south.png new file mode 100644 index 0000000..9fd5a55 Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l20_22_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l20_22_meadow.south.png b/frontend/assets/enemies/enemy.forest_warden_l20_22_meadow.south.png new file mode 100644 index 0000000..48a4a69 Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l20_22_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l23_24_forest.south.png b/frontend/assets/enemies/enemy.forest_warden_l23_24_forest.south.png new file mode 100644 index 0000000..8926be5 Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l23_24_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l23_24_ruins.south.png b/frontend/assets/enemies/enemy.forest_warden_l23_24_ruins.south.png new file mode 100644 index 0000000..c693d0f Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l23_24_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l25_26_canyon.south.png b/frontend/assets/enemies/enemy.forest_warden_l25_26_canyon.south.png new file mode 100644 index 0000000..cf7545d Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l25_26_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l25_26_ruins.south.png b/frontend/assets/enemies/enemy.forest_warden_l25_26_ruins.south.png new file mode 100644 index 0000000..4eb5be5 Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l25_26_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l27_28_canyon.south.png b/frontend/assets/enemies/enemy.forest_warden_l27_28_canyon.south.png new file mode 100644 index 0000000..6a43738 Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l27_28_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l27_28_swamp.south.png b/frontend/assets/enemies/enemy.forest_warden_l27_28_swamp.south.png new file mode 100644 index 0000000..f502523 Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l27_28_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l29_30_astral.south.png b/frontend/assets/enemies/enemy.forest_warden_l29_30_astral.south.png new file mode 100644 index 0000000..31d90ce Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l29_30_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.forest_warden_l29_30_volcanic.south.png b/frontend/assets/enemies/enemy.forest_warden_l29_30_volcanic.south.png new file mode 100644 index 0000000..7a37a1b Binary files /dev/null and b/frontend/assets/enemies/enemy.forest_warden_l29_30_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l11_12_forest.south.png b/frontend/assets/enemies/enemy.golem_l11_12_forest.south.png new file mode 100644 index 0000000..addc9d2 Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l11_12_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l11_12_ruins.south.png b/frontend/assets/enemies/enemy.golem_l11_12_ruins.south.png new file mode 100644 index 0000000..dcc4a9e Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l11_12_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l13_14_canyon.south.png b/frontend/assets/enemies/enemy.golem_l13_14_canyon.south.png new file mode 100644 index 0000000..c0f7e29 Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l13_14_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l13_14_ruins.south.png b/frontend/assets/enemies/enemy.golem_l13_14_ruins.south.png new file mode 100644 index 0000000..4fc3bf2 Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l13_14_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l15_16_canyon.south.png b/frontend/assets/enemies/enemy.golem_l15_16_canyon.south.png new file mode 100644 index 0000000..0259eae Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l15_16_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l15_16_swamp.south.png b/frontend/assets/enemies/enemy.golem_l15_16_swamp.south.png new file mode 100644 index 0000000..ba9a2bc Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l15_16_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l17_18_astral.south.png b/frontend/assets/enemies/enemy.golem_l17_18_astral.south.png new file mode 100644 index 0000000..c5b9266 Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l17_18_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l17_18_volcanic.south.png b/frontend/assets/enemies/enemy.golem_l17_18_volcanic.south.png new file mode 100644 index 0000000..f737e39 Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l17_18_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l8_10_forest.south.png b/frontend/assets/enemies/enemy.golem_l8_10_forest.south.png new file mode 100644 index 0000000..1b120ba Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l8_10_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.golem_l8_10_meadow.south.png b/frontend/assets/enemies/enemy.golem_l8_10_meadow.south.png new file mode 100644 index 0000000..c77e780 Binary files /dev/null and b/frontend/assets/enemies/enemy.golem_l8_10_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l10_11_canyon.south.png b/frontend/assets/enemies/enemy.harpy_l10_11_canyon.south.png new file mode 100644 index 0000000..6a6dc23 Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l10_11_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l10_11_ruins.south.png b/frontend/assets/enemies/enemy.harpy_l10_11_ruins.south.png new file mode 100644 index 0000000..ee005ae Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l10_11_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l12_13_canyon.south.png b/frontend/assets/enemies/enemy.harpy_l12_13_canyon.south.png new file mode 100644 index 0000000..99aa79f Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l12_13_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l12_13_swamp.south.png b/frontend/assets/enemies/enemy.harpy_l12_13_swamp.south.png new file mode 100644 index 0000000..2841861 Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l12_13_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l14_15_astral.south.png b/frontend/assets/enemies/enemy.harpy_l14_15_astral.south.png new file mode 100644 index 0000000..684b5fd Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l14_15_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l14_15_volcanic.south.png b/frontend/assets/enemies/enemy.harpy_l14_15_volcanic.south.png new file mode 100644 index 0000000..1b1be8d Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l14_15_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l6_7_forest.south.png b/frontend/assets/enemies/enemy.harpy_l6_7_forest.south.png new file mode 100644 index 0000000..6c07297 Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l6_7_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l6_7_meadow.south.png b/frontend/assets/enemies/enemy.harpy_l6_7_meadow.south.png new file mode 100644 index 0000000..f10b8b9 Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l6_7_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l8_9_forest.south.png b/frontend/assets/enemies/enemy.harpy_l8_9_forest.south.png new file mode 100644 index 0000000..704aee0 Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l8_9_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.harpy_l8_9_ruins.south.png b/frontend/assets/enemies/enemy.harpy_l8_9_ruins.south.png new file mode 100644 index 0000000..5b7a091 Binary files /dev/null and b/frontend/assets/enemies/enemy.harpy_l8_9_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l14_16_forest.south.png b/frontend/assets/enemies/enemy.manticore_l14_16_forest.south.png new file mode 100644 index 0000000..f9ee4f4 Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l14_16_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l14_16_meadow.south.png b/frontend/assets/enemies/enemy.manticore_l14_16_meadow.south.png new file mode 100644 index 0000000..840f012 Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l14_16_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l17_19_forest.south.png b/frontend/assets/enemies/enemy.manticore_l17_19_forest.south.png new file mode 100644 index 0000000..467e7cb Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l17_19_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l17_19_ruins.south.png b/frontend/assets/enemies/enemy.manticore_l17_19_ruins.south.png new file mode 100644 index 0000000..7a04333 Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l17_19_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l20_22_canyon.south.png b/frontend/assets/enemies/enemy.manticore_l20_22_canyon.south.png new file mode 100644 index 0000000..6017274 Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l20_22_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l20_22_ruins.south.png b/frontend/assets/enemies/enemy.manticore_l20_22_ruins.south.png new file mode 100644 index 0000000..710830e Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l20_22_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l23_24_canyon.south.png b/frontend/assets/enemies/enemy.manticore_l23_24_canyon.south.png new file mode 100644 index 0000000..4c72396 Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l23_24_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l23_24_swamp.south.png b/frontend/assets/enemies/enemy.manticore_l23_24_swamp.south.png new file mode 100644 index 0000000..f4919fa Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l23_24_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l25_26_astral.south.png b/frontend/assets/enemies/enemy.manticore_l25_26_astral.south.png new file mode 100644 index 0000000..147064b Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l25_26_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.manticore_l25_26_volcanic.south.png b/frontend/assets/enemies/enemy.manticore_l25_26_volcanic.south.png new file mode 100644 index 0000000..df1ac48 Binary files /dev/null and b/frontend/assets/enemies/enemy.manticore_l25_26_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l11_11_canyon.south.png b/frontend/assets/enemies/enemy.orc_l11_11_canyon.south.png new file mode 100644 index 0000000..dd63a72 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l11_11_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l11_11_swamp.south.png b/frontend/assets/enemies/enemy.orc_l11_11_swamp.south.png new file mode 100644 index 0000000..8a528d9 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l11_11_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l12_12_astral.south.png b/frontend/assets/enemies/enemy.orc_l12_12_astral.south.png new file mode 100644 index 0000000..b485aee Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l12_12_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l12_12_volcanic.south.png b/frontend/assets/enemies/enemy.orc_l12_12_volcanic.south.png new file mode 100644 index 0000000..44942e4 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l12_12_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l5_6_forest.south.png b/frontend/assets/enemies/enemy.orc_l5_6_forest.south.png new file mode 100644 index 0000000..fc790ed Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l5_6_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l5_6_meadow.south.png b/frontend/assets/enemies/enemy.orc_l5_6_meadow.south.png new file mode 100644 index 0000000..8234eb3 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l5_6_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l7_8_forest.south.png b/frontend/assets/enemies/enemy.orc_l7_8_forest.south.png new file mode 100644 index 0000000..87ab389 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l7_8_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l7_8_ruins.south.png b/frontend/assets/enemies/enemy.orc_l7_8_ruins.south.png new file mode 100644 index 0000000..e10d245 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l7_8_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l9_10_canyon.south.png b/frontend/assets/enemies/enemy.orc_l9_10_canyon.south.png new file mode 100644 index 0000000..592d090 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l9_10_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.orc_l9_10_ruins.south.png b/frontend/assets/enemies/enemy.orc_l9_10_ruins.south.png new file mode 100644 index 0000000..2a861c5 Binary files /dev/null and b/frontend/assets/enemies/enemy.orc_l9_10_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l10_12_forest.south.png b/frontend/assets/enemies/enemy.shade_l10_12_forest.south.png new file mode 100644 index 0000000..c32dacd Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l10_12_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l10_12_meadow.south.png b/frontend/assets/enemies/enemy.shade_l10_12_meadow.south.png new file mode 100644 index 0000000..f372c9c Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l10_12_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l13_15_forest.south.png b/frontend/assets/enemies/enemy.shade_l13_15_forest.south.png new file mode 100644 index 0000000..428cfff Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l13_15_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l13_15_ruins.south.png b/frontend/assets/enemies/enemy.shade_l13_15_ruins.south.png new file mode 100644 index 0000000..e743c0a Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l13_15_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l16_18_canyon.south.png b/frontend/assets/enemies/enemy.shade_l16_18_canyon.south.png new file mode 100644 index 0000000..9d77349 Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l16_18_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l16_18_ruins.south.png b/frontend/assets/enemies/enemy.shade_l16_18_ruins.south.png new file mode 100644 index 0000000..36c566f Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l16_18_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l19_20_canyon.south.png b/frontend/assets/enemies/enemy.shade_l19_20_canyon.south.png new file mode 100644 index 0000000..9f6e921 Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l19_20_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l19_20_swamp.south.png b/frontend/assets/enemies/enemy.shade_l19_20_swamp.south.png new file mode 100644 index 0000000..c9b29bf Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l19_20_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l21_22_astral.south.png b/frontend/assets/enemies/enemy.shade_l21_22_astral.south.png new file mode 100644 index 0000000..25e71b9 Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l21_22_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.shade_l21_22_volcanic.south.png b/frontend/assets/enemies/enemy.shade_l21_22_volcanic.south.png new file mode 100644 index 0000000..b100ad2 Binary files /dev/null and b/frontend/assets/enemies/enemy.shade_l21_22_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l15_17_forest.south.png b/frontend/assets/enemies/enemy.skeleton_king_l15_17_forest.south.png new file mode 100644 index 0000000..7612fad Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l15_17_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l15_17_meadow.south.png b/frontend/assets/enemies/enemy.skeleton_king_l15_17_meadow.south.png new file mode 100644 index 0000000..9ffa762 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l15_17_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l18_19_forest.south.png b/frontend/assets/enemies/enemy.skeleton_king_l18_19_forest.south.png new file mode 100644 index 0000000..2e0e91e Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l18_19_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l18_19_ruins.south.png b/frontend/assets/enemies/enemy.skeleton_king_l18_19_ruins.south.png new file mode 100644 index 0000000..a8ce9d0 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l18_19_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l20_21_canyon.south.png b/frontend/assets/enemies/enemy.skeleton_king_l20_21_canyon.south.png new file mode 100644 index 0000000..6e3c0c5 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l20_21_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l20_21_ruins.south.png b/frontend/assets/enemies/enemy.skeleton_king_l20_21_ruins.south.png new file mode 100644 index 0000000..ac5d0dd Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l20_21_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l22_23_canyon.south.png b/frontend/assets/enemies/enemy.skeleton_king_l22_23_canyon.south.png new file mode 100644 index 0000000..02da912 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l22_23_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l22_23_swamp.south.png b/frontend/assets/enemies/enemy.skeleton_king_l22_23_swamp.south.png new file mode 100644 index 0000000..c44fbf4 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l22_23_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l24_25_astral.south.png b/frontend/assets/enemies/enemy.skeleton_king_l24_25_astral.south.png new file mode 100644 index 0000000..125b639 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l24_25_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_king_l24_25_volcanic.south.png b/frontend/assets/enemies/enemy.skeleton_king_l24_25_volcanic.south.png new file mode 100644 index 0000000..245a1d2 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_king_l24_25_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l10_11_canyon.south.png b/frontend/assets/enemies/enemy.skeleton_l10_11_canyon.south.png new file mode 100644 index 0000000..af12a31 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l10_11_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l10_11_ruins.south.png b/frontend/assets/enemies/enemy.skeleton_l10_11_ruins.south.png new file mode 100644 index 0000000..3cf7eaf Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l10_11_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l12_13_canyon.south.png b/frontend/assets/enemies/enemy.skeleton_l12_13_canyon.south.png new file mode 100644 index 0000000..095e908 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l12_13_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l12_13_swamp.south.png b/frontend/assets/enemies/enemy.skeleton_l12_13_swamp.south.png new file mode 100644 index 0000000..a2b31d3 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l12_13_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l14_14_astral.south.png b/frontend/assets/enemies/enemy.skeleton_l14_14_astral.south.png new file mode 100644 index 0000000..3174e7b Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l14_14_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l14_14_volcanic.south.png b/frontend/assets/enemies/enemy.skeleton_l14_14_volcanic.south.png new file mode 100644 index 0000000..9a43940 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l14_14_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l6_7_forest.south.png b/frontend/assets/enemies/enemy.skeleton_l6_7_forest.south.png new file mode 100644 index 0000000..12697fd Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l6_7_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l6_7_meadow.south.png b/frontend/assets/enemies/enemy.skeleton_l6_7_meadow.south.png new file mode 100644 index 0000000..4b7cd76 Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l6_7_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l8_9_forest.south.png b/frontend/assets/enemies/enemy.skeleton_l8_9_forest.south.png new file mode 100644 index 0000000..3fa046a Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l8_9_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.skeleton_l8_9_ruins.south.png b/frontend/assets/enemies/enemy.skeleton_l8_9_ruins.south.png new file mode 100644 index 0000000..2dd4ebd Binary files /dev/null and b/frontend/assets/enemies/enemy.skeleton_l8_9_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l4_5_forest.south.png b/frontend/assets/enemies/enemy.spider_l4_5_forest.south.png new file mode 100644 index 0000000..960365c Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l4_5_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l4_5_meadow.south.png b/frontend/assets/enemies/enemy.spider_l4_5_meadow.south.png new file mode 100644 index 0000000..e0b7c3c Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l4_5_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l6_6_forest.south.png b/frontend/assets/enemies/enemy.spider_l6_6_forest.south.png new file mode 100644 index 0000000..0493166 Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l6_6_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l6_6_ruins.south.png b/frontend/assets/enemies/enemy.spider_l6_6_ruins.south.png new file mode 100644 index 0000000..e55674b Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l6_6_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l7_7_canyon.south.png b/frontend/assets/enemies/enemy.spider_l7_7_canyon.south.png new file mode 100644 index 0000000..bcb0c9d Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l7_7_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l7_7_ruins.south.png b/frontend/assets/enemies/enemy.spider_l7_7_ruins.south.png new file mode 100644 index 0000000..18b0698 Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l7_7_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l8_8_canyon.south.png b/frontend/assets/enemies/enemy.spider_l8_8_canyon.south.png new file mode 100644 index 0000000..c3f7a00 Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l8_8_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l8_8_swamp.south.png b/frontend/assets/enemies/enemy.spider_l8_8_swamp.south.png new file mode 100644 index 0000000..e4b76df Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l8_8_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l9_9_astral.south.png b/frontend/assets/enemies/enemy.spider_l9_9_astral.south.png new file mode 100644 index 0000000..2ca8442 Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l9_9_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.spider_l9_9_volcanic.south.png b/frontend/assets/enemies/enemy.spider_l9_9_volcanic.south.png new file mode 100644 index 0000000..c546e37 Binary files /dev/null and b/frontend/assets/enemies/enemy.spider_l9_9_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l25_27_forest.south.png b/frontend/assets/enemies/enemy.titan_l25_27_forest.south.png new file mode 100644 index 0000000..46306b2 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l25_27_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l25_27_meadow.south.png b/frontend/assets/enemies/enemy.titan_l25_27_meadow.south.png new file mode 100644 index 0000000..226a893 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l25_27_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l28_29_forest.south.png b/frontend/assets/enemies/enemy.titan_l28_29_forest.south.png new file mode 100644 index 0000000..f2c54a5 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l28_29_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l28_29_ruins.south.png b/frontend/assets/enemies/enemy.titan_l28_29_ruins.south.png new file mode 100644 index 0000000..20c3943 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l28_29_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l30_31_canyon.south.png b/frontend/assets/enemies/enemy.titan_l30_31_canyon.south.png new file mode 100644 index 0000000..5dfaf55 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l30_31_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l30_31_ruins.south.png b/frontend/assets/enemies/enemy.titan_l30_31_ruins.south.png new file mode 100644 index 0000000..229ba14 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l30_31_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l32_33_canyon.south.png b/frontend/assets/enemies/enemy.titan_l32_33_canyon.south.png new file mode 100644 index 0000000..6fb32be Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l32_33_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l32_33_swamp.south.png b/frontend/assets/enemies/enemy.titan_l32_33_swamp.south.png new file mode 100644 index 0000000..d8ff8fe Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l32_33_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l34_35_astral.south.png b/frontend/assets/enemies/enemy.titan_l34_35_astral.south.png new file mode 100644 index 0000000..cfa6130 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l34_35_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.titan_l34_35_volcanic.south.png b/frontend/assets/enemies/enemy.titan_l34_35_volcanic.south.png new file mode 100644 index 0000000..078b7b3 Binary files /dev/null and b/frontend/assets/enemies/enemy.titan_l34_35_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l18_20_forest.south.png b/frontend/assets/enemies/enemy.treant_l18_20_forest.south.png new file mode 100644 index 0000000..3ca4eaf Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l18_20_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l18_20_meadow.south.png b/frontend/assets/enemies/enemy.treant_l18_20_meadow.south.png new file mode 100644 index 0000000..1be780b Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l18_20_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l21_23_forest.south.png b/frontend/assets/enemies/enemy.treant_l21_23_forest.south.png new file mode 100644 index 0000000..93aeb7f Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l21_23_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l21_23_ruins.south.png b/frontend/assets/enemies/enemy.treant_l21_23_ruins.south.png new file mode 100644 index 0000000..9fcb55d Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l21_23_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l24_26_canyon.south.png b/frontend/assets/enemies/enemy.treant_l24_26_canyon.south.png new file mode 100644 index 0000000..95dff80 Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l24_26_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l24_26_ruins.south.png b/frontend/assets/enemies/enemy.treant_l24_26_ruins.south.png new file mode 100644 index 0000000..febdd47 Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l24_26_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l27_28_canyon.south.png b/frontend/assets/enemies/enemy.treant_l27_28_canyon.south.png new file mode 100644 index 0000000..792964a Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l27_28_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l27_28_swamp.south.png b/frontend/assets/enemies/enemy.treant_l27_28_swamp.south.png new file mode 100644 index 0000000..1fafd42 Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l27_28_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l29_30_astral.south.png b/frontend/assets/enemies/enemy.treant_l29_30_astral.south.png new file mode 100644 index 0000000..a705131 Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l29_30_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.treant_l29_30_volcanic.south.png b/frontend/assets/enemies/enemy.treant_l29_30_volcanic.south.png new file mode 100644 index 0000000..991adb3 Binary files /dev/null and b/frontend/assets/enemies/enemy.treant_l29_30_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l11_12_canyon.south.png b/frontend/assets/enemies/enemy.wraith_l11_12_canyon.south.png new file mode 100644 index 0000000..726def7 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l11_12_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l11_12_swamp.south.png b/frontend/assets/enemies/enemy.wraith_l11_12_swamp.south.png new file mode 100644 index 0000000..93503d4 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l11_12_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l13_14_astral.south.png b/frontend/assets/enemies/enemy.wraith_l13_14_astral.south.png new file mode 100644 index 0000000..500e6e6 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l13_14_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l13_14_volcanic.south.png b/frontend/assets/enemies/enemy.wraith_l13_14_volcanic.south.png new file mode 100644 index 0000000..2cab778 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l13_14_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l5_6_forest.south.png b/frontend/assets/enemies/enemy.wraith_l5_6_forest.south.png new file mode 100644 index 0000000..8c5c10e Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l5_6_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l5_6_meadow.south.png b/frontend/assets/enemies/enemy.wraith_l5_6_meadow.south.png new file mode 100644 index 0000000..c37fed0 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l5_6_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l7_8_forest.south.png b/frontend/assets/enemies/enemy.wraith_l7_8_forest.south.png new file mode 100644 index 0000000..b631385 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l7_8_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l7_8_ruins.south.png b/frontend/assets/enemies/enemy.wraith_l7_8_ruins.south.png new file mode 100644 index 0000000..5624886 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l7_8_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l9_10_canyon.south.png b/frontend/assets/enemies/enemy.wraith_l9_10_canyon.south.png new file mode 100644 index 0000000..a16db96 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l9_10_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.wraith_l9_10_ruins.south.png b/frontend/assets/enemies/enemy.wraith_l9_10_ruins.south.png new file mode 100644 index 0000000..9da9665 Binary files /dev/null and b/frontend/assets/enemies/enemy.wraith_l9_10_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l12_14_forest.south.png b/frontend/assets/enemies/enemy.wyvern_l12_14_forest.south.png new file mode 100644 index 0000000..4fe40c5 Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l12_14_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l12_14_meadow.south.png b/frontend/assets/enemies/enemy.wyvern_l12_14_meadow.south.png new file mode 100644 index 0000000..8f94cfc Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l12_14_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l15_17_forest.south.png b/frontend/assets/enemies/enemy.wyvern_l15_17_forest.south.png new file mode 100644 index 0000000..8f9defc Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l15_17_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l15_17_ruins.south.png b/frontend/assets/enemies/enemy.wyvern_l15_17_ruins.south.png new file mode 100644 index 0000000..58923fe Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l15_17_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l18_20_canyon.south.png b/frontend/assets/enemies/enemy.wyvern_l18_20_canyon.south.png new file mode 100644 index 0000000..1c50984 Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l18_20_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l18_20_ruins.south.png b/frontend/assets/enemies/enemy.wyvern_l18_20_ruins.south.png new file mode 100644 index 0000000..2de0970 Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l18_20_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l21_22_canyon.south.png b/frontend/assets/enemies/enemy.wyvern_l21_22_canyon.south.png new file mode 100644 index 0000000..6e6bb6f Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l21_22_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l21_22_swamp.south.png b/frontend/assets/enemies/enemy.wyvern_l21_22_swamp.south.png new file mode 100644 index 0000000..7cf8e38 Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l21_22_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l23_24_astral.south.png b/frontend/assets/enemies/enemy.wyvern_l23_24_astral.south.png new file mode 100644 index 0000000..1af0b0b Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l23_24_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.wyvern_l23_24_volcanic.south.png b/frontend/assets/enemies/enemy.wyvern_l23_24_volcanic.south.png new file mode 100644 index 0000000..b47d5e2 Binary files /dev/null and b/frontend/assets/enemies/enemy.wyvern_l23_24_volcanic.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l3_4_forest.south.png b/frontend/assets/enemies/enemy.zombie_l3_4_forest.south.png new file mode 100644 index 0000000..f10a83f Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l3_4_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l3_4_meadow.south.png b/frontend/assets/enemies/enemy.zombie_l3_4_meadow.south.png new file mode 100644 index 0000000..cc9d36e Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l3_4_meadow.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l5_5_forest.south.png b/frontend/assets/enemies/enemy.zombie_l5_5_forest.south.png new file mode 100644 index 0000000..5d026cd Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l5_5_forest.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l5_5_ruins.south.png b/frontend/assets/enemies/enemy.zombie_l5_5_ruins.south.png new file mode 100644 index 0000000..230e4aa Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l5_5_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l6_6_canyon.south.png b/frontend/assets/enemies/enemy.zombie_l6_6_canyon.south.png new file mode 100644 index 0000000..b67d564 Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l6_6_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l6_6_ruins.south.png b/frontend/assets/enemies/enemy.zombie_l6_6_ruins.south.png new file mode 100644 index 0000000..b8e2387 Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l6_6_ruins.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l7_7_canyon.south.png b/frontend/assets/enemies/enemy.zombie_l7_7_canyon.south.png new file mode 100644 index 0000000..ec35cfc Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l7_7_canyon.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l7_7_swamp.south.png b/frontend/assets/enemies/enemy.zombie_l7_7_swamp.south.png new file mode 100644 index 0000000..f8a2725 Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l7_7_swamp.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l8_8_astral.south.png b/frontend/assets/enemies/enemy.zombie_l8_8_astral.south.png new file mode 100644 index 0000000..d29613d Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l8_8_astral.south.png differ diff --git a/frontend/assets/enemies/enemy.zombie_l8_8_volcanic.south.png b/frontend/assets/enemies/enemy.zombie_l8_8_volcanic.south.png new file mode 100644 index 0000000..7e39d88 Binary files /dev/null and b/frontend/assets/enemies/enemy.zombie_l8_8_volcanic.south.png differ diff --git a/frontend/assets/obj/obj.wall.cobble_e.v1.png b/frontend/assets/obj/obj.wall.cobble_e.v1.png new file mode 100644 index 0000000..d7ce28d Binary files /dev/null and b/frontend/assets/obj/obj.wall.cobble_e.v1.png differ diff --git a/frontend/assets/obj/obj.wall.cobble_n.v1.png b/frontend/assets/obj/obj.wall.cobble_n.v1.png new file mode 100644 index 0000000..1b0aa54 Binary files /dev/null and b/frontend/assets/obj/obj.wall.cobble_n.v1.png differ diff --git a/frontend/assets/obj/obj.wall.cobble_ne.v1.png b/frontend/assets/obj/obj.wall.cobble_ne.v1.png new file mode 100644 index 0000000..b23d0ad Binary files /dev/null and b/frontend/assets/obj/obj.wall.cobble_ne.v1.png differ diff --git a/frontend/assets/prop/prop.fountain.plaza.v0.png b/frontend/assets/prop/prop.fountain.plaza.v0.png new file mode 100644 index 0000000..79e11d4 Binary files /dev/null and b/frontend/assets/prop/prop.fountain.plaza.v0.png differ diff --git a/frontend/assets/prop/prop.market.stall.v0.png b/frontend/assets/prop/prop.market.stall.v0.png new file mode 100644 index 0000000..2ee559b Binary files /dev/null and b/frontend/assets/prop/prop.market.stall.v0.png differ diff --git a/frontend/package.json b/frontend/package.json index ca901e3..508d713 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,7 +8,7 @@ "build": "tsc -b && vite build", "preview": "vite preview", "lint": "eslint src --ext .ts,.tsx --max-warnings 0", - "gen:enemy-south:pixellab": "node scripts/pixellab-enemy-south-batch.mjs" + "gen:enemy-south:pixellab": "node ../scripts/pixellab-fill-missing-south-one-shot.mjs --delay-between-creates-sec=10 --post-queue-wait-sec=110 --concurrency=10" }, "dependencies": { "pixi.js": "^8.6.6", diff --git a/frontend/public/assets/game/manifest.json b/frontend/public/assets/game/manifest.json index 9f5427f..a1f3b35 100644 --- a/frontend/public/assets/game/manifest.json +++ b/frontend/public/assets/game/manifest.json @@ -1,5 +1,5 @@ { - "version": 36, + "version": 82, "assetsRoot": "frontend/assets", "note": "file paths relative to frontend/assets. Rest camp: prop.camp_tent/fire/bag.v0 (wild rest). Other props + heroes + NPC.", "textures": { @@ -293,6 +293,36 @@ "kind": "map_object", "pixellabObjectId": "84f736b6-12dd-4d94-93e5-d65dad6b2c42" }, + "building.house.v2": { + "file": "building/building.house.v2.png", + "kind": "map_object", + "pixellabObjectId": "7f3d48e7-2246-4f00-86e3-1fcbd76e40fe" + }, + "building.tavern.v0": { + "file": "building/building.tavern.v0.png", + "kind": "map_object", + "pixellabObjectId": "270d54a9-f589-4a0d-ab65-02979de68fed" + }, + "building.tavern.v1": { + "file": "building/building.tavern.v1.png", + "kind": "map_object", + "pixellabObjectId": "4c04957a-bee8-4606-9969-9e23fd52984f" + }, + "building.civic.townhall.v0": { + "file": "building/building.civic.townhall.v0.png", + "kind": "map_object", + "pixellabObjectId": "f759c92a-0d07-40fc-9604-a0d4b6f6ea98" + }, + "prop.fountain.plaza.v0": { + "file": "prop/prop.fountain.plaza.v0.png", + "kind": "map_object", + "pixellabObjectId": "1b1eddc0-d238-48d6-8fb9-6ed2fd4cf581" + }, + "prop.market.stall.v0": { + "file": "prop/prop.market.stall.v0.png", + "kind": "map_object", + "pixellabObjectId": "cca3bea4-0caa-49fd-ba70-75c5a2f36cce" + }, "enemy.wolf": { "file": "enemies/enemy.wolf.png", "kind": "map_object", @@ -803,102 +833,122 @@ "enemy.orc_l5_6_meadow.south": { "file": "enemies/enemy.orc_l5_6_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "b67f475b-6cbc-4797-9cbe-cecf9d13b3d0" }, "enemy.orc_l5_6_forest.south": { "file": "enemies/enemy.orc_l5_6_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "971e9081-fd8f-4baf-9af1-c1e497b24bd3" }, "enemy.orc_l7_8_forest.south": { "file": "enemies/enemy.orc_l7_8_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "a00b39c7-f595-4ce5-9e6e-32255e891ea8" }, "enemy.orc_l7_8_ruins.south": { "file": "enemies/enemy.orc_l7_8_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "9bbbf755-1207-45ea-aaf8-4d234e3df161" }, "enemy.orc_l9_10_ruins.south": { "file": "enemies/enemy.orc_l9_10_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "9cb7d0f4-a1e0-4ffc-b042-89fd9f893e86" }, "enemy.orc_l9_10_canyon.south": { "file": "enemies/enemy.orc_l9_10_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "4d51531f-a408-452d-bffa-e85977803ed1" }, "enemy.orc_l11_11_canyon.south": { "file": "enemies/enemy.orc_l11_11_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "b5c70457-a980-40d8-aec3-04fbf9b4c997" }, "enemy.orc_l11_11_swamp.south": { "file": "enemies/enemy.orc_l11_11_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "e9a02115-4e0e-4e1f-9bdf-4b84e44cb1c3" }, "enemy.orc_l12_12_volcanic.south": { "file": "enemies/enemy.orc_l12_12_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "ad4400eb-5668-4eb1-810b-233cfcfa892d" }, "enemy.orc_l12_12_astral.south": { "file": "enemies/enemy.orc_l12_12_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "f5f0e841-45e1-40d2-aee5-ef4c245f3302" }, "enemy.skeleton_l6_7_meadow.south": { "file": "enemies/enemy.skeleton_l6_7_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "8fc3dc27-76be-4ba2-8b92-b93a16e376a3" }, "enemy.skeleton_l6_7_forest.south": { "file": "enemies/enemy.skeleton_l6_7_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "11939a86-ae18-488e-85a5-4bc8ed209065" }, "enemy.skeleton_l8_9_forest.south": { "file": "enemies/enemy.skeleton_l8_9_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "ee8c4632-419f-47f6-9885-963e5e290e99" }, "enemy.skeleton_l8_9_ruins.south": { "file": "enemies/enemy.skeleton_l8_9_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "d07a0a85-df81-4afb-a365-cc1605bc4cc0" }, "enemy.skeleton_l10_11_ruins.south": { "file": "enemies/enemy.skeleton_l10_11_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "13a0b778-3221-4ee5-a0f9-0bdf2ddc9028" }, "enemy.skeleton_l10_11_canyon.south": { "file": "enemies/enemy.skeleton_l10_11_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "eab47bfb-2674-490a-b526-004323c2d40d" }, "enemy.skeleton_l12_13_canyon.south": { "file": "enemies/enemy.skeleton_l12_13_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "82352e7d-b422-4136-88d3-ddbf1f353f31" }, "enemy.skeleton_l12_13_swamp.south": { "file": "enemies/enemy.skeleton_l12_13_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "61b35aed-b370-427c-8498-a3bbc029d327" }, "enemy.skeleton_l14_14_volcanic.south": { "file": "enemies/enemy.skeleton_l14_14_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "20d4b822-dbdf-4fbd-91da-9bd210ceb0cb" }, "enemy.skeleton_l14_14_astral.south": { "file": "enemies/enemy.skeleton_l14_14_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "ed536f83-2e26-4c0d-b8b2-b35147e08d56" }, "enemy.battle_lizard_l7_8_meadow.south": { "file": "enemies/enemy.battle_lizard_l7_8_meadow.south.png", @@ -1083,52 +1133,62 @@ "enemy.skeleton_king_l15_17_meadow.south": { "file": "enemies/enemy.skeleton_king_l15_17_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "454f2319-1a29-4a34-95ac-75644be8e650" }, "enemy.skeleton_king_l15_17_forest.south": { "file": "enemies/enemy.skeleton_king_l15_17_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "8db28b02-31b5-4d54-a34b-81f38522b465" }, "enemy.skeleton_king_l18_19_forest.south": { "file": "enemies/enemy.skeleton_king_l18_19_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "fb455528-045e-49f0-9c60-b023f5e40cf9" }, "enemy.skeleton_king_l18_19_ruins.south": { "file": "enemies/enemy.skeleton_king_l18_19_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "e0aa8fd3-af5b-4db8-a697-59e5afc0f874" }, "enemy.skeleton_king_l20_21_ruins.south": { "file": "enemies/enemy.skeleton_king_l20_21_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "d2b0c94f-9f4b-4600-b46b-e636847934b6" }, "enemy.skeleton_king_l20_21_canyon.south": { "file": "enemies/enemy.skeleton_king_l20_21_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "6d56d508-3d38-4b3d-9166-9de7cbe806fa" }, "enemy.skeleton_king_l22_23_canyon.south": { "file": "enemies/enemy.skeleton_king_l22_23_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "67d25f77-b5fd-4e0f-9122-b9090486a8cc" }, "enemy.skeleton_king_l22_23_swamp.south": { "file": "enemies/enemy.skeleton_king_l22_23_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "db1e82e0-92d3-4952-b5da-a067faf5f482" }, "enemy.skeleton_king_l24_25_volcanic.south": { "file": "enemies/enemy.skeleton_king_l24_25_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "ba5b3c64-a890-418c-b5d9-7cbb6d390e30" }, "enemy.skeleton_king_l24_25_astral.south": { "file": "enemies/enemy.skeleton_king_l24_25_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "b25f2506-82ed-4043-b332-5399f773e0a2" }, "enemy.forest_warden_l20_22_meadow.south": { "file": "enemies/enemy.forest_warden_l20_22_meadow.south.png", @@ -1313,52 +1373,62 @@ "enemy.wraith_l5_6_meadow.south": { "file": "enemies/enemy.wraith_l5_6_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "f5a1b06a-2d42-4f99-af6b-1fa3908bdbba" }, "enemy.wraith_l5_6_forest.south": { "file": "enemies/enemy.wraith_l5_6_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "d48aefc7-1b6c-474c-bede-5406d55a1e6a" }, "enemy.wraith_l7_8_forest.south": { "file": "enemies/enemy.wraith_l7_8_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "84e899b0-333b-412d-ac11-ae5d1feee7a9" }, "enemy.wraith_l7_8_ruins.south": { "file": "enemies/enemy.wraith_l7_8_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "cd0f0091-949b-486d-a485-914a4fcc9681" }, "enemy.wraith_l9_10_ruins.south": { "file": "enemies/enemy.wraith_l9_10_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "dff7f23c-aed9-4389-a6f7-d1859e4e7b79" }, "enemy.wraith_l9_10_canyon.south": { "file": "enemies/enemy.wraith_l9_10_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "a50aa266-b41d-4d5d-96ac-855a9df57366" }, "enemy.wraith_l11_12_canyon.south": { "file": "enemies/enemy.wraith_l11_12_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "bf93d8f9-6529-4c5c-bf8f-ed8b9384644e" }, "enemy.wraith_l11_12_swamp.south": { "file": "enemies/enemy.wraith_l11_12_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "bc392fcf-12ba-4145-a58a-097884fd88c1" }, "enemy.wraith_l13_14_volcanic.south": { "file": "enemies/enemy.wraith_l13_14_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "6bd297e8-22c9-43c6-8fca-503142d2e0bf" }, "enemy.wraith_l13_14_astral.south": { "file": "enemies/enemy.wraith_l13_14_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "c6b423e9-680d-461c-9635-68e11ab789d0" }, "enemy.bandit_l4_5_meadow.south": { "file": "enemies/enemy.bandit_l4_5_meadow.south.png", @@ -1483,52 +1553,62 @@ "enemy.treant_l18_20_meadow.south": { "file": "enemies/enemy.treant_l18_20_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "b308070a-5139-4129-ae50-df69b8c03fa4" }, "enemy.treant_l18_20_forest.south": { "file": "enemies/enemy.treant_l18_20_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "9ac4c6d7-44a3-41b2-9186-b1ad304b853f" }, "enemy.treant_l21_23_forest.south": { "file": "enemies/enemy.treant_l21_23_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "9bcc26e5-2c16-4049-9208-5b10b1f99328" }, "enemy.treant_l21_23_ruins.south": { "file": "enemies/enemy.treant_l21_23_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "15eac363-fbbc-48fb-9b70-9e910251102c" }, "enemy.treant_l24_26_ruins.south": { "file": "enemies/enemy.treant_l24_26_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "492548d4-b3c2-438a-80fc-811c8166c83e" }, "enemy.treant_l24_26_canyon.south": { "file": "enemies/enemy.treant_l24_26_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "20722805-7ac5-4d3f-93d2-5d2225e08a1b" }, "enemy.treant_l27_28_canyon.south": { "file": "enemies/enemy.treant_l27_28_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "61a64581-c257-4aa5-8261-c57116852897" }, "enemy.treant_l27_28_swamp.south": { "file": "enemies/enemy.treant_l27_28_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "ec51a64c-3218-49ba-9243-3b85c163ff9b" }, "enemy.treant_l29_30_volcanic.south": { "file": "enemies/enemy.treant_l29_30_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "e89b7ef1-af39-415d-9ac8-2fc50cf85938" }, "enemy.treant_l29_30_astral.south": { "file": "enemies/enemy.treant_l29_30_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "6f238b2e-588b-4edd-84a9-c9efae04e81d" }, "enemy.basilisk_l9_11_meadow.south": { "file": "enemies/enemy.basilisk_l9_11_meadow.south.png", @@ -1593,52 +1673,62 @@ "enemy.wyvern_l12_14_meadow.south": { "file": "enemies/enemy.wyvern_l12_14_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "c46f6ff2-b627-4869-8ce7-55de07ac22a0" }, "enemy.wyvern_l12_14_forest.south": { "file": "enemies/enemy.wyvern_l12_14_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "a0ea253d-d2fe-4639-8d24-b15b2e92b00f" }, "enemy.wyvern_l15_17_forest.south": { "file": "enemies/enemy.wyvern_l15_17_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "0ba2e3fe-600c-468a-a849-9f9c909192a8" }, "enemy.wyvern_l15_17_ruins.south": { "file": "enemies/enemy.wyvern_l15_17_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "00024d15-2ee7-4d29-b37e-08b6cdc581d9" }, "enemy.wyvern_l18_20_ruins.south": { "file": "enemies/enemy.wyvern_l18_20_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "f9f75ce9-9fa4-4ebb-8bb6-2b6ce6e71dbd" }, "enemy.wyvern_l18_20_canyon.south": { "file": "enemies/enemy.wyvern_l18_20_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "0981e890-6b50-4b57-95bf-3ee13cbfed89" }, "enemy.wyvern_l21_22_canyon.south": { "file": "enemies/enemy.wyvern_l21_22_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "935269c5-cc37-45c5-b6c3-095e9bf4d5cb" }, "enemy.wyvern_l21_22_swamp.south": { "file": "enemies/enemy.wyvern_l21_22_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "dadf2135-4a49-4d9a-ae5c-a0ea1afa73a2" }, "enemy.wyvern_l23_24_volcanic.south": { "file": "enemies/enemy.wyvern_l23_24_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "c5cdbdb2-1599-4d25-8810-0b6992f637b1" }, "enemy.wyvern_l23_24_astral.south": { "file": "enemies/enemy.wyvern_l23_24_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "f446d3f3-ebe3-45e2-9396-ce37e0a4b453" }, "enemy.harpy_l6_7_meadow.south": { "file": "enemies/enemy.harpy_l6_7_meadow.south.png", @@ -1727,82 +1817,98 @@ "enemy.manticore_l20_22_ruins.south": { "file": "enemies/enemy.manticore_l20_22_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "29ae1396-2790-41f8-b700-893279862c92" }, "enemy.manticore_l20_22_canyon.south": { "file": "enemies/enemy.manticore_l20_22_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "5e09a97a-5af4-4e9c-8603-f26013782e60" }, "enemy.manticore_l23_24_canyon.south": { "file": "enemies/enemy.manticore_l23_24_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "da3e608f-1b6e-4ee4-9314-79faec5a2f8c" }, "enemy.manticore_l23_24_swamp.south": { "file": "enemies/enemy.manticore_l23_24_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "e79212e1-a02a-44ca-86ad-6db71654e610" }, "enemy.manticore_l25_26_volcanic.south": { "file": "enemies/enemy.manticore_l25_26_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "147a970c-67c2-4c89-8230-2684c236055b" }, "enemy.manticore_l25_26_astral.south": { "file": "enemies/enemy.manticore_l25_26_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "1fe8ae19-5236-4924-9132-9474b27d7077" }, "enemy.shade_l10_12_meadow.south": { "file": "enemies/enemy.shade_l10_12_meadow.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "55cd16f4-16f4-4be1-8db3-16932b6f7592" }, "enemy.shade_l10_12_forest.south": { "file": "enemies/enemy.shade_l10_12_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "8c8792d4-5dd3-41a8-97a5-64b500fce5fa" }, "enemy.shade_l13_15_forest.south": { "file": "enemies/enemy.shade_l13_15_forest.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "60197732-5382-4460-aed0-ae48588236df" }, "enemy.shade_l13_15_ruins.south": { "file": "enemies/enemy.shade_l13_15_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "d6322a1d-053c-45d4-b08b-247c12bb95af" }, "enemy.shade_l16_18_ruins.south": { "file": "enemies/enemy.shade_l16_18_ruins.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "96de793f-1bfd-4886-aaee-9f42a57b0303" }, "enemy.shade_l16_18_canyon.south": { "file": "enemies/enemy.shade_l16_18_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "b1f5b523-4ae4-4498-8771-f15e3c6ba11a" }, "enemy.shade_l19_20_canyon.south": { "file": "enemies/enemy.shade_l19_20_canyon.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "d7a33c79-2216-491f-950f-80655ef6135c" }, "enemy.shade_l19_20_swamp.south": { "file": "enemies/enemy.shade_l19_20_swamp.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "395addb8-1d91-4c54-a213-c9b08ce50aae" }, "enemy.shade_l21_22_volcanic.south": { "file": "enemies/enemy.shade_l21_22_volcanic.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "c053841f-6fe2-4466-bf8c-b4fcdd6d97ca" }, "enemy.shade_l21_22_astral.south": { "file": "enemies/enemy.shade_l21_22_astral.south.png", "kind": "map_object", - "rotation": "south" + "rotation": "south", + "pixellabObjectId": "c71f839d-a7bd-4685-ba9b-1e9c2837973e" } } } diff --git a/frontend/src/game/assets/spriteMapping.ts b/frontend/src/game/assets/spriteMapping.ts index cbc7ba5..bde10bf 100644 --- a/frontend/src/game/assets/spriteMapping.ts +++ b/frontend/src/game/assets/spriteMapping.ts @@ -45,15 +45,24 @@ const NPC_TEXTURE_BY_TYPE: Record = { quest_giver: 'npc.quest_giver', }; +/** Town hall / civic building offset from plaza (not a `town_buildings` row). */ +export const TOWN_HALL_TEXTURE_KEY = 'building.civic.townhall.v0'; +/** Plaza center feature: DB uses `decoration.well` at (0,0) for the central water feature. */ +export const PLAZA_FOUNTAIN_TEXTURE_KEY = 'prop.fountain.plaza.v0'; +export const MARKET_STALL_TEXTURE_KEY = 'prop.market.stall.v0'; + const BUILDING_TEXTURE_BY_TYPE: Record = { - 'house.quest_giver': 'building.house.v1', - 'house.merchant': 'building.house.v1', + 'house.quest_giver': 'building.house.v0', + 'house.merchant': 'building.tavern.v0', 'house.armorer': 'building.house.v1', - 'house.weapon_smith': 'building.house.v1', - 'house.jeweler': 'building.house.v1', + 'house.weapon_smith': 'building.tavern.v1', + 'house.jeweler': 'building.house.v2', 'house.bounty_hunter': 'building.house.v1', - 'house.elder': 'building.house.v1', - 'house.healer': 'building.house.v1', + 'house.elder': 'building.house.v0', + 'house.healer': 'building.house.v2', + /** Town square feature — rendered as fountain art (see PLAZA_FOUNTAIN_TEXTURE_KEY). */ + 'decoration.well': PLAZA_FOUNTAIN_TEXTURE_KEY, + 'decoration.stall': MARKET_STALL_TEXTURE_KEY, }; export function terrainToTextureKey(terrain: string): string { @@ -104,6 +113,7 @@ export function getRequiredSpriteKeys(): string[] { ...objectKeys, ...npcKeys, ...buildingKeys, + TOWN_HALL_TEXTURE_KEY, ...HERO_MODEL_VARIANTS.flatMap((v) => [`hero.player.v${v}.south`, `hero.player.v${v}.north`]), CAMP_TENT_TEXTURE_KEY, CAMP_FIRE_TEXTURE_KEY, diff --git a/frontend/src/game/renderer.ts b/frontend/src/game/renderer.ts index de26b1e..633dd2b 100644 --- a/frontend/src/game/renderer.ts +++ b/frontend/src/game/renderer.ts @@ -8,11 +8,14 @@ import { GameSpriteRegistry } from './assets/gameSpriteRegistry'; import { buildingTypeToTextureKey, heroTextureKey, + MARKET_STALL_TEXTURE_KEY, npcTypeToTextureKey, objectToTextureKey, + PLAZA_FOUNTAIN_TEXTURE_KEY, restCampTextureKeys, enemySouthTextureKey, terrainToTextureKey, + TOWN_HALL_TEXTURE_KEY, } from './assets/spriteMapping'; /** @@ -1321,6 +1324,38 @@ export class GameRenderer { gfx.fill({ color: 0x6a5a3a, alpha: 0.7 }); } + /** + * Building-layer sprite (anchor bottom-center). Returns true if the texture was placed. + */ + private _placeBuildingLayerSprite( + poolKey: string, + textureKey: string, + bx: number, + by: number, + targetWidth: number, + usedBuildingSprites: Set, + ): boolean { + if (!this._spritesReady) return false; + const spriteTexture = this._spriteRegistry.getTexture(textureKey); + if (!spriteTexture) return false; + usedBuildingSprites.add(poolKey); + const entry = this._ensureSprite( + this._buildingSpritePool, + poolKey, + textureKey, + spriteTexture, + this._buildingSpriteLayer, + ); + entry.sprite.x = bx; + entry.sprite.y = by; + const texW = entry.sprite.texture.width || targetWidth; + const scaleFactor = texW > 0 ? targetWidth / texW : 1; + entry.sprite.scale.set(scaleFactor); + entry.sprite.zIndex = by; + entry.sprite.visible = true; + return true; + } + /** Draw a market stall structure for town variety. */ private _drawTownStall(gfx: Graphics, cx: number, cy: number, s: number): void { // Counter / table @@ -1375,6 +1410,12 @@ export class GameRenderer { const w = isNpcHouse ? Math.max(baseW * houseSizeBoost, 54 * scale) : baseW; const h = isNpcHouse ? Math.max(baseH * houseSizeBoost, 42 * scale) : baseH; const rh = isNpcHouse ? 32 * scale * 1.65 : 32 * scale; + const spriteDrawW = + bt === 'decoration.well' + ? Math.max(w, 56 * scale) + : bt === 'decoration.stall' + ? Math.max(w, 44 * scale) + : w; const spriteKey = this._spritesReady ? buildingTypeToTextureKey(bt) : null; const spriteTexture = spriteKey ? this._spriteRegistry.getTexture(spriteKey) : null; const hasUsableSprite = spriteKey !== null && spriteTexture !== null; @@ -1390,8 +1431,8 @@ export class GameRenderer { ); entry.sprite.x = bx; entry.sprite.y = by; - const texW = entry.sprite.texture.width || w; - const scaleFactor = texW > 0 ? w / texW : 1; + const texW = entry.sprite.texture.width || spriteDrawW; + const scaleFactor = texW > 0 ? spriteDrawW / texW : 1; entry.sprite.scale.set(scaleFactor); entry.sprite.zIndex = by; entry.sprite.visible = true; @@ -1427,22 +1468,29 @@ export class GameRenderer { } } + const twIcon = spriteTexture?.width ?? 0; + const thIcon = spriteTexture?.height ?? 0; + const iconYBase = + hasUsableSprite && twIcon > 0 + ? by - ((thIcon * spriteDrawW) / twIcon) * 0.9 + : by - h - rh * 0.45; + if (bt === 'house.quest_giver') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.5, '!', 0xffd700, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase - rh * 0.05, '!', 0xffd700, scale); } else if (bt === 'house.merchant') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.3, '$', 0x88dd88, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase + rh * 0.12, '$', 0x88dd88, scale); } else if (bt === 'house.armorer') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.4, 'A', 0xaaccff, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase, 'A', 0xaaccff, scale); } else if (bt === 'house.weapon_smith') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.35, 'W', 0xffaa66, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase + rh * 0.04, 'W', 0xffaa66, scale); } else if (bt === 'house.jeweler') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.45, 'J', 0xdd88ff, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase - rh * 0.06, 'J', 0xdd88ff, scale); } else if (bt === 'house.bounty_hunter') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.5, 'B', 0xffcc44, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase - rh * 0.05, 'B', 0xffcc44, scale); } else if (bt === 'house.elder') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.5, 'E', 0xeeddaa, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase - rh * 0.05, 'E', 0xeeddaa, scale); } else if (bt === 'house.healer') { - this._drawBuildingIcon(iconGfx, bx, by - h - rh * 0.5, '+', 0xff6666, scale); + this._drawBuildingIcon(iconGfx, bx, iconYBase - rh * 0.05, '+', 0xff6666, scale); } } } @@ -1554,6 +1602,7 @@ export class GameRenderer { private _drawProceduralBuildings( gfx: Graphics, tx: number, ty: number, s: number, spread: number, size: string, townSeed: number, + usedBuildingSprites: Set, townId: number, ): void { const houseCount = size === 'XS' ? 5 : size === 'S' ? 7 : size === 'M' ? 10 : 14; @@ -1596,12 +1645,22 @@ export class GameRenderer { for (let si = 0; si < stallCount; si++) { const stallAngle = (si + 0.5) * Math.PI + (townSeed & 0xf) * 0.1; const stallDist = spread * 0.42; - this._drawTownStall( - gfx, - tx + Math.cos(stallAngle) * stallDist, - ty + Math.sin(stallAngle) * stallDist * 0.5, - s * 0.9, - ); + const sx = tx + Math.cos(stallAngle) * stallDist; + const sy = ty + Math.sin(stallAngle) * stallDist * 0.5; + const stallW = 44 * s * 0.9; + const poolKey = `town:${townId}:proc_stall:${si}`; + if ( + !this._placeBuildingLayerSprite( + poolKey, + MARKET_STALL_TEXTURE_KEY, + sx, + sy, + stallW, + usedBuildingSprites, + ) + ) { + this._drawTownStall(gfx, sx, sy, s * 0.9); + } } } @@ -1681,18 +1740,11 @@ export class GameRenderer { } } - // --- Ground plane: tan/brown dirt ellipse --- + // --- Ground plane: tan/brown dirt ellipse (no extra transparent radius rings) --- const groundW = borderRadius * 0.85; const groundH = groundW * 0.5; gfx.ellipse(tx, ty, groundW, groundH); gfx.fill({ color: 0x8a7454, alpha: 0.35 }); - // Inner lighter patch - gfx.ellipse(tx, ty, groundW * 0.6, groundH * 0.6); - gfx.fill({ color: 0x9a8462, alpha: 0.2 }); - - // Glow circle behind town - gfx.circle(tx, ty, borderRadius * 0.6); - gfx.fill({ color: 0xdaa520, alpha: 0.04 }); // --- Central plaza (paving); well/fountain + civic sit on or beside it --- this._drawTownPlaza(gfx, tx, ty, groundW, groundH); @@ -1706,14 +1758,40 @@ export class GameRenderer { // --- Buildings: server-driven if available, fallback procedural --- if (town.buildings && town.buildings.length > 0) { this._drawServerBuildings(gfx, iconGfx, usedBuildingSprites, town.buildings, tx, ty, s); - this._drawCivicBuilding(gfx, civicScreen.x, civicScreen.y, s); } else { - this._drawProceduralBuildings(gfx, tx, ty, s, spread, town.size, townSeed); - if ((townSeed & 1) === 0) { - this._drawTownFountain(gfx, tx, ty, s); - } else { - this._drawTownWell(gfx, tx, ty, s); + this._drawProceduralBuildings( + gfx, tx, ty, s, spread, town.size, townSeed, usedBuildingSprites, town.id, + ); + const plazaKey = `town:${town.id}:plaza:center`; + if ( + !this._placeBuildingLayerSprite( + plazaKey, + PLAZA_FOUNTAIN_TEXTURE_KEY, + tx, + ty, + 56 * s, + usedBuildingSprites, + ) + ) { + if ((townSeed & 1) === 0) { + this._drawTownFountain(gfx, tx, ty, s); + } else { + this._drawTownWell(gfx, tx, ty, s); + } } + } + + const civicKey = `town:${town.id}:civic`; + if ( + !this._placeBuildingLayerSprite( + civicKey, + TOWN_HALL_TEXTURE_KEY, + civicScreen.x, + civicScreen.y, + 52 * s, + usedBuildingSprites, + ) + ) { this._drawCivicBuilding(gfx, civicScreen.x, civicScreen.y, s); } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c3b4b6c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "AutoHero", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/scripts/pixellab-commit-enemy-south-batch.mjs b/scripts/pixellab-commit-enemy-south-batch.mjs new file mode 100644 index 0000000..d719c08 --- /dev/null +++ b/scripts/pixellab-commit-enemy-south-batch.mjs @@ -0,0 +1,53 @@ +/** + * After PixelLab MCP returns object IDs, save PNGs and set manifest pixellabObjectId. + * + * Usage (pairs: slug without enemy. prefix): + * node scripts/pixellab-commit-enemy-south-batch.mjs boar_l5_5_canyon boar_l5_5_swamp ... + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); +const MANIFEST = path.join(ROOT, 'frontend/public/assets/game/manifest.json'); + +async function download(url, dest) { + const res = await fetch(url); + if (!res.ok) throw new Error(`GET ${url} ${res.status}`); + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.writeFileSync(dest, Buffer.from(await res.arrayBuffer())); +} + +async function main() { + const args = process.argv.slice(2); + if (args.length < 2 || args.length % 2 !== 0) { + console.error('Need even number of args: slug objectId ...'); + process.exit(1); + } + const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); + for (let i = 0; i < args.length; i += 2) { + const slug = args[i]; + const objectId = args[i + 1]; + const key = `enemy.${slug}.south`; + const file = `enemies/enemy.${slug}.south.png`; + const entry = manifest.textures[key]; + if (!entry) throw new Error(`No manifest key ${key}`); + const url = `https://api.pixellab.ai/mcp/map-objects/${objectId}/download`; + const dest = path.join(ROOT, 'frontend/assets', file); + await download(url, dest); + entry.pixellabObjectId = objectId; + entry.file = file; + entry.kind = 'map_object'; + entry.rotation = 'south'; + console.log('OK', key); + } + manifest.version = (manifest.version ?? 0) + 1; + fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2) + '\n', 'utf8'); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/pixellab-commit-enemy-south-pairs-file.mjs b/scripts/pixellab-commit-enemy-south-pairs-file.mjs new file mode 100644 index 0000000..23864aa --- /dev/null +++ b/scripts/pixellab-commit-enemy-south-pairs-file.mjs @@ -0,0 +1,67 @@ +/** + * Commit many enemy south sprites from a JSON file (one manifest version bump). + * + * File format: [ { "slug": "golem_l8_10_forest", "objectId": "uuid" }, ... ] + * slug = enemies.type without enemy. prefix or .south suffix + * + * node scripts/pixellab-commit-enemy-south-pairs-file.mjs path/to/pairs.json + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); +const MANIFEST = path.join(ROOT, 'frontend/public/assets/game/manifest.json'); + +async function download(url, dest) { + const res = await fetch(url); + if (!res.ok) throw new Error(`GET ${url} ${res.status}`); + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.writeFileSync(dest, Buffer.from(await res.arrayBuffer())); +} + +async function main() { + const file = process.argv[2]; + if (!file) { + console.error('Usage: node scripts/pixellab-commit-enemy-south-pairs-file.mjs '); + process.exit(1); + } + const pairs = JSON.parse(fs.readFileSync(file, 'utf8')); + if (!Array.isArray(pairs) || pairs.length === 0) { + console.error('Expected non-empty JSON array'); + process.exit(1); + } + if (pairs.length > 120) { + console.error('Max 120 pairs per run'); + process.exit(1); + } + + const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); + for (const p of pairs) { + const slug = p.slug; + const objectId = p.objectId; + if (!slug || !objectId) throw new Error('Each entry needs slug and objectId'); + const key = `enemy.${slug}.south`; + const fileRel = `enemies/enemy.${slug}.south.png`; + const entry = manifest.textures[key]; + if (!entry) throw new Error(`No manifest key ${key}`); + const url = `https://api.pixellab.ai/mcp/map-objects/${objectId}/download`; + const dest = path.join(ROOT, 'frontend/assets', fileRel); + await download(url, dest); + entry.pixellabObjectId = objectId; + entry.file = fileRel; + entry.kind = 'map_object'; + entry.rotation = 'south'; + console.log('OK', key); + } + manifest.version = (manifest.version ?? 0) + 1; + fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2) + '\n', 'utf8'); + console.log('Wrote manifest version', manifest.version, 'count', pairs.length); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/pixellab-download-map-objects.mjs b/scripts/pixellab-download-map-objects.mjs new file mode 100644 index 0000000..8663812 --- /dev/null +++ b/scripts/pixellab-download-map-objects.mjs @@ -0,0 +1,34 @@ +/** + * Download PixelLab map objects by id into frontend/assets paths. + * Usage: node scripts/pixellab-download-map-objects.mjs rel/path.png ... + */ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); + +async function main() { + const args = process.argv.slice(2); + if (args.length < 2 || args.length % 2 !== 0) { + console.error('Usage: rel/path.png ...'); + process.exit(1); + } + for (let i = 0; i < args.length; i += 2) { + const rel = args[i]; + const id = args[i + 1]; + const url = `https://api.pixellab.ai/mcp/map-objects/${id}/download`; + const dest = path.join(ROOT, 'frontend/assets', rel); + const res = await fetch(url); + if (!res.ok) throw new Error(`${rel} GET ${res.status}`); + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.writeFileSync(dest, Buffer.from(await res.arrayBuffer())); + console.log('OK', rel); + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/pixellab-enemy-south-v2.mjs b/scripts/pixellab-enemy-south-v2.mjs new file mode 100644 index 0000000..e322703 --- /dev/null +++ b/scripts/pixellab-enemy-south-v2.mjs @@ -0,0 +1,197 @@ +/** + * Regenerate enemy..south sprites via PixelLab API v2 (POST /map-objects). + * Uses local archetype PNG as init_image for style continuity. + * + * Requires: PIXELLAB_API_TOKEN in the environment (https://api.pixellab.ai/mcp). + * + * Usage: + * node scripts/pixellab-enemy-south-v2.mjs --limit=5 + * node scripts/pixellab-enemy-south-v2.mjs --slug=boar_l5_5_canyon + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); +const SQL = path.join(ROOT, 'backend/migrations/000006b_enemy_data.sql'); +const MANIFEST = path.join(ROOT, 'frontend/public/assets/game/manifest.json'); + +const BASE = 'https://api.pixellab.ai/v2'; + +const ARCHETYPE_REF = { + wolf: 'enemy.wolf', + boar: 'enemy.boar', + zombie: 'enemy.zombie', + spider: 'enemy.spider', + orc: 'enemy.orc', + skeleton: 'enemy.skeleton_archer', + battle_lizard: 'enemy.battle_lizard', + element: 'enemy.water_element', + demon: 'enemy.fire_demon', + skeleton_king: 'enemy.skeleton_king', + forest_warden: 'enemy.forest_warden', + titan: 'enemy.lightning_titan', + bandit: 'enemy.orc', + cultist: 'enemy.skeleton_archer', + golem: 'enemy.ice_guardian', + wraith: 'enemy.zombie', + treant: 'enemy.forest_warden', + basilisk: 'enemy.battle_lizard', + wyvern: 'enemy.battle_lizard', + harpy: 'enemy.spider', + manticore: 'enemy.battle_lizard', + shade: 'enemy.zombie', +}; + +const BIOME_HINT = { + meadow: 'sunlit meadow greens and wildflowers', + forest: 'deep forest shadows, moss and bark tones', + ruins: 'grey stone dust, cracked masonry palette', + canyon: 'red rock, dust, harsh sun', + swamp: 'murky teal, bog mist, rotting wood', + volcanic: 'ember glow, black rock, lava highlights', + astral: 'cosmic violet-blue, star specks, ethereal glow', +}; + +function parseArgs() { + const a = process.argv.slice(2); + let limit = Infinity; + let slug = null; + for (const x of a) { + if (x.startsWith('--limit=')) limit = parseInt(x.slice(8), 10); + if (x.startsWith('--slug=')) slug = x.slice(7); + } + return { limit, slug }; +} + +function parseEnemyRows(sqlText) { + const re = + /VALUES\s*\(\d+,\s*'([^']+)',\s*'([^']+)',\s*'([^']+)',\s*'([^']*)'/g; + const rows = []; + let m; + while ((m = re.exec(sqlText))) { + rows.push({ type: m[1], archetype: m[2], biome: m[3], name: m[4] }); + } + return rows; +} + +function buildPrompt(row) { + const b = BIOME_HINT[row.biome] ?? row.biome; + return ( + `Pixel RPG combat sprite: ${row.name}. ` + + `${row.archetype.replace(/_/g, ' ')} monster, ${b}. ` + + `Low top-down view, facing south toward camera, full body, transparent background, ` + + `game-ready, single black outline, medium shading — match init image proportions and style.` + ); +} + +async function pollObject(token, objectId) { + const url = `${BASE}/objects/${objectId}`; + for (let i = 0; i < 120; i++) { + const res = await fetch(url, { + headers: { Authorization: `Bearer ${token}` }, + }); + const j = await res.json().catch(() => ({})); + const data = j.data ?? j; + const status = data?.status ?? data?.generation_status; + if (status === 'completed' || data?.image_url || data?.download_url) { + return data; + } + if (status === 'failed' || data?.error) { + throw new Error(JSON.stringify(data?.error ?? data)); + } + await new Promise((r) => setTimeout(r, 3000)); + } + throw new Error('Timeout waiting for object'); +} + +async function downloadMcpObject(objectId, destPath) { + const url = `https://api.pixellab.ai/mcp/map-objects/${objectId}/download`; + const res = await fetch(url); + if (!res.ok) throw new Error(`download ${res.status}`); + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + fs.writeFileSync(destPath, Buffer.from(await res.arrayBuffer())); +} + +async function main() { + const token = process.env.PIXELLAB_API_TOKEN; + if (!token) { + console.error('Set PIXELLAB_API_TOKEN'); + process.exit(1); + } + + const { limit, slug } = parseArgs(); + const sql = fs.readFileSync(SQL, 'utf8'); + let rows = parseEnemyRows(sql); + if (slug) rows = rows.filter((r) => r.type === slug); + rows = rows.slice(0, limit); + + const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); + const textures = manifest.textures; + let updated = 0; + + for (const row of rows) { + const refKey = ARCHETYPE_REF[row.archetype]; + const refFile = textures[refKey].file; + const refPath = path.join(ROOT, 'frontend/assets', refFile); + const b64 = fs.readFileSync(refPath).toString('base64'); + + const body = { + description: buildPrompt(row), + image_size: { width: 96, height: 96 }, + view: 'low top-down', + outline: 'single color outline', + shading: 'medium shading', + detail: 'medium detail', + init_image: { type: 'base64', base64: b64 }, + init_image_strength: 420, + text_guidance_scale: 9, + }; + + const cr = await fetch(`${BASE}/map-objects`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + const cj = await cr.json().catch(() => ({})); + if (!cr.ok) { + console.error('Create failed', row.type, cj); + continue; + } + const objectId = cj.data?.object_id ?? cj.object_id ?? cj.data?.id; + if (!objectId) { + console.error('No object_id', row.type, cj); + continue; + } + + await pollObject(token, objectId); + const southFile = `enemies/enemy.${row.type}.south.png`; + const southAbs = path.join(ROOT, 'frontend/assets', southFile); + await downloadMcpObject(objectId, southAbs); + + const texKey = `enemy.${row.type}.south`; + textures[texKey] = { + file: southFile, + kind: 'map_object', + rotation: 'south', + pixellabObjectId: objectId, + }; + updated++; + console.log('OK', row.type, objectId); + } + + if (updated > 0) { + manifest.version = (manifest.version ?? 0) + 1; + fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2) + '\n', 'utf8'); + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/pixellab-fill-missing-south-one-shot.mjs b/scripts/pixellab-fill-missing-south-one-shot.mjs new file mode 100644 index 0000000..45424db --- /dev/null +++ b/scripts/pixellab-fill-missing-south-one-shot.mjs @@ -0,0 +1,334 @@ +/** + * One-shot: fill every manifest enemy..south missing pixellabObjectId (PixelLab API v2). + * Requires PIXELLAB_API_TOKEN. + * + * node scripts/pixellab-fill-missing-south-one-shot.mjs + * node scripts/pixellab-fill-missing-south-one-shot.mjs --concurrency=4 + * + * Wave mode (stagger creates, then shared wait, then parallel poll/download): + * node scripts/pixellab-fill-missing-south-one-shot.mjs --delay-between-creates-sec=10 --post-queue-wait-sec=110 --concurrency=10 + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); +const SQL = path.join(ROOT, 'backend/migrations/000006b_enemy_data.sql'); +const MANIFEST = path.join(ROOT, 'frontend/public/assets/game/manifest.json'); +const BASE = 'https://api.pixellab.ai/v2'; + +const ARCHETYPE_REF = { + wolf: 'enemy.wolf', + boar: 'enemy.boar', + zombie: 'enemy.zombie', + spider: 'enemy.spider', + orc: 'enemy.orc', + skeleton: 'enemy.skeleton_archer', + battle_lizard: 'enemy.battle_lizard', + element: 'enemy.water_element', + demon: 'enemy.fire_demon', + skeleton_king: 'enemy.skeleton_king', + forest_warden: 'enemy.forest_warden', + titan: 'enemy.lightning_titan', + bandit: 'enemy.orc', + cultist: 'enemy.skeleton_archer', + golem: 'enemy.ice_guardian', + wraith: 'enemy.zombie', + treant: 'enemy.forest_warden', + basilisk: 'enemy.battle_lizard', + wyvern: 'enemy.battle_lizard', + harpy: 'enemy.spider', + manticore: 'enemy.battle_lizard', + shade: 'enemy.zombie', +}; + +const BIOME_HINT = { + meadow: 'sunlit meadow greens and wildflowers', + forest: 'deep forest shadows, moss and bark tones', + ruins: 'grey stone dust, cracked masonry palette', + canyon: 'red rock, dust, harsh sun', + swamp: 'murky teal, bog mist, rotting wood', + volcanic: 'ember glow, black rock, lava highlights', + astral: 'cosmic violet-blue, star specks, ethereal glow', +}; + +function parseArgs() { + let concurrency = 6; + let delayBetweenCreatesSec = 0; + let postQueueWaitSec = 0; + for (const x of process.argv.slice(2)) { + if (x.startsWith('--delay-between-creates-sec=')) { + delayBetweenCreatesSec = Math.max(0, parseInt(x.slice(28), 10) || 0); + } + if (x.startsWith('--post-queue-wait-sec=')) { + postQueueWaitSec = Math.max(0, parseInt(x.slice(22), 10) || 0); + } + } + return { concurrency, delayBetweenCreatesSec, postQueueWaitSec }; +} + +function sleep(ms) { + return new Promise((r) => setTimeout(r, ms)); +} + +function parseEnemyRows(sqlText) { + const re = + /VALUES\s*\(\d+,\s*'([^']+)',\s*'([^']+)',\s*'([^']+)',\s*'([^']*)'/g; + const map = new Map(); + let m; + while ((m = re.exec(sqlText))) { + map.set(m[1], { type: m[1], archetype: m[2], biome: m[3], name: m[4] }); + } + return map; +} + +function buildPrompt(row) { + const b = BIOME_HINT[row.biome] ?? row.biome; + return ( + `Pixel RPG combat sprite: ${row.name}. ` + + `${row.archetype.replace(/_/g, ' ')} monster, ${b}. ` + + `Low top-down view, facing south toward camera, full body, transparent background, ` + + `game-ready, single black outline, medium shading — match init image proportions and style.` + ); +} + +async function pollObject(token, objectId) { + const url = `${BASE}/objects/${objectId}`; + for (let i = 0; i < 120; i++) { + const res = await fetch(url, { + headers: { Authorization: `Bearer ${token}` }, + }); + const j = await res.json().catch(() => ({})); + const data = j.data ?? j; + const status = data?.status ?? data?.generation_status; + if (status === 'completed' || data?.image_url || data?.download_url) { + return data; + } + if (status === 'failed' || data?.error) { + throw new Error(JSON.stringify(data?.error ?? data)); + } + await sleep(3000); + } + throw new Error('Timeout waiting for object'); +} + +async function downloadMcpObject(objectId, destPath) { + const url = `https://api.pixellab.ai/mcp/map-objects/${objectId}/download`; + const res = await fetch(url); + if (!res.ok) throw new Error(`download ${res.status}`); + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + fs.writeFileSync(destPath, Buffer.from(await res.arrayBuffer())); +} + +async function createMapObject(token, body, retries = 4) { + for (let a = 0; a < retries; a++) { + const cr = await fetch(`${BASE}/map-objects`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + const cj = await cr.json().catch(() => ({})); + if (cr.status === 429 && a < retries - 1) { + await sleep(15000 * (a + 1)); + continue; + } + if (!cr.ok) { + throw new Error(`create ${cr.status} ${JSON.stringify(cj)}`); + } + const objectId = cj.data?.object_id ?? cj.object_id ?? cj.data?.id; + if (!objectId) throw new Error(`no object_id ${JSON.stringify(cj)}`); + return objectId; + } +} + +async function runPool(concurrency, items, fn) { + let ix = 0; + const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => { + while (true) { + const j = ix++; + if (j >= items.length) break; + await fn(items[j], j); + } + }); + await Promise.all(workers); +} + +/** @returns {{ slug: string, body: object } | { error: string }} */ +function prepareCreateBody(texKey, textures, byType) { + const slug = texKey.slice('enemy.'.length, -'.south'.length); + const row = byType.get(slug); + if (!row) return { error: `No SQL row ${slug}` }; + const refKey = ARCHETYPE_REF[row.archetype]; + if (!refKey || !textures[refKey]?.file) { + return { error: `No archetype ref ${slug} ${row.archetype} ${refKey}` }; + } + const refPath = path.join(ROOT, 'frontend/assets', textures[refKey].file); + if (!fs.existsSync(refPath)) { + return { error: `Missing ref file ${refPath}` }; + } + const b64 = fs.readFileSync(refPath).toString('base64'); + const body = { + description: buildPrompt(row), + image_size: { width: 96, height: 96 }, + view: 'low top-down', + outline: 'single color outline', + shading: 'medium shading', + detail: 'medium detail', + init_image: { type: 'base64', base64: b64 }, + init_image_strength: 420, + text_guidance_scale: 9, + }; + return { slug, body }; +} + +async function finalizeSouthEntry(token, textures, texKey, slug, objectId) { + await pollObject(token, objectId); + const southFile = `enemies/enemy.${slug}.south.png`; + const southAbs = path.join(ROOT, 'frontend/assets', southFile); + await downloadMcpObject(objectId, southAbs); + const entry = textures[texKey]; + entry.file = southFile; + entry.kind = 'map_object'; + entry.rotation = 'south'; + entry.pixellabObjectId = objectId; +} + +function loadPixellabTokenFromEnvFiles() { + if (process.env.PIXELLAB_API_TOKEN) return; + const candidates = [ + path.join(ROOT, '.env'), + path.join(ROOT, 'frontend', '.env.local'), + ]; + for (const p of candidates) { + if (!fs.existsSync(p)) continue; + const text = fs.readFileSync(p, 'utf8'); + for (const line of text.split(/\r?\n/)) { + const t = line.trim(); + if (!t || t.startsWith('#')) continue; + const m = t.match(/^PIXELLAB_API_TOKEN\s*=\s*(.*)$/); + if (!m) continue; + let v = m[1].trim(); + if ( + (v.startsWith('"') && v.endsWith('"')) || + (v.startsWith("'") && v.endsWith("'")) + ) { + v = v.slice(1, -1); + } + process.env.PIXELLAB_API_TOKEN = v; + return; + } + } +} + +async function main() { + loadPixellabTokenFromEnvFiles(); + const token = process.env.PIXELLAB_API_TOKEN; + if (!token) { + console.error( + 'Set PIXELLAB_API_TOKEN in the environment or in repo root .env / frontend/.env.local', + ); + process.exit(1); + } + + const { concurrency, delayBetweenCreatesSec, postQueueWaitSec } = parseArgs(); + const sql = fs.readFileSync(SQL, 'utf8'); + const byType = parseEnemyRows(sql); + + const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); + const { textures } = manifest; + + const missing = Object.keys(textures) + .filter((k) => k.startsWith('enemy.') && k.endsWith('.south') && !textures[k].pixellabObjectId) + .sort(); + + if (missing.length === 0) { + console.log('Nothing missing.'); + return; + } + + console.log( + 'Missing count:', + missing.length, + 'concurrency:', + concurrency, + delayBetweenCreatesSec > 0 + ? `wave: delayBetweenCreatesSec=${delayBetweenCreatesSec} postQueueWaitSec=${postQueueWaitSec}` + : '', + ); + + let ok = 0; + let fail = 0; + + if (delayBetweenCreatesSec > 0) { + const queued = []; + for (let i = 0; i < missing.length; i++) { + const texKey = missing[i]; + const prep = prepareCreateBody(texKey, textures, byType); + if ('error' in prep) { + console.error('SKIP', texKey, prep.error); + fail++; + continue; + } + try { + const objectId = await createMapObject(token, prep.body); + queued.push({ texKey, slug: prep.slug, objectId }); + console.log('Queued', prep.slug, objectId, `(${queued.length}/${missing.length})`); + } catch (e) { + console.error('FAIL create', prep.slug, e.message); + fail++; + } + if (i < missing.length - 1 && delayBetweenCreatesSec > 0) { + await sleep(delayBetweenCreatesSec * 1000); + } + } + if (postQueueWaitSec > 0) { + console.log(`Post-queue wait ${postQueueWaitSec}s…`); + await sleep(postQueueWaitSec * 1000); + } + await runPool(concurrency, queued, async (job) => { + try { + await finalizeSouthEntry(token, textures, job.texKey, job.slug, job.objectId); + ok++; + console.log('OK', job.slug, job.objectId); + } catch (e) { + console.error('FAIL', job.slug, e.message); + fail++; + } + }); + } else { + await runPool(concurrency, missing, async (texKey) => { + const prep = prepareCreateBody(texKey, textures, byType); + if ('error' in prep) { + console.error('SKIP', texKey, prep.error); + fail++; + return; + } + try { + const objectId = await createMapObject(token, prep.body); + await finalizeSouthEntry(token, textures, texKey, prep.slug, objectId); + ok++; + console.log('OK', prep.slug, objectId); + } catch (e) { + console.error('FAIL', prep.slug, e.message); + fail++; + } + }); + } + + if (ok > 0) { + manifest.version = (manifest.version ?? 0) + 1; + fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2) + '\n', 'utf8'); + } + + console.log('Done. OK', ok, 'FAIL', fail, 'manifest.version', manifest.version); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/sync-enemy-south-sprites.mjs b/scripts/sync-enemy-south-sprites.mjs new file mode 100644 index 0000000..5461ce1 --- /dev/null +++ b/scripts/sync-enemy-south-sprites.mjs @@ -0,0 +1,129 @@ +/** + * Ensures one south-facing enemy PNG per DB template (enemies.type). + * 1) Downloads archetype reference sprites from PixelLab MCP URLs (manifest pixellabObjectId). + * 2) Copies the right archetype PNG to enemies/enemy..south.png for every row in 000006b_enemy_data.sql. + * 3) Merges manifest.json entries for all enemy..south keys. + * + * For per-slug PixelLab generations (init_image + unique prompt), use pixellab-enemy-south-v2.mjs with PIXELLAB_API_TOKEN. + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); +const SQL = path.join(ROOT, 'backend/migrations/000006b_enemy_data.sql'); +const MANIFEST = path.join(ROOT, 'frontend/public/assets/game/manifest.json'); +const ASSETS_ENEMIES = path.join(ROOT, 'frontend/assets/enemies'); + +const ARCHETYPE_REF = { + wolf: 'enemy.wolf', + boar: 'enemy.boar', + zombie: 'enemy.zombie', + spider: 'enemy.spider', + orc: 'enemy.orc', + skeleton: 'enemy.skeleton_archer', + battle_lizard: 'enemy.battle_lizard', + element: 'enemy.water_element', + demon: 'enemy.fire_demon', + skeleton_king: 'enemy.skeleton_king', + forest_warden: 'enemy.forest_warden', + titan: 'enemy.lightning_titan', + bandit: 'enemy.orc', + cultist: 'enemy.skeleton_archer', + golem: 'enemy.ice_guardian', + wraith: 'enemy.zombie', + treant: 'enemy.forest_warden', + basilisk: 'enemy.battle_lizard', + wyvern: 'enemy.battle_lizard', + harpy: 'enemy.spider', + manticore: 'enemy.battle_lizard', + shade: 'enemy.zombie', +}; + +function parseEnemyRows(sqlText) { + const re = + /VALUES\s*\(\d+,\s*'([^']+)',\s*'([^']+)',\s*'([^']+)',\s*'([^']*)'/g; + const rows = []; + let m; + while ((m = re.exec(sqlText))) { + rows.push({ type: m[1], archetype: m[2], biome: m[3], name: m[4] }); + } + return rows; +} + +async function downloadFile(url, destPath) { + const res = await fetch(url, { redirect: 'follow' }); + if (!res.ok) throw new Error(`GET ${url} -> ${res.status}`); + const buf = Buffer.from(await res.arrayBuffer()); + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + fs.writeFileSync(destPath, buf); +} + +async function main() { + const sql = fs.readFileSync(SQL, 'utf8'); + const rows = parseEnemyRows(sql); + if (rows.length !== 220) { + throw new Error(`Expected 220 enemy rows, got ${rows.length}`); + } + + const manifest = JSON.parse(fs.readFileSync(MANIFEST, 'utf8')); + const textures = manifest.textures; + + const refKeys = [...new Set(Object.values(ARCHETYPE_REF))]; + for (const refKey of refKeys) { + const entry = textures[refKey]; + if (!entry?.pixellabObjectId || !entry?.file) { + throw new Error(`Manifest missing reference ${refKey}`); + } + const dest = path.join(ROOT, 'frontend/assets', entry.file); + if (fs.existsSync(dest) && fs.statSync(dest).size > 0) continue; + const url = `https://api.pixellab.ai/mcp/map-objects/${entry.pixellabObjectId}/download`; + console.log('Download', refKey, '->', entry.file); + await downloadFile(url, dest); + } + + fs.mkdirSync(ASSETS_ENEMIES, { recursive: true }); + + for (const row of rows) { + const refManifestKey = ARCHETYPE_REF[row.archetype]; + if (!refManifestKey) { + throw new Error(`No ARCHETYPE_REF for archetype ${row.archetype}`); + } + const refEntry = textures[refManifestKey]; + const refAbs = path.join(ROOT, 'frontend/assets', refEntry.file); + if (!fs.existsSync(refAbs)) throw new Error(`Missing ref file ${refAbs}`); + + const southFile = `enemies/enemy.${row.type}.south.png`; + const southAbs = path.join(ROOT, 'frontend/assets', southFile); + const texKey = `enemy.${row.type}.south`; + const existing = textures[texKey]; + + if (existing?.pixellabObjectId) { + const url = `https://api.pixellab.ai/mcp/map-objects/${existing.pixellabObjectId}/download`; + await downloadFile(url, southAbs); + } else { + fs.copyFileSync(refAbs, southAbs); + } + + textures[texKey] = { + file: southFile, + kind: 'map_object', + rotation: 'south', + ...(existing?.pixellabObjectId + ? { pixellabObjectId: existing.pixellabObjectId } + : {}), + }; + } + + manifest.version = (manifest.version ?? 0) + 1; + + fs.writeFileSync(MANIFEST, JSON.stringify(manifest, null, 2) + '\n', 'utf8'); + console.log('Wrote manifest version', manifest.version, 'with', Object.keys(textures).filter((k) => k.endsWith('.south') && k.startsWith('enemy.')).length, 'enemy south keys'); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +});