diff --git a/README.md b/README.md index edb0512..a8dd1fc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Jeu coop 3 joueurs en ligne, jouable dans le navigateur. Horde survival isométrique : on défend un portail (le Soulgate) contre 3 vagues d'ennemis avec 2 boss à la fin. -Projet de fin d'année DSP CNAM Paris. +Projet de fin d'année DSP — CNAM Paris. ## Stack diff --git a/client/assets/discord.svg b/client/assets/discord.svg new file mode 100644 index 0000000..c03e8e1 --- /dev/null +++ b/client/assets/discord.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/client/assets/fond_soulgate.png b/client/assets/fond_soulgate.png new file mode 100644 index 0000000..9fbfab5 Binary files /dev/null and b/client/assets/fond_soulgate.png differ diff --git a/client/assets/maps/Isometric/barrel_E.png b/client/assets/maps/Isometric/barrel_E.png new file mode 100644 index 0000000..d14ea5c Binary files /dev/null and b/client/assets/maps/Isometric/barrel_E.png differ diff --git a/client/assets/maps/Isometric/barrel_N.png b/client/assets/maps/Isometric/barrel_N.png new file mode 100644 index 0000000..f310f5e Binary files /dev/null and b/client/assets/maps/Isometric/barrel_N.png differ diff --git a/client/assets/maps/Isometric/barrel_S.png b/client/assets/maps/Isometric/barrel_S.png new file mode 100644 index 0000000..0d45933 Binary files /dev/null and b/client/assets/maps/Isometric/barrel_S.png differ diff --git a/client/assets/maps/Isometric/barrel_W.png b/client/assets/maps/Isometric/barrel_W.png new file mode 100644 index 0000000..685b279 Binary files /dev/null and b/client/assets/maps/Isometric/barrel_W.png differ diff --git a/client/assets/maps/Isometric/barrelsStacked_E.png b/client/assets/maps/Isometric/barrelsStacked_E.png new file mode 100644 index 0000000..915a4c8 Binary files /dev/null and b/client/assets/maps/Isometric/barrelsStacked_E.png differ diff --git a/client/assets/maps/Isometric/barrelsStacked_N.png b/client/assets/maps/Isometric/barrelsStacked_N.png new file mode 100644 index 0000000..57f5404 Binary files /dev/null and b/client/assets/maps/Isometric/barrelsStacked_N.png differ diff --git a/client/assets/maps/Isometric/barrelsStacked_S.png b/client/assets/maps/Isometric/barrelsStacked_S.png new file mode 100644 index 0000000..33b6cde Binary files /dev/null and b/client/assets/maps/Isometric/barrelsStacked_S.png differ diff --git a/client/assets/maps/Isometric/barrelsStacked_W.png b/client/assets/maps/Isometric/barrelsStacked_W.png new file mode 100644 index 0000000..d8586f8 Binary files /dev/null and b/client/assets/maps/Isometric/barrelsStacked_W.png differ diff --git a/client/assets/maps/Isometric/barrels_E.png b/client/assets/maps/Isometric/barrels_E.png new file mode 100644 index 0000000..bca9fdb Binary files /dev/null and b/client/assets/maps/Isometric/barrels_E.png differ diff --git a/client/assets/maps/Isometric/barrels_N.png b/client/assets/maps/Isometric/barrels_N.png new file mode 100644 index 0000000..a7fadaf Binary files /dev/null and b/client/assets/maps/Isometric/barrels_N.png differ diff --git a/client/assets/maps/Isometric/barrels_S.png b/client/assets/maps/Isometric/barrels_S.png new file mode 100644 index 0000000..94c6dd9 Binary files /dev/null and b/client/assets/maps/Isometric/barrels_S.png differ diff --git a/client/assets/maps/Isometric/barrels_W.png b/client/assets/maps/Isometric/barrels_W.png new file mode 100644 index 0000000..cdd5edc Binary files /dev/null and b/client/assets/maps/Isometric/barrels_W.png differ diff --git a/client/assets/maps/Isometric/bridgeBroken_E.png b/client/assets/maps/Isometric/bridgeBroken_E.png new file mode 100644 index 0000000..5b6ae2f Binary files /dev/null and b/client/assets/maps/Isometric/bridgeBroken_E.png differ diff --git a/client/assets/maps/Isometric/bridgeBroken_N.png b/client/assets/maps/Isometric/bridgeBroken_N.png new file mode 100644 index 0000000..60bd95e Binary files /dev/null and b/client/assets/maps/Isometric/bridgeBroken_N.png differ diff --git a/client/assets/maps/Isometric/bridgeBroken_S.png b/client/assets/maps/Isometric/bridgeBroken_S.png new file mode 100644 index 0000000..897a5bd Binary files /dev/null and b/client/assets/maps/Isometric/bridgeBroken_S.png differ diff --git a/client/assets/maps/Isometric/bridgeBroken_W.png b/client/assets/maps/Isometric/bridgeBroken_W.png new file mode 100644 index 0000000..2afb84c Binary files /dev/null and b/client/assets/maps/Isometric/bridgeBroken_W.png differ diff --git a/client/assets/maps/Isometric/bridge_E.png b/client/assets/maps/Isometric/bridge_E.png new file mode 100644 index 0000000..73babd2 Binary files /dev/null and b/client/assets/maps/Isometric/bridge_E.png differ diff --git a/client/assets/maps/Isometric/bridge_N.png b/client/assets/maps/Isometric/bridge_N.png new file mode 100644 index 0000000..99dc06d Binary files /dev/null and b/client/assets/maps/Isometric/bridge_N.png differ diff --git a/client/assets/maps/Isometric/bridge_S.png b/client/assets/maps/Isometric/bridge_S.png new file mode 100644 index 0000000..8768264 Binary files /dev/null and b/client/assets/maps/Isometric/bridge_S.png differ diff --git a/client/assets/maps/Isometric/bridge_W.png b/client/assets/maps/Isometric/bridge_W.png new file mode 100644 index 0000000..ec3b5d6 Binary files /dev/null and b/client/assets/maps/Isometric/bridge_W.png differ diff --git a/client/assets/maps/Isometric/chair_E.png b/client/assets/maps/Isometric/chair_E.png new file mode 100644 index 0000000..f019c8f Binary files /dev/null and b/client/assets/maps/Isometric/chair_E.png differ diff --git a/client/assets/maps/Isometric/chair_N.png b/client/assets/maps/Isometric/chair_N.png new file mode 100644 index 0000000..403c48e Binary files /dev/null and b/client/assets/maps/Isometric/chair_N.png differ diff --git a/client/assets/maps/Isometric/chair_S.png b/client/assets/maps/Isometric/chair_S.png new file mode 100644 index 0000000..871207f Binary files /dev/null and b/client/assets/maps/Isometric/chair_S.png differ diff --git a/client/assets/maps/Isometric/chair_W.png b/client/assets/maps/Isometric/chair_W.png new file mode 100644 index 0000000..b96f221 Binary files /dev/null and b/client/assets/maps/Isometric/chair_W.png differ diff --git a/client/assets/maps/Isometric/chestClosed_E.png b/client/assets/maps/Isometric/chestClosed_E.png new file mode 100644 index 0000000..ca60ced Binary files /dev/null and b/client/assets/maps/Isometric/chestClosed_E.png differ diff --git a/client/assets/maps/Isometric/chestClosed_N.png b/client/assets/maps/Isometric/chestClosed_N.png new file mode 100644 index 0000000..74ab1ba Binary files /dev/null and b/client/assets/maps/Isometric/chestClosed_N.png differ diff --git a/client/assets/maps/Isometric/chestClosed_S.png b/client/assets/maps/Isometric/chestClosed_S.png new file mode 100644 index 0000000..7f4eddf Binary files /dev/null and b/client/assets/maps/Isometric/chestClosed_S.png differ diff --git a/client/assets/maps/Isometric/chestClosed_W.png b/client/assets/maps/Isometric/chestClosed_W.png new file mode 100644 index 0000000..36902a4 Binary files /dev/null and b/client/assets/maps/Isometric/chestClosed_W.png differ diff --git a/client/assets/maps/Isometric/chestOpen_E.png b/client/assets/maps/Isometric/chestOpen_E.png new file mode 100644 index 0000000..9e79846 Binary files /dev/null and b/client/assets/maps/Isometric/chestOpen_E.png differ diff --git a/client/assets/maps/Isometric/chestOpen_N.png b/client/assets/maps/Isometric/chestOpen_N.png new file mode 100644 index 0000000..6e3da75 Binary files /dev/null and b/client/assets/maps/Isometric/chestOpen_N.png differ diff --git a/client/assets/maps/Isometric/chestOpen_S.png b/client/assets/maps/Isometric/chestOpen_S.png new file mode 100644 index 0000000..55661fb Binary files /dev/null and b/client/assets/maps/Isometric/chestOpen_S.png differ diff --git a/client/assets/maps/Isometric/chestOpen_W.png b/client/assets/maps/Isometric/chestOpen_W.png new file mode 100644 index 0000000..13a66eb Binary files /dev/null and b/client/assets/maps/Isometric/chestOpen_W.png differ diff --git a/client/assets/maps/Isometric/dirtTiles_E.png b/client/assets/maps/Isometric/dirtTiles_E.png new file mode 100644 index 0000000..a880528 Binary files /dev/null and b/client/assets/maps/Isometric/dirtTiles_E.png differ diff --git a/client/assets/maps/Isometric/dirtTiles_N.png b/client/assets/maps/Isometric/dirtTiles_N.png new file mode 100644 index 0000000..f76016d Binary files /dev/null and b/client/assets/maps/Isometric/dirtTiles_N.png differ diff --git a/client/assets/maps/Isometric/dirtTiles_S.png b/client/assets/maps/Isometric/dirtTiles_S.png new file mode 100644 index 0000000..2bdbeb8 Binary files /dev/null and b/client/assets/maps/Isometric/dirtTiles_S.png differ diff --git a/client/assets/maps/Isometric/dirtTiles_W.png b/client/assets/maps/Isometric/dirtTiles_W.png new file mode 100644 index 0000000..9075736 Binary files /dev/null and b/client/assets/maps/Isometric/dirtTiles_W.png differ diff --git a/client/assets/maps/Isometric/dirt_E.png b/client/assets/maps/Isometric/dirt_E.png new file mode 100644 index 0000000..e121ed1 Binary files /dev/null and b/client/assets/maps/Isometric/dirt_E.png differ diff --git a/client/assets/maps/Isometric/dirt_N.png b/client/assets/maps/Isometric/dirt_N.png new file mode 100644 index 0000000..e3f78a7 Binary files /dev/null and b/client/assets/maps/Isometric/dirt_N.png differ diff --git a/client/assets/maps/Isometric/dirt_S.png b/client/assets/maps/Isometric/dirt_S.png new file mode 100644 index 0000000..1744c2f Binary files /dev/null and b/client/assets/maps/Isometric/dirt_S.png differ diff --git a/client/assets/maps/Isometric/dirt_W.png b/client/assets/maps/Isometric/dirt_W.png new file mode 100644 index 0000000..cd50587 Binary files /dev/null and b/client/assets/maps/Isometric/dirt_W.png differ diff --git a/client/assets/maps/Isometric/planksBroken_E.png b/client/assets/maps/Isometric/planksBroken_E.png new file mode 100644 index 0000000..826ee82 Binary files /dev/null and b/client/assets/maps/Isometric/planksBroken_E.png differ diff --git a/client/assets/maps/Isometric/planksBroken_N.png b/client/assets/maps/Isometric/planksBroken_N.png new file mode 100644 index 0000000..732312c Binary files /dev/null and b/client/assets/maps/Isometric/planksBroken_N.png differ diff --git a/client/assets/maps/Isometric/planksBroken_S.png b/client/assets/maps/Isometric/planksBroken_S.png new file mode 100644 index 0000000..911605c Binary files /dev/null and b/client/assets/maps/Isometric/planksBroken_S.png differ diff --git a/client/assets/maps/Isometric/planksBroken_W.png b/client/assets/maps/Isometric/planksBroken_W.png new file mode 100644 index 0000000..a5d143d Binary files /dev/null and b/client/assets/maps/Isometric/planksBroken_W.png differ diff --git a/client/assets/maps/Isometric/planksHole_E.png b/client/assets/maps/Isometric/planksHole_E.png new file mode 100644 index 0000000..67f35df Binary files /dev/null and b/client/assets/maps/Isometric/planksHole_E.png differ diff --git a/client/assets/maps/Isometric/planksHole_N.png b/client/assets/maps/Isometric/planksHole_N.png new file mode 100644 index 0000000..1d92f40 Binary files /dev/null and b/client/assets/maps/Isometric/planksHole_N.png differ diff --git a/client/assets/maps/Isometric/planksHole_S.png b/client/assets/maps/Isometric/planksHole_S.png new file mode 100644 index 0000000..c757e71 Binary files /dev/null and b/client/assets/maps/Isometric/planksHole_S.png differ diff --git a/client/assets/maps/Isometric/planksHole_W.png b/client/assets/maps/Isometric/planksHole_W.png new file mode 100644 index 0000000..367201f Binary files /dev/null and b/client/assets/maps/Isometric/planksHole_W.png differ diff --git a/client/assets/maps/Isometric/planks_E.png b/client/assets/maps/Isometric/planks_E.png new file mode 100644 index 0000000..7d51caa Binary files /dev/null and b/client/assets/maps/Isometric/planks_E.png differ diff --git a/client/assets/maps/Isometric/planks_N.png b/client/assets/maps/Isometric/planks_N.png new file mode 100644 index 0000000..5072b43 Binary files /dev/null and b/client/assets/maps/Isometric/planks_N.png differ diff --git a/client/assets/maps/Isometric/planks_S.png b/client/assets/maps/Isometric/planks_S.png new file mode 100644 index 0000000..ac0f55e Binary files /dev/null and b/client/assets/maps/Isometric/planks_S.png differ diff --git a/client/assets/maps/Isometric/planks_W.png b/client/assets/maps/Isometric/planks_W.png new file mode 100644 index 0000000..1c7b91d Binary files /dev/null and b/client/assets/maps/Isometric/planks_W.png differ diff --git a/client/assets/maps/Isometric/stairsAged_E.png b/client/assets/maps/Isometric/stairsAged_E.png new file mode 100644 index 0000000..b04fdc0 Binary files /dev/null and b/client/assets/maps/Isometric/stairsAged_E.png differ diff --git a/client/assets/maps/Isometric/stairsAged_N.png b/client/assets/maps/Isometric/stairsAged_N.png new file mode 100644 index 0000000..55d2a6f Binary files /dev/null and b/client/assets/maps/Isometric/stairsAged_N.png differ diff --git a/client/assets/maps/Isometric/stairsAged_S.png b/client/assets/maps/Isometric/stairsAged_S.png new file mode 100644 index 0000000..2f92129 Binary files /dev/null and b/client/assets/maps/Isometric/stairsAged_S.png differ diff --git a/client/assets/maps/Isometric/stairsAged_W.png b/client/assets/maps/Isometric/stairsAged_W.png new file mode 100644 index 0000000..976cbb8 Binary files /dev/null and b/client/assets/maps/Isometric/stairsAged_W.png differ diff --git a/client/assets/maps/Isometric/stairsCorner_E.png b/client/assets/maps/Isometric/stairsCorner_E.png new file mode 100644 index 0000000..38d3b23 Binary files /dev/null and b/client/assets/maps/Isometric/stairsCorner_E.png differ diff --git a/client/assets/maps/Isometric/stairsCorner_N.png b/client/assets/maps/Isometric/stairsCorner_N.png new file mode 100644 index 0000000..2d4c1d1 Binary files /dev/null and b/client/assets/maps/Isometric/stairsCorner_N.png differ diff --git a/client/assets/maps/Isometric/stairsCorner_S.png b/client/assets/maps/Isometric/stairsCorner_S.png new file mode 100644 index 0000000..6e00823 Binary files /dev/null and b/client/assets/maps/Isometric/stairsCorner_S.png differ diff --git a/client/assets/maps/Isometric/stairsCorner_W.png b/client/assets/maps/Isometric/stairsCorner_W.png new file mode 100644 index 0000000..8dfd8d4 Binary files /dev/null and b/client/assets/maps/Isometric/stairsCorner_W.png differ diff --git a/client/assets/maps/Isometric/stairsSpiral_E.png b/client/assets/maps/Isometric/stairsSpiral_E.png new file mode 100644 index 0000000..a83961b Binary files /dev/null and b/client/assets/maps/Isometric/stairsSpiral_E.png differ diff --git a/client/assets/maps/Isometric/stairsSpiral_N.png b/client/assets/maps/Isometric/stairsSpiral_N.png new file mode 100644 index 0000000..353fff3 Binary files /dev/null and b/client/assets/maps/Isometric/stairsSpiral_N.png differ diff --git a/client/assets/maps/Isometric/stairsSpiral_S.png b/client/assets/maps/Isometric/stairsSpiral_S.png new file mode 100644 index 0000000..ab6e69c Binary files /dev/null and b/client/assets/maps/Isometric/stairsSpiral_S.png differ diff --git a/client/assets/maps/Isometric/stairsSpiral_W.png b/client/assets/maps/Isometric/stairsSpiral_W.png new file mode 100644 index 0000000..d02324d Binary files /dev/null and b/client/assets/maps/Isometric/stairsSpiral_W.png differ diff --git a/client/assets/maps/Isometric/stairs_E.png b/client/assets/maps/Isometric/stairs_E.png new file mode 100644 index 0000000..d2a75a8 Binary files /dev/null and b/client/assets/maps/Isometric/stairs_E.png differ diff --git a/client/assets/maps/Isometric/stairs_N.png b/client/assets/maps/Isometric/stairs_N.png new file mode 100644 index 0000000..cc72f45 Binary files /dev/null and b/client/assets/maps/Isometric/stairs_N.png differ diff --git a/client/assets/maps/Isometric/stairs_S.png b/client/assets/maps/Isometric/stairs_S.png new file mode 100644 index 0000000..b964acd Binary files /dev/null and b/client/assets/maps/Isometric/stairs_S.png differ diff --git a/client/assets/maps/Isometric/stairs_W.png b/client/assets/maps/Isometric/stairs_W.png new file mode 100644 index 0000000..df4b6f4 Binary files /dev/null and b/client/assets/maps/Isometric/stairs_W.png differ diff --git a/client/assets/maps/Isometric/stoneColumnWood_E.png b/client/assets/maps/Isometric/stoneColumnWood_E.png new file mode 100644 index 0000000..f47031a Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumnWood_E.png differ diff --git a/client/assets/maps/Isometric/stoneColumnWood_N.png b/client/assets/maps/Isometric/stoneColumnWood_N.png new file mode 100644 index 0000000..f3da58f Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumnWood_N.png differ diff --git a/client/assets/maps/Isometric/stoneColumnWood_S.png b/client/assets/maps/Isometric/stoneColumnWood_S.png new file mode 100644 index 0000000..3013017 Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumnWood_S.png differ diff --git a/client/assets/maps/Isometric/stoneColumnWood_W.png b/client/assets/maps/Isometric/stoneColumnWood_W.png new file mode 100644 index 0000000..a0eed17 Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumnWood_W.png differ diff --git a/client/assets/maps/Isometric/stoneColumn_E.png b/client/assets/maps/Isometric/stoneColumn_E.png new file mode 100644 index 0000000..d0d7dc9 Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumn_E.png differ diff --git a/client/assets/maps/Isometric/stoneColumn_N.png b/client/assets/maps/Isometric/stoneColumn_N.png new file mode 100644 index 0000000..c153f57 Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumn_N.png differ diff --git a/client/assets/maps/Isometric/stoneColumn_S.png b/client/assets/maps/Isometric/stoneColumn_S.png new file mode 100644 index 0000000..9f4b175 Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumn_S.png differ diff --git a/client/assets/maps/Isometric/stoneColumn_W.png b/client/assets/maps/Isometric/stoneColumn_W.png new file mode 100644 index 0000000..6279b95 Binary files /dev/null and b/client/assets/maps/Isometric/stoneColumn_W.png differ diff --git a/client/assets/maps/Isometric/stoneCorner_E.png b/client/assets/maps/Isometric/stoneCorner_E.png new file mode 100644 index 0000000..9dc99de Binary files /dev/null and b/client/assets/maps/Isometric/stoneCorner_E.png differ diff --git a/client/assets/maps/Isometric/stoneCorner_N.png b/client/assets/maps/Isometric/stoneCorner_N.png new file mode 100644 index 0000000..c4f6d67 Binary files /dev/null and b/client/assets/maps/Isometric/stoneCorner_N.png differ diff --git a/client/assets/maps/Isometric/stoneCorner_S.png b/client/assets/maps/Isometric/stoneCorner_S.png new file mode 100644 index 0000000..960ade4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneCorner_S.png differ diff --git a/client/assets/maps/Isometric/stoneCorner_W.png b/client/assets/maps/Isometric/stoneCorner_W.png new file mode 100644 index 0000000..42294d9 Binary files /dev/null and b/client/assets/maps/Isometric/stoneCorner_W.png differ diff --git a/client/assets/maps/Isometric/stoneInset_E.png b/client/assets/maps/Isometric/stoneInset_E.png new file mode 100644 index 0000000..d3ec462 Binary files /dev/null and b/client/assets/maps/Isometric/stoneInset_E.png differ diff --git a/client/assets/maps/Isometric/stoneInset_N.png b/client/assets/maps/Isometric/stoneInset_N.png new file mode 100644 index 0000000..58aa8af Binary files /dev/null and b/client/assets/maps/Isometric/stoneInset_N.png differ diff --git a/client/assets/maps/Isometric/stoneInset_S.png b/client/assets/maps/Isometric/stoneInset_S.png new file mode 100644 index 0000000..50dfe1f Binary files /dev/null and b/client/assets/maps/Isometric/stoneInset_S.png differ diff --git a/client/assets/maps/Isometric/stoneInset_W.png b/client/assets/maps/Isometric/stoneInset_W.png new file mode 100644 index 0000000..dc2d12a Binary files /dev/null and b/client/assets/maps/Isometric/stoneInset_W.png differ diff --git a/client/assets/maps/Isometric/stoneLeft_E.png b/client/assets/maps/Isometric/stoneLeft_E.png new file mode 100644 index 0000000..097a611 Binary files /dev/null and b/client/assets/maps/Isometric/stoneLeft_E.png differ diff --git a/client/assets/maps/Isometric/stoneLeft_N.png b/client/assets/maps/Isometric/stoneLeft_N.png new file mode 100644 index 0000000..e72b5d6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneLeft_N.png differ diff --git a/client/assets/maps/Isometric/stoneLeft_S.png b/client/assets/maps/Isometric/stoneLeft_S.png new file mode 100644 index 0000000..a8a1e7b Binary files /dev/null and b/client/assets/maps/Isometric/stoneLeft_S.png differ diff --git a/client/assets/maps/Isometric/stoneLeft_W.png b/client/assets/maps/Isometric/stoneLeft_W.png new file mode 100644 index 0000000..66b3ed2 Binary files /dev/null and b/client/assets/maps/Isometric/stoneLeft_W.png differ diff --git a/client/assets/maps/Isometric/stoneMissingTiles_E.png b/client/assets/maps/Isometric/stoneMissingTiles_E.png new file mode 100644 index 0000000..1d34564 Binary files /dev/null and b/client/assets/maps/Isometric/stoneMissingTiles_E.png differ diff --git a/client/assets/maps/Isometric/stoneMissingTiles_N.png b/client/assets/maps/Isometric/stoneMissingTiles_N.png new file mode 100644 index 0000000..b28bc4f Binary files /dev/null and b/client/assets/maps/Isometric/stoneMissingTiles_N.png differ diff --git a/client/assets/maps/Isometric/stoneMissingTiles_S.png b/client/assets/maps/Isometric/stoneMissingTiles_S.png new file mode 100644 index 0000000..062a4cb Binary files /dev/null and b/client/assets/maps/Isometric/stoneMissingTiles_S.png differ diff --git a/client/assets/maps/Isometric/stoneMissingTiles_W.png b/client/assets/maps/Isometric/stoneMissingTiles_W.png new file mode 100644 index 0000000..9c91607 Binary files /dev/null and b/client/assets/maps/Isometric/stoneMissingTiles_W.png differ diff --git a/client/assets/maps/Isometric/stoneRight_E.png b/client/assets/maps/Isometric/stoneRight_E.png new file mode 100644 index 0000000..b168ec0 Binary files /dev/null and b/client/assets/maps/Isometric/stoneRight_E.png differ diff --git a/client/assets/maps/Isometric/stoneRight_N.png b/client/assets/maps/Isometric/stoneRight_N.png new file mode 100644 index 0000000..7db312f Binary files /dev/null and b/client/assets/maps/Isometric/stoneRight_N.png differ diff --git a/client/assets/maps/Isometric/stoneRight_S.png b/client/assets/maps/Isometric/stoneRight_S.png new file mode 100644 index 0000000..a77e1f7 Binary files /dev/null and b/client/assets/maps/Isometric/stoneRight_S.png differ diff --git a/client/assets/maps/Isometric/stoneRight_W.png b/client/assets/maps/Isometric/stoneRight_W.png new file mode 100644 index 0000000..a5658bc Binary files /dev/null and b/client/assets/maps/Isometric/stoneRight_W.png differ diff --git a/client/assets/maps/Isometric/stoneSideUneven_E.png b/client/assets/maps/Isometric/stoneSideUneven_E.png new file mode 100644 index 0000000..85748f4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSideUneven_E.png differ diff --git a/client/assets/maps/Isometric/stoneSideUneven_N.png b/client/assets/maps/Isometric/stoneSideUneven_N.png new file mode 100644 index 0000000..9219756 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSideUneven_N.png differ diff --git a/client/assets/maps/Isometric/stoneSideUneven_S.png b/client/assets/maps/Isometric/stoneSideUneven_S.png new file mode 100644 index 0000000..0c0a8b7 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSideUneven_S.png differ diff --git a/client/assets/maps/Isometric/stoneSideUneven_W.png b/client/assets/maps/Isometric/stoneSideUneven_W.png new file mode 100644 index 0000000..d4d0381 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSideUneven_W.png differ diff --git a/client/assets/maps/Isometric/stoneSide_E.png b/client/assets/maps/Isometric/stoneSide_E.png new file mode 100644 index 0000000..9c5f4c2 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSide_E.png differ diff --git a/client/assets/maps/Isometric/stoneSide_N.png b/client/assets/maps/Isometric/stoneSide_N.png new file mode 100644 index 0000000..c97b823 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSide_N.png differ diff --git a/client/assets/maps/Isometric/stoneSide_S.png b/client/assets/maps/Isometric/stoneSide_S.png new file mode 100644 index 0000000..541f30c Binary files /dev/null and b/client/assets/maps/Isometric/stoneSide_S.png differ diff --git a/client/assets/maps/Isometric/stoneSide_W.png b/client/assets/maps/Isometric/stoneSide_W.png new file mode 100644 index 0000000..5583d02 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSide_W.png differ diff --git a/client/assets/maps/Isometric/stoneStep_E.png b/client/assets/maps/Isometric/stoneStep_E.png new file mode 100644 index 0000000..551a2ad Binary files /dev/null and b/client/assets/maps/Isometric/stoneStep_E.png differ diff --git a/client/assets/maps/Isometric/stoneStep_N.png b/client/assets/maps/Isometric/stoneStep_N.png new file mode 100644 index 0000000..1ec600f Binary files /dev/null and b/client/assets/maps/Isometric/stoneStep_N.png differ diff --git a/client/assets/maps/Isometric/stoneStep_S.png b/client/assets/maps/Isometric/stoneStep_S.png new file mode 100644 index 0000000..591541f Binary files /dev/null and b/client/assets/maps/Isometric/stoneStep_S.png differ diff --git a/client/assets/maps/Isometric/stoneStep_W.png b/client/assets/maps/Isometric/stoneStep_W.png new file mode 100644 index 0000000..7108df8 Binary files /dev/null and b/client/assets/maps/Isometric/stoneStep_W.png differ diff --git a/client/assets/maps/Isometric/stoneSteps_E.png b/client/assets/maps/Isometric/stoneSteps_E.png new file mode 100644 index 0000000..c63eb3c Binary files /dev/null and b/client/assets/maps/Isometric/stoneSteps_E.png differ diff --git a/client/assets/maps/Isometric/stoneSteps_N.png b/client/assets/maps/Isometric/stoneSteps_N.png new file mode 100644 index 0000000..c953187 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSteps_N.png differ diff --git a/client/assets/maps/Isometric/stoneSteps_S.png b/client/assets/maps/Isometric/stoneSteps_S.png new file mode 100644 index 0000000..4f1c32e Binary files /dev/null and b/client/assets/maps/Isometric/stoneSteps_S.png differ diff --git a/client/assets/maps/Isometric/stoneSteps_W.png b/client/assets/maps/Isometric/stoneSteps_W.png new file mode 100644 index 0000000..c2c0338 Binary files /dev/null and b/client/assets/maps/Isometric/stoneSteps_W.png differ diff --git a/client/assets/maps/Isometric/stoneTile_E.png b/client/assets/maps/Isometric/stoneTile_E.png new file mode 100644 index 0000000..6f00fe6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneTile_E.png differ diff --git a/client/assets/maps/Isometric/stoneTile_N.png b/client/assets/maps/Isometric/stoneTile_N.png new file mode 100644 index 0000000..27af0c8 Binary files /dev/null and b/client/assets/maps/Isometric/stoneTile_N.png differ diff --git a/client/assets/maps/Isometric/stoneTile_S.png b/client/assets/maps/Isometric/stoneTile_S.png new file mode 100644 index 0000000..4ca4505 Binary files /dev/null and b/client/assets/maps/Isometric/stoneTile_S.png differ diff --git a/client/assets/maps/Isometric/stoneTile_W.png b/client/assets/maps/Isometric/stoneTile_W.png new file mode 100644 index 0000000..41e3250 Binary files /dev/null and b/client/assets/maps/Isometric/stoneTile_W.png differ diff --git a/client/assets/maps/Isometric/stoneUneven_E.png b/client/assets/maps/Isometric/stoneUneven_E.png new file mode 100644 index 0000000..c9eafb4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneUneven_E.png differ diff --git a/client/assets/maps/Isometric/stoneUneven_N.png b/client/assets/maps/Isometric/stoneUneven_N.png new file mode 100644 index 0000000..4b884bd Binary files /dev/null and b/client/assets/maps/Isometric/stoneUneven_N.png differ diff --git a/client/assets/maps/Isometric/stoneUneven_S.png b/client/assets/maps/Isometric/stoneUneven_S.png new file mode 100644 index 0000000..c56dbf1 Binary files /dev/null and b/client/assets/maps/Isometric/stoneUneven_S.png differ diff --git a/client/assets/maps/Isometric/stoneUneven_W.png b/client/assets/maps/Isometric/stoneUneven_W.png new file mode 100644 index 0000000..319537b Binary files /dev/null and b/client/assets/maps/Isometric/stoneUneven_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedLeft_E.png b/client/assets/maps/Isometric/stoneWallAgedLeft_E.png new file mode 100644 index 0000000..010a3db Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedLeft_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedLeft_N.png b/client/assets/maps/Isometric/stoneWallAgedLeft_N.png new file mode 100644 index 0000000..39e48d3 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedLeft_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedLeft_S.png b/client/assets/maps/Isometric/stoneWallAgedLeft_S.png new file mode 100644 index 0000000..61079e9 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedLeft_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedLeft_W.png b/client/assets/maps/Isometric/stoneWallAgedLeft_W.png new file mode 100644 index 0000000..0feafcb Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedLeft_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedRight_E.png b/client/assets/maps/Isometric/stoneWallAgedRight_E.png new file mode 100644 index 0000000..6cd5c4c Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedRight_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedRight_N.png b/client/assets/maps/Isometric/stoneWallAgedRight_N.png new file mode 100644 index 0000000..c7b6621 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedRight_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedRight_S.png b/client/assets/maps/Isometric/stoneWallAgedRight_S.png new file mode 100644 index 0000000..fbf61ab Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedRight_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallAgedRight_W.png b/client/assets/maps/Isometric/stoneWallAgedRight_W.png new file mode 100644 index 0000000..08ad0c2 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAgedRight_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallAged_E.png b/client/assets/maps/Isometric/stoneWallAged_E.png new file mode 100644 index 0000000..c850826 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAged_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallAged_N.png b/client/assets/maps/Isometric/stoneWallAged_N.png new file mode 100644 index 0000000..fbbf0d6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAged_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallAged_S.png b/client/assets/maps/Isometric/stoneWallAged_S.png new file mode 100644 index 0000000..8bc2c04 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAged_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallAged_W.png b/client/assets/maps/Isometric/stoneWallAged_W.png new file mode 100644 index 0000000..9cb6b50 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallAged_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallArchway_E.png b/client/assets/maps/Isometric/stoneWallArchway_E.png new file mode 100644 index 0000000..9c670df Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallArchway_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallArchway_N.png b/client/assets/maps/Isometric/stoneWallArchway_N.png new file mode 100644 index 0000000..b2fa8e4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallArchway_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallArchway_S.png b/client/assets/maps/Isometric/stoneWallArchway_S.png new file mode 100644 index 0000000..7819e8b Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallArchway_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallArchway_W.png b/client/assets/maps/Isometric/stoneWallArchway_W.png new file mode 100644 index 0000000..d4871fd Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallArchway_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenLeft_E.png b/client/assets/maps/Isometric/stoneWallBrokenLeft_E.png new file mode 100644 index 0000000..2d575a4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenLeft_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenLeft_N.png b/client/assets/maps/Isometric/stoneWallBrokenLeft_N.png new file mode 100644 index 0000000..c09e525 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenLeft_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenLeft_S.png b/client/assets/maps/Isometric/stoneWallBrokenLeft_S.png new file mode 100644 index 0000000..f726c75 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenLeft_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenLeft_W.png b/client/assets/maps/Isometric/stoneWallBrokenLeft_W.png new file mode 100644 index 0000000..2023ef9 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenLeft_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenRight_E.png b/client/assets/maps/Isometric/stoneWallBrokenRight_E.png new file mode 100644 index 0000000..fe68156 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenRight_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenRight_N.png b/client/assets/maps/Isometric/stoneWallBrokenRight_N.png new file mode 100644 index 0000000..20dfd51 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenRight_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenRight_S.png b/client/assets/maps/Isometric/stoneWallBrokenRight_S.png new file mode 100644 index 0000000..c2c7b3b Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenRight_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallBrokenRight_W.png b/client/assets/maps/Isometric/stoneWallBrokenRight_W.png new file mode 100644 index 0000000..c7d79fb Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBrokenRight_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallBroken_E.png b/client/assets/maps/Isometric/stoneWallBroken_E.png new file mode 100644 index 0000000..abd63f6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBroken_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallBroken_N.png b/client/assets/maps/Isometric/stoneWallBroken_N.png new file mode 100644 index 0000000..0758cb7 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBroken_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallBroken_S.png b/client/assets/maps/Isometric/stoneWallBroken_S.png new file mode 100644 index 0000000..8785cf4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBroken_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallBroken_W.png b/client/assets/maps/Isometric/stoneWallBroken_W.png new file mode 100644 index 0000000..f514520 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallBroken_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumnIn_E.png b/client/assets/maps/Isometric/stoneWallColumnIn_E.png new file mode 100644 index 0000000..fc39750 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumnIn_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumnIn_N.png b/client/assets/maps/Isometric/stoneWallColumnIn_N.png new file mode 100644 index 0000000..a7b3091 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumnIn_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumnIn_S.png b/client/assets/maps/Isometric/stoneWallColumnIn_S.png new file mode 100644 index 0000000..df88ba4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumnIn_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumnIn_W.png b/client/assets/maps/Isometric/stoneWallColumnIn_W.png new file mode 100644 index 0000000..38d3ff3 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumnIn_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumn_E.png b/client/assets/maps/Isometric/stoneWallColumn_E.png new file mode 100644 index 0000000..a63d8e8 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumn_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumn_N.png b/client/assets/maps/Isometric/stoneWallColumn_N.png new file mode 100644 index 0000000..660c7c2 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumn_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumn_S.png b/client/assets/maps/Isometric/stoneWallColumn_S.png new file mode 100644 index 0000000..38705e4 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumn_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallColumn_W.png b/client/assets/maps/Isometric/stoneWallColumn_W.png new file mode 100644 index 0000000..c6386a6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallColumn_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallCorner_E.png b/client/assets/maps/Isometric/stoneWallCorner_E.png new file mode 100644 index 0000000..e7ef38f Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallCorner_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallCorner_N.png b/client/assets/maps/Isometric/stoneWallCorner_N.png new file mode 100644 index 0000000..7f7dffd Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallCorner_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallCorner_S.png b/client/assets/maps/Isometric/stoneWallCorner_S.png new file mode 100644 index 0000000..defb582 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallCorner_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallCorner_W.png b/client/assets/maps/Isometric/stoneWallCorner_W.png new file mode 100644 index 0000000..0964303 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallCorner_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorBars_E.png b/client/assets/maps/Isometric/stoneWallDoorBars_E.png new file mode 100644 index 0000000..aa1db6f Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorBars_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorBars_N.png b/client/assets/maps/Isometric/stoneWallDoorBars_N.png new file mode 100644 index 0000000..6abc5b0 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorBars_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorBars_S.png b/client/assets/maps/Isometric/stoneWallDoorBars_S.png new file mode 100644 index 0000000..dbacc81 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorBars_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorBars_W.png b/client/assets/maps/Isometric/stoneWallDoorBars_W.png new file mode 100644 index 0000000..4cdb117 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorBars_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorClosed_E.png b/client/assets/maps/Isometric/stoneWallDoorClosed_E.png new file mode 100644 index 0000000..0972124 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorClosed_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorClosed_N.png b/client/assets/maps/Isometric/stoneWallDoorClosed_N.png new file mode 100644 index 0000000..4cdfdf3 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorClosed_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorClosed_S.png b/client/assets/maps/Isometric/stoneWallDoorClosed_S.png new file mode 100644 index 0000000..3c972df Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorClosed_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorClosed_W.png b/client/assets/maps/Isometric/stoneWallDoorClosed_W.png new file mode 100644 index 0000000..fdf9a76 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorClosed_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorOpen_E.png b/client/assets/maps/Isometric/stoneWallDoorOpen_E.png new file mode 100644 index 0000000..d38ce46 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorOpen_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorOpen_N.png b/client/assets/maps/Isometric/stoneWallDoorOpen_N.png new file mode 100644 index 0000000..a5140dc Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorOpen_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorOpen_S.png b/client/assets/maps/Isometric/stoneWallDoorOpen_S.png new file mode 100644 index 0000000..e5c7fdc Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorOpen_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoorOpen_W.png b/client/assets/maps/Isometric/stoneWallDoorOpen_W.png new file mode 100644 index 0000000..22819c2 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoorOpen_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoor_E.png b/client/assets/maps/Isometric/stoneWallDoor_E.png new file mode 100644 index 0000000..f7a3861 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoor_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoor_N.png b/client/assets/maps/Isometric/stoneWallDoor_N.png new file mode 100644 index 0000000..3aa18a6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoor_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoor_S.png b/client/assets/maps/Isometric/stoneWallDoor_S.png new file mode 100644 index 0000000..0753aaa Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoor_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallDoor_W.png b/client/assets/maps/Isometric/stoneWallDoor_W.png new file mode 100644 index 0000000..1109347 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallDoor_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateBars_E.png b/client/assets/maps/Isometric/stoneWallGateBars_E.png new file mode 100644 index 0000000..1f277e8 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateBars_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateBars_N.png b/client/assets/maps/Isometric/stoneWallGateBars_N.png new file mode 100644 index 0000000..0c3969a Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateBars_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateBars_S.png b/client/assets/maps/Isometric/stoneWallGateBars_S.png new file mode 100644 index 0000000..edc9e49 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateBars_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateBars_W.png b/client/assets/maps/Isometric/stoneWallGateBars_W.png new file mode 100644 index 0000000..f55adb2 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateBars_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateClosed_E.png b/client/assets/maps/Isometric/stoneWallGateClosed_E.png new file mode 100644 index 0000000..3e765ed Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateClosed_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateClosed_N.png b/client/assets/maps/Isometric/stoneWallGateClosed_N.png new file mode 100644 index 0000000..0229c48 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateClosed_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateClosed_S.png b/client/assets/maps/Isometric/stoneWallGateClosed_S.png new file mode 100644 index 0000000..bd29711 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateClosed_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateClosed_W.png b/client/assets/maps/Isometric/stoneWallGateClosed_W.png new file mode 100644 index 0000000..977f511 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateClosed_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateOpen_E.png b/client/assets/maps/Isometric/stoneWallGateOpen_E.png new file mode 100644 index 0000000..22621cd Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateOpen_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateOpen_N.png b/client/assets/maps/Isometric/stoneWallGateOpen_N.png new file mode 100644 index 0000000..6a56a0e Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateOpen_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateOpen_S.png b/client/assets/maps/Isometric/stoneWallGateOpen_S.png new file mode 100644 index 0000000..f4cfe28 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateOpen_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallGateOpen_W.png b/client/assets/maps/Isometric/stoneWallGateOpen_W.png new file mode 100644 index 0000000..67d7b61 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGateOpen_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallGate_E.png b/client/assets/maps/Isometric/stoneWallGate_E.png new file mode 100644 index 0000000..1f2fe88 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGate_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallGate_N.png b/client/assets/maps/Isometric/stoneWallGate_N.png new file mode 100644 index 0000000..3707af5 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGate_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallGate_S.png b/client/assets/maps/Isometric/stoneWallGate_S.png new file mode 100644 index 0000000..61865ba Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGate_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallGate_W.png b/client/assets/maps/Isometric/stoneWallGate_W.png new file mode 100644 index 0000000..7effe39 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallGate_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallHalf_E.png b/client/assets/maps/Isometric/stoneWallHalf_E.png new file mode 100644 index 0000000..292bcae Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHalf_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallHalf_N.png b/client/assets/maps/Isometric/stoneWallHalf_N.png new file mode 100644 index 0000000..3f1906f Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHalf_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallHalf_S.png b/client/assets/maps/Isometric/stoneWallHalf_S.png new file mode 100644 index 0000000..45f86de Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHalf_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallHalf_W.png b/client/assets/maps/Isometric/stoneWallHalf_W.png new file mode 100644 index 0000000..4de6b33 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHalf_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallHole_E.png b/client/assets/maps/Isometric/stoneWallHole_E.png new file mode 100644 index 0000000..872b087 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHole_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallHole_N.png b/client/assets/maps/Isometric/stoneWallHole_N.png new file mode 100644 index 0000000..9bd3eab Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHole_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallHole_S.png b/client/assets/maps/Isometric/stoneWallHole_S.png new file mode 100644 index 0000000..daebafd Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHole_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallHole_W.png b/client/assets/maps/Isometric/stoneWallHole_W.png new file mode 100644 index 0000000..79b2c82 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallHole_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundBroken_E.png b/client/assets/maps/Isometric/stoneWallRoundBroken_E.png new file mode 100644 index 0000000..a8dea57 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundBroken_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundBroken_N.png b/client/assets/maps/Isometric/stoneWallRoundBroken_N.png new file mode 100644 index 0000000..2113cc6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundBroken_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundBroken_S.png b/client/assets/maps/Isometric/stoneWallRoundBroken_S.png new file mode 100644 index 0000000..cea849a Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundBroken_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundBroken_W.png b/client/assets/maps/Isometric/stoneWallRoundBroken_W.png new file mode 100644 index 0000000..4badbb5 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundBroken_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundWindow_E.png b/client/assets/maps/Isometric/stoneWallRoundWindow_E.png new file mode 100644 index 0000000..62f26ab Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundWindow_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundWindow_N.png b/client/assets/maps/Isometric/stoneWallRoundWindow_N.png new file mode 100644 index 0000000..85dbc48 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundWindow_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundWindow_S.png b/client/assets/maps/Isometric/stoneWallRoundWindow_S.png new file mode 100644 index 0000000..899e820 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundWindow_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallRoundWindow_W.png b/client/assets/maps/Isometric/stoneWallRoundWindow_W.png new file mode 100644 index 0000000..3e8e63d Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRoundWindow_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallRound_E.png b/client/assets/maps/Isometric/stoneWallRound_E.png new file mode 100644 index 0000000..38054f3 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRound_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallRound_N.png b/client/assets/maps/Isometric/stoneWallRound_N.png new file mode 100644 index 0000000..af4b965 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRound_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallRound_S.png b/client/assets/maps/Isometric/stoneWallRound_S.png new file mode 100644 index 0000000..c5791ab Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRound_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallRound_W.png b/client/assets/maps/Isometric/stoneWallRound_W.png new file mode 100644 index 0000000..a181d9a Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallRound_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallStructure_E.png b/client/assets/maps/Isometric/stoneWallStructure_E.png new file mode 100644 index 0000000..aea286b Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallStructure_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallStructure_N.png b/client/assets/maps/Isometric/stoneWallStructure_N.png new file mode 100644 index 0000000..01c5823 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallStructure_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallStructure_S.png b/client/assets/maps/Isometric/stoneWallStructure_S.png new file mode 100644 index 0000000..a931456 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallStructure_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallStructure_W.png b/client/assets/maps/Isometric/stoneWallStructure_W.png new file mode 100644 index 0000000..bec9c18 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallStructure_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallTop_E.png b/client/assets/maps/Isometric/stoneWallTop_E.png new file mode 100644 index 0000000..e0a0d55 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallTop_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallTop_N.png b/client/assets/maps/Isometric/stoneWallTop_N.png new file mode 100644 index 0000000..867e32d Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallTop_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallTop_S.png b/client/assets/maps/Isometric/stoneWallTop_S.png new file mode 100644 index 0000000..5d5193e Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallTop_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallTop_W.png b/client/assets/maps/Isometric/stoneWallTop_W.png new file mode 100644 index 0000000..fff0071 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallTop_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindowBars_E.png b/client/assets/maps/Isometric/stoneWallWindowBars_E.png new file mode 100644 index 0000000..b04c3a9 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindowBars_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindowBars_N.png b/client/assets/maps/Isometric/stoneWallWindowBars_N.png new file mode 100644 index 0000000..e7a6a80 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindowBars_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindowBars_S.png b/client/assets/maps/Isometric/stoneWallWindowBars_S.png new file mode 100644 index 0000000..a4c6cc6 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindowBars_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindowBars_W.png b/client/assets/maps/Isometric/stoneWallWindowBars_W.png new file mode 100644 index 0000000..f500d29 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindowBars_W.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindow_E.png b/client/assets/maps/Isometric/stoneWallWindow_E.png new file mode 100644 index 0000000..b633115 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindow_E.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindow_N.png b/client/assets/maps/Isometric/stoneWallWindow_N.png new file mode 100644 index 0000000..0a767ae Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindow_N.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindow_S.png b/client/assets/maps/Isometric/stoneWallWindow_S.png new file mode 100644 index 0000000..90e36fa Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindow_S.png differ diff --git a/client/assets/maps/Isometric/stoneWallWindow_W.png b/client/assets/maps/Isometric/stoneWallWindow_W.png new file mode 100644 index 0000000..0aa4e40 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWallWindow_W.png differ diff --git a/client/assets/maps/Isometric/stoneWall_E.png b/client/assets/maps/Isometric/stoneWall_E.png new file mode 100644 index 0000000..fae9460 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWall_E.png differ diff --git a/client/assets/maps/Isometric/stoneWall_N.png b/client/assets/maps/Isometric/stoneWall_N.png new file mode 100644 index 0000000..4c3c669 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWall_N.png differ diff --git a/client/assets/maps/Isometric/stoneWall_S.png b/client/assets/maps/Isometric/stoneWall_S.png new file mode 100644 index 0000000..4ba7ecb Binary files /dev/null and b/client/assets/maps/Isometric/stoneWall_S.png differ diff --git a/client/assets/maps/Isometric/stoneWall_W.png b/client/assets/maps/Isometric/stoneWall_W.png new file mode 100644 index 0000000..664b658 Binary files /dev/null and b/client/assets/maps/Isometric/stoneWall_W.png differ diff --git a/client/assets/maps/Isometric/stone_E.png b/client/assets/maps/Isometric/stone_E.png new file mode 100644 index 0000000..e68410b Binary files /dev/null and b/client/assets/maps/Isometric/stone_E.png differ diff --git a/client/assets/maps/Isometric/stone_N.png b/client/assets/maps/Isometric/stone_N.png new file mode 100644 index 0000000..304b265 Binary files /dev/null and b/client/assets/maps/Isometric/stone_N.png differ diff --git a/client/assets/maps/Isometric/stone_S.png b/client/assets/maps/Isometric/stone_S.png new file mode 100644 index 0000000..7901978 Binary files /dev/null and b/client/assets/maps/Isometric/stone_S.png differ diff --git a/client/assets/maps/Isometric/stone_W.png b/client/assets/maps/Isometric/stone_W.png new file mode 100644 index 0000000..f8fe5df Binary files /dev/null and b/client/assets/maps/Isometric/stone_W.png differ diff --git a/client/assets/maps/Isometric/tableChairsBroken_E.png b/client/assets/maps/Isometric/tableChairsBroken_E.png new file mode 100644 index 0000000..48ae4e9 Binary files /dev/null and b/client/assets/maps/Isometric/tableChairsBroken_E.png differ diff --git a/client/assets/maps/Isometric/tableChairsBroken_N.png b/client/assets/maps/Isometric/tableChairsBroken_N.png new file mode 100644 index 0000000..0664956 Binary files /dev/null and b/client/assets/maps/Isometric/tableChairsBroken_N.png differ diff --git a/client/assets/maps/Isometric/tableChairsBroken_S.png b/client/assets/maps/Isometric/tableChairsBroken_S.png new file mode 100644 index 0000000..c463def Binary files /dev/null and b/client/assets/maps/Isometric/tableChairsBroken_S.png differ diff --git a/client/assets/maps/Isometric/tableChairsBroken_W.png b/client/assets/maps/Isometric/tableChairsBroken_W.png new file mode 100644 index 0000000..940834c Binary files /dev/null and b/client/assets/maps/Isometric/tableChairsBroken_W.png differ diff --git a/client/assets/maps/Isometric/tableRoundChairs_E.png b/client/assets/maps/Isometric/tableRoundChairs_E.png new file mode 100644 index 0000000..e49ad6f Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundChairs_E.png differ diff --git a/client/assets/maps/Isometric/tableRoundChairs_N.png b/client/assets/maps/Isometric/tableRoundChairs_N.png new file mode 100644 index 0000000..c30b935 Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundChairs_N.png differ diff --git a/client/assets/maps/Isometric/tableRoundChairs_S.png b/client/assets/maps/Isometric/tableRoundChairs_S.png new file mode 100644 index 0000000..c9a7dca Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundChairs_S.png differ diff --git a/client/assets/maps/Isometric/tableRoundChairs_W.png b/client/assets/maps/Isometric/tableRoundChairs_W.png new file mode 100644 index 0000000..8974b47 Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundChairs_W.png differ diff --git a/client/assets/maps/Isometric/tableRoundItemsChairs_E.png b/client/assets/maps/Isometric/tableRoundItemsChairs_E.png new file mode 100644 index 0000000..79f9c42 Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundItemsChairs_E.png differ diff --git a/client/assets/maps/Isometric/tableRoundItemsChairs_N.png b/client/assets/maps/Isometric/tableRoundItemsChairs_N.png new file mode 100644 index 0000000..f30ac1e Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundItemsChairs_N.png differ diff --git a/client/assets/maps/Isometric/tableRoundItemsChairs_S.png b/client/assets/maps/Isometric/tableRoundItemsChairs_S.png new file mode 100644 index 0000000..16dd219 Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundItemsChairs_S.png differ diff --git a/client/assets/maps/Isometric/tableRoundItemsChairs_W.png b/client/assets/maps/Isometric/tableRoundItemsChairs_W.png new file mode 100644 index 0000000..87d604f Binary files /dev/null and b/client/assets/maps/Isometric/tableRoundItemsChairs_W.png differ diff --git a/client/assets/maps/Isometric/tableRound_E.png b/client/assets/maps/Isometric/tableRound_E.png new file mode 100644 index 0000000..bca77f3 Binary files /dev/null and b/client/assets/maps/Isometric/tableRound_E.png differ diff --git a/client/assets/maps/Isometric/tableRound_N.png b/client/assets/maps/Isometric/tableRound_N.png new file mode 100644 index 0000000..beca065 Binary files /dev/null and b/client/assets/maps/Isometric/tableRound_N.png differ diff --git a/client/assets/maps/Isometric/tableRound_S.png b/client/assets/maps/Isometric/tableRound_S.png new file mode 100644 index 0000000..4139d56 Binary files /dev/null and b/client/assets/maps/Isometric/tableRound_S.png differ diff --git a/client/assets/maps/Isometric/tableRound_W.png b/client/assets/maps/Isometric/tableRound_W.png new file mode 100644 index 0000000..127cfbd Binary files /dev/null and b/client/assets/maps/Isometric/tableRound_W.png differ diff --git a/client/assets/maps/Isometric/tableShortChairs_E.png b/client/assets/maps/Isometric/tableShortChairs_E.png new file mode 100644 index 0000000..22574ab Binary files /dev/null and b/client/assets/maps/Isometric/tableShortChairs_E.png differ diff --git a/client/assets/maps/Isometric/tableShortChairs_N.png b/client/assets/maps/Isometric/tableShortChairs_N.png new file mode 100644 index 0000000..50af37f Binary files /dev/null and b/client/assets/maps/Isometric/tableShortChairs_N.png differ diff --git a/client/assets/maps/Isometric/tableShortChairs_S.png b/client/assets/maps/Isometric/tableShortChairs_S.png new file mode 100644 index 0000000..8eb7db1 Binary files /dev/null and b/client/assets/maps/Isometric/tableShortChairs_S.png differ diff --git a/client/assets/maps/Isometric/tableShortChairs_W.png b/client/assets/maps/Isometric/tableShortChairs_W.png new file mode 100644 index 0000000..0b1f07d Binary files /dev/null and b/client/assets/maps/Isometric/tableShortChairs_W.png differ diff --git a/client/assets/maps/Isometric/tableShort_E.png b/client/assets/maps/Isometric/tableShort_E.png new file mode 100644 index 0000000..0ce49c3 Binary files /dev/null and b/client/assets/maps/Isometric/tableShort_E.png differ diff --git a/client/assets/maps/Isometric/tableShort_N.png b/client/assets/maps/Isometric/tableShort_N.png new file mode 100644 index 0000000..8203b90 Binary files /dev/null and b/client/assets/maps/Isometric/tableShort_N.png differ diff --git a/client/assets/maps/Isometric/tableShort_S.png b/client/assets/maps/Isometric/tableShort_S.png new file mode 100644 index 0000000..aa0fc81 Binary files /dev/null and b/client/assets/maps/Isometric/tableShort_S.png differ diff --git a/client/assets/maps/Isometric/tableShort_W.png b/client/assets/maps/Isometric/tableShort_W.png new file mode 100644 index 0000000..5b676cb Binary files /dev/null and b/client/assets/maps/Isometric/tableShort_W.png differ diff --git a/client/assets/maps/Isometric/woodenCrate_E.png b/client/assets/maps/Isometric/woodenCrate_E.png new file mode 100644 index 0000000..6df1422 Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrate_E.png differ diff --git a/client/assets/maps/Isometric/woodenCrate_N.png b/client/assets/maps/Isometric/woodenCrate_N.png new file mode 100644 index 0000000..7913549 Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrate_N.png differ diff --git a/client/assets/maps/Isometric/woodenCrate_S.png b/client/assets/maps/Isometric/woodenCrate_S.png new file mode 100644 index 0000000..5a6196d Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrate_S.png differ diff --git a/client/assets/maps/Isometric/woodenCrate_W.png b/client/assets/maps/Isometric/woodenCrate_W.png new file mode 100644 index 0000000..032fc89 Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrate_W.png differ diff --git a/client/assets/maps/Isometric/woodenCrates_E.png b/client/assets/maps/Isometric/woodenCrates_E.png new file mode 100644 index 0000000..5e5f52d Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrates_E.png differ diff --git a/client/assets/maps/Isometric/woodenCrates_N.png b/client/assets/maps/Isometric/woodenCrates_N.png new file mode 100644 index 0000000..69b6276 Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrates_N.png differ diff --git a/client/assets/maps/Isometric/woodenCrates_S.png b/client/assets/maps/Isometric/woodenCrates_S.png new file mode 100644 index 0000000..83dd5bb Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrates_S.png differ diff --git a/client/assets/maps/Isometric/woodenCrates_W.png b/client/assets/maps/Isometric/woodenCrates_W.png new file mode 100644 index 0000000..8853473 Binary files /dev/null and b/client/assets/maps/Isometric/woodenCrates_W.png differ diff --git a/client/assets/maps/Isometric/woodenPile_E.png b/client/assets/maps/Isometric/woodenPile_E.png new file mode 100644 index 0000000..d247cd6 Binary files /dev/null and b/client/assets/maps/Isometric/woodenPile_E.png differ diff --git a/client/assets/maps/Isometric/woodenPile_N.png b/client/assets/maps/Isometric/woodenPile_N.png new file mode 100644 index 0000000..8bfd822 Binary files /dev/null and b/client/assets/maps/Isometric/woodenPile_N.png differ diff --git a/client/assets/maps/Isometric/woodenPile_S.png b/client/assets/maps/Isometric/woodenPile_S.png new file mode 100644 index 0000000..e897bbc Binary files /dev/null and b/client/assets/maps/Isometric/woodenPile_S.png differ diff --git a/client/assets/maps/Isometric/woodenPile_W.png b/client/assets/maps/Isometric/woodenPile_W.png new file mode 100644 index 0000000..e59476c Binary files /dev/null and b/client/assets/maps/Isometric/woodenPile_W.png differ diff --git a/client/assets/maps/Isometric/woodenSupportBeams_E.png b/client/assets/maps/Isometric/woodenSupportBeams_E.png new file mode 100644 index 0000000..ed85ad2 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportBeams_E.png differ diff --git a/client/assets/maps/Isometric/woodenSupportBeams_N.png b/client/assets/maps/Isometric/woodenSupportBeams_N.png new file mode 100644 index 0000000..48c47d5 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportBeams_N.png differ diff --git a/client/assets/maps/Isometric/woodenSupportBeams_S.png b/client/assets/maps/Isometric/woodenSupportBeams_S.png new file mode 100644 index 0000000..5caaf18 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportBeams_S.png differ diff --git a/client/assets/maps/Isometric/woodenSupportBeams_W.png b/client/assets/maps/Isometric/woodenSupportBeams_W.png new file mode 100644 index 0000000..ad40211 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportBeams_W.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBeam_E.png b/client/assets/maps/Isometric/woodenSupportsBeam_E.png new file mode 100644 index 0000000..d39b269 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBeam_E.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBeam_N.png b/client/assets/maps/Isometric/woodenSupportsBeam_N.png new file mode 100644 index 0000000..3898b1b Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBeam_N.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBeam_S.png b/client/assets/maps/Isometric/woodenSupportsBeam_S.png new file mode 100644 index 0000000..3041e51 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBeam_S.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBeam_W.png b/client/assets/maps/Isometric/woodenSupportsBeam_W.png new file mode 100644 index 0000000..3350676 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBeam_W.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBlock_E.png b/client/assets/maps/Isometric/woodenSupportsBlock_E.png new file mode 100644 index 0000000..af3a67f Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBlock_E.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBlock_N.png b/client/assets/maps/Isometric/woodenSupportsBlock_N.png new file mode 100644 index 0000000..b3efe14 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBlock_N.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBlock_S.png b/client/assets/maps/Isometric/woodenSupportsBlock_S.png new file mode 100644 index 0000000..049b61d Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBlock_S.png differ diff --git a/client/assets/maps/Isometric/woodenSupportsBlock_W.png b/client/assets/maps/Isometric/woodenSupportsBlock_W.png new file mode 100644 index 0000000..ec64837 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupportsBlock_W.png differ diff --git a/client/assets/maps/Isometric/woodenSupports_E.png b/client/assets/maps/Isometric/woodenSupports_E.png new file mode 100644 index 0000000..e62f88f Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupports_E.png differ diff --git a/client/assets/maps/Isometric/woodenSupports_N.png b/client/assets/maps/Isometric/woodenSupports_N.png new file mode 100644 index 0000000..978e2fd Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupports_N.png differ diff --git a/client/assets/maps/Isometric/woodenSupports_S.png b/client/assets/maps/Isometric/woodenSupports_S.png new file mode 100644 index 0000000..404feba Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupports_S.png differ diff --git a/client/assets/maps/Isometric/woodenSupports_W.png b/client/assets/maps/Isometric/woodenSupports_W.png new file mode 100644 index 0000000..80c2031 Binary files /dev/null and b/client/assets/maps/Isometric/woodenSupports_W.png differ diff --git a/client/assets/maps/arrena.tmj b/client/assets/maps/arrena.tmj new file mode 100644 index 0000000..5fb76bc --- /dev/null +++ b/client/assets/maps/arrena.tmj @@ -0,0 +1,179 @@ +{ "compressionlevel":-1, + "height":35, + "infinite":false, + "layers":[ + { + "data":[15, 2, 2, 27, 27, 27, 27, 27, 27, 20, 20, 20, 20, 27, 2, 19, 27, 27, 27, 27, 27, 27, 27, 27, 2, 39, 39, 2, 19, 19, 19, 2, 2, 2, 16, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 2, 45, 2, 2, 2, 2, 20, 20, 20, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 29, 2, 2, 2, 48, 48, 2, 2, 2, 42, 2, 2, 2, 2, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 28, + 29, 2, 2, 48, 2, 2, 2, 2, 2, 48, 48, 48, 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, 2, 2, 2, 2, 2, 20, 2, 2, 20, 20, 2, 2, 28, + 29, 2, 2, 48, 2, 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 2, 2, 2, 45, 2, 2, 2, 2, 19, 2, 2, 2, 20, 2, 19, 2, 2, 2, 2, 28, + 2, 2, 41, 2, 2, 48, 48, 2, 42, 2, 48, 48, 2, 20, 2, 2, 2, 45, 2, 2, 2, 2, 11, 11, 11, 2, 2, 20, 2, 2, 2, 2, 2, 2, 28, + 33, 2, 2, 48, 2, 2, 2, 2, 2, 2, 2, 48, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 2, 2, 20, 2, 2, 2, 2, 2, 2, 2, + 33, 2, 2, 48, 2, 2, 2, 2, 2, 2, 48, 48, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 11, 11, 11, 2, 2, 20, 2, 2, 2, 2, 2, 2, 28, + 33, 2, 2, 2, 2, 2, 20, 20, 2, 2, 48, 48, 2, 41, 2, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 2, 2, 2, 28, + 33, 2, 2, 2, 2, 2, 2, 2, 2, 42, 2, 2, 2, 41, 2, 20, 2, 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 45, 2, 2, 2, + 2, 41, 2, 48, 48, 2, 2, 2, 2, 42, 2, 41, 2, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 20, 2, 2, 20, 20, 2, 2, 2, 45, 2, 2, 2, + 29, 41, 2, 2, 2, 2, 2, 48, 48, 2, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 45, 2, 2, 28, + 2, 2, 2, 2, 2, 20, 2, 2, 2, 2, 2, 2, 2, 48, 2, 2, 9, 9, 2, 2, 2, 2, 2, 2, 45, 45, 2, 2, 2, 2, 2, 45, 2, 20, 2, + 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 2, 2, 2, 45, 2, 2, 36, + 41, 41, 2, 2, 2, 2, 2, 42, 2, 2, 2, 41, 48, 2, 2, 2, 20, 20, 2, 2, 2, 41, 45, 45, 2, 45, 2, 45, 2, 2, 2, 45, 2, 2, 36, + 2, 2, 2, 2, 2, 2, 2, 42, 2, 2, 2, 2, 2, 48, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 2, 2, 2, 2, 2, 2, 2, 2, 36, + 29, 2, 2, 2, 2, 20, 2, 2, 2, 2, 48, 48, 2, 2, 2, 2, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 40, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 40, + 33, 2, 41, 2, 2, 2, 2, 2, 2, 48, 2, 2, 2, 2, 20, 2, 2, 2, 2, 20, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 2, 2, 2, 40, + 33, 2, 2, 2, 2, 2, 2, 2, 2, 2, 48, 48, 2, 2, 2, 2, 2, 2, 2, 20, 20, 2, 2, 2, 45, 2, 2, 2, 2, 2, 45, 45, 2, 2, 40, + 33, 2, 2, 45, 2, 2, 2, 20, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 33, 2, 2, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 48, 48, 2, 48, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 2, 2, 2, 2, 14, + 33, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 48, 2, 2, 2, 20, 2, 2, 2, 20, 2, 45, 45, 2, 2, 2, 2, 2, + 33, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 2, 2, 2, 48, 2, 2, 2, 2, 2, 2, 2, 2, 45, 45, 2, 2, 2, 2, 2, 2, + 33, 2, 2, 2, 42, 2, 2, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 45, 45, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 42, 42, 2, 2, 45, 2, 2, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 29, 2, 2, 2, 2, 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 20, 2, 2, 2, 2, + 29, 2, 2, 2, 2, 2, 42, 42, 2, 2, 2, 2, 2, 41, 2, 2, 41, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 2, 20, 20, 20, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 48, 2, 2, 2, 20, 2, 2, 2, 2, 2, 23, + 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 11, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, + 7, 2, 2, 34, 2, 2, 38, 38, 2, 2, 2, 2, 2, 2, 2, 2, 38, 38, 38, 38, 38, 38, 42, 42, 42, 2, 2, 2, 2, 22, 22, 22, 20, 20, 5], + "height":35, + "id":3, + "name":"Floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":35, + "x":0, + "y":0 + }, + { + "data":[137, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 55, 139, + 145, 0, 67, 0, 67, 67, 0, 0, 67, 0, 0, 67, 0, 0, 67, 0, 67, 0, 0, 67, 0, 0, 67, 67, 67, 0, 67, 67, 67, 0, 0, 67, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 65, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 148, + 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148, + 145, 0, 0, 67, 67, 0, 67, 67, 0, 0, 67, 67, 0, 0, 67, 67, 67, 0, 67, 0, 0, 67, 67, 0, 0, 0, 67, 67, 0, 67, 0, 0, 67, 0, 148, + 134, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 136], + "height":35, + "id":1, + "name":"Foundations", + "offsetx":0, + "offsety":32, + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":35, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 265, 0, 0, 163, 0, 0, 0, 264, 264, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 265, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 264, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 271, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 170, 0, 0, + 0, 0, 0, 163, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 264, 0, 0, 170, 0, 0, + 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 163, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, + 0, 0, 264, 0, 0, 163, 0, 0, 0, 0, 0, 0, 271, 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 264, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 271, 0, 0, 0, 0, 0, 0, 264, 0, 0, 264, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 272, 0, 0, 0, 264, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 272, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":35, + "id":4, + "name":"Props", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":35, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":5, + "name":"Triggers", + "objects":[], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":6, + "nextobjectid":1, + "orientation":"isometric", + "renderorder":"right-down", + "tiledversion":"1.12.1", + "tileheight":128, + "tilesets":[ + { + "firstgid":1, + "source":"floor.tsx" + }, + { + "firstgid":49, + "source":"wall.tsx" + }, + { + "firstgid":161, + "source":"props.tsx" + }], + "tilewidth":256, + "type":"map", + "version":"1.10", + "width":35 +} \ No newline at end of file diff --git a/client/assets/maps/floor.tsx b/client/assets/maps/floor.tsx new file mode 100644 index 0000000..c25b50b --- /dev/null +++ b/client/assets/maps/floor.tsx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/assets/maps/props.tsx b/client/assets/maps/props.tsx new file mode 100644 index 0000000..6acadb4 --- /dev/null +++ b/client/assets/maps/props.tsx @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/assets/maps/wall.tsx b/client/assets/maps/wall.tsx new file mode 100644 index 0000000..f2e3095 --- /dev/null +++ b/client/assets/maps/wall.tsx @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/assets/sky.jpg b/client/assets/sky.jpg new file mode 100644 index 0000000..9a392e6 Binary files /dev/null and b/client/assets/sky.jpg differ diff --git a/client/assets/soulgate.png b/client/assets/soulgate.png new file mode 100644 index 0000000..d481b4d Binary files /dev/null and b/client/assets/soulgate.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_000.png b/client/assets/sprites/aldric/animations/attack/east/frame_000.png new file mode 100644 index 0000000..9d2ace8 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_001.png b/client/assets/sprites/aldric/animations/attack/east/frame_001.png new file mode 100644 index 0000000..9ce4242 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_002.png b/client/assets/sprites/aldric/animations/attack/east/frame_002.png new file mode 100644 index 0000000..7371b01 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_003.png b/client/assets/sprites/aldric/animations/attack/east/frame_003.png new file mode 100644 index 0000000..f8356f6 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_004.png b/client/assets/sprites/aldric/animations/attack/east/frame_004.png new file mode 100644 index 0000000..a918325 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_005.png b/client/assets/sprites/aldric/animations/attack/east/frame_005.png new file mode 100644 index 0000000..50e80ce Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/attack/east/frame_006.png b/client/assets/sprites/aldric/animations/attack/east/frame_006.png new file mode 100644 index 0000000..d2712ec Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/east/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_000.png b/client/assets/sprites/aldric/animations/attack/north/frame_000.png new file mode 100644 index 0000000..944e3a5 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_001.png b/client/assets/sprites/aldric/animations/attack/north/frame_001.png new file mode 100644 index 0000000..5ff030e Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_002.png b/client/assets/sprites/aldric/animations/attack/north/frame_002.png new file mode 100644 index 0000000..25e5939 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_003.png b/client/assets/sprites/aldric/animations/attack/north/frame_003.png new file mode 100644 index 0000000..dd70a7f Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_004.png b/client/assets/sprites/aldric/animations/attack/north/frame_004.png new file mode 100644 index 0000000..e5438fe Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_005.png b/client/assets/sprites/aldric/animations/attack/north/frame_005.png new file mode 100644 index 0000000..a3f4bdc Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/attack/north/frame_006.png b/client/assets/sprites/aldric/animations/attack/north/frame_006.png new file mode 100644 index 0000000..7cc8919 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/north/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_000.png b/client/assets/sprites/aldric/animations/attack/south/frame_000.png new file mode 100644 index 0000000..f972a1a Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_001.png b/client/assets/sprites/aldric/animations/attack/south/frame_001.png new file mode 100644 index 0000000..d531d1e Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_002.png b/client/assets/sprites/aldric/animations/attack/south/frame_002.png new file mode 100644 index 0000000..55acae0 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_003.png b/client/assets/sprites/aldric/animations/attack/south/frame_003.png new file mode 100644 index 0000000..22d4d19 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_004.png b/client/assets/sprites/aldric/animations/attack/south/frame_004.png new file mode 100644 index 0000000..81d7419 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_005.png b/client/assets/sprites/aldric/animations/attack/south/frame_005.png new file mode 100644 index 0000000..d0a85a5 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/attack/south/frame_006.png b/client/assets/sprites/aldric/animations/attack/south/frame_006.png new file mode 100644 index 0000000..3a45ac6 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/south/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_000.png b/client/assets/sprites/aldric/animations/attack/west/frame_000.png new file mode 100644 index 0000000..1a30670 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_001.png b/client/assets/sprites/aldric/animations/attack/west/frame_001.png new file mode 100644 index 0000000..1bfdb63 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_002.png b/client/assets/sprites/aldric/animations/attack/west/frame_002.png new file mode 100644 index 0000000..cc77449 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_003.png b/client/assets/sprites/aldric/animations/attack/west/frame_003.png new file mode 100644 index 0000000..ff886a4 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_004.png b/client/assets/sprites/aldric/animations/attack/west/frame_004.png new file mode 100644 index 0000000..1115f25 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_005.png b/client/assets/sprites/aldric/animations/attack/west/frame_005.png new file mode 100644 index 0000000..6c6b9ee Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/attack/west/frame_006.png b/client/assets/sprites/aldric/animations/attack/west/frame_006.png new file mode 100644 index 0000000..e679c35 Binary files /dev/null and b/client/assets/sprites/aldric/animations/attack/west/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_000.png b/client/assets/sprites/aldric/animations/fly/south/frame_000.png new file mode 100644 index 0000000..f972a1a Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_001.png b/client/assets/sprites/aldric/animations/fly/south/frame_001.png new file mode 100644 index 0000000..42c5582 Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_002.png b/client/assets/sprites/aldric/animations/fly/south/frame_002.png new file mode 100644 index 0000000..dd8b3a9 Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_003.png b/client/assets/sprites/aldric/animations/fly/south/frame_003.png new file mode 100644 index 0000000..6aead8f Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_004.png b/client/assets/sprites/aldric/animations/fly/south/frame_004.png new file mode 100644 index 0000000..15ea54f Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_005.png b/client/assets/sprites/aldric/animations/fly/south/frame_005.png new file mode 100644 index 0000000..19a2fb6 Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_006.png b/client/assets/sprites/aldric/animations/fly/south/frame_006.png new file mode 100644 index 0000000..0dba274 Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_007.png b/client/assets/sprites/aldric/animations/fly/south/frame_007.png new file mode 100644 index 0000000..d40b282 Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_007.png differ diff --git a/client/assets/sprites/aldric/animations/fly/south/frame_008.png b/client/assets/sprites/aldric/animations/fly/south/frame_008.png new file mode 100644 index 0000000..3876d20 Binary files /dev/null and b/client/assets/sprites/aldric/animations/fly/south/frame_008.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_000.png b/client/assets/sprites/aldric/animations/skill1/south/frame_000.png new file mode 100644 index 0000000..f972a1a Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_001.png b/client/assets/sprites/aldric/animations/skill1/south/frame_001.png new file mode 100644 index 0000000..286b5d9 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_002.png b/client/assets/sprites/aldric/animations/skill1/south/frame_002.png new file mode 100644 index 0000000..20e9539 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_003.png b/client/assets/sprites/aldric/animations/skill1/south/frame_003.png new file mode 100644 index 0000000..83e2676 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_004.png b/client/assets/sprites/aldric/animations/skill1/south/frame_004.png new file mode 100644 index 0000000..73796ac Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_005.png b/client/assets/sprites/aldric/animations/skill1/south/frame_005.png new file mode 100644 index 0000000..cb09c6d Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_006.png b/client/assets/sprites/aldric/animations/skill1/south/frame_006.png new file mode 100644 index 0000000..7584780 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_007.png b/client/assets/sprites/aldric/animations/skill1/south/frame_007.png new file mode 100644 index 0000000..5e9cce5 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_007.png differ diff --git a/client/assets/sprites/aldric/animations/skill1/south/frame_008.png b/client/assets/sprites/aldric/animations/skill1/south/frame_008.png new file mode 100644 index 0000000..a57184c Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill1/south/frame_008.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_000.png b/client/assets/sprites/aldric/animations/skill2/south/frame_000.png new file mode 100644 index 0000000..f972a1a Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_001.png b/client/assets/sprites/aldric/animations/skill2/south/frame_001.png new file mode 100644 index 0000000..e1d8277 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_002.png b/client/assets/sprites/aldric/animations/skill2/south/frame_002.png new file mode 100644 index 0000000..0b05842 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_003.png b/client/assets/sprites/aldric/animations/skill2/south/frame_003.png new file mode 100644 index 0000000..1ced6ef Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_004.png b/client/assets/sprites/aldric/animations/skill2/south/frame_004.png new file mode 100644 index 0000000..7df4f17 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_005.png b/client/assets/sprites/aldric/animations/skill2/south/frame_005.png new file mode 100644 index 0000000..481b9d9 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_006.png b/client/assets/sprites/aldric/animations/skill2/south/frame_006.png new file mode 100644 index 0000000..deeed4b Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_006.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_007.png b/client/assets/sprites/aldric/animations/skill2/south/frame_007.png new file mode 100644 index 0000000..ae60c9b Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_007.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_008.png b/client/assets/sprites/aldric/animations/skill2/south/frame_008.png new file mode 100644 index 0000000..8abb06b Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_008.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_009.png b/client/assets/sprites/aldric/animations/skill2/south/frame_009.png new file mode 100644 index 0000000..3e9b51c Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_009.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_010.png b/client/assets/sprites/aldric/animations/skill2/south/frame_010.png new file mode 100644 index 0000000..8ef3f5a Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_010.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_011.png b/client/assets/sprites/aldric/animations/skill2/south/frame_011.png new file mode 100644 index 0000000..087e339 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_011.png differ diff --git a/client/assets/sprites/aldric/animations/skill2/south/frame_012.png b/client/assets/sprites/aldric/animations/skill2/south/frame_012.png new file mode 100644 index 0000000..521b475 Binary files /dev/null and b/client/assets/sprites/aldric/animations/skill2/south/frame_012.png differ diff --git a/client/assets/sprites/aldric/animations/walking/east/frame_000.png b/client/assets/sprites/aldric/animations/walking/east/frame_000.png new file mode 100644 index 0000000..2312445 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/east/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/walking/east/frame_001.png b/client/assets/sprites/aldric/animations/walking/east/frame_001.png new file mode 100644 index 0000000..c11ac0a Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/east/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/walking/east/frame_002.png b/client/assets/sprites/aldric/animations/walking/east/frame_002.png new file mode 100644 index 0000000..ab047cc Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/east/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/walking/east/frame_003.png b/client/assets/sprites/aldric/animations/walking/east/frame_003.png new file mode 100644 index 0000000..759c5ad Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/east/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/walking/east/frame_004.png b/client/assets/sprites/aldric/animations/walking/east/frame_004.png new file mode 100644 index 0000000..bc02b88 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/east/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/walking/east/frame_005.png b/client/assets/sprites/aldric/animations/walking/east/frame_005.png new file mode 100644 index 0000000..c3fd17a Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/east/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/walking/north/frame_000.png b/client/assets/sprites/aldric/animations/walking/north/frame_000.png new file mode 100644 index 0000000..6d944cd Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/north/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/walking/north/frame_001.png b/client/assets/sprites/aldric/animations/walking/north/frame_001.png new file mode 100644 index 0000000..52ea8df Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/north/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/walking/north/frame_002.png b/client/assets/sprites/aldric/animations/walking/north/frame_002.png new file mode 100644 index 0000000..80051c9 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/north/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/walking/north/frame_003.png b/client/assets/sprites/aldric/animations/walking/north/frame_003.png new file mode 100644 index 0000000..f5373fb Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/north/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/walking/north/frame_004.png b/client/assets/sprites/aldric/animations/walking/north/frame_004.png new file mode 100644 index 0000000..62d5c81 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/north/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/walking/north/frame_005.png b/client/assets/sprites/aldric/animations/walking/north/frame_005.png new file mode 100644 index 0000000..1c02a0e Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/north/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/walking/south/frame_000.png b/client/assets/sprites/aldric/animations/walking/south/frame_000.png new file mode 100644 index 0000000..1864534 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/south/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/walking/south/frame_001.png b/client/assets/sprites/aldric/animations/walking/south/frame_001.png new file mode 100644 index 0000000..73f87cc Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/south/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/walking/south/frame_002.png b/client/assets/sprites/aldric/animations/walking/south/frame_002.png new file mode 100644 index 0000000..a1b527e Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/south/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/walking/south/frame_003.png b/client/assets/sprites/aldric/animations/walking/south/frame_003.png new file mode 100644 index 0000000..8cd666e Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/south/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/walking/south/frame_004.png b/client/assets/sprites/aldric/animations/walking/south/frame_004.png new file mode 100644 index 0000000..f6b909b Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/south/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/walking/south/frame_005.png b/client/assets/sprites/aldric/animations/walking/south/frame_005.png new file mode 100644 index 0000000..594db1f Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/south/frame_005.png differ diff --git a/client/assets/sprites/aldric/animations/walking/west/frame_000.png b/client/assets/sprites/aldric/animations/walking/west/frame_000.png new file mode 100644 index 0000000..799274f Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/west/frame_000.png differ diff --git a/client/assets/sprites/aldric/animations/walking/west/frame_001.png b/client/assets/sprites/aldric/animations/walking/west/frame_001.png new file mode 100644 index 0000000..c5b52a3 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/west/frame_001.png differ diff --git a/client/assets/sprites/aldric/animations/walking/west/frame_002.png b/client/assets/sprites/aldric/animations/walking/west/frame_002.png new file mode 100644 index 0000000..9a9cddc Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/west/frame_002.png differ diff --git a/client/assets/sprites/aldric/animations/walking/west/frame_003.png b/client/assets/sprites/aldric/animations/walking/west/frame_003.png new file mode 100644 index 0000000..d3b0bb1 Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/west/frame_003.png differ diff --git a/client/assets/sprites/aldric/animations/walking/west/frame_004.png b/client/assets/sprites/aldric/animations/walking/west/frame_004.png new file mode 100644 index 0000000..dc0c1ba Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/west/frame_004.png differ diff --git a/client/assets/sprites/aldric/animations/walking/west/frame_005.png b/client/assets/sprites/aldric/animations/walking/west/frame_005.png new file mode 100644 index 0000000..0e6e4bb Binary files /dev/null and b/client/assets/sprites/aldric/animations/walking/west/frame_005.png differ diff --git a/client/assets/sprites/aldric/metadata.json b/client/assets/sprites/aldric/metadata.json new file mode 100644 index 0000000..d7bb0b5 --- /dev/null +++ b/client/assets/sprites/aldric/metadata.json @@ -0,0 +1,142 @@ +{ + "character": { + "id": "d328cf6f-2822-40d0-bf98-9c91e8ec3f11", + "name": "Male human, slim and upright build, slightly shorter than Kael. Long flowing robe split at the legs, ivory and deep teal with gold trim along the collar and cuffs. Over the robe, a lightweight breastplate of\n polished bone-white metal engraved with glowing teal runes. Long silver-white hair tied back loosely with a few strands framing the face, eyes glowing with a soft teal light. Two large ethereal wings made of\n translucent teal energy trails, folded at rest on his back. He carries a tall ornate staff topped with a crystal orb radiating a faint glow. Warm light brown skin.", + "prompt": "Male human, slim and upright build, slightly shorter than Kael. Long flowing robe split at the legs, ivory and deep teal with gold trim along the collar and cuffs. Over the robe, a lightweight breastplate of\n polished bone-white metal engraved with glowing teal runes. Long silver-white hair tied back loosely with a few strands framing the face, eyes glowing with a soft teal light. Two large ethereal wings made of\n translucent teal energy trails, folded at rest on his back. He carries a tall ornate staff topped with a crystal orb radiating a faint glow. Warm light brown skin.", + "size": { + "width": 68, + "height": 68 + }, + "template_id": "mannequin", + "directions": 4, + "view": "low top-down", + "created_at": "2026-04-13T14:45:11.934975+00:00" + }, + "frames": { + "rotations": { + "south": "rotations/south.png", + "west": "rotations/west.png", + "east": "rotations/east.png", + "north": "rotations/north.png" + }, + "animations": { + "animation-b8b5cbee": { + "north": [ + "animations/animation-b8b5cbee/north/frame_000.png", + "animations/animation-b8b5cbee/north/frame_001.png", + "animations/animation-b8b5cbee/north/frame_002.png", + "animations/animation-b8b5cbee/north/frame_003.png", + "animations/animation-b8b5cbee/north/frame_004.png", + "animations/animation-b8b5cbee/north/frame_005.png" + ], + "west": [ + "animations/animation-b8b5cbee/west/frame_000.png", + "animations/animation-b8b5cbee/west/frame_001.png", + "animations/animation-b8b5cbee/west/frame_002.png", + "animations/animation-b8b5cbee/west/frame_003.png", + "animations/animation-b8b5cbee/west/frame_004.png", + "animations/animation-b8b5cbee/west/frame_005.png" + ], + "east": [ + "animations/animation-b8b5cbee/east/frame_000.png", + "animations/animation-b8b5cbee/east/frame_001.png", + "animations/animation-b8b5cbee/east/frame_002.png", + "animations/animation-b8b5cbee/east/frame_003.png", + "animations/animation-b8b5cbee/east/frame_004.png", + "animations/animation-b8b5cbee/east/frame_005.png" + ], + "south": [ + "animations/animation-b8b5cbee/south/frame_000.png", + "animations/animation-b8b5cbee/south/frame_001.png", + "animations/animation-b8b5cbee/south/frame_002.png", + "animations/animation-b8b5cbee/south/frame_003.png", + "animations/animation-b8b5cbee/south/frame_004.png", + "animations/animation-b8b5cbee/south/frame_005.png" + ] + }, + "He_thrusts_the_staff_forward_toward_the_target_the-f52393a5": { + "east": [ + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_000.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_001.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_002.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_003.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_004.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_005.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/east/frame_006.png" + ], + "south": [ + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_000.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_001.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_002.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_003.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_004.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_005.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/south/frame_006.png" + ], + "west": [ + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_000.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_001.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_002.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_003.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_004.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_005.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/west/frame_006.png" + ], + "north": [ + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_000.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_001.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_002.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_003.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_004.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_005.png", + "animations/He_thrusts_the_staff_forward_toward_the_target_the-f52393a5/north/frame_006.png" + ] + }, + "He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83": { + "south": [ + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_000.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_001.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_002.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_003.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_004.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_005.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_006.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_007.png", + "animations/He_rises_slightly_off_the_ground_his_ethereal_wing-4d8cca83/south/frame_008.png" + ] + }, + "He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f": { + "south": [ + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_000.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_001.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_002.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_003.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_004.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_005.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_006.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_007.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_008.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_009.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_010.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_011.png", + "animations/He_drops_to_one_knee_and_drives_the_tip_of_the_sta-69376f5f/south/frame_012.png" + ] + }, + "He_raises_the_staff_above_his_head_with_both_hands-46f23803": { + "south": [ + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_000.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_001.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_002.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_003.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_004.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_005.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_006.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_007.png", + "animations/He_raises_the_staff_above_his_head_with_both_hands-46f23803/south/frame_008.png" + ] + } + } + }, + "export_version": "2.0", + "export_date": "2026-04-13T15:12:48.178349" +} \ No newline at end of file diff --git a/client/assets/sprites/aldric/rotations/east.png b/client/assets/sprites/aldric/rotations/east.png new file mode 100644 index 0000000..49441d1 Binary files /dev/null and b/client/assets/sprites/aldric/rotations/east.png differ diff --git a/client/assets/sprites/aldric/rotations/north.png b/client/assets/sprites/aldric/rotations/north.png new file mode 100644 index 0000000..3b5284f Binary files /dev/null and b/client/assets/sprites/aldric/rotations/north.png differ diff --git a/client/assets/sprites/aldric/rotations/south.png b/client/assets/sprites/aldric/rotations/south.png new file mode 100644 index 0000000..3d07e49 Binary files /dev/null and b/client/assets/sprites/aldric/rotations/south.png differ diff --git a/client/assets/sprites/aldric/rotations/west.png b/client/assets/sprites/aldric/rotations/west.png new file mode 100644 index 0000000..7a1b18f Binary files /dev/null and b/client/assets/sprites/aldric/rotations/west.png differ diff --git a/client/assets/sprites/colosse/animations/running/east/frame_000.png b/client/assets/sprites/colosse/animations/running/east/frame_000.png new file mode 100644 index 0000000..3d67f11 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/colosse/animations/running/east/frame_001.png b/client/assets/sprites/colosse/animations/running/east/frame_001.png new file mode 100644 index 0000000..4f4093f Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/colosse/animations/running/east/frame_002.png b/client/assets/sprites/colosse/animations/running/east/frame_002.png new file mode 100644 index 0000000..80e4893 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/colosse/animations/running/east/frame_003.png b/client/assets/sprites/colosse/animations/running/east/frame_003.png new file mode 100644 index 0000000..b54fa60 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/colosse/animations/running/east/frame_004.png b/client/assets/sprites/colosse/animations/running/east/frame_004.png new file mode 100644 index 0000000..479eafa Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/east/frame_004.png differ diff --git a/client/assets/sprites/colosse/animations/running/east/frame_005.png b/client/assets/sprites/colosse/animations/running/east/frame_005.png new file mode 100644 index 0000000..063b385 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/east/frame_005.png differ diff --git a/client/assets/sprites/colosse/animations/running/north/frame_000.png b/client/assets/sprites/colosse/animations/running/north/frame_000.png new file mode 100644 index 0000000..5be60c8 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/colosse/animations/running/north/frame_001.png b/client/assets/sprites/colosse/animations/running/north/frame_001.png new file mode 100644 index 0000000..574563e Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/colosse/animations/running/north/frame_002.png b/client/assets/sprites/colosse/animations/running/north/frame_002.png new file mode 100644 index 0000000..a3714db Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/colosse/animations/running/north/frame_003.png b/client/assets/sprites/colosse/animations/running/north/frame_003.png new file mode 100644 index 0000000..6f2a13c Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/colosse/animations/running/north/frame_004.png b/client/assets/sprites/colosse/animations/running/north/frame_004.png new file mode 100644 index 0000000..9116a6d Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/north/frame_004.png differ diff --git a/client/assets/sprites/colosse/animations/running/north/frame_005.png b/client/assets/sprites/colosse/animations/running/north/frame_005.png new file mode 100644 index 0000000..e8a5565 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/north/frame_005.png differ diff --git a/client/assets/sprites/colosse/animations/running/south/frame_000.png b/client/assets/sprites/colosse/animations/running/south/frame_000.png new file mode 100644 index 0000000..2059d00 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/colosse/animations/running/south/frame_001.png b/client/assets/sprites/colosse/animations/running/south/frame_001.png new file mode 100644 index 0000000..870f7ab Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/colosse/animations/running/south/frame_002.png b/client/assets/sprites/colosse/animations/running/south/frame_002.png new file mode 100644 index 0000000..0cef474 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/colosse/animations/running/south/frame_003.png b/client/assets/sprites/colosse/animations/running/south/frame_003.png new file mode 100644 index 0000000..7e48f4d Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/colosse/animations/running/south/frame_004.png b/client/assets/sprites/colosse/animations/running/south/frame_004.png new file mode 100644 index 0000000..dd1e42e Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/south/frame_004.png differ diff --git a/client/assets/sprites/colosse/animations/running/south/frame_005.png b/client/assets/sprites/colosse/animations/running/south/frame_005.png new file mode 100644 index 0000000..78c4f96 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/south/frame_005.png differ diff --git a/client/assets/sprites/colosse/animations/running/west/frame_000.png b/client/assets/sprites/colosse/animations/running/west/frame_000.png new file mode 100644 index 0000000..dc85b7f Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/colosse/animations/running/west/frame_001.png b/client/assets/sprites/colosse/animations/running/west/frame_001.png new file mode 100644 index 0000000..7246970 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/colosse/animations/running/west/frame_002.png b/client/assets/sprites/colosse/animations/running/west/frame_002.png new file mode 100644 index 0000000..e408e63 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/colosse/animations/running/west/frame_003.png b/client/assets/sprites/colosse/animations/running/west/frame_003.png new file mode 100644 index 0000000..9a83708 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/colosse/animations/running/west/frame_004.png b/client/assets/sprites/colosse/animations/running/west/frame_004.png new file mode 100644 index 0000000..906bb9a Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/west/frame_004.png differ diff --git a/client/assets/sprites/colosse/animations/running/west/frame_005.png b/client/assets/sprites/colosse/animations/running/west/frame_005.png new file mode 100644 index 0000000..756d8a8 Binary files /dev/null and b/client/assets/sprites/colosse/animations/running/west/frame_005.png differ diff --git a/client/assets/sprites/colosse/metadata.json b/client/assets/sprites/colosse/metadata.json new file mode 100644 index 0000000..fbedbe1 --- /dev/null +++ b/client/assets/sprites/colosse/metadata.json @@ -0,0 +1,61 @@ +{ + "character": { + "id": "64d33d78-9732-4d70-bbd2-9c091bdb6751", + "name": "Not humanoid, more like a massive corrupted stone titan. Enormous broad body with no neck, head fused directly into the chest. Arms too long, dragging near the ground. Body made of cracked black\n rock with glowing orange lava visible through the fissures. No visible face, just a smooth dark surface with two deep orange glowing cracks as eyes. Massive shoulder width, hunched forward under its own\n weight.", + "prompt": "Not humanoid, more like a massive corrupted stone titan. Enormous broad body with no neck, head fused directly into the chest. Arms too long, dragging near the ground. Body made of cracked black\n rock with glowing orange lava visible through the fissures. No visible face, just a smooth dark surface with two deep orange glowing cracks as eyes. Massive shoulder width, hunched forward under its own\n weight.", + "size": { + "width": 92, + "height": 92 + }, + "template_id": "mannequin", + "directions": 4, + "view": "low top-down", + "created_at": "2026-04-21T15:45:21.678943+00:00" + }, + "frames": { + "rotations": { + "south": "rotations/south.png", + "west": "rotations/west.png", + "east": "rotations/east.png", + "north": "rotations/north.png" + }, + "animations": { + "Running-040f2cf4": { + "south": [ + "animations/Running-040f2cf4/south/frame_000.png", + "animations/Running-040f2cf4/south/frame_001.png", + "animations/Running-040f2cf4/south/frame_002.png", + "animations/Running-040f2cf4/south/frame_003.png", + "animations/Running-040f2cf4/south/frame_004.png", + "animations/Running-040f2cf4/south/frame_005.png" + ], + "north": [ + "animations/Running-040f2cf4/north/frame_000.png", + "animations/Running-040f2cf4/north/frame_001.png", + "animations/Running-040f2cf4/north/frame_002.png", + "animations/Running-040f2cf4/north/frame_003.png", + "animations/Running-040f2cf4/north/frame_004.png", + "animations/Running-040f2cf4/north/frame_005.png" + ], + "west": [ + "animations/Running-040f2cf4/west/frame_000.png", + "animations/Running-040f2cf4/west/frame_001.png", + "animations/Running-040f2cf4/west/frame_002.png", + "animations/Running-040f2cf4/west/frame_003.png", + "animations/Running-040f2cf4/west/frame_004.png", + "animations/Running-040f2cf4/west/frame_005.png" + ], + "east": [ + "animations/Running-040f2cf4/east/frame_000.png", + "animations/Running-040f2cf4/east/frame_001.png", + "animations/Running-040f2cf4/east/frame_002.png", + "animations/Running-040f2cf4/east/frame_003.png", + "animations/Running-040f2cf4/east/frame_004.png", + "animations/Running-040f2cf4/east/frame_005.png" + ] + } + } + }, + "export_version": "2.0", + "export_date": "2026-04-21T15:50:51.833482" +} \ No newline at end of file diff --git a/client/assets/sprites/colosse/rotations/east.png b/client/assets/sprites/colosse/rotations/east.png new file mode 100644 index 0000000..179e281 Binary files /dev/null and b/client/assets/sprites/colosse/rotations/east.png differ diff --git a/client/assets/sprites/colosse/rotations/north.png b/client/assets/sprites/colosse/rotations/north.png new file mode 100644 index 0000000..4975eb5 Binary files /dev/null and b/client/assets/sprites/colosse/rotations/north.png differ diff --git a/client/assets/sprites/colosse/rotations/south.png b/client/assets/sprites/colosse/rotations/south.png new file mode 100644 index 0000000..d555703 Binary files /dev/null and b/client/assets/sprites/colosse/rotations/south.png differ diff --git a/client/assets/sprites/colosse/rotations/west.png b/client/assets/sprites/colosse/rotations/west.png new file mode 100644 index 0000000..4d97629 Binary files /dev/null and b/client/assets/sprites/colosse/rotations/west.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_000.png b/client/assets/sprites/eclat/animations/running/east/frame_000.png new file mode 100644 index 0000000..b9c80ec Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_001.png b/client/assets/sprites/eclat/animations/running/east/frame_001.png new file mode 100644 index 0000000..65690d8 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_002.png b/client/assets/sprites/eclat/animations/running/east/frame_002.png new file mode 100644 index 0000000..96e6d3c Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_003.png b/client/assets/sprites/eclat/animations/running/east/frame_003.png new file mode 100644 index 0000000..a8c1199 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_004.png b/client/assets/sprites/eclat/animations/running/east/frame_004.png new file mode 100644 index 0000000..a09c895 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_004.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_005.png b/client/assets/sprites/eclat/animations/running/east/frame_005.png new file mode 100644 index 0000000..68ceaf8 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_005.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_006.png b/client/assets/sprites/eclat/animations/running/east/frame_006.png new file mode 100644 index 0000000..ccbb237 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_006.png differ diff --git a/client/assets/sprites/eclat/animations/running/east/frame_007.png b/client/assets/sprites/eclat/animations/running/east/frame_007.png new file mode 100644 index 0000000..36b7140 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/east/frame_007.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_000.png b/client/assets/sprites/eclat/animations/running/north/frame_000.png new file mode 100644 index 0000000..d071fc1 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_001.png b/client/assets/sprites/eclat/animations/running/north/frame_001.png new file mode 100644 index 0000000..761be03 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_002.png b/client/assets/sprites/eclat/animations/running/north/frame_002.png new file mode 100644 index 0000000..4a40493 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_003.png b/client/assets/sprites/eclat/animations/running/north/frame_003.png new file mode 100644 index 0000000..8f4f0db Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_004.png b/client/assets/sprites/eclat/animations/running/north/frame_004.png new file mode 100644 index 0000000..69d2324 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_004.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_005.png b/client/assets/sprites/eclat/animations/running/north/frame_005.png new file mode 100644 index 0000000..e1ea18d Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_005.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_006.png b/client/assets/sprites/eclat/animations/running/north/frame_006.png new file mode 100644 index 0000000..491d2fe Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_006.png differ diff --git a/client/assets/sprites/eclat/animations/running/north/frame_007.png b/client/assets/sprites/eclat/animations/running/north/frame_007.png new file mode 100644 index 0000000..0ce46a1 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/north/frame_007.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_000.png b/client/assets/sprites/eclat/animations/running/south/frame_000.png new file mode 100644 index 0000000..cce4f05 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_001.png b/client/assets/sprites/eclat/animations/running/south/frame_001.png new file mode 100644 index 0000000..af07f72 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_002.png b/client/assets/sprites/eclat/animations/running/south/frame_002.png new file mode 100644 index 0000000..d1d10d9 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_003.png b/client/assets/sprites/eclat/animations/running/south/frame_003.png new file mode 100644 index 0000000..24129c8 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_004.png b/client/assets/sprites/eclat/animations/running/south/frame_004.png new file mode 100644 index 0000000..0b8b7fe Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_004.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_005.png b/client/assets/sprites/eclat/animations/running/south/frame_005.png new file mode 100644 index 0000000..cd640ee Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_005.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_006.png b/client/assets/sprites/eclat/animations/running/south/frame_006.png new file mode 100644 index 0000000..2c77d93 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_006.png differ diff --git a/client/assets/sprites/eclat/animations/running/south/frame_007.png b/client/assets/sprites/eclat/animations/running/south/frame_007.png new file mode 100644 index 0000000..c0a6483 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/south/frame_007.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_000.png b/client/assets/sprites/eclat/animations/running/west/frame_000.png new file mode 100644 index 0000000..f6d1827 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_001.png b/client/assets/sprites/eclat/animations/running/west/frame_001.png new file mode 100644 index 0000000..5322799 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_002.png b/client/assets/sprites/eclat/animations/running/west/frame_002.png new file mode 100644 index 0000000..13a8c66 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_003.png b/client/assets/sprites/eclat/animations/running/west/frame_003.png new file mode 100644 index 0000000..9ba1360 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_004.png b/client/assets/sprites/eclat/animations/running/west/frame_004.png new file mode 100644 index 0000000..555cf3d Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_004.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_005.png b/client/assets/sprites/eclat/animations/running/west/frame_005.png new file mode 100644 index 0000000..b717a80 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_005.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_006.png b/client/assets/sprites/eclat/animations/running/west/frame_006.png new file mode 100644 index 0000000..b131bd7 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_006.png differ diff --git a/client/assets/sprites/eclat/animations/running/west/frame_007.png b/client/assets/sprites/eclat/animations/running/west/frame_007.png new file mode 100644 index 0000000..8596565 Binary files /dev/null and b/client/assets/sprites/eclat/animations/running/west/frame_007.png differ diff --git a/client/assets/sprites/eclat/metadata.json b/client/assets/sprites/eclat/metadata.json new file mode 100644 index 0000000..70b95c3 --- /dev/null +++ b/client/assets/sprites/eclat/metadata.json @@ -0,0 +1,69 @@ +{ + "character": { + "id": "c11eab6a-2440-4279-a082-be809bd2d2f2", + "name": "Not humanoid at all. A small fragment of corrupted soul energy, shaped like a jagged broken crystal shard. No limbs, no face, no body \u2014 just a glowing yellow-white core surrounded by sharp broken\n edges that spin and flicker as it moves. Trails a faint light behind it", + "prompt": "Not humanoid at all. A small fragment of corrupted soul energy, shaped like a jagged broken crystal shard. No limbs, no face, no body \u2014 just a glowing yellow-white core surrounded by sharp broken\n edges that spin and flicker as it moves. Trails a faint light behind it", + "size": { + "width": 36, + "height": 36 + }, + "template_id": "mannequin", + "directions": 4, + "view": "low top-down", + "created_at": "2026-04-21T15:39:29.335016+00:00" + }, + "frames": { + "rotations": { + "south": "rotations/south.png", + "west": "rotations/west.png", + "east": "rotations/east.png", + "north": "rotations/north.png" + }, + "animations": { + "Running-c63e1f30": { + "south": [ + "animations/Running-c63e1f30/south/frame_000.png", + "animations/Running-c63e1f30/south/frame_001.png", + "animations/Running-c63e1f30/south/frame_002.png", + "animations/Running-c63e1f30/south/frame_003.png", + "animations/Running-c63e1f30/south/frame_004.png", + "animations/Running-c63e1f30/south/frame_005.png", + "animations/Running-c63e1f30/south/frame_006.png", + "animations/Running-c63e1f30/south/frame_007.png" + ], + "west": [ + "animations/Running-c63e1f30/west/frame_000.png", + "animations/Running-c63e1f30/west/frame_001.png", + "animations/Running-c63e1f30/west/frame_002.png", + "animations/Running-c63e1f30/west/frame_003.png", + "animations/Running-c63e1f30/west/frame_004.png", + "animations/Running-c63e1f30/west/frame_005.png", + "animations/Running-c63e1f30/west/frame_006.png", + "animations/Running-c63e1f30/west/frame_007.png" + ], + "north": [ + "animations/Running-c63e1f30/north/frame_000.png", + "animations/Running-c63e1f30/north/frame_001.png", + "animations/Running-c63e1f30/north/frame_002.png", + "animations/Running-c63e1f30/north/frame_003.png", + "animations/Running-c63e1f30/north/frame_004.png", + "animations/Running-c63e1f30/north/frame_005.png", + "animations/Running-c63e1f30/north/frame_006.png", + "animations/Running-c63e1f30/north/frame_007.png" + ], + "east": [ + "animations/Running-c63e1f30/east/frame_000.png", + "animations/Running-c63e1f30/east/frame_001.png", + "animations/Running-c63e1f30/east/frame_002.png", + "animations/Running-c63e1f30/east/frame_003.png", + "animations/Running-c63e1f30/east/frame_004.png", + "animations/Running-c63e1f30/east/frame_005.png", + "animations/Running-c63e1f30/east/frame_006.png", + "animations/Running-c63e1f30/east/frame_007.png" + ] + } + } + }, + "export_version": "2.0", + "export_date": "2026-04-21T15:44:02.029717" +} \ No newline at end of file diff --git a/client/assets/sprites/eclat/rotations/east.png b/client/assets/sprites/eclat/rotations/east.png new file mode 100644 index 0000000..e9f8ebf Binary files /dev/null and b/client/assets/sprites/eclat/rotations/east.png differ diff --git a/client/assets/sprites/eclat/rotations/north.png b/client/assets/sprites/eclat/rotations/north.png new file mode 100644 index 0000000..1ba0ac9 Binary files /dev/null and b/client/assets/sprites/eclat/rotations/north.png differ diff --git a/client/assets/sprites/eclat/rotations/south.png b/client/assets/sprites/eclat/rotations/south.png new file mode 100644 index 0000000..7e5f59d Binary files /dev/null and b/client/assets/sprites/eclat/rotations/south.png differ diff --git a/client/assets/sprites/eclat/rotations/west.png b/client/assets/sprites/eclat/rotations/west.png new file mode 100644 index 0000000..870e7d9 Binary files /dev/null and b/client/assets/sprites/eclat/rotations/west.png differ diff --git a/client/assets/sprites/fracture/animations/running/east/frame_000.png b/client/assets/sprites/fracture/animations/running/east/frame_000.png new file mode 100644 index 0000000..462ba96 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/fracture/animations/running/east/frame_001.png b/client/assets/sprites/fracture/animations/running/east/frame_001.png new file mode 100644 index 0000000..e5624e3 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/fracture/animations/running/east/frame_002.png b/client/assets/sprites/fracture/animations/running/east/frame_002.png new file mode 100644 index 0000000..d83505b Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/fracture/animations/running/east/frame_003.png b/client/assets/sprites/fracture/animations/running/east/frame_003.png new file mode 100644 index 0000000..4e51e31 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/fracture/animations/running/north/frame_000.png b/client/assets/sprites/fracture/animations/running/north/frame_000.png new file mode 100644 index 0000000..6526fec Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/fracture/animations/running/north/frame_001.png b/client/assets/sprites/fracture/animations/running/north/frame_001.png new file mode 100644 index 0000000..3123756 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/fracture/animations/running/north/frame_002.png b/client/assets/sprites/fracture/animations/running/north/frame_002.png new file mode 100644 index 0000000..f165026 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/fracture/animations/running/north/frame_003.png b/client/assets/sprites/fracture/animations/running/north/frame_003.png new file mode 100644 index 0000000..d668f18 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/fracture/animations/running/south/frame_000.png b/client/assets/sprites/fracture/animations/running/south/frame_000.png new file mode 100644 index 0000000..b10070b Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/fracture/animations/running/south/frame_001.png b/client/assets/sprites/fracture/animations/running/south/frame_001.png new file mode 100644 index 0000000..e0877df Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/fracture/animations/running/south/frame_002.png b/client/assets/sprites/fracture/animations/running/south/frame_002.png new file mode 100644 index 0000000..b626718 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/fracture/animations/running/south/frame_003.png b/client/assets/sprites/fracture/animations/running/south/frame_003.png new file mode 100644 index 0000000..9d0a5c5 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/fracture/animations/running/west/frame_000.png b/client/assets/sprites/fracture/animations/running/west/frame_000.png new file mode 100644 index 0000000..ba1309d Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/fracture/animations/running/west/frame_001.png b/client/assets/sprites/fracture/animations/running/west/frame_001.png new file mode 100644 index 0000000..0282446 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/fracture/animations/running/west/frame_002.png b/client/assets/sprites/fracture/animations/running/west/frame_002.png new file mode 100644 index 0000000..531ab31 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/fracture/animations/running/west/frame_003.png b/client/assets/sprites/fracture/animations/running/west/frame_003.png new file mode 100644 index 0000000..fd463c4 Binary files /dev/null and b/client/assets/sprites/fracture/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/fracture/metadata.json b/client/assets/sprites/fracture/metadata.json new file mode 100644 index 0000000..2aa4e44 --- /dev/null +++ b/client/assets/sprites/fracture/metadata.json @@ -0,0 +1,53 @@ +{ + "character": { + "id": "8b4f8abd-9061-486d-9a33-b9ee926a2b4b", + "name": "A corrupted humanoid soul, hunched posture with arms slightly too long. Body made of cracked dark stone with glowing red fissures running across the torso and limbs, like magma visible through broken rock. No\n visible face, just a smooth cracked surface with two dim red glowing eyes. Tattered remnants of dark cloth hanging around the waist. Medium build, roughly human-sized.", + "prompt": "A corrupted humanoid soul, hunched posture with arms slightly too long. Body made of cracked dark stone with glowing red fissures running across the torso and limbs, like magma visible through broken rock. No\n visible face, just a smooth cracked surface with two dim red glowing eyes. Tattered remnants of dark cloth hanging around the waist. Medium build, roughly human-sized.", + "size": { + "width": 68, + "height": 68 + }, + "template_id": "mannequin", + "directions": 4, + "view": "low top-down", + "created_at": "2026-04-16T12:56:49.807063+00:00" + }, + "frames": { + "rotations": { + "south": "rotations/south.png", + "west": "rotations/west.png", + "east": "rotations/east.png", + "north": "rotations/north.png" + }, + "animations": { + "Running-8ac5d8e0": { + "west": [ + "animations/Running-8ac5d8e0/west/frame_000.png", + "animations/Running-8ac5d8e0/west/frame_001.png", + "animations/Running-8ac5d8e0/west/frame_002.png", + "animations/Running-8ac5d8e0/west/frame_003.png" + ], + "north": [ + "animations/Running-8ac5d8e0/north/frame_000.png", + "animations/Running-8ac5d8e0/north/frame_001.png", + "animations/Running-8ac5d8e0/north/frame_002.png", + "animations/Running-8ac5d8e0/north/frame_003.png" + ], + "south": [ + "animations/Running-8ac5d8e0/south/frame_000.png", + "animations/Running-8ac5d8e0/south/frame_001.png", + "animations/Running-8ac5d8e0/south/frame_002.png", + "animations/Running-8ac5d8e0/south/frame_003.png" + ], + "east": [ + "animations/Running-8ac5d8e0/east/frame_000.png", + "animations/Running-8ac5d8e0/east/frame_001.png", + "animations/Running-8ac5d8e0/east/frame_002.png", + "animations/Running-8ac5d8e0/east/frame_003.png" + ] + } + } + }, + "export_version": "2.0", + "export_date": "2026-04-16T13:00:54.235142" +} \ No newline at end of file diff --git a/client/assets/sprites/fracture/rotations/east.png b/client/assets/sprites/fracture/rotations/east.png new file mode 100644 index 0000000..cfa77b1 Binary files /dev/null and b/client/assets/sprites/fracture/rotations/east.png differ diff --git a/client/assets/sprites/fracture/rotations/north.png b/client/assets/sprites/fracture/rotations/north.png new file mode 100644 index 0000000..f4c14b9 Binary files /dev/null and b/client/assets/sprites/fracture/rotations/north.png differ diff --git a/client/assets/sprites/fracture/rotations/south.png b/client/assets/sprites/fracture/rotations/south.png new file mode 100644 index 0000000..c13f20e Binary files /dev/null and b/client/assets/sprites/fracture/rotations/south.png differ diff --git a/client/assets/sprites/fracture/rotations/west.png b/client/assets/sprites/fracture/rotations/west.png new file mode 100644 index 0000000..27e2fe7 Binary files /dev/null and b/client/assets/sprites/fracture/rotations/west.png differ diff --git a/client/assets/sprites/kael/animations/attack/east/frame_000.png b/client/assets/sprites/kael/animations/attack/east/frame_000.png new file mode 100644 index 0000000..0867fa4 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/east/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/attack/east/frame_001.png b/client/assets/sprites/kael/animations/attack/east/frame_001.png new file mode 100644 index 0000000..754f368 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/east/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/attack/east/frame_002.png b/client/assets/sprites/kael/animations/attack/east/frame_002.png new file mode 100644 index 0000000..150794d Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/east/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/attack/east/frame_003.png b/client/assets/sprites/kael/animations/attack/east/frame_003.png new file mode 100644 index 0000000..5cfea95 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/east/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/attack/east/frame_004.png b/client/assets/sprites/kael/animations/attack/east/frame_004.png new file mode 100644 index 0000000..7441871 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/east/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/attack/north/frame_000.png b/client/assets/sprites/kael/animations/attack/north/frame_000.png new file mode 100644 index 0000000..836a3a1 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/north/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/attack/north/frame_001.png b/client/assets/sprites/kael/animations/attack/north/frame_001.png new file mode 100644 index 0000000..fb5f8a2 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/north/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/attack/north/frame_002.png b/client/assets/sprites/kael/animations/attack/north/frame_002.png new file mode 100644 index 0000000..d896875 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/north/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/attack/north/frame_003.png b/client/assets/sprites/kael/animations/attack/north/frame_003.png new file mode 100644 index 0000000..90fcbd2 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/north/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/attack/north/frame_004.png b/client/assets/sprites/kael/animations/attack/north/frame_004.png new file mode 100644 index 0000000..9939634 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/north/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/attack/south/frame_000.png b/client/assets/sprites/kael/animations/attack/south/frame_000.png new file mode 100644 index 0000000..4028172 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/south/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/attack/south/frame_001.png b/client/assets/sprites/kael/animations/attack/south/frame_001.png new file mode 100644 index 0000000..6c4117f Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/south/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/attack/south/frame_002.png b/client/assets/sprites/kael/animations/attack/south/frame_002.png new file mode 100644 index 0000000..9d92cb8 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/south/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/attack/south/frame_003.png b/client/assets/sprites/kael/animations/attack/south/frame_003.png new file mode 100644 index 0000000..dcdd34c Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/south/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/attack/south/frame_004.png b/client/assets/sprites/kael/animations/attack/south/frame_004.png new file mode 100644 index 0000000..1efcd62 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/south/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/attack/west/frame_000.png b/client/assets/sprites/kael/animations/attack/west/frame_000.png new file mode 100644 index 0000000..91f13ad Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/west/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/attack/west/frame_001.png b/client/assets/sprites/kael/animations/attack/west/frame_001.png new file mode 100644 index 0000000..a8e126e Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/west/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/attack/west/frame_002.png b/client/assets/sprites/kael/animations/attack/west/frame_002.png new file mode 100644 index 0000000..75f0932 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/west/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/attack/west/frame_003.png b/client/assets/sprites/kael/animations/attack/west/frame_003.png new file mode 100644 index 0000000..1620a2d Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/west/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/attack/west/frame_004.png b/client/assets/sprites/kael/animations/attack/west/frame_004.png new file mode 100644 index 0000000..eaf8646 Binary files /dev/null and b/client/assets/sprites/kael/animations/attack/west/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/dash/east/frame_000.png b/client/assets/sprites/kael/animations/dash/east/frame_000.png new file mode 100644 index 0000000..0867fa4 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/east/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/dash/east/frame_001.png b/client/assets/sprites/kael/animations/dash/east/frame_001.png new file mode 100644 index 0000000..9c3ead1 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/east/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/dash/east/frame_002.png b/client/assets/sprites/kael/animations/dash/east/frame_002.png new file mode 100644 index 0000000..26183b6 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/east/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/dash/east/frame_003.png b/client/assets/sprites/kael/animations/dash/east/frame_003.png new file mode 100644 index 0000000..8b39163 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/east/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/dash/east/frame_004.png b/client/assets/sprites/kael/animations/dash/east/frame_004.png new file mode 100644 index 0000000..c8caeb8 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/east/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/dash/north/frame_000.png b/client/assets/sprites/kael/animations/dash/north/frame_000.png new file mode 100644 index 0000000..836a3a1 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/north/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/dash/north/frame_001.png b/client/assets/sprites/kael/animations/dash/north/frame_001.png new file mode 100644 index 0000000..86194a2 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/north/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/dash/north/frame_002.png b/client/assets/sprites/kael/animations/dash/north/frame_002.png new file mode 100644 index 0000000..e8083a7 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/north/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/dash/north/frame_003.png b/client/assets/sprites/kael/animations/dash/north/frame_003.png new file mode 100644 index 0000000..01a0592 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/north/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/dash/north/frame_004.png b/client/assets/sprites/kael/animations/dash/north/frame_004.png new file mode 100644 index 0000000..9b1daff Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/north/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/dash/south/frame_000.png b/client/assets/sprites/kael/animations/dash/south/frame_000.png new file mode 100644 index 0000000..4028172 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/south/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/dash/south/frame_001.png b/client/assets/sprites/kael/animations/dash/south/frame_001.png new file mode 100644 index 0000000..86e625f Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/south/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/dash/south/frame_002.png b/client/assets/sprites/kael/animations/dash/south/frame_002.png new file mode 100644 index 0000000..f7623cd Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/south/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/dash/south/frame_003.png b/client/assets/sprites/kael/animations/dash/south/frame_003.png new file mode 100644 index 0000000..4c2aef1 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/south/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/dash/south/frame_004.png b/client/assets/sprites/kael/animations/dash/south/frame_004.png new file mode 100644 index 0000000..dd21e6b Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/south/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/dash/west/frame_000.png b/client/assets/sprites/kael/animations/dash/west/frame_000.png new file mode 100644 index 0000000..91f13ad Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/west/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/dash/west/frame_001.png b/client/assets/sprites/kael/animations/dash/west/frame_001.png new file mode 100644 index 0000000..239605f Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/west/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/dash/west/frame_002.png b/client/assets/sprites/kael/animations/dash/west/frame_002.png new file mode 100644 index 0000000..243bcf6 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/west/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/dash/west/frame_003.png b/client/assets/sprites/kael/animations/dash/west/frame_003.png new file mode 100644 index 0000000..129e8c0 Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/west/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/dash/west/frame_004.png b/client/assets/sprites/kael/animations/dash/west/frame_004.png new file mode 100644 index 0000000..e7430bd Binary files /dev/null and b/client/assets/sprites/kael/animations/dash/west/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/running/east/frame_000.png b/client/assets/sprites/kael/animations/running/east/frame_000.png new file mode 100644 index 0000000..48df494 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/running/east/frame_001.png b/client/assets/sprites/kael/animations/running/east/frame_001.png new file mode 100644 index 0000000..4f77083 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/running/east/frame_002.png b/client/assets/sprites/kael/animations/running/east/frame_002.png new file mode 100644 index 0000000..912daa2 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/running/east/frame_003.png b/client/assets/sprites/kael/animations/running/east/frame_003.png new file mode 100644 index 0000000..d4cda5d Binary files /dev/null and b/client/assets/sprites/kael/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/running/frame_000.png b/client/assets/sprites/kael/animations/running/frame_000.png new file mode 100644 index 0000000..dcfe455 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/running/frame_001.png b/client/assets/sprites/kael/animations/running/frame_001.png new file mode 100644 index 0000000..6b7ba4b Binary files /dev/null and b/client/assets/sprites/kael/animations/running/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/running/frame_002.png b/client/assets/sprites/kael/animations/running/frame_002.png new file mode 100644 index 0000000..a93c028 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/running/frame_003.png b/client/assets/sprites/kael/animations/running/frame_003.png new file mode 100644 index 0000000..32dd68f Binary files /dev/null and b/client/assets/sprites/kael/animations/running/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/running/north/frame_000.png b/client/assets/sprites/kael/animations/running/north/frame_000.png new file mode 100644 index 0000000..92b5759 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/running/north/frame_001.png b/client/assets/sprites/kael/animations/running/north/frame_001.png new file mode 100644 index 0000000..b291539 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/running/north/frame_002.png b/client/assets/sprites/kael/animations/running/north/frame_002.png new file mode 100644 index 0000000..9f076b5 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/running/north/frame_003.png b/client/assets/sprites/kael/animations/running/north/frame_003.png new file mode 100644 index 0000000..8c3788c Binary files /dev/null and b/client/assets/sprites/kael/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/running/south/frame_000.png b/client/assets/sprites/kael/animations/running/south/frame_000.png new file mode 100644 index 0000000..dcfe455 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/running/south/frame_001.png b/client/assets/sprites/kael/animations/running/south/frame_001.png new file mode 100644 index 0000000..6b7ba4b Binary files /dev/null and b/client/assets/sprites/kael/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/running/south/frame_002.png b/client/assets/sprites/kael/animations/running/south/frame_002.png new file mode 100644 index 0000000..a93c028 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/running/south/frame_003.png b/client/assets/sprites/kael/animations/running/south/frame_003.png new file mode 100644 index 0000000..32dd68f Binary files /dev/null and b/client/assets/sprites/kael/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/running/west/frame_000.png b/client/assets/sprites/kael/animations/running/west/frame_000.png new file mode 100644 index 0000000..f760bf1 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/running/west/frame_001.png b/client/assets/sprites/kael/animations/running/west/frame_001.png new file mode 100644 index 0000000..d4a0d37 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/running/west/frame_002.png b/client/assets/sprites/kael/animations/running/west/frame_002.png new file mode 100644 index 0000000..d1a9899 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/running/west/frame_003.png b/client/assets/sprites/kael/animations/running/west/frame_003.png new file mode 100644 index 0000000..91a8f25 Binary files /dev/null and b/client/assets/sprites/kael/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_000.png b/client/assets/sprites/kael/animations/skill2/south/frame_000.png new file mode 100644 index 0000000..4028172 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_001.png b/client/assets/sprites/kael/animations/skill2/south/frame_001.png new file mode 100644 index 0000000..e1595f3 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_002.png b/client/assets/sprites/kael/animations/skill2/south/frame_002.png new file mode 100644 index 0000000..ebcefd7 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_003.png b/client/assets/sprites/kael/animations/skill2/south/frame_003.png new file mode 100644 index 0000000..f8de4f5 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_004.png b/client/assets/sprites/kael/animations/skill2/south/frame_004.png new file mode 100644 index 0000000..fe0e9ec Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_005.png b/client/assets/sprites/kael/animations/skill2/south/frame_005.png new file mode 100644 index 0000000..2d80ccf Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_005.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_006.png b/client/assets/sprites/kael/animations/skill2/south/frame_006.png new file mode 100644 index 0000000..9f2191a Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_006.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_007.png b/client/assets/sprites/kael/animations/skill2/south/frame_007.png new file mode 100644 index 0000000..0c147a3 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_007.png differ diff --git a/client/assets/sprites/kael/animations/skill2/south/frame_008.png b/client/assets/sprites/kael/animations/skill2/south/frame_008.png new file mode 100644 index 0000000..39c7536 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill2/south/frame_008.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_000.png b/client/assets/sprites/kael/animations/skill3/south/frame_000.png new file mode 100644 index 0000000..4028172 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_000.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_001.png b/client/assets/sprites/kael/animations/skill3/south/frame_001.png new file mode 100644 index 0000000..0c20994 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_001.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_002.png b/client/assets/sprites/kael/animations/skill3/south/frame_002.png new file mode 100644 index 0000000..222a6f7 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_002.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_003.png b/client/assets/sprites/kael/animations/skill3/south/frame_003.png new file mode 100644 index 0000000..59e54fe Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_003.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_004.png b/client/assets/sprites/kael/animations/skill3/south/frame_004.png new file mode 100644 index 0000000..afc863b Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_004.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_005.png b/client/assets/sprites/kael/animations/skill3/south/frame_005.png new file mode 100644 index 0000000..00c223d Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_005.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_006.png b/client/assets/sprites/kael/animations/skill3/south/frame_006.png new file mode 100644 index 0000000..2c27057 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_006.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_007.png b/client/assets/sprites/kael/animations/skill3/south/frame_007.png new file mode 100644 index 0000000..55ce974 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_007.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_008.png b/client/assets/sprites/kael/animations/skill3/south/frame_008.png new file mode 100644 index 0000000..e8b85f7 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_008.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_009.png b/client/assets/sprites/kael/animations/skill3/south/frame_009.png new file mode 100644 index 0000000..aac5ffa Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_009.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_010.png b/client/assets/sprites/kael/animations/skill3/south/frame_010.png new file mode 100644 index 0000000..8b0ef73 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_010.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_011.png b/client/assets/sprites/kael/animations/skill3/south/frame_011.png new file mode 100644 index 0000000..a8b2d84 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_011.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_012.png b/client/assets/sprites/kael/animations/skill3/south/frame_012.png new file mode 100644 index 0000000..1c990f6 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_012.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_013.png b/client/assets/sprites/kael/animations/skill3/south/frame_013.png new file mode 100644 index 0000000..86d72dc Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_013.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_014.png b/client/assets/sprites/kael/animations/skill3/south/frame_014.png new file mode 100644 index 0000000..318e586 Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_014.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_015.png b/client/assets/sprites/kael/animations/skill3/south/frame_015.png new file mode 100644 index 0000000..43c1fed Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_015.png differ diff --git a/client/assets/sprites/kael/animations/skill3/south/frame_016.png b/client/assets/sprites/kael/animations/skill3/south/frame_016.png new file mode 100644 index 0000000..1dc90fe Binary files /dev/null and b/client/assets/sprites/kael/animations/skill3/south/frame_016.png differ diff --git a/client/assets/sprites/kael/rotations/east.png b/client/assets/sprites/kael/rotations/east.png new file mode 100644 index 0000000..a52afb8 Binary files /dev/null and b/client/assets/sprites/kael/rotations/east.png differ diff --git a/client/assets/sprites/kael/rotations/north.png b/client/assets/sprites/kael/rotations/north.png new file mode 100644 index 0000000..1069e10 Binary files /dev/null and b/client/assets/sprites/kael/rotations/north.png differ diff --git a/client/assets/sprites/kael/rotations/south.png b/client/assets/sprites/kael/rotations/south.png new file mode 100644 index 0000000..60d1bab Binary files /dev/null and b/client/assets/sprites/kael/rotations/south.png differ diff --git a/client/assets/sprites/kael/rotations/west.png b/client/assets/sprites/kael/rotations/west.png new file mode 100644 index 0000000..69d75fd Binary files /dev/null and b/client/assets/sprites/kael/rotations/west.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_000.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_000.png new file mode 100644 index 0000000..981896e Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_000.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_001.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_001.png new file mode 100644 index 0000000..cd7e34f Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_001.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_002.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_002.png new file mode 100644 index 0000000..5a90b93 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_002.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_003.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_003.png new file mode 100644 index 0000000..f825659 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_003.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_004.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_004.png new file mode 100644 index 0000000..28ebee7 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_004.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_005.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_005.png new file mode 100644 index 0000000..55fd9c0 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_005.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_006.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_006.png new file mode 100644 index 0000000..01868b7 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_006.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_007.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_007.png new file mode 100644 index 0000000..9ba80b7 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_007.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_008.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_008.png new file mode 100644 index 0000000..bfef0bb Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_008.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_000.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_000.png new file mode 100644 index 0000000..0ba1e88 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_000.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_001.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_001.png new file mode 100644 index 0000000..72e0ecf Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_001.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_002.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_002.png new file mode 100644 index 0000000..3dfe865 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_002.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_003.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_003.png new file mode 100644 index 0000000..c58e3cf Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_003.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_004.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_004.png new file mode 100644 index 0000000..fe83a5e Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_004.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_005.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_005.png new file mode 100644 index 0000000..8c84a38 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_005.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_006.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_006.png new file mode 100644 index 0000000..c1ddc03 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_006.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_007.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_007.png new file mode 100644 index 0000000..909389b Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_007.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_008.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_008.png new file mode 100644 index 0000000..32ead17 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_008.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_000.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_000.png new file mode 100644 index 0000000..bd75e50 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_000.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_001.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_001.png new file mode 100644 index 0000000..5ca7dd7 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_001.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_002.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_002.png new file mode 100644 index 0000000..9510a1b Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_002.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_003.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_003.png new file mode 100644 index 0000000..835d518 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_003.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_004.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_004.png new file mode 100644 index 0000000..531b904 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_004.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_005.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_005.png new file mode 100644 index 0000000..d027f4c Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_005.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_006.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_006.png new file mode 100644 index 0000000..59ea921 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_006.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_007.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_007.png new file mode 100644 index 0000000..8fc7dfe Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_007.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_008.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_008.png new file mode 100644 index 0000000..b61272b Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_008.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_000.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_000.png new file mode 100644 index 0000000..0fea246 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_000.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_001.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_001.png new file mode 100644 index 0000000..4b82a72 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_001.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_002.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_002.png new file mode 100644 index 0000000..5651fe3 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_002.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_003.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_003.png new file mode 100644 index 0000000..9728fa6 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_003.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_004.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_004.png new file mode 100644 index 0000000..ddaab56 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_004.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_005.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_005.png new file mode 100644 index 0000000..141bfaa Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_005.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_006.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_006.png new file mode 100644 index 0000000..8a26fc8 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_006.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_007.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_007.png new file mode 100644 index 0000000..f7b2786 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_007.png differ diff --git a/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_008.png b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_008.png new file mode 100644 index 0000000..16e72a9 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_008.png differ diff --git a/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_000.png b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_000.png new file mode 100644 index 0000000..0cd8441 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_000.png differ diff --git a/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_001.png b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_001.png new file mode 100644 index 0000000..bf292ac Binary files /dev/null and b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_001.png differ diff --git a/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_002.png b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_002.png new file mode 100644 index 0000000..f6360ed Binary files /dev/null and b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_002.png differ diff --git a/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_003.png b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_003.png new file mode 100644 index 0000000..c62bfa9 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_003.png differ diff --git a/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_004.png b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_004.png new file mode 100644 index 0000000..596f7ec Binary files /dev/null and b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_004.png differ diff --git a/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_005.png b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_005.png new file mode 100644 index 0000000..b6df468 Binary files /dev/null and b/client/assets/sprites/morveth/animations/Running-de5993f1/south/frame_005.png differ diff --git a/client/assets/sprites/morveth/metadata.json b/client/assets/sprites/morveth/metadata.json new file mode 100644 index 0000000..b814a9f --- /dev/null +++ b/client/assets/sprites/morveth/metadata.json @@ -0,0 +1,83 @@ +{ + "character": { + "id": "7e6c7399-64e8-4630-9e8c-cffb48cc33a9", + "name": "Morveth is an ancient and colossal corrupted warlord, far larger and heavier than Vexaris. Massive hunched body, twice as wide as it is tall, built like a fortress. The torso is encased in thick black\n obsidian plate armor, cracked and fused directly into the flesh beneath \u2014 there is no separation between armor and body. Eight massive tentacle-like appendages emerge from the back and shoulders, each tipped\n with a jagged hooked blade. Three eyes arranged vertically on the face, all glowing deep crimson. No mouth visible, just smooth cracked black stone where the lower face should be. The arms are huge and\n disproportionately long, dragging slightly at rest. The legs are short, thick pillars of dark rock. Eight gemstones embedded across the chest, alternating between deep red and sickly green, pulsing slowly.\n Dark purple smoke seeps from every joint and crack in the armor.", + "prompt": "Morveth is an ancient and colossal corrupted warlord, far larger and heavier than Vexaris. Massive hunched body, twice as wide as it is tall, built like a fortress. The torso is encased in thick black\n obsidian plate armor, cracked and fused directly into the flesh beneath \u2014 there is no separation between armor and body. Eight massive tentacle-like appendages emerge from the back and shoulders, each tipped\n with a jagged hooked blade. Three eyes arranged vertically on the face, all glowing deep crimson. No mouth visible, just smooth cracked black stone where the lower face should be. The arms are huge and\n disproportionately long, dragging slightly at rest. The legs are short, thick pillars of dark rock. Eight gemstones embedded across the chest, alternating between deep red and sickly green, pulsing slowly.\n Dark purple smoke seeps from every joint and crack in the armor.", + "size": { + "width": 92, + "height": 92 + }, + "template_id": "mannequin", + "directions": 4, + "view": "low top-down", + "created_at": "2026-04-29T10:33:37.739007+00:00" + }, + "frames": { + "rotations": { + "south": "rotations/south.png", + "west": "rotations/west.png", + "east": "rotations/east.png", + "north": "rotations/north.png" + }, + "animations": { + "Running-de5993f1": { + "south": [ + "animations/Running-de5993f1/south/frame_000.png", + "animations/Running-de5993f1/south/frame_001.png", + "animations/Running-de5993f1/south/frame_002.png", + "animations/Running-de5993f1/south/frame_003.png", + "animations/Running-de5993f1/south/frame_004.png", + "animations/Running-de5993f1/south/frame_005.png" + ] + }, + "Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c": { + "east": [ + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_000.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_001.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_002.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_003.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_004.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_005.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_006.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_007.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/east/frame_008.png" + ], + "south": [ + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_000.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_001.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_002.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_003.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_004.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_005.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_006.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_007.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/south/frame_008.png" + ], + "west": [ + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_000.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_001.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_002.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_003.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_004.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_005.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_006.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_007.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/west/frame_008.png" + ], + "north": [ + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_000.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_001.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_002.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_003.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_004.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_005.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_006.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_007.png", + "animations/Morveth_raises_one_enormous_arm_high_pauses_for_a-ccb9856c/north/frame_008.png" + ] + } + } + }, + "export_version": "2.0", + "export_date": "2026-05-03T15:13:12.276413" +} \ No newline at end of file diff --git a/client/assets/sprites/morveth/rotations/east.png b/client/assets/sprites/morveth/rotations/east.png new file mode 100644 index 0000000..4f6248b Binary files /dev/null and b/client/assets/sprites/morveth/rotations/east.png differ diff --git a/client/assets/sprites/morveth/rotations/north.png b/client/assets/sprites/morveth/rotations/north.png new file mode 100644 index 0000000..88f98bb Binary files /dev/null and b/client/assets/sprites/morveth/rotations/north.png differ diff --git a/client/assets/sprites/morveth/rotations/south.png b/client/assets/sprites/morveth/rotations/south.png new file mode 100644 index 0000000..ea96c5b Binary files /dev/null and b/client/assets/sprites/morveth/rotations/south.png differ diff --git a/client/assets/sprites/morveth/rotations/west.png b/client/assets/sprites/morveth/rotations/west.png new file mode 100644 index 0000000..0f404e5 Binary files /dev/null and b/client/assets/sprites/morveth/rotations/west.png differ diff --git a/client/assets/sprites/rampant/animations/running/east/frame_000.png b/client/assets/sprites/rampant/animations/running/east/frame_000.png new file mode 100644 index 0000000..0174e1f Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/rampant/animations/running/east/frame_001.png b/client/assets/sprites/rampant/animations/running/east/frame_001.png new file mode 100644 index 0000000..ff18b22 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/rampant/animations/running/east/frame_002.png b/client/assets/sprites/rampant/animations/running/east/frame_002.png new file mode 100644 index 0000000..a11106e Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/rampant/animations/running/east/frame_003.png b/client/assets/sprites/rampant/animations/running/east/frame_003.png new file mode 100644 index 0000000..2f22617 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/rampant/animations/running/east/frame_004.png b/client/assets/sprites/rampant/animations/running/east/frame_004.png new file mode 100644 index 0000000..b43d2b0 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/east/frame_004.png differ diff --git a/client/assets/sprites/rampant/animations/running/east/frame_005.png b/client/assets/sprites/rampant/animations/running/east/frame_005.png new file mode 100644 index 0000000..92becab Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/east/frame_005.png differ diff --git a/client/assets/sprites/rampant/animations/running/north/frame_000.png b/client/assets/sprites/rampant/animations/running/north/frame_000.png new file mode 100644 index 0000000..043c3ec Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/rampant/animations/running/north/frame_001.png b/client/assets/sprites/rampant/animations/running/north/frame_001.png new file mode 100644 index 0000000..35b672c Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/rampant/animations/running/north/frame_002.png b/client/assets/sprites/rampant/animations/running/north/frame_002.png new file mode 100644 index 0000000..7125a1a Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/rampant/animations/running/north/frame_003.png b/client/assets/sprites/rampant/animations/running/north/frame_003.png new file mode 100644 index 0000000..ddbf692 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/rampant/animations/running/north/frame_004.png b/client/assets/sprites/rampant/animations/running/north/frame_004.png new file mode 100644 index 0000000..203b836 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/north/frame_004.png differ diff --git a/client/assets/sprites/rampant/animations/running/north/frame_005.png b/client/assets/sprites/rampant/animations/running/north/frame_005.png new file mode 100644 index 0000000..3468f69 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/north/frame_005.png differ diff --git a/client/assets/sprites/rampant/animations/running/south/frame_000.png b/client/assets/sprites/rampant/animations/running/south/frame_000.png new file mode 100644 index 0000000..60447c8 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/rampant/animations/running/south/frame_001.png b/client/assets/sprites/rampant/animations/running/south/frame_001.png new file mode 100644 index 0000000..8dbe4b6 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/rampant/animations/running/south/frame_002.png b/client/assets/sprites/rampant/animations/running/south/frame_002.png new file mode 100644 index 0000000..802cd4c Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/rampant/animations/running/south/frame_003.png b/client/assets/sprites/rampant/animations/running/south/frame_003.png new file mode 100644 index 0000000..7fbf55a Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/rampant/animations/running/south/frame_004.png b/client/assets/sprites/rampant/animations/running/south/frame_004.png new file mode 100644 index 0000000..bdc3a22 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/south/frame_004.png differ diff --git a/client/assets/sprites/rampant/animations/running/south/frame_005.png b/client/assets/sprites/rampant/animations/running/south/frame_005.png new file mode 100644 index 0000000..8c0acdd Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/south/frame_005.png differ diff --git a/client/assets/sprites/rampant/animations/running/west/frame_000.png b/client/assets/sprites/rampant/animations/running/west/frame_000.png new file mode 100644 index 0000000..b7fc11d Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/rampant/animations/running/west/frame_001.png b/client/assets/sprites/rampant/animations/running/west/frame_001.png new file mode 100644 index 0000000..a482480 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/rampant/animations/running/west/frame_002.png b/client/assets/sprites/rampant/animations/running/west/frame_002.png new file mode 100644 index 0000000..23365ce Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/rampant/animations/running/west/frame_003.png b/client/assets/sprites/rampant/animations/running/west/frame_003.png new file mode 100644 index 0000000..f780602 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/rampant/animations/running/west/frame_004.png b/client/assets/sprites/rampant/animations/running/west/frame_004.png new file mode 100644 index 0000000..232f35a Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/west/frame_004.png differ diff --git a/client/assets/sprites/rampant/animations/running/west/frame_005.png b/client/assets/sprites/rampant/animations/running/west/frame_005.png new file mode 100644 index 0000000..ded7657 Binary files /dev/null and b/client/assets/sprites/rampant/animations/running/west/frame_005.png differ diff --git a/client/assets/sprites/rampant/metadata.json b/client/assets/sprites/rampant/metadata.json new file mode 100644 index 0000000..2b01838 --- /dev/null +++ b/client/assets/sprites/rampant/metadata.json @@ -0,0 +1,61 @@ +{ + "character": { + "id": "fbd59661-83c7-4ee0-9758-7220de825133", + "name": "A corrupted soul turned feral predator, small and low to the ground, almost crawling. Hunched humanoid body with elongated limbs built for lunging forward. Translucent grey-smoke skin with glowing orange\n veins pulsing across the torso and limbs. No clothing, just the bare corrupted form. Wide open jaw with jagged uneven teeth, two bright orange glowing eyes. Smaller and more compact than the Fracture enemy.\n", + "prompt": "A corrupted soul turned feral predator, small and low to the ground, almost crawling. Hunched humanoid body with elongated limbs built for lunging forward. Translucent grey-smoke skin with glowing orange\n veins pulsing across the torso and limbs. No clothing, just the bare corrupted form. Wide open jaw with jagged uneven teeth, two bright orange glowing eyes. Smaller and more compact than the Fracture enemy.\n", + "size": { + "width": 48, + "height": 48 + }, + "template_id": "mannequin", + "directions": 4, + "view": "low top-down", + "created_at": "2026-04-17T13:58:19.307396+00:00" + }, + "frames": { + "rotations": { + "south": "rotations/south.png", + "west": "rotations/west.png", + "east": "rotations/east.png", + "north": "rotations/north.png" + }, + "animations": { + "Running-835ba967": { + "south": [ + "animations/Running-835ba967/south/frame_000.png", + "animations/Running-835ba967/south/frame_001.png", + "animations/Running-835ba967/south/frame_002.png", + "animations/Running-835ba967/south/frame_003.png", + "animations/Running-835ba967/south/frame_004.png", + "animations/Running-835ba967/south/frame_005.png" + ], + "north": [ + "animations/Running-835ba967/north/frame_000.png", + "animations/Running-835ba967/north/frame_001.png", + "animations/Running-835ba967/north/frame_002.png", + "animations/Running-835ba967/north/frame_003.png", + "animations/Running-835ba967/north/frame_004.png", + "animations/Running-835ba967/north/frame_005.png" + ], + "east": [ + "animations/Running-835ba967/east/frame_000.png", + "animations/Running-835ba967/east/frame_001.png", + "animations/Running-835ba967/east/frame_002.png", + "animations/Running-835ba967/east/frame_003.png", + "animations/Running-835ba967/east/frame_004.png", + "animations/Running-835ba967/east/frame_005.png" + ], + "west": [ + "animations/Running-835ba967/west/frame_000.png", + "animations/Running-835ba967/west/frame_001.png", + "animations/Running-835ba967/west/frame_002.png", + "animations/Running-835ba967/west/frame_003.png", + "animations/Running-835ba967/west/frame_004.png", + "animations/Running-835ba967/west/frame_005.png" + ] + } + } + }, + "export_version": "2.0", + "export_date": "2026-04-17T14:06:15.363084" +} \ No newline at end of file diff --git a/client/assets/sprites/rampant/rotations/east.png b/client/assets/sprites/rampant/rotations/east.png new file mode 100644 index 0000000..f4fa13f Binary files /dev/null and b/client/assets/sprites/rampant/rotations/east.png differ diff --git a/client/assets/sprites/rampant/rotations/north.png b/client/assets/sprites/rampant/rotations/north.png new file mode 100644 index 0000000..ff1fa23 Binary files /dev/null and b/client/assets/sprites/rampant/rotations/north.png differ diff --git a/client/assets/sprites/rampant/rotations/south.png b/client/assets/sprites/rampant/rotations/south.png new file mode 100644 index 0000000..170af49 Binary files /dev/null and b/client/assets/sprites/rampant/rotations/south.png differ diff --git a/client/assets/sprites/rampant/rotations/west.png b/client/assets/sprites/rampant/rotations/west.png new file mode 100644 index 0000000..cd9db58 Binary files /dev/null and b/client/assets/sprites/rampant/rotations/west.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_000.png b/client/assets/sprites/seris/animations/attack/east/frame_000.png new file mode 100644 index 0000000..12a4a2d Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_001.png b/client/assets/sprites/seris/animations/attack/east/frame_001.png new file mode 100644 index 0000000..87ced98 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_002.png b/client/assets/sprites/seris/animations/attack/east/frame_002.png new file mode 100644 index 0000000..aab6d40 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_003.png b/client/assets/sprites/seris/animations/attack/east/frame_003.png new file mode 100644 index 0000000..76f6cca Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_004.png b/client/assets/sprites/seris/animations/attack/east/frame_004.png new file mode 100644 index 0000000..4a9cc5b Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_005.png b/client/assets/sprites/seris/animations/attack/east/frame_005.png new file mode 100644 index 0000000..f4a5901 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/attack/east/frame_006.png b/client/assets/sprites/seris/animations/attack/east/frame_006.png new file mode 100644 index 0000000..f6aa8cd Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/east/frame_006.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_000.png b/client/assets/sprites/seris/animations/attack/north/frame_000.png new file mode 100644 index 0000000..bb09e34 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_001.png b/client/assets/sprites/seris/animations/attack/north/frame_001.png new file mode 100644 index 0000000..a2fb37a Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_002.png b/client/assets/sprites/seris/animations/attack/north/frame_002.png new file mode 100644 index 0000000..9e578ab Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_003.png b/client/assets/sprites/seris/animations/attack/north/frame_003.png new file mode 100644 index 0000000..9eba388 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_004.png b/client/assets/sprites/seris/animations/attack/north/frame_004.png new file mode 100644 index 0000000..98d5f6c Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_005.png b/client/assets/sprites/seris/animations/attack/north/frame_005.png new file mode 100644 index 0000000..36f60cd Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/attack/north/frame_006.png b/client/assets/sprites/seris/animations/attack/north/frame_006.png new file mode 100644 index 0000000..9729b6c Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/north/frame_006.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_000.png b/client/assets/sprites/seris/animations/attack/south/frame_000.png new file mode 100644 index 0000000..56dd8e9 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_001.png b/client/assets/sprites/seris/animations/attack/south/frame_001.png new file mode 100644 index 0000000..6540b82 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_002.png b/client/assets/sprites/seris/animations/attack/south/frame_002.png new file mode 100644 index 0000000..d78f70f Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_003.png b/client/assets/sprites/seris/animations/attack/south/frame_003.png new file mode 100644 index 0000000..a9455d7 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_004.png b/client/assets/sprites/seris/animations/attack/south/frame_004.png new file mode 100644 index 0000000..7f90cb9 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_005.png b/client/assets/sprites/seris/animations/attack/south/frame_005.png new file mode 100644 index 0000000..71348ed Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/attack/south/frame_006.png b/client/assets/sprites/seris/animations/attack/south/frame_006.png new file mode 100644 index 0000000..af4616a Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/south/frame_006.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_000.png b/client/assets/sprites/seris/animations/attack/west/frame_000.png new file mode 100644 index 0000000..25c0f94 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_001.png b/client/assets/sprites/seris/animations/attack/west/frame_001.png new file mode 100644 index 0000000..573cc0e Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_002.png b/client/assets/sprites/seris/animations/attack/west/frame_002.png new file mode 100644 index 0000000..cc6c5c8 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_003.png b/client/assets/sprites/seris/animations/attack/west/frame_003.png new file mode 100644 index 0000000..de369bf Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_004.png b/client/assets/sprites/seris/animations/attack/west/frame_004.png new file mode 100644 index 0000000..c38c576 Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_005.png b/client/assets/sprites/seris/animations/attack/west/frame_005.png new file mode 100644 index 0000000..fe2224a Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/attack/west/frame_006.png b/client/assets/sprites/seris/animations/attack/west/frame_006.png new file mode 100644 index 0000000..1ee375c Binary files /dev/null and b/client/assets/sprites/seris/animations/attack/west/frame_006.png differ diff --git a/client/assets/sprites/seris/animations/running/east/frame_000.png b/client/assets/sprites/seris/animations/running/east/frame_000.png new file mode 100644 index 0000000..b3fd6ad Binary files /dev/null and b/client/assets/sprites/seris/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/running/east/frame_001.png b/client/assets/sprites/seris/animations/running/east/frame_001.png new file mode 100644 index 0000000..c11e6d5 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/running/east/frame_002.png b/client/assets/sprites/seris/animations/running/east/frame_002.png new file mode 100644 index 0000000..be4a09b Binary files /dev/null and b/client/assets/sprites/seris/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/running/east/frame_003.png b/client/assets/sprites/seris/animations/running/east/frame_003.png new file mode 100644 index 0000000..d91587f Binary files /dev/null and b/client/assets/sprites/seris/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/running/east/frame_004.png b/client/assets/sprites/seris/animations/running/east/frame_004.png new file mode 100644 index 0000000..28dd5cc Binary files /dev/null and b/client/assets/sprites/seris/animations/running/east/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/running/east/frame_005.png b/client/assets/sprites/seris/animations/running/east/frame_005.png new file mode 100644 index 0000000..88fbdb4 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/east/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/running/north/frame_000.png b/client/assets/sprites/seris/animations/running/north/frame_000.png new file mode 100644 index 0000000..ee3bcce Binary files /dev/null and b/client/assets/sprites/seris/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/running/north/frame_001.png b/client/assets/sprites/seris/animations/running/north/frame_001.png new file mode 100644 index 0000000..57dc573 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/running/north/frame_002.png b/client/assets/sprites/seris/animations/running/north/frame_002.png new file mode 100644 index 0000000..5cd1770 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/running/north/frame_003.png b/client/assets/sprites/seris/animations/running/north/frame_003.png new file mode 100644 index 0000000..b743946 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/running/north/frame_004.png b/client/assets/sprites/seris/animations/running/north/frame_004.png new file mode 100644 index 0000000..780442a Binary files /dev/null and b/client/assets/sprites/seris/animations/running/north/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/running/north/frame_005.png b/client/assets/sprites/seris/animations/running/north/frame_005.png new file mode 100644 index 0000000..d6d4443 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/north/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/running/south/frame_000.png b/client/assets/sprites/seris/animations/running/south/frame_000.png new file mode 100644 index 0000000..64ed095 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/running/south/frame_001.png b/client/assets/sprites/seris/animations/running/south/frame_001.png new file mode 100644 index 0000000..1265866 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/running/south/frame_002.png b/client/assets/sprites/seris/animations/running/south/frame_002.png new file mode 100644 index 0000000..9899f01 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/running/south/frame_003.png b/client/assets/sprites/seris/animations/running/south/frame_003.png new file mode 100644 index 0000000..3f7a338 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/running/south/frame_004.png b/client/assets/sprites/seris/animations/running/south/frame_004.png new file mode 100644 index 0000000..5eeb273 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/south/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/running/south/frame_005.png b/client/assets/sprites/seris/animations/running/south/frame_005.png new file mode 100644 index 0000000..1022e0f Binary files /dev/null and b/client/assets/sprites/seris/animations/running/south/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/running/west/frame_000.png b/client/assets/sprites/seris/animations/running/west/frame_000.png new file mode 100644 index 0000000..dbba088 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/running/west/frame_001.png b/client/assets/sprites/seris/animations/running/west/frame_001.png new file mode 100644 index 0000000..bfce1ef Binary files /dev/null and b/client/assets/sprites/seris/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/running/west/frame_002.png b/client/assets/sprites/seris/animations/running/west/frame_002.png new file mode 100644 index 0000000..a7e54f9 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/running/west/frame_003.png b/client/assets/sprites/seris/animations/running/west/frame_003.png new file mode 100644 index 0000000..9ab2587 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/running/west/frame_004.png b/client/assets/sprites/seris/animations/running/west/frame_004.png new file mode 100644 index 0000000..6a1d139 Binary files /dev/null and b/client/assets/sprites/seris/animations/running/west/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/running/west/frame_005.png b/client/assets/sprites/seris/animations/running/west/frame_005.png new file mode 100644 index 0000000..7a2f67f Binary files /dev/null and b/client/assets/sprites/seris/animations/running/west/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_000.png b/client/assets/sprites/seris/animations/skill1/south/frame_000.png new file mode 100644 index 0000000..56dd8e9 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_001.png b/client/assets/sprites/seris/animations/skill1/south/frame_001.png new file mode 100644 index 0000000..335993a Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_002.png b/client/assets/sprites/seris/animations/skill1/south/frame_002.png new file mode 100644 index 0000000..0814b7d Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_003.png b/client/assets/sprites/seris/animations/skill1/south/frame_003.png new file mode 100644 index 0000000..34937e2 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_004.png b/client/assets/sprites/seris/animations/skill1/south/frame_004.png new file mode 100644 index 0000000..3a81179 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_005.png b/client/assets/sprites/seris/animations/skill1/south/frame_005.png new file mode 100644 index 0000000..0d6a4af Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_006.png b/client/assets/sprites/seris/animations/skill1/south/frame_006.png new file mode 100644 index 0000000..7042f7b Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_006.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_007.png b/client/assets/sprites/seris/animations/skill1/south/frame_007.png new file mode 100644 index 0000000..f35222e Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_007.png differ diff --git a/client/assets/sprites/seris/animations/skill1/south/frame_008.png b/client/assets/sprites/seris/animations/skill1/south/frame_008.png new file mode 100644 index 0000000..169dcd4 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill1/south/frame_008.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_000.png b/client/assets/sprites/seris/animations/skill2/south/frame_000.png new file mode 100644 index 0000000..56dd8e9 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_001.png b/client/assets/sprites/seris/animations/skill2/south/frame_001.png new file mode 100644 index 0000000..8c0e19a Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_002.png b/client/assets/sprites/seris/animations/skill2/south/frame_002.png new file mode 100644 index 0000000..829360e Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_003.png b/client/assets/sprites/seris/animations/skill2/south/frame_003.png new file mode 100644 index 0000000..e161c89 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_004.png b/client/assets/sprites/seris/animations/skill2/south/frame_004.png new file mode 100644 index 0000000..6498e7b Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_004.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_005.png b/client/assets/sprites/seris/animations/skill2/south/frame_005.png new file mode 100644 index 0000000..294ad28 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_005.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_006.png b/client/assets/sprites/seris/animations/skill2/south/frame_006.png new file mode 100644 index 0000000..bdadac1 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_006.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_007.png b/client/assets/sprites/seris/animations/skill2/south/frame_007.png new file mode 100644 index 0000000..04baece Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_007.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_008.png b/client/assets/sprites/seris/animations/skill2/south/frame_008.png new file mode 100644 index 0000000..b251ee9 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_008.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_009.png b/client/assets/sprites/seris/animations/skill2/south/frame_009.png new file mode 100644 index 0000000..5c89c4c Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_009.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_010.png b/client/assets/sprites/seris/animations/skill2/south/frame_010.png new file mode 100644 index 0000000..b111c20 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_010.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_011.png b/client/assets/sprites/seris/animations/skill2/south/frame_011.png new file mode 100644 index 0000000..5566df7 Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_011.png differ diff --git a/client/assets/sprites/seris/animations/skill2/south/frame_012.png b/client/assets/sprites/seris/animations/skill2/south/frame_012.png new file mode 100644 index 0000000..bf8b34d Binary files /dev/null and b/client/assets/sprites/seris/animations/skill2/south/frame_012.png differ diff --git a/client/assets/sprites/seris/animations/teleport/south/frame_000.png b/client/assets/sprites/seris/animations/teleport/south/frame_000.png new file mode 100644 index 0000000..56dd8e9 Binary files /dev/null and b/client/assets/sprites/seris/animations/teleport/south/frame_000.png differ diff --git a/client/assets/sprites/seris/animations/teleport/south/frame_001.png b/client/assets/sprites/seris/animations/teleport/south/frame_001.png new file mode 100644 index 0000000..7eb25e2 Binary files /dev/null and b/client/assets/sprites/seris/animations/teleport/south/frame_001.png differ diff --git a/client/assets/sprites/seris/animations/teleport/south/frame_002.png b/client/assets/sprites/seris/animations/teleport/south/frame_002.png new file mode 100644 index 0000000..4949bc6 Binary files /dev/null and b/client/assets/sprites/seris/animations/teleport/south/frame_002.png differ diff --git a/client/assets/sprites/seris/animations/teleport/south/frame_003.png b/client/assets/sprites/seris/animations/teleport/south/frame_003.png new file mode 100644 index 0000000..1da269f Binary files /dev/null and b/client/assets/sprites/seris/animations/teleport/south/frame_003.png differ diff --git a/client/assets/sprites/seris/animations/teleport/south/frame_004.png b/client/assets/sprites/seris/animations/teleport/south/frame_004.png new file mode 100644 index 0000000..5122382 Binary files /dev/null and b/client/assets/sprites/seris/animations/teleport/south/frame_004.png differ diff --git a/client/assets/sprites/seris/rotations/east.png b/client/assets/sprites/seris/rotations/east.png new file mode 100644 index 0000000..646c09b Binary files /dev/null and b/client/assets/sprites/seris/rotations/east.png differ diff --git a/client/assets/sprites/seris/rotations/north.png b/client/assets/sprites/seris/rotations/north.png new file mode 100644 index 0000000..03210e7 Binary files /dev/null and b/client/assets/sprites/seris/rotations/north.png differ diff --git a/client/assets/sprites/seris/rotations/south.png b/client/assets/sprites/seris/rotations/south.png new file mode 100644 index 0000000..5dc2153 Binary files /dev/null and b/client/assets/sprites/seris/rotations/south.png differ diff --git a/client/assets/sprites/seris/rotations/west.png b/client/assets/sprites/seris/rotations/west.png new file mode 100644 index 0000000..bf5dd76 Binary files /dev/null and b/client/assets/sprites/seris/rotations/west.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_000.png b/client/assets/sprites/vexaris/animations/attack/east/frame_000.png new file mode 100644 index 0000000..32d1c43 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_001.png b/client/assets/sprites/vexaris/animations/attack/east/frame_001.png new file mode 100644 index 0000000..ac11862 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_002.png b/client/assets/sprites/vexaris/animations/attack/east/frame_002.png new file mode 100644 index 0000000..8ac78c3 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_003.png b/client/assets/sprites/vexaris/animations/attack/east/frame_003.png new file mode 100644 index 0000000..e5cf23e Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_004.png b/client/assets/sprites/vexaris/animations/attack/east/frame_004.png new file mode 100644 index 0000000..79bcb3a Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_005.png b/client/assets/sprites/vexaris/animations/attack/east/frame_005.png new file mode 100644 index 0000000..e65c47f Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/east/frame_006.png b/client/assets/sprites/vexaris/animations/attack/east/frame_006.png new file mode 100644 index 0000000..f2ca0d7 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/east/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_000.png b/client/assets/sprites/vexaris/animations/attack/north/frame_000.png new file mode 100644 index 0000000..66a3241 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_001.png b/client/assets/sprites/vexaris/animations/attack/north/frame_001.png new file mode 100644 index 0000000..5ea84e0 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_002.png b/client/assets/sprites/vexaris/animations/attack/north/frame_002.png new file mode 100644 index 0000000..36b53e4 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_003.png b/client/assets/sprites/vexaris/animations/attack/north/frame_003.png new file mode 100644 index 0000000..84e9ff6 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_004.png b/client/assets/sprites/vexaris/animations/attack/north/frame_004.png new file mode 100644 index 0000000..50c3d76 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_005.png b/client/assets/sprites/vexaris/animations/attack/north/frame_005.png new file mode 100644 index 0000000..ed8e491 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/north/frame_006.png b/client/assets/sprites/vexaris/animations/attack/north/frame_006.png new file mode 100644 index 0000000..801e74f Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/north/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_000.png b/client/assets/sprites/vexaris/animations/attack/south/frame_000.png new file mode 100644 index 0000000..1539db5 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_001.png b/client/assets/sprites/vexaris/animations/attack/south/frame_001.png new file mode 100644 index 0000000..11b62fe Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_002.png b/client/assets/sprites/vexaris/animations/attack/south/frame_002.png new file mode 100644 index 0000000..a998c83 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_003.png b/client/assets/sprites/vexaris/animations/attack/south/frame_003.png new file mode 100644 index 0000000..9815ab0 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_004.png b/client/assets/sprites/vexaris/animations/attack/south/frame_004.png new file mode 100644 index 0000000..962b1c7 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_005.png b/client/assets/sprites/vexaris/animations/attack/south/frame_005.png new file mode 100644 index 0000000..895d2b1 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/south/frame_006.png b/client/assets/sprites/vexaris/animations/attack/south/frame_006.png new file mode 100644 index 0000000..f24f56e Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/south/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_000.png b/client/assets/sprites/vexaris/animations/attack/west/frame_000.png new file mode 100644 index 0000000..6ecf192 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_001.png b/client/assets/sprites/vexaris/animations/attack/west/frame_001.png new file mode 100644 index 0000000..81b3bc2 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_002.png b/client/assets/sprites/vexaris/animations/attack/west/frame_002.png new file mode 100644 index 0000000..95114c6 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_003.png b/client/assets/sprites/vexaris/animations/attack/west/frame_003.png new file mode 100644 index 0000000..cbdda85 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_004.png b/client/assets/sprites/vexaris/animations/attack/west/frame_004.png new file mode 100644 index 0000000..14130b4 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_005.png b/client/assets/sprites/vexaris/animations/attack/west/frame_005.png new file mode 100644 index 0000000..2291cec Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/attack/west/frame_006.png b/client/assets/sprites/vexaris/animations/attack/west/frame_006.png new file mode 100644 index 0000000..fb755b7 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/attack/west/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_000.png b/client/assets/sprites/vexaris/animations/burst/south/frame_000.png new file mode 100644 index 0000000..1539db5 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_001.png b/client/assets/sprites/vexaris/animations/burst/south/frame_001.png new file mode 100644 index 0000000..b31a542 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_002.png b/client/assets/sprites/vexaris/animations/burst/south/frame_002.png new file mode 100644 index 0000000..1b2b5c0 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_003.png b/client/assets/sprites/vexaris/animations/burst/south/frame_003.png new file mode 100644 index 0000000..7d1870d Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_004.png b/client/assets/sprites/vexaris/animations/burst/south/frame_004.png new file mode 100644 index 0000000..dc5a528 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_005.png b/client/assets/sprites/vexaris/animations/burst/south/frame_005.png new file mode 100644 index 0000000..1ca28c5 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_006.png b/client/assets/sprites/vexaris/animations/burst/south/frame_006.png new file mode 100644 index 0000000..25c24e7 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_007.png b/client/assets/sprites/vexaris/animations/burst/south/frame_007.png new file mode 100644 index 0000000..408e186 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_007.png differ diff --git a/client/assets/sprites/vexaris/animations/burst/south/frame_008.png b/client/assets/sprites/vexaris/animations/burst/south/frame_008.png new file mode 100644 index 0000000..31d941e Binary files /dev/null and b/client/assets/sprites/vexaris/animations/burst/south/frame_008.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_000.png b/client/assets/sprites/vexaris/animations/charge/east/frame_000.png new file mode 100644 index 0000000..32d1c43 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_001.png b/client/assets/sprites/vexaris/animations/charge/east/frame_001.png new file mode 100644 index 0000000..564a5c6 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_002.png b/client/assets/sprites/vexaris/animations/charge/east/frame_002.png new file mode 100644 index 0000000..a4193b1 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_003.png b/client/assets/sprites/vexaris/animations/charge/east/frame_003.png new file mode 100644 index 0000000..b8c0882 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_004.png b/client/assets/sprites/vexaris/animations/charge/east/frame_004.png new file mode 100644 index 0000000..00e6d78 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_005.png b/client/assets/sprites/vexaris/animations/charge/east/frame_005.png new file mode 100644 index 0000000..38c97d9 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/east/frame_006.png b/client/assets/sprites/vexaris/animations/charge/east/frame_006.png new file mode 100644 index 0000000..c44e9cc Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/east/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_000.png b/client/assets/sprites/vexaris/animations/charge/north/frame_000.png new file mode 100644 index 0000000..66a3241 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_001.png b/client/assets/sprites/vexaris/animations/charge/north/frame_001.png new file mode 100644 index 0000000..7e6d9fe Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_002.png b/client/assets/sprites/vexaris/animations/charge/north/frame_002.png new file mode 100644 index 0000000..1cfdeb1 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_003.png b/client/assets/sprites/vexaris/animations/charge/north/frame_003.png new file mode 100644 index 0000000..2e65faf Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_004.png b/client/assets/sprites/vexaris/animations/charge/north/frame_004.png new file mode 100644 index 0000000..dd3c3da Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_005.png b/client/assets/sprites/vexaris/animations/charge/north/frame_005.png new file mode 100644 index 0000000..0e6cc43 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/north/frame_006.png b/client/assets/sprites/vexaris/animations/charge/north/frame_006.png new file mode 100644 index 0000000..ccba9d0 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/north/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_000.png b/client/assets/sprites/vexaris/animations/charge/south/frame_000.png new file mode 100644 index 0000000..1539db5 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_001.png b/client/assets/sprites/vexaris/animations/charge/south/frame_001.png new file mode 100644 index 0000000..c6dff64 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_002.png b/client/assets/sprites/vexaris/animations/charge/south/frame_002.png new file mode 100644 index 0000000..a55f440 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_003.png b/client/assets/sprites/vexaris/animations/charge/south/frame_003.png new file mode 100644 index 0000000..ead2fb1 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_004.png b/client/assets/sprites/vexaris/animations/charge/south/frame_004.png new file mode 100644 index 0000000..bf64464 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_005.png b/client/assets/sprites/vexaris/animations/charge/south/frame_005.png new file mode 100644 index 0000000..8a0fda9 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/south/frame_006.png b/client/assets/sprites/vexaris/animations/charge/south/frame_006.png new file mode 100644 index 0000000..2de2926 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/south/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_000.png b/client/assets/sprites/vexaris/animations/charge/west/frame_000.png new file mode 100644 index 0000000..6ecf192 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_001.png b/client/assets/sprites/vexaris/animations/charge/west/frame_001.png new file mode 100644 index 0000000..228a8bb Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_002.png b/client/assets/sprites/vexaris/animations/charge/west/frame_002.png new file mode 100644 index 0000000..8c1fb9e Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_003.png b/client/assets/sprites/vexaris/animations/charge/west/frame_003.png new file mode 100644 index 0000000..baadf9d Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_004.png b/client/assets/sprites/vexaris/animations/charge/west/frame_004.png new file mode 100644 index 0000000..7efed30 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_004.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_005.png b/client/assets/sprites/vexaris/animations/charge/west/frame_005.png new file mode 100644 index 0000000..be82933 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_005.png differ diff --git a/client/assets/sprites/vexaris/animations/charge/west/frame_006.png b/client/assets/sprites/vexaris/animations/charge/west/frame_006.png new file mode 100644 index 0000000..2d4e962 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/charge/west/frame_006.png differ diff --git a/client/assets/sprites/vexaris/animations/running/east/frame_000.png b/client/assets/sprites/vexaris/animations/running/east/frame_000.png new file mode 100644 index 0000000..894d84f Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/east/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/running/east/frame_001.png b/client/assets/sprites/vexaris/animations/running/east/frame_001.png new file mode 100644 index 0000000..3c8e280 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/east/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/running/east/frame_002.png b/client/assets/sprites/vexaris/animations/running/east/frame_002.png new file mode 100644 index 0000000..e89a9f9 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/east/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/running/east/frame_003.png b/client/assets/sprites/vexaris/animations/running/east/frame_003.png new file mode 100644 index 0000000..da35ea9 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/east/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/running/north/frame_000.png b/client/assets/sprites/vexaris/animations/running/north/frame_000.png new file mode 100644 index 0000000..ed604c7 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/north/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/running/north/frame_001.png b/client/assets/sprites/vexaris/animations/running/north/frame_001.png new file mode 100644 index 0000000..bdfa17d Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/north/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/running/north/frame_002.png b/client/assets/sprites/vexaris/animations/running/north/frame_002.png new file mode 100644 index 0000000..3a56b5f Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/north/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/running/north/frame_003.png b/client/assets/sprites/vexaris/animations/running/north/frame_003.png new file mode 100644 index 0000000..a47d8b8 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/north/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/running/south/frame_000.png b/client/assets/sprites/vexaris/animations/running/south/frame_000.png new file mode 100644 index 0000000..615f9a6 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/south/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/running/south/frame_001.png b/client/assets/sprites/vexaris/animations/running/south/frame_001.png new file mode 100644 index 0000000..62c2e0c Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/south/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/running/south/frame_002.png b/client/assets/sprites/vexaris/animations/running/south/frame_002.png new file mode 100644 index 0000000..167fa9a Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/south/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/running/south/frame_003.png b/client/assets/sprites/vexaris/animations/running/south/frame_003.png new file mode 100644 index 0000000..9f95609 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/south/frame_003.png differ diff --git a/client/assets/sprites/vexaris/animations/running/west/frame_000.png b/client/assets/sprites/vexaris/animations/running/west/frame_000.png new file mode 100644 index 0000000..89cb1c5 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/west/frame_000.png differ diff --git a/client/assets/sprites/vexaris/animations/running/west/frame_001.png b/client/assets/sprites/vexaris/animations/running/west/frame_001.png new file mode 100644 index 0000000..8c73a23 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/west/frame_001.png differ diff --git a/client/assets/sprites/vexaris/animations/running/west/frame_002.png b/client/assets/sprites/vexaris/animations/running/west/frame_002.png new file mode 100644 index 0000000..4df5aa6 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/west/frame_002.png differ diff --git a/client/assets/sprites/vexaris/animations/running/west/frame_003.png b/client/assets/sprites/vexaris/animations/running/west/frame_003.png new file mode 100644 index 0000000..2f37fa9 Binary files /dev/null and b/client/assets/sprites/vexaris/animations/running/west/frame_003.png differ diff --git a/client/assets/sprites/vexaris/rotations/east.png b/client/assets/sprites/vexaris/rotations/east.png new file mode 100644 index 0000000..8b3b50e Binary files /dev/null and b/client/assets/sprites/vexaris/rotations/east.png differ diff --git a/client/assets/sprites/vexaris/rotations/north.png b/client/assets/sprites/vexaris/rotations/north.png new file mode 100644 index 0000000..b42fd7f Binary files /dev/null and b/client/assets/sprites/vexaris/rotations/north.png differ diff --git a/client/assets/sprites/vexaris/rotations/south.png b/client/assets/sprites/vexaris/rotations/south.png new file mode 100644 index 0000000..f0606ef Binary files /dev/null and b/client/assets/sprites/vexaris/rotations/south.png differ diff --git a/client/assets/sprites/vexaris/rotations/west.png b/client/assets/sprites/vexaris/rotations/west.png new file mode 100644 index 0000000..e516547 Binary files /dev/null and b/client/assets/sprites/vexaris/rotations/west.png differ diff --git a/client/assets/twitter.png b/client/assets/twitter.png new file mode 100644 index 0000000..84d3485 Binary files /dev/null and b/client/assets/twitter.png differ diff --git a/client/css/style.css b/client/css/style.css new file mode 100644 index 0000000..b2ff14d --- /dev/null +++ b/client/css/style.css @@ -0,0 +1,794 @@ +/* Reset */ +* { margin: 0; padding: 0; box-sizing: border-box; } + +:root { + --bg: #0d0a07; + --panel: #1a1208; + --panel-alt: #221a0c; + --border-hi: #7a5c2a; + --border-lo: #1a0e04; + --border-mid: #3a2810; + --accent: #d4a843; + --accent-dim: #8a6a30; + --text: #e8d5a3; + --text-dim: #7a6040; + --text-faint: #3a2c18; + --btn-bg: #3a2810; + --btn-hover: #4a3818; + --btn-active: #251a08; + --input-bg: #0f0a05; + --red: #8a2a1a; + --red-hi: #cc3322; + --green: #2a5a2a; + --blue: #1a2a5a; +} + +body { + background: var(--bg) url('../assets/sky.jpg') center / cover no-repeat fixed; + overflow: hidden; + font-family: 'Press Start 2P', 'Courier New', monospace; +} + +#game-canvas { + display: block; + position: fixed; + inset: 0; + width: 100vw; + height: 100vh; +} + +.hidden { display: none !important; } + +/* Minecraft border mixin */ +.mc-border { + border: 3px solid; + border-color: var(--border-hi) var(--border-lo) var(--border-lo) var(--border-hi); +} + +/* BUTTONS — Desert Minecraft style */ + +.mc-btn { + font-family: 'Press Start 2P', monospace; + font-size: 9px; + background: var(--btn-bg); + border-top: 3px solid var(--border-hi); + border-left: 3px solid var(--border-hi); + border-bottom: 3px solid var(--border-lo); + border-right: 3px solid var(--border-lo); + color: var(--text); + padding: 12px 24px; + cursor: pointer; + text-align: center; + letter-spacing: 1px; + width: 100%; + text-shadow: 1px 1px 0 #000; + transition: background 0.05s; + image-rendering: pixelated; +} +.mc-btn:hover:not(:disabled) { + background: var(--btn-hover); + border-top-color: #9a7c3a; + border-left-color: #9a7c3a; +} +.mc-btn:active:not(:disabled) { + background: var(--btn-active); + border-top-color: var(--border-lo); + border-left-color: var(--border-lo); + border-bottom-color: var(--border-hi); + border-right-color: var(--border-hi); + padding-top: 13px; + padding-bottom: 11px; +} +.mc-btn:disabled { + background: #1a1208; + border-top-color: #2a1e0c; + border-left-color: #2a1e0c; + border-bottom-color: #0a0804; + border-right-color: #0a0804; + color: var(--text-faint); + cursor: not-allowed; + text-shadow: none; +} + +.mc-btn-sm { + width: auto; + padding: 8px 14px; + font-size: 8px; +} + +.mc-btn-cancel { + color: #cc7755; +} +.mc-btn-cancel:hover:not(:disabled) { + background: #3a1808; + border-top-color: #8a4422; + border-left-color: #8a4422; +} + +.mc-btn-gold { + background: #3a2a08; + border-top-color: var(--accent); + border-left-color: var(--accent); + color: var(--accent); +} +.mc-btn-gold:hover:not(:disabled) { + background: #4a3810; + border-top-color: #f0cc60; + border-left-color: #f0cc60; + color: #f0d860; +} + +/* Inputs */ + +.mc-input { + background: var(--input-bg); + border-top: 2px solid #1a1208; + border-left: 2px solid #1a1208; + border-bottom: 2px solid var(--border-hi); + border-right: 2px solid var(--border-hi); + color: var(--text); + padding: 10px 12px; + font-family: 'Press Start 2P', monospace; + font-size: 9px; + outline: none; + width: 100%; + transition: border-color 0.1s; +} +.mc-input::placeholder { color: var(--text-faint); } +.mc-input:focus { + border-top-color: #0a0804; + border-left-color: #0a0804; + border-bottom-color: var(--accent); + border-right-color: var(--accent); +} +.mc-input-code { + width: 120px; + text-align: center; + text-transform: uppercase; + letter-spacing: 4px; + font-size: 11px; +} + +/* HOME SCREEN */ + +#home-screen { + position: fixed; + inset: 0; + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: var(--bg) url('../assets/fond_soulgate.png') center / cover no-repeat; +} + +/* Top-right gear */ +.home-top-bar { + position: absolute; + top: 16px; + right: 16px; +} + +.icon-btn { + background: var(--btn-hover); + border-top: 2px solid var(--border-hi); + border-left: 2px solid var(--border-hi); + border-bottom: 2px solid var(--border-lo); + border-right: 2px solid var(--border-lo); + color: var(--accent); + font-size: 18px; + width: 40px; + height: 40px; + cursor: pointer; + font-family: inherit; + line-height: 1; + display: flex; + align-items: center; + justify-content: center; +} + +/* Bottom-left social icons */ +.home-bottom-bar { + position: absolute; + bottom: 16px; + left: 16px; + display: flex; + align-items: center; + gap: 10px; +} + +.social-link { + width: 36px; + height: 36px; + background: var(--btn-hover); + border-top: 2px solid var(--border-hi); + border-left: 2px solid var(--border-hi); + border-bottom: 2px solid var(--border-lo); + border-right: 2px solid var(--border-lo); + display: flex; + align-items: center; + justify-content: center; + text-decoration: none; + cursor: pointer; + padding: 0; + flex-shrink: 0; +} +.social-link span { + font-family: 'Press Start 2P', monospace; + font-size: 11px; + color: var(--accent); + line-height: 1; +} +.social-icon { + width: 20px; + height: 20px; + display: block; + object-fit: contain; +} +.social-icon-invert { + filter: invert(1) brightness(1.1); +} + +.version-label { + font-size: 7px; + color: var(--text-faint); + letter-spacing: 1px; + margin-left: 8px; + line-height: 1; +} + +/* Center panel */ +.home-center { + display: flex; + flex-direction: column; + align-items: center; + width: 360px; +} + +.home-panel { + width: 100%; + background: rgba(26, 18, 8, 0.92); + border-top: 3px solid var(--border-hi); + border-left: 3px solid var(--border-hi); + border-bottom: 3px solid var(--border-lo); + border-right: 3px solid var(--border-lo); + padding: 32px 28px 24px; + display: flex; + flex-direction: column; + align-items: center; + gap: 0; +} + +/* Title */ +.home-title { + font-family: 'Press Start 2P', monospace; + font-size: 28px; + color: var(--accent); + letter-spacing: 4px; + text-align: center; + margin-bottom: 8px; + text-shadow: 2px 2px 0 #3a2000; +} + +.home-subtitle { + font-family: 'Press Start 2P', monospace; + font-size: 7px; + color: var(--accent-dim); + letter-spacing: 3px; + text-align: center; + margin-bottom: 28px; + opacity: 0.7; +} + +/* Divider */ +.home-divider { + width: 100%; + height: 1px; + background: linear-gradient(to right, transparent, var(--border-hi), transparent); + margin-bottom: 20px; +} + +.home-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + width: 100%; + margin-bottom: 8px; +} + +#home-username { + flex-direction: row; + gap: 6px; + margin-bottom: 16px; +} + +#join-row { + flex-direction: row; + gap: 6px; +} + +/* Status */ +.home-status { + font-size: 7px; + color: var(--text-faint); + letter-spacing: 1px; + margin-top: 14px; + text-align: center; +} +.home-status.ok { color: #6a9a50; } +.home-status.err { color: var(--red-hi); } + +/* LOBBY SCREEN */ + +#lobby-screen { + position: fixed; + inset: 0; + z-index: 10; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg) url('../assets/fond_soulgate.png') center / cover no-repeat; +} + +#loading-screen { + position: fixed; + inset: 0; + z-index: 20; + background: var(--bg) url('../assets/fond_soulgate.png') center / cover no-repeat; + display: flex; + align-items: center; + justify-content: center; +} +.loading-text { + font-family: 'Press Start 2P', monospace; + font-size: 18px; + color: var(--accent); + letter-spacing: 4px; + background: rgba(13, 10, 7, 0.85); + padding: 18px 32px; + border-top: 3px solid var(--border-hi); + border-left: 3px solid var(--border-hi); + border-bottom: 3px solid var(--border-lo); + border-right: 3px solid var(--border-lo); + animation: loading-pulse 1.4s ease-in-out infinite; +} +@keyframes loading-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +.lobby-panel { + background: rgba(26, 18, 8, 0.94); + border-top: 3px solid var(--border-hi); + border-left: 3px solid var(--border-hi); + border-bottom: 3px solid var(--border-lo); + border-right: 3px solid var(--border-lo); + padding: 24px 28px 20px; + width: 440px; + color: var(--text); +} + +.lobby-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; + padding-bottom: 12px; + border-bottom: 1px solid var(--border-mid); +} + +.lobby-code { + font-size: 8px; + color: var(--accent-dim); + letter-spacing: 1px; +} +.lobby-code strong { + color: var(--accent); + letter-spacing: 5px; + font-size: 11px; +} + +.lobby-section-title { + font-size: 8px; + color: var(--accent-dim); + letter-spacing: 2px; + margin-bottom: 10px; + text-align: center; +} + +/* Class cards */ +.class-btns { + display: flex; + gap: 6px; + margin-bottom: 14px; +} + +.class-btn { + flex: 1; + background: var(--panel); + border-top: 2px solid var(--border-hi); + border-left: 2px solid var(--border-hi); + border-bottom: 2px solid var(--border-lo); + border-right: 2px solid var(--border-lo); + padding: 12px 6px; + text-align: center; + cursor: pointer; + display: flex; + flex-direction: column; + gap: 5px; + font-family: 'Press Start 2P', monospace; + transition: background 0.05s; +} +.class-btn:hover:not(:disabled) { + background: var(--btn-hover); + border-top-color: #9a7c3a; + border-left-color: #9a7c3a; +} + +.class-name { font-size: 9px; color: var(--text); } +.class-role { font-size: 6px; color: var(--text-dim); } + +.class-btn.active[data-class="kael"] { + background: rgba(80, 20, 10, 0.8); + border-top-color: #cc4433; + border-left-color: #cc4433; +} +.class-btn.active[data-class="kael"] .class-name { color: #ff7766; } +.class-btn.active[data-class="kael"] .class-role { color: #883322; } + +.class-btn.active[data-class="seris"] { + background: rgba(10, 20, 60, 0.8); + border-top-color: #4477cc; + border-left-color: #4477cc; +} +.class-btn.active[data-class="seris"] .class-name { color: #7799ff; } +.class-btn.active[data-class="seris"] .class-role { color: #334488; } + +.class-btn.active[data-class="aldric"] { + background: rgba(10, 40, 15, 0.8); + border-top-color: #44aa66; + border-left-color: #44aa66; +} +.class-btn.active[data-class="aldric"] .class-name { color: #77cc99; } +.class-btn.active[data-class="aldric"] .class-role { color: #336644; } + +/* Players list */ +.players-lobby { + font-size: 8px; + color: var(--text-dim); + min-height: 18px; + line-height: 2.2; + letter-spacing: 0.5px; + margin-bottom: 12px; + text-align: center; + padding: 8px 0; + border-top: 1px solid var(--border-mid); + border-bottom: 1px solid var(--border-mid); +} + +.lobby-actions { + display: flex; + gap: 6px; + margin-top: 12px; +} + +.msg { + margin-top: 8px; + font-size: 7px; + color: var(--accent-dim); + min-height: 12px; + letter-spacing: 0.5px; + text-align: center; +} +.msg.err { color: var(--red-hi); } + +/* IN-GAME: Barre de vie boss */ + +#boss-bar { + position: fixed; + bottom: 40px; + left: 50%; + transform: translateX(-50%); + z-index: 15; + min-width: 320px; +} +.boss-bar-box { + background: rgba(10, 5, 20, 0.88); + border: 1px solid #6a22aa; + padding: 8px 16px 10px; + font-family: 'Courier New', monospace; + text-align: center; +} +.boss-bar-name { font-size: 11px; letter-spacing: 3px; color: #bb88ff; margin-bottom: 6px; } +.boss-bar-track { background: #1a0a2a; height: 10px; width: 100%; margin-bottom: 4px; border: 1px solid #3a1a5a; } +.boss-bar-fill { height: 100%; width: 100%; background: linear-gradient(to right, #7722cc, #aa44ff); transition: width 0.1s linear; } +.boss-bar-hp { font-size: 10px; color: #8855bb; } + +/* IN-GAME: Panneau d'upgrade */ + +#upgrade-panel { + position: fixed; + inset: 0; + z-index: 15; + display: flex; + align-items: center; + justify-content: center; + background: rgba(8, 5, 2, 0.78); /* backdrop : intercepte les clics, focus sur le menu */ +} +.upgrade-box { + background: #12121e; + border: 1px solid #4a2a8a; + padding: 20px 28px; + min-width: 280px; + color: #c8c8d8; + font-size: 13px; + font-family: 'Courier New', monospace; +} +.upgrade-title { font-size: 14px; color: #9977cc; letter-spacing: 2px; margin-bottom: 10px; text-align: center; } +.upgrade-souls { text-align: center; margin-bottom: 14px; font-size: 12px; color: #aaa; } +.upgrade-souls strong { color: #f0c040; } +.upgrade-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; } +.upgrade-info { flex: 1; } +.upgrade-name { font-size: 12px; color: #ddd; } +.upgrade-desc { font-size: 10px; color: #666; margin-top: 2px; } +.upgrade-stacks { font-size: 10px; color: #7755aa; white-space: nowrap; } +.upgrade-buy { padding: 4px 10px; font-size: 11px; white-space: nowrap; color: #f0c040; border-color: #8a6a10; background: #0a0a18; border: 1px solid #8a6a10; cursor: pointer; font-family: 'Courier New', monospace; } +.upgrade-buy:disabled { color: #555; border-color: #333; cursor: not-allowed; } + +/* IN-GAME: Bouton plein ecran */ + +.fullscreen-btn { + position: fixed; + top: 8px; + right: 8px; + z-index: 20; + padding: 4px 8px; + font-size: 18px; + line-height: 1; + opacity: 0.35; + border: 1px solid #333; + background: transparent; + color: #aaa; + cursor: pointer; + font-family: inherit; +} +.fullscreen-btn:hover { opacity: 0.85; } + +/* HUD joueur (bas-gauche) */ + +#player-hud { + position: fixed; + bottom: 16px; + left: 16px; + z-index: 15; + background: rgba(8, 8, 20, 0.82); + border: 1px solid #2a2a4a; + padding: 10px 12px; + font-family: 'Courier New', monospace; + font-size: 11px; + color: #c8c8d8; + min-width: 220px; +} +#hud-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 7px; } +#hud-class-name { font-size: 12px; letter-spacing: 2px; color: #9988ff; } +#hud-souls { color: #f0c040; font-size: 10px; } + +#hud-hp-wrap { position: relative; height: 14px; background: #1a0808; border: 1px solid #3a1a1a; margin-bottom: 8px; overflow: hidden; } +#hud-hp-fill { position: absolute; inset: 0; background: linear-gradient(to right, #882222, #cc3333); transition: width 0.1s linear; width: 100%; } +#hud-hp-text { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 10px; color: #eee; z-index: 1; } + +#hud-skills { display: flex; gap: 5px; margin-bottom: 6px; } +.hud-skill { position: relative; width: 36px; height: 36px; background: #0e0e1e; border: 1px solid #333; overflow: hidden; flex-shrink: 0; } +.hud-skill .cd-fill { position: absolute; bottom: 0; left: 0; width: 100%; height: 0%; background: rgba(100, 60, 220, 0.45); transition: height 0.1s linear; } +.hud-skill .key { position: absolute; bottom: 2px; left: 0; right: 0; text-align: center; font-size: 10px; color: #888; z-index: 1; } +.hud-skill.ready { border-color: #8866ff; } +.hud-skill.locked { opacity: 0.5; } +.hud-skill.divine-ready { border-color: #f0c040; box-shadow: 0 0 6px rgba(240, 192, 64, 0.5); } +#hud-divine-uses { position: absolute; top: 2px; right: 3px; font-size: 8px; color: #f0c040; z-index: 1; letter-spacing: -1px; } + +#hud-buffs { display: flex; flex-wrap: wrap; gap: 4px; min-height: 4px; } +.buff-tag { font-size: 9px; padding: 1px 5px; border: 1px solid; font-family: 'Courier New', monospace; } +.buff-tag.invulnerable { color: #f0c040; border-color: #8a6a10; background: rgba(240,192,64,0.12); } +.buff-tag.casting { color: #44ffff; border-color: #228888; background: rgba(68,255,255,0.10); } +.buff-tag.flying { color: #88ccff; border-color: #336688; background: rgba(136,204,255,0.10); } +.buff-tag.intangible { color: #cc88ff; border-color: #553388; background: rgba(204,136,255,0.10); } +.buff-tag.combat_buff { color: #ff8844; border-color: #883322; background: rgba(255,136,68,0.10); } +.buff-tag.damage_mult { color: #ff4444; border-color: #882222; background: rgba(255,68,68,0.10); } + +/* IN-GAME: Barre Soulgate (haut-centre) */ + +#soulgate-hud { + position: fixed; top: 14px; left: 50%; transform: translateX(-50%); z-index: 15; + display: flex; align-items: center; gap: 8px; + background: rgba(8, 8, 20, 0.82); border: 1px solid #2a2a4a; padding: 6px 12px; + font-family: 'Courier New', monospace; font-size: 10px; color: #c8c8d8; +} +.sg-label { letter-spacing: 2px; color: #9977cc; font-size: 10px; white-space: nowrap; } +#sg-track { width: 180px; height: 8px; background: #1a0a2a; border: 1px solid #2a1a4a; overflow: hidden; } +#sg-fill { height: 100%; width: 100%; background: linear-gradient(to right, #5522aa, #9944ff); transition: width 0.1s linear; } +#sg-hp-text { white-space: nowrap; color: #8855bb; font-size: 10px; } + +/* IN-GAME: Infos vague (haut-droite) */ + +#wave-hud { + position: fixed; top: 14px; right: 16px; z-index: 15; + background: rgba(8, 8, 20, 0.82); border: 1px solid #2a2a4a; padding: 8px 12px; + font-family: 'Courier New', monospace; text-align: right; +} +#wave-label { font-size: 11px; color: #9977cc; letter-spacing: 1px; margin-bottom: 3px; } +#wave-state-label { font-size: 10px; color: #c8c8d8; margin-bottom: 2px; } +#wave-enemies { font-size: 10px; color: #f0c040; } + +/* END GAME OVERLAY */ + +#endgame-overlay { + position: fixed; inset: 0; + background: rgba(8, 5, 2, 0.88); + display: flex; align-items: center; justify-content: center; + z-index: 100; +} + +#endgame-box { + background: rgba(26, 18, 8, 0.97); + border-top: 3px solid var(--border-hi); + border-left: 3px solid var(--border-hi); + border-bottom: 3px solid var(--border-lo); + border-right: 3px solid var(--border-lo); + padding: 32px 40px; + text-align: center; + font-family: 'Press Start 2P', monospace; + max-height: 90vh; + overflow-y: auto; + max-width: 520px; + width: 92vw; +} + +#endgame-title { + font-size: 28px; + letter-spacing: 4px; + margin-bottom: 10px; +} +#endgame-title.victory { color: var(--accent); text-shadow: 2px 2px 0 #3a2000; } +#endgame-title.defeat { color: #cc3322; text-shadow: 2px 2px 0 #2a0808; } + +#endgame-sub { + font-size: 8px; + color: var(--text-dim); + margin-bottom: 12px; +} + +.endgame-stat { + font-size: 9px; + color: var(--text); + margin-bottom: 5px; +} + +/* Leaderboard form inside endgame */ +#endgame-form { margin-top: 16px; text-align: left; } + +.lb-host-only { + font-family: 'Press Start 2P', monospace; + font-size: 8px; + line-height: 1.6; + color: var(--accent); + text-align: center; + padding: 14px 8px; + background: var(--panel); + border-top: 2px solid var(--border-hi); + border-left: 2px solid var(--border-hi); + border-bottom: 2px solid var(--border-lo); + border-right: 2px solid var(--border-lo); + margin-bottom: 8px; +} + +.lb-field { margin-bottom: 10px; } +.lb-field label { + display: block; + font-size: 7px; + color: var(--accent-dim); + margin-bottom: 4px; + letter-spacing: 0.5px; +} +.lb-field input { + width: 100%; + background: var(--input-bg); + border-top: 2px solid #1a1208; + border-left: 2px solid #1a1208; + border-bottom: 2px solid var(--border-hi); + border-right: 2px solid var(--border-hi); + color: var(--text); + padding: 6px 8px; + font-family: 'Press Start 2P', monospace; + font-size: 8px; + outline: none; +} +.lb-field input.taken { + border-bottom-color: var(--red-hi); + border-right-color: var(--red-hi); +} +.lb-discord-error { + display: block; + font-size: 7px; + margin-top: 3px; + min-height: 11px; + color: #6a9a50; +} +.lb-discord-error.error { color: var(--red-hi); } +#lb-submit-btn { width: 100%; margin-top: 12px; } + +/* LEADERBOARD SCREEN */ + +#leaderboard-screen { + position: fixed; inset: 0; z-index: 50; + background: var(--bg) url('../assets/fond_soulgate.png') center / cover no-repeat; + display: flex; align-items: center; justify-content: center; +} + +.lb-panel { + width: min(860px, 95vw); + max-height: 88vh; + display: flex; + flex-direction: column; + background: rgba(26, 18, 8, 0.97); + border-top: 3px solid var(--border-hi); + border-left: 3px solid var(--border-hi); + border-bottom: 3px solid var(--border-lo); + border-right: 3px solid var(--border-lo); + padding: 24px 28px; +} + +.lb-header { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 18px; + padding-bottom: 14px; + border-bottom: 1px solid var(--border-mid); +} + +.lb-title { + font-family: 'Press Start 2P', monospace; + font-size: 14px; + color: var(--accent); + letter-spacing: 4px; + flex: 1; + text-align: center; + text-shadow: 2px 2px 0 #3a2000; +} + +.lb-table-wrap { overflow-y: auto; flex: 1; } + +.lb-table { + width: 100%; + border-collapse: collapse; + font-family: 'Press Start 2P', monospace; + font-size: 8px; +} +.lb-table thead { + position: sticky; + top: 0; + background: var(--panel); +} +.lb-table th { + padding: 10px 12px; + color: var(--accent-dim); + border-bottom: 1px solid var(--border-mid); + text-align: left; + font-size: 7px; + letter-spacing: 1px; +} +.lb-table td { + padding: 10px 12px; + border-bottom: 1px solid var(--border-mid); + color: var(--text); + font-size: 8px; +} +.lb-table tr:nth-child(even) td { background: rgba(58, 40, 16, 0.18); } +.lb-table tr:hover td { background: rgba(212, 168, 67, 0.06); } +.lb-table tr.lb-first td { color: var(--accent); } + +.lb-rank { color: var(--accent-dim); width: 36px; } +.lb-team { color: var(--text); } +.lb-score { color: var(--accent); } +.lb-players { color: #8ab4d4; font-size: 7px; } \ No newline at end of file diff --git a/client/debug.html b/client/debug.html new file mode 100644 index 0000000..81bacfc --- /dev/null +++ b/client/debug.html @@ -0,0 +1,277 @@ + + + + + SOULGATE — Debug + + + + +
+

SOULGATE — Debug

+ Connexion... +
+ +
+ +
+

Connexion

+ +
+ + +
+
+ +
+ +
+

Lobby

+ +
+ +
+ +
+ + +
+
+ +
+ +
+

Classe

+
+ + + +
+ +
+ + +
+ +
+

Joueurs

+
En attente...
+
+ +
+

État du jeu en attente de la partie...

+ +
+
+
Tick
+
+ +
+
+
Vague
+
+
+
+ +
+
Soulgate
+
+ + +
+
+
+ +

Joueurs

+
En attente de la partie...
+
+ +
+ + + + + + \ No newline at end of file diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..adc1530 --- /dev/null +++ b/client/index.html @@ -0,0 +1,184 @@ + + + + + + SOULGATE + + + + + + + + + +
+ +
+ +
+ +
+
+

SOULGATE

+

GUARDIANS OF SOULS

+
+ +
+ + +
+ + + + + +
Connecting...
+
+
+ +
+ + + by Ylies Amara +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/js/bindings.js b/client/js/bindings.js new file mode 100644 index 0000000..74db5af --- /dev/null +++ b/client/js/bindings.js @@ -0,0 +1,88 @@ +// bindings.js : tous les listeners DOM du menu et du lobby + +import { send } from './network.js'; + + +export function bindEvents(callbacks) { + const $ = id => document.getElementById(id); + + // pseudo + $('username-btn').addEventListener('click', () => { + const v = $('username-input').value.trim(); + if (v) { + callbacks.setUsername(v); + send('set_username', { username: v }); + } + }); + + $('username-input').addEventListener('keydown', e => { + if (e.key === 'Enter') $('username-btn').click(); + }); + + // creer une partie + $('create-game-btn').addEventListener('click', () => send('create_lobby')); + + // rejoindre une partie + $('join-game-btn').addEventListener('click', () => { + $('home-buttons').classList.add('hidden'); + $('join-row').classList.remove('hidden'); + $('code-input').focus(); + }); + + $('join-confirm-btn').addEventListener('click', () => { + const code = $('code-input').value.trim().toUpperCase(); + if (code) send('join_lobby', { code }); + }); + + $('code-input').addEventListener('keydown', e => { + if (e.key === 'Enter') $('join-confirm-btn').click(); + }); + + $('join-cancel-btn').addEventListener('click', () => { + $('join-row').classList.add('hidden'); + $('home-buttons').classList.remove('hidden'); + }); + + // retour au menu + $('lobby-back-btn').addEventListener('click', () => { + send('leave_lobby'); + callbacks.switchToHome(); + }); + + // selection de classe + document.querySelectorAll('.class-btn').forEach(btn => { + btn.addEventListener('click', () => { + document.querySelectorAll('.class-btn').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + send('select_class', { class: btn.dataset.class }); + $('ready-btn').disabled = false; + }); + }); + + // pret + $('ready-btn').addEventListener('click', () => { + send('ready'); + $('ready-btn').disabled = true; + }); + + // demarrer (host) + $('start-btn').addEventListener('click', () => send('start_game')); + + // plein ecran + $('fullscreen-btn')?.addEventListener('click', () => { + if (!document.fullscreenElement) { + document.documentElement.requestFullscreen(); + } else { + document.exitFullscreen(); + } + }); + + // settings (todo) + $('settings-btn').addEventListener('click', () => { + callbacks.notify('Settings coming soon'); + }); + + // leaderboard + $('leaderboard-btn').addEventListener('click', () => callbacks.showLeaderboard?.()); + $('lb-back-btn').addEventListener('click', () => callbacks.hideLeaderboard?.()); +} diff --git a/client/js/constants.js b/client/js/constants.js new file mode 100644 index 0000000..6bab136 --- /dev/null +++ b/client/js/constants.js @@ -0,0 +1,75 @@ +// constants.js : miroir cote client de server/constants.py +// si tu modifies une valeur ici, modifier aussi dans constants.py + +export const ARENA_WIDTH = 35; +export const ARENA_HEIGHT = 35; + +export const SCALE = 16; + +// rendu iso : ratio 2:1 classique +export const TILE_WIDTH = 64; +export const TILE_HEIGHT = 32; + +export const SOULGATE_X = 0; +export const SOULGATE_Y = -15; +export const PLAYER_SPAWN_X = 0; +export const PLAYER_SPAWN_Y = -10; + +export const TICK_RATE = 20; +export const TICK_DURATION = 0.05; + +export const SOULGATE_MAX_HP = 100; + +export const CLASS_COLORS = { + kael: 0xe06060, + seris: 0x6080e0, + aldric: 0x60c080, +}; + +export const PREPARATION_DURATION = 20; + +export const UPGRADE_CATALOG = { + damage_up: { name: "Frappe +", desc: "+25% dégâts projectile", cost: 30, max: 2 }, + cooldown_down: { name: "Cadence +", desc: "-20% cooldown d'attaque", cost: 25, max: 2 }, + hp_up: { name: "Vitalité +", desc: "+30 PV max", cost: 35, max: 2 }, + speed_up: { name: "Vitesse +", desc: "+15% vitesse de déplacement", cost: 20, max: 3 }, +}; + +// cd des skills 1/2 par classe +export const ABILITY_COOLDOWNS = { + kael: [7.0, 12.0], + seris: [6.0, 10.0], + aldric: [8.0, 15.0], +}; + +export const DISPLACEMENT_COOLDOWNS = { kael: 9.0, seris: 4.0, aldric: 14.0 }; + +// hitboxes (miroir constants.py, sert au debug visuel) +export const PLAYER_HITBOX_RADIUS = 0.5; +export const ENEMY_HITBOX_RADIUS = 0.5; +export const PROJECTILE_HIT_RADIUS = 0.4; +export const SOULGATE_HITBOX_RADIUS = 1.0; + +// rayons d'attaque Kael +export const KAEL_MELEE_RADIUS = 3.0; +export const KAEL_SLAM_RADIUS = 3.5; +export const KAEL_SLAM_TICKS = 8; +export const KAEL_STORM_RADIUS = 5.5; +export const KAEL_STORM_TICKS = 20; + +// Seris +export const SERIS_VOID_RADIUS = 4.0; +export const SERIS_VOID_TICKS = 10; + +// Aldric +export const ALDRIC_HEAL_RADIUS = 6.0; +export const ALDRIC_PULSE_RADIUS = 5.0; +export const ALDRIC_PULSE_TICKS = 8; + +export const DIVINE_POST_USE_COOLDOWN = 60.0; + +// score +export const SCORE_PER_KILL = 10; +export const SCORE_PER_SOUL = 2; +export const SCORE_VICTORY = 1000; +export const SCORE_PER_WAVE = 200; diff --git a/client/js/debug.js b/client/js/debug.js new file mode 100644 index 0000000..5c1361d --- /dev/null +++ b/client/js/debug.js @@ -0,0 +1,273 @@ +// Debug client — gestion WebSocket, lobby et affichage live du game state + +const WS_URL = `ws://${location.hostname}:8000/ws`; + +let ws = null; +let isHost = false; +let inLobby = false; +let gameStarted = false; +let myConnId = null; // identifié via lobby_created / lobby_joined +let lastTickTime = null; +let lastTick = null; + + +// Connexion + +function connect() { + ws = new WebSocket(WS_URL); + ws.onopen = () => setStatus("Connecté", "ok"); + ws.onclose = () => { + setStatus("Déconnecté", "error"); + gameStarted = false; + inLobby = false; + }; + ws.onerror = () => setStatus("Erreur WebSocket", "error"); + ws.onmessage = (e) => { + try { + handleMessage(JSON.parse(e.data)); + } catch { + log("⚠ message non-JSON reçu"); + } + }; +} + +function send(type, data = {}) { + if (ws?.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type, ...data })); + } +} + + +// Dispatch des messages + +function handleMessage(msg) { + if (msg.type === "game_state") { + updateGameState(msg); + return; // pas de log à 20 Hz + } + if (msg.type === "error") { + log(`⚠ ${msg.code}: ${msg.message}`, "err"); + return; + } + log(`← ${msg.type}: ${JSON.stringify(msg).slice(0, 100)}`); + + switch (msg.type) { + case "username_set": onUsernameSet(msg); break; + case "lobby_created": onLobbyCreated(msg); break; + case "lobby_joined": onLobbyJoined(msg); break; + case "player_joined": onPlayerJoined(msg); break; + case "player_left": onPlayerLeft(msg); break; + case "class_selected": onClassSelected(msg); break; + case "player_ready": onPlayerReady(msg); break; + case "game_starting": onGameStarting(msg); break; + } +} + + +// Handlers lobby + +function onUsernameSet(msg) { + $("create-btn").disabled = false; + $("join-btn").disabled = false; + log(`Pseudo défini : ${msg.username}`, "ok"); +} + +function onLobbyCreated(msg) { + isHost = true; + inLobby = true; + $("code-input").value = msg.code; + $("class-kael").disabled = false; + $("class-seris").disabled = false; + $("class-aldric").disabled = false; + $("start-btn").disabled = false; + log(`Lobby créé — code : ${msg.code}`, "ok"); + renderLobbyPlayers([{ username: $("username-input").value, player_class: null, ready: false, host: true }]); +} + +function onLobbyJoined(msg) { + inLobby = true; + $("code-input").value = msg.code; + $("class-kael").disabled = false; + $("class-seris").disabled = false; + $("class-aldric").disabled = false; + renderLobbyPlayers(msg.players); +} + +function onPlayerJoined(msg) { + refreshLobbyFromEvent("player_joined", msg.player); +} + +function onPlayerLeft(msg) { + refreshLobbyFromEvent("player_left", { id: msg.player_id }); +} + +function onClassSelected(msg) { + // met à jour le bouton de classe si c'est nous + if (msg.player_id === myConnId || !myConnId) { + ["kael", "seris", "aldric"].forEach(c => { + $(`class-${c}`).classList.toggle("active", c === msg.class); + }); + } +} + +function onPlayerReady(msg) { + if (msg.player_id === myConnId || !myConnId) { + $("ready-btn").classList.add("active"); + $("ready-btn").textContent = "✓ Prêt"; + } +} + +function onGameStarting(msg) { + gameStarted = true; + log(`▶ Partie démarrée ! (countdown: ${msg.countdown}s)`, "ok"); + $("streaming").textContent = "● streaming 20 Hz"; + $("streaming").style.color = "#4caf50"; +} + + +// Game state + +function updateGameState(msg) { + // Tick + estimation du tick rate + const now = performance.now(); + if (lastTick !== null && lastTickTime !== null) { + const dt = now - lastTickTime; + const rate = (1000 / dt).toFixed(0); + $("tick-rate").textContent = `~${rate}/s`; + } + lastTick = msg.tick; + lastTickTime = now; + $("tick").textContent = msg.tick; + + // Soulgate + const sg = msg.soulgate; + const sgPct = Math.round(sg.hp / sg.max_hp * 100); + $("sg-hp").textContent = `${sg.hp} / ${sg.max_hp}`; + $("sg-pct").textContent = `${sgPct}%`; + $("sg-bar").style.width = `${sgPct}%`; + + // Vague + const w = msg.wave; + const badge = $("wave-info"); + badge.innerHTML = `Vague ${w.number} — ${w.state}${w.enemies_remaining > 0 ? ` (${w.enemies_remaining} ennemis)` : ""}`; + + // Joueurs + const container = $("players-list"); + container.innerHTML = msg.players.map(p => { + const pct = p.alive ? Math.round(p.hp / p.max_hp * 100) : 0; + return ` +
+
+ ${p.username} + ${p.class} +
+
+ ${p.hp} / ${p.max_hp} HP + ${p.alive ? pct + "%" : "mort"} +
+
+
+
+
`; + }).join(""); +} + + +// Helpers UI + +function setStatus(text, cls = "") { + const el = $("status"); + el.textContent = text; + el.className = "status " + cls; +} + +function log(text, cls = "") { + const pre = $("log"); + const ts = new Date().toLocaleTimeString("fr"); + const line = document.createElement("span"); + line.className = cls; + line.textContent = `[${ts}] ${text}\n`; + pre.appendChild(line); + pre.scrollTop = pre.scrollHeight; +} + +function $(id) { return document.getElementById(id); } + +let _lobbyPlayers = []; + +function renderLobbyPlayers(players) { + _lobbyPlayers = players; + const container = $("lobby-players"); + if (!players.length) { + container.innerHTML = `Aucun joueur`; + return; + } + container.innerHTML = players.map(p => ` +
+ ${p.username ?? p.id ?? "?"} + ${p.class ?? p.player_class ?? "—"} + ${p.ready ? `` : ""} + ${p.host ? `[chef]` : ""} +
+ `).join(""); +} + +function refreshLobbyFromEvent(eventType, data) { + // re-render best-effort sans refetch complet + if (eventType === "player_joined") { + _lobbyPlayers.push(data); + } else if (eventType === "player_left") { + _lobbyPlayers = _lobbyPlayers.filter(p => p.id !== data.id); + } + renderLobbyPlayers(_lobbyPlayers); +} + + +// Event listeners + +$("username-btn").addEventListener("click", () => { + const v = $("username-input").value.trim(); + if (v) send("set_username", { username: v }); +}); + +$("username-input").addEventListener("keydown", e => { + if (e.key === "Enter") $("username-btn").click(); +}); + +$("create-btn").addEventListener("click", () => send("create_lobby")); + +$("join-btn").addEventListener("click", () => { + const code = $("code-input").value.trim().toUpperCase(); + if (code) send("join_lobby", { code }); +}); + +$("code-input").addEventListener("keydown", e => { + if (e.key === "Enter") $("join-btn").click(); +}); + +["kael", "seris", "aldric"].forEach(cls => { + $(`class-${cls}`).addEventListener("click", () => { + send("select_class", { class: cls }); + }); +}); + +$("ready-btn").addEventListener("click", () => { + $("ready-btn").disabled = true; + send("ready"); +}); + +$("start-btn").addEventListener("click", () => send("start_game")); + +// Activer Ready dès qu'une classe est sélectionnée +["kael", "seris", "aldric"].forEach(cls => { + $(`class-${cls}`).addEventListener("click", () => { + $("ready-btn").disabled = false; + $("ready-btn").classList.remove("active"); + $("ready-btn").textContent = "Prêt"; + }); +}); + + +// Init + +connect(); diff --git a/client/js/hud.js b/client/js/hud.js new file mode 100644 index 0000000..dba19ec --- /dev/null +++ b/client/js/hud.js @@ -0,0 +1,218 @@ +// hud.js : maj des elements html du hud (hp, cd, vagues, upgrades, end game) + +import { UPGRADE_CATALOG, ABILITY_COOLDOWNS, DISPLACEMENT_COOLDOWNS, SCORE_PER_KILL, SCORE_PER_SOUL, SCORE_VICTORY, SCORE_PER_WAVE } from './constants.js'; +import { showEndGameForm } from './leaderboard.js'; +import { send } from './network.js'; +import { setInputBlocked } from './input.js'; + + +const BUFF_LABELS = { + invulnerable: 'Invulnerable', + casting: 'Incantation', + flying: 'Vol', + intangible: 'Intangible', + combat_buff: 'Buff combat', + damage_mult: 'Degats x3', +}; + +const WAVE_STATES = { + combat: 'Combat', + preparation: 'Preparation', + boss: 'Boss', + victory: 'Victoire', +}; + + +export function updatePlayerHud(local) { + document.getElementById('hud-class-name').textContent = (local.class ?? '').toUpperCase(); + document.getElementById('hud-souls').textContent = 'Ames : ' + (local.souls ?? 0); + + const hpPct = local.max_hp > 0 ? Math.max(0, local.hp / local.max_hp * 100) : 0; + document.getElementById('hud-hp-fill').style.width = hpPct + '%'; + document.getElementById('hud-hp-text').textContent = local.hp + ' / ' + local.max_hp; + + _updateSkillCooldowns(local); + _updateBuffTags(local); +} + + +function _updateSkillCooldowns(local) { + const cds = local.cooldowns ?? {}; + const abMaxes = ABILITY_COOLDOWNS[local.class] ?? [8, 12]; + + [['hud-s1', 0], ['hud-s2', 1]].forEach(([id, i]) => { + const el = document.getElementById(id); + const cd = cds['ability_' + (i + 1)] ?? 0; + const maxCd = abMaxes[i]; + const fill = cd <= 0 ? 100 : (1 - cd / maxCd) * 100; + el.querySelector('.cd-fill').style.height = fill + '%'; + el.classList.toggle('ready', cd <= 0); + el.classList.remove('locked'); + }); + + const elE = document.getElementById('hud-se'); + const cdE = cds['displacement'] ?? 0; + const maxCdE = DISPLACEMENT_COOLDOWNS[local.class] ?? 6; + const fillE = cdE <= 0 ? 100 : (1 - cdE / maxCdE) * 100; + elE.querySelector('.cd-fill').style.height = fillE + '%'; + elE.classList.toggle('ready', cdE <= 0); + elE.classList.remove('locked', 'divine-ready'); +} + + +function _updateBuffTags(local) { + const buffsEl = document.getElementById('hud-buffs'); + buffsEl.innerHTML = ''; + + for (const b of (local.buffs ?? [])) { + const label = BUFF_LABELS[b.type]; + if (!label) continue; + + const tag = document.createElement('span'); + tag.className = 'buff-tag ' + b.type; + tag.textContent = label; + buffsEl.appendChild(tag); + } +} + + +export function updateSoulgateBar(sg) { + if (!sg) return; + const pct = sg.max_hp > 0 ? Math.max(0, sg.hp / sg.max_hp * 100) : 0; + document.getElementById('sg-fill').style.width = pct + '%'; + document.getElementById('sg-hp-text').textContent = sg.hp + ' / ' + sg.max_hp; +} + + +export function updateWaveInfo(wave) { + if (!wave) return; + document.getElementById('wave-label').textContent = 'Vague ' + wave.number + ' / 3'; + document.getElementById('wave-state-label').textContent = WAVE_STATES[wave.state] ?? wave.state; + document.getElementById('wave-enemies').textContent = 'Ennemis : ' + (wave.enemies_remaining ?? 0); +} + + +export function updateBossBar(wave) { + const isBoss = wave.state === 'boss' && wave.boss_max_hp > 0; + document.getElementById('boss-bar').classList.toggle('hidden', !isBoss); + + if (isBoss) { + document.getElementById('boss-bar-name').textContent = wave.boss_name.toUpperCase(); + const pct = Math.max(0, wave.boss_hp / wave.boss_max_hp * 100); + document.getElementById('boss-bar-fill').style.width = pct + '%'; + document.getElementById('boss-bar-hp').textContent = wave.boss_hp + ' / ' + wave.boss_max_hp; + } +} + + +export function updateUpgradePanel(wave, local) { + const inPrep = wave.state === 'preparation'; + document.getElementById('upgrade-panel').classList.toggle('hidden', !inPrep); + // pendant la prep on bloque les inputs pour focus sur le menu + setInputBlocked(inPrep || gameOver); + + if (inPrep) { + document.getElementById('prep-timer').textContent = Math.ceil(wave.prep_timer); + const souls = local?.souls ?? 0; + document.getElementById('soul-count').textContent = souls; + _updateUpgradeList(souls, local?.upgrades ?? {}); + } +} + + +let _upgradeListBuilt = false; +let _upgradeClickBound = false; + +function _updateUpgradeList(souls, upgrades) { + // on construit la liste 1 seule fois et on met juste a jour les stacks/disabled apres + // sinon on detruit les boutons pendant qu'on clique dessus + const list = document.getElementById('upgrade-list'); + + if (!_upgradeListBuilt) { + list.innerHTML = ''; + for (const [id, spec] of Object.entries(UPGRADE_CATALOG)) { + const item = document.createElement('div'); + item.className = 'upgrade-item'; + item.dataset.id = id; + item.innerHTML = ` +
+
${spec.name}
+
${spec.desc}
+
+
+ `; + list.appendChild(item); + } + _upgradeListBuilt = true; + } + + if (!_upgradeClickBound) { + // Délégation : 1 seul listener sur la liste, ne meurt jamais avec les boutons + list.addEventListener('click', (e) => { + const btn = e.target.closest('.upgrade-buy'); + if (!btn || btn.disabled) return; + send('player_upgrade', { upgrade_id: btn.dataset.id }); + }); + _upgradeClickBound = true; + } + + for (const [id, spec] of Object.entries(UPGRADE_CATALOG)) { + const item = list.querySelector(`.upgrade-item[data-id="${id}"]`); + if (!item) continue; + const stacks = upgrades[id] ?? 0; + const maxed = stacks >= spec.max; + item.querySelector('.upgrade-stacks').textContent = + '\u25cf'.repeat(stacks) + '\u25cb'.repeat(spec.max - stacks); + const btn = item.querySelector('.upgrade-buy'); + btn.disabled = maxed || souls < spec.cost; + btn.textContent = maxed ? 'MAX' : spec.cost + ' ames'; + } +} + +export function resetUpgradeList() { + _upgradeListBuilt = false; + _upgradeClickBound = false; +} + + +let gameOver = false; + +export function resetGameOver() { gameOver = false; } + +export function checkGameEnd(msg, gameStartTime, isHost = false, lobbyCode = '', submitterId = '') { + if (gameOver) return; + + const victory = msg.wave?.state === 'victory'; + const defeat = msg.soulgate?.hp === 0; + if (!victory && !defeat) return; + + gameOver = true; + setInputBlocked(true); + + const timeSecs = gameStartTime ? Math.floor((Date.now() - gameStartTime) / 1000) : 0; + const wavesCompleted = msg.wave?.number ?? 1; + const players = msg.players ?? []; + + const totalKills = players.reduce((s, p) => s + (p.enemies_killed ?? 0), 0); + const totalSouls = players.reduce((s, p) => s + (p.souls ?? 0), 0); + const score = totalKills * SCORE_PER_KILL + + totalSouls * SCORE_PER_SOUL + + (victory ? SCORE_VICTORY : 0) + + wavesCompleted * SCORE_PER_WAVE; + + showEndGameForm({ + victory, + score, + timeSecs, + wavesCompleted, + isHost, + lobbyCode, + submitterId, + players: players.map(p => ({ + username: p.username, + class: p.class, + souls: p.souls ?? 0, + enemies_killed: p.enemies_killed ?? 0, + })), + }); +} diff --git a/client/js/input.js b/client/js/input.js new file mode 100644 index 0000000..2e10b74 --- /dev/null +++ b/client/js/input.js @@ -0,0 +1,110 @@ +// input.js : capture clavier + souris, envoi au serveur + +import { send } from './network.js'; + +const moveKeys = { up: false, down: false, left: false, right: false }; + +let lastDx = 0, lastDy = 0; +let inputBlocked = false; + +export function setInputBlocked(blocked) { + if (blocked && !inputBlocked) { + // on coupe le mouvement cote serveur si on bougeait, sinon le perso continue d avancer + if (lastDx !== 0 || lastDy !== 0) { + lastDx = 0; lastDy = 0; + send('input', { dx: 0, dy: 0 }); + } + moveKeys.up = moveKeys.down = moveKeys.left = moveKeys.right = false; + } + inputBlocked = blocked; +} + +export function isInputBlocked() { return inputBlocked; } + + +const MOVE_KEY_MAP = { + 'z': 'up', 'ArrowUp': 'up', + 's': 'down', 'ArrowDown': 'down', + 'q': 'left', 'ArrowLeft': 'left', + 'd': 'right', 'ArrowRight': 'right', +}; + +// AZERTY : on utilise e.code (Digit1) plutot que e.key (qui donne '&', 'é') +const ABILITY_CODE_MAP = { 'Digit1': 1, 'Digit2': 2 }; + + +function computeDirection() { + let dx = 0, dy = 0; + if (moveKeys.left) dx -= 1; + if (moveKeys.right) dx += 1; + if (moveKeys.up) dy -= 1; + if (moveKeys.down) dy += 1; + + // normaliser sinon la diagonale est plus rapide que les axes + if (dx !== 0 && dy !== 0) { + const inv = 1 / Math.SQRT2; + dx *= inv; + dy *= inv; + } + + return { dx, dy }; +} + + +function onKeyDown(e, getTarget) { + if (inputBlocked) return; + + const moveAction = MOVE_KEY_MAP[e.key]; + if (moveAction) { + e.preventDefault(); + if (moveKeys[moveAction]) return; + moveKeys[moveAction] = true; + + const { dx, dy } = computeDirection(); + if (dx !== lastDx || dy !== lastDy) { + lastDx = dx; lastDy = dy; + send('input', { dx, dy }); + } + return; + } + + const abilityId = ABILITY_CODE_MAP[e.code]; + if (abilityId) { + e.preventDefault(); + const { wx, wy } = getTarget(); + send('ability', { id: abilityId, tx: wx, ty: wy }); + return; + } + + if (e.key === 'e') { + e.preventDefault(); + const { wx, wy } = getTarget(); + send('displacement', { tx: wx, ty: wy }); + return; + } +} + + +function onKeyUp(e) { + if (inputBlocked) return; + + const moveAction = MOVE_KEY_MAP[e.key]; + if (!moveAction) return; + + e.preventDefault(); + if (!moveKeys[moveAction]) return; + + moveKeys[moveAction] = false; + const { dx, dy } = computeDirection(); + + if (dx !== lastDx || dy !== lastDy) { + lastDx = dx; lastDy = dy; + send('input', { dx, dy }); + } +} + + +export function startInputTracking(getTarget) { + document.addEventListener('keydown', e => onKeyDown(e, getTarget)); + document.addEventListener('keyup', e => onKeyUp(e)); +} diff --git a/client/js/leaderboard.js b/client/js/leaderboard.js new file mode 100644 index 0000000..f7ea1e5 --- /dev/null +++ b/client/js/leaderboard.js @@ -0,0 +1,247 @@ +// leaderboard.js — Affichage du classement et soumission d'un résultat + +const API = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' + ? 'http://localhost:8000' + : `http://${window.location.hostname}:8000`; + +const CLASS_ICONS = { kael: '⚔️', seris: '🗡️', aldric: '🌿' }; + +// Leaderboard screen + +export async function showLeaderboard() { + document.getElementById('home-screen').classList.add('hidden'); + const screen = document.getElementById('leaderboard-screen'); + screen.classList.remove('hidden'); + await _loadAndRender(); +} + +export function hideLeaderboard() { + document.getElementById('leaderboard-screen').classList.add('hidden'); + document.getElementById('home-screen').classList.remove('hidden'); +} + +async function _loadAndRender() { + const tbody = document.getElementById('lb-tbody'); + tbody.innerHTML = 'Chargement...'; + try { + const res = await fetch(`${API}/leaderboard`); + const rows = await res.json(); + _renderTable(rows); + } catch { + tbody.innerHTML = 'Impossible de charger le classement.'; + } +} + +function _renderTable(rows) { + const tbody = document.getElementById('lb-tbody'); + if (!rows.length) { + tbody.innerHTML = 'Aucune partie enregistrée.'; + return; + } + tbody.innerHTML = rows.map((r, i) => { + const time = _formatTime(r.time_seconds); + const vic = r.victory ? '✓' : '✗'; + const discords = [ + r.p1_discord ? `@${r.p1_discord}` : (r.p1_username || ''), + r.p2_discord ? `@${r.p2_discord}` : (r.p2_username || ''), + r.p3_discord ? `@${r.p3_discord}` : (r.p3_username || ''), + ].filter(Boolean).join(' · '); + return ` + #${i + 1} + ${_esc(r.team_name)} + ${r.score.toLocaleString()} + ${time} + ${vic} ${r.waves_completed}/3 + ${_esc(discords)} + `; + }).join(''); +} + +function _formatTime(s) { + const m = Math.floor(s / 60); + const sec = String(s % 60).padStart(2, '0'); + return `${m}m${sec}s`; +} + +function _esc(s) { + return String(s ?? '').replace(/&/g, '&').replace(//g, '>'); +} + + +// End-game form + +export function showEndGameForm(data) { + // data = { victory, score, timeSecs, wavesCompleted, players: [{username, class, souls, enemies_killed}] } + const overlay = document.getElementById('endgame-overlay'); + const title = document.getElementById('endgame-title'); + const sub = document.getElementById('endgame-sub'); + + overlay.classList.remove('hidden'); + title.textContent = data.victory ? 'VICTOIRE !' : 'DÉFAITE'; + title.className = data.victory ? 'victory' : 'defeat'; + sub.textContent = data.victory + ? 'Le Soulgate est sauvegardé.' + : 'Le Soulgate a été détruit.'; + + document.getElementById('endgame-score').textContent = `Score : ${data.score.toLocaleString()} pts`; + document.getElementById('endgame-time').textContent = `Temps : ${_formatTime(data.timeSecs)}`; + document.getElementById('endgame-waves').textContent = `Vagues : ${data.wavesCompleted}/3`; + + _buildDiscordForm(data); +} + +function _buildDiscordForm(data) { + const form = document.getElementById('endgame-form'); + form.innerHTML = ''; + + // Defaite : pas de soumission au leaderboard, juste rejouer + if (!data.victory) { + const info = document.createElement('div'); + info.className = 'lb-host-only'; + info.textContent = "Défaite : le score n'est pas enregistré dans le leaderboard."; + form.appendChild(info); + + const replayBtn = document.createElement('button'); + replayBtn.className = 'mc-btn'; + replayBtn.textContent = 'Rejouer'; + replayBtn.style.marginTop = '8px'; + replayBtn.addEventListener('click', () => location.reload()); + form.appendChild(replayBtn); + return; + } + + // Seul le chef d'équipe peut soumettre le score → les autres voient un message + Rejouer + if (!data.isHost) { + const info = document.createElement('div'); + info.className = 'lb-host-only'; + info.textContent = "Seul le chef d'équipe peut enregistrer le score dans le leaderboard."; + form.appendChild(info); + + const replayBtn = document.createElement('button'); + replayBtn.className = 'mc-btn'; + replayBtn.textContent = 'Rejouer'; + replayBtn.style.marginTop = '8px'; + replayBtn.addEventListener('click', () => location.reload()); + form.appendChild(replayBtn); + return; + } + + // Champ nom d'équipe + const teamRow = document.createElement('div'); + teamRow.className = 'lb-field'; + teamRow.innerHTML = ``; + form.appendChild(teamRow); + + // Un champ Discord par joueur + data.players.forEach((p, i) => { + const icon = CLASS_ICONS[p.class] ?? ''; + const row = document.createElement('div'); + row.className = 'lb-field'; + row.innerHTML = ` + + + `; + form.appendChild(row); + }); + + // Bouton soumettre + const submitBtn = document.createElement('button'); + submitBtn.id = 'lb-submit-btn'; + submitBtn.className = 'mc-btn mc-btn-gold'; + submitBtn.textContent = 'Enregistrer dans le leaderboard'; + form.appendChild(submitBtn); + + // Bouton rejouer + const replayBtn = document.createElement('button'); + replayBtn.className = 'mc-btn'; + replayBtn.textContent = 'Rejouer'; + replayBtn.style.marginTop = '8px'; + replayBtn.addEventListener('click', () => location.reload()); + form.appendChild(replayBtn); + + // Vérification Discord à la saisie + form.querySelectorAll('.lb-discord-input').forEach(input => { + let _debounce; + input.addEventListener('input', () => { + clearTimeout(_debounce); + _debounce = setTimeout(() => _checkDiscord(input), 500); + }); + }); + + submitBtn.addEventListener('click', () => _submitForm(data, form, submitBtn)); +} + +async function _checkDiscord(input) { + const tag = input.value.trim().replace(/^@/, ''); + const errEl = input.parentElement.querySelector('.lb-discord-error'); + if (!tag) { errEl.textContent = ''; input.classList.remove('taken'); return; } + + try { + const res = await fetch(`${API}/leaderboard/check-discord/${encodeURIComponent(tag)}`); + const json = await res.json(); + if (json.taken) { + errEl.textContent = '❌ Ce Discord est déjà dans le leaderboard — choisis-en un autre.'; + input.classList.add('taken'); + } else { + errEl.textContent = '✓'; + input.classList.remove('taken'); + } + } catch { + errEl.textContent = ''; + } +} + +async function _submitForm(data, form, btn) { + // Bloquer si un Discord est déjà pris + if (form.querySelector('.lb-discord-input.taken')) { + alert('Un ou plusieurs Discord sont déjà utilisés dans le leaderboard.'); + return; + } + + const teamName = document.getElementById('lb-team-name').value.trim(); + if (!teamName) { alert("Entre un nom d'équipe."); return; } + + const discordInputs = [...form.querySelectorAll('.lb-discord-input')]; + const players = data.players; + + const payload = { + team_name: teamName, + score: data.score, + time_seconds: data.timeSecs, + waves_completed: data.wavesCompleted, + victory: data.victory, + lobby_code: data.lobbyCode ?? '', // enforcement host-only côté serveur + submitter_id: data.submitterId ?? '', + p1_username: players[0]?.username ?? '', p1_class: players[0]?.class ?? '', + p1_discord: (discordInputs[0]?.value ?? '').trim().replace(/^@/, ''), + p2_username: players[1]?.username ?? '', p2_class: players[1]?.class ?? '', + p2_discord: (discordInputs[1]?.value ?? '').trim().replace(/^@/, ''), + p3_username: players[2]?.username ?? '', p3_class: players[2]?.class ?? '', + p3_discord: (discordInputs[2]?.value ?? '').trim().replace(/^@/, ''), + }; + + btn.disabled = true; + btn.textContent = 'Envoi...'; + + try { + const res = await fetch(`${API}/leaderboard/submit`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + const json = await res.json(); + if (!res.ok) { + alert(json.detail ?? 'Erreur lors de l\'envoi.'); + btn.disabled = false; + btn.textContent = 'Enregistrer dans le leaderboard'; + return; + } + btn.textContent = `✓ Enregistré ! Vous êtes #${json.rank} au classement.`; + btn.style.background = '#2a7'; + } catch { + alert('Erreur réseau — impossible de contacter le serveur.'); + btn.disabled = false; + btn.textContent = 'Enregistrer dans le leaderboard'; + } +} diff --git a/client/js/main.js b/client/js/main.js new file mode 100644 index 0000000..a0a1b70 --- /dev/null +++ b/client/js/main.js @@ -0,0 +1,230 @@ +// main.js — point d'entree client, init Pixi et handlers reseau + +import { Application, Container } from 'pixi.js'; +import { connect, send, on } from './network.js'; +import { showLeaderboard, hideLeaderboard } from './leaderboard.js'; +import { updateIsoLayout, drawStaticArena, updatePlayers, updateProjectiles, updateEnemies, updateAoeZones, updateCamera, screenToWorld } from './renderer.js'; +import { loadArenaMap } from './renderArena.js'; +import { startInputTracking, isInputBlocked } from './input.js'; +import { updatePlayerHud, updateSoulgateBar, updateWaveInfo, updateBossBar, updateUpgradePanel, checkGameEnd } from './hud.js'; +import { bindEvents } from './bindings.js'; +import { loadKaelAssets, loadSerisAssets, loadAldricAssets } from './renderPlayers.js'; +import { loadFractureAssets, loadRampantAssets, loadColosseAssets, loadEclatAssets, loadVexarisAssets } from './renderEnemies.js'; + +let localUsername = null; +let localId = null; +let isHost = false; +let lobbyCode = ''; +let camX = 0, camY = 0; +let gameStartTime = null; + + +async function main() { + const app = new Application(); + await app.init({ + canvas: document.getElementById('game-canvas'), + resizeTo: window, + backgroundAlpha: 0, + antialias: true, + resolution: window.devicePixelRatio || 1, + autoDensity: true, + }); + + let worldContainer = null; + let layerArena = null; + let layerAoe = null; + let layerEnemies = null; + let layerEntities = null; + let layerProjectiles = null; + + const playerPool = {}; + const projPool = {}; + const enemyPool = {}; + const aoePool = {}; + + async function initGameWorld() { + await loadArenaMap(); + await loadKaelAssets(); + await loadSerisAssets(); + await loadAldricAssets(); + await loadFractureAssets(); + await loadRampantAssets(); + await loadColosseAssets(); + await loadEclatAssets(); + await loadVexarisAssets(); + + worldContainer = new Container(); + layerArena = new Container(); + layerAoe = new Container(); + layerEnemies = new Container(); + layerEntities = new Container(); + layerProjectiles = new Container(); + + // ordre d'affichage : arene tout en bas, projectiles tout en haut + worldContainer.addChild(layerArena, layerAoe, layerEnemies, layerEntities, layerProjectiles); + app.stage.addChild(worldContainer); + + updateIsoLayout(app.screen.width, app.screen.height); + + layerArena.removeChildren(); + drawStaticArena(layerArena); + + updateCamera(worldContainer, app.screen.width, app.screen.height, camX, camY); + + app.renderer.on('resize', (w, h) => { + updateIsoLayout(w, h); + layerArena.removeChildren(); + drawStaticArena(layerArena); + updateCamera(worldContainer, w, h, camX, camY); + }); + } + + connect(); + + on('_open', () => setStatus('ok', 'Connected')); + on('_close', () => setStatus('err', 'Disconnected')); + + on('username_set', (msg) => { + if (msg.my_id) localId = msg.my_id; + document.getElementById('home-buttons').classList.remove('hidden'); + document.getElementById('home-username').classList.add('hidden'); + }); + + on('lobby_created', (msg) => { + isHost = true; + lobbyCode = msg.code; + document.getElementById('code-display').textContent = msg.code; + document.getElementById('start-btn').classList.remove('hidden'); + switchToLobby(); + notify('Lobby created — code: ' + msg.code); + }); + + on('lobby_joined', (msg) => { + lobbyCode = msg.code; + switchToLobby(); + document.getElementById('code-display').textContent = msg.code; + notify('Joined lobby ' + msg.code); + }); + + on('player_joined', (msg) => notify(msg.player.username + ' joined')); + + on('player_ready', () => { + const btn = document.getElementById('ready-btn'); + btn.textContent = 'READY!'; + btn.classList.add('mc-btn-gold'); + btn.disabled = true; + }); + + on('game_starting', async () => { + gameStartTime = Date.now(); + document.getElementById('home-screen').classList.add('hidden'); + document.getElementById('lobby-screen').classList.add('hidden'); + document.getElementById('loading-screen').classList.remove('hidden'); + + await initGameWorld(); + + document.getElementById('loading-screen').classList.add('hidden'); + + ['player-hud', 'soulgate-hud', 'wave-hud', 'fullscreen-btn'].forEach(id => + document.getElementById(id).classList.remove('hidden') + ); + + const getTarget = () => screenToWorld(mouseX, mouseY, worldContainer.x, worldContainer.y); + startInputTracking(getTarget); + + app.canvas.addEventListener('click', (e) => { + if (isInputBlocked()) return; + const { wx, wy } = screenToWorld(e.clientX, e.clientY, worldContainer.x, worldContainer.y); + send('attack', { tx: wx, ty: wy }); + }); + + app.canvas.addEventListener('contextmenu', e => e.preventDefault()); + }); + + on('game_state', (msg) => { + if (!worldContainer) return; + + updatePlayers(layerEntities, playerPool, msg.players); + updateEnemies(layerEnemies, enemyPool, msg.enemies); + updateProjectiles(layerProjectiles, projPool, msg.projectiles); + updateAoeZones(layerAoe, aoePool, msg.aoe_zones ?? []); + + // retrouver le joueur local : par id si dispo (fiable meme avec pseudos en doublon), sinon par username + const local = localId + ? msg.players.find(p => p.id === localId) + : localUsername + ? msg.players.find(p => p.username === localUsername) + : null; + + if (local) { + camX = local.x; + camY = local.y; + updateCamera(worldContainer, app.screen.width, app.screen.height, camX, camY); + updatePlayerHud(local); + } + + updateSoulgateBar(msg.soulgate); + updateWaveInfo(msg.wave); + updateBossBar(msg.wave); + updateUpgradePanel(msg.wave, local); + checkGameEnd(msg, gameStartTime, isHost, lobbyCode, localId); + }); + + on('error', (msg) => notify(msg.message, true)); + + bindEvents({ + setUsername: (v) => { localUsername = v; }, + switchToHome, + notify, + showLeaderboard, + hideLeaderboard, + }); +} + + +let mouseX = 0, mouseY = 0; +document.addEventListener('mousemove', e => { + mouseX = e.clientX; + mouseY = e.clientY; +}); + + +function switchToLobby() { + document.getElementById('home-screen').classList.add('hidden'); + document.getElementById('lobby-screen').classList.remove('hidden'); +} + +function switchToHome() { + isHost = false; + lobbyCode = ''; + document.getElementById('lobby-screen').classList.add('hidden'); + document.getElementById('home-screen').classList.remove('hidden'); + document.getElementById('home-buttons').classList.remove('hidden'); + document.getElementById('home-username').classList.add('hidden'); + document.getElementById('join-row').classList.add('hidden'); + document.getElementById('code-input').value = ''; + document.getElementById('code-display').textContent = '------'; + const readyBtn = document.getElementById('ready-btn'); + readyBtn.disabled = true; + readyBtn.textContent = 'Ready'; + readyBtn.classList.remove('mc-btn-gold'); + document.getElementById('start-btn').classList.add('hidden'); + document.querySelectorAll('.class-btn').forEach(b => b.classList.remove('active')); +} + + +function setStatus(cls, text) { + const el = document.getElementById('home-status'); + el.className = 'home-status ' + cls; + el.textContent = text; +} + +function notify(text, isError = false) { + const el = document.getElementById('overlay-msg'); + if (!el) return; + el.textContent = text; + el.className = 'msg' + (isError ? ' err' : ''); +} + + +main().catch(console.error); diff --git a/client/js/network.js b/client/js/network.js new file mode 100644 index 0000000..0e327ca --- /dev/null +++ b/client/js/network.js @@ -0,0 +1,37 @@ +// network.js : websocket client (ouverture, send, listeners par type) + +const WS_URL = `ws://${location.hostname}:8000/ws`; + +let _ws = null; +const _handlers = {}; + + +export function connect() { + _ws = new WebSocket(WS_URL); + _ws.onopen = () => _emit({ type: '_open' }); + _ws.onclose = () => _emit({ type: '_close' }); + _ws.onmessage = ({ data }) => { + try { + _emit(JSON.parse(data)); + } catch { + console.warn('SOULGATE — message non-JSON reçu'); + } + }; +} + + +export function send(type, data = {}) { + if (_ws?.readyState === WebSocket.OPEN) { + _ws.send(JSON.stringify({ type, ...data })); + } +} + + +export function on(type, fn) { + _handlers[type] = fn; +} + + +function _emit(msg) { + _handlers[msg.type]?.(msg); +} diff --git a/client/js/renderArena.js b/client/js/renderArena.js new file mode 100644 index 0000000..2c608fa --- /dev/null +++ b/client/js/renderArena.js @@ -0,0 +1,39 @@ +// renderArena.js : dessin de l'arene a partir de la map Tiled + sprite Soulgate + +import { Sprite, Assets } from 'pixi.js'; +import { loadTiledMap, drawTiledMap } from './tiledLoader.js'; +import { iso, getTw, getTh } from './renderer.js'; +import { SOULGATE_X, SOULGATE_Y } from './constants.js'; + +let _mapData = null; + +export async function loadArenaMap() { + if (_mapData) return; + _mapData = await loadTiledMap('assets/maps/arrena.tmj'); + await Assets.load('assets/soulgate.png'); +} + +export function drawStaticArena(layer) { + if (!_mapData) { + console.warn('arena map not loaded yet'); + return; + } + drawTiledMap(layer, _mapData); + _drawSoulgate(layer); +} + +function _drawSoulgate(layer) { + const tex = Assets.get('assets/soulgate.png'); + if (!tex) return; + const tw = getTw(); + const th = getTh(); + + const sprite = new Sprite(tex); + sprite.anchor.set(0.5, 1.0); + const p = iso(SOULGATE_X, SOULGATE_Y); + sprite.x = p.x; + sprite.y = p.y + th / 2; + // taille : ~1.5 unite monde de large (source 128px), assez present sans ecraser + sprite.scale.set((tw * 1.5) / 128); + layer.addChild(sprite); +} diff --git a/client/js/renderEnemies.js b/client/js/renderEnemies.js new file mode 100644 index 0000000..384b73e --- /dev/null +++ b/client/js/renderEnemies.js @@ -0,0 +1,616 @@ +// renderEnemies.js : rendu des ennemis (sprites + barre de vie + ombre) +// pool de sprites reutilises (pas de recreate par frame) + +import { Container, Graphics, Assets, Sprite } from 'pixi.js'; +import { iso, getTh, getTw } from './renderer.js'; +import { ENEMY_HITBOX_RADIUS } from './constants.js'; + +let _debugHitboxes = false; +export function setDebugHitboxes(v) { _debugHitboxes = v; } + +// Assets Fracture +const _ft = {}; +let _fractureReady = false; + +export async function loadFractureAssets() { + if (_fractureReady) return; + const b = '../assets/sprites/fracture/'; + + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + + const D4 = ['south', 'north', 'east', 'west']; + const entries = [ + ['f_south', b + 'rotations/south.png'], + ['f_north', b + 'rotations/north.png'], + ['f_east', b + 'rotations/east.png'], + ['f_west', b + 'rotations/west.png'], + ...anim('frun', D4, 4, 'running'), + ]; + + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _ft[key] = tex; + } + _fractureReady = true; +} + +// Assets Rampant +const _rt = {}; +let _rampantReady = false; + +export async function loadRampantAssets() { + if (_rampantReady) return; + const b = '../assets/sprites/rampant/'; + + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + + const D4 = ['south', 'north', 'east', 'west']; + const entries = [ + ['r_south', b + 'rotations/south.png'], + ['r_north', b + 'rotations/north.png'], + ['r_east', b + 'rotations/east.png'], + ['r_west', b + 'rotations/west.png'], + ...anim('rrun', D4, 6, 'running'), + ]; + + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _rt[key] = tex; + } + _rampantReady = true; +} + +// Assets Colosse +const _ct = {}; +let _colosseReady = false; + +export async function loadColosseAssets() { + if (_colosseReady) return; + const b = '../assets/sprites/colosse/'; + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + const D4 = ['south', 'north', 'east', 'west']; + const entries = [ + ['c_south', b + 'rotations/south.png'], + ['c_north', b + 'rotations/north.png'], + ['c_east', b + 'rotations/east.png'], + ['c_west', b + 'rotations/west.png'], + ...anim('crun', D4, 6, 'running'), + ]; + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _ct[key] = tex; + } + _colosseReady = true; +} + +// Assets Éclat +const _et = {}; +let _eclatReady = false; + +export async function loadEclatAssets() { + if (_eclatReady) return; + const b = '../assets/sprites/eclat/'; + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + const D4 = ['south', 'north', 'east', 'west']; + const entries = [ + ['e_south', b + 'rotations/south.png'], + ['e_north', b + 'rotations/north.png'], + ['e_east', b + 'rotations/east.png'], + ['e_west', b + 'rotations/west.png'], + ...anim('erun', D4, 8, 'running'), + ]; + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _et[key] = tex; + } + _eclatReady = true; +} + +// Assets Vexaris +const _vt = {}; +let _vexarisReady = false; + +export async function loadVexarisAssets() { + if (_vexarisReady) return; + const b = '../assets/sprites/vexaris/'; + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + const D4 = ['south', 'north', 'east', 'west']; + const entries = [ + ['v_south', b + 'rotations/south.png'], + ['v_north', b + 'rotations/north.png'], + ['v_east', b + 'rotations/east.png'], + ['v_west', b + 'rotations/west.png'], + ...anim('vrun', D4, 4, 'running'), + ...anim('vatk', D4, 7, 'attack'), + ...anim('vchg', D4, 7, 'charge'), + ...anim('vbst', ['south'], 9, 'burst'), + ]; + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _vt[key] = tex; + } + _vexarisReady = true; +} + +// Animation state par ennemi (position précédente + état animation) +const _ePrevPos = {}; // id → { x, y } +const _eAnimState = {}; // id → { facing, frame, timer, action } + +const F_RUN_FPS = 8; const F_RUN_TICK = 1 / F_RUN_FPS; +const R_RUN_FPS = 12; const R_RUN_TICK = 1 / R_RUN_FPS; +const C_RUN_FPS = 5; const C_RUN_TICK = 1 / C_RUN_FPS; // colosse très lent +const E_RUN_FPS = 16; const E_RUN_TICK = 1 / E_RUN_FPS; // éclat ultrarapide +const V_RUN_FPS = 8; const V_RUN_TICK = 1 / V_RUN_FPS; // running 4 frames +const V_ATK_FPS = 10; const V_ATK_TICK = 1 / V_ATK_FPS; // attack 7 frames +const V_CHG_FPS = 14; const V_CHG_TICK = 1 / V_CHG_FPS; // charge 7 frames +const V_BST_FPS = 8; const V_BST_TICK = 1 / V_BST_FPS; // burst 9 frames south +const TICK_DT = 0.05; +// iso() = projection monde → pixels isométriques +// getTh() = pixels par unité monde (pour les tailles proportionnelles) + + +// Couleur de chaque type d'ennemi (valeur hexadécimale RGB) +const ENEMY_COLORS = { + fracture: 0xcc3333, // rouge foncé — ennemi de base qui fonce sur le Soulgate + rampant: 0xff6600, // orange — attaque les joueurs + colosse: 0x882200, // brun-rouge — lent et résistant + eclat: 0xffcc00, // jaune — petit et rapide + vexaris: 0xaa22ff, // violet — boss vague 2 + general: 0xcc6600, // orange foncé — sous-boss invoqué par Morveth + morveth: 0x550088, // violet très foncé — boss final vague 3 +}; + +// Tailles des ennemis selon le type +// min/max = limites en pixels (pour les petits et grands écrans) +// scale = facteur multiplicateur de th (pixels par unité monde) +const ENEMY_SIZES = { + morveth: { min: 14, max: 55, scale: 0.65 }, // boss final = très grand + vexaris: { min: 12, max: 45, scale: 0.50 }, // boss vague 2 = grand + general: { min: 8, max: 30, scale: 0.32 }, // sous-boss = moyen +}; +const DEFAULT_SIZE = { min: 6, max: 25, scale: 0.22 }; +// Taille par défaut pour les ennemis normaux (fracture, rampant, colosse, eclat) + + +export function updateEnemies(layer, pool, enemies) { + // Met à jour les sprites de tous les ennemis depuis les données serveur + // layer = Container PixiJS du layer "ennemis" + // pool = { id → Container } — sprites existants + // enemies = tableau d'EnemyState reçus dans msg.enemies + + // 1. Supprimer les sprites des ennemis morts (plus dans la liste serveur) + const ids = new Set(enemies.map(e => e.id)); + // ids = Set des IDs encore vivants ce tick + + for (const [id, container] of Object.entries(pool)) { + if (!ids.has(id)) { + layer.removeChild(container); + delete pool[id]; + delete _ePrevPos[id]; + delete _eAnimState[id]; + } + } + + // 2. Créer ou mettre à jour chaque ennemi + for (const e of enemies) { + // e = objet ennemi : { id, type, x, y, hp, max_hp, is_boss, frozen } + + if (!pool[e.id]) { + pool[e.id] = _createEnemyGraphic(e.type); + layer.addChild(pool[e.id]); + _ePrevPos[e.id] = { x: e.x, y: e.y }; + _eAnimState[e.id] = { facing: 'south', frame: 0, timer: 0 }; + } + + const ctr = pool[e.id]; + const pos = iso(e.x, e.y); + const prev = _ePrevPos[e.id]; + const anim = _eAnimState[e.id]; + + // Détecter direction depuis le déplacement + const ddx = e.x - prev.x; + const ddy = e.y - prev.y; + const moving = Math.abs(ddx) > 0.01 || Math.abs(ddy) > 0.01; + if (moving) anim.facing = _enemyDir(ddx, ddy) ?? anim.facing; + _ePrevPos[e.id] = { x: e.x, y: e.y }; + + ctr.x = pos.x; + ctr.y = pos.y; + + // Sprite Fracture + if (e.type === 'fracture' && _fractureReady && ctr._sprite) { + anim.timer += TICK_DT; + if (anim.timer >= F_RUN_TICK) { + anim.timer -= F_RUN_TICK; + anim.frame = (anim.frame + 1) % 4; + } + ctr._sprite.texture = moving + ? _ft[`frun_${anim.facing}_${anim.frame}`] + : _ft[`f_${anim.facing}`]; + } + + // Sprite Rampant + if (e.type === 'rampant' && _rampantReady && ctr._sprite) { + anim.timer += TICK_DT; + if (anim.timer >= R_RUN_TICK) { + anim.timer -= R_RUN_TICK; + anim.frame = (anim.frame + 1) % 6; + } + ctr._sprite.texture = moving + ? _rt[`rrun_${anim.facing}_${anim.frame}`] + : _rt[`r_${anim.facing}`]; + } + + // Sprite Colosse + if (e.type === 'colosse' && _colosseReady && ctr._sprite) { + anim.timer += TICK_DT; + if (anim.timer >= C_RUN_TICK) { + anim.timer -= C_RUN_TICK; + anim.frame = (anim.frame + 1) % 6; + } + ctr._sprite.texture = moving + ? _ct[`crun_${anim.facing}_${anim.frame}`] + : _ct[`c_${anim.facing}`]; + } + + // Sprite Éclat + if (e.type === 'eclat' && _eclatReady && ctr._sprite) { + anim.timer += TICK_DT; + if (anim.timer >= E_RUN_TICK) { + anim.timer -= E_RUN_TICK; + anim.frame = (anim.frame + 1) % 8; + } + ctr._sprite.texture = moving + ? _et[`erun_${anim.facing}_${anim.frame}`] + : _et[`e_${anim.facing}`]; + } + + // Sprite Vexaris + if (e.type === 'vexaris' && _vexarisReady && ctr._sprite) { + const prevAction = anim.action ?? 'idle'; + let action; + if (e.is_charging) { + action = 'charge'; + } else if (moving) { + action = 'run'; + } else if (anim.action === 'burst' && anim.frame < 8) { + action = 'burst'; // laisser le burst se terminer + } else { + action = 'attack'; + } + if (action !== prevAction) { anim.frame = 0; anim.timer = 0; } + anim.action = action; + anim.timer += TICK_DT; + + if (action === 'charge') { + if (anim.timer >= V_CHG_TICK) { anim.timer -= V_CHG_TICK; anim.frame = (anim.frame + 1) % 7; } + ctr._sprite.texture = _vt[`vchg_${anim.facing}_${anim.frame}`]; + } else if (action === 'run') { + if (anim.timer >= V_RUN_TICK) { anim.timer -= V_RUN_TICK; anim.frame = (anim.frame + 1) % 4; } + ctr._sprite.texture = _vt[`vrun_${anim.facing}_${anim.frame}`]; + } else if (action === 'burst') { + if (anim.timer >= V_BST_TICK) { anim.timer -= V_BST_TICK; anim.frame = Math.min(8, anim.frame + 1); } + ctr._sprite.texture = _vt[`vbst_south_${anim.frame}`]; + } else { + if (anim.timer >= V_ATK_TICK) { anim.timer -= V_ATK_TICK; anim.frame = (anim.frame + 1) % 7; } + ctr._sprite.texture = _vt[`vatk_${anim.facing}_${anim.frame}`]; + } + } + + ctr.tint = e.frozen ? 0x88bbff : 0xffffff; + // Si gelé (sort divin Seris) → teinte bleue + // Sinon → blanc = pas de teinte (couleur de base) + // e.frozen = bool envoyé par le serveur (frozen_timer > 0) + + _updateHealthBar(ctr, e.hp, e.max_hp); + + // Debug : hitbox de collision + if (ctr._hboxG) { + const hg = ctr._hboxG; + hg.clear(); + if (_debugHitboxes) { + const rx = ENEMY_HITBOX_RADIUS * getTw() / 2; + const ry = ENEMY_HITBOX_RADIUS * getTh() / 2; + hg.ellipse(0, 0, rx, ry).stroke({ color: 0xff4400, width: 1.5, alpha: 0.9 }); + } + } + } +} + + +function _updateHealthBar(ctr, hp, maxHp) { + // Met à jour la barre de vie d'un ennemi + // ctr = le Container de l'ennemi (contient barFg en tant que propriété custom) + // hp = points de vie actuels + // maxHp = points de vie max + + const ratio = maxHp > 0 ? Math.max(0, hp / maxHp) : 0; + // ratio = pourcentage de vie restante (0.0 à 1.0) + // Math.max(0, ...) = éviter les valeurs négatives si hp < 0 (sécurité) + // maxHp > 0 ? ... : 0 = éviter la division par zéro + + const barFg = ctr._barFg; + // _barFg = la Graphics de la barre de vie (stockée comme propriété custom sur le Container) + // Le _ devant = convention "propriété interne" + + if (!barFg) return; // sécurité si le Container n'a pas été initialisé correctement + + barFg.clear(); // effacer le dessin précédent de la barre + + if (ratio > 0) { + // Redessiner la barre avec la nouvelle largeur proportionnelle au ratio + barFg.rect(ctr._barX, ctr._barY, ctr._barW * ratio, ctr._barH) + .fill({ color: 0xff3333 }); + // ctr._barW * ratio = largeur totale × ratio HP → barre qui rétrécit + // 0xff3333 = rouge + } + // Si ratio = 0 → on ne dessine rien (ennemi presque mort) +} + + +function _enemyDir(dx, dy) { + if (Math.abs(dx) < 0.01 && Math.abs(dy) < 0.01) return null; + if (Math.abs(dx) >= Math.abs(dy)) return dx > 0 ? 'east' : 'west'; + return dy > 0 ? 'south' : 'north'; +} + + +function _createEnemyGraphic(type) { + // Crée le Container PixiJS pour un ennemi (appelé une seule fois par ennemi) + // type = string : "fracture", "rampant", "colosse", "eclat", "vexaris", "morveth", "general" + + const th = getTh(); // pixels par unité monde (pour les tailles proportionnelles) + const container = new Container(); + + // Calculer le rayon selon le type + const sz = ENEMY_SIZES[type] ?? DEFAULT_SIZE; + // ENEMY_SIZES[type] = taille spécifique si boss/général, sinon DEFAULT_SIZE + // ?? = operateur nullish coalescing : si null ou undefined → utiliser DEFAULT_SIZE + + const r = Math.max(sz.min, Math.min(sz.max, th * sz.scale)); + // th * sz.scale = taille proportionnelle au zoom + // Math.max(sz.min, Math.min(sz.max, ...)) = clamp entre min et max + + const color = ENEMY_COLORS[type] ?? 0xff0000; + + // Sprite Rampant + // Canvas 48×48, char bbox y=[8..41], anchor y=0.75 → anchor canvas=36 + // Char top au-dessus de l'anchor = 36-8 = 28 canvas pixels + // Scale = (th*1.4)/48 → char top à screen y = -28*(th*1.4/48) = -th*0.817 + if (type === 'rampant' && _rampantReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.4, r * 0.5, r * 0.12).fill({ color: 0x000000, alpha: 0.3 }); + const scale = (th * 1.4) / 48; + const sprite = new Sprite(_rt.r_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + + const barW = th * 0.7; + const barH = Math.max(2, th * 0.035); + const barY = -(th * 0.82 + barH + 8); // 8px marge au-dessus de la tête + const barBg = new Graphics(); + barBg.rect(-barW / 2, barY, barW, barH).fill({ color: 0x220000 }); + const barFg = new Graphics(); + barFg.rect(-barW / 2, barY, barW, barH).fill({ color: 0xff3333 }); + const hboxG = new Graphics(); + container.addChild(shadow, sprite, barBg, barFg, hboxG); + container._sprite = sprite; + container._barFg = barFg; + container._barX = -barW / 2; + container._barY = barY; + container._barW = barW; + container._barH = barH; + container._hboxG = hboxG; + return container; + } + + // Sprite Fracture + // Canvas 68×68, char bbox y=[10..58], anchor y=0.75 → anchor canvas=51 + // Char top au-dessus de l'anchor = 51-10 = 41 canvas pixels + // Scale = (th*1.2)/48 → char top à screen y = -41*(th*1.2/48) = -th*1.025 + if (type === 'fracture' && _fractureReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.5, r * 0.6, r * 0.15).fill({ color: 0x000000, alpha: 0.3 }); + const scale = (th * 1.2) / 48; + const sprite = new Sprite(_ft.f_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + + const barW = th * 0.8; + const barH = Math.max(2, th * 0.04); + const barY = -(th * 1.025 + barH + 10); // 10px marge au-dessus de la tête + const barBg = new Graphics(); + barBg.rect(-barW / 2, barY, barW, barH).fill({ color: 0x220000 }); + const barFg = new Graphics(); + barFg.rect(-barW / 2, barY, barW, barH).fill({ color: 0xff3333 }); + const hboxG = new Graphics(); + container.addChild(shadow, sprite, barBg, barFg, hboxG); + container._sprite = sprite; + container._barFg = barFg; + container._barX = -barW / 2; + container._barY = barY; + container._barW = barW; + container._barH = barH; + container._hboxG = hboxG; + return container; + } + + // Sprite Colosse + // Canvas 92×92, char top à y=13, anchor y=0.75 → anchor=69, dist=56px + // Scale = (th*2.0)/92 → char_top = -56*(th*2.0/92) = -th*1.217 + if (type === 'colosse' && _colosseReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.6, r * 0.9, r * 0.22).fill({ color: 0x000000, alpha: 0.35 }); + const scale = (th * 2.0) / 92; + const sprite = new Sprite(_ct.c_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + const barW = th * 1.2; + const barH = Math.max(2, th * 0.05); + const barY = -(th * 1.22 + barH + 10); + const barBg = new Graphics(); + barBg.rect(-barW / 2, barY, barW, barH).fill({ color: 0x220000 }); + const barFg = new Graphics(); + barFg.rect(-barW / 2, barY, barW, barH).fill({ color: 0xff3333 }); + const hboxG = new Graphics(); + container.addChild(shadow, sprite, barBg, barFg, hboxG); + container._sprite = sprite; + container._barFg = barFg; + container._barX = -barW / 2; + container._barY = barY; + container._barW = barW; + container._barH = barH; + container._hboxG = hboxG; + return container; + } + + // Sprite Vexaris + // Canvas 92×92, char top à y≈12, anchor y=0.75 → anchor=69, dist=57px + // Scale = (th*2.2)/92 → char_top = -57*(th*2.2/92) = -th*1.363 + if (type === 'vexaris' && _vexarisReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.7, r * 1.1, r * 0.28).fill({ color: 0x330033, alpha: 0.45 }); + const scale = (th * 2.2) / 92; + const sprite = new Sprite(_vt.v_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + const barW = th * 1.6; + const barH = Math.max(3, th * 0.06); + const barY = -(th * 1.36 + barH + 12); + const barBg = new Graphics(); + barBg.rect(-barW / 2, barY, barW, barH).fill({ color: 0x220022 }); + const barFg = new Graphics(); + barFg.rect(-barW / 2, barY, barW, barH).fill({ color: 0xaa22ff }); + const hboxG = new Graphics(); + container.addChild(shadow, sprite, barBg, barFg, hboxG); + container._sprite = sprite; + container._barFg = barFg; + container._barX = -barW / 2; + container._barY = barY; + container._barW = barW; + container._barH = barH; + container._hboxG = hboxG; + return container; + } + + // Sprite Éclat + // Canvas 36×36, char top à y=4, anchor y=0.75 → anchor=27, dist=23px + // Scale = (th*1.0)/36 → char_top = -23*(th/36) = -th*0.639 + if (type === 'eclat' && _eclatReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.3, r * 0.4, r * 0.10).fill({ color: 0x000000, alpha: 0.25 }); + const scale = (th * 1.0) / 36; + const sprite = new Sprite(_et.e_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + const barW = th * 0.5; + const barH = Math.max(2, th * 0.03); + const barY = -(th * 0.64 + barH + 6); + const barBg = new Graphics(); + barBg.rect(-barW / 2, barY, barW, barH).fill({ color: 0x220000 }); + const barFg = new Graphics(); + barFg.rect(-barW / 2, barY, barW, barH).fill({ color: 0xff3333 }); + const hboxG = new Graphics(); + container.addChild(shadow, sprite, barBg, barFg, hboxG); + container._sprite = sprite; + container._barFg = barFg; + container._barX = -barW / 2; + container._barY = barY; + container._barW = barW; + container._barH = barH; + container._hboxG = hboxG; + return container; + } + + const body = new Graphics(); + + // Ombre au sol + body.ellipse(0, r * 0.5, r * 0.7, r * 0.18).fill({ color: 0x000000, alpha: 0.25 }); + // Ellipse aplatie légèrement en dessous du centre (r*0.5) → simuler une ombre + + // Cercle principal de l'ennemi + body.circle(0, 0, r).fill({ color }); + // Contour blanc subtil + body.circle(0, 0, r).stroke({ color: 0xffffff, width: 1, alpha: 0.2 }); + + // Barre de vie + const barW = r * 2.4; // largeur totale de la barre = légèrement plus large que le cercle + const barH = Math.max(2, th * 0.04); // hauteur de la barre (au moins 2px) + const barY = -(r * 1.1 + barH + 3); // position Y = au-dessus du cercle (négatif = vers le haut) + + const barBg = new Graphics(); + // Fond de la barre (rouge très foncé = barre vide) + barBg.rect(-barW / 2, barY, barW, barH).fill({ color: 0x220000 }); + // rect(x, y, largeur, hauteur) — centré horizontalement (-barW/2) + + const barFg = new Graphics(); + // Remplissage de la barre (rouge vif = vie restante) + // Dessiné à 100% au départ, puis mis à jour par _updateHealthBar() + barFg.rect(-barW / 2, barY, barW, barH).fill({ color: 0xff3333 }); + + const hboxG = new Graphics(); + + container.addChild(body, barBg, barFg, hboxG); + // Ordre d'ajout = ordre d'affichage (barFg par-dessus barBg par-dessus body) + + // Stocker les infos de la barre sur le Container pour les réutiliser dans _updateHealthBar + // On les préfixe avec _ pour indiquer que c'est de l'état interne + container._barFg = barFg; // référence au Graphics à redessiner + container._barX = -barW / 2; // X de départ de la barre + container._barY = barY; // Y de la barre + container._barW = barW; // largeur TOTALE (× ratio HP = largeur réelle) + container._barH = barH; // hauteur de la barre + container._hboxG = hboxG; // Graphics de la hitbox debug + + return container; +} diff --git a/client/js/renderPlayers.js b/client/js/renderPlayers.js new file mode 100644 index 0000000..cce9050 --- /dev/null +++ b/client/js/renderPlayers.js @@ -0,0 +1,735 @@ +// renderPlayers.js — Rendu des joueurs (sprites Kael ou cercles colorés) + +import { Container, Graphics, Text, Assets, Sprite } from 'pixi.js'; +import { CLASS_COLORS, PLAYER_HITBOX_RADIUS, KAEL_MELEE_RADIUS, KAEL_SLAM_RADIUS, KAEL_STORM_RADIUS, + DISPLACEMENT_COOLDOWNS } from './constants.js'; +import { iso, getTh, getTw } from './renderer.js'; + +let _debugAttackRange = false; +export function setDebugAttackRange(v) { _debugAttackRange = v; } + +let _debugHitboxes = false; +export function setDebugHitboxes(v) { _debugHitboxes = v; } + +const BUFF_TINTS = { + invulnerable: 0xffcc44, + casting: 0x44ffff, + flying: 0x88ccff, + intangible: 0xcc88ff, +}; + +// Assets Kael +const _kt = {}; +let _kaelReady = false; + +// Assets Seris +const _st = {}; +let _serisReady = false; + +// Assets Aldric +const _at = {}; +let _aldricReady = false; + +export async function loadKaelAssets() { + if (_kaelReady) return; + const b = '../assets/sprites/kael/'; + + // Génère les entrées [clé, chemin] pour une animation + // prefix = 'run', 'atk', 'dash', 'sk2', 'sk3' + // dirs = tableau de directions (ex: ['south','north','east','west']) + // n = nombre de frames + // folder = dossier dans animations/ + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + + const D4 = ['south', 'north', 'east', 'west']; + const D1 = ['south']; + + const entries = [ + // Rotations statiques (idle) + ['south', b + 'rotations/south.png'], + ['north', b + 'rotations/north.png'], + ['east', b + 'rotations/east.png'], + ['west', b + 'rotations/west.png'], + // Animations + ...anim('run', D4, 4, 'running'), // running 4 dirs × 4 frames + ...anim('atk', D4, 5, 'attack'), // attaque 4 dirs × 5 frames + ...anim('dash', D4, 5, 'dash'), // dash 4 dirs × 5 frames + ...anim('sk2', D1, 9, 'skill2'), // frappe lourde south × 9 frames + ...anim('sk3', D1,17, 'skill3'), // tempête south × 17 frames + ]; + + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _kt[key] = tex; + } + _kaelReady = true; +} + +export async function loadSerisAssets() { + if (_serisReady) return; + const b = '../assets/sprites/seris/'; + + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + + const D4 = ['south', 'north', 'east', 'west']; + const D1 = ['south']; + + const entries = [ + ['s_south', b + 'rotations/south.png'], + ['s_north', b + 'rotations/north.png'], + ['s_east', b + 'rotations/east.png'], + ['s_west', b + 'rotations/west.png'], + ...anim('srun', D4, 6, 'running'), // course 4 dirs × 6 frames + ...anim('satk', D4, 7, 'attack'), // attaque 4 dirs × 7 frames + ...anim('stele', D1, 5, 'teleport'), // téléport south × 5 frames + ...anim('ssk1', D1, 9, 'skill1'), // éventail south × 9 frames + ...anim('ssk2', D1,13, 'skill2'), // vide south × 13 frames + ]; + + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _st[key] = tex; + } + _serisReady = true; +} + + +export async function loadAldricAssets() { + if (_aldricReady) return; + const b = '../assets/sprites/aldric/'; + + function anim(prefix, dirs, n, folder) { + const out = []; + for (const d of dirs) { + for (let i = 0; i < n; i++) { + const pad = String(i).padStart(3, '0'); + out.push([`${prefix}_${d}_${i}`, `${b}animations/${folder}/${d}/frame_${pad}.png`]); + } + } + return out; + } + + const D4 = ['south', 'north', 'east', 'west']; + const D1 = ['south']; + + const entries = [ + ['a_south', b + 'rotations/south.png'], + ['a_north', b + 'rotations/north.png'], + ['a_east', b + 'rotations/east.png'], + ['a_west', b + 'rotations/west.png'], + ...anim('awk', D4, 6, 'walking'), // marche 4 dirs × 6 frames + ...anim('aatk', D4, 7, 'attack'), // attaque 4 dirs × 7 frames + ...anim('afly', D1, 9, 'fly'), // envol south × 9 frames + ...anim('ask1', D1, 9, 'skill1'), // halo south × 9 frames + ...anim('ask2', D1,13, 'skill2'), // vague south × 13 frames + ]; + + for (const [key, path] of entries) { + const tex = await Assets.load(path); + tex.source.scaleMode = 'nearest'; + _at[key] = tex; + } + _aldricReady = true; +} + + +// Constantes d'animation +const TICK_DT = 0.05; // durée d'un tick serveur (20 Hz) + +const RUN_FPS = 8; const RUN_TICK = 1 / RUN_FPS; +const ATK_FPS = 14; const ATK_TICK = 1 / ATK_FPS; const ATK_FRAMES = 5; +const DASH_FPS = 14; const DASH_TICK = 1 / DASH_FPS; const DASH_FRAMES = 5; +const DASH_DUR = DASH_FRAMES / DASH_FPS; // ~0.36s — durée totale du dash visuel + +const SK2_FPS = 12; const SK2_TICK = 1 / SK2_FPS; const SK2_FRAMES = 9; +const SK3_FPS = 10; const SK3_TICK = 1 / SK3_FPS; const SK3_FRAMES = 17; + +// Cooldowns max des skills (pour détecter le déclenchement côté client) +// Si le CD passe de ~0 à une valeur > THRESHOLD, le skill vient d'être utilisé. +const DISP_CD_MAX = 9.0; +const SK2_CD_MAX = 7.0; // kael slam (ability_1) +const SK3_CD_MAX = 12.0; // kael tempête (ability_2) + +// Seris animation frame counts + FPS +const S_RUN_FRAMES = 6; +const S_ATK_FRAMES = 7; +const S_TELE_FRAMES = 5; +const S_SK1_FRAMES = 9; +const S_SK2_FRAMES = 13; +const S_RUN_FPS = 10; const S_RUN_TICK = 1 / S_RUN_FPS; +const S_ATK_FPS = 14; const S_ATK_TICK = 1 / S_ATK_FPS; +const S_TELE_FPS = 14; const S_TELE_TICK = 1 / S_TELE_FPS; +const S_SK1_FPS = 12; const S_SK1_TICK = 1 / S_SK1_FPS; +const S_SK2_FPS = 10; const S_SK2_TICK = 1 / S_SK2_FPS; + +// Cooldowns Seris pour détection côté client +const S_DISP_CD_MAX = 4.0; // téléport +const S_SK1_CD_MAX = 6.0; // éventail +const S_SK2_CD_MAX = 10.0; // vide + +// Aldric animation frame counts + FPS +const A_WK_FRAMES = 6; +const A_ATK_FRAMES = 7; +const A_FLY_FRAMES = 9; +const A_SK1_FRAMES = 9; +const A_SK2_FRAMES = 13; +const A_WK_FPS = 8; const A_WK_TICK = 1 / A_WK_FPS; +const A_ATK_FPS = 12; const A_ATK_TICK = 1 / A_ATK_FPS; +const A_FLY_FPS = 12; const A_FLY_TICK = 1 / A_FLY_FPS; +const A_SK1_FPS = 10; const A_SK1_TICK = 1 / A_SK1_FPS; +const A_SK2_FPS = 10; const A_SK2_TICK = 1 / A_SK2_FPS; + +// Cooldowns Aldric pour détection côté client +const A_DISP_CD_MAX = 14.0; // envol +const A_SK1_CD_MAX = 8.0; // halo +const A_SK2_CD_MAX = 15.0; // vague + +// État d'animation par joueur +const _prevPos = {}; // position précédente (pour détecter direction et dash) +const _animState = {}; // état d'animation complet +const _prevAtkCd = {}; // cooldown attaque au tick précédent +const _prevDispCd = {}; // cooldown displacement au tick précédent +const _prevAb2Cd = {}; // cooldown ability_2 au tick précédent +const _prevAb3Cd = {}; // cooldown ability_3 au tick précédent +const _dashAnim = {}; // { fromX, fromY, toX, toY, t } — interpolation de position dash + + +// Mise à jour + +export function updatePlayers(layer, pool, players) { + const ids = new Set(players.map(p => p.id)); + + // Supprimer les joueurs déconnectés + for (const [id, container] of Object.entries(pool)) { + if (!ids.has(id)) { + layer.removeChild(container); + delete pool[id]; + delete _prevPos[id]; delete _animState[id]; + delete _prevAtkCd[id]; delete _prevDispCd[id]; + delete _prevAb2Cd[id]; delete _prevAb3Cd[id]; + delete _dashAnim[id]; + } + } + + for (const p of players) { + // Création du sprite si nouveau joueur + if (!pool[p.id]) { + pool[p.id] = _createPlayerGraphic(p); + layer.addChild(pool[p.id]); + _prevPos[p.id] = { x: p.x, y: p.y }; + _animState[p.id] = { + facing: 'south', + frame: 0, timer: 0, + attacking: false, atkFrame: 0, atkTimer: 0, + dashing: false, dashDir: 'south', dashFrame: 0, dashTimer: 0, + sk2ing: false, sk2Frame: 0, sk2Timer: 0, + sk3ing: false, sk3Frame: 0, sk3Timer: 0, + // Seris + steling: false, steleFrame: 0, steleTimer: 0, + ssk1ing: false, ssk1Frame: 0, ssk1Timer: 0, + ssk2ing: false, ssk2Frame: 0, ssk2Timer: 0, + // Aldric + aflying: false, aflyFrame: 0, aflyTimer: 0, + ask1ing: false, ask1Frame: 0, ask1Timer: 0, + ask2ing: false, ask2Frame: 0, ask2Timer: 0, + }; + _prevAtkCd[p.id] = 0; + _prevDispCd[p.id] = 0; + _prevAb2Cd[p.id] = 0; + _prevAb3Cd[p.id] = 0; + } + + const prev = _prevPos[p.id]; + const dx = p.x - prev.x; + const dy = p.y - prev.y; + const moving = Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01; + const anim = _animState[p.id]; + + // Détecter les déclenchements d'actions + // Attaque de base : attack CD 0 → > 0 + const curAtkCd = p.cooldowns?.attack ?? 0; + if (curAtkCd > 0 && _prevAtkCd[p.id] === 0) { + anim.attacking = true; anim.atkFrame = 0; anim.atkTimer = 0; + } + _prevAtkCd[p.id] = curAtkCd; + + // Displacement (E) — seuil par classe + const dispCdMax = DISPLACEMENT_COOLDOWNS[p.class] ?? DISP_CD_MAX; + const curDispCd = p.cooldowns?.displacement ?? 0; + if (curDispCd > _prevDispCd[p.id] + dispCdMax * 0.5) { + if (p.class === 'kael') { + anim.dashing = true; + anim.dashDir = anim.facing; + anim.dashFrame = 0; + anim.dashTimer = 0; + _dashAnim[p.id] = { + fromX: prev.x, fromY: prev.y, + toX: p.x, toY: p.y, + t: 0, + }; + } else if (p.class === 'seris') { + anim.steling = true; + anim.steleFrame = 0; + anim.steleTimer = 0; + } else if (p.class === 'aldric') { + anim.aflying = true; + anim.aflyFrame = 0; + anim.aflyTimer = 0; + } + } + _prevDispCd[p.id] = curDispCd; + + // Skill 1 + const curAb2Cd = p.cooldowns?.ability_1 ?? 0; + if (p.class === 'kael') { + if (curAb2Cd > _prevAb2Cd[p.id] + SK2_CD_MAX * 0.5) { + anim.sk2ing = true; anim.sk2Frame = 0; anim.sk2Timer = 0; + } + } else if (p.class === 'seris') { + if (curAb2Cd > _prevAb2Cd[p.id] + S_SK1_CD_MAX * 0.5) { + anim.ssk1ing = true; anim.ssk1Frame = 0; anim.ssk1Timer = 0; + } + } else if (p.class === 'aldric') { + if (curAb2Cd > _prevAb2Cd[p.id] + A_SK1_CD_MAX * 0.5) { + anim.ask1ing = true; anim.ask1Frame = 0; anim.ask1Timer = 0; + } + } + _prevAb2Cd[p.id] = curAb2Cd; + + // Skill 2 + const curAb3Cd = p.cooldowns?.ability_2 ?? 0; + if (p.class === 'kael') { + if (curAb3Cd > _prevAb3Cd[p.id] + SK3_CD_MAX * 0.5) { + anim.sk3ing = true; anim.sk3Frame = 0; anim.sk3Timer = 0; + } + } else if (p.class === 'seris') { + if (curAb3Cd > _prevAb3Cd[p.id] + S_SK2_CD_MAX * 0.5) { + anim.ssk2ing = true; anim.ssk2Frame = 0; anim.ssk2Timer = 0; + } + } else if (p.class === 'aldric') { + if (curAb3Cd > _prevAb3Cd[p.id] + A_SK2_CD_MAX * 0.5) { + anim.ask2ing = true; anim.ask2Frame = 0; anim.ask2Timer = 0; + } + } + _prevAb3Cd[p.id] = curAb3Cd; + + // Mise à jour direction depuis le mouvement + if (moving && !anim.dashing) { + anim.facing = _detectDir(dx, dy) || anim.facing; + } + + // Calcul de la position à afficher + // Pendant un dash : interpoler linéairement de fromPos à toPos + let renderX = p.x, renderY = p.y; + const da = _dashAnim[p.id]; + if (da) { + da.t += TICK_DT; + if (da.t >= DASH_DUR) { + delete _dashAnim[p.id]; // dash terminé + } else { + const alpha = da.t / DASH_DUR; + renderX = da.fromX + (da.toX - da.fromX) * alpha; + renderY = da.fromY + (da.toY - da.fromY) * alpha; + } + } + const pos = iso(renderX, renderY); + + // Choisir la texture + if (p.class === 'kael' && _kaelReady && pool[p.id]._sprite) { + _updateKaelSprite(pool[p.id]._sprite, anim, moving); + } else if (p.class === 'seris' && _serisReady && pool[p.id]._sprite) { + _updateSerisSprite(pool[p.id]._sprite, anim, moving); + } else if (p.class === 'aldric' && _aldricReady && pool[p.id]._sprite) { + _updateAldricSprite(pool[p.id]._sprite, anim, moving); + } + + // Debug : zones d'attaque + if (p.class === 'kael' && pool[p.id]._rangeGraphic) { + const rg = pool[p.id]._rangeGraphic; + rg.clear(); + if (_debugAttackRange) { + const tw = getTw(); + const th2 = getTh(); + if (anim.attacking) { + // Attaque de base — demi-cercle (on affiche un cercle complet pour simplifier) + const rx = KAEL_MELEE_RADIUS * tw / 2; + const ry = KAEL_MELEE_RADIUS * th2 / 2; + rg.ellipse(0, 0, rx, ry).fill({ color: 0xff4444, alpha: 0.18 }); + rg.ellipse(0, 0, rx, ry).stroke({ color: 0xff6666, width: 1.5, alpha: 0.7 }); + } + if (anim.sk2ing) { + // Skill 2 — slam cercle complet (orange) + const rx = KAEL_SLAM_RADIUS * tw / 2; + const ry = KAEL_SLAM_RADIUS * th2 / 2; + rg.ellipse(0, 0, rx, ry).fill({ color: 0xff8800, alpha: 0.18 }); + rg.ellipse(0, 0, rx, ry).stroke({ color: 0xffaa44, width: 1.5, alpha: 0.8 }); + } + if (anim.sk3ing) { + // Skill 3 — tempête cercle large (violet) + const rx = KAEL_STORM_RADIUS * tw / 2; + const ry = KAEL_STORM_RADIUS * th2 / 2; + rg.ellipse(0, 0, rx, ry).fill({ color: 0xaa44ff, alpha: 0.15 }); + rg.ellipse(0, 0, rx, ry).stroke({ color: 0xcc88ff, width: 2, alpha: 0.8 }); + } + } + } + + // Debug : hitbox de collision du joueur + if (pool[p.id]._hboxG) { + const hg = pool[p.id]._hboxG; + hg.clear(); + if (_debugHitboxes) { + const rx = PLAYER_HITBOX_RADIUS * getTw() / 2; + const ry = PLAYER_HITBOX_RADIUS * getTh() / 2; + hg.ellipse(0, 0, rx, ry).stroke({ color: 0x00ffff, width: 1.5, alpha: 0.9 }); + } + } + + _prevPos[p.id] = { x: p.x, y: p.y }; + + // Teinte selon buff + let tint = 0xffffff; + let alpha = 1.0; + for (const b of (p.buffs ?? [])) { + if (BUFF_TINTS[b.type]) { tint = BUFF_TINTS[b.type]; break; } + } + if (p.buffs?.some(b => b.type === 'intangible')) alpha = 0.5; + + pool[p.id].x = pos.x; + pool[p.id].y = pos.y; + pool[p.id].tint = tint; + pool[p.id].alpha = p.alive ? alpha : 0.2; + } +} + + +// Logique de sprite Kael + +function _updateKaelSprite(sprite, anim, moving) { + // Priorité : dash > tempête (sk3) > frappe lourde (sk2) > attaque > run > idle + + if (anim.dashing) { + anim.dashTimer += TICK_DT; + if (anim.dashTimer >= DASH_TICK) { + anim.dashTimer -= DASH_TICK; + anim.dashFrame++; + } + if (anim.dashFrame >= DASH_FRAMES) { + anim.dashing = false; + } else { + sprite.texture = _kt[`dash_${anim.dashDir}_${anim.dashFrame}`]; + return; + } + } + + if (anim.sk3ing) { + anim.sk3Timer += TICK_DT; + if (anim.sk3Timer >= SK3_TICK) { + anim.sk3Timer -= SK3_TICK; + anim.sk3Frame++; + } + if (anim.sk3Frame >= SK3_FRAMES) { + anim.sk3ing = false; + } else { + sprite.texture = _kt[`sk3_south_${anim.sk3Frame}`]; + return; + } + } + + if (anim.sk2ing) { + anim.sk2Timer += TICK_DT; + if (anim.sk2Timer >= SK2_TICK) { + anim.sk2Timer -= SK2_TICK; + anim.sk2Frame++; + } + if (anim.sk2Frame >= SK2_FRAMES) { + anim.sk2ing = false; + } else { + sprite.texture = _kt[`sk2_south_${anim.sk2Frame}`]; + return; + } + } + + if (anim.attacking) { + anim.atkTimer += TICK_DT; + if (anim.atkTimer >= ATK_TICK) { + anim.atkTimer -= ATK_TICK; + anim.atkFrame++; + } + if (anim.atkFrame >= ATK_FRAMES) { + anim.attacking = false; + } else { + sprite.texture = _kt[`atk_${anim.facing}_${anim.atkFrame}`]; + return; + } + } + + if (moving) { + anim.timer += TICK_DT; + if (anim.timer >= RUN_TICK) { + anim.timer -= RUN_TICK; + anim.frame = (anim.frame + 1) % 4; + } + sprite.texture = _kt[`run_${anim.facing}_${anim.frame}`]; + } else { + anim.frame = 0; + anim.timer = 0; + sprite.texture = _kt[anim.facing]; + } +} + + +// Logique de sprite Seris + +function _updateSerisSprite(sprite, anim, moving) { + // Priorité : téléport > vide (sk2) > éventail (sk1) > attaque > run > idle + + if (anim.steling) { + anim.steleTimer += TICK_DT; + if (anim.steleTimer >= S_TELE_TICK) { + anim.steleTimer -= S_TELE_TICK; + anim.steleFrame++; + } + if (anim.steleFrame >= S_TELE_FRAMES) { + anim.steling = false; + } else { + sprite.texture = _st[`stele_south_${anim.steleFrame}`]; + return; + } + } + + if (anim.ssk2ing) { + anim.ssk2Timer += TICK_DT; + if (anim.ssk2Timer >= S_SK2_TICK) { + anim.ssk2Timer -= S_SK2_TICK; + anim.ssk2Frame++; + } + if (anim.ssk2Frame >= S_SK2_FRAMES) { + anim.ssk2ing = false; + } else { + sprite.texture = _st[`ssk2_south_${anim.ssk2Frame}`]; + return; + } + } + + if (anim.ssk1ing) { + anim.ssk1Timer += TICK_DT; + if (anim.ssk1Timer >= S_SK1_TICK) { + anim.ssk1Timer -= S_SK1_TICK; + anim.ssk1Frame++; + } + if (anim.ssk1Frame >= S_SK1_FRAMES) { + anim.ssk1ing = false; + } else { + sprite.texture = _st[`ssk1_south_${anim.ssk1Frame}`]; + return; + } + } + + if (anim.attacking) { + anim.atkTimer += TICK_DT; + if (anim.atkTimer >= S_ATK_TICK) { + anim.atkTimer -= S_ATK_TICK; + anim.atkFrame++; + } + if (anim.atkFrame >= S_ATK_FRAMES) { + anim.attacking = false; + } else { + sprite.texture = _st[`satk_${anim.facing}_${anim.atkFrame}`]; + return; + } + } + + if (moving) { + anim.timer += TICK_DT; + if (anim.timer >= S_RUN_TICK) { + anim.timer -= S_RUN_TICK; + anim.frame = (anim.frame + 1) % S_RUN_FRAMES; + } + sprite.texture = _st[`srun_${anim.facing}_${anim.frame}`]; + } else { + anim.frame = 0; + anim.timer = 0; + sprite.texture = _st[`s_${anim.facing}`]; + } +} + + +// Logique de sprite Aldric + +function _updateAldricSprite(sprite, anim, moving) { + // Priorité : envol > vague (sk2) > halo (sk1) > attaque > marche > idle + + if (anim.aflying) { + anim.aflyTimer += TICK_DT; + if (anim.aflyTimer >= A_FLY_TICK) { + anim.aflyTimer -= A_FLY_TICK; + anim.aflyFrame++; + } + if (anim.aflyFrame >= A_FLY_FRAMES) { + anim.aflying = false; + } else { + sprite.texture = _at[`afly_south_${anim.aflyFrame}`]; + return; + } + } + + if (anim.ask2ing) { + anim.ask2Timer += TICK_DT; + if (anim.ask2Timer >= A_SK2_TICK) { + anim.ask2Timer -= A_SK2_TICK; + anim.ask2Frame++; + } + if (anim.ask2Frame >= A_SK2_FRAMES) { + anim.ask2ing = false; + } else { + sprite.texture = _at[`ask2_south_${anim.ask2Frame}`]; + return; + } + } + + if (anim.ask1ing) { + anim.ask1Timer += TICK_DT; + if (anim.ask1Timer >= A_SK1_TICK) { + anim.ask1Timer -= A_SK1_TICK; + anim.ask1Frame++; + } + if (anim.ask1Frame >= A_SK1_FRAMES) { + anim.ask1ing = false; + } else { + sprite.texture = _at[`ask1_south_${anim.ask1Frame}`]; + return; + } + } + + if (anim.attacking) { + anim.atkTimer += TICK_DT; + if (anim.atkTimer >= A_ATK_TICK) { + anim.atkTimer -= A_ATK_TICK; + anim.atkFrame++; + } + if (anim.atkFrame >= A_ATK_FRAMES) { + anim.attacking = false; + } else { + sprite.texture = _at[`aatk_${anim.facing}_${anim.atkFrame}`]; + return; + } + } + + if (moving) { + anim.timer += TICK_DT; + if (anim.timer >= A_WK_TICK) { + anim.timer -= A_WK_TICK; + anim.frame = (anim.frame + 1) % A_WK_FRAMES; + } + sprite.texture = _at[`awk_${anim.facing}_${anim.frame}`]; + } else { + anim.frame = 0; + anim.timer = 0; + sprite.texture = _at[`a_${anim.facing}`]; + } +} + + +// Helpers + +// Renvoie la direction dominante ('south'|'north'|'east'|'west') ou null si stationnaire +function _detectDir(dx, dy) { + if (Math.abs(dx) < 0.01 && Math.abs(dy) < 0.01) return null; + if (Math.abs(dx) >= Math.abs(dy)) return dx > 0 ? 'east' : 'west'; + return dy > 0 ? 'south' : 'north'; +} + + +// Création du sprite + +function _createPlayerGraphic(player) { + const th = getTh(); + const container = new Container(); + const r = Math.max(10, Math.min(40, th * 0.4)); + + const hboxG = new Graphics(); + container._hboxG = hboxG; + + if (player.class === 'kael' && _kaelReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.3, r * 0.5, r * 0.13).fill({ color: 0x000000, alpha: 0.3 }); + + const scale = (th * 1.2) / 48; + const sprite = new Sprite(_kt.south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + container._sprite = sprite; + + const rangeG = new Graphics(); + container._rangeGraphic = rangeG; + + container.addChild(rangeG, shadow, sprite, hboxG); + + } else if (player.class === 'seris' && _serisReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.3, r * 0.5, r * 0.13).fill({ color: 0x000000, alpha: 0.3 }); + + const scale = (th * 1.2) / 48; + const sprite = new Sprite(_st.s_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + container._sprite = sprite; + + container.addChild(shadow, sprite, hboxG); + + } else if (player.class === 'aldric' && _aldricReady) { + const shadow = new Graphics(); + shadow.ellipse(0, r * 0.3, r * 0.5, r * 0.13).fill({ color: 0x000000, alpha: 0.3 }); + + const scale = (th * 1.2) / 48; + const sprite = new Sprite(_at.a_south); + sprite.anchor.set(0.5, 0.75); + sprite.scale.set(scale); + container._sprite = sprite; + + container.addChild(shadow, sprite, hboxG); + + } else { + const color = CLASS_COLORS[player.class] ?? 0xffffff; + const body = new Graphics(); + body.ellipse(0, r * 0.5, r * 0.6, r * 0.15).fill({ color: 0x000000, alpha: 0.25 }); + body.circle(0, 0, r).fill({ color }); + body.circle(0, 0, r).stroke({ color: 0xffffff, width: 1.5, alpha: 0.3 }); + + const label = new Text({ + text: player.username, + style: { + fontSize: Math.max(9, Math.min(14, th * 0.15)), + fill: 0xdddddd, + fontFamily: 'Courier New', + }, + }); + label.anchor.set(0.5, 1); + label.y = -(r * 1.05 + 8); + + container.addChild(body, label, hboxG); + } + + return container; +} diff --git a/client/js/renderer.js b/client/js/renderer.js new file mode 100644 index 0000000..254b5a5 --- /dev/null +++ b/client/js/renderer.js @@ -0,0 +1,184 @@ +// renderer.js : projection iso (world -> screen et inverse), camera, projectiles + AOE +// formule : sx = (wx - wy) * tw/2 / sy = (wx + wy) * th/2 + +import { Graphics } from 'pixi.js'; + +import { + ARENA_WIDTH, ARENA_HEIGHT, + TILE_WIDTH, TILE_HEIGHT, + PROJECTILE_HIT_RADIUS, SOULGATE_HITBOX_RADIUS, + SOULGATE_X, SOULGATE_Y, +} from './constants.js'; + +let _debugHitboxes = false; +export function setDebugHitboxes(v) { _debugHitboxes = v; } + +// Re-exports : ces fonctions viennent d'autres fichiers mais sont exposées ici +// → main.js peut tout importer depuis renderer.js en un seul endroit +export { drawStaticArena } from './renderArena.js'; // dessine l'arène statique (sol, Soulgate, etc.) +export { updateEnemies } from './renderEnemies.js'; // met à jour les sprites ennemis +export { updatePlayers } from './renderPlayers.js'; // met à jour les sprites joueurs + + +// Zoom / Layout + +// Nombre d'unités monde visibles en diagonale iso — détermine le niveau de zoom +// Plus c'est grand → plus on voit loin → plus les objets sont petits +const CAMERA_VIEWPORT_DIAG = 22; + +// tw / th = pixels par "unité monde" dans les axes X et Y isométriques +// recalcules au resize +let tw, th; + +export function getTh() { return th; } +export function getTw() { return tw; } + + +export function updateIsoLayout(screenW, screenH) { + const bW = CAMERA_VIEWPORT_DIAG * TILE_WIDTH / 2; + const bH = CAMERA_VIEWPORT_DIAG * TILE_HEIGHT / 2; + tw = TILE_WIDTH * (screenW / bW); + th = TILE_HEIGHT * (screenH / bH); +} + + +// projection iso : (wx - wy) * tw/2 / (wx + wy) * th/2 +export function iso(wx, wy) { + return { + x: (wx - wy) * tw / 2, + y: (wx + wy) * th / 2, + }; +} + + +export function screenToWorld(screenX, screenY, containerX, containerY) { + // inverse de iso, sert au clic souris + const sx = screenX - containerX; + const sy = screenY - containerY; + return { + wx: sx / tw + sy / th, + wy: sy / th - sx / tw, + }; +} + + +export function updateCamera(worldContainer, screenW, screenH, wx, wy) { + const HW = ARENA_WIDTH / 2; + const HH = ARENA_HEIGHT / 2; + // clamp pour pas voir du vide au bord + const camX = Math.max(-HW, Math.min(HW, wx)); + const camY = Math.max(-HH, Math.min(HH, wy)); + + const p = iso(camX, camY); + worldContainer.x = screenW / 2 - p.x; + worldContainer.y = screenH / 2 - p.y; +} + + +export function updateProjectiles(layer, pool, projectiles) { + const ids = new Set(projectiles.map(p => p.id)); + for (const [id, g] of Object.entries(pool)) { + if (!ids.has(id)) { + layer.removeChild(g); + delete pool[id]; + } + } + + for (const p of projectiles) { + if (!pool[p.id]) { + pool[p.id] = _createProjectileGraphic(); + layer.addChild(pool[p.id]); + } + const pos = iso(p.x, p.y); + pool[p.id].x = pos.x; + pool[p.id].y = pos.y; + + // Debug : hitbox du projectile + if (_debugHitboxes && pool[p.id]._hboxG) { + const hg = pool[p.id]._hboxG; + hg.clear(); + const rx = PROJECTILE_HIT_RADIUS * tw / 2; + const ry = PROJECTILE_HIT_RADIUS * th / 2; + hg.ellipse(0, 0, rx, ry).stroke({ color: 0xffff00, width: 1, alpha: 0.8 }); + } else if (!_debugHitboxes && pool[p.id]._hboxG) { + pool[p.id]._hboxG.clear(); + } + } +} + + +function _createProjectileGraphic() { + const g = new Graphics(); + const r = Math.max(3, th * 0.08); + g.circle(0, 0, r).fill({ color: 0xffee44 }); + g.circle(0, 0, r).stroke({ color: 0xffffff, width: 1, alpha: 0.3 }); + + // sous-graphics pour la hitbox debug + const hboxG = new Graphics(); + g.addChild(hboxG); + g._hboxG = hboxG; + + return g; +} + + +// AOE + +const _AOE_STYLE = { + slam: { fill: 0xff6600, stroke: 0xffaa44 }, + storm: { fill: 0x8800ff, stroke: 0xcc88ff }, + void: { fill: 0x00ccff, stroke: 0x88eeff }, + pulse: { fill: 0xffd700, stroke: 0xfff0a0 }, +}; + +export function updateAoeZones(layer, pool, zones) { + // Supprimer les zones disparues + const ids = new Set(zones.map(z => z.id)); + for (const [id, g] of Object.entries(pool)) { + if (!ids.has(id)) { layer.removeChild(g); delete pool[id]; } + } + + for (const z of zones) { + if (!pool[z.id]) { + pool[z.id] = new Graphics(); + layer.addChild(pool[z.id]); + } + const g = pool[z.id]; + const pos = iso(z.x, z.y); + g.x = pos.x; + g.y = pos.y; + + const progress = z.ticks / z.max_ticks; // 1.0 → 0.0 au fil du temps + const pal = _AOE_STYLE[z.type] ?? _AOE_STYLE.slam; + const rx = z.radius * tw / 2; + const ry = z.radius * th / 2; + + g.clear(); + g.ellipse(0, 0, rx, ry).fill({ color: pal.fill, alpha: 0.18 * progress }); + g.ellipse(0, 0, rx, ry).stroke({ color: pal.stroke, width: 2, alpha: 0.85 * progress }); + } +} + + +// hitbox debug Soulgate (plus utilise mais on garde le graphics au cas ou) + +let _sgHboxG = null; + +export function initSoulgateHitbox(layer) { + if (_sgHboxG) { layer.removeChild(_sgHboxG); } + _sgHboxG = new Graphics(); + layer.addChild(_sgHboxG); +} + +export function updateSoulgateHitbox() { + if (!_sgHboxG) return; + _sgHboxG.clear(); + if (_debugHitboxes) { + const pos = iso(SOULGATE_X, SOULGATE_Y); + _sgHboxG.x = pos.x; + _sgHboxG.y = pos.y; + const rx = SOULGATE_HITBOX_RADIUS * tw / 2; + const ry = SOULGATE_HITBOX_RADIUS * th / 2; + _sgHboxG.ellipse(0, 0, rx, ry).stroke({ color: 0x00ff88, width: 2, alpha: 0.9 }); + } +} diff --git a/client/js/tiledLoader.js b/client/js/tiledLoader.js new file mode 100644 index 0000000..78cd4cc --- /dev/null +++ b/client/js/tiledLoader.js @@ -0,0 +1,95 @@ +// tiledLoader.js : charge une map Tiled (.tmj + .tsx) et la dessine en iso + +import { Sprite, Assets } from 'pixi.js'; +import { iso, getTw, getTh } from './renderer.js'; + + +export async function loadTiledMap(mapPath) { + // baseUrl = dossier du .tmj (les .tsx sont a cote) + const baseUrl = mapPath.substring(0, mapPath.lastIndexOf('/') + 1); + const map = await fetch(mapPath).then(r => r.json()); + + // parse les .tsx (XML) referencements (firstgid + chemin image par tile id) + const tilesets = []; + for (const ts of map.tilesets) { + const tsxText = await fetch(baseUrl + ts.source).then(r => r.text()); + const xml = new DOMParser().parseFromString(tsxText, 'text/xml'); + const tiles = {}; + for (const tile of xml.querySelectorAll('tile')) { + const id = parseInt(tile.getAttribute('id')); + const img = tile.querySelector('image'); + if (img) { + tiles[id] = { + src: baseUrl + img.getAttribute('source'), + w: parseInt(img.getAttribute('width')), + h: parseInt(img.getAttribute('height')), + }; + } + } + tilesets.push({ firstgid: ts.firstgid, tiles }); + } + + // precharger toutes les textures + const urls = []; + for (const ts of tilesets) { + for (const id in ts.tiles) urls.push(ts.tiles[id].src); + } + await Assets.load(urls); + + return { map, tilesets }; +} + + +function _gidToTile(gid, tilesets) { + // chercher dans le bon tileset (firstgid descendant) + for (let i = tilesets.length - 1; i >= 0; i--) { + if (gid >= tilesets[i].firstgid) { + return tilesets[i].tiles[gid - tilesets[i].firstgid] || null; + } + } + return null; +} + + +export function drawTiledMap(layer, data) { + const { map, tilesets } = data; + const W = map.width; + const H = map.height; + const tw = getTw(); + const th = getTh(); + + // reference : la base (footprint iso) d'un sprite Kenney fait 256x128 px + // au tw courant, on veut que cette base remplisse 1 unite monde (tw x th px) + const REF_TILE_W = 256; + const scale = tw / REF_TILE_W; + + for (const tlayer of map.layers) { + if (tlayer.type !== 'tilelayer') continue; + if (tlayer.visible === false) continue; + + for (let i = 0; i < tlayer.data.length; i++) { + const gid = tlayer.data[i]; + if (gid === 0) continue; // 0 = case vide + + const tile = _gidToTile(gid, tilesets); + if (!tile) continue; + + const col = i % W; + const row = Math.floor(i / W); + // centrer la map sur (0, 0) + const wx = col - (W - 1) / 2; + const wy = row - (H - 1) / 2; + + const tex = Assets.get(tile.src); + if (!tex) continue; + + const sprite = new Sprite(tex); + sprite.anchor.set(0.5, 1.0); // base centree sur la tile, le sprite "monte" + const p = iso(wx, wy); + sprite.x = p.x; + sprite.y = p.y + th / 2; // decalage half pour que la pointe basse de la diamond touche la base + sprite.scale.set(scale); + layer.addChild(sprite); + } + } +} diff --git a/server/__pycache__/boss_ai.cpython-313.pyc b/server/__pycache__/boss_ai.cpython-313.pyc new file mode 100644 index 0000000..3fba631 Binary files /dev/null and b/server/__pycache__/boss_ai.cpython-313.pyc differ diff --git a/server/__pycache__/buff_system.cpython-313.pyc b/server/__pycache__/buff_system.cpython-313.pyc new file mode 100644 index 0000000..6cb05a5 Binary files /dev/null and b/server/__pycache__/buff_system.cpython-313.pyc differ diff --git a/server/__pycache__/combat.cpython-313.pyc b/server/__pycache__/combat.cpython-313.pyc new file mode 100644 index 0000000..973b4a7 Binary files /dev/null and b/server/__pycache__/combat.cpython-313.pyc differ diff --git a/server/__pycache__/constants.cpython-313.pyc b/server/__pycache__/constants.cpython-313.pyc new file mode 100644 index 0000000..05d1a84 Binary files /dev/null and b/server/__pycache__/constants.cpython-313.pyc differ diff --git a/server/__pycache__/displacement.cpython-313.pyc b/server/__pycache__/displacement.cpython-313.pyc new file mode 100644 index 0000000..df51310 Binary files /dev/null and b/server/__pycache__/displacement.cpython-313.pyc differ diff --git a/server/__pycache__/game_loop.cpython-313.pyc b/server/__pycache__/game_loop.cpython-313.pyc new file mode 100644 index 0000000..03244ca Binary files /dev/null and b/server/__pycache__/game_loop.cpython-313.pyc differ diff --git a/server/__pycache__/game_state.cpython-313.pyc b/server/__pycache__/game_state.cpython-313.pyc new file mode 100644 index 0000000..e245f42 Binary files /dev/null and b/server/__pycache__/game_state.cpython-313.pyc differ diff --git a/server/__pycache__/lobby.cpython-313.pyc b/server/__pycache__/lobby.cpython-313.pyc new file mode 100644 index 0000000..24064b7 Binary files /dev/null and b/server/__pycache__/lobby.cpython-313.pyc differ diff --git a/server/__pycache__/main.cpython-313.pyc b/server/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..0e3f582 Binary files /dev/null and b/server/__pycache__/main.cpython-313.pyc differ diff --git a/server/__pycache__/stats.cpython-313.pyc b/server/__pycache__/stats.cpython-313.pyc new file mode 100644 index 0000000..1ffd864 Binary files /dev/null and b/server/__pycache__/stats.cpython-313.pyc differ diff --git a/server/__pycache__/waves.cpython-313.pyc b/server/__pycache__/waves.cpython-313.pyc new file mode 100644 index 0000000..8356db0 Binary files /dev/null and b/server/__pycache__/waves.cpython-313.pyc differ diff --git a/server/__pycache__/websocket.cpython-313.pyc b/server/__pycache__/websocket.cpython-313.pyc new file mode 100644 index 0000000..cf59411 Binary files /dev/null and b/server/__pycache__/websocket.cpython-313.pyc differ diff --git a/server/boss_ai.py b/server/boss_ai.py new file mode 100644 index 0000000..a33405e --- /dev/null +++ b/server/boss_ai.py @@ -0,0 +1,254 @@ +# boss_ai.py : IA des boss Vexaris et Morveth +# l'etat interne du boss est stocke dans enemy.data (pas broadcast au client) + +import logging + +from buff_system import player_is_immune +from combat import circle_overlap, normalize +from constants import ( + ARENA_HEIGHT, ARENA_WIDTH, + ENEMY_ATTACK_COOLDOWN, + MORVETH_BURST_COOLDOWN, MORVETH_BURST_COUNT, + MORVETH_CHARGE_COOLDOWN, MORVETH_CHARGE_DURATION, MORVETH_CHARGE_SPEED, + MORVETH_HITBOX_RADIUS, MORVETH_MELEE_DAMAGE, MORVETH_SG_DAMAGE, + MORVETH_SPEED_P1, MORVETH_SPEED_P2, MORVETH_SPEED_P3, + PLAYER_HITBOX_RADIUS, + SOULGATE_HITBOX_RADIUS, SOULGATE_X, SOULGATE_Y, + TICK_DURATION, + VEXARIS_BURST_COOLDOWN, VEXARIS_BURST_COUNT, + VEXARIS_CHARGE_COOLDOWN, VEXARIS_CHARGE_DURATION, VEXARIS_CHARGE_SPEED, + VEXARIS_HITBOX_RADIUS, VEXARIS_MELEE_DAMAGE, VEXARIS_SG_DAMAGE, + VEXARIS_SPEED_P1, VEXARIS_SPEED_P2, +) + +logger = logging.getLogger(__name__) + + +def nearest_player(ex, ey, players): + # joueur le plus proche de (ex, ey), None si liste vide + best = None + best_dist = float("inf") + for p in players: + d = (p.x - ex) ** 2 + (p.y - ey) ** 2 # carre, evite math.sqrt + if d < best_dist: + best_dist = d + best = p + return best + + +def update_boss_ai(enemy, state_players, state_soulgate, pending_spawns): + """dispatch vers la bonne IA selon le type de boss. + + pending_spawns = liste de (type_ennemi, x, y) à créer. + On n'appelle pas spawn_enemy() directement depuis ici pour éviter de modifier + la liste d'ennemis pendant qu'on l'itère dans game_loop. + """ + alive = [p for p in state_players if p.alive] # uniquement les joueurs vivants + + if enemy.type == "morveth": + _update_morveth(enemy, alive, state_soulgate, pending_spawns) + else: + # Vexaris ou Général (les généraux ont une IA simplifiée du même type) + _update_vexaris(enemy, alive, state_soulgate, pending_spawns) + + +# Vexaris + +def _update_vexaris(enemy, alive_players, soulgate, pending_spawns): + # 2 phases (>50% / <=50%), charge sur joueur, burst d'eclats en phase 2 + phase2 = enemy.hp <= enemy.max_hp // 2 + speed = VEXARIS_SPEED_P2 if phase2 else VEXARIS_SPEED_P1 + d = enemy.data + + _init_vexaris_data(d) + _tick_boss_cooldowns(enemy, d, phase2, VEXARIS_BURST_COOLDOWN) + _check_soulgate_contact(enemy, soulgate, VEXARIS_HITBOX_RADIUS, VEXARIS_SG_DAMAGE) + _vexaris_burst(d, enemy, phase2, pending_spawns) + + if d["is_charging"]: + _boss_charge_move(enemy, d, alive_players, VEXARIS_CHARGE_SPEED, VEXARIS_HITBOX_RADIUS, VEXARIS_MELEE_DAMAGE * 2) + elif d["charge_cd"] <= 0 and alive_players: + _start_charge(d, enemy, alive_players, VEXARIS_CHARGE_DURATION, VEXARIS_CHARGE_COOLDOWN, "Vexaris") + else: + _boss_normal_move(enemy, alive_players, speed, VEXARIS_HITBOX_RADIUS, VEXARIS_MELEE_DAMAGE) + + +def _init_vexaris_data(d): + if "charge_cd" in d: + return + d.update({ + "charge_cd": VEXARIS_CHARGE_COOLDOWN, + "charge_timer": 0.0, + "charge_tx": 0.0, "charge_ty": 0.0, + "is_charging": False, + "burst_cd": VEXARIS_BURST_COOLDOWN, + }) + + +def _vexaris_burst(d, enemy, phase2, pending): + """Spawn un burst d'éclats autour de Vexaris (seulement en phase 2, si CD = 0).""" + if not phase2 or d["burst_cd"] > 0: + return # pas en phase 2, ou burst en cooldown → on ne fait rien + + # Spawner VEXARIS_BURST_COUNT éclats à la position du boss + for _ in range(VEXARIS_BURST_COUNT): + pending.append(("eclat", enemy.x, enemy.y)) # ("type", x, y) — game_loop les crée après + + d["burst_cd"] = VEXARIS_BURST_COOLDOWN # réinitialiser le cooldown du burst + logger.debug("Vexaris burst : %d eclats", VEXARIS_BURST_COUNT) + + +# Morveth + +def _update_morveth(enemy, alive_players, soulgate, pending_spawns): + # 3 phases : <75% (p2), <25% (p3), invoque des generaux a 75/50/25 + hp_pct = enemy.hp / enemy.max_hp + phase3 = hp_pct <= 0.25 + phase2 = hp_pct <= 0.75 + + speed = MORVETH_SPEED_P3 if phase3 else (MORVETH_SPEED_P2 if phase2 else MORVETH_SPEED_P1) + d = enemy.data + + _init_morveth_data(d) + _tick_boss_cooldowns(enemy, d, phase2, MORVETH_BURST_COOLDOWN) + _morveth_spawn_generals(d, hp_pct, enemy, pending_spawns) # invoquer généraux aux seuils HP + _check_soulgate_contact(enemy, soulgate, MORVETH_HITBOX_RADIUS, MORVETH_SG_DAMAGE) + _morveth_burst(d, enemy, phase2, phase3, pending_spawns) + + # En phase 3, le cooldown de charge est divisé par 2 → il charge 2× plus souvent + charge_reset = MORVETH_CHARGE_COOLDOWN / 2 if phase3 else MORVETH_CHARGE_COOLDOWN + + if d["is_charging"]: + _boss_charge_move(enemy, d, alive_players, MORVETH_CHARGE_SPEED, MORVETH_HITBOX_RADIUS, MORVETH_MELEE_DAMAGE * 2) + elif d["charge_cd"] <= 0 and alive_players: + _start_charge(d, enemy, alive_players, MORVETH_CHARGE_DURATION, charge_reset, "Morveth") + else: + _boss_normal_move(enemy, alive_players, speed, MORVETH_HITBOX_RADIUS, MORVETH_MELEE_DAMAGE) + + +def _init_morveth_data(d): + # init des champs internes de Morveth, fait 1 seule fois + if "charge_cd" in d: + return + d.update({ + "charge_cd": MORVETH_CHARGE_COOLDOWN, + "charge_timer": 0.0, + "charge_tx": 0.0, "charge_ty": 0.0, + "is_charging": False, + "burst_cd": MORVETH_BURST_COOLDOWN, + "general_1_spawned": False, # généraux déjà invoqués ? (évite de les respawner) + "general_2_spawned": False, + "general_3_spawned": False, + }) + + +def _morveth_spawn_generals(d, hp_pct, enemy, pending): + """Invoque un Général à chaque seuil HP (une seule fois par seuil). + + hp_pct = pourcentage de HP de Morveth (0.0 à 1.0). + Les seuils : 75%, 50%, 25%. + Le flag (ex: "general_1_spawned") empêche de respawner si Morveth repasse sous le seuil. + """ + thresholds = [ + # (seuil HP, flag dans d, offset X spawn, offset Y spawn) + (0.75, "general_1_spawned", -3, 0), # général 1 à gauche de Morveth + (0.50, "general_2_spawned", 3, 0), # général 2 à droite + (0.25, "general_3_spawned", 0, 2), # général 3 devant + ] + for pct, flag, dx, dy in thresholds: + if hp_pct <= pct and not d[flag]: # seuil atteint ET pas encore spawné + pending.append(("general", enemy.x + dx, enemy.y + dy)) # ajouter à la file + d[flag] = True # marquer comme spawné pour ne pas recommencer + logger.info("Morveth invoque General (%s)", flag) + + +def _morveth_burst(d, enemy, phase2, phase3, pending): + """Burst d'éclats de Morveth. En phase 3, le cooldown est divisé par 2.""" + if not phase2 or d["burst_cd"] > 0: + return + + for _ in range(MORVETH_BURST_COUNT): + pending.append(("eclat", enemy.x, enemy.y)) + + # Phase 3 = rage → burst 2× plus fréquent + d["burst_cd"] = MORVETH_BURST_COOLDOWN / 2 if phase3 else MORVETH_BURST_COOLDOWN + logger.debug("Morveth burst : %d eclats", MORVETH_BURST_COUNT) + + +# helpers partages Vexaris/Morveth + +def _tick_boss_cooldowns(enemy, d, phase2, burst_cd_const): + if enemy.attack_cooldown > 0: + enemy.attack_cooldown = max(0.0, enemy.attack_cooldown - TICK_DURATION) + d["charge_cd"] = max(0.0, d["charge_cd"] - TICK_DURATION) + if phase2: + d["burst_cd"] = max(0.0, d["burst_cd"] - TICK_DURATION) + + +def _check_soulgate_contact(enemy, soulgate, hitbox, damage): + sg_x, sg_y = float(SOULGATE_X), float(SOULGATE_Y) + if not circle_overlap(enemy.x, enemy.y, hitbox, sg_x, sg_y, float(SOULGATE_HITBOX_RADIUS)): + return + if enemy.attack_cooldown > 0: + return + soulgate.hp = max(0, soulgate.hp - damage) + enemy.attack_cooldown = ENEMY_ATTACK_COOLDOWN + + +def _start_charge(d, enemy, alive, duration, cooldown, name): + # la cible est fixee au declenchement (le joueur peut bouger pendant la charge) + target = nearest_player(enemy.x, enemy.y, alive) + if target is None: + return + d["is_charging"] = True + d["charge_timer"] = duration + d["charge_tx"] = target.x + d["charge_ty"] = target.y + d["charge_cd"] = cooldown + logger.debug("%s charge vers %s", name, target.username) + + +def _boss_charge_move(enemy, d, alive_players, charge_speed, hitbox, charge_damage): + d["charge_timer"] -= TICK_DURATION + if d["charge_timer"] <= 0.0: + d["is_charging"] = False + return + + # direction vers la cible figee au declenchement (le joueur peut bouger pendant la charge) + dx, dy = normalize(d["charge_tx"] - enemy.x, d["charge_ty"] - enemy.y) + + if dx != 0.0 or dy != 0.0: + half_w, half_h = ARENA_WIDTH / 2, ARENA_HEIGHT / 2 + enemy.x = max(-half_w, min(half_w, enemy.x + dx * charge_speed * TICK_DURATION)) + enemy.y = max(-half_h, min(half_h, enemy.y + dy * charge_speed * TICK_DURATION)) + + # check collision avec chaque joueur vivant non immunise + for player in alive_players: + if not circle_overlap(enemy.x, enemy.y, hitbox, player.x, player.y, float(PLAYER_HITBOX_RADIUS)): + continue + if player.alive and not player_is_immune(player): + player.hp = max(0, player.hp - charge_damage) + if player.hp <= 0: + player.alive = False + + +def _boss_normal_move(enemy, alive_players, speed, hitbox, melee_damage): + if not alive_players: + return + target = nearest_player(enemy.x, enemy.y, alive_players) + if target is None: + return + + dx, dy = normalize(target.x - enemy.x, target.y - enemy.y) + enemy.x += dx * speed * TICK_DURATION + enemy.y += dy * speed * TICK_DURATION + + if not circle_overlap(enemy.x, enemy.y, hitbox, target.x, target.y, float(PLAYER_HITBOX_RADIUS)): + return + if enemy.attack_cooldown > 0 or player_is_immune(target): + return + + target.hp = max(0, target.hp - melee_damage) + if target.hp <= 0: + target.alive = False + enemy.attack_cooldown = ENEMY_ATTACK_COOLDOWN diff --git a/server/buff_system.py b/server/buff_system.py new file mode 100644 index 0000000..9499608 --- /dev/null +++ b/server/buff_system.py @@ -0,0 +1,54 @@ +# buff_system.py : buffs joueurs (immunites, vitesse, sorts divins) +# un buff = {"type": "...", "timer": float, ...} dans player.buffs + +import logging + +from constants import ( + ALDRIC_FLYING_SPEED_MULT, + TICK_DURATION, +) + +logger = logging.getLogger(__name__) + + +def player_has_buff(player, buff_type): + return any(b["type"] == buff_type for b in player.buffs) + + +def player_is_immune(player): + """immunise aux degats si invuln/intangible/flying. + + invuln = sort divin Kael, intangible = teleport Seris, flying = vol Aldric + """ + return any(b["type"] in {"invulnerable", "intangible", "flying"} for b in player.buffs) + + +def player_damage_mult(player): + mult = 1.0 + for b in player.buffs: + if b["type"] in ("damage_mult", "combat_buff"): + mult *= b.get("damage_mult", 1.0) + return mult + + +def player_speed_mult(player): + # on prend le max et pas le produit : 2 buffs vitesse en meme temps doivent pas se cumuler a fond + mult = 1.0 + for b in player.buffs: + if b["type"] == "flying": + mult = max(mult, ALDRIC_FLYING_SPEED_MULT) + elif b["type"] in ("speed_mult", "combat_buff"): + mult = max(mult, b.get("speed_mult", 1.0)) + return mult + + +def update_buffs(state): + # decremente les timers de tous les buffs, vire les expires + for player in state.players: + kept = [] + for buff in player.buffs: + buff["timer"] = max(0.0, buff["timer"] - TICK_DURATION) + if buff["timer"] > 0: + kept.append(buff) + # TODO si on rajoute des effets en fin de buff, c'est ici + player.buffs = kept diff --git a/server/combat.py b/server/combat.py new file mode 100644 index 0000000..02b9bc3 --- /dev/null +++ b/server/combat.py @@ -0,0 +1,34 @@ +# combat.py : maths pures (hitbox, normalisation, distance segment) + +import math + + +def circle_overlap(ax, ay, ar, bx, by, br): + # comparer les carres evite math.sqrt + dx = ax - bx + dy = ay - by + dist_sq = dx * dx + dy * dy + radii = ar + br + return dist_sq <= radii * radii + + +def normalize(dx, dy): + mag_sq = dx * dx + dy * dy + if mag_sq == 0.0: + return 0.0, 0.0 + mag = math.sqrt(mag_sq) + return dx / mag, dy / mag + + +def segment_point_dist(px, py, ax, ay, bx, by): + # distance min entre le point P et le segment [AB], utilise pour la charge de Kael + dx, dy = bx - ax, by - ay + + if dx == 0.0 and dy == 0.0: + # A == B, le segment est un point + return math.sqrt((px - ax) ** 2 + (py - ay) ** 2) + + # projection de P sur AB, clamp entre 0 et 1 + t = max(0.0, min(1.0, ((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy))) + cx, cy = ax + t * dx, ay + t * dy + return math.sqrt((px - cx) ** 2 + (py - cy) ** 2) diff --git a/server/constants.py b/server/constants.py new file mode 100644 index 0000000..528f524 --- /dev/null +++ b/server/constants.py @@ -0,0 +1,220 @@ +# Constantes gameplay de SOULGATE — toutes les valeurs en un seul endroit + +# --- Arène --- +ARENA_WIDTH: float = 35.0 # world units, X : -17.5 à +17.5 +ARENA_HEIGHT: float = 35.0 # world units, Y : -17.5 à +17.5 + +# Positions clés (recalees pour la nouvelle taille 35x35) +SOULGATE_X: float = 0.0 +SOULGATE_Y: float = -15.0 +PLAYER_SPAWN_X: float = 0.0 +PLAYER_SPAWN_Y: float = -10.0 +ENEMY_MAIN_SPAWN_X: float = 0.0 +ENEMY_MAIN_SPAWN_Y: float = 15.0 + +# --- Game loop --- +TICK_RATE: int = 20 # ticks par seconde +TICK_DURATION: float = 0.05 # secondes par tick (1 / TICK_RATE) + +# --- Vagues --- +PREPARATION_DURATION: float = 20.0 # secondes de préparation entre les vagues + +# --- Soulgate --- +SOULGATE_MAX_HP: int = 100 + +# --- Reconnexion --- +RECONNECT_TIMEOUT: int = 30 # secondes avant passage en IA basique + +# --- Joueurs --- +# Vitesse de déplacement (world units / seconde) +PLAYER_SPEED: float = 10.0 + +# HP max par classe +PLAYER_MAX_HP: dict[str, int] = { + "kael": 150, # Tank + "seris": 80, # Mage DPS (fragile) + "aldric": 100, # Support +} + +# --- Combat --- +# Cooldown entre deux attaques basiques (secondes) +ATTACK_COOLDOWN: float = 0.5 + +# Vitesse des projectiles par classe (world units / seconde) +PROJECTILE_SPEED: dict[str, float] = { + "kael": 8.0, # lent — portée courte (pseudo-mêlée) + "seris": 20.0, # rapide — longue portée + "aldric": 12.0, # moyen — portée moyenne +} + +# Dégâts de l'attaque basique par classe +PROJECTILE_DAMAGE: dict[str, int] = { + "kael": 30, + "seris": 15, + "aldric": 10, +} + +# Durée de vie des projectiles (secondes) — détermine la portée effective +PROJECTILE_TTL: dict[str, float] = { + "kael": 0.3, # ~2.4 wu (mêlée) + "seris": 1.5, # ~30 wu (longue portée) + "aldric": 0.8, # ~9.6 wu +} + +# --- Attaque mêlée de Kael (remplace le projectile) --- +KAEL_MELEE_RADIUS: float = 3.0 # portée du slash en world units +KAEL_MELEE_DAMAGE: int = 40 # dégâts par coup (un peu plus qu'avant car pas de portée) + +# --- Compétences actives de Kael --- +KAEL_SLAM_RADIUS: float = 3.5 # rayon du slam AOE (skill 2 — frappe lourde, cercle complet) +KAEL_SLAM_TICKS: int = 8 # durée de la zone en ticks (8 × 50ms = 0.4s) +KAEL_SLAM_DMG_PER_TICK: int = 8 # dégâts par tick (8 × 8 = 64 total max) +KAEL_STORM_RADIUS: float = 5.5 # rayon de la tempête (skill 3 — AOE large autour de Kael) +KAEL_STORM_TICKS: int = 20 # durée de la zone en ticks (20 × 50ms = 1.0s) +KAEL_STORM_DMG_PER_TICK: int = 5 # dégâts par tick (5 × 20 = 100 total max) +KAEL_SHIELD_DURATION: float = 3.0 # durée du bouclier (skill 1 — invulnérabilité en secondes) + +# Rayons des hitboxes (world units) +PROJECTILE_RADIUS: float = 0.4 +PLAYER_HITBOX_RADIUS: float = 0.5 +ENEMY_HITBOX_RADIUS: float = 0.5 + +# --- Boss Vexaris (vague 2) --- +VEXARIS_HP: int = 600 +VEXARIS_HITBOX_RADIUS: float = 1.2 +VEXARIS_SPEED_P1: float = 3.5 # wu/s — phase 1 (>50 % PV) +VEXARIS_SPEED_P2: float = 5.5 # wu/s — phase 2 (≤50 % PV) +VEXARIS_MELEE_DAMAGE: int = 35 +VEXARIS_SG_DAMAGE: int = 40 +VEXARIS_CHARGE_COOLDOWN: float = 7.0 # s entre deux charges +VEXARIS_CHARGE_SPEED: float = 20.0 # wu/s pendant la charge +VEXARIS_CHARGE_DURATION: float = 0.5 # s de durée de charge +VEXARIS_BURST_COOLDOWN: float = 10.0 # s entre deux bursts (phase 2) +VEXARIS_BURST_COUNT: int = 3 # éclats par burst + +# --- Boss Morveth (vague 3) --- +MORVETH_HP: int = 1000 +MORVETH_HITBOX_RADIUS: float = 1.5 +MORVETH_SPEED_P1: float = 2.5 # wu/s — phase 1 (>75 % PV) +MORVETH_SPEED_P2: float = 4.0 # wu/s — phase 2 (25–75 % PV) +MORVETH_SPEED_P3: float = 6.5 # wu/s — phase 3 enragé (≤25 % PV) +MORVETH_MELEE_DAMAGE: int = 45 +MORVETH_SG_DAMAGE: int = 50 +MORVETH_CHARGE_COOLDOWN: float = 9.0 +MORVETH_CHARGE_SPEED: float = 22.0 +MORVETH_CHARGE_DURATION: float = 0.6 +MORVETH_BURST_COOLDOWN: float = 12.0 # s entre deux bursts (phase 2+) +MORVETH_BURST_COUNT: int = 4 + +# Généraux (sub-boss invoqués par Morveth) +GENERAL_HP: int = 150 +GENERAL_HITBOX_RADIUS: float = 0.8 +GENERAL_SPEED: float = 4.5 +GENERAL_MELEE_DAMAGE: int = 25 +GENERAL_SG_DAMAGE: int = 15 + +# Bosses par vague (numéro 1-indexed → type) +WAVE_BOSSES: dict[int, str] = {2: "vexaris", 3: "morveth"} + +# --- Ennemis --- +# Stats par type : hp, vitesse (wu/s), dégâts au joueur, dégâts au Soulgate, rayon hitbox +ENEMY_STATS: dict[str, dict] = { + "fracture": {"hp": 40, "speed": 4.0, "damage": 10, "sg_damage": 0, "radius": 0.5}, + "rampant": {"hp": 20, "speed": 7.0, "damage": 5, "sg_damage": 5, "radius": 0.4}, + "colosse": {"hp": 200, "speed": 2.0, "damage": 20, "sg_damage": 20, "radius": 1.0}, + "eclat": {"hp": 10, "speed": 8.0, "damage": 8, "sg_damage": 2, "radius": 0.3}, + # Boss — valeurs doivent correspondre aux constantes VEXARIS_* / MORVETH_* ci-dessus + "vexaris": {"hp": 600, "speed": 3.5, "damage": 35, "sg_damage": 40, "radius": 1.2}, + "morveth": {"hp": 1000, "speed": 2.5, "damage": 45, "sg_damage": 50, "radius": 1.5}, + "general": {"hp": 150, "speed": 4.5, "damage": 25, "sg_damage": 15, "radius": 0.8}, +} + +# Intervalle minimum entre deux frappes du même ennemi sur la même cible (secondes) +ENEMY_ATTACK_COOLDOWN: float = 1.0 + +# Rayon de la hitbox du Soulgate (wu) — correspond approximativement au pad visuel +SOULGATE_HITBOX_RADIUS: float = 1.0 + +# --- Cooldowns des compétences --- +# Cooldowns des compétences par classe (ability_1, ability_2, ability_3) en secondes +ABILITY_COOLDOWNS: dict[str, list[float]] = { + "kael": [7.0, 12.0], # skill 1 = slam, skill 2 = tempête + "seris": [6.0, 10.0], # skill 1 : éventail, skill 2 : vide (ratio ×1.67) + "aldric": [8.0, 15.0], # à définir lors de l'implémentation d'Aldric +} + +# --- Positions de spawn --- +# Positions de spawn initiales des 3 joueurs (devant le Soulgate, légèrement décalés) +PLAYER_SPAWN_POSITIONS: list[tuple[float, float]] = [ + (0.0, -10.0), # joueur 1 — centre + (-2.0, -9.0), # joueur 2 — gauche + (2.0, -9.0), # joueur 3 — droite +] + +# --- Déplacements spéciaux (touche E) --- +DISPLACEMENT_COOLDOWNS: dict[str, float] = { + "kael": 9.0, # augmenté (dash puissant même court) + "seris": 4.0, + "aldric": 14.0, +} +KAEL_CHARGE_DISTANCE: float = 2.5 # wu parcourus +KAEL_CHARGE_DAMAGE: int = 20 # dégâts aux ennemis traversés +KAEL_CHARGE_HIT_RADIUS: float = 1.2 # rayon de détection autour du segment +SERIS_INTANGIBLE_DURATION: float = 0.3 # s intangible post-téléport + +# --- Compétences actives de Seris --- +SERIS_FAN_COUNT: int = 3 # nombre de dagues dans l'éventail (skill 1) +SERIS_FAN_SPREAD: float = 0.35 # demi-angle du cône en radians (~20°) +SERIS_FAN_DAMAGE: int = 12 # dégâts par dague (3 × 12 = 36 max) +SERIS_VOID_RADIUS: float = 4.0 # rayon explosion vide (skill 2) +SERIS_VOID_TICKS: int = 10 # durée en ticks (10 × 50ms = 0.5s) +SERIS_VOID_DMG_PER_TICK: int = 8 # dégâts/tick (8 × 10 = 80 total max) +ALDRIC_FLYING_DURATION: float = 4.0 # s de vol +ALDRIC_FLYING_SPEED_MULT: float = 1.6 # vitesse ×1,6 en vol + +# --- Compétences actives d'Aldric --- +ALDRIC_HEAL_RADIUS: float = 6.0 # rayon du halo de soin (skill 1) +ALDRIC_HEAL_AMOUNT: int = 25 # PV soignés par utilisation +ALDRIC_PULSE_RADIUS: float = 5.0 # rayon de la vague d'énergie (skill 2) +ALDRIC_PULSE_TICKS: int = 8 # durée en ticks (8 × 50ms = 0.4s) +ALDRIC_PULSE_DMG_PER_TICK: int = 8 # dégâts/tick (8 × 8 = 64 total max) + +# --- Sorts Divins (touche R) --- +DIVINE_POST_USE_COOLDOWN: float = 60.0 # cooldown entre 2 utilisations (évite spam) +KAEL_DIVINE_DURATION: float = 8.0 +KAEL_DIVINE_DAMAGE_MULT: float = 3.0 +KAEL_DIVINE_SG_HEAL_PER_KILL: float = 0.01 # 1 % HP max par kill +SERIS_DIVINE_CHANNEL: float = 3.0 # s de canalisation +SERIS_DIVINE_FREEZE: float = 6.0 # s de gel des ennemis +SERIS_DIVINE_DPS: int = 15 # dégâts/s aux ennemis gelés +ALDRIC_DIVINE_CHANNEL: float = 2.0 # s de canalisation +ALDRIC_DIVINE_BUFF_DURATION: float = 10.0 +ALDRIC_DIVINE_DAMAGE_MULT: float = 1.3 +ALDRIC_DIVINE_SPEED_MULT: float = 1.3 +ALDRIC_DIVINE_SG_HEAL: float = 0.10 # 10 % HP max Soulgate + +# --- Upgrades --- +# Âmes gagnées par kill selon le type d'ennemi +SOUL_REWARDS: dict[str, int] = { + "fracture": 10, + "rampant": 8, + "colosse": 50, + "eclat": 5, + "vexaris": 200, + "general": 75, + "morveth": 300, +} + +# Catalogue d'upgrades disponibles pendant la phase de préparation +UPGRADE_CATALOG: dict[str, dict] = { + "damage_up": {"name": "Frappe +", "description": "+25% dégâts projectile", "cost": 30, "max_stacks": 2}, + "cooldown_down": {"name": "Cadence +", "description": "-20% cooldown d'attaque", "cost": 25, "max_stacks": 2}, + "hp_up": {"name": "Vitalité +", "description": "+30 PV max", "cost": 35, "max_stacks": 2}, + "speed_up": {"name": "Vitesse +", "description": "+15% vitesse de déplacement", "cost": 20, "max_stacks": 3}, +} + +# --- Leaderboard / Score --- +SCORE_PER_KILL = 10 # points par ennemi tué +SCORE_PER_SOUL = 2 # points par âme collectée +SCORE_VICTORY = 1000 # bonus de victoire +SCORE_PER_WAVE = 200 # points par vague complétée diff --git a/server/displacement.py b/server/displacement.py new file mode 100644 index 0000000..ca7f1dc --- /dev/null +++ b/server/displacement.py @@ -0,0 +1,71 @@ +# displacement.py : touche E (charge Kael / teleport Seris / vol Aldric) + +import logging + +from buff_system import player_has_buff +from combat import normalize, segment_point_dist +from constants import ( + ALDRIC_FLYING_DURATION, + ARENA_HEIGHT, ARENA_WIDTH, + DISPLACEMENT_COOLDOWNS, + KAEL_CHARGE_DAMAGE, KAEL_CHARGE_DISTANCE, KAEL_CHARGE_HIT_RADIUS, + SERIS_INTANGIBLE_DURATION, + SOUL_REWARDS, +) + +logger = logging.getLogger(__name__) + + +def handle_displacement(player, tx, ty, state): + if not player.alive or player.cooldowns.get("displacement", 0) > 0: + return + if player_has_buff(player, "casting"): + return # canalisation sort divin = on bloque le E + + cls = player.class_name + if cls == "kael": + _kael_charge(player, tx, ty, state) + elif cls == "seris": + _seris_teleport(player, tx, ty) + elif cls == "aldric": + _aldric_fly(player) + + player.cooldowns["displacement"] = DISPLACEMENT_COOLDOWNS[cls] + + +def _kael_charge(player, tx, ty, state): + # charge en ligne droite, frappe les ennemis dans le couloir + dx, dy = normalize(tx - player.x, ty - player.y) + if dx == 0.0 and dy == 0.0: + return + + half_w, half_h = ARENA_WIDTH / 2, ARENA_HEIGHT / 2 + end_x = max(-half_w, min(half_w, player.x + dx * KAEL_CHARGE_DISTANCE)) + end_y = max(-half_h, min(half_h, player.y + dy * KAEL_CHARGE_DISTANCE)) + + dead_ids = set() + for enemy in state.enemies: + dist = segment_point_dist(enemy.x, enemy.y, player.x, player.y, end_x, end_y) + if dist >= KAEL_CHARGE_HIT_RADIUS: + continue + enemy.hp = max(0, enemy.hp - KAEL_CHARGE_DAMAGE) + if enemy.hp <= 0: + dead_ids.add(enemy.id) + player.souls += SOUL_REWARDS.get(enemy.type, 0) + + if dead_ids: + state.enemies = [e for e in state.enemies if e.id not in dead_ids] + + player.x, player.y = end_x, end_y + + +def _seris_teleport(player, tx, ty): + half_w, half_h = ARENA_WIDTH / 2, ARENA_HEIGHT / 2 + player.x = max(-half_w, min(half_w, tx)) + player.y = max(-half_h, min(half_h, ty)) + # immunite breve pendant le teleport (sinon on peut tomber sur un boss et perdre direct) + player.buffs.append({"type": "intangible", "timer": SERIS_INTANGIBLE_DURATION}) + + +def _aldric_fly(player): + player.buffs.append({"type": "flying", "timer": ALDRIC_FLYING_DURATION}) diff --git a/server/game_loop.py b/server/game_loop.py new file mode 100644 index 0000000..b6a6e61 --- /dev/null +++ b/server/game_loop.py @@ -0,0 +1,557 @@ +# game_loop.py : boucle 20Hz du serveur (mouvement, projectiles, ennemis, vagues, broadcast) + +import asyncio +import logging +import time + +from boss_ai import nearest_player, update_boss_ai +from buff_system import player_damage_mult, player_has_buff, player_is_immune, player_speed_mult, update_buffs +from combat import circle_overlap, normalize +from displacement import handle_displacement +from waves import WaveManager +from constants import ( + KAEL_MELEE_RADIUS, KAEL_MELEE_DAMAGE, + KAEL_SLAM_RADIUS, KAEL_SLAM_TICKS, KAEL_SLAM_DMG_PER_TICK, + KAEL_STORM_RADIUS, KAEL_STORM_TICKS, KAEL_STORM_DMG_PER_TICK, + SERIS_FAN_COUNT, SERIS_FAN_SPREAD, SERIS_FAN_DAMAGE, + SERIS_VOID_RADIUS, SERIS_VOID_TICKS, SERIS_VOID_DMG_PER_TICK, + ALDRIC_HEAL_RADIUS, ALDRIC_HEAL_AMOUNT, + ALDRIC_PULSE_RADIUS, ALDRIC_PULSE_TICKS, ALDRIC_PULSE_DMG_PER_TICK, + ABILITY_COOLDOWNS, ARENA_HEIGHT, ARENA_WIDTH, + ATTACK_COOLDOWN, + ENEMY_ATTACK_COOLDOWN, ENEMY_MAIN_SPAWN_X, ENEMY_MAIN_SPAWN_Y, ENEMY_STATS, + PLAYER_HITBOX_RADIUS, PLAYER_MAX_HP, PLAYER_SPAWN_POSITIONS, PLAYER_SPEED, + PROJECTILE_DAMAGE, PROJECTILE_RADIUS, PROJECTILE_SPEED, PROJECTILE_TTL, + SOULGATE_HITBOX_RADIUS, SOULGATE_MAX_HP, SOULGATE_X, SOULGATE_Y, TICK_DURATION, + SOUL_REWARDS, UPGRADE_CATALOG, + VEXARIS_HP, MORVETH_HP, +) +from game_state import AoeZone, EnemyState, GameState, PlayerState, ProjectileState, SoulgateState, WaveState + +logger = logging.getLogger(__name__) + + +class GameLoop: + def __init__(self, lobby, broadcast): + self.lobby = lobby + self._broadcast = broadcast + self.state = self._init_state() + self._running = False + self._inputs = {} # {conn_id: (dx, dy)} dernier input de mouvement + self._proj_counter = 0 + self._enemy_counter = 0 + self._aoe_counter = 0 + self._wave_manager = WaveManager() + self._pending_spawns = [] # spawns au prochain tick (eviter de modif la liste pendant l'iteration) + self._debug_invincible = False + self._debug_one_shot = False + + def _init_state(self): + players = [ + PlayerState( + id=p.conn_id, + class_name=p.player_class, + username=p.username, + x=float(PLAYER_SPAWN_POSITIONS[i][0]), + y=float(PLAYER_SPAWN_POSITIONS[i][1]), + hp=PLAYER_MAX_HP[p.player_class], + max_hp=PLAYER_MAX_HP[p.player_class], + alive=True, + souls=50, # ames de depart pour pouvoir acheter qq upgrades dans la 1ere prep + ) + for i, p in enumerate(self.lobby.players) + ] + return GameState( + tick=0, + players=players, + enemies=[], + projectiles=[], + soulgate=SoulgateState(hp=SOULGATE_MAX_HP, max_hp=SOULGATE_MAX_HP), + wave=WaveState(number=1, phase=0, enemies_remaining=0, state="preparation"), + effects=[], + ) + + def _get_player(self, conn_id): + return next((p for p in self.state.players if p.id == conn_id), None) + + # --- inputs (appeles depuis main.py au reception ws) --- + + def handle_input(self, conn_id, dx, dy): + self._inputs[conn_id] = (dx, dy) + + def handle_attack(self, conn_id, tx, ty): + # melee Kael / projectile Seris+Aldric + player = self._get_player(conn_id) + if not player or not player.alive: + return + if player_has_buff(player, "casting") or player.cooldowns.get("attack", 0) > 0: + return + + dx, dy = normalize(tx - player.x, ty - player.y) + if dx == 0.0 and dy == 0.0: + return + + cls = player.class_name + cd = ATTACK_COOLDOWN * (1 - 0.20 * player.upgrades.get("cooldown_down", 0)) + + if cls == "kael": + self._kael_melee(player, dx, dy) + else: + damage = int(PROJECTILE_DAMAGE[cls] * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + damage = int(damage * player_damage_mult(player)) + self._proj_counter += 1 + self.state.projectiles.append(ProjectileState( + id=f"pr_{self._proj_counter}", + owner_id=conn_id, + x=player.x, y=player.y, + vx=dx * PROJECTILE_SPEED[cls], + vy=dy * PROJECTILE_SPEED[cls], + damage=damage, + radius=PROJECTILE_RADIUS, + ttl=PROJECTILE_TTL[cls], + )) + + player.cooldowns["attack"] = cd + + def _kael_melee(self, player, dx, dy): + # frappe melee Kael, demi-cercle devant + import math + # FIXME le bonus de damage_up je sais pas si c'est bien 25% par stack + damage = int(KAEL_MELEE_DAMAGE * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + damage = int(damage * player_damage_mult(player)) + + dead = set() + for e in self.state.enemies: + ex = e.x - player.x + ey = e.y - player.y + d = math.sqrt(ex*ex + ey*ey) + if d > KAEL_MELEE_RADIUS: + continue + # check qu'on tape devant et pas derriere (produit scalaire) + if d > 0: + if dx * (ex/d) + dy * (ey/d) < 0: + continue + e.hp = max(0, e.hp - damage) + if e.hp <= 0: + dead.add(e.id) + player.souls += SOUL_REWARDS.get(e.type, 10) + + if dead: + self.state.enemies = [x for x in self.state.enemies if x.id not in dead] + + def handle_ability(self, conn_id, ability_id, tx, ty): + # touches 1 et 2 (skill par classe) + player = self._get_player(conn_id) + if not player or not player.alive: + return + key = f"ability_{ability_id}" + if player.cooldowns.get(key, 0) > 0: + return + + cooldown_list = ABILITY_COOLDOWNS.get(player.class_name, [8.0, 12.0]) + idx = ability_id - 1 + if not (0 <= idx < len(cooldown_list)): + return + + cls = player.class_name + if cls == "kael": + if ability_id == 1: self._kael_slam(player) + elif ability_id == 2: self._kael_storm(player) + elif cls == "seris": + if ability_id == 1: self._seris_fan(player, tx, ty) + elif ability_id == 2: self._seris_void(player) + elif cls == "aldric": + if ability_id == 1: self._aldric_heal(player) + elif ability_id == 2: self._aldric_pulse(player) + + player.cooldowns[key] = cooldown_list[idx] + + def _kael_slam(self, player): + self._aoe_counter += 1 + dmg = int(KAEL_SLAM_DMG_PER_TICK * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + dmg = int(dmg * player_damage_mult(player)) + self.state.aoe_zones.append(AoeZone( + id=f"aoe_{self._aoe_counter}", + owner_id=player.id, + zone_type="slam", + x=player.x, y=player.y, + radius=KAEL_SLAM_RADIUS, + damage_per_tick=dmg, + ticks_remaining=KAEL_SLAM_TICKS, + max_ticks=KAEL_SLAM_TICKS, + )) + + def _kael_storm(self, player): + # tempete : grosse zone aoe sur plusieurs ticks + self._aoe_counter += 1 + dmg = int(KAEL_STORM_DMG_PER_TICK * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + dmg = int(dmg * player_damage_mult(player)) + self.state.aoe_zones.append(AoeZone( + id=f"aoe_{self._aoe_counter}", + owner_id=player.id, + zone_type="storm", + x=player.x, y=player.y, + radius=KAEL_STORM_RADIUS, + damage_per_tick=dmg, + ticks_remaining=KAEL_STORM_TICKS, + max_ticks=KAEL_STORM_TICKS, + )) + + def _seris_fan(self, player, tx, ty): + # 3 dagues en eventail vers la souris + import math + dx, dy = normalize(tx - player.x, ty - player.y) + if dx == 0.0 and dy == 0.0: + return + base_angle = math.atan2(dy, dx) + dmg = int(SERIS_FAN_DAMAGE * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + dmg = int(dmg * player_damage_mult(player)) + + for i in range(SERIS_FAN_COUNT): + # repartition symetrique : -spread, 0, +spread (avec count impair) + offset = SERIS_FAN_SPREAD * (i - (SERIS_FAN_COUNT - 1) / 2) + angle = base_angle + offset + vx = math.cos(angle) * PROJECTILE_SPEED["seris"] + vy = math.sin(angle) * PROJECTILE_SPEED["seris"] + self._proj_counter += 1 + self.state.projectiles.append(ProjectileState( + id=f"pr_{self._proj_counter}", + owner_id=player.id, + x=player.x, y=player.y, + vx=vx, vy=vy, + damage=dmg, + radius=PROJECTILE_RADIUS, + ttl=PROJECTILE_TTL["seris"], + )) + + def _seris_void(self, player): + self._aoe_counter += 1 + dmg = int(SERIS_VOID_DMG_PER_TICK * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + dmg = int(dmg * player_damage_mult(player)) + self.state.aoe_zones.append(AoeZone( + id=f"aoe_{self._aoe_counter}", + owner_id=player.id, + zone_type="void", + x=player.x, y=player.y, + radius=SERIS_VOID_RADIUS, + damage_per_tick=dmg, + ticks_remaining=SERIS_VOID_TICKS, + max_ticks=SERIS_VOID_TICKS, + )) + + def _aldric_heal(self, player): + # halo de soin sur tous les joueurs dans le rayon + for t in self.state.players: + if not t.alive: + continue + if circle_overlap(player.x, player.y, ALDRIC_HEAL_RADIUS, t.x, t.y, PLAYER_HITBOX_RADIUS): + t.hp = min(t.hp + ALDRIC_HEAL_AMOUNT, t.max_hp) + + def _aldric_pulse(self, player): + self._aoe_counter += 1 + dmg = int(ALDRIC_PULSE_DMG_PER_TICK * (1 + 0.25 * player.upgrades.get("damage_up", 0))) + dmg = int(dmg * player_damage_mult(player)) + self.state.aoe_zones.append(AoeZone( + id=f"aoe_{self._aoe_counter}", + owner_id=player.id, + zone_type="pulse", + x=player.x, y=player.y, + radius=ALDRIC_PULSE_RADIUS, + damage_per_tick=dmg, + ticks_remaining=ALDRIC_PULSE_TICKS, + max_ticks=ALDRIC_PULSE_TICKS, + )) + + def _process_aoe_zones(self): + import math + if not self.state.aoe_zones: + return + + dead = set() + owner_map = {p.id: p for p in self.state.players} + + for zone in self.state.aoe_zones: + owner = owner_map.get(zone.owner_id) + for e in self.state.enemies: + if e.id in dead: + continue + dx = e.x - zone.x + dy = e.y - zone.y + if math.sqrt(dx*dx + dy*dy) <= zone.radius: + e.hp = max(0, e.hp - zone.damage_per_tick) + if e.hp <= 0: + dead.add(e.id) + if owner: + owner.souls += SOUL_REWARDS.get(e.type, 10) + zone.ticks_remaining -= 1 + + if dead: + self.state.enemies = [e for e in self.state.enemies if e.id not in dead] + + self.state.aoe_zones = [z for z in self.state.aoe_zones if z.ticks_remaining > 0] + + def handle_displacement(self, conn_id, tx, ty): + # touche E, on delegue a displacement.py + p = self._get_player(conn_id) + if p: + handle_displacement(p, tx, ty, self.state) + + def spawn_enemy(self, enemy_type): + self._enemy_counter += 1 + eid = f"en_{self._enemy_counter}" + + if enemy_type == "vexaris": + self.state.enemies.append(EnemyState( + id=eid, type="vexaris", x=0.0, y=12.0, + hp=VEXARIS_HP, max_hp=VEXARIS_HP, is_boss=True, + )) + elif enemy_type == "morveth": + # TODO peut etre baisser ses HP si c trop dur + self.state.enemies.append(EnemyState( + id=eid, type="morveth", x=0.0, y=12.0, + hp=MORVETH_HP, max_hp=MORVETH_HP, is_boss=True, + )) + else: + stats = ENEMY_STATS[enemy_type] + self.state.enemies.append(EnemyState( + id=eid, type=enemy_type, + x=float(ENEMY_MAIN_SPAWN_X), y=float(ENEMY_MAIN_SPAWN_Y), + hp=stats["hp"], max_hp=stats["hp"], + )) + + def _spawn_enemy_at(self, enemy_type, x, y): + # spawn a une position precise (pour les generaux de Morveth) + stats = ENEMY_STATS[enemy_type] + self._enemy_counter += 1 + self.state.enemies.append(EnemyState( + id=f"en_{self._enemy_counter}", type=enemy_type, + x=x, y=y, hp=stats["hp"], max_hp=stats["hp"], + )) + + def _update_enemies(self): + sg_x, sg_y = float(SOULGATE_X), float(SOULGATE_Y) + alive_players = [p for p in self.state.players if p.alive] + surviving = [] + + for enemy in self.state.enemies: + + if enemy.frozen_timer > 0: + # Ennemi gelé (sort divin Seris) → il ne bouge pas ce tick + surviving.append(enemy) + continue + + if enemy.is_boss: + # Boss → IA spéciale dans boss_ai.py + update_boss_ai(enemy, self.state.players, self.state.soulgate, self._pending_spawns) + surviving.append(enemy) # les boss ne "meurent" pas via keep=False + continue + + # Ennemi normal + keep = self._update_single_enemy(enemy, sg_x, sg_y, alive_players) + if keep: + surviving.append(enemy) # False = ennemi mort (a touché le Soulgate) + + self.state.enemies = surviving # remplacer par la liste filtrée + + # Spawner les ennemis en attente (ex: généraux de Morveth) + for etype, ex, ey in self._pending_spawns: + self._spawn_enemy_at(etype, ex, ey) + self._pending_spawns.clear() # vider la file d'attente + + def _update_single_enemy(self, enemy, sg_x, sg_y, alive_players): + # retourne False si l'ennemi doit etre supprime (mort) + stats = ENEMY_STATS[enemy.type] + speed, e_radius = stats["speed"], stats["radius"] + + if enemy.attack_cooldown > 0: + enemy.attack_cooldown = max(0.0, enemy.attack_cooldown - TICK_DURATION) + + if stats["sg_damage"] > 0: + # vise le Soulgate (ex fracture) + return self._enemy_target_soulgate(enemy, sg_x, sg_y, e_radius, stats) + # vise les joueurs (rampant, eclat) + return self._enemy_target_player(enemy, alive_players, e_radius, speed, stats) + + def _enemy_target_soulgate(self, enemy, sg_x, sg_y, e_radius, stats): + t_radius = float(SOULGATE_HITBOX_RADIUS) + + if circle_overlap(enemy.x, enemy.y, e_radius, sg_x, sg_y, t_radius): + if enemy.attack_cooldown <= 0: + self.state.soulgate.hp = max(0, self.state.soulgate.hp - stats["sg_damage"]) + return False + else: + dx, dy = normalize(sg_x - enemy.x, sg_y - enemy.y) + enemy.x += dx * stats["speed"] * TICK_DURATION + enemy.y += dy * stats["speed"] * TICK_DURATION + return True + + def _enemy_target_player(self, enemy, alive_players, e_radius, speed, stats): + target = nearest_player(enemy.x, enemy.y, alive_players) + if target is None: + return True + + t_radius = float(PLAYER_HITBOX_RADIUS) + in_contact = circle_overlap(enemy.x, enemy.y, e_radius, target.x, target.y, t_radius) + + if in_contact and enemy.attack_cooldown <= 0: + if not player_is_immune(target): + target.hp = max(0, target.hp - stats["damage"]) + if target.hp <= 0: + target.alive = False + enemy.attack_cooldown = ENEMY_ATTACK_COOLDOWN + + if not in_contact: + dx, dy = normalize(target.x - enemy.x, target.y - enemy.y) + enemy.x += dx * speed * TICK_DURATION + enemy.y += dy * speed * TICK_DURATION + + return True # l'ennemi reste vivant (il ne se consume pas) + + def debug_kill_all(self): + self.state.enemies.clear() + self.state.projectiles.clear() + + def debug_toggle_invincible(self): + self._debug_invincible = not self._debug_invincible + + def debug_toggle_one_shot(self): + self._debug_one_shot = not self._debug_one_shot + + def debug_revive_all(self): + for p in self.state.players: + p.hp = p.max_hp + p.alive = True + + def handle_upgrade(self, conn_id, upgrade_id): + # achat d'upgrade pendant la phase de prep + if self._wave_manager.state != "preparation": + return + p = self._get_player(conn_id) + if not p or upgrade_id not in UPGRADE_CATALOG: + return + + spec = UPGRADE_CATALOG[upgrade_id] + stacks = p.upgrades.get(upgrade_id, 0) + if stacks >= spec["max_stacks"] or p.souls < spec["cost"]: + return + + p.souls -= spec["cost"] + p.upgrades[upgrade_id] = stacks + 1 + + if upgrade_id == "hp_up": + # cas special : augmente les hp max + soigne du delta + p.max_hp += 30 + p.hp = min(p.hp + 30, p.max_hp) + + def _apply_movement(self): + half_w, half_h = ARENA_WIDTH / 2, ARENA_HEIGHT / 2 + + for p in self.state.players: + if not p.alive or player_has_buff(p, "casting"): + continue + dx, dy = self._inputs.get(p.id, (0.0, 0.0)) + if dx == 0.0 and dy == 0.0: + continue + + speed = PLAYER_SPEED * (1 + 0.15 * p.upgrades.get("speed_up", 0)) + speed *= player_speed_mult(p) + + # clamp dans l'arene + p.x = max(-half_w, min(half_w, p.x + dx * speed * TICK_DURATION)) + p.y = max(-half_h, min(half_h, p.y + dy * speed * TICK_DURATION)) + + def _update_projectiles(self): + half_w, half_h = ARENA_WIDTH / 2, ARENA_HEIGHT / 2 + dead_enemy_ids = set() + surviving = [] + + for proj in self.state.projectiles: + # Déplacer le projectile : position += vitesse × durée tick + proj.x += proj.vx * TICK_DURATION + proj.y += proj.vy * TICK_DURATION + proj.ttl -= TICK_DURATION # réduire le temps de vie restant + + # Vérifier si le projectile doit disparaître + if proj.ttl <= 0 or abs(proj.x) > half_w or abs(proj.y) > half_h: + continue # TTL écoulé ou hors arène → ne pas ajouter à surviving + + # Vérifier les collisions avec les ennemis + hit = self._check_proj_hit(proj, dead_enemy_ids) + if not hit: + surviving.append(proj) # pas de collision → projectile continue + + self.state.projectiles = surviving # remplacer par les projectiles encore en vol + + # Supprimer les ennemis tués par des projectiles ce tick + if dead_enemy_ids: + self.state.enemies = [e for e in self.state.enemies if e.id not in dead_enemy_ids] + + def _check_proj_hit(self, proj, dead_ids): + for e in self.state.enemies: + if e.id in dead_ids: + continue + r = ENEMY_STATS[e.type]["radius"] + if not circle_overlap(proj.x, proj.y, proj.radius, e.x, e.y, r): + continue + # collision + e.hp = 0 if self._debug_one_shot else e.hp - proj.damage + if e.hp <= 0: + dead_ids.add(e.id) + owner = self._get_player(proj.owner_id) + if owner: + owner.souls += SOUL_REWARDS.get(e.type, 0) + owner.enemies_killed += 1 + return True + return False + + def _update_cooldowns(self): + for p in self.state.players: + for k, v in p.cooldowns.items(): + if v > 0: + p.cooldowns[k] = max(0.0, v - TICK_DURATION) + + def _update_wave(self): + wm = self._wave_manager + wm.tick(self.spawn_enemy, len(self.state.enemies)) + + self.state.wave.number = wm.wave_number + self.state.wave.phase = wm.phase_number + self.state.wave.state = wm.state + self.state.wave.enemies_remaining = len(self.state.enemies) + wm.enemies_remaining() + self.state.wave.prep_timer = wm.prep_timer_remaining + + boss = next((e for e in self.state.enemies if e.is_boss), None) + self.state.wave.boss_hp = boss.hp if boss else 0 + self.state.wave.boss_max_hp = boss.max_hp if boss else 0 + self.state.wave.boss_name = boss.type if boss else "" + + def _tick(self): + # 1 tick = 1 frame de jeu (20Hz) + self.state.tick += 1 + + self._apply_movement() + self._update_projectiles() + self._process_aoe_zones() + self._update_enemies() + self._update_cooldowns() + update_buffs(self.state) + self._update_wave() + + if self._debug_invincible: + for p in self.state.players: + p.hp = p.max_hp + p.alive = True + + async def run(self): + self._running = True + logger.info("Game loop demarree — lobby %s", self.lobby.code) + while self._running: + start = time.monotonic() + self._tick() + await self._broadcast(self.state.to_dict()) + elapsed = time.monotonic() - start + # si le tick a pris plus de 50ms on rattrape pas, on enchaine + await asyncio.sleep(max(0.0, TICK_DURATION - elapsed)) + + def stop(self): + self._running = False diff --git a/server/game_state.py b/server/game_state.py new file mode 100644 index 0000000..6143620 --- /dev/null +++ b/server/game_state.py @@ -0,0 +1,184 @@ +# game_state.py : structures de donnees broadcastees au client a chaque tick + +from dataclasses import dataclass, field + + +@dataclass +class ProjectileState: + id: str + owner_id: str + x: float + y: float + vx: float + vy: float + damage: int + radius: float + ttl: float + + def to_dict(self): + # vx/vy/damage/ttl : info serveur, pas envoyes au client + return { + "id": self.id, + "owner": self.owner_id, + "x": self.x, + "y": self.y, + "radius": self.radius, + } + + +@dataclass +class EnemyState: + id: str + type: str + x: float + y: float + hp: int + max_hp: int + attack_cooldown: float = 0.0 + is_boss: bool = False + frozen_timer: float = 0.0 # gel du sort divin Seris + data: dict = field(default_factory=dict) # interne au boss (phases, charge), pas broadcast + + def to_dict(self): + return { + "id": self.id, + "type": self.type, + "x": self.x, + "y": self.y, + "hp": self.hp, + "max_hp": self.max_hp, + "is_boss": self.is_boss, + "frozen": self.frozen_timer > 0, + "is_charging": self.data.get("is_charging", False) if self.is_boss else False, + } + + +@dataclass +class PlayerState: + id: str + class_name: str + username: str + x: float + y: float + hp: int + max_hp: int + alive: bool + + revive_progress: float = 0.0 + + cooldowns: dict = field(default_factory=lambda: { + "attack": 0, + "ability_1": 0, + "ability_2": 0, + "displacement": 0, + }) + + # buffs actifs : {"type": "...", "timer": float, ...} + # types : invulnerable, intangible, flying, casting, damage_mult, speed_mult, combat_buff, divine_freeze_dps + buffs: list = field(default_factory=list) + + souls: int = 0 + enemies_killed: int = 0 + + # upgrade_id -> nb stacks (damage_up, cooldown_down, hp_up, speed_up) + upgrades: dict = field(default_factory=dict) + + def to_dict(self): + return { + "id": self.id, + "class": self.class_name, + "username": self.username, + "x": self.x, + "y": self.y, + "hp": self.hp, + "max_hp": self.max_hp, + "alive": self.alive, + "revive_progress": self.revive_progress, + "cooldowns": self.cooldowns, + "buffs": self.buffs, + "souls": self.souls, + "enemies_killed": self.enemies_killed, + "upgrades": self.upgrades, + } + + +@dataclass +class AoeZone: + id: str + owner_id: str + zone_type: str + x: float + y: float + radius: float + damage_per_tick: int + ticks_remaining: int + max_ticks: int + + def to_dict(self): + return { + "id": self.id, + "type": self.zone_type, + "x": self.x, + "y": self.y, + "radius": self.radius, + "ticks": self.ticks_remaining, + "max_ticks": self.max_ticks, + } + + +@dataclass +class SoulgateState: + hp: int + max_hp: int + + def to_dict(self): + return {"hp": self.hp, "max_hp": self.max_hp} + + +@dataclass +class WaveState: + number: int + phase: int + enemies_remaining: int + state: str # combat | preparation | boss | victory + prep_timer: float = 0.0 + boss_hp: int = 0 + boss_max_hp: int = 0 + boss_name: str = "" + + def to_dict(self): + return { + "number": self.number, + "phase": self.phase, + "enemies_remaining": self.enemies_remaining, + "state": self.state, + "prep_timer": self.prep_timer, + "boss_hp": self.boss_hp, + "boss_max_hp": self.boss_max_hp, + "boss_name": self.boss_name, + } + + +@dataclass +class GameState: + tick: int + players: list + enemies: list + projectiles: list + soulgate: SoulgateState + wave: WaveState + effects: list + aoe_zones: list = field(default_factory=list) + + def to_dict(self): + return { + "type": "game_state", + "tick": self.tick, + "players": [p.to_dict() for p in self.players], + "enemies": [e.to_dict() for e in self.enemies], + "projectiles": [p.to_dict() for p in self.projectiles], + "aoe_zones": [z.to_dict() for z in self.aoe_zones], + "soulgate": self.soulgate.to_dict(), + "wave": self.wave.to_dict(), + "effects": self.effects, + } diff --git a/server/lobby.py b/server/lobby.py new file mode 100644 index 0000000..5d19454 --- /dev/null +++ b/server/lobby.py @@ -0,0 +1,206 @@ +# lobby.py : gestion des lobbies (creation, classe, ready, demarrage) +# les methodes retournent (conn_id, message), c'est main.py qui envoie + +import logging +import random +from dataclasses import dataclass, field + +logger = logging.getLogger(__name__) + +# pas de 0/O/I/1 pour eviter les confusions +_LOBBY_CODE_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" +_LOBBY_CODE_LENGTH = 6 +MAX_PLAYERS = 3 +VALID_CLASSES = {"kael", "seris", "aldric"} + + +@dataclass +class LobbyPlayer: + conn_id: str + username: str + player_class: str | None = None + is_ready: bool = False + is_host: bool = False + + def to_dict(self) -> dict: + return { + "id": self.conn_id, + "username": self.username, + "class": self.player_class, + "ready": self.is_ready, + "host": self.is_host, + } + + +@dataclass +class Lobby: + code: str + players: list[LobbyPlayer] = field(default_factory=list) + started: bool = False + + def get_conn_ids(self) -> list[str]: + return [p.conn_id for p in self.players] + + def get_player(self, conn_id): + return next((p for p in self.players if p.conn_id == conn_id), None) + + def is_class_taken(self, class_name): + return any(p.player_class == class_name for p in self.players) + + def all_ready(self): + return ( + len(self.players) == MAX_PLAYERS + and all(p.is_ready and p.player_class for p in self.players) + ) + + +class LobbyManager: + def __init__(self): + self.lobbies: dict[str, Lobby] = {} + self.conn_to_lobby: dict[str, str] = {} + + def _generate_code(self): + while True: + code = "".join(random.choices(_LOBBY_CODE_CHARS, k=_LOBBY_CODE_LENGTH)) + if code not in self.lobbies: + return code + + def _error(self, conn_id, code, message): + return [(conn_id, {"type": "error", "code": code, "message": message})] + + def create_lobby(self, conn_id, username): + # si on est deja dans un lobby on le libere d'abord, evite les blocages au retour menu + leave_msgs = [] + if conn_id in self.conn_to_lobby: + leave_msgs = self.disconnect(conn_id) + + code = self._generate_code() + player = LobbyPlayer(conn_id=conn_id, username=username, is_host=True) + lobby = Lobby(code=code, players=[player]) + + self.lobbies[code] = lobby + self.conn_to_lobby[conn_id] = code + + logger.info("Lobby %s créé par %s", code, username) + return leave_msgs + [(conn_id, {"type": "lobby_created", "code": code})] + + def join_lobby(self, conn_id, username, code): + code = code.upper() + + leave_msgs = [] + if conn_id in self.conn_to_lobby: + leave_msgs = self.disconnect(conn_id) + + lobby = self.lobbies.get(code) + if not lobby: + return self._error(conn_id, "lobby_not_found", f"Lobby {code} introuvable.") + if lobby.started: + return self._error(conn_id, "game_in_progress", "La partie a déjà commencé.") + if len(lobby.players) >= MAX_PLAYERS: + return self._error(conn_id, "lobby_full", "Le lobby est plein (3/3).") + + player = LobbyPlayer(conn_id=conn_id, username=username) + lobby.players.append(player) + self.conn_to_lobby[conn_id] = code + + messages = [ + (conn_id, {"type": "lobby_joined", "code": code, "players": [p.to_dict() for p in lobby.players]}), + ] + # notifier les autres + for p in lobby.players: + if p.conn_id != conn_id: + messages.append((p.conn_id, {"type": "player_joined", "player": player.to_dict()})) + + logger.info("%s a rejoint le lobby %s (%d/3)", username, code, len(lobby.players)) + return leave_msgs + messages + + def select_class(self, conn_id, class_name): + code = self.conn_to_lobby.get(conn_id) + if not code: + return self._error(conn_id, "not_in_lobby", "Tu n'es pas dans un lobby.") + + lobby = self.lobbies[code] + player = lobby.get_player(conn_id) + if not player: + return self._error(conn_id, "not_in_lobby", "Joueur introuvable dans le lobby.") + if class_name not in VALID_CLASSES: + return self._error(conn_id, "invalid_class", f"Classe invalide : {class_name}.") + # un joueur peut reselectionner sa propre classe sans erreur + if lobby.is_class_taken(class_name) and player.player_class != class_name: + return self._error(conn_id, "class_taken", f"La classe {class_name} est déjà prise.") + + player.player_class = class_name + player.is_ready = False # reset du ready si on change de classe + + logger.info("%s a choisi la classe %s", player.username, class_name) + return [(p.conn_id, {"type": "class_selected", "player_id": conn_id, "class": class_name}) for p in lobby.players] + + def set_ready(self, conn_id): + code = self.conn_to_lobby.get(conn_id) + if not code: + return self._error(conn_id, "not_in_lobby", "Tu n'es pas dans un lobby.") + + lobby = self.lobbies[code] + player = lobby.get_player(conn_id) + if not player: + return self._error(conn_id, "not_in_lobby", "Joueur introuvable.") + if not player.player_class: + return self._error(conn_id, "no_class_selected", "Choisis une classe avant de te mettre prêt.") + + player.is_ready = True + logger.info("%s est prêt", player.username) + return [(p.conn_id, {"type": "player_ready", "player_id": conn_id}) for p in lobby.players] + + def start_game(self, conn_id): + code = self.conn_to_lobby.get(conn_id) + if not code: + return self._error(conn_id, "not_in_lobby", "Tu n'es pas dans un lobby.") + + lobby = self.lobbies[code] + player = lobby.get_player(conn_id) + if not player or not player.is_host: + return self._error(conn_id, "not_host", "Seul le chef de partie peut démarrer.") + if not lobby.all_ready(): + return self._error( + conn_id, "not_all_ready", + f"Tous les joueurs doivent être prêts avec une classe ({len(lobby.players)}/3).", + ) + + lobby.started = True + logger.info("Partie démarrée dans le lobby %s", code) + return [(p.conn_id, {"type": "game_starting", "countdown": 3}) for p in lobby.players] + + def leave_lobby(self, conn_id): + # bouton Back : on quitte volontairement, meme logique que disconnect + confirmation + if conn_id not in self.conn_to_lobby: + return [] + notifications = self.disconnect(conn_id) + notifications.insert(0, (conn_id, {"type": "lobby_left"})) + return notifications + + def disconnect(self, conn_id): + code = self.conn_to_lobby.pop(conn_id, None) + if not code: + return [] + + lobby = self.lobbies.get(code) + if not lobby: + return [] + + player = lobby.get_player(conn_id) + if not player: + return [] + + lobby.players = [p for p in lobby.players if p.conn_id != conn_id] + logger.info("%s a quitté le lobby %s", player.username, code) + + if not lobby.players: + del self.lobbies[code] + logger.info("Lobby %s supprimé (vide)", code) + return [] + + # transferer l'host au premier restant + if player.is_host: + lobby.players[0].is_host = True + + return [(p.conn_id, {"type": "player_left", "player_id": conn_id}) for p in lobby.players] diff --git a/server/main.py b/server/main.py new file mode 100644 index 0000000..5168d59 --- /dev/null +++ b/server/main.py @@ -0,0 +1,243 @@ +# main.py : point d'entree FastAPI (routes + websocket + lancement game loop) + +import asyncio +import logging +import os +from contextlib import asynccontextmanager + +from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect +from fastapi.middleware.cors import CORSMiddleware +from pydantic import BaseModel + +from game_loop import GameLoop +from lobby import Lobby, LobbyManager +from stats import get_leaderboard, init_db, is_discord_taken, save_result +from websocket import ConnectionManager + +LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper() +logging.basicConfig( + level=getattr(logging, LOG_LEVEL, logging.INFO), + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", +) +logger = logging.getLogger(__name__) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + await init_db() + logger.info("Base de données initialisée") + yield + + +app = FastAPI(title="SOULGATE", lifespan=lifespan) +app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) + +manager = ConnectionManager() +lobby_manager = LobbyManager() +active_games: dict[str, tuple[GameLoop, asyncio.Task]] = {} + +logger.info("Serveur SOULGATE démarré") + + +# --- routes leaderboard --- + +class SubmitPayload(BaseModel): + team_name: str + score: int + time_seconds: int + waves_completed: int + victory: bool + lobby_code: str = "" + submitter_id: str = "" + p1_username: str = "" + p1_class: str = "" + p1_discord: str = "" + p2_username: str = "" + p2_class: str = "" + p2_discord: str = "" + p3_username: str = "" + p3_class: str = "" + p3_discord: str = "" + + +# lobbies dont le score a deja ete soumis : empeche les doublons +_submitted_lobbies: set[str] = set() + + +@app.get("/leaderboard") +async def route_leaderboard() -> list[dict]: + return await get_leaderboard() + + +@app.get("/leaderboard/check-discord/{tag}") +async def route_check_discord(tag: str) -> dict: + taken = await is_discord_taken(tag) + return {"taken": taken} + + +@app.post("/leaderboard/submit") +async def route_submit(payload: SubmitPayload) -> dict: + if not payload.team_name.strip(): + raise HTTPException(400, "Le nom d'équipe est obligatoire.") + + # une defaite n'a rien a faire dans le leaderboard + if not payload.victory: + raise HTTPException(403, "Seules les victoires peuvent être enregistrées.") + + # seul le host peut soumettre, et 1 seule fois par lobby + code = payload.lobby_code.upper().strip() + submitter = payload.submitter_id.strip() + if not code or not submitter: + raise HTTPException(400, "Identifiants de session manquants.") + if code in _submitted_lobbies: + raise HTTPException(409, "Le score de cette partie a déjà été enregistré.") + lobby = lobby_manager.lobbies.get(code) + if not lobby: + raise HTTPException(404, "Lobby introuvable ou déjà nettoyé.") + player = lobby.get_player(submitter) + if not player or not player.is_host: + raise HTTPException(403, "Seul le chef d'équipe peut enregistrer le score.") + + # pas de doublon de discord + + for tag in (payload.p1_discord, payload.p2_discord, payload.p3_discord): + if tag and await is_discord_taken(tag): + raise HTTPException(409, f"Le Discord '{tag}' est déjà dans le leaderboard.") + + rank = await save_result(payload.model_dump(exclude={"lobby_code", "submitter_id"})) + _submitted_lobbies.add(code) + return {"rank": rank} + + +@app.get("/health") +async def health() -> dict: + return {"status": "ok"} + + +@app.websocket("/ws") +async def websocket_endpoint(websocket: WebSocket) -> None: + conn_id = await manager.connect(websocket) + try: + while True: + try: + data = await websocket.receive_json() + except WebSocketDisconnect: + raise + except Exception: + try: + await manager.send(conn_id, {"type": "error", "code": "invalid_json", "message": "Message JSON invalide."}) + except Exception: + pass + continue + await _handle_message(conn_id, data.get("type", ""), data) + except WebSocketDisconnect: + messages = lobby_manager.disconnect(conn_id) + await manager.dispatch(messages) + manager.disconnect(conn_id) + + +async def _handle_message(conn_id: str, msg_type: str, data: dict) -> None: + username = manager.get_username(conn_id) + + if msg_type == "set_username": + uname = str(data.get("username", "")).strip() + if not uname: + await manager.send(conn_id, {"type": "error", "code": "invalid_username", "message": "Le pseudo ne peut pas être vide."}) + return + manager.set_username(conn_id, uname) + await manager.send(conn_id, {"type": "username_set", "username": uname, "my_id": conn_id}) + + elif msg_type in ("create_lobby", "join_lobby"): + if not username: + await manager.send(conn_id, {"type": "error", "code": "no_username", "message": "Définis ton pseudo d'abord (set_username)."}) + return + if msg_type == "create_lobby": + messages = lobby_manager.create_lobby(conn_id, username) + else: + messages = lobby_manager.join_lobby(conn_id, username, data.get("code", "")) + await manager.dispatch(messages) + + elif msg_type == "leave_lobby": + await manager.dispatch(lobby_manager.leave_lobby(conn_id)) + + elif msg_type == "select_class": + await manager.dispatch(lobby_manager.select_class(conn_id, data.get("class", ""))) + + elif msg_type == "ready": + await manager.dispatch(lobby_manager.set_ready(conn_id)) + + elif msg_type == "start_game": + messages = lobby_manager.start_game(conn_id) + await manager.dispatch(messages) + if messages and messages[0][1].get("type") == "game_starting": + code = lobby_manager.conn_to_lobby.get(conn_id) + if code and code in lobby_manager.lobbies: + await _launch_game(lobby_manager.lobbies[code]) + + elif msg_type == "input": + code = lobby_manager.conn_to_lobby.get(conn_id) + if code and code in active_games: + game, _ = active_games[code] + try: + dx = float(data.get("dx", 0)) + dy = float(data.get("dy", 0)) + except (ValueError, TypeError): + return + game.handle_input(conn_id, dx, dy) + + elif msg_type == "attack": + code = lobby_manager.conn_to_lobby.get(conn_id) + if code and code in active_games: + game, _ = active_games[code] + try: + tx = float(data.get("tx", 0)) + ty = float(data.get("ty", 0)) + except (ValueError, TypeError): + return + game.handle_attack(conn_id, tx, ty) + + elif msg_type == "ability": + code = lobby_manager.conn_to_lobby.get(conn_id) + if code and code in active_games: + game, _ = active_games[code] + try: + ability_id = int(data.get("id", 0)) + tx = float(data.get("tx", 0)) + ty = float(data.get("ty", 0)) + except (ValueError, TypeError): + return + if ability_id in (1, 2, 3): + game.handle_ability(conn_id, ability_id, tx, ty) + + elif msg_type == "player_upgrade": + code = lobby_manager.conn_to_lobby.get(conn_id) + if code and code in active_games: + game, _ = active_games[code] + game.handle_upgrade(conn_id, data.get("upgrade_id", "")) + + elif msg_type == "displacement": + code = lobby_manager.conn_to_lobby.get(conn_id) + if code and code in active_games: + game, _ = active_games[code] + try: + tx = float(data.get("tx", 0)) + ty = float(data.get("ty", 0)) + except (ValueError, TypeError): + return + game.handle_displacement(conn_id, tx, ty) + + + else: + logger.warning("Message inconnu de %s : %s", conn_id, msg_type) + await manager.send(conn_id, {"type": "error", "code": "unknown_message", "message": f"Type de message inconnu : {msg_type}"}) + + +async def _launch_game(lobby: Lobby) -> None: + async def broadcast(msg: dict) -> None: + for conn_id in lobby.get_conn_ids(): + await manager.send(conn_id, msg) + + game = GameLoop(lobby, broadcast) + task = asyncio.create_task(game.run()) + active_games[lobby.code] = (game, task) + logger.info("Game loop lancée — lobby %s", lobby.code) diff --git a/server/pyproject.toml b/server/pyproject.toml new file mode 100644 index 0000000..f0e4c8a --- /dev/null +++ b/server/pyproject.toml @@ -0,0 +1,3 @@ +[tool.pytest.ini_options] +pythonpath = ["."] +testpaths = ["tests"] diff --git a/server/requirements.txt b/server/requirements.txt new file mode 100644 index 0000000..73f2efe --- /dev/null +++ b/server/requirements.txt @@ -0,0 +1,50 @@ +aiosqlite==0.21.0 +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.12.1 +certifi==2026.1.4 +click==8.3.1 +dnspython==2.8.0 +email-validator==2.3.0 +fastapi==0.133.0 +fastapi-cli==0.0.24 +fastapi-cloud-cli==0.13.0 +fastar==0.8.0 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.7.1 +httpx==0.28.1 +idna==3.11 +iniconfig==2.3.0 +Jinja2==3.1.6 +lxml==6.0.2 +markdown-it-py==4.0.0 +MarkupSafe==3.0.3 +mdurl==0.1.2 +packaging==26.0 +pillow==12.2.0 +pluggy==1.6.0 +pydantic==2.12.5 +pydantic-extra-types==2.11.0 +pydantic-settings==2.13.1 +pydantic_core==2.41.5 +Pygments==2.19.2 +pytest==9.0.2 +python-docx==1.2.0 +python-dotenv==1.2.1 +python-multipart==0.0.22 +PyYAML==6.0.3 +rich==14.3.3 +rich-toolkit==0.19.7 +rignore==0.7.6 +sentry-sdk==2.53.0 +shellingham==1.5.4 +starlette==0.52.1 +typer==0.24.1 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +urllib3==2.6.3 +uvicorn==0.41.0 +uvloop==0.22.1 +watchfiles==1.1.1 +websockets==16.0 diff --git a/server/stats.py b/server/stats.py new file mode 100644 index 0000000..f53b77f --- /dev/null +++ b/server/stats.py @@ -0,0 +1,80 @@ +"""Leaderboard — stockage SQLite et requêtes.""" + +import aiosqlite +from pathlib import Path + +DB_PATH = str(Path(__file__).parent / "soulgate.db") + +_CREATE = """ +CREATE TABLE IF NOT EXISTS game_results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + team_name TEXT NOT NULL, + score INTEGER NOT NULL, + time_seconds INTEGER NOT NULL, + waves_completed INTEGER NOT NULL, + victory INTEGER NOT NULL DEFAULT 0, + p1_username TEXT, p1_class TEXT, p1_discord TEXT, + p2_username TEXT, p2_class TEXT, p2_discord TEXT, + p3_username TEXT, p3_class TEXT, p3_discord TEXT, + played_at TEXT NOT NULL DEFAULT (datetime('now')) +) +""" + + +async def init_db() -> None: + async with aiosqlite.connect(DB_PATH) as db: + await db.execute(_CREATE) + await db.commit() + + +async def is_discord_taken(tag: str) -> bool: + """Retourne True si ce tag Discord est déjà enregistré dans le leaderboard.""" + if not tag: + return False + async with aiosqlite.connect(DB_PATH) as db: + async with db.execute( + "SELECT COUNT(*) FROM game_results WHERE p1_discord=? OR p2_discord=? OR p3_discord=?", + (tag, tag, tag), + ) as cur: + row = await cur.fetchone() + return bool(row and row[0] > 0) + + +async def save_result(data: dict) -> int: + """Sauvegarde un résultat de partie. Retourne le rang (1 = meilleur score).""" + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + """INSERT INTO game_results + (team_name, score, time_seconds, waves_completed, victory, + p1_username, p1_class, p1_discord, + p2_username, p2_class, p2_discord, + p3_username, p3_class, p3_discord) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", + ( + data["team_name"], + int(data["score"]), + int(data["time_seconds"]), + int(data["waves_completed"]), + 1 if data.get("victory") else 0, + data.get("p1_username"), data.get("p1_class"), data.get("p1_discord") or None, + data.get("p2_username"), data.get("p2_class"), data.get("p2_discord") or None, + data.get("p3_username"), data.get("p3_class"), data.get("p3_discord") or None, + ), + ) + await db.commit() + async with db.execute( + "SELECT COUNT(*) FROM game_results WHERE score > ?", (int(data["score"]),) + ) as cur: + row = await cur.fetchone() + return (row[0] if row else 0) + 1 # rang 1-based + + +async def get_leaderboard(limit: int = 20) -> list[dict]: + """Retourne les `limit` meilleures parties par score décroissant.""" + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT * FROM game_results ORDER BY score DESC LIMIT ?", (limit,) + ) as cur: + rows = await cur.fetchall() + return [dict(r) for r in rows] diff --git a/server/tests/__init__.py b/server/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/tests/__pycache__/__init__.cpython-313.pyc b/server/tests/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..038d189 Binary files /dev/null and b/server/tests/__pycache__/__init__.cpython-313.pyc differ diff --git a/server/tests/__pycache__/test_boss.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_boss.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..3622c0a Binary files /dev/null and b/server/tests/__pycache__/test_boss.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_combat.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_combat.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..31fdf0a Binary files /dev/null and b/server/tests/__pycache__/test_combat.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_displacement.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_displacement.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..13ceb7c Binary files /dev/null and b/server/tests/__pycache__/test_displacement.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_game_state.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_game_state.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..f34cdb6 Binary files /dev/null and b/server/tests/__pycache__/test_game_state.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_health.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_health.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..b6ee1d4 Binary files /dev/null and b/server/tests/__pycache__/test_health.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_lobby.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_lobby.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..bf19a3a Binary files /dev/null and b/server/tests/__pycache__/test_lobby.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_waves.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_waves.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..200c550 Binary files /dev/null and b/server/tests/__pycache__/test_waves.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/__pycache__/test_websocket.cpython-313-pytest-9.0.2.pyc b/server/tests/__pycache__/test_websocket.cpython-313-pytest-9.0.2.pyc new file mode 100644 index 0000000..2fe2690 Binary files /dev/null and b/server/tests/__pycache__/test_websocket.cpython-313-pytest-9.0.2.pyc differ diff --git a/server/tests/test_boss.py b/server/tests/test_boss.py new file mode 100644 index 0000000..03cb7a3 --- /dev/null +++ b/server/tests/test_boss.py @@ -0,0 +1,142 @@ +# tests boss : vexaris + morveth + transitions + +from unittest.mock import AsyncMock + +from lobby import Lobby, LobbyPlayer +from game_loop import GameLoop +from constants import ( + VEXARIS_HP, VEXARIS_HITBOX_RADIUS, + MORVETH_HP, + WAVE_BOSSES, SOUL_REWARDS, +) +from waves import WaveManager +from constants import PREPARATION_DURATION, TICK_DURATION + + +def make_lobby(): + lobby = Lobby("TEST1", "p1") + lobby.players = [ + LobbyPlayer("p1", "Alice", "kael"), + LobbyPlayer("p2", "Bob", "seris"), + LobbyPlayer("p3", "Clara", "aldric"), + ] + return lobby + + +def _ticks(wm, n, enemy_count=0): + events = [] + for _ in range(n): + events.extend(wm.tick(lambda t: None, enemy_count)) + return events + + +def _complete_wave_phases(wm): + for _ in range(200_000): + wm.tick(lambda t: None, 0) + if wm.state != "combat": + return + raise AssertionError("vague combat infinie") + + +def _prep_ticks(): + return int(PREPARATION_DURATION / TICK_DURATION) + 1 + + +def _skip_initial_prep(wm): + while wm.state == "preparation": + wm.tick(lambda t: None, 0) + + +def test_wave_bosses(): + assert WAVE_BOSSES[2] == "vexaris" + assert WAVE_BOSSES[3] == "morveth" + assert 1 not in WAVE_BOSSES + + +def test_spawn_vexaris(): + loop = GameLoop(make_lobby(), AsyncMock()) + loop.spawn_enemy("vexaris") + boss = loop.state.enemies[0] + assert boss.type == "vexaris" + assert boss.hp == VEXARIS_HP + assert boss.is_boss is True + + +def test_boss_tape_joueur_au_contact(): + loop = GameLoop(make_lobby(), AsyncMock()) + loop.spawn_enemy("vexaris") + boss = loop.state.enemies[0] + player = loop.state.players[0] + boss.x, boss.y = player.x, player.y + hp_initial = player.hp + loop._update_enemies() + assert player.hp < hp_initial + + +def test_boss_kill_donne_des_ames(): + loop = GameLoop(make_lobby(), AsyncMock()) + loop.spawn_enemy("vexaris") + boss = loop.state.enemies[0] + player = loop.state.players[0] + initial_souls = player.souls + from game_state import ProjectileState + loop.state.projectiles.append(ProjectileState( + id="pr_test", owner_id=player.id, + x=boss.x, y=boss.y, vx=0, vy=0, + damage=VEXARIS_HP, radius=VEXARIS_HITBOX_RADIUS + 1, ttl=1.0, + )) + loop._update_projectiles() + assert player.souls == initial_souls + SOUL_REWARDS["vexaris"] + + +def test_spawn_morveth(): + loop = GameLoop(make_lobby(), AsyncMock()) + loop.spawn_enemy("morveth") + boss = loop.state.enemies[0] + assert boss.type == "morveth" + assert boss.hp == MORVETH_HP + + +def _morveth_at_pct(pct): + loop = GameLoop(make_lobby(), AsyncMock()) + loop.spawn_enemy("morveth") + boss = loop.state.enemies[0] + boss.hp = max(1, int(boss.max_hp * pct)) + boss.data.update({ + "charge_cd": 99.0, "charge_timer": 0.0, + "charge_tx": 0.0, "charge_ty": 0.0, + "is_charging": False, + "burst_cd": 99.0, + "general_1_spawned": False, + "general_2_spawned": False, + "general_3_spawned": False, + }) + return loop + + +def test_morveth_invoque_general_a_75(): + loop = _morveth_at_pct(0.74) + loop._update_enemies() + generals = [e for e in loop.state.enemies if e.type == "general"] + assert len(generals) == 1 + + +def test_morveth_pas_de_double_invocation(): + loop = _morveth_at_pct(0.74) + loop._update_enemies() + loop._update_enemies() + generals = [e for e in loop.state.enemies if e.type == "general"] + assert len(generals) == 1 + + +def test_victory_apres_morveth(): + wm = WaveManager() + _skip_initial_prep(wm) + _complete_wave_phases(wm) + _ticks(wm, _prep_ticks()) + _complete_wave_phases(wm) + _ticks(wm, 3) # Vexaris spawn + kill + _ticks(wm, _prep_ticks()) + _complete_wave_phases(wm) + _ticks(wm, 3) # Morveth spawn + kill + assert wm.state == "victory" diff --git a/server/tests/test_combat.py b/server/tests/test_combat.py new file mode 100644 index 0000000..3eaf3fe --- /dev/null +++ b/server/tests/test_combat.py @@ -0,0 +1,74 @@ +# tests combat : hitbox, cooldown, projectile + +import math +from unittest.mock import AsyncMock + +import pytest + +from combat import circle_overlap, normalize +from constants import ATTACK_COOLDOWN, TICK_DURATION +from game_loop import GameLoop +from lobby import Lobby, LobbyPlayer + + +def make_game_loop(class_name="seris"): + player = LobbyPlayer(conn_id="c1", username="Test", player_class=class_name, is_ready=True, is_host=True) + lobby = Lobby(code="TEST01", players=[player], started=True) + return GameLoop(lobby, AsyncMock()) + + +def test_circle_overlap_overlapping(): + assert circle_overlap(0, 0, 1, 0.5, 0, 1) is True + +def test_circle_overlap_far_apart(): + assert circle_overlap(0, 0, 0.5, 10, 10, 0.5) is False + + +def test_normalize_diagonal(): + dx, dy = normalize(1, 1) + assert dx == pytest.approx(1 / math.sqrt(2)) + assert dy == pytest.approx(1 / math.sqrt(2)) + +def test_normalize_zero(): + assert normalize(0, 0) == (0.0, 0.0) + + +def test_cooldown_decroit(): + loop = make_game_loop() + loop.state.players[0].cooldowns["attack"] = 0.5 + loop._update_cooldowns() + assert loop.state.players[0].cooldowns["attack"] == pytest.approx(0.5 - TICK_DURATION) + + +def test_handle_attack_cree_projectile(): + loop = make_game_loop() + p = loop.state.players[0] + loop.handle_attack(p.id, p.x + 5, p.y) + assert len(loop.state.projectiles) == 1 + assert p.cooldowns["attack"] == pytest.approx(ATTACK_COOLDOWN) + + +def test_handle_attack_bloque_par_cd(): + loop = make_game_loop() + p = loop.state.players[0] + p.cooldowns["attack"] = 0.5 + loop.handle_attack(p.id, p.x + 5, p.y) + assert len(loop.state.projectiles) == 0 + + +def test_projectile_se_deplace(): + loop = make_game_loop() + p = loop.state.players[0] + loop.handle_attack(p.id, p.x + 5, p.y) + old_x = loop.state.projectiles[0].x + loop._update_projectiles() + assert loop.state.projectiles[0].x != old_x + + +def test_projectile_disparait_quand_ttl_zero(): + loop = make_game_loop() + p = loop.state.players[0] + loop.handle_attack(p.id, p.x + 5, p.y) + loop.state.projectiles[0].ttl = 0.01 + loop._update_projectiles() + assert len(loop.state.projectiles) == 0 diff --git a/server/tests/test_displacement.py b/server/tests/test_displacement.py new file mode 100644 index 0000000..be7f378 --- /dev/null +++ b/server/tests/test_displacement.py @@ -0,0 +1,78 @@ +# tests touche E (Kael / Seris / Aldric) + +from unittest.mock import AsyncMock + +from lobby import Lobby, LobbyPlayer +from game_loop import GameLoop +from game_state import EnemyState +from constants import ( + DISPLACEMENT_COOLDOWNS, + KAEL_CHARGE_DISTANCE, + SERIS_INTANGIBLE_DURATION, + ALDRIC_FLYING_DURATION, + ENEMY_STATS, +) + + +def make_game(): + classes = [("p1", "Alice", "kael"), ("p2", "Bob", "seris"), ("p3", "Clara", "aldric")] + lobby = Lobby("TEST1", classes[0][0]) + lobby.players = [LobbyPlayer(cid, uname, cls) for cid, uname, cls in classes] + return GameLoop(lobby, AsyncMock()) + + +def add_enemy(game, etype="fracture", x=0.0, y=5.0): + stats = ENEMY_STATS[etype] + game._enemy_counter += 1 + e = EnemyState(id=f"en_{game._enemy_counter}", type=etype, x=x, y=y, hp=stats["hp"], max_hp=stats["hp"]) + game.state.enemies.append(e) + return e + + +def test_kael_charge(): + game = make_game() + p = game.state.players[0] + p.x, p.y = 0.0, 0.0 + game.handle_displacement("p1", 10.0, 0.0) + assert abs(p.x - KAEL_CHARGE_DISTANCE) < 0.01 + assert p.cooldowns["displacement"] == DISPLACEMENT_COOLDOWNS["kael"] + + +def test_kael_charge_bloque_par_cd(): + game = make_game() + p = game.state.players[0] + p.x, p.y = 0.0, 0.0 + p.cooldowns["displacement"] = 3.0 + game.handle_displacement("p1", 10.0, 0.0) + assert p.x == 0.0 + + +def test_seris_teleport(): + game = make_game() + p = game.state.players[1] + p.x, p.y = 0.0, 0.0 + game.handle_displacement("p2", 5.0, 3.0) + assert abs(p.x - 5.0) < 0.01 + assert abs(p.y - 3.0) < 0.01 + assert any(b["type"] == "intangible" for b in p.buffs) + + +def test_seris_intangible_immunise(): + game = make_game() + p = game.state.players[1] + p.x, p.y = 0.0, 0.0 + p.hp = p.max_hp + p.buffs.append({"type": "intangible", "timer": SERIS_INTANGIBLE_DURATION}) + e = add_enemy(game, "fracture", x=0.0, y=0.0) + e.attack_cooldown = 0.0 + game._update_enemies() + assert p.hp == p.max_hp + + +def test_aldric_vol(): + game = make_game() + p = game.state.players[2] + game.handle_displacement("p3", 0.0, 0.0) + flying = next((b for b in p.buffs if b["type"] == "flying"), None) + assert flying is not None + assert abs(flying["timer"] - ALDRIC_FLYING_DURATION) < 0.001 diff --git a/server/tests/test_game_state.py b/server/tests/test_game_state.py new file mode 100644 index 0000000..86e605d --- /dev/null +++ b/server/tests/test_game_state.py @@ -0,0 +1,59 @@ +# tests game state + game loop init + +from unittest.mock import AsyncMock + +from game_loop import GameLoop +from game_state import GameState, PlayerState, SoulgateState, WaveState +from lobby import Lobby, LobbyPlayer + + +def make_lobby(): + lobby = Lobby(code="TEST01") + lobby.players = [ + LobbyPlayer(conn_id="c1", username="Alice", player_class="kael", is_ready=True, is_host=True), + LobbyPlayer(conn_id="c2", username="Bob", player_class="seris", is_ready=True), + LobbyPlayer(conn_id="c3", username="Charlie", player_class="aldric", is_ready=True), + ] + return lobby + + +def test_game_state_to_dict(): + state = GameState( + tick=5, players=[], enemies=[], projectiles=[], + soulgate=SoulgateState(hp=80, max_hp=100), + wave=WaveState(number=2, phase=1, enemies_remaining=3, state="combat"), + effects=[], + ) + d = state.to_dict() + assert d["type"] == "game_state" + assert d["tick"] == 5 + for key in ("players", "enemies", "projectiles", "soulgate", "wave"): + assert key in d + + +def test_player_state_to_dict(): + p = PlayerState(id="c1", class_name="kael", username="Alice", x=0.0, y=-10.0, hp=150, max_hp=150, alive=True) + d = p.to_dict() + assert d["class"] == "kael" + assert d["hp"] == 150 + assert "attack" in d["cooldowns"] + + +def test_game_loop_3_joueurs(): + loop = GameLoop(make_lobby(), AsyncMock()) + assert len(loop.state.players) == 3 + + +def test_hp_par_classe(): + loop = GameLoop(make_lobby(), AsyncMock()) + by_class = {p.class_name: p.max_hp for p in loop.state.players} + assert by_class["kael"] == 150 + assert by_class["seris"] == 80 + assert by_class["aldric"] == 100 + + +def test_tick_incremente(): + loop = GameLoop(make_lobby(), AsyncMock()) + assert loop.state.tick == 0 + loop._tick() + assert loop.state.tick == 1 diff --git a/server/tests/test_health.py b/server/tests/test_health.py new file mode 100644 index 0000000..34f7509 --- /dev/null +++ b/server/tests/test_health.py @@ -0,0 +1,13 @@ +# Tests de base — vérification du serveur FastAPI + +from fastapi.testclient import TestClient + +from main import app + +client = TestClient(app) + + +def test_health() -> None: + response = client.get("/health") + assert response.status_code == 200 + assert response.json() == {"status": "ok"} diff --git a/server/tests/test_lobby.py b/server/tests/test_lobby.py new file mode 100644 index 0000000..ecc0a41 --- /dev/null +++ b/server/tests/test_lobby.py @@ -0,0 +1,100 @@ +# tests lobby + +from lobby import LobbyManager + + +def _make(): + return LobbyManager() + + +def _full_lobby(): + m = _make() + code = m.create_lobby("c1", "Alice")[0][1]["code"] + m.join_lobby("c2", "Bob", code) + m.join_lobby("c3", "Charlie", code) + m.select_class("c1", "kael") + m.select_class("c2", "seris") + m.select_class("c3", "aldric") + m.set_ready("c1") + m.set_ready("c2") + m.set_ready("c3") + return m, code + + +def test_create_lobby(): + m = _make() + msgs = m.create_lobby("c1", "Alice") + assert msgs[0][1]["type"] == "lobby_created" + assert len(msgs[0][1]["code"]) == 6 + + +def test_create_lobby_recreate(): + # quand on recree, l'ancien est libere (corrige un bug de "Back" qui bloquait) + m = _make() + code1 = m.create_lobby("c1", "Alice")[0][1]["code"] + msgs = m.create_lobby("c1", "Alice") + last = next(msg for cid, msg in msgs if cid == "c1" and msg["type"] == "lobby_created") + assert last["code"] != code1 + assert code1 not in m.lobbies + + +def test_join_lobby(): + m = _make() + code = m.create_lobby("c1", "Alice")[0][1]["code"] + msgs = m.join_lobby("c2", "Bob", code) + types = {msg["type"] for _, msg in msgs} + assert "lobby_joined" in types and "player_joined" in types + + +def test_join_lobby_not_found(): + m = _make() + msgs = m.join_lobby("c1", "Alice", "XXXXXX") + assert msgs[0][1]["code"] == "lobby_not_found" + + +def test_join_lobby_full(): + m = _make() + code = m.create_lobby("c1", "Alice")[0][1]["code"] + m.join_lobby("c2", "Bob", code) + m.join_lobby("c3", "Charlie", code) + msgs = m.join_lobby("c4", "Dave", code) + assert msgs[0][1]["code"] == "lobby_full" + + +def test_class_taken(): + m = _make() + code = m.create_lobby("c1", "Alice")[0][1]["code"] + m.join_lobby("c2", "Bob", code) + m.select_class("c1", "kael") + msgs = m.select_class("c2", "kael") + assert msgs[0][1]["code"] == "class_taken" + + +def test_ready_sans_classe(): + m = _make() + m.create_lobby("c1", "Alice") + msgs = m.set_ready("c1") + assert msgs[0][1]["code"] == "no_class_selected" + + +def test_start_game_not_host(): + m = _make() + code = m.create_lobby("c1", "Alice")[0][1]["code"] + m.join_lobby("c2", "Bob", code) + msgs = m.start_game("c2") + assert msgs[0][1]["code"] == "not_host" + + +def test_start_game_ok(): + m, _ = _full_lobby() + msgs = m.start_game("c1") + assert len(msgs) == 3 + assert all(msg["type"] == "game_starting" for _, msg in msgs) + + +def test_disconnect_transfere_host(): + m = _make() + code = m.create_lobby("c1", "Alice")[0][1]["code"] + m.join_lobby("c2", "Bob", code) + m.disconnect("c1") + assert m.lobbies[code].players[0].is_host is True diff --git a/server/tests/test_waves.py b/server/tests/test_waves.py new file mode 100644 index 0000000..ffe4bad --- /dev/null +++ b/server/tests/test_waves.py @@ -0,0 +1,91 @@ +# tests vagues : WaveManager + WAVE_CONFIGS + +from waves import WaveManager, WAVE_CONFIGS +from constants import PREPARATION_DURATION, TICK_DURATION + + +def _ticks(wm, n, enemy_count=0): + events = [] + for _ in range(n): + events.extend(wm.tick(lambda t: None, enemy_count)) + return events + + +def _complete_wave(wm): + # avance jusqu'a la fin de la vague (preparation ou victory) + for _ in range(100_000): + wm.tick(lambda t: None, 0) + if wm.state in ("preparation", "victory"): + return + raise AssertionError("vague jamais finie") + + +def _prep_ticks(): + return int(PREPARATION_DURATION / TICK_DURATION) + 1 + + +def _skip_initial_prep(wm): + while wm.state == "preparation": + wm.tick(lambda t: None, 0) + + +def test_three_waves(): + assert len(WAVE_CONFIGS) == 3 + + +def test_initial_state(): + wm = WaveManager() + assert wm.wave_number == 1 + assert wm.state == "preparation" + + +def test_first_spawn(): + spawned = [] + wm = WaveManager() + _skip_initial_prep(wm) + wm.tick(spawned.append, 999) + assert len(spawned) >= 1 + assert spawned[0] == WAVE_CONFIGS[0][0].groups[0].enemy_type + + +def test_phase_avance_quand_zero_ennemis(): + wm = WaveManager() + for _ in range(100_000): + if wm.enemies_remaining() == 0: + break + wm.tick(lambda t: None, 999) + wm.tick(lambda t: None, 0) + assert wm.phase_number == 2 + + +def test_preparation_apres_vague(): + wm = WaveManager() + _complete_wave(wm) + assert wm.state == "preparation" + + +def test_pas_de_spawn_en_prep(): + wm = WaveManager() + _complete_wave(wm) + spawned = [] + wm.tick(spawned.append, 0) + assert spawned == [] + + +def test_vague_2_apres_timer_prep(): + wm = WaveManager() + _skip_initial_prep(wm) + _complete_wave(wm) + _ticks(wm, _prep_ticks()) + assert wm.wave_number == 2 + assert wm.state == "combat" + + +def test_victory_apres_3_vagues(): + wm = WaveManager() + _skip_initial_prep(wm) + for _ in range(3): + _complete_wave(wm) + if wm.state == "preparation": + _ticks(wm, _prep_ticks()) + assert wm.state == "victory" diff --git a/server/tests/test_websocket.py b/server/tests/test_websocket.py new file mode 100644 index 0000000..627afef --- /dev/null +++ b/server/tests/test_websocket.py @@ -0,0 +1,30 @@ +# tests integration WebSocket + +from fastapi.testclient import TestClient + +from main import app + +client = TestClient(app) + + +def test_set_username(): + with client.websocket_connect("/ws") as ws: + ws.send_json({"type": "set_username", "username": "Alice"}) + msg = ws.receive_json() + assert msg["type"] == "username_set" + + +def test_create_lobby_sans_pseudo(): + with client.websocket_connect("/ws") as ws: + ws.send_json({"type": "create_lobby"}) + msg = ws.receive_json() + assert msg["code"] == "no_username" + + +def test_create_lobby_ok(): + with client.websocket_connect("/ws") as ws: + ws.send_json({"type": "set_username", "username": "Alice"}) + ws.receive_json() + ws.send_json({"type": "create_lobby"}) + msg = ws.receive_json() + assert msg["type"] == "lobby_created" diff --git a/server/waves.py b/server/waves.py new file mode 100644 index 0000000..c2d1dcb --- /dev/null +++ b/server/waves.py @@ -0,0 +1,180 @@ +# waves.py : machine a etats des vagues (combat / boss / preparation / victory) + +from dataclasses import dataclass + +from constants import PREPARATION_DURATION, TICK_DURATION, WAVE_BOSSES + + +@dataclass +class SpawnGroup: + enemy_type: str # type d'ennemi à spawner : "fracture", "rampant", "colosse", "eclat" + count: int # nombre total d'ennemis à spawner dans ce groupe + interval: int # ticks entre chaque spawn (ex: 40 ticks = 2s à 20Hz) + # 20 Hz = 20 ticks par seconde → 40 ticks = 2 secondes + + +@dataclass +class PhaseConfig: + """Configuration d'une phase : plusieurs groupes d'ennemis spawnent en parallèle.""" + groups: list[SpawnGroup] # chaque groupe spawn indépendamment selon son interval + + +# Données des vagues +# WAVE_CONFIGS[i] = liste de PhaseConfig pour la vague i+1 +# Chaque vague a plusieurs phases. Dans chaque phase, plusieurs groupes spawnent en parallèle. + +WAVE_CONFIGS: list[list[PhaseConfig]] = [ + + # Vague 1 — L'Éveil (5 phases de difficulté croissante) + [ + PhaseConfig([SpawnGroup("fracture", 4, 40)]), # phase 1 : 4 fractures, 1 toutes les 2s + PhaseConfig([SpawnGroup("rampant", 6, 30)]), # phase 2 : 6 rampants, 1 toutes les 1.5s + PhaseConfig([SpawnGroup("fracture", 4, 30), SpawnGroup("rampant", 3, 60)]), # phase 3 : les deux en même temps + PhaseConfig([SpawnGroup("colosse", 2, 80), SpawnGroup("fracture", 4, 40)]), # phase 4 : colosses + fractures + PhaseConfig([SpawnGroup("fracture", 6, 20), SpawnGroup("rampant", 6, 20), SpawnGroup("eclat", 4, 15)]), # phase 5 : chaos total + ], + + # Vague 2 — Le Héraut (3 phases, puis boss Vexaris) + [ + PhaseConfig([SpawnGroup("fracture", 6, 30), SpawnGroup("rampant", 4, 40)]), + PhaseConfig([SpawnGroup("eclat", 8, 15), SpawnGroup("colosse", 2, 80)]), + PhaseConfig([SpawnGroup("fracture", 8, 20), SpawnGroup("rampant", 6, 25), SpawnGroup("eclat", 6, 15)]), + ], + + # Vague 3 — Morveth (3 phases encore plus dures, puis boss Morveth) + [ + PhaseConfig([SpawnGroup("fracture", 10, 15), SpawnGroup("rampant", 8, 20), SpawnGroup("colosse", 3, 60)]), + PhaseConfig([SpawnGroup("eclat", 10, 10), SpawnGroup("fracture", 8, 15), SpawnGroup("colosse", 3, 60)]), + PhaseConfig([SpawnGroup("fracture", 12, 10), SpawnGroup("rampant", 10, 15), SpawnGroup("colosse", 4, 50), SpawnGroup("eclat", 8, 12)]), + ], +] + + +# WaveManager + +class WaveManager: + """ + machine a etats : combat -> boss -> preparation -> ... -> victory + """ + + def __init__(self): + self._wave_idx = 0 + self._phase_idx = 0 + self._state = "preparation" + self._prep_timer = PREPARATION_DURATION + self._initial_prep = True + self._phase_spawned = [] + self._phase_ticks = [] + self._total_to_spawn = 0 + self._total_spawned = 0 + self._boss_spawned = False + self._init_phase() + + @property + def wave_number(self): + return self._wave_idx + 1 # affiche 1/2/3 + + @property + def phase_number(self): + return self._phase_idx + 1 + + @property + def state(self): + return self._state + + @property + def prep_timer_remaining(self): + return max(0.0, self._prep_timer) + + def _current_phase(self): + return WAVE_CONFIGS[self._wave_idx][self._phase_idx] + + def _init_phase(self): + phase = self._current_phase() + n = len(phase.groups) + self._phase_spawned = [0] * n + # ticks initialises a l'intervalle pour spawn des le 1er tick + self._phase_ticks = [g.interval for g in phase.groups] + self._total_to_spawn = sum(g.count for g in phase.groups) + self._total_spawned = 0 + + def enemies_remaining(self): + # ennemis restants a spawner dans la phase courante (pas ceux vivants sur la map) + return self._total_to_spawn - self._total_spawned + + def tick(self, spawn_fn, enemy_count): + events = [] + + if self._state == "victory": + return events + + # phase boss : on spawn le boss 1 fois, puis on attend qu'il meure + if self._state == "boss": + if not self._boss_spawned: + spawn_fn(WAVE_BOSSES[self._wave_idx + 1]) + self._boss_spawned = True + elif enemy_count == 0: + next_wave = self._wave_idx + 1 + if next_wave < len(WAVE_CONFIGS): + self._state = "preparation" + self._prep_timer = PREPARATION_DURATION + events.append("wave_complete") + events.append("preparation_start") + else: + self._state = "victory" + events.append("game_over_victory") + return events + + # phase preparation : timer 20s avant la vague suivante + if self._state == "preparation": + self._prep_timer -= TICK_DURATION + if self._prep_timer <= 0.0: + if self._initial_prep: + self._initial_prep = False # vague 1 : pas d'increment + else: + self._wave_idx += 1 + self._phase_idx = 0 + self._init_phase() + self._state = "combat" + events.append("wave_start") + return events + + # combat : spawn des groupes en parallele + phase = self._current_phase() + + for i, group in enumerate(phase.groups): + if self._phase_spawned[i] >= group.count: + continue + self._phase_ticks[i] += 1 + if self._phase_ticks[i] >= group.interval: + spawn_fn(group.enemy_type) + self._phase_spawned[i] += 1 + self._total_spawned += 1 + self._phase_ticks[i] = 0 + + # fin de phase : tout spawne ET 0 ennemis vivants + all_spawned = self._total_spawned >= self._total_to_spawn + if all_spawned and enemy_count == 0: + next_phase = self._phase_idx + 1 + if next_phase < len(WAVE_CONFIGS[self._wave_idx]): + self._phase_idx = next_phase + self._init_phase() + events.append("wave_phase") + else: + wave_number = self._wave_idx + 1 + next_wave = self._wave_idx + 1 + if wave_number in WAVE_BOSSES: + self._state = "boss" + self._boss_spawned = False + events.append("boss_incoming") + elif next_wave < len(WAVE_CONFIGS): + self._state = "preparation" + self._prep_timer = PREPARATION_DURATION + events.append("wave_complete") + events.append("preparation_start") + else: + # Dernière vague sans boss → victoire + self._state = "victory" + events.append("game_over_victory") + + return events diff --git a/server/websocket.py b/server/websocket.py new file mode 100644 index 0000000..00ad0d7 --- /dev/null +++ b/server/websocket.py @@ -0,0 +1,46 @@ +# Gestionnaire de connexions WebSocket — accepte, envoie, broadcast + +import logging + +from fastapi import WebSocket + +logger = logging.getLogger(__name__) + + +class ConnectionManager: + def __init__(self) -> None: + self.connections: dict[str, WebSocket] = {} + self.usernames: dict[str, str] = {} + self._counter: int = 0 + + def _next_id(self) -> str: + self._counter += 1 + return f"conn_{self._counter}" + + async def connect(self, websocket: WebSocket) -> str: + await websocket.accept() + conn_id = self._next_id() + self.connections[conn_id] = websocket + logger.info("Connexion %s établie", conn_id) + return conn_id + + def disconnect(self, conn_id: str) -> None: + self.connections.pop(conn_id, None) + self.usernames.pop(conn_id, None) + logger.info("Connexion %s fermée", conn_id) + + def set_username(self, conn_id: str, username: str) -> None: + self.usernames[conn_id] = username + + def get_username(self, conn_id: str) -> str | None: + return self.usernames.get(conn_id) + + async def send(self, conn_id: str, message: dict) -> None: + ws = self.connections.get(conn_id) + if ws: + await ws.send_json(message) + + async def dispatch(self, messages: list[tuple[str, dict]]) -> None: + """Envoie une liste de (conn_id, message) produite par le LobbyManager.""" + for conn_id, message in messages: + await self.send(conn_id, message)