diff --git a/assets/credits.mid b/assets/credits.mid index 949583f2d..5dddb8d44 100644 Binary files a/assets/credits.mid and b/assets/credits.mid differ diff --git a/assets/menu/menu_bg.png b/assets/menu/menu_bg.png new file mode 100644 index 000000000..d0bfec0a8 Binary files /dev/null and b/assets/menu/menu_bg.png differ diff --git a/assets/ray/doors/BG_DOOR.png b/assets/ray/doors/BG_DOOR.png index bfb6c033f..bc9d3512f 100644 Binary files a/assets/ray/doors/BG_DOOR.png and b/assets/ray/doors/BG_DOOR.png differ diff --git a/assets/ray/doors/BG_DOOR_ARTIFACT.png b/assets/ray/doors/BG_DOOR_ARTIFACT.png new file mode 100755 index 000000000..ca65f23a3 Binary files /dev/null and b/assets/ray/doors/BG_DOOR_ARTIFACT.png differ diff --git a/assets/ray/doors/BG_DOOR_CHARGE.png b/assets/ray/doors/BG_DOOR_CHARGE.png index e0742ef53..c0a68b08d 100644 Binary files a/assets/ray/doors/BG_DOOR_CHARGE.png and b/assets/ray/doors/BG_DOOR_CHARGE.png differ diff --git a/assets/ray/doors/BG_DOOR_ICE.png b/assets/ray/doors/BG_DOOR_ICE.png index 0bf576255..a2dac6653 100644 Binary files a/assets/ray/doors/BG_DOOR_ICE.png and b/assets/ray/doors/BG_DOOR_ICE.png differ diff --git a/assets/ray/doors/BG_DOOR_KEY_A.png b/assets/ray/doors/BG_DOOR_KEY_A.png index 0f670083f..66e6fb197 100644 Binary files a/assets/ray/doors/BG_DOOR_KEY_A.png and b/assets/ray/doors/BG_DOOR_KEY_A.png differ diff --git a/assets/ray/doors/BG_DOOR_KEY_B.png b/assets/ray/doors/BG_DOOR_KEY_B.png index 8afe14fcb..582433f5d 100644 Binary files a/assets/ray/doors/BG_DOOR_KEY_B.png and b/assets/ray/doors/BG_DOOR_KEY_B.png differ diff --git a/assets/ray/doors/BG_DOOR_KEY_C.png b/assets/ray/doors/BG_DOOR_KEY_C.png index 6328f75fa..b2baf58c6 100644 Binary files a/assets/ray/doors/BG_DOOR_KEY_C.png and b/assets/ray/doors/BG_DOOR_KEY_C.png differ diff --git a/assets/ray/doors/BG_DOOR_MISSILE.png b/assets/ray/doors/BG_DOOR_MISSILE.png index 8356a72f6..6af2d9eea 100644 Binary files a/assets/ray/doors/BG_DOOR_MISSILE.png and b/assets/ray/doors/BG_DOOR_MISSILE.png differ diff --git a/assets/ray/doors/BG_DOOR_SCRIPT.png b/assets/ray/doors/BG_DOOR_SCRIPT.png index ab5174adb..89547eda0 100644 Binary files a/assets/ray/doors/BG_DOOR_SCRIPT.png and b/assets/ray/doors/BG_DOOR_SCRIPT.png differ diff --git a/assets/ray/doors/BG_DOOR_XRAY.png b/assets/ray/doors/BG_DOOR_XRAY.png index 1ab397251..70f29c5fe 100644 Binary files a/assets/ray/doors/BG_DOOR_XRAY.png and b/assets/ray/doors/BG_DOOR_XRAY.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_0.png index 31e9a25c8..df4653bf5 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_0.png and b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_0.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_1.png index 977224659..5a9f1accf 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_1.png and b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_1.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_2.png index bf47d3285..15ef316dd 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_2.png and b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_2.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_3.png index f14690614..898e6c516 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_3.png and b/assets/ray/enemies/ARMORED/E_ARMORED_BLOCK_3.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_0.png index cfa766143..ff6b083ba 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_0.png and b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_0.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_1.png index ff9086353..5f0ee8947 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_1.png and b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_1.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_2.png index 7f00de898..7cdfed2c7 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_2.png and b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_2.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_3.png index c90c882f5..af2084c75 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_3.png and b/assets/ray/enemies/ARMORED/E_ARMORED_DEAD_3.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_0.png index 4b58b7171..aeb0b3789 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_0.png and b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_0.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_1.png index 5b011ff86..7d70d1c43 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_1.png and b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_1.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_2.png index 0108726f3..025d5d9a5 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_2.png and b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_2.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_3.png index 7d9270061..a83a32f57 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_HURT_3.png and b/assets/ray/enemies/ARMORED/E_ARMORED_HURT_3.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_0.png index 349a2d345..e490fa8ce 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_0.png and b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_0.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_1.png index 8c48ff2d8..cdbe4d27f 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_1.png and b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_1.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_2.png index dcad8c6a8..befc93b92 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_2.png and b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_2.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_3.png index 651c3a6db..778fa3d8a 100644 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_3.png and b/assets/ray/enemies/ARMORED/E_ARMORED_SHOOT_3.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0.png deleted file mode 100644 index 36fc87755..000000000 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0.png and /dev/null differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_0.png new file mode 100644 index 000000000..5e07f3776 Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_0.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_1.png new file mode 100644 index 000000000..7acd0addc Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_1.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_2.png new file mode 100644 index 000000000..0baa4a50b Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_2.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_3.png new file mode 100644 index 000000000..26623fb49 Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_3.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1.png deleted file mode 100644 index b385ecf73..000000000 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1.png and /dev/null differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_0.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_0.png new file mode 100644 index 000000000..2aa49d4f6 Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_0.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_1.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_1.png new file mode 100644 index 000000000..e518df84c Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_1.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_2.png new file mode 100644 index 000000000..60c8d4578 Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_2.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_3.png new file mode 100644 index 000000000..cea285ea8 Binary files /dev/null and b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_1_3.png differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_2.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_2.png deleted file mode 100644 index 9163ca4fe..000000000 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_2.png and /dev/null differ diff --git a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_3.png b/assets/ray/enemies/ARMORED/E_ARMORED_WALK_3.png deleted file mode 100644 index c5ddaeacd..000000000 Binary files a/assets/ray/enemies/ARMORED/E_ARMORED_WALK_3.png and /dev/null differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0.png index e3c330bb4..417bf51eb 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0.png and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_I.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_I.png new file mode 100755 index 000000000..8e94f0332 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_M.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_M.png new file mode 100755 index 000000000..015a06712 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_X.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_X.png new file mode 100755 index 000000000..80ac1ff96 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_0_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1.png index 0478a3446..49c1d7d08 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1.png and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_I.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_I.png new file mode 100755 index 000000000..7d1e06170 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_M.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_M.png new file mode 100755 index 000000000..fe758e111 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_X.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_X.png new file mode 100755 index 000000000..62505eb0c Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_1_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2.png index 28410823c..294fbf51a 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2.png and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_I.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_I.png new file mode 100755 index 000000000..980e1514d Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_M.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_M.png new file mode 100755 index 000000000..885313eda Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_X.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_X.png new file mode 100755 index 000000000..93786c9ad Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_2_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3.png index 3e1b5d76c..15d0648ff 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3.png and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_I.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_I.png new file mode 100755 index 000000000..92b16f953 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_M.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_M.png new file mode 100755 index 000000000..1c7d243f7 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_X.png b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_X.png new file mode 100755 index 000000000..bf1e92cfe Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_BLOCK_3_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_0.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0.png index 61f9037a4..136cc9e69 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_DEAD_0.png and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_I.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_I.png new file mode 100755 index 000000000..d424c8eb5 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_M.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_M.png new file mode 100755 index 000000000..38c4a0f88 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_X.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_X.png new file mode 100755 index 000000000..a69984f7b Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_0_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_1.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1.png index 2a3ae3a42..b032cc706 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_DEAD_1.png and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_I.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_I.png new file mode 100755 index 000000000..3fd74478d Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_M.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_M.png new file mode 100755 index 000000000..12d1b66e5 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_X.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_X.png new file mode 100755 index 000000000..94997dd22 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_1_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_2.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2.png index b359450d0..6dcc93e04 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_DEAD_2.png and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_I.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_I.png new file mode 100755 index 000000000..075064656 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_M.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_M.png new file mode 100755 index 000000000..73a16f2fa Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_X.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_X.png new file mode 100755 index 000000000..408ee8a2e Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_2_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_3.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3.png index 95ed72196..8011a5220 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_DEAD_3.png and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_I.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_I.png new file mode 100755 index 000000000..25f039f27 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_M.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_M.png new file mode 100755 index 000000000..d508153c5 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_X.png b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_X.png new file mode 100755 index 000000000..8a35d36e1 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_DEAD_3_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_0.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_0.png index 0b9f96448..c3be613eb 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_HURT_0.png and b/assets/ray/enemies/BOSS/E_BOSS_HURT_0.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_0_I.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_0_I.png new file mode 100755 index 000000000..0da465304 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_0_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_0_M.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_0_M.png new file mode 100755 index 000000000..30e8e803c Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_0_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_0_X.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_0_X.png new file mode 100755 index 000000000..b3728e01a Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_0_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_1.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_1.png index 81fec7496..e3fcd8448 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_HURT_1.png and b/assets/ray/enemies/BOSS/E_BOSS_HURT_1.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_1_I.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_1_I.png new file mode 100755 index 000000000..910dd75ef Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_1_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_1_M.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_1_M.png new file mode 100755 index 000000000..797049beb Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_1_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_1_X.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_1_X.png new file mode 100755 index 000000000..2b61c1b99 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_1_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_2.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_2.png index 4e319e7ec..24ffc2be8 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_HURT_2.png and b/assets/ray/enemies/BOSS/E_BOSS_HURT_2.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_2_I.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_2_I.png new file mode 100755 index 000000000..e111f2347 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_2_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_2_M.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_2_M.png new file mode 100755 index 000000000..17b443821 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_2_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_2_X.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_2_X.png new file mode 100755 index 000000000..db11aa972 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_2_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_3.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_3.png index af711c58d..d036c816c 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_HURT_3.png and b/assets/ray/enemies/BOSS/E_BOSS_HURT_3.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_3_I.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_3_I.png new file mode 100755 index 000000000..b64447c84 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_3_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_3_M.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_3_M.png new file mode 100755 index 000000000..74d075c74 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_3_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_HURT_3_X.png b/assets/ray/enemies/BOSS/E_BOSS_HURT_3_X.png new file mode 100755 index 000000000..f78e0cbd9 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_HURT_3_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0.png index 618dfadd1..61d68e905 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0.png and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_I.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_I.png new file mode 100755 index 000000000..fd7d4b271 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_M.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_M.png new file mode 100755 index 000000000..c942cb173 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_X.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_X.png new file mode 100755 index 000000000..f1bc71c92 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_0_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1.png index 780d23e56..64a1265a4 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1.png and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_I.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_I.png new file mode 100755 index 000000000..98c48998b Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_M.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_M.png new file mode 100755 index 000000000..3668707c2 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_X.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_X.png new file mode 100755 index 000000000..de104d818 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_1_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2.png index 27b8dba82..23388a5cb 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2.png and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_I.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_I.png new file mode 100755 index 000000000..6ce251a32 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_M.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_M.png new file mode 100755 index 000000000..c7b4cf7ab Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_X.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_X.png new file mode 100755 index 000000000..be1f67ecc Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_2_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3.png index 7d7a8ec2a..58cacbfa6 100644 Binary files a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3.png and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_I.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_I.png new file mode 100755 index 000000000..f75d628bb Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_M.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_M.png new file mode 100755 index 000000000..045cfdd67 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_X.png b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_X.png new file mode 100755 index 000000000..19af6aea9 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_SHOOT_3_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0.png deleted file mode 100644 index 4b3e09d99..000000000 Binary files a/assets/ray/enemies/BOSS/E_BOSS_WALK_0.png and /dev/null differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0.png new file mode 100644 index 000000000..27d916354 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_I.png new file mode 100644 index 000000000..3aba66345 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_M.png new file mode 100644 index 000000000..670b83dc5 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_X.png new file mode 100644 index 000000000..3c9bc2aac Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_0_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1.png new file mode 100644 index 000000000..3fc6eadef Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_I.png new file mode 100644 index 000000000..215ddc9ef Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_M.png new file mode 100644 index 000000000..c6f2d9967 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_X.png new file mode 100644 index 000000000..55a239f29 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_1_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2.png new file mode 100644 index 000000000..c276b005a Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_I.png new file mode 100644 index 000000000..fff16aa94 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_M.png new file mode 100644 index 000000000..03c2a26e3 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_X.png new file mode 100644 index 000000000..2de5a9714 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_2_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3.png new file mode 100644 index 000000000..a5ec479b4 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_I.png new file mode 100644 index 000000000..5f7a2826e Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_M.png new file mode 100644 index 000000000..4e34261a1 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_X.png new file mode 100644 index 000000000..bc8cdbd0d Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_0_3_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1.png deleted file mode 100644 index aa03364d1..000000000 Binary files a/assets/ray/enemies/BOSS/E_BOSS_WALK_1.png and /dev/null differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0.png new file mode 100644 index 000000000..9dea7a17b Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_I.png new file mode 100644 index 000000000..1d1177297 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_M.png new file mode 100644 index 000000000..52bae159a Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_X.png new file mode 100644 index 000000000..1fe59d9c8 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_0_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1.png new file mode 100644 index 000000000..9a59523bc Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_I.png new file mode 100644 index 000000000..f51e1a606 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_M.png new file mode 100644 index 000000000..6922b22cb Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_X.png new file mode 100644 index 000000000..15857701f Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_1_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2.png new file mode 100644 index 000000000..671065b33 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_I.png new file mode 100644 index 000000000..fe9998880 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_M.png new file mode 100644 index 000000000..d4077116d Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_X.png new file mode 100644 index 000000000..c710a455c Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_2_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3.png new file mode 100644 index 000000000..ae1d6bad9 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_I.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_I.png new file mode 100644 index 000000000..337888c0a Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_I.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_M.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_M.png new file mode 100644 index 000000000..55d7684f1 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_M.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_X.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_X.png new file mode 100644 index 000000000..375956c70 Binary files /dev/null and b/assets/ray/enemies/BOSS/E_BOSS_WALK_1_3_X.png differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_2.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_2.png deleted file mode 100644 index e2c9be371..000000000 Binary files a/assets/ray/enemies/BOSS/E_BOSS_WALK_2.png and /dev/null differ diff --git a/assets/ray/enemies/BOSS/E_BOSS_WALK_3.png b/assets/ray/enemies/BOSS/E_BOSS_WALK_3.png deleted file mode 100644 index fa44afc80..000000000 Binary files a/assets/ray/enemies/BOSS/E_BOSS_WALK_3.png and /dev/null differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0.png deleted file mode 100644 index 7e5f61880..000000000 Binary files a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0.png and /dev/null differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_0.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_0.png new file mode 100644 index 000000000..764295562 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_0.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_1.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_1.png new file mode 100644 index 000000000..78f95ee82 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_1.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_2.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_2.png new file mode 100644 index 000000000..330438db0 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_2.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_3.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_3.png new file mode 100644 index 000000000..eea9909b6 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_3.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1.png deleted file mode 100644 index 04fe0c55f..000000000 Binary files a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1.png and /dev/null differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_0.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_0.png new file mode 100644 index 000000000..b084512c2 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_0.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_1.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_1.png new file mode 100644 index 000000000..ed4b74680 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_1.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_2.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_2.png new file mode 100644 index 000000000..eebfaff07 Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_2.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_3.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_3.png new file mode 100644 index 000000000..da03ddf7c Binary files /dev/null and b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_1_3.png differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_2.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_2.png deleted file mode 100644 index dcb86551b..000000000 Binary files a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_2.png and /dev/null differ diff --git a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_3.png b/assets/ray/enemies/FLAMING/E_FLAMING_WALK_3.png deleted file mode 100644 index f7289e579..000000000 Binary files a/assets/ray/enemies/FLAMING/E_FLAMING_WALK_3.png and /dev/null differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0.png index 886b99425..c0c8482c8 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0_X.png new file mode 100755 index 000000000..af036891a Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_0_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1.png index 08706c67d..5cc9e44ac 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1_X.png new file mode 100755 index 000000000..a6b160c18 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_1_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2.png index d1a1f7feb..7183dda69 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2_X.png new file mode 100755 index 000000000..26f10c716 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_2_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3.png index 7ff5baba9..a08edda23 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3_X.png new file mode 100755 index 000000000..11c595b4c Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_BLOCK_3_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0.png index 99608be29..82eebacbe 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0_X.png new file mode 100755 index 000000000..6202b1a12 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_0_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1.png index 90831eb9c..c2ad1498b 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1_X.png new file mode 100755 index 000000000..e54dec4b0 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_1_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2.png index 7cbb33ccc..4d7af620d 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2_X.png new file mode 100755 index 000000000..3f86154f5 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_2_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3.png index e8cb07597..f9d07ba54 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3_X.png new file mode 100755 index 000000000..561d2c375 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_DEAD_3_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0.png index 9ab462b87..d44102f17 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0_X.png new file mode 100755 index 000000000..c44cb4990 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_0_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1.png index 7e069171c..c322515f0 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1_X.png new file mode 100755 index 000000000..1a8201a7f Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_1_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2.png index 51e8ed4b3..0b5ab7dc7 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2_X.png new file mode 100755 index 000000000..cfd153815 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_2_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3.png index 43596a4a1..e493ffab5 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3_X.png new file mode 100755 index 000000000..b15442df5 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_HURT_3_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0.png index 1f717d044..9259fd8d1 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0_X.png new file mode 100755 index 000000000..fdb4c7407 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_0_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1.png index 8ea68ecdb..4953d3cee 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1_X.png new file mode 100755 index 000000000..72bf1eb03 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_1_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2.png index b61d233c7..40f3d982f 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2_X.png new file mode 100755 index 000000000..a5429dd4a Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_2_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3.png index 97df48cf8..e996a5ebc 100644 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3.png and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3_X.png new file mode 100755 index 000000000..e722b2ef9 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_SHOOT_3_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0.png deleted file mode 100644 index c88b9ca77..000000000 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0.png and /dev/null differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0.png new file mode 100644 index 000000000..b3b5589f4 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0_X.png new file mode 100644 index 000000000..7407abbd4 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_1.png new file mode 100644 index 000000000..cc84d325a Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_1.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_1_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_1_X.png new file mode 100644 index 000000000..24f42bf0c Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_1_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_2.png new file mode 100644 index 000000000..b757818b6 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_2.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_2_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_2_X.png new file mode 100644 index 000000000..65eac1d83 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_2_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_3.png new file mode 100644 index 000000000..db95df765 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_3.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_3_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_3_X.png new file mode 100644 index 000000000..a12fe88dd Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_3_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1.png deleted file mode 100644 index 1f656adc0..000000000 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1.png and /dev/null differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_0.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_0.png new file mode 100644 index 000000000..e46d1253b Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_0.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_0_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_0_X.png new file mode 100644 index 000000000..fed735883 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_0_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_1.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_1.png new file mode 100644 index 000000000..564bc9454 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_1.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_1_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_1_X.png new file mode 100644 index 000000000..2bafced08 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_1_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_2.png new file mode 100644 index 000000000..1e801658a Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_2.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_2_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_2_X.png new file mode 100644 index 000000000..8eccf4342 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_2_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_3.png new file mode 100644 index 000000000..534e2f11a Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_3.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_3_X.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_3_X.png new file mode 100644 index 000000000..fd0558e35 Binary files /dev/null and b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_1_3_X.png differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_2.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_2.png deleted file mode 100644 index bf5d20505..000000000 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_2.png and /dev/null differ diff --git a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_3.png b/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_3.png deleted file mode 100644 index e9dd7a000..000000000 Binary files a/assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_3.png and /dev/null differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_0.png index aee9a6f10..db78581c8 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_0.png and b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_0.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_1.png index 3399aa717..957b0fb44 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_1.png and b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_1.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_2.png index 213c7e7f7..256b56bc6 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_2.png and b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_2.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_3.png index 502c59d12..b8fd44695 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_3.png and b/assets/ray/enemies/NORMAL/E_NORMAL_BLOCK_3.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_0.png index 11e88733e..7dd9afb31 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_0.png and b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_0.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_1.png index 8b24c4899..bd1ae61a6 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_1.png and b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_1.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_2.png index 81b297246..54ace82f5 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_2.png and b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_2.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_3.png index a2dfe2f71..7f339fd75 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_3.png and b/assets/ray/enemies/NORMAL/E_NORMAL_DEAD_3.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_0.png index 3d4a212d6..4592b3510 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_0.png and b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_0.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_1.png index fde21c533..520d0e891 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_1.png and b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_1.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_2.png index 692fd5a81..ad639e881 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_2.png and b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_2.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_3.png index 24ef231b2..f0a01a485 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_HURT_3.png and b/assets/ray/enemies/NORMAL/E_NORMAL_HURT_3.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_0.png index c36d04d30..3e6c0de67 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_0.png and b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_0.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_1.png index 5cd92e6a5..55f4aa3d6 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_1.png and b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_1.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_2.png index 722dcc5e2..1163c9bb9 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_2.png and b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_2.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_3.png index f05771f02..d523986d6 100644 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_3.png and b/assets/ray/enemies/NORMAL/E_NORMAL_SHOOT_3.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0.png deleted file mode 100644 index 34d911bac..000000000 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0.png and /dev/null differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_0.png new file mode 100644 index 000000000..9e906d771 Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_0.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_1.png new file mode 100644 index 000000000..bafce486b Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_1.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_2.png new file mode 100644 index 000000000..0e8d4e12c Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_2.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_3.png new file mode 100644 index 000000000..14281d1a4 Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_3.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1.png deleted file mode 100644 index d37f3b608..000000000 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1.png and /dev/null differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_0.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_0.png new file mode 100644 index 000000000..7ef2e4032 Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_0.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_1.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_1.png new file mode 100644 index 000000000..794f6ebfa Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_1.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_2.png new file mode 100644 index 000000000..0f9cc3ffa Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_2.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_3.png new file mode 100644 index 000000000..69c74f702 Binary files /dev/null and b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_1_3.png differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_2.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_2.png deleted file mode 100644 index 6e987250e..000000000 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_2.png and /dev/null differ diff --git a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_3.png b/assets/ray/enemies/NORMAL/E_NORMAL_WALK_3.png deleted file mode 100644 index a54e339fc..000000000 Binary files a/assets/ray/enemies/NORMAL/E_NORMAL_WALK_3.png and /dev/null differ diff --git a/assets/ray/enemies/NORMAL/GUN_NORMAL.png b/assets/ray/enemies/NORMAL/GUN_NORMAL.png deleted file mode 100644 index 34d66e758..000000000 Binary files a/assets/ray/enemies/NORMAL/GUN_NORMAL.png and /dev/null differ diff --git a/assets/ray/enemies/NORMAL/OBJ_BULLET_NORMAL.png b/assets/ray/enemies/NORMAL/OBJ_BULLET_NORMAL.png deleted file mode 100644 index d4e807895..000000000 Binary files a/assets/ray/enemies/NORMAL/OBJ_BULLET_NORMAL.png and /dev/null differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_0.png b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_0.png index 16f44c708..99f708abb 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_0.png and b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_0.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_1.png b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_1.png index b14843012..74d2c07ae 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_1.png and b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_1.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_2.png b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_2.png index acb3d99c4..86b7ed48c 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_2.png and b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_2.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_3.png b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_3.png index 23def2ff2..ead3b6805 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_BLOCK_3.png and b/assets/ray/enemies/STRONG/E_STRONG_BLOCK_3.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_DEAD_0.png b/assets/ray/enemies/STRONG/E_STRONG_DEAD_0.png index fd9a40e0b..fccefcd6c 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_DEAD_0.png and b/assets/ray/enemies/STRONG/E_STRONG_DEAD_0.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_DEAD_1.png b/assets/ray/enemies/STRONG/E_STRONG_DEAD_1.png index 296c0fa6b..320f00e67 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_DEAD_1.png and b/assets/ray/enemies/STRONG/E_STRONG_DEAD_1.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_DEAD_2.png b/assets/ray/enemies/STRONG/E_STRONG_DEAD_2.png index 47443a094..1196d7e9c 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_DEAD_2.png and b/assets/ray/enemies/STRONG/E_STRONG_DEAD_2.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_DEAD_3.png b/assets/ray/enemies/STRONG/E_STRONG_DEAD_3.png index 75b85ea64..5415e5c0b 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_DEAD_3.png and b/assets/ray/enemies/STRONG/E_STRONG_DEAD_3.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_HURT_0.png b/assets/ray/enemies/STRONG/E_STRONG_HURT_0.png index 4592eb023..f947c6a22 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_HURT_0.png and b/assets/ray/enemies/STRONG/E_STRONG_HURT_0.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_HURT_1.png b/assets/ray/enemies/STRONG/E_STRONG_HURT_1.png index caac5220d..e27727da5 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_HURT_1.png and b/assets/ray/enemies/STRONG/E_STRONG_HURT_1.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_HURT_2.png b/assets/ray/enemies/STRONG/E_STRONG_HURT_2.png index 79de6e23b..39b93a9d2 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_HURT_2.png and b/assets/ray/enemies/STRONG/E_STRONG_HURT_2.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_HURT_3.png b/assets/ray/enemies/STRONG/E_STRONG_HURT_3.png index ac1118fc9..df6ef3607 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_HURT_3.png and b/assets/ray/enemies/STRONG/E_STRONG_HURT_3.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_0.png b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_0.png index e5cdd8ccf..b0cc5e65c 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_0.png and b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_0.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_1.png b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_1.png index 2da665636..ed87274b7 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_1.png and b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_1.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_2.png b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_2.png index 757a61f62..8c34aced6 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_2.png and b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_2.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_3.png b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_3.png index 39119f748..7b72d05ef 100644 Binary files a/assets/ray/enemies/STRONG/E_STRONG_SHOOT_3.png and b/assets/ray/enemies/STRONG/E_STRONG_SHOOT_3.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_0.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_0.png deleted file mode 100644 index 97c2f1e15..000000000 Binary files a/assets/ray/enemies/STRONG/E_STRONG_WALK_0.png and /dev/null differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_0_0.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_0.png new file mode 100644 index 000000000..f4e940d04 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_0.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_0_1.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_1.png new file mode 100644 index 000000000..cd42a0689 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_1.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_0_2.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_2.png new file mode 100644 index 000000000..e02e3ad72 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_2.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_0_3.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_3.png new file mode 100644 index 000000000..e260241b7 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_0_3.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_1.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_1.png deleted file mode 100644 index e0ec7c59e..000000000 Binary files a/assets/ray/enemies/STRONG/E_STRONG_WALK_1.png and /dev/null differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_1_0.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_0.png new file mode 100644 index 000000000..e3a2e2480 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_0.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_1_1.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_1.png new file mode 100644 index 000000000..258afb606 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_1.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_1_2.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_2.png new file mode 100644 index 000000000..8b5a59040 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_2.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_1_3.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_3.png new file mode 100644 index 000000000..1e120f5c8 Binary files /dev/null and b/assets/ray/enemies/STRONG/E_STRONG_WALK_1_3.png differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_2.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_2.png deleted file mode 100644 index 4ab172f71..000000000 Binary files a/assets/ray/enemies/STRONG/E_STRONG_WALK_2.png and /dev/null differ diff --git a/assets/ray/enemies/STRONG/E_STRONG_WALK_3.png b/assets/ray/enemies/STRONG/E_STRONG_WALK_3.png deleted file mode 100644 index a75a21545..000000000 Binary files a/assets/ray/enemies/STRONG/E_STRONG_WALK_3.png and /dev/null differ diff --git a/assets/ray/env/BASE/BG_BASE_CEILING.png b/assets/ray/env/BASE/BG_BASE_CEILING.png new file mode 100644 index 000000000..8ae3aee8b Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_CEILING.png differ diff --git a/assets/ray/env/BASE/BG_BASE_FLOOR.png b/assets/ray/env/BASE/BG_BASE_FLOOR.png new file mode 100644 index 000000000..e1de173c1 Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_FLOOR.png differ diff --git a/assets/ray/env/BASE/BG_BASE_WALL_1.png b/assets/ray/env/BASE/BG_BASE_WALL_1.png new file mode 100644 index 000000000..21c006ddd Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_WALL_1.png differ diff --git a/assets/ray/env/BASE/BG_BASE_WALL_2.png b/assets/ray/env/BASE/BG_BASE_WALL_2.png new file mode 100644 index 000000000..980bf09f9 Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_WALL_2.png differ diff --git a/assets/ray/env/BASE/BG_BASE_WALL_3.png b/assets/ray/env/BASE/BG_BASE_WALL_3.png new file mode 100644 index 000000000..221393628 Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_WALL_3.png differ diff --git a/assets/ray/env/BASE/BG_BASE_WALL_4.png b/assets/ray/env/BASE/BG_BASE_WALL_4.png new file mode 100644 index 000000000..3bbd2e2ae Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_WALL_4.png differ diff --git a/assets/ray/env/BASE/BG_BASE_WALL_5.png b/assets/ray/env/BASE/BG_BASE_WALL_5.png new file mode 100644 index 000000000..66ed76230 Binary files /dev/null and b/assets/ray/env/BASE/BG_BASE_WALL_5.png differ diff --git a/assets/ray/env/BG_CEILING.png b/assets/ray/env/BG_CEILING.png deleted file mode 100644 index 71d6bc1c4..000000000 Binary files a/assets/ray/env/BG_CEILING.png and /dev/null differ diff --git a/assets/ray/env/BG_FLOOR.png b/assets/ray/env/BG_FLOOR.png deleted file mode 100644 index 6e6ba3fb7..000000000 Binary files a/assets/ray/env/BG_FLOOR.png and /dev/null differ diff --git a/assets/ray/env/BG_WALL_1.png b/assets/ray/env/BG_WALL_1.png deleted file mode 100644 index 7ea986ff8..000000000 Binary files a/assets/ray/env/BG_WALL_1.png and /dev/null differ diff --git a/assets/ray/env/BG_WALL_2.png b/assets/ray/env/BG_WALL_2.png deleted file mode 100644 index f27cec50e..000000000 Binary files a/assets/ray/env/BG_WALL_2.png and /dev/null differ diff --git a/assets/ray/env/BG_WALL_3.png b/assets/ray/env/BG_WALL_3.png deleted file mode 100644 index 26b520234..000000000 Binary files a/assets/ray/env/BG_WALL_3.png and /dev/null differ diff --git a/assets/ray/env/BG_WALL_4.png b/assets/ray/env/BG_WALL_4.png deleted file mode 100644 index a9678c2c9..000000000 Binary files a/assets/ray/env/BG_WALL_4.png and /dev/null differ diff --git a/assets/ray/env/BG_WALL_5.png b/assets/ray/env/BG_WALL_5.png deleted file mode 100644 index 48ddda7de..000000000 Binary files a/assets/ray/env/BG_WALL_5.png and /dev/null differ diff --git a/assets/ray/env/CAVE/BG_CAVE_CEILING.png b/assets/ray/env/CAVE/BG_CAVE_CEILING.png new file mode 100644 index 000000000..8683110c1 Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_CEILING.png differ diff --git a/assets/ray/env/CAVE/BG_CAVE_FLOOR.png b/assets/ray/env/CAVE/BG_CAVE_FLOOR.png new file mode 100644 index 000000000..065650128 Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_FLOOR.png differ diff --git a/assets/ray/env/CAVE/BG_CAVE_WALL_1.png b/assets/ray/env/CAVE/BG_CAVE_WALL_1.png new file mode 100644 index 000000000..599fe756e Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_WALL_1.png differ diff --git a/assets/ray/env/CAVE/BG_CAVE_WALL_2.png b/assets/ray/env/CAVE/BG_CAVE_WALL_2.png new file mode 100644 index 000000000..cbdfe0e9a Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_WALL_2.png differ diff --git a/assets/ray/env/CAVE/BG_CAVE_WALL_3.png b/assets/ray/env/CAVE/BG_CAVE_WALL_3.png new file mode 100644 index 000000000..cac7bce65 Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_WALL_3.png differ diff --git a/assets/ray/env/CAVE/BG_CAVE_WALL_4.png b/assets/ray/env/CAVE/BG_CAVE_WALL_4.png new file mode 100644 index 000000000..365f6677a Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_WALL_4.png differ diff --git a/assets/ray/env/CAVE/BG_CAVE_WALL_5.png b/assets/ray/env/CAVE/BG_CAVE_WALL_5.png new file mode 100644 index 000000000..db4dbfc21 Binary files /dev/null and b/assets/ray/env/CAVE/BG_CAVE_WALL_5.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_CEILING.png b/assets/ray/env/JUNGLE/BG_JUNGLE_CEILING.png new file mode 100644 index 000000000..4a1423ee7 Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_CEILING.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_FLOOR.png b/assets/ray/env/JUNGLE/BG_JUNGLE_FLOOR.png new file mode 100644 index 000000000..564e33fc9 Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_FLOOR.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_1.png b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_1.png new file mode 100644 index 000000000..24e62833a Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_1.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_2.png b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_2.png new file mode 100644 index 000000000..0480f6c43 Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_2.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_3.png b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_3.png new file mode 100644 index 000000000..2c73fbd17 Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_3.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_4.png b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_4.png new file mode 100644 index 000000000..41e61abdf Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_4.png differ diff --git a/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_5.png b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_5.png new file mode 100644 index 000000000..c21f277c7 Binary files /dev/null and b/assets/ray/env/JUNGLE/BG_JUNGLE_WALL_5.png differ diff --git a/assets/ray/env/test/ICE_FLOOR.png b/assets/ray/env/test/ICE_FLOOR.png deleted file mode 100644 index e69f619b9..000000000 Binary files a/assets/ray/env/test/ICE_FLOOR.png and /dev/null differ diff --git a/assets/ray/env/test/ICE_WALL.png b/assets/ray/env/test/ICE_WALL.png deleted file mode 100644 index aac2669ea..000000000 Binary files a/assets/ray/env/test/ICE_WALL.png and /dev/null differ diff --git a/assets/ray/env/test/JUN_FLOOR.png b/assets/ray/env/test/JUN_FLOOR.png deleted file mode 100644 index bbae33838..000000000 Binary files a/assets/ray/env/test/JUN_FLOOR.png and /dev/null differ diff --git a/assets/ray/env/test/JUN_WALL.png b/assets/ray/env/test/JUN_WALL.png deleted file mode 100644 index 902483786..000000000 Binary files a/assets/ray/env/test/JUN_WALL.png and /dev/null differ diff --git a/assets/ray/env/test/LAV_FLOOR.png b/assets/ray/env/test/LAV_FLOOR.png deleted file mode 100644 index 1e227ef4c..000000000 Binary files a/assets/ray/env/test/LAV_FLOOR.png and /dev/null differ diff --git a/assets/ray/env/test/LAV_WALL.png b/assets/ray/env/test/LAV_WALL.png deleted file mode 100644 index 2d0342f82..000000000 Binary files a/assets/ray/env/test/LAV_WALL.png and /dev/null differ diff --git a/assets/ray/env/test/SPA_FLOOR.png b/assets/ray/env/test/SPA_FLOOR.png deleted file mode 100644 index 7695feb82..000000000 Binary files a/assets/ray/env/test/SPA_FLOOR.png and /dev/null differ diff --git a/assets/ray/env/test/SPA_WALL.png b/assets/ray/env/test/SPA_WALL.png deleted file mode 100644 index 7d53f058c..000000000 Binary files a/assets/ray/env/test/SPA_WALL.png and /dev/null differ diff --git a/assets/ray/friends/OBJ_SCENERY_F1.png b/assets/ray/friends/OBJ_SCENERY_F1.png new file mode 100644 index 000000000..a838f7098 Binary files /dev/null and b/assets/ray/friends/OBJ_SCENERY_F1.png differ diff --git a/assets/ray/friends/OBJ_SCENERY_F2.png b/assets/ray/friends/OBJ_SCENERY_F2.png new file mode 100644 index 000000000..be26fca4e Binary files /dev/null and b/assets/ray/friends/OBJ_SCENERY_F2.png differ diff --git a/assets/ray/friends/OBJ_SCENERY_F3.png b/assets/ray/friends/OBJ_SCENERY_F3.png new file mode 100644 index 000000000..cf9a82d9a Binary files /dev/null and b/assets/ray/friends/OBJ_SCENERY_F3.png differ diff --git a/assets/ray/friends/OBJ_SCENERY_F4.png b/assets/ray/friends/OBJ_SCENERY_F4.png new file mode 100644 index 000000000..7c9a7414a Binary files /dev/null and b/assets/ray/friends/OBJ_SCENERY_F4.png differ diff --git a/assets/ray/friends/OBJ_SCENERY_F5.png b/assets/ray/friends/OBJ_SCENERY_F5.png new file mode 100644 index 000000000..a91720450 Binary files /dev/null and b/assets/ray/friends/OBJ_SCENERY_F5.png differ diff --git a/assets/ray/friends/OBJ_SCENERY_F6.png b/assets/ray/friends/OBJ_SCENERY_F6.png new file mode 100644 index 000000000..aa8179c05 Binary files /dev/null and b/assets/ray/friends/OBJ_SCENERY_F6.png differ diff --git a/assets/ray/guns/GUN_ICE.png b/assets/ray/guns/GUN_ICE.png index 76f98df1d..9767eb3c8 100644 Binary files a/assets/ray/guns/GUN_ICE.png and b/assets/ray/guns/GUN_ICE.png differ diff --git a/assets/ray/guns/GUN_MISSILE.png b/assets/ray/guns/GUN_MISSILE.png index 0048bb0f4..6c0ceecc5 100644 Binary files a/assets/ray/guns/GUN_MISSILE.png and b/assets/ray/guns/GUN_MISSILE.png differ diff --git a/assets/ray/guns/GUN_NORMAL.png b/assets/ray/guns/GUN_NORMAL.png new file mode 100644 index 000000000..11ff687e6 Binary files /dev/null and b/assets/ray/guns/GUN_NORMAL.png differ diff --git a/assets/ray/guns/GUN_XRAY.png b/assets/ray/guns/GUN_XRAY.png index 6cb5c6d33..4a1ccf78e 100644 Binary files a/assets/ray/guns/GUN_XRAY.png and b/assets/ray/guns/GUN_XRAY.png differ diff --git a/assets/ray/guns/HUD_MISSILE.png b/assets/ray/guns/HUD_MISSILE.png new file mode 100644 index 000000000..b7d4c6344 Binary files /dev/null and b/assets/ray/guns/HUD_MISSILE.png differ diff --git a/assets/ray/guns/OBJ_BULLET_CHARGE.png b/assets/ray/guns/OBJ_BULLET_CHARGE.png index ecfc9e712..f12af3b3b 100644 Binary files a/assets/ray/guns/OBJ_BULLET_CHARGE.png and b/assets/ray/guns/OBJ_BULLET_CHARGE.png differ diff --git a/assets/ray/guns/OBJ_BULLET_ICE.png b/assets/ray/guns/OBJ_BULLET_ICE.png index 567cc0425..a7053e2ca 100644 Binary files a/assets/ray/guns/OBJ_BULLET_ICE.png and b/assets/ray/guns/OBJ_BULLET_ICE.png differ diff --git a/assets/ray/guns/OBJ_BULLET_MISSILE.png b/assets/ray/guns/OBJ_BULLET_MISSILE.png index 8115b6fba..56af51824 100644 Binary files a/assets/ray/guns/OBJ_BULLET_MISSILE.png and b/assets/ray/guns/OBJ_BULLET_MISSILE.png differ diff --git a/assets/ray/guns/OBJ_BULLET_NORMAL.png b/assets/ray/guns/OBJ_BULLET_NORMAL.png new file mode 100644 index 000000000..fbc56ef54 Binary files /dev/null and b/assets/ray/guns/OBJ_BULLET_NORMAL.png differ diff --git a/assets/ray/guns/OBJ_BULLET_XRAY.png b/assets/ray/guns/OBJ_BULLET_XRAY.png index c8b9294d0..c82ad5dc8 100644 Binary files a/assets/ray/guns/OBJ_BULLET_XRAY.png and b/assets/ray/guns/OBJ_BULLET_XRAY.png differ diff --git a/assets/ray/items/OBJ_ITEM_BEAM.png b/assets/ray/items/OBJ_ITEM_BEAM.png index 9f919f7a6..50fe13239 100644 Binary files a/assets/ray/items/OBJ_ITEM_BEAM.png and b/assets/ray/items/OBJ_ITEM_BEAM.png differ diff --git a/assets/ray/items/OBJ_ITEM_CHARGE_BEAM.png b/assets/ray/items/OBJ_ITEM_CHARGE_BEAM.png index 66b75495a..1e30d6bec 100644 Binary files a/assets/ray/items/OBJ_ITEM_CHARGE_BEAM.png and b/assets/ray/items/OBJ_ITEM_CHARGE_BEAM.png differ diff --git a/assets/ray/items/OBJ_ITEM_ENERGY_TANK.png b/assets/ray/items/OBJ_ITEM_ENERGY_TANK.png index a16e04dca..72ab02bd9 100644 Binary files a/assets/ray/items/OBJ_ITEM_ENERGY_TANK.png and b/assets/ray/items/OBJ_ITEM_ENERGY_TANK.png differ diff --git a/assets/ray/items/OBJ_ITEM_ICE.png b/assets/ray/items/OBJ_ITEM_ICE.png index 9d7977e8d..239c88cee 100644 Binary files a/assets/ray/items/OBJ_ITEM_ICE.png and b/assets/ray/items/OBJ_ITEM_ICE.png differ diff --git a/assets/ray/items/OBJ_ITEM_KEY_A.png b/assets/ray/items/OBJ_ITEM_KEY_A.png index 1faf1f2f6..55bb3caf5 100644 Binary files a/assets/ray/items/OBJ_ITEM_KEY_A.png and b/assets/ray/items/OBJ_ITEM_KEY_A.png differ diff --git a/assets/ray/items/OBJ_ITEM_KEY_B.png b/assets/ray/items/OBJ_ITEM_KEY_B.png index c5ec0546e..3c7c4df8a 100644 Binary files a/assets/ray/items/OBJ_ITEM_KEY_B.png and b/assets/ray/items/OBJ_ITEM_KEY_B.png differ diff --git a/assets/ray/items/OBJ_ITEM_KEY_C.png b/assets/ray/items/OBJ_ITEM_KEY_C.png index cb3383200..1006592be 100644 Binary files a/assets/ray/items/OBJ_ITEM_KEY_C.png and b/assets/ray/items/OBJ_ITEM_KEY_C.png differ diff --git a/assets/ray/items/OBJ_ITEM_MISSILE.png b/assets/ray/items/OBJ_ITEM_MISSILE.png index b04a0ab95..a22e0f238 100644 Binary files a/assets/ray/items/OBJ_ITEM_MISSILE.png and b/assets/ray/items/OBJ_ITEM_MISSILE.png differ diff --git a/assets/ray/items/OBJ_ITEM_PICKUP_ENERGY.png b/assets/ray/items/OBJ_ITEM_PICKUP_ENERGY.png index d733b5c9e..4b822b069 100644 Binary files a/assets/ray/items/OBJ_ITEM_PICKUP_ENERGY.png and b/assets/ray/items/OBJ_ITEM_PICKUP_ENERGY.png differ diff --git a/assets/ray/items/OBJ_ITEM_PICKUP_MISSILE.png b/assets/ray/items/OBJ_ITEM_PICKUP_MISSILE.png index 90fea127f..ccc04dfc2 100644 Binary files a/assets/ray/items/OBJ_ITEM_PICKUP_MISSILE.png and b/assets/ray/items/OBJ_ITEM_PICKUP_MISSILE.png differ diff --git a/assets/ray/items/OBJ_ITEM_SUIT_LAVA.png b/assets/ray/items/OBJ_ITEM_SUIT_LAVA.png index 40077e6c9..eaecf0a20 100644 Binary files a/assets/ray/items/OBJ_ITEM_SUIT_LAVA.png and b/assets/ray/items/OBJ_ITEM_SUIT_LAVA.png differ diff --git a/assets/ray/items/OBJ_ITEM_SUIT_WATER.png b/assets/ray/items/OBJ_ITEM_SUIT_WATER.png index 5c286df58..f180b7904 100644 Binary files a/assets/ray/items/OBJ_ITEM_SUIT_WATER.png and b/assets/ray/items/OBJ_ITEM_SUIT_WATER.png differ diff --git a/assets/ray/items/OBJ_ITEM_XRAY.png b/assets/ray/items/OBJ_ITEM_XRAY.png index 84efd8d7a..d28599ae2 100644 Binary files a/assets/ray/items/OBJ_ITEM_XRAY.png and b/assets/ray/items/OBJ_ITEM_XRAY.png differ diff --git a/assets/ray/maps/0.rmd b/assets/ray/maps/0.rmd index d453b50d0..75f051214 100755 Binary files a/assets/ray/maps/0.rmd and b/assets/ray/maps/0.rmd differ diff --git a/assets/ray/maps/1.rmd b/assets/ray/maps/1.rmd index fac6c6ba3..eb9b4d040 100755 Binary files a/assets/ray/maps/1.rmd and b/assets/ray/maps/1.rmd differ diff --git a/assets/ray/maps/2.rmd b/assets/ray/maps/2.rmd index 37a8175d2..0d776d1ac 100755 Binary files a/assets/ray/maps/2.rmd and b/assets/ray/maps/2.rmd differ diff --git a/assets/ray/maps/3.rmd b/assets/ray/maps/3.rmd index 03bd1a3e9..cc6d4ceec 100755 Binary files a/assets/ray/maps/3.rmd and b/assets/ray/maps/3.rmd differ diff --git a/assets/ray/maps/4.rmd b/assets/ray/maps/4.rmd index 3529cb0f0..a22317830 100755 Binary files a/assets/ray/maps/4.rmd and b/assets/ray/maps/4.rmd differ diff --git a/assets/ray/maps/5.rmd b/assets/ray/maps/5.rmd index 446e1b9f5..2d952ebf7 100755 Binary files a/assets/ray/maps/5.rmd and b/assets/ray/maps/5.rmd differ diff --git a/assets/ray/music/base_0.mid b/assets/ray/music/base_0.mid new file mode 100644 index 000000000..cbb038b89 Binary files /dev/null and b/assets/ray/music/base_0.mid differ diff --git a/assets/ray/music/base_1.mid b/assets/ray/music/base_1.mid new file mode 100644 index 000000000..a7f00d8e3 Binary files /dev/null and b/assets/ray/music/base_1.mid differ diff --git a/assets/ray/music/cave_0.mid b/assets/ray/music/cave_0.mid new file mode 100644 index 000000000..8212001b3 Binary files /dev/null and b/assets/ray/music/cave_0.mid differ diff --git a/assets/ray/music/cave_1.mid b/assets/ray/music/cave_1.mid new file mode 100644 index 000000000..3a7532e7e Binary files /dev/null and b/assets/ray/music/cave_1.mid differ diff --git a/assets/ray/music/jungle_0.mid b/assets/ray/music/jungle_0.mid new file mode 100644 index 000000000..8951478dd Binary files /dev/null and b/assets/ray/music/jungle_0.mid differ diff --git a/assets/ray/music/jungle_1.mid b/assets/ray/music/jungle_1.mid new file mode 100644 index 000000000..2d1ebdfae Binary files /dev/null and b/assets/ray/music/jungle_1.mid differ diff --git a/assets/ray/music/ray_boss.mid b/assets/ray/music/ray_boss.mid new file mode 100644 index 000000000..3fdecccd3 Binary files /dev/null and b/assets/ray/music/ray_boss.mid differ diff --git a/assets/ray/scenery/OBJ_SCENERY_PORTAL.png b/assets/ray/scenery/OBJ_SCENERY_PORTAL.png index 5e2c723ad..2622ccb6d 100644 Binary files a/assets/ray/scenery/OBJ_SCENERY_PORTAL.png and b/assets/ray/scenery/OBJ_SCENERY_PORTAL.png differ diff --git a/assets/ray/scenery/OBJ_SCENERY_TERMINAL.png b/assets/ray/scenery/OBJ_SCENERY_TERMINAL.png index 3f1062c9a..8f4904c43 100644 Binary files a/assets/ray/scenery/OBJ_SCENERY_TERMINAL.png and b/assets/ray/scenery/OBJ_SCENERY_TERMINAL.png differ diff --git a/components/hdw-bzr/hdw-bzr.c b/components/hdw-bzr/hdw-bzr.c index 2db7578ce..fd299fda5 100644 --- a/components/hdw-bzr/hdw-bzr.c +++ b/components/hdw-bzr/hdw-bzr.c @@ -268,12 +268,6 @@ static void bzrPlayTrack(bzrTrack_t* trackL, bzrTrack_t* trackR, const song_t* s */ void bzrPlayBgm(const song_t* song, buzzerPlayTrack_t track) { - // Don't play if muted - if (0 == bgmVolume) - { - return; - } - // Play this song on the BGM track bzrPlayTrack(&buzzers[0].bgm, &buzzers[1].bgm, song, track); @@ -295,12 +289,6 @@ void bzrPlayBgm(const song_t* song, buzzerPlayTrack_t track) */ void bzrPlaySfx(const song_t* song, buzzerPlayTrack_t track) { - // Don't play if muted - if (0 == sfxVolume) - { - return; - } - // Play this song on the SFX track bzrPlayTrack(&buzzers[0].sfx, &buzzers[1].sfx, song, track); @@ -421,8 +409,8 @@ static bool IRAM_ATTR buzzer_check_next_note_isr(gptimer_handle_t timer, const g int32_t tElapsedUs = tNowUs - tLastLoopUs; tLastLoopUs = tNowUs; - // Don't do much if muted or paused. Check here so that tElapsedUs stays sane - if (bzrPaused || ((0 == bgmVolume) && (0 == sfxVolume))) + // Don't do much if paused. Check here so that tElapsedUs stays sane + if (bzrPaused) { return false; } @@ -535,14 +523,18 @@ static bool IRAM_ATTR buzzer_track_check_next_note(bzrTrack_t* track, buzzerPlay /** * @brief Pause the buzzer but do not reset the song + * + * @return true if the buzzer was running and paused, false if it was not running to begin with */ -void bzrPause(void) +bool bzrPause(void) { if (!bzrPaused) { bzrPaused = true; bzrStop(false); + return true; } + return false; } /** diff --git a/components/hdw-bzr/include/hdw-bzr.h b/components/hdw-bzr/include/hdw-bzr.h index e826c0496..512b029cf 100644 --- a/components/hdw-bzr/include/hdw-bzr.h +++ b/components/hdw-bzr/include/hdw-bzr.h @@ -268,7 +268,7 @@ void bzrPlaySfx(const song_t* song, buzzerPlayTrack_t track); void bzrStop(bool resetTracks); void bzrPlayNote(noteFrequency_t freq, buzzerPlayTrack_t track, uint16_t volume); void bzrStopNote(buzzerPlayTrack_t track); -void bzrPause(void); +bool bzrPause(void); void bzrResume(void); void* bzrSave(void); void bzrRestore(void* data); diff --git a/components/hdw-nvs/CMakeLists.txt b/components/hdw-nvs/CMakeLists.txt index 778a5a873..b88358ccb 100644 --- a/components/hdw-nvs/CMakeLists.txt +++ b/components/hdw-nvs/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRCS "hdw-nvs.c" INCLUDE_DIRS "include" - REQUIRES nvs_flash) + REQUIRES nvs_flash + PRIV_REQUIRES hdw-bzr) diff --git a/components/hdw-nvs/hdw-nvs.c b/components/hdw-nvs/hdw-nvs.c index 82173a5e9..ca42f53ce 100644 --- a/components/hdw-nvs/hdw-nvs.c +++ b/components/hdw-nvs/hdw-nvs.c @@ -8,6 +8,7 @@ #include #include "hdw-nvs.h" +#include "hdw-bzr.h" //============================================================================== // Functions @@ -89,20 +90,31 @@ bool deinitNvs(void) */ bool eraseNvs(void) { + bool bzrPaused = bzrPause(); + bool retVal = false; + switch (nvs_flash_erase()) { case ESP_OK: { // NVS erased successfully, need to re-initialize - return initNvs(true); + retVal = initNvs(true); + break; } default: case ESP_ERR_NOT_FOUND: { // Couldn't erase flash - return false; + break; } } + + // Resume the buzzer if it was paused + if (bzrPaused) + { + bzrResume(); + } + return retVal; } /** @@ -139,14 +151,16 @@ bool writeNvs32(const char* key, int32_t val) */ bool readNamespaceNvs32(const char* namespace, const char* key, int32_t* outVal) { + // Pause the buzzer before NVS operations + bool bzrPaused = bzrPause(); + bool retVal = false; + nvs_handle_t handle; esp_err_t openErr = nvs_open(namespace, NVS_READONLY, &handle); switch (openErr) { case ESP_OK: { - // Assume the commit is bad - bool readOk = false; // Write the NVS esp_err_t readErr = nvs_get_i32(handle, key, outVal); // Check the write error @@ -154,7 +168,7 @@ bool readNamespaceNvs32(const char* namespace, const char* key, int32_t* outVal) { case ESP_OK: { - readOk = true; + retVal = true; break; } default: @@ -169,7 +183,7 @@ bool readNamespaceNvs32(const char* namespace, const char* key, int32_t* outVal) } // Close the handle nvs_close(handle); - return readOk; + break; } default: case ESP_ERR_NVS_NOT_INITIALIZED: @@ -179,9 +193,16 @@ bool readNamespaceNvs32(const char* namespace, const char* key, int32_t* outVal) case ESP_ERR_NO_MEM: { ESP_LOGE("NVS", "%s openErr %s", __func__, esp_err_to_name(openErr)); - return false; + break; } } + + // Resume the buzzer if it was paused + if (bzrPaused) + { + bzrResume(); + } + return retVal; } /** @@ -194,14 +215,15 @@ bool readNamespaceNvs32(const char* namespace, const char* key, int32_t* outVal) */ bool writeNamespaceNvs32(const char* namespace, const char* key, int32_t val) { + bool bzrPaused = bzrPause(); + bool retVal = false; + nvs_handle_t handle; esp_err_t openErr = nvs_open(namespace, NVS_READWRITE, &handle); switch (openErr) { case ESP_OK: { - // Assume the commit is bad - bool commitOk = false; // Write the NVS esp_err_t writeErr = nvs_set_i32(handle, key, val); // Check the write error @@ -210,7 +232,7 @@ bool writeNamespaceNvs32(const char* namespace, const char* key, int32_t val) case ESP_OK: { // Commit NVS - commitOk = (ESP_OK == nvs_commit(handle)); + retVal = (ESP_OK == nvs_commit(handle)); break; } default: @@ -227,7 +249,7 @@ bool writeNamespaceNvs32(const char* namespace, const char* key, int32_t val) // Close the handle nvs_close(handle); - return commitOk; + break; } default: case ESP_ERR_NVS_NOT_INITIALIZED: @@ -237,9 +259,16 @@ bool writeNamespaceNvs32(const char* namespace, const char* key, int32_t val) case ESP_ERR_NO_MEM: { ESP_LOGE("NVS", "%s openErr %s", __func__, esp_err_to_name(openErr)); - return false; + break; } } + + // Resume the buzzer if it was paused + if (bzrPaused) + { + bzrResume(); + } + return retVal; } /** @@ -285,14 +314,15 @@ bool writeNvsBlob(const char* key, const void* value, size_t length) */ bool readNamespaceNvsBlob(const char* namespace, const char* key, void* out_value, size_t* length) { + bool bzrPaused = bzrPause(); + bool retVal = false; + nvs_handle_t handle; esp_err_t openErr = nvs_open(namespace, NVS_READONLY, &handle); switch (openErr) { case ESP_OK: { - // Assume the commit is bad - bool readOk = false; // Write the NVS esp_err_t readErr = nvs_get_blob(handle, key, out_value, length); // Check the write error @@ -300,7 +330,7 @@ bool readNamespaceNvsBlob(const char* namespace, const char* key, void* out_valu { case ESP_OK: { - readOk = true; + retVal = true; break; } default: @@ -315,7 +345,7 @@ bool readNamespaceNvsBlob(const char* namespace, const char* key, void* out_valu } // Close the handle nvs_close(handle); - return readOk; + break; } default: case ESP_ERR_NVS_NOT_INITIALIZED: @@ -325,9 +355,16 @@ bool readNamespaceNvsBlob(const char* namespace, const char* key, void* out_valu case ESP_ERR_NO_MEM: { ESP_LOGE("NVS", "%s openErr %s", __func__, esp_err_to_name(openErr)); - return false; + break; } } + + // Resume the buzzer if it was paused + if (bzrPaused) + { + bzrResume(); + } + return retVal; } /** @@ -341,14 +378,15 @@ bool readNamespaceNvsBlob(const char* namespace, const char* key, void* out_valu */ bool writeNamespaceNvsBlob(const char* namespace, const char* key, const void* value, size_t length) { + bool bzrPaused = bzrPause(); + bool retVal = false; + nvs_handle_t handle; esp_err_t openErr = nvs_open(namespace, NVS_READWRITE, &handle); switch (openErr) { case ESP_OK: { - // Assume the commit is bad - bool commitOk = false; // Write the NVS esp_err_t writeErr = nvs_set_blob(handle, key, value, length); // Check the write error @@ -357,7 +395,7 @@ bool writeNamespaceNvsBlob(const char* namespace, const char* key, const void* v case ESP_OK: { // Commit NVS - commitOk = (ESP_OK == nvs_commit(handle)); + retVal = (ESP_OK == nvs_commit(handle)); break; } default: @@ -374,7 +412,7 @@ bool writeNamespaceNvsBlob(const char* namespace, const char* key, const void* v // Close the handle nvs_close(handle); - return commitOk; + break; } default: case ESP_ERR_NVS_NOT_INITIALIZED: @@ -384,9 +422,16 @@ bool writeNamespaceNvsBlob(const char* namespace, const char* key, const void* v case ESP_ERR_NO_MEM: { ESP_LOGE("NVS", "%s openErr %s", __func__, esp_err_to_name(openErr)); - return false; + break; } } + + // Resume the buzzer if it was paused + if (bzrPaused) + { + bzrResume(); + } + return retVal; } /** @@ -409,14 +454,15 @@ bool eraseNvsKey(const char* key) */ bool eraseNamespaceNvsKey(const char* namespace, const char* key) { + bool bzrPaused = bzrPause(); + bool retVal = false; + nvs_handle_t handle; esp_err_t openErr = nvs_open(namespace, NVS_READWRITE, &handle); switch (openErr) { case ESP_OK: { - // Assume the commit is bad - bool commitOk = false; // Write the NVS esp_err_t writeErr = nvs_erase_key(handle, key); // Check the write error @@ -425,7 +471,7 @@ bool eraseNamespaceNvsKey(const char* namespace, const char* key) case ESP_OK: { // Commit NVS - commitOk = (ESP_OK == nvs_commit(handle)); + retVal = (ESP_OK == nvs_commit(handle)); break; } default: @@ -442,7 +488,7 @@ bool eraseNamespaceNvsKey(const char* namespace, const char* key) // Close the handle nvs_close(handle); - return commitOk; + break; } default: case ESP_ERR_NVS_NOT_INITIALIZED: @@ -452,9 +498,16 @@ bool eraseNamespaceNvsKey(const char* namespace, const char* key) case ESP_ERR_NO_MEM: { ESP_LOGE("NVS", "%s openErr %s", __func__, esp_err_to_name(openErr)); - return false; + break; } } + + // Resume the buzzer if it was paused + if (bzrPaused) + { + bzrResume(); + } + return retVal; } /** diff --git a/emulator/src/components/hdw-bzr/hdw-bzr.c b/emulator/src/components/hdw-bzr/hdw-bzr.c index 69e036d68..b66fe5906 100644 --- a/emulator/src/components/hdw-bzr/hdw-bzr.c +++ b/emulator/src/components/hdw-bzr/hdw-bzr.c @@ -461,14 +461,18 @@ void EmuSoundCb(struct SoundDriver* sd, short* in, short* out, int samples_R, in /** * @brief Pause the buzzer but do not reset the song + * + * @return true if the buzzer was running and paused, false if it was not running to begin with */ -void bzrPause(void) +bool bzrPause(void) { if (!bzrPaused) { bzrPaused = true; bzrStop(false); + return true; } + return false; } /** diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 68a8e581b..676f0a665 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -70,6 +70,7 @@ idf_component_register(SRCS "swadge2024.c" "modes/ray/ray_tex_manager.c" "modes/ray/ray_player.c" "modes/ray/ray_warp_screen.c" + "modes/ray/ray_death_screen.c" "modes/ray/enemies/ray_enemy.c" "modes/ray/enemies/ray_enemy.h" "modes/ray/enemies/ray_enemy_armored.c" diff --git a/main/asset_loaders/spiffs_song.c b/main/asset_loaders/spiffs_song.c index b5a80787d..294b50ce4 100644 --- a/main/asset_loaders/spiffs_song.c +++ b/main/asset_loaders/spiffs_song.c @@ -27,7 +27,7 @@ * @return true if the SNG was loaded successfully, * false if the SNG load failed and should not be used */ -bool loadSong(char* name, song_t* song, bool spiRam) +bool loadSong(const char* name, song_t* song, bool spiRam) { uint32_t caps = spiRam ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DEFAULT; diff --git a/main/asset_loaders/spiffs_song.h b/main/asset_loaders/spiffs_song.h index acf280dec..9732c25cf 100644 --- a/main/asset_loaders/spiffs_song.h +++ b/main/asset_loaders/spiffs_song.h @@ -39,7 +39,7 @@ #include "hdw-bzr.h" -bool loadSong(char* name, song_t* song, bool spiRam); +bool loadSong(const char* name, song_t* song, bool spiRam); void freeSong(song_t* song); #endif \ No newline at end of file diff --git a/main/display/wsg.c b/main/display/wsg.c index 0ad191dae..d9c5e9297 100644 --- a/main/display/wsg.c +++ b/main/display/wsg.c @@ -323,6 +323,54 @@ void drawWsgSimple(const wsg_t* wsg, int16_t xOff, int16_t yOff) } } +/** + * @brief Draw a WSG to the display without flipping or rotation at half size + * + * @param wsg The WSG to draw to the display + * @param xOff The x offset to draw the WSG at + * @param yOff The y offset to draw the WSG at + */ +void drawWsgSimpleHalf(const wsg_t* wsg, int16_t xOff, int16_t yOff) +{ + // This function has been micro optimized by cnlohr on 2022-09-07, using gcc version 8.4.0 (crosstool-NG + // esp-2021r2-patch3) + + if (NULL == wsg->px) + { + return; + } + + // Only draw in bounds + int dWidth = TFT_WIDTH; + int wWidth = wsg->w; + int xMin = CLAMP(xOff, 0, dWidth); + int xMax = CLAMP(xOff + (wWidth / 2), 0, dWidth); + int yMin = CLAMP(yOff, 0, TFT_HEIGHT); + int yMax = CLAMP(yOff + (wsg->h / 2), 0, TFT_HEIGHT); + paletteColor_t* px = getPxTftFramebuffer(); + int numX = xMax - xMin; + int wsgY = (yMin - yOff); + int wsgX = (xMin - xOff); + paletteColor_t* lineout = &px[(yMin * dWidth) + xMin]; + const paletteColor_t* linein = &wsg->px[wsgY * wWidth + wsgX]; + + // Draw each pixel + for (int y = yMin; y < yMax; y++) + { + for (int x = 0; x < numX; x++) + { + int color = linein[x * 2]; + if (color != cTransparent) + { + lineout[x] = color; + } + } + lineout += dWidth; + linein += (2 * wWidth); + wsgY++; + } +} + /** * @brief Quickly copy a WSG to the display without flipping or rotation or transparency * diff --git a/main/display/wsg.h b/main/display/wsg.h index b332e4ba2..c3f2bbdf9 100644 --- a/main/display/wsg.h +++ b/main/display/wsg.h @@ -59,5 +59,6 @@ typedef struct void drawWsg(const wsg_t* wsg, int16_t xOff, int16_t yOff, bool flipLR, bool flipUD, int16_t rotateDeg); void drawWsgSimple(const wsg_t* wsg, int16_t xOff, int16_t yOff); void drawWsgTile(const wsg_t* wsg, int32_t xOff, int32_t yOff); +void drawWsgSimpleHalf(const wsg_t* wsg, int16_t xOff, int16_t yOff); #endif \ No newline at end of file diff --git a/main/menu/menu.c b/main/menu/menu.c index 56c19a20d..feb7d46fc 100644 --- a/main/menu/menu.c +++ b/main/menu/menu.c @@ -53,6 +53,7 @@ menu_t* initMenu(const char* title, menuCb cbFunc) menu->currentItem = NULL; menu->items = calloc(1, sizeof(list_t)); menu->parentMenu = NULL; + menu->showBattery = false; return menu; } @@ -794,3 +795,33 @@ menu_t* menuButton(menu_t* menu, buttonEvt_t evt) } return menu; } + +/** + * @brief Show or hide the battery on the menu. This should be called from the + * root menu after the menu is constructed + * + * @param menu The menu to show a battery indicator on + * @param showBattery true to show the battery, false to hide it + */ +void setShowBattery(menu_t* menu, bool showBattery) +{ + // Set this menu + menu->showBattery = showBattery; + + // For each item + node_t* itemNode = menu->items->first; + while (itemNode) + { + menuItem_t* item = (menuItem_t*)itemNode->val; + + // If this item has a submenu + if (NULL != item->subMenu) + { + // Set the battery there too (recursive) + setShowBattery(item->subMenu, showBattery); + } + + // Move to the next item + itemNode = itemNode->next; + } +} diff --git a/main/menu/menu.h b/main/menu/menu.h index 8ac1fae3e..2d0b9836d 100644 --- a/main/menu/menu.h +++ b/main/menu/menu.h @@ -179,6 +179,7 @@ typedef struct _menu_t list_t* items; ///< A list_t of menu items to display node_t* currentItem; ///< The currently selected menu item menu_t* parentMenu; ///< The parent menu, may be NULL if this is not a submenu + bool showBattery; ///< true if the battery measurement should be shown. false by default int32_t batteryReadTimer; ///< A timer to read the battery every 10s int batteryLevel; ///< The current battery measurement } menu_t; @@ -209,5 +210,6 @@ menu_t* menuNavigateToNextOption(menu_t* menu); menu_t* menuSelectCurrentItem(menu_t* menu); menu_t* menuButton(menu_t* menu, buttonEvt_t evt) __attribute__((warn_unused_result)); +void setShowBattery(menu_t* menu, bool showBattery); #endif \ No newline at end of file diff --git a/main/menu/menuLogbookRenderer.c b/main/menu/menuLogbookRenderer.c index 4fb24f31b..ad950148a 100644 --- a/main/menu/menuLogbookRenderer.c +++ b/main/menu/menuLogbookRenderer.c @@ -63,6 +63,9 @@ menuLogbookRenderer_t* initMenuLogbookRenderer(font_t* menuFont) loadWsg("batt3.wsg", &renderer->batt[2], false); loadWsg("batt4.wsg", &renderer->batt[3], false); + // Load a background + loadWsg("menu_bg.wsg", &renderer->menu_bg, true); + // Initialize LEDs for (uint16_t idx = 0; idx < CONFIG_NUM_LEDS; idx++) { @@ -88,6 +91,7 @@ void deinitMenuLogbookRenderer(menuLogbookRenderer_t* renderer) freeWsg(&renderer->batt[1]); freeWsg(&renderer->batt[2]); freeWsg(&renderer->batt[3]); + freeWsg(&renderer->menu_bg); free(renderer); } @@ -108,12 +112,12 @@ static void drawMenuText(menuLogbookRenderer_t* renderer, const char* text, int1 { // Pick colors based on selection paletteColor_t cornerColor = c411; - paletteColor_t textColor = c511; + paletteColor_t textColor = c553; paletteColor_t topLineColor = c211; if (isSelected) { cornerColor = c532; - textColor = c554; + textColor = c555; topLineColor = c422; } @@ -217,12 +221,16 @@ static void drawMenuText(menuLogbookRenderer_t* renderer, const char* text, int1 */ void drawMenuLogbook(menu_t* menu, menuLogbookRenderer_t* renderer, int64_t elapsedUs) { - // Read battery every 10s - menu->batteryReadTimer -= elapsedUs; - if (0 >= menu->batteryReadTimer) + // Only poll the battery if requested + if (menu->showBattery) { - menu->batteryReadTimer += 10000000; - menu->batteryLevel = readBattmon(); + // Read battery every 10s + menu->batteryReadTimer -= elapsedUs; + if (0 >= menu->batteryReadTimer) + { + menu->batteryReadTimer += 10000000; + menu->batteryLevel = readBattmon(); + } } // For each LED @@ -267,8 +275,8 @@ void drawMenuLogbook(menu_t* menu, menuLogbookRenderer_t* renderer, int64_t elap // Light the LEDs setLeds(renderer->leds, CONFIG_NUM_LEDS); - // Clear the TFT - fillDisplayArea(0, 0, TFT_WIDTH, TFT_HEIGHT, c100); + // Clear the TFT with a background + drawWsgTile(&renderer->menu_bg, 0, 0); // Find the start of the 'page' node_t* pageStart = menu->items->first; @@ -347,25 +355,29 @@ void drawMenuLogbook(menu_t* menu, menuLogbookRenderer_t* renderer, int64_t elap drawWsg(&renderer->arrow, arrowX, arrowY, false, false, 90); } - // Draw the battery indicator depending on the last read value - wsg_t* toDraw = NULL; - // 872 is full - if (menu->batteryLevel == 0 || menu->batteryLevel > 741) - { - toDraw = &renderer->batt[3]; - } - else if (menu->batteryLevel > 695) + // Only draw the battery if requested + if (menu->showBattery) { - toDraw = &renderer->batt[2]; - } - else if (menu->batteryLevel > 652) - { - toDraw = &renderer->batt[1]; - } - else // 452 is dead - { - toDraw = &renderer->batt[0]; - } + // Draw the battery indicator depending on the last read value + wsg_t* toDraw = NULL; + // 872 is full + if (menu->batteryLevel == 0 || menu->batteryLevel > 741) + { + toDraw = &renderer->batt[3]; + } + else if (menu->batteryLevel > 695) + { + toDraw = &renderer->batt[2]; + } + else if (menu->batteryLevel > 652) + { + toDraw = &renderer->batt[1]; + } + else // 452 is dead + { + toDraw = &renderer->batt[0]; + } - drawWsg(toDraw, 212, 3, false, false, 0); + drawWsg(toDraw, 212, 3, false, false, 0); + } } diff --git a/main/menu/menuLogbookRenderer.h b/main/menu/menuLogbookRenderer.h index 382dfcb7e..8bd456e98 100644 --- a/main/menu/menuLogbookRenderer.h +++ b/main/menu/menuLogbookRenderer.h @@ -54,7 +54,8 @@ typedef struct font_t* font; ///< The font to render the menu with led_t leds[CONFIG_NUM_LEDS]; ///< An array with the RGB LED state to be output menuLed_t ledTimers[CONFIG_NUM_LEDS]; ///< An array with the LED timers for animation - wsg_t batt[4]; + wsg_t batt[4]; ///< Images for the battery levels + wsg_t menu_bg; ///< Background image for the menu } menuLogbookRenderer_t; menuLogbookRenderer_t* initMenuLogbookRenderer(font_t* menuFont); diff --git a/main/modes/mainMenu/mainMenu.c b/main/modes/mainMenu/mainMenu.c index bed8b1844..3a6c786b5 100644 --- a/main/modes/mainMenu/mainMenu.c +++ b/main/modes/mainMenu/mainMenu.c @@ -171,6 +171,9 @@ static void mainMenuEnterMode(void) // End the submenu for settings mainMenu->menu = endSubMenu(mainMenu->menu); + // Show the battery on the main menu + setShowBattery(mainMenu->menu, true); + // Initialize menu renderer mainMenu->renderer = initMenuLogbookRenderer(&mainMenu->logbook); } diff --git a/main/modes/ray/enemies/ray_enemy.c b/main/modes/ray/enemies/ray_enemy.c index d586f82b4..6233bc94f 100644 --- a/main/modes/ray/enemies/ray_enemy.c +++ b/main/modes/ray/enemies/ray_enemy.c @@ -11,6 +11,23 @@ #include "ray_enemy_hidden.h" #include "ray_enemy_boss.h" +//============================================================================== +// Typedefs +//============================================================================== + +typedef void (*rayEnemyMove_t)(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +typedef bool (*rayEnemyGetShot_t)(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +typedef int32_t (*rayEnemyGetTimer_t)(rayEnemy_t* enemy, rayEnemyTimerType_t type); +typedef rayMapCellType_t (*rayEnemyGetBullet_t)(rayEnemy_t* enemy); + +typedef struct ray_enemy +{ + rayEnemyMove_t move; + rayEnemyGetShot_t getShot; + rayEnemyGetTimer_t getTimer; + rayEnemyGetBullet_t getBullet; +} enemyFuncs_t; + //============================================================================== // Const data //============================================================================== @@ -22,36 +39,54 @@ static const enemyFuncs_t enemyFuncs[] = { { // OBJ_ENEMY_NORMAL - .moveAnimate = rayEnemyNormalMoveAnimate, - .getShot = rayEnemyNormalGetShot, + .move = rayEnemyNormalMove, + .getShot = rayEnemyNormalGetShot, + .getTimer = rayEnemyNormalGetTimer, + .getBullet = rayEnemyNormalGetBullet, }, { // OBJ_ENEMY_STRONG - .moveAnimate = rayEnemyStrongMoveAnimate, - .getShot = rayEnemyStrongGetShot, + .move = rayEnemyStrongMove, + .getShot = rayEnemyStrongGetShot, + .getTimer = rayEnemyStrongGetTimer, + .getBullet = rayEnemyStrongGetBullet, }, { // OBJ_ENEMY_ARMORED - .moveAnimate = rayEnemyArmoredMoveAnimate, - .getShot = rayEnemyArmoredGetShot, + .move = rayEnemyArmoredMove, + .getShot = rayEnemyArmoredGetShot, + .getTimer = rayEnemyArmoredGetTimer, + .getBullet = rayEnemyArmoredGetBullet, }, { // OBJ_ENEMY_FLAMING - .moveAnimate = rayEnemyFlamingMoveAnimate, - .getShot = rayEnemyFlamingGetShot, + .move = rayEnemyFlamingMove, + .getShot = rayEnemyFlamingGetShot, + .getTimer = rayEnemyFlamingGetTimer, + .getBullet = rayEnemyFlamingGetBullet, }, { // OBJ_ENEMY_HIDDEN - .moveAnimate = rayEnemyHiddenMoveAnimate, - .getShot = rayEnemyHiddenGetShot, + .move = rayEnemyHiddenMove, + .getShot = rayEnemyHiddenGetShot, + .getTimer = rayEnemyHiddenGetTimer, + .getBullet = rayEnemyHiddenGetBullet, }, { // OBJ_ENEMY_BOSS - .moveAnimate = rayEnemyBossMoveAnimate, - .getShot = rayEnemyBossGetShot, + .move = rayEnemyBossMove, + .getShot = rayEnemyBossGetShot, + .getTimer = rayEnemyBossGetTimer, + .getBullet = rayEnemyBossGetBullet, }, }; +//============================================================================== +// Function Prototypes +//============================================================================== + +static bool animateEnemy(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); + //============================================================================== // Functions //============================================================================== @@ -71,14 +106,172 @@ void rayEnemiesMoveAnimate(ray_t* ray, uint32_t elapsedUs) // Get a pointer from the linked list rayEnemy_t* enemy = ((rayEnemy_t*)currentNode->val); - // Move enemies and run timers like animation - enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].moveAnimate(ray, enemy, elapsedUs); + // If this enemy is warping in + if (0 < enemy->warpTimer) + { + // Run down the warp timer + enemy->warpTimer -= elapsedUs; + // Iterate to the next node and continue + currentNode = currentNode->next; + continue; + } - // Iterate to the next node - currentNode = currentNode->next; + // Copy the elapsed time in case the enemy is frozen + uint32_t eElapsedUs = elapsedUs; + + // If the enemy is frozen + if (0 < enemy->freezeTimer) + { + // Decrement the timer + enemy->freezeTimer -= eElapsedUs; + // Slow time for other timers + eElapsedUs /= 3; + } + + // Move enemies in the walking state, or if it's a non-dead boss + if ((E_WALKING_1 == enemy->state) || (E_WALKING_2 == enemy->state) + || ((OBJ_ENEMY_BOSS == enemy->c.type) && (E_DEAD != enemy->state))) + { + enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].move(ray, enemy, eElapsedUs); + } + + // Run other timers if not dead + if (E_DEAD != enemy->state) + { + // Run the shot timer down to zero + enemy->shootTimer -= eElapsedUs; + if (enemy->shootTimer <= 0) + { + // Try to transition to shooting + if (rayEnemyTransitionState(enemy, E_SHOOTING)) + { + // If successful, restart the shot timer + enemy->shootTimer = enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].getTimer(enemy, SHOT); + } + } + + // Run the block timer down to zero + enemy->blockTimer -= eElapsedUs; + if (enemy->blockTimer <= 0) + { + // Try to transition to blocking + if (rayEnemyTransitionState(enemy, E_BLOCKING)) + { + // If successful, restart the block timer + enemy->blockTimer = enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].getTimer(enemy, BLOCK); + } + } + + if (OBJ_ENEMY_BOSS == enemy->c.type) + { + // Special boss timer, pick a new weakness every 4s + enemy->bossTimer -= elapsedUs; + if (enemy->bossTimer <= 0) + { + enemy->bossTimer += 4000000; + + // Pick a new boss state + enemy->bossState + = (enemy->bossState + (1 + esp_random() % (NUM_BOSS_STATES - 1))) % NUM_BOSS_STATES; + + // Update all sprites the boss uses + if (B_NORMAL == enemy->bossState) + { + enemy->sprites = &ray->enemyTex[OBJ_ENEMY_BOSS - OBJ_ENEMY_NORMAL]; + } + else + { + enemy->sprites = &ray->bossTex[enemy->bossState]; + } + // Update the current sprite + enemy->c.sprite = &enemy->sprites[0][enemy->state][enemy->animFrame]; + } + } + + // Run the invincible timer down to zero + if (0 < enemy->invincibleTimer) + { + enemy->invincibleTimer -= eElapsedUs; + } + } + + // Animate the enemy + if (animateEnemy(ray, enemy, eElapsedUs)) + { + // Enemy was killed + checkScriptKill(ray, enemy->c.id, &enemy->sprites[0][E_WALKING_1][0]); + + // save the next node + node_t* nextNode = currentNode->next; + + // Remove the lock + if (ray->targetedObj == &(enemy->c)) + { + ray->targetedObj = NULL; + } + + // Randomly drop a pickup where the enemy was + switch (esp_random() % 4) + { + case 0: + { + // 25% + rayCreateCommonObj(ray, OBJ_ITEM_PICKUP_ENERGY, 0x100, enemy->c.posX, enemy->c.posY); + break; + } + case 1: + { + // 25%, if missiles are unlocked + if (ray->p.i.missileLoadOut) + { + rayCreateCommonObj(ray, OBJ_ITEM_PICKUP_MISSILE, 0x100, enemy->c.posX, enemy->c.posY); + } + break; + } + default: + { + // No drop + break; + } + } + + // Unlink and free + removeEntry(&ray->enemies, currentNode); + free(enemy); + + // Set the next node + currentNode = nextNode; + } + else + { + // Iterate to the next node + currentNode = currentNode->next; + } } } +/** + * @brief Get the next shot timer for an enemy + * + * @param enemy The shooting enemy + * @return int32_t The time in uS until the next shot + */ +int32_t getTimerForEnemy(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].getTimer(enemy, type); +} + +/** + * @brief Get the bullet type for an enemy + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t getBulletForEnemy(rayEnemy_t* enemy) +{ + return enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].getBullet(enemy); +} + /** * @brief This is called when an enemy is shot. It adds damage based on bullet type, checks scripts, and handles freeing * defeated enemies @@ -86,9 +279,178 @@ void rayEnemiesMoveAnimate(ray_t* ray, uint32_t elapsedUs) * @param ray The entire game state * @param enemy The enemy which was shot * @param bullet The type of bullet it was shot by - * @return true if the enemy was killed, false if it's still alive */ -bool rayEnemyGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) +void rayEnemyGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) +{ + // Don't get shot when blocking + if ((E_BLOCKING == enemy->state) || (E_HURT == enemy->state)) + { + return; + } + + // Don't get shot when invincible + // Ignore this for now, being invincible when in E_HURT is enough + // if (0 < enemy->invincibleTimer) + // { + // return; + // } + + // Save old health to see if the enemy took damage + int32_t oldHealth = enemy->health; + + // Apply damage + if (enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].getShot(ray, enemy, bullet)) + { + // Transition to dying + rayEnemyTransitionState(enemy, E_DEAD); + } + + // If the enemy took + if (oldHealth != enemy->health) + { + // Don't get stun-locked. Hurt animation is 200000*4, so double that + // enemy->invincibleTimer = 200000 * 8; + + // If it was ice damage + if (OBJ_BULLET_ICE == bullet) + { + // Slow it for a moment + enemy->freezeTimer = 2000000; + } + } +} + +/** + * @brief Transition from the current animation state to the next + * + * @param enemy The enemy to transition + * @param newState The new state to transition to + * @return true if the transition was allowed, false if it was not + */ +bool rayEnemyTransitionState(rayEnemy_t* enemy, rayEnemyState_t newState) +{ + const bool transitionTable[E_NUM_STATES][E_NUM_STATES] = { + {false, true, true, true, true, false}, // E_WALKING_1, don't transition to E_DEAD + {true, false, true, true, true, false}, // E_WALKING_2, don't transition to E_DEAD + {true, true, false, false, false, false}, // E_SHOOTING, transition to E_WALKING + {true, true, false, false, false, true}, // E_HURT, transition to E_WALKING or E_DEAD + {true, true, false, false, false, false}, // E_BLOCKING, transition to E_WALKING + {false, false, false, false, false, false} // E_DEAD, don't transition to anything + }; + + if (transitionTable[enemy->state][newState]) + { + // Switch to the new state + enemy->state = newState; + enemy->animTimer = 0; + enemy->animFrame = 0; + enemy->animTimerLimit = 200000; + // Pick the sprites + enemy->c.sprite = &enemy->sprites[0][newState][0]; + if ((E_WALKING_1 == newState) || (E_WALKING_2 == newState)) + { + enemy->animTimerLimit = 125000; + } + return true; + } + return false; +} + +/** + * @brief Animate a single enemy + * + * @param ray The entire game state + * @param enemy The enemy to animate + * @param elapsedUs The elapsed time since this function was last called + * @return True if the enemy died, false if not + */ +static bool animateEnemy(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { - return enemyFuncs[enemy->c.type - OBJ_ENEMY_NORMAL].getShot(ray, enemy, bullet); + // Accumulate time + enemy->animTimer += elapsedUs; + // Check if it's time to transition states + if (enemy->animTimer >= enemy->animTimerLimit) + { + // Decrement timer + enemy->animTimer -= enemy->animTimerLimit; + + // Increment to the next frame + enemy->animFrame++; + + // If the animation is over + if (enemy->animFrame >= NUM_ANIM_FRAMES) + { + if (E_DEAD == enemy->state) + { + // Dead, caller unlinks and frees the enemy + return true; + } + else + { + if (E_WALKING_1 == enemy->state) + { + // Return to walking + rayEnemyTransitionState(enemy, E_WALKING_2); + } + else + { + // Return to walking + rayEnemyTransitionState(enemy, E_WALKING_1); + } + } + } + else + { + // Pick the next sprite + enemy->c.sprite = &enemy->sprites[0][enemy->state][enemy->animFrame]; + + // If this is the 3rd frame of shooting + if ((E_SHOOTING == enemy->state) && (2 == enemy->animFrame)) + { + // Spawn a bullet + q24_8 xDiff = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDiff = SUB_FX(ray->p.posY, enemy->c.posY); + fastNormVec(&xDiff, &yDiff); + rayCreateBullet(ray, getBulletForEnemy(enemy), enemy->c.posX, enemy->c.posY, xDiff, yDiff, false); + } + } + } + return false; } + +/** + * @brief Switch sprites for all enemies to the X-Ray alternate + * + * @param ray The entire game state + * @param isXray true to switch to X-Ray sprites, false to switch to normal sprites + */ +void switchEnemiesToXray(ray_t* ray, bool isXray) +{ + // Assign each enemy a distance from the player + node_t* currentNode = ray->enemies.first; + while (currentNode != NULL) + { + // Get a pointer from the linked list + rayEnemy_t* enemy = ((rayEnemy_t*)currentNode->val); + + // If this is a hidden enemy + if (OBJ_ENEMY_HIDDEN == enemy->c.type) + { + // Switch all the sprites + if (isXray) + { + enemy->sprites = &ray->hiddenXRTex; + } + else + { + enemy->sprites = &ray->enemyTex[OBJ_ENEMY_HIDDEN - OBJ_ENEMY_NORMAL]; + } + + // Switch the current sprite + enemy->c.sprite = &enemy->sprites[0][enemy->state][enemy->animFrame]; + } + + // Iterate to the next node + currentNode = currentNode->next; + } +} \ No newline at end of file diff --git a/main/modes/ray/enemies/ray_enemy.h b/main/modes/ray/enemies/ray_enemy.h index c3b12377b..699535277 100644 --- a/main/modes/ray/enemies/ray_enemy.h +++ b/main/modes/ray/enemies/ray_enemy.h @@ -1,15 +1,19 @@ #pragma once #include "mode_ray.h" +#include "ray_map.h" +#include "ray_object.h" -typedef void (*rayEnemyMoveAnimate_t)(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); -typedef bool (*rayEnemyGetShot_t)(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); - -typedef struct ray_enemy +/// @brief Types of timers for enemy actions +typedef enum { - rayEnemyMoveAnimate_t moveAnimate; - rayEnemyGetShot_t getShot; -} enemyFuncs_t; + SHOT, ///< A timer for shooting + BLOCK, ///< A timer for blocking +} rayEnemyTimerType_t; void rayEnemiesMoveAnimate(ray_t* ray, uint32_t elapsedUs); -bool rayEnemyGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +void rayEnemyGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +bool rayEnemyTransitionState(rayEnemy_t* enemy, rayEnemyState_t newState); +void switchEnemiesToXray(ray_t* ray, bool isXray); +int32_t getTimerForEnemy(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t getBulletForEnemy(rayEnemy_t* enemy); diff --git a/main/modes/ray/enemies/ray_enemy_armored.c b/main/modes/ray/enemies/ray_enemy_armored.c index e85944231..4cce9c10a 100644 --- a/main/modes/ray/enemies/ray_enemy_armored.c +++ b/main/modes/ray/enemies/ray_enemy_armored.c @@ -1,14 +1,109 @@ +#include "ray_enemy.h" #include "ray_enemy_armored.h" /** - * @brief Run timers for an armored enemy, which include AI, movement, and animation + * @brief Run timers for an armored enemy, which include AI, and movement * * @param ray The entire game state * @param enemy The enemy to run timers for * @param elapsedUs The elapsed time since this function was last called */ -void rayEnemyArmoredMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) +void rayEnemyArmoredMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { + // Pick an initial direction to move in + q24_8 xDiff = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDiff = SUB_FX(ray->p.posY, enemy->c.posY); + + // If the enemy is doing nothing + if (DOING_NOTHING == enemy->behavior) + { + // Move orthogonal to the player + if (ABS(xDiff) > ABS(yDiff)) + { + enemy->behavior = MOVE_POS_Y; + } + else + { + enemy->behavior = MOVE_POS_X; + } + } + +// Player is 40000 * 6 +#define SPEED_DENOM (int32_t)(40000 * 18) + + q24_8 delX = 0; + q24_8 delY = 0; + switch (enemy->behavior) + { + case MOVE_POS_X: + { + delX = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_NEG_X: + { + delX = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_POS_Y: + { + delY = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_NEG_Y: + { + delY = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + default: + { + // Do nothing + break; + } + } + + q24_8 marginX = (delX > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + q24_8 marginY = (delY > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + + // Move if in bounds + if (isPassableCell( + &ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY + delY + marginY)])) + { + enemy->c.posX += delX; + enemy->c.posY += delY; + } + else + { + // Bounce off walls + switch (enemy->behavior) + { + case MOVE_POS_X: + { + enemy->behavior = MOVE_NEG_X; + break; + } + case MOVE_NEG_X: + { + enemy->behavior = MOVE_POS_X; + break; + } + case MOVE_POS_Y: + { + enemy->behavior = MOVE_NEG_Y; + break; + } + case MOVE_NEG_Y: + { + enemy->behavior = MOVE_POS_Y; + break; + } + default: + { + // Do nothing + break; + } + } + } } /** @@ -22,5 +117,60 @@ void rayEnemyArmoredMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedU */ bool rayEnemyArmoredGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) { - return false; + // Health starts at 100 + bool hurt = false; + switch (bullet) + { + case OBJ_BULLET_MISSILE: + { + // Two shots to kill + enemy->health -= 50; + hurt = true; + break; + } + case OBJ_BULLET_NORMAL: + case OBJ_BULLET_CHARGE: + case OBJ_BULLET_ICE: + case OBJ_BULLET_XRAY: + { + // Five shots to kill + enemy->health -= 20; + hurt = true; + break; + } + default: + { + // Not a bullet + break; + } + } + + if (hurt) + { + rayEnemyTransitionState(enemy, E_HURT); + } + return enemy->health <= 0; +} + +/** + * @brief Get the time until the next shot is taken + * + * @param enemy The enemy taking the shot + * @param type the timer of timer to get + * @return The time, in uS, until the next shot + */ +int32_t rayEnemyArmoredGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return 2000000 + (esp_random() % 2000000); +} + +/** + * @brief Get the bullet this enemy fires + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t rayEnemyArmoredGetBullet(rayEnemy_t* enemy) +{ + return OBJ_BULLET_MISSILE; } diff --git a/main/modes/ray/enemies/ray_enemy_armored.h b/main/modes/ray/enemies/ray_enemy_armored.h index 9b5840a0c..36f5d4deb 100644 --- a/main/modes/ray/enemies/ray_enemy_armored.h +++ b/main/modes/ray/enemies/ray_enemy_armored.h @@ -2,5 +2,7 @@ #include "mode_ray.h" -void rayEnemyArmoredMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +void rayEnemyArmoredMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); bool rayEnemyArmoredGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +int32_t rayEnemyArmoredGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t rayEnemyArmoredGetBullet(rayEnemy_t* enemy); diff --git a/main/modes/ray/enemies/ray_enemy_boss.c b/main/modes/ray/enemies/ray_enemy_boss.c index dee8c3790..26298acdb 100644 --- a/main/modes/ray/enemies/ray_enemy_boss.c +++ b/main/modes/ray/enemies/ray_enemy_boss.c @@ -1,14 +1,109 @@ +#include "ray_enemy.h" #include "ray_enemy_boss.h" /** - * @brief Run timers for a boss enemy, which include AI, movement, and animation + * @brief Run timers for a boss enemy, which include AI, and movement * * @param ray The entire game state * @param enemy The enemy to run timers for * @param elapsedUs The elapsed time since this function was last called */ -void rayEnemyBossMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) +void rayEnemyBossMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { + // Pick a new direction every 1s + enemy->behaviorTimer -= elapsedUs; + if (enemy->behaviorTimer <= 0) + { + enemy->behaviorTimer += 1000000; + + // Randomize movement + switch (esp_random() % 8) + { + case 0: + { + enemy->behavior = MOVE_AWAY_PLAYER; + break; + } + case 1 ... 2: + { + enemy->behavior = MOVE_STRAFE_R; + break; + } + case 3 ... 4: + { + enemy->behavior = MOVE_STRAFE_L; + break; + } + case 5 ... 7: + { + enemy->behavior = MOVE_TOWARDS_PLAYER; + break; + } + } + } + + // Reverse behavior if too close to the player + q24_8 xDist = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDist = SUB_FX(ray->p.posY, enemy->c.posY); + q24_8 distToPlayer = ADD_FX(MUL_FX(xDist, xDist), MUL_FX(yDist, yDist)); + if (distToPlayer < TO_FX(4) && (MOVE_TOWARDS_PLAYER == enemy->behavior)) + { + enemy->behavior = MOVE_AWAY_PLAYER; + } + + // Find the vector from the enemy to the player and normalize it + q24_8 xDiff = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDiff = SUB_FX(ray->p.posY, enemy->c.posY); + fastNormVec(&xDiff, &yDiff); + +// Player is 40000 * 6 +#define SPEED_DENOM (int32_t)(40000 * 9) + + q24_8 delX = 0; + q24_8 delY = 0; + switch (enemy->behavior) + { + case MOVE_AWAY_PLAYER: + { + delX = -(xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = -(yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_TOWARDS_PLAYER: + { + delX = (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_STRAFE_L: + { + delX = (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = -(xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_STRAFE_R: + { + delX = -(yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + default: + { + // Do nothing + break; + } + } + + q24_8 marginX = (delX > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + q24_8 marginY = (delY > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + + // Move if in bounds + if (isPassableCell( + &ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY + delY + marginY)])) + { + enemy->c.posX += delX; + enemy->c.posY += delY; + } } /** @@ -22,5 +117,105 @@ void rayEnemyBossMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) */ bool rayEnemyBossGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) { - return false; + bool hurt = false; + switch (enemy->bossState) + { + case B_NORMAL: + { + if (OBJ_BULLET_NORMAL == bullet) + { + enemy->health -= 5; + hurt = true; + } + else if (OBJ_BULLET_CHARGE == BULLET) + { + enemy->health -= 10; + hurt = true; + } + break; + } + case B_MISSILE: + { + if (OBJ_BULLET_MISSILE == bullet) + { + enemy->health -= 5; + hurt = true; + } + break; + } + case B_ICE: + { + if (OBJ_BULLET_ICE == bullet) + { + enemy->health -= 5; + hurt = true; + } + break; + } + case B_XRAY: + { + if (OBJ_BULLET_XRAY == bullet) + { + enemy->health -= 5; + hurt = true; + } + break; + } + default: + { + break; + } + } + + if (hurt) + { + rayEnemyTransitionState(enemy, E_HURT); + } + + return (enemy->health <= 0); +} + +/** + * @brief Get the time until the next shot is taken + * + * @param enemy The enemy taking the shot + * @param type the timer of timer to get + * @return The time, in uS, until the next shot + */ +int32_t rayEnemyBossGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return 4 * 200000; +} + +/** + * @brief Get the bullet this enemy fires + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t rayEnemyBossGetBullet(rayEnemy_t* enemy) +{ + switch (enemy->bossState) + { + case B_NORMAL: + { + return OBJ_BULLET_NORMAL; + } + case B_MISSILE: + { + return OBJ_BULLET_MISSILE; + } + case B_ICE: + { + return OBJ_BULLET_ICE; + } + case B_XRAY: + { + return OBJ_BULLET_XRAY; + } + default: + { + return OBJ_BULLET_NORMAL; + } + } } diff --git a/main/modes/ray/enemies/ray_enemy_boss.h b/main/modes/ray/enemies/ray_enemy_boss.h index f95012c83..507d1984c 100644 --- a/main/modes/ray/enemies/ray_enemy_boss.h +++ b/main/modes/ray/enemies/ray_enemy_boss.h @@ -2,5 +2,7 @@ #include "mode_ray.h" -void rayEnemyBossMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +void rayEnemyBossMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); bool rayEnemyBossGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +int32_t rayEnemyBossGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t rayEnemyBossGetBullet(rayEnemy_t* enemy); diff --git a/main/modes/ray/enemies/ray_enemy_flaming.c b/main/modes/ray/enemies/ray_enemy_flaming.c index cd05f4719..dc27d92a6 100644 --- a/main/modes/ray/enemies/ray_enemy_flaming.c +++ b/main/modes/ray/enemies/ray_enemy_flaming.c @@ -1,14 +1,132 @@ +#include "ray_enemy.h" #include "ray_enemy_flaming.h" /** - * @brief Run timers for a flaming enemy, which include AI, movement, and animation + * @brief Run timers for a flaming enemy, which include AI, and movement * * @param ray The entire game state * @param enemy The enemy to run timers for * @param elapsedUs The elapsed time since this function was last called */ -void rayEnemyFlamingMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) +void rayEnemyFlamingMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { + // If the enemy is doing nothing + if (DOING_NOTHING == enemy->behavior) + { + // Pick a random starting direction + enemy->behavior = MOVE_NE + (esp_random() % 4); + } + +// Player is 40000 * 6 +#define SPEED_DENOM (int32_t)(40000 * 18) + + q24_8 delX = 0; + q24_8 delY = 0; + switch (enemy->behavior) + { + case MOVE_NE: + { + delX = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_SE: + { + delX = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_SW: + { + delX = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_NW: + { + delX = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + default: + { + break; + } + } + + q24_8 marginX = (delX > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + q24_8 marginY = (delY > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + + // If the cell can be moved into + if (isPassableCell( + &ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY + delY + marginY)])) + { + // Move into it + enemy->c.posX += delX; + enemy->c.posY += delY; + } + else if (!isPassableCell(&ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY)])) + { + // Collision on X axis, invert that + switch (enemy->behavior) + { + case MOVE_NE: + { + enemy->behavior = MOVE_NW; + break; + } + case MOVE_SE: + { + enemy->behavior = MOVE_SW; + break; + } + case MOVE_SW: + { + enemy->behavior = MOVE_SE; + break; + } + case MOVE_NW: + { + enemy->behavior = MOVE_NE; + break; + } + default: + { + break; + } + } + } + else + { + // Collision on Y axis, invert that + switch (enemy->behavior) + { + case MOVE_NE: + { + enemy->behavior = MOVE_SE; + break; + } + case MOVE_SE: + { + enemy->behavior = MOVE_NE; + break; + } + case MOVE_SW: + { + enemy->behavior = MOVE_NW; + break; + } + case MOVE_NW: + { + enemy->behavior = MOVE_SW; + break; + } + default: + { + break; + } + } + } } /** @@ -22,5 +140,60 @@ void rayEnemyFlamingMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedU */ bool rayEnemyFlamingGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) { - return false; + // Health starts at 100 + bool hurt = false; + switch (bullet) + { + case OBJ_BULLET_ICE: + { + // Two shots to kill + enemy->health -= 50; + hurt = true; + break; + } + case OBJ_BULLET_NORMAL: + case OBJ_BULLET_CHARGE: + case OBJ_BULLET_XRAY: + case OBJ_BULLET_MISSILE: + { + // Five shots to kill + enemy->health -= 20; + hurt = true; + break; + } + default: + { + // Not a bullet + break; + } + } + + if (hurt) + { + rayEnemyTransitionState(enemy, E_HURT); + } + return enemy->health <= 0; +} + +/** + * @brief Get the time until the next shot is taken + * + * @param enemy The enemy taking the shot + * @param type the timer of timer to get + * @return The time, in uS, until the next shot + */ +int32_t rayEnemyFlamingGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return 2000000 + (esp_random() % 2000000); +} + +/** + * @brief Get the bullet this enemy fires + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t rayEnemyFlamingGetBullet(rayEnemy_t* enemy) +{ + return OBJ_BULLET_ICE; } diff --git a/main/modes/ray/enemies/ray_enemy_flaming.h b/main/modes/ray/enemies/ray_enemy_flaming.h index 798b81f41..90231657b 100644 --- a/main/modes/ray/enemies/ray_enemy_flaming.h +++ b/main/modes/ray/enemies/ray_enemy_flaming.h @@ -2,5 +2,7 @@ #include "mode_ray.h" -void rayEnemyFlamingMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +void rayEnemyFlamingMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); bool rayEnemyFlamingGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +int32_t rayEnemyFlamingGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t rayEnemyFlamingGetBullet(rayEnemy_t* enemy); diff --git a/main/modes/ray/enemies/ray_enemy_hidden.c b/main/modes/ray/enemies/ray_enemy_hidden.c index c52a3bfc5..f28269306 100644 --- a/main/modes/ray/enemies/ray_enemy_hidden.c +++ b/main/modes/ray/enemies/ray_enemy_hidden.c @@ -1,14 +1,93 @@ +#include "ray_enemy.h" #include "ray_enemy_hidden.h" /** - * @brief Run timers for a hidden enemy, which include AI, movement, and animation + * @brief Run timers for a hidden enemy, which include AI, and movement * * @param ray The entire game state * @param enemy The enemy to run timers for * @param elapsedUs The elapsed time since this function was last called */ -void rayEnemyHiddenMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) +void rayEnemyHiddenMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { + // Pick a new direction every 1s + enemy->behaviorTimer -= elapsedUs; + if (enemy->behaviorTimer <= 0) + { + enemy->behaviorTimer += 1000000; + + // Randomize Strafe + if (esp_random() % 2) + { + enemy->behavior = MOVE_STRAFE_R; + } + else + { + enemy->behavior = MOVE_STRAFE_L; + } + } + + // Find the vector from the enemy to the player and normalize it + q24_8 xDiff = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDiff = SUB_FX(ray->p.posY, enemy->c.posY); + fastNormVec(&xDiff, &yDiff); + +// Player is 40000 * 6 +#define SPEED_DENOM_RETREAT (int32_t)(40000 * 6) +#define SPEED_DENOM_STRAFE (int32_t)(40000 * 16) + + q24_8 delX = 0; + q24_8 delY = 0; + + // Try to stay a constant-ish distance away + q24_8 xDist = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDist = SUB_FX(ray->p.posY, enemy->c.posY); + q24_8 distToPlayer = ADD_FX(MUL_FX(xDist, xDist), MUL_FX(yDist, yDist)); + if (distToPlayer < TO_FX(16)) + { + delX -= (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_RETREAT; + delY -= (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_RETREAT; + } + else if (distToPlayer > TO_FX(17)) + { + delX += (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_RETREAT; + delY += (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_RETREAT; + } + + // Do some strafing + if (MOVE_STRAFE_L == enemy->behavior) + { + delX += (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_STRAFE; + delY -= (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_STRAFE; + } + else + { + delX -= (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_STRAFE; + delY += (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM_STRAFE; + } + + q24_8 marginX = (delX > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + q24_8 marginY = (delY > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + + // Move if in bounds + if (isPassableCell( + &ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY + delY + marginY)])) + { + enemy->c.posX += delX; + enemy->c.posY += delY; + } + else + { + // Could not move into wall, reverse the strafe + if (MOVE_STRAFE_L == enemy->behavior) + { + enemy->behavior = MOVE_STRAFE_R; + } + else + { + enemy->behavior = MOVE_STRAFE_L; + } + } } /** @@ -22,5 +101,54 @@ void rayEnemyHiddenMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs */ bool rayEnemyHiddenGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) { - return false; + // Health starts at 100 + bool hurt = false; + switch (bullet) + { + case OBJ_BULLET_XRAY: + { + // Two shots to kill + enemy->health -= 50; + hurt = true; + break; + } + case OBJ_BULLET_ICE: + case OBJ_BULLET_NORMAL: + case OBJ_BULLET_CHARGE: + case OBJ_BULLET_MISSILE: + default: + { + // Not a bullet or invulnerable + break; + } + } + + if (hurt) + { + rayEnemyTransitionState(enemy, E_HURT); + } + return enemy->health <= 0; +} + +/** + * @brief Get the time until the next shot is taken + * + * @param enemy The enemy taking the shot + * @param type the timer of timer to get + * @return The time, in uS, until the next shot + */ +int32_t rayEnemyHiddenGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return 2000000 + (esp_random() % 2000000); +} + +/** + * @brief Get the bullet this enemy fires + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t rayEnemyHiddenGetBullet(rayEnemy_t* enemy) +{ + return OBJ_BULLET_XRAY; } diff --git a/main/modes/ray/enemies/ray_enemy_hidden.h b/main/modes/ray/enemies/ray_enemy_hidden.h index 0bd2f1872..73061a51c 100644 --- a/main/modes/ray/enemies/ray_enemy_hidden.h +++ b/main/modes/ray/enemies/ray_enemy_hidden.h @@ -2,5 +2,7 @@ #include "mode_ray.h" -void rayEnemyHiddenMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +void rayEnemyHiddenMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); bool rayEnemyHiddenGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +int32_t rayEnemyHiddenGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t rayEnemyHiddenGetBullet(rayEnemy_t* enemy); diff --git a/main/modes/ray/enemies/ray_enemy_normal.c b/main/modes/ray/enemies/ray_enemy_normal.c index 76cafe18b..6fc9c9686 100644 --- a/main/modes/ray/enemies/ray_enemy_normal.c +++ b/main/modes/ray/enemies/ray_enemy_normal.c @@ -1,14 +1,130 @@ +#include "ray_enemy.h" #include "ray_enemy_normal.h" /** - * @brief Run timers for a normal enemy, which include AI, movement, and animation + * @brief Run timers for a normal enemy, which include AI, and movement * * @param ray The entire game state * @param enemy The enemy to run timers for * @param elapsedUs The elapsed time since this function was last called */ -void rayEnemyNormalMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) +void rayEnemyNormalMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { + // Pick a new direction every 2s + enemy->behaviorTimer -= elapsedUs; + if (enemy->behaviorTimer <= 0) + { + enemy->behaviorTimer += 2000000; + + // Find the vector from the enemy to the player and normalize it + q24_8 xDiff = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDiff = SUB_FX(ray->p.posY, enemy->c.posY); + + // Pick the movement direction on the grid + if (esp_random() % 2 == 0) + { + if (xDiff > 0) + { + enemy->behavior = MOVE_POS_X; + } + else + { + enemy->behavior = MOVE_NEG_X; + } + } + else + { + if (yDiff > 0) + { + enemy->behavior = MOVE_POS_Y; + } + else + { + enemy->behavior = MOVE_NEG_Y; + } + } + } + + // Reverse behavior if too close to the player + q24_8 xDist = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDist = SUB_FX(ray->p.posY, enemy->c.posY); + q24_8 distToPlayer = ADD_FX(MUL_FX(xDist, xDist), MUL_FX(yDist, yDist)); + if (distToPlayer < TO_FX(4)) + { + switch (enemy->behavior) + { + case MOVE_POS_X: + { + enemy->behavior = MOVE_NEG_X; + break; + } + case MOVE_NEG_X: + { + enemy->behavior = MOVE_POS_X; + break; + } + case MOVE_POS_Y: + { + enemy->behavior = MOVE_NEG_Y; + break; + } + case MOVE_NEG_Y: + { + enemy->behavior = MOVE_POS_Y; + break; + } + default: + { + // Do nothing + break; + } + } + } + +// Player is 40000 * 6 +#define SPEED_DENOM (int32_t)(40000 * 18) + + q24_8 delX = 0; + q24_8 delY = 0; + switch (enemy->behavior) + { + case MOVE_POS_X: + { + delX = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_NEG_X: + { + delX = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_POS_Y: + { + delY = (TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_NEG_Y: + { + delY = -(TO_FX(1) * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + default: + { + // Do nothing + break; + } + } + + q24_8 marginX = (delX > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + q24_8 marginY = (delY > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + + // Move if in bounds + if (isPassableCell( + &ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY + delY + marginY)])) + { + enemy->c.posX += delX; + enemy->c.posY += delY; + } } /** @@ -22,5 +138,66 @@ void rayEnemyNormalMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs */ bool rayEnemyNormalGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) { - return true; + // Health starts at 100 + bool hurt = false; + switch (bullet) + { + case OBJ_BULLET_NORMAL: + { + // Three shots to kill + enemy->health -= 34; + hurt = true; + break; + } + case OBJ_BULLET_CHARGE: + { + // Two shots to kill + enemy->health -= 50; + hurt = true; + break; + } + case OBJ_BULLET_ICE: + case OBJ_BULLET_MISSILE: + case OBJ_BULLET_XRAY: + { + // Five shots to kill + enemy->health -= 20; + hurt = true; + break; + } + default: + { + // Not a bullet + break; + } + } + + if (hurt) + { + rayEnemyTransitionState(enemy, E_HURT); + } + return enemy->health <= 0; +} + +/** + * @brief Get the time until the next shot is taken + * + * @param enemy The enemy taking the shot + * @param type the timer of timer to get + * @return The time, in uS, until the next shot + */ +int32_t rayEnemyNormalGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return 2000000 + (esp_random() % 2000000); +} + +/** + * @brief Get the bullet this enemy fires + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t rayEnemyNormalGetBullet(rayEnemy_t* enemy) +{ + return OBJ_BULLET_NORMAL; } diff --git a/main/modes/ray/enemies/ray_enemy_normal.h b/main/modes/ray/enemies/ray_enemy_normal.h index f8f68770c..46a6b28b3 100644 --- a/main/modes/ray/enemies/ray_enemy_normal.h +++ b/main/modes/ray/enemies/ray_enemy_normal.h @@ -2,5 +2,7 @@ #include "mode_ray.h" -void rayEnemyNormalMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +void rayEnemyNormalMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); bool rayEnemyNormalGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +int32_t rayEnemyNormalGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t rayEnemyNormalGetBullet(rayEnemy_t* enemy); diff --git a/main/modes/ray/enemies/ray_enemy_strong.c b/main/modes/ray/enemies/ray_enemy_strong.c index d139f5e35..b7c5c9c5e 100644 --- a/main/modes/ray/enemies/ray_enemy_strong.c +++ b/main/modes/ray/enemies/ray_enemy_strong.c @@ -1,14 +1,109 @@ +#include "ray_enemy.h" #include "ray_enemy_strong.h" /** - * @brief Run timers for a strong enemy, which include AI, movement, and animation + * @brief Run timers for a strong enemy, which include AI, and movement * * @param ray The entire game state * @param enemy The enemy to run timers for * @param elapsedUs The elapsed time since this function was last called */ -void rayEnemyStrongMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) +void rayEnemyStrongMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs) { + // Pick a new direction every 2s + enemy->behaviorTimer -= elapsedUs; + if (enemy->behaviorTimer <= 0) + { + enemy->behaviorTimer += 2000000; + + // Randomize movement + switch (esp_random() % 8) + { + case 0: + { + enemy->behavior = MOVE_AWAY_PLAYER; + break; + } + case 1 ... 2: + { + enemy->behavior = MOVE_STRAFE_R; + break; + } + case 3 ... 4: + { + enemy->behavior = MOVE_STRAFE_L; + break; + } + case 5 ... 7: + { + enemy->behavior = MOVE_TOWARDS_PLAYER; + break; + } + } + } + + // Reverse behavior if too close to the player + q24_8 xDist = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDist = SUB_FX(ray->p.posY, enemy->c.posY); + q24_8 distToPlayer = ADD_FX(MUL_FX(xDist, xDist), MUL_FX(yDist, yDist)); + if (distToPlayer < TO_FX(4) && (MOVE_TOWARDS_PLAYER == enemy->behavior)) + { + enemy->behavior = MOVE_AWAY_PLAYER; + } + + // Find the vector from the enemy to the player and normalize it + q24_8 xDiff = SUB_FX(ray->p.posX, enemy->c.posX); + q24_8 yDiff = SUB_FX(ray->p.posY, enemy->c.posY); + fastNormVec(&xDiff, &yDiff); + +// Player is 40000 * 6 +#define SPEED_DENOM (int32_t)(40000 * 18) + + q24_8 delX = 0; + q24_8 delY = 0; + switch (enemy->behavior) + { + case MOVE_AWAY_PLAYER: + { + delX = -(xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = -(yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_TOWARDS_PLAYER: + { + delX = (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_STRAFE_L: + { + delX = (yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = -(xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + case MOVE_STRAFE_R: + { + delX = -(yDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + delY = (xDiff * (int32_t)(elapsedUs)) / SPEED_DENOM; + break; + } + default: + { + // Do nothing + break; + } + } + + q24_8 marginX = (delX > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + q24_8 marginY = (delY > 0 ? 1 : -1) * TO_FX_FRAC(1, 2); + + // Move if in bounds + if (isPassableCell( + &ray->map.tiles[FROM_FX(enemy->c.posX + delX + marginX)][FROM_FX(enemy->c.posY + delY + marginY)])) + { + enemy->c.posX += delX; + enemy->c.posY += delY; + } } /** @@ -22,9 +117,64 @@ void rayEnemyStrongMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs */ bool rayEnemyStrongGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet) { - if (OBJ_BULLET_CHARGE == bullet) + // Health starts at 100 + bool hurt = false; + switch (bullet) { - return true; + case OBJ_BULLET_NORMAL: + { + // Invincible to normal bullets + break; + } + case OBJ_BULLET_CHARGE: + { + // One shot, one kill + enemy->health -= 100; + hurt = true; + break; + } + case OBJ_BULLET_ICE: + case OBJ_BULLET_MISSILE: + case OBJ_BULLET_XRAY: + { + // Five shots to kill + enemy->health -= 20; + hurt = true; + break; + } + default: + { + // Not a bullet + break; + } } - return false; + + if (hurt) + { + rayEnemyTransitionState(enemy, E_HURT); + } + return enemy->health <= 0; +} + +/** + * @brief Get the time until the next shot is taken + * + * @param enemy The enemy taking the shot + * @param type the timer of timer to get + * @return The time, in uS, until the next shot + */ +int32_t rayEnemyStrongGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type) +{ + return 2000000 + (esp_random() % 2000000); +} + +/** + * @brief Get the bullet this enemy fires + * + * @param enemy The shooting enemy + * @return The bullet type + */ +rayMapCellType_t rayEnemyStrongGetBullet(rayEnemy_t* enemy) +{ + return OBJ_BULLET_CHARGE; } diff --git a/main/modes/ray/enemies/ray_enemy_strong.h b/main/modes/ray/enemies/ray_enemy_strong.h index d53ac5d20..3b1f31f00 100644 --- a/main/modes/ray/enemies/ray_enemy_strong.h +++ b/main/modes/ray/enemies/ray_enemy_strong.h @@ -2,5 +2,7 @@ #include "mode_ray.h" -void rayEnemyStrongMoveAnimate(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); +void rayEnemyStrongMove(ray_t* ray, rayEnemy_t* enemy, uint32_t elapsedUs); bool rayEnemyStrongGetShot(ray_t* ray, rayEnemy_t* enemy, rayMapCellType_t bullet); +int32_t rayEnemyStrongGetTimer(rayEnemy_t* enemy, rayEnemyTimerType_t type); +rayMapCellType_t rayEnemyStrongGetBullet(rayEnemy_t* enemy); diff --git a/main/modes/ray/mode_ray.c b/main/modes/ray/mode_ray.c index 1b404e799..7f06a6f0e 100644 --- a/main/modes/ray/mode_ray.c +++ b/main/modes/ray/mode_ray.c @@ -14,6 +14,7 @@ #include "ray_pause.h" #include "ray_script.h" #include "ray_warp_screen.h" +#include "ray_death_screen.h" //============================================================================== // Function Prototypes @@ -24,7 +25,6 @@ static void rayExitMode(void); static void rayMainLoop(int64_t elapsedUs); static void rayBackgroundDrawCallback(int16_t x, int16_t y, int16_t w, int16_t h, int16_t up, int16_t upNum); static void rayMenuCb(const char* label, bool selected, uint32_t settingVal); -static void rayStartGame(void); //============================================================================== // Const Variables @@ -38,7 +38,7 @@ const char rayExitStr[] = "Exit"; /// @brief A list of the map names const char* const rayMapNames[] = { - "World 0", "World 1", "World 2", "World 3", "World 4", "World 5", + "Station Zero", "Vinegrasp", "Floriss", "Station One", "Mosspire", "Scalderia", }; /// @brief A list of the map colors, in order @@ -51,6 +51,10 @@ const paletteColor_t rayMapColors[] = { c500, // Red }; +/// @brief The songs to play, must be in map order +const char* const songFiles[] + = {"base_0.sng", "jungle_0.sng", "cave_0.sng", "base_1.sng", "jungle_1.sng", "cave_1.sng", "ray_boss.sng"}; + /// @brief The NVS key to save and load player data const char RAY_NVS_KEY[] = "ray"; // The NVS key to save and load visited tiles @@ -106,18 +110,34 @@ static void rayEnterMode(void) // Initialize a renderer ray->renderer = initMenuLogbookRenderer(&ray->logbook); + // Force draw a loading screen + fillDisplayArea(0, 0, TFT_WIDTH, TFT_HEIGHT, c100); + const char loadingStr[] = "Loading..."; + int32_t tWidth = textWidth(&ray->logbook, loadingStr); + drawText(&ray->logbook, c542, loadingStr, (TFT_WIDTH - tWidth) / 2, (TFT_HEIGHT - ray->logbook.height) / 2); + drawDisplayTft(NULL); + // Initialize texture manager and environment textures loadEnvTextures(ray); - // Initialize enemy templates and textures - initEnemyTemplates(ray); + // Load songs + for (int32_t sIdx = 0; sIdx < ARRAY_SIZE(songFiles); sIdx++) + { + loadSong(songFiles[sIdx], &ray->songs[sIdx], true); + } // Set the menu as the screen ray->screen = RAY_MENU; + // Start a blink for dialog and pause and such + ray->blinkTimer = BLINK_US; + // Turn off LEDs led_t leds[CONFIG_NUM_LEDS] = {0}; setLeds(leds, CONFIG_NUM_LEDS); + + // Set frame rate to 60 FPS + setFrameRateUs(16666); } /** @@ -135,6 +155,12 @@ static void rayExitMode(void) // Free the textures freeAllTex(ray); + // Free songs + for (int32_t sIdx = 0; sIdx < ARRAY_SIZE(songFiles); sIdx++) + { + freeSong(&ray->songs[sIdx]); + } + // Free the font freeFont(&ray->ibm); freeFont(&ray->logbook); @@ -150,6 +176,32 @@ static void rayExitMode(void) */ void rayFreeCurrentState(ray_t* cRay) { + // Zero and NULL the door timer + ray->doorTimer = 0; + // Head bob + ray->posZ = 0; + ray->bobTimer = 0; + ray->bobCount = 0; + // Strafe and lock + ray->isStrafing = false; + ray->targetedObj = NULL; + // Player timers + ray->lavaTimer = 0; + ray->chargeTimer = 0; + ray->pRotationTimer = 0; + // Dialog variables + ray->dialogText = NULL; + ray->nextDialogText = NULL; + ray->dialogPortrait = NULL; + ray->btnLockoutUs = 0; + // Pause menu variables + ray->blinkTimer = 0; + ray->blink = false; + // Item rotation + ray->itemRotateTimer = 0; + ray->itemRotateDeg = 0; + ray->itemRotateMirror = false; + // Set invalid IDs for all bullets for (uint16_t objIdx = 0; objIdx < MAX_RAY_BULLETS; objIdx++) { @@ -188,6 +240,30 @@ void rayFreeCurrentState(ray_t* cRay) */ static void rayMainLoop(int64_t elapsedUs) { + // Run a button lockout, regardless of mode + if (0 < ray->btnLockoutUs) + { + ray->btnLockoutUs -= elapsedUs; + if (0 >= ray->btnLockoutUs) + { + ray->btnLockoutUs = 0; + // Restart blinks when the lockout ends + ray->blink = true; + ray->blinkTimer = BLINK_US; + } + } + + // Run a timer to blink things + if (0 < ray->blinkTimer) + { + ray->blinkTimer -= elapsedUs; + if (0 >= ray->blinkTimer) + { + ray->blink = !ray->blink; + ray->blinkTimer = BLINK_US; + } + } + switch (ray->screen) { case RAY_MENU: @@ -238,11 +314,7 @@ static void rayMainLoop(int64_t elapsedUs) checkRayCollisions(ray); // Check for time-based scripts - if (checkScriptTime(ray, elapsedUs)) - { - // Script warped, return - return; - } + checkScriptTime(ray, elapsedUs); // If the warp timer is active if (ray->warpTimerUs > 0) @@ -258,7 +330,7 @@ static void rayMainLoop(int64_t elapsedUs) case RAY_DIALOG: { // Render first - rayDialogRender(ray); + rayDialogRender(ray, elapsedUs); // Then check buttons rayDialogCheckButtons(ray); break; @@ -277,6 +349,11 @@ static void rayMainLoop(int64_t elapsedUs) rayWarpScreenRender(ray, elapsedUs); break; } + case RAY_DEATH_SCREEN: + { + rayDeathScreenRender(ray, elapsedUs); + break; + } } } @@ -297,6 +374,8 @@ static void rayBackgroundDrawCallback(int16_t x, int16_t y, int16_t w, int16_t h { case RAY_MENU: case RAY_DIALOG: + case RAY_DEATH_SCREEN: + case RAY_PAUSE: { // Do nothing break; @@ -307,12 +386,6 @@ static void rayBackgroundDrawCallback(int16_t x, int16_t y, int16_t w, int16_t h castFloorCeiling(ray, y, y + h); break; } - case RAY_PAUSE: - { - // Draw a black background - fillDisplayArea(x, y, x + w, y + h, c000); - break; - } case RAY_WARP_SCREEN: { drawWarpBackground(x, y, w, h); @@ -357,7 +430,7 @@ static void rayMenuCb(const char* label, bool selected, uint32_t settingVal) /** * @brief Start the game from the menu */ -static void rayStartGame(void) +void rayStartGame(void) { // Clear all lists rayFreeCurrentState(ray); @@ -366,12 +439,8 @@ static void rayStartGame(void) bool initFromScratch = initializePlayer(ray); // Load the map and object data - // Construct the map name - char mapName[] = "0.rmh"; - mapName[0] = '0' + ray->p.mapId; - // Load the new map q24_8 pStartX = 0, pStartY = 0; - loadRayMap(mapName, ray, &pStartX, &pStartY, true); + loadRayMap(ray->p.mapId, ray, &pStartX, &pStartY, true); // If the player was initialized from scratch if (initFromScratch) @@ -381,6 +450,9 @@ static void rayStartGame(void) ray->p.posY = pStartY; } + // Save a backup of the player state to restore in case of death + memcpy(&ray->p_backup, &ray->p, sizeof(rayPlayer_t)); + // Mark the starting tile as visited markTileVisited(&ray->map, FROM_FX(ray->p.posX), FROM_FX(ray->p.posY)); diff --git a/main/modes/ray/mode_ray.h b/main/modes/ray/mode_ray.h index 059a26a77..5b3504d8e 100644 --- a/main/modes/ray/mode_ray.h +++ b/main/modes/ray/mode_ray.h @@ -8,14 +8,12 @@ #include "swadge2024.h" #include "fp_math.h" #include "starfield.h" +#include "esp_random.h" //============================================================================== // Defines //============================================================================== -/** Use test textures. TODO: DELETE THIS */ -#define TEST_TEX 1 - /** The number of total maps */ #define NUM_MAPS 6 /** The number of keys per map */ @@ -44,14 +42,21 @@ /** The number of bullets tracked at a given point in time */ #define MAX_RAY_BULLETS 32 -/** The number of non-walking frames in an animation */ -#define NUM_NON_WALK_FRAMES 4 -/** The number of walking frames in an animation (non-walking doubled) */ -#define NUM_WALK_FRAMES (NUM_NON_WALK_FRAMES * 2) +/** The number of frames in an animation */ +#define NUM_ANIM_FRAMES 4 /** The time to swap out and swap in a gun, in microseconds */ #define LOADOUT_TIMER_US (1 << 18) +/** The blink time for pause and dialog items */ +#define BLINK_US 500000 + +/** The number of distinct enemies */ +#define NUM_ENEMIES (OBJ_ENEMY_BOSS - OBJ_ENEMY_NORMAL + 1) + +/** The time in uS for an enemy to warp in*/ +#define E_WARP_TIME 1000000 + /** * @brief Helper macro to check if a cell is of a given type * The type is the top three bits of the type @@ -90,24 +95,25 @@ typedef enum __attribute__((packed)) // Special delete tile, only used in map editor DELETE = (BG | META | 1), // Background tiles - BG_FLOOR = (BG | FLOOR | 1), - BG_FLOOR_WATER = (BG | FLOOR | 2), - BG_FLOOR_LAVA = (BG | FLOOR | 3), - BG_CEILING = (BG | FLOOR | 4), - BG_WALL_1 = (BG | WALL | 1), - BG_WALL_2 = (BG | WALL | 2), - BG_WALL_3 = (BG | WALL | 3), - BG_WALL_4 = (BG | WALL | 4), - BG_WALL_5 = (BG | WALL | 5), - BG_DOOR = (BG | DOOR | 1), - BG_DOOR_CHARGE = (BG | DOOR | 2), - BG_DOOR_MISSILE = (BG | DOOR | 3), - BG_DOOR_ICE = (BG | DOOR | 4), - BG_DOOR_XRAY = (BG | DOOR | 5), - BG_DOOR_SCRIPT = (BG | DOOR | 6), - BG_DOOR_KEY_A = (BG | DOOR | 7), - BG_DOOR_KEY_B = (BG | DOOR | 8), - BG_DOOR_KEY_C = (BG | DOOR | 9), + BG_FLOOR = (BG | FLOOR | 1), + BG_FLOOR_WATER = (BG | FLOOR | 2), + BG_FLOOR_LAVA = (BG | FLOOR | 3), + BG_CEILING = (BG | FLOOR | 4), + BG_WALL_1 = (BG | WALL | 1), + BG_WALL_2 = (BG | WALL | 2), + BG_WALL_3 = (BG | WALL | 3), + BG_WALL_4 = (BG | WALL | 4), + BG_WALL_5 = (BG | WALL | 5), + BG_DOOR = (BG | DOOR | 1), + BG_DOOR_CHARGE = (BG | DOOR | 2), + BG_DOOR_MISSILE = (BG | DOOR | 3), + BG_DOOR_ICE = (BG | DOOR | 4), + BG_DOOR_XRAY = (BG | DOOR | 5), + BG_DOOR_SCRIPT = (BG | DOOR | 6), + BG_DOOR_KEY_A = (BG | DOOR | 7), + BG_DOOR_KEY_B = (BG | DOOR | 8), + BG_DOOR_KEY_C = (BG | DOOR | 9), + BG_DOOR_ARTIFACT = (BG | DOOR | 10), // Enemies OBJ_ENEMY_START_POINT = (OBJ | ENEMY | 1), OBJ_ENEMY_NORMAL = (OBJ | ENEMY | 2), @@ -142,6 +148,12 @@ typedef enum __attribute__((packed)) // Scenery OBJ_SCENERY_TERMINAL = (OBJ | SCENERY | 1), OBJ_SCENERY_PORTAL = (OBJ | SCENERY | 2), + OBJ_SCENERY_F1 = (OBJ | SCENERY | 3), + OBJ_SCENERY_F2 = (OBJ | SCENERY | 4), + OBJ_SCENERY_F3 = (OBJ | SCENERY | 5), + OBJ_SCENERY_F4 = (OBJ | SCENERY | 6), + OBJ_SCENERY_F5 = (OBJ | SCENERY | 7), + OBJ_SCENERY_F6 = (OBJ | SCENERY | 8), } rayMapCellType_t; /** @@ -149,11 +161,32 @@ typedef enum __attribute__((packed)) */ typedef enum { - E_WALKING, ///< The enemy is walking - E_SHOOTING, ///< The enemy is shooting (may move while shooting) - E_HURT, ///< The enemy was shot + E_WALKING_1, ///< The enemy is walking, half cycle + E_WALKING_2, ///< The enemy is walking, the other half + E_SHOOTING, ///< The enemy is shooting (may move while shooting) + E_HURT, ///< The enemy was shot + E_BLOCKING, ///< The enemy is blocking + E_DEAD, ///< The enemy is dead + E_NUM_STATES, ///< The number of enemy states } rayEnemyState_t; +typedef enum +{ + DOING_NOTHING, + MOVE_POS_X, + MOVE_NEG_X, + MOVE_POS_Y, + MOVE_NEG_Y, + MOVE_STRAFE_L, + MOVE_STRAFE_R, + MOVE_TOWARDS_PLAYER, + MOVE_AWAY_PLAYER, + MOVE_NE, + MOVE_SE, + MOVE_SW, + MOVE_NW, +} rayEnemyBehavior_t; + /** * @brief All the possible loadouts */ @@ -167,6 +200,44 @@ typedef enum NUM_LOADOUTS ///< The number of loadouts } rayLoadout_t; +/** + * @brief All the possible environments + */ +typedef enum +{ + V_BASE, ///< Sci-fi base / space station + V_JUNGLE, ///< Jungle with alien ruins + V_CAVE, ///< Cave with lava + NUM_ENVS, ///< The number of environments +} rayEnv_t; + +/** + * @brief All the possible textures per-environments + */ +typedef enum +{ + TX_WALL_1, ///< Wall 1 + TX_WALL_2, ///< Wall 2 + TX_WALL_3, ///< Wall 3 + TX_WALL_4, ///< Wall 4 + TX_WALL_5, ///< Wall 5 + TX_FLOOR, ///< The floor texture + TX_CEILING, ///< The ceiling texture + NUM_ENV_TEXES, ///< The number of per-environment textures +} rayEnvTex_t; + +/** + * @brief All the possible boss states + */ +typedef enum +{ + B_MISSILE, ///< Weak to missiles + B_ICE, ///< Weak to ice + B_XRAY, ///< Weak to xray + B_NORMAL, ///< Weak to normal + NUM_BOSS_STATES, ///< The number of boss states +} rayBossState_t; + /** * @brief Types of events that trigger scripts */ @@ -235,11 +306,12 @@ typedef enum */ typedef enum { - RAY_MENU, ///< The main menu is being shown - RAY_GAME, ///< The game loop is being shown - RAY_DIALOG, ///< A dialog box is being shown - RAY_PAUSE, ///< The pause menu is being shown - RAY_WARP_SCREEN, ///< The warp screen animation is being shown + RAY_MENU, ///< The main menu is being shown + RAY_GAME, ///< The game loop is being shown + RAY_DIALOG, ///< A dialog box is being shown + RAY_PAUSE, ///< The pause menu is being shown + RAY_WARP_SCREEN, ///< The warp screen animation is being shown + RAY_DEATH_SCREEN, ///< The player has died } rayScreen_t; /** @@ -292,8 +364,9 @@ typedef struct */ typedef struct { - bool isActive; ///< true if the script is active, false if it is not - ifOp_t ifOp; ///< The type of condition that triggers the script + bool isActive; ///< true if the script is active, false if it is not + int32_t resetTimerSec; ///< Timer to not re-trigger the script immediately + ifOp_t ifOp; ///< The type of condition that triggers the script /// A union of arguments for the condition that triggers the script union { @@ -417,14 +490,22 @@ typedef struct */ typedef struct { - rayObjCommon_t c; ///< Common object properties - rayEnemyState_t state; ///< This enemy's current state - uint32_t animTimer; ///< A timer used for this enemy's animations - uint32_t animTimerLimit; ///< The time at which the texture should switch - uint32_t animTimerFrame; ///< The current animation frame - wsg_t* walkSprites[NUM_NON_WALK_FRAMES]; ///< The walking sprites for this enemy - wsg_t* shootSprites[NUM_NON_WALK_FRAMES]; ///< The shooting sprites for this enemy - wsg_t* hurtSprites[NUM_NON_WALK_FRAMES]; ///< The getting shot sprites for this enemy + rayObjCommon_t c; ///< Common object properties + int32_t health; ///< The enemy's health + rayEnemyState_t state; ///< This enemy's current state + rayEnemyBehavior_t behavior; ///< What the enemy is currently doing + int32_t warpTimer; ///< A timer for warping in + rayBossState_t bossState; ///< The current boss state, unused for non-boss + int32_t bossTimer; ///< A timer for changing bossState, unused for non-boss + int32_t behaviorTimer; ///< A timer used for this enemy's behaviors + int32_t shootTimer; ///< A timer used for this enemy's shooting + int32_t blockTimer; ///< A timer used for this enemy's blocking + int32_t invincibleTimer; ///< Timer for being invincible + int32_t freezeTimer; ///< Timer for slowdown after ice beam + int32_t animTimer; ///< A timer used for this enemy's animations + int32_t animTimerLimit; ///< The time at which the texture should switch + int32_t animFrame; ///< The current animation frame + wsg_t (*sprites)[E_NUM_STATES][NUM_ANIM_FRAMES]; ///< All of this enemy's sprites } rayEnemy_t; /** @@ -485,7 +566,8 @@ typedef struct rayMap_t map; ///< The loaded map int32_t doorTimer; ///< A timer used to open doors - rayPlayer_t p; ///< All the player's state, loaded from NVM + rayPlayer_t p; ///< All the player's state, loaded from NVM + rayPlayer_t p_backup; ///< All the player's state at the beginning of the map int32_t warpDestMapId; ///< The ID of the current map q24_8 warpDestPosX; ///< The player's X position @@ -517,15 +599,15 @@ typedef struct int32_t lavaTimer; ///< Timer to apply lava damage int32_t chargeTimer; ///< Timer to charge shots -#ifdef TEST_TEX - wsg_t testTextures[8]; ///< Test textures, TODO DELETE THESE -#endif - namedTexture_t* loadedTextures; ///< A list of loaded textures - uint8_t* typeToIdxMap; ///< A map of rayMapCellType_t to respective textures - wsg_t guns[NUM_LOADOUTS]; ///< Textures for the HUD guns - wsg_t portrait; ///< A portrait used for text dialogs - - rayEnemy_t eTemplates[6]; ///< Enemy type templates, copied when initializing enemies + namedTexture_t* loadedTextures; ///< A list of loaded textures + uint8_t* typeToIdxMap; ///< A map of rayMapCellType_t to respective textures + wsg_t envTex[NUM_ENVS][NUM_ENV_TEXES]; ///< The environment textures + wsg_t guns[NUM_LOADOUTS]; ///< Textures for the HUD guns + wsg_t portrait; ///< A portrait used for text dialogs + wsg_t missileHUDicon; ///< A missile icon drawn in the HUD + wsg_t enemyTex[NUM_ENEMIES][E_NUM_STATES][NUM_ANIM_FRAMES]; ///< The enemy textures + wsg_t hiddenXRTex[E_NUM_STATES][NUM_ANIM_FRAMES]; ///< The textures for X-Ray hidden enemies + wsg_t bossTex[NUM_BOSS_STATES - 1][E_NUM_STATES][NUM_ANIM_FRAMES]; ///< The textures for the boss font_t ibm; ///< A font to draw the HUD font_t logbook; ///< A font to draw the menu @@ -534,14 +616,23 @@ typedef struct const char* nextDialogText; ///< A pointer to the next dialog text, if it doesn't fit in one box wsg_t* dialogPortrait; ///< A portrait to draw above the dialog text - uint32_t pauseBlinkTimer; ///< A timer to blink things on the pause menu - bool pauseBlink; ///< Boolean for two draw states on the pause menu + int32_t btnLockoutUs; ///< A timer to block buttons when first showing a dialog + int32_t blinkTimer; ///< A timer to blink things on the pause menu + bool blink; ///< Boolean for two draw states on the pause menu list_t scripts[NUM_IF_OP_TYPES]; ///< An array of lists of scripts uint32_t scriptTimer; ///< A microsecond timer to check for time based scripts uint32_t secondsSinceStart; ///< The number of seconds since this map was loaded starfield_t starfield; ///< Starfield used for warp animation + + song_t songs[NUM_MAPS + 1]; ///< Per-map background music, plus a boss theme + + int32_t pRotationTimer; ///< timer for player rotation + + int32_t itemRotateTimer; ///< A timer to 'rotate' items by scaling the X direction + int32_t itemRotateDeg; ///< The number of degrees all items are 'rotated' by + bool itemRotateMirror; ///< If items should be drawn mirrored } ray_t; //============================================================================== @@ -560,5 +651,6 @@ extern const char* const RAY_NVS_VISITED_KEYS[]; //============================================================================== void rayFreeCurrentState(ray_t* ray); +void rayStartGame(void); #endif diff --git a/main/modes/ray/ray_death_screen.c b/main/modes/ray/ray_death_screen.c new file mode 100644 index 000000000..915f66558 --- /dev/null +++ b/main/modes/ray/ray_death_screen.c @@ -0,0 +1,66 @@ +//============================================================================== +// Includes +//============================================================================== + +#include "ray_death_screen.h" + +//============================================================================== +// Functions +//============================================================================== + +/** + * @brief Show the death screen and set up the button lockout + * + * @param ray The entire game state + */ +void rayShowDeathScreen(ray_t* ray) +{ + ray->btnLockoutUs = 2000000; + ray->screen = RAY_DEATH_SCREEN; + // Stop BGM when dead + bzrStop(true); +} + +/** + * @brief Draw the foreground for the death screen and run the timer + * + * @param ray The entire game state + * @param elapsedUs The elapsed time since this function was last called + */ +void rayDeathScreenRender(ray_t* ray, uint32_t elapsedUs) +{ + // Check the button queue + buttonEvt_t evt; + while (checkButtonQueueWrapper(&evt)) + { + if (0 == ray->btnLockoutUs) + { + // If A was pressed + if (evt.down) + { + if ((PB_A == evt.button) || (PB_B == evt.button)) + { + // Return to the menu + ray->screen = RAY_MENU; + } + } + } + } + + // Fill dark red background + fillDisplayArea(0, 0, TFT_WIDTH, TFT_HEIGHT, c100); + + // Draw some text + static const char deathText[] = "GG scrub git gud"; + int16_t tWidth = textWidth(&ray->logbook, deathText); + drawText(&ray->logbook, c542, deathText, (TFT_WIDTH - tWidth) / 2, (TFT_HEIGHT - ray->logbook.height) / 2); + + // Blink an arrow to show there's more dialog + if (ray->blink && 0 == ray->btnLockoutUs) + { +#define TRIANGLE_OFFSET 20 + drawTriangleOutlined(TFT_WIDTH - TRIANGLE_OFFSET - 16, TFT_HEIGHT - TRIANGLE_OFFSET - 4, + TFT_WIDTH - TRIANGLE_OFFSET - 4, TFT_HEIGHT - TRIANGLE_OFFSET - 10, + TFT_WIDTH - TRIANGLE_OFFSET - 16, TFT_HEIGHT - TRIANGLE_OFFSET - 16, c100, c542); + } +} diff --git a/main/modes/ray/ray_death_screen.h b/main/modes/ray/ray_death_screen.h new file mode 100644 index 000000000..04bf6868f --- /dev/null +++ b/main/modes/ray/ray_death_screen.h @@ -0,0 +1,9 @@ +#ifndef _RAY_DEATH_SCREEN_H_ +#define _RAY_DEATH_SCREEN_H_ + +#include "mode_ray.h" + +void rayShowDeathScreen(ray_t* ray); +void rayDeathScreenRender(ray_t* ray, uint32_t elapsedUs); + +#endif \ No newline at end of file diff --git a/main/modes/ray/ray_dialog.c b/main/modes/ray/ray_dialog.c index c402dffb3..3b36dae62 100644 --- a/main/modes/ray/ray_dialog.c +++ b/main/modes/ray/ray_dialog.c @@ -1,9 +1,10 @@ //============================================================================== -// Inclues +// Includes //============================================================================== #include "fill.h" #include "ray_dialog.h" +#include "esp_wifi.h" //============================================================================== // Defines @@ -17,6 +18,21 @@ #define DIALOG_BG_COLOR c000 #define DIALOG_TEXT_COLOR c240 +//============================================================================== +// Constant text +//============================================================================== + +const char* const macPuzzleText[] = { + "WARNING DATA CORRUPTED. Seek other bounty hunters to reconstruct data.\n\n4! is too excited, subtract 12 from " + "it\n[=/+._?@\n}+&)(/&?\n+[:&${)}", + "WARNING DATA CORRUPTED. Seek other bounty hunters to reconstruct data.\n\n.?%?-}{;\nTake the next answer, like a " + "tree find the square\n&,?:)/&:\n:/{#_!&,", + "WARNING DATA CORRUPTED. Seek other bounty hunters to reconstruct data.\n\n:#:$/{)]\n{?$-}*].\nArgon's number is " + "nice, but in two it must split\n/}%-%?}%", + "WARNING DATA CORRUPTED. Seek other bounty hunters to reconstruct data.\n\n{@#??_[!\n#,;{(;-{\n]#_!]=,.\nAverage " + "two prior, that's it, almost there", +}; + //============================================================================== // Functions //============================================================================== @@ -30,9 +46,19 @@ */ void rayShowDialog(ray_t* ray, const char* dialogText, wsg_t* dialogPortrait) { - ray->screen = RAY_DIALOG; - ray->dialogText = dialogText; + ray->screen = RAY_DIALOG; + if (0 == strcmp("MAC_PZL", dialogText)) + { + uint8_t macAddr[6]; + esp_wifi_get_mac(WIFI_IF_STA, macAddr); + ray->dialogText = macPuzzleText[macAddr[5] % 4]; + } + else + { + ray->dialogText = dialogText; + } ray->dialogPortrait = dialogPortrait; + ray->btnLockoutUs = 2000000; } /** @@ -47,19 +73,22 @@ void rayDialogCheckButtons(ray_t* ray) buttonEvt_t evt; while (checkButtonQueueWrapper(&evt)) { - // If A was pressed - if (PB_A == evt.button && evt.down) + if (0 == ray->btnLockoutUs) { - // If there is more dialog - if (NULL != ray->nextDialogText) - { - // Show the next part of the dialog - ray->dialogText = ray->nextDialogText; - } - else + // If A was pressed + if (PB_A == evt.button && evt.down) { - // Dialog over, return to game - ray->screen = RAY_GAME; + // If there is more dialog + if (NULL != ray->nextDialogText) + { + // Show the next part of the dialog + ray->dialogText = ray->nextDialogText; + } + else + { + // Dialog over, return to game + ray->screen = RAY_GAME; + } } } } @@ -69,8 +98,9 @@ void rayDialogCheckButtons(ray_t* ray) * @brief Render the current dialog box * * @param ray The entire game state + * @param elapsedUs The elapsed time since this function was last called */ -void rayDialogRender(ray_t* ray) +void rayDialogRender(ray_t* ray, uint32_t elapsedUs) { // Draw the background text box, outline drawRect(DIALOG_MARGIN, // @@ -113,4 +143,13 @@ void rayDialogRender(ray_t* ray) &yOff, // TFT_WIDTH - DIALOG_MARGIN - TEXT_MARGIN, // TFT_HEIGHT - DIALOG_MARGIN - TEXT_MARGIN); + + // Blink an arrow to show there's more dialog + if (ray->blink && 0 == ray->btnLockoutUs) + { + drawTriangleOutlined(TFT_WIDTH - DIALOG_MARGIN - 16, TFT_HEIGHT - DIALOG_MARGIN - 4, + TFT_WIDTH - DIALOG_MARGIN - 4, TFT_HEIGHT - DIALOG_MARGIN - 10, + TFT_WIDTH - DIALOG_MARGIN - 16, TFT_HEIGHT - DIALOG_MARGIN - 16, DIALOG_BG_COLOR, + DIALOG_TEXT_COLOR); + } } diff --git a/main/modes/ray/ray_dialog.h b/main/modes/ray/ray_dialog.h index 08068ba1a..a4e7b1968 100644 --- a/main/modes/ray/ray_dialog.h +++ b/main/modes/ray/ray_dialog.h @@ -5,6 +5,6 @@ void rayShowDialog(ray_t* ray, const char* dialogText, wsg_t* dialogPortrait); void rayDialogCheckButtons(ray_t* ray); -void rayDialogRender(ray_t* ray); +void rayDialogRender(ray_t* ray, uint32_t elapsedUs); #endif \ No newline at end of file diff --git a/main/modes/ray/ray_map.c b/main/modes/ray/ray_map.c index fbd6d9243..39762b925 100644 --- a/main/modes/ray/ray_map.c +++ b/main/modes/ray/ray_map.c @@ -15,6 +15,7 @@ #include "ray_tex_manager.h" #include "ray_renderer.h" #include "ray_script.h" +#include "ray_enemy.h" //============================================================================== // Functions @@ -24,27 +25,28 @@ * @brief Load a RMH from ROM to RAM. RMHs placed in the spiffs_image folder * before compilation will be automatically flashed to ROM * - * @param name The filename of the RMH to load + * @param mapId The map ID to load * @param ray The ray_t to load the map into * @param pStartX The starting X coordinate for this map * @param pStartY The starting Y coordinate for this map * @param spiRam true to load to SPI RAM, false to load to normal RAM. SPI RAM is more plentiful but slower to access * than normal RAM */ -void loadRayMap(const char* name, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bool spiRam) +void loadRayMap(int32_t mapId, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bool spiRam) { // Convenience inventory to know what not to spawn rayInventory_t* inv = &ray->p.i; - // Map ID is the first digit of the name - int16_t mapId = name[0] - '0'; - // Pick the allocation type uint32_t caps = spiRam ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DEFAULT; // Convenience pointers rayMap_t* map = &ray->map; + // Construct map name + char name[] = "0.rmh"; + name[0] = '0' + mapId; + // Read and decompress the file uint32_t decompressedSize = 0; uint8_t* fileData = readHeatshrinkFile(name, &decompressedSize, spiRam); @@ -107,7 +109,7 @@ void loadRayMap(const char* name, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bo // Allocate a new object if ((oType & 0x60) == ENEMY) { - rayCreateEnemy(ray, oType, id, x, y); + rayCreateEnemy(ray, oType, id, TO_FX(x) + TO_FX_FRAC(1, 2), TO_FX(y) + TO_FX_FRAC(1, 2)); } else { @@ -216,7 +218,8 @@ void loadRayMap(const char* name, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bo // Create this object if it wasn't already picked up if (shouldCreate) { - rayCreateCommonObj(ray, oType, id, x, y); + rayCreateCommonObj(ray, oType, id, TO_FX(x) + TO_FX_FRAC(1, 2), + TO_FX(y) + TO_FX_FRAC(1, 2)); } } } @@ -224,11 +227,19 @@ void loadRayMap(const char* name, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bo } } + // Reset script timers + ray->scriptTimer = 0; + ray->secondsSinceStart = 0; + // Load Scripts loadScripts(ray, &fileData[fileIdx], decompressedSize - fileIdx, caps); // Free the file data free(fileData); + + // Play this map's music + ray->songs[ray->p.mapId].shouldLoop = true; + bzrPlayBgm(&ray->songs[ray->p.mapId], BZR_STEREO); } /** @@ -237,27 +248,45 @@ void loadRayMap(const char* name, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bo * @param ray The entire game state * @param type The type of enemy to spawn * @param id The ID for this enemy - * @param x The X cell position for this enemy - * @param y The Y cell position for this enemy + * @param x The X position for this enemy + * @param y The Y position for this enemy */ -void rayCreateEnemy(ray_t* ray, rayMapCellType_t type, int32_t id, int32_t x, int32_t y) +void rayCreateEnemy(ray_t* ray, rayMapCellType_t type, int32_t id, q24_8 x, q24_8 y) { // Allocate the enemy rayEnemy_t* newObj = (rayEnemy_t*)heap_caps_calloc(1, sizeof(rayEnemy_t), MALLOC_CAP_SPIRAM); - // Copy enemy data from the template (sprite indices, type) - memcpy(newObj, &(ray->eTemplates[type - OBJ_ENEMY_NORMAL]), sizeof(rayEnemy_t)); - - // Set ID - newObj->c.id = id; - - // Set spatial values - newObj->c.posX = TO_FX(x) + TO_FX_FRAC(1, 2); - newObj->c.posY = TO_FX(y) + TO_FX_FRAC(1, 2); - newObj->c.radius = TO_FX_FRAC(newObj->c.sprite->w, 2 * TEX_WIDTH); + // Set type and ID first + newObj->c.type = type; + newObj->c.id = id; + + // Set initial enemy state + newObj->health = 100; + newObj->behavior = DOING_NOTHING; + newObj->behaviorTimer = esp_random() % 1000000; + newObj->shootTimer = getTimerForEnemy(newObj, SHOT); + newObj->blockTimer = getTimerForEnemy(newObj, BLOCK); + newObj->sprites = &ray->enemyTex[type - OBJ_ENEMY_NORMAL]; + newObj->warpTimer = E_WARP_TIME; + + // This sets state, animTimer, animTimerLimit, animFrame, and c.sprite + newObj->state = E_WALKING_2; + rayEnemyTransitionState(newObj, E_WALKING_1); + + // Set initial common state + newObj->c.posX = x; + newObj->c.posY = y; + newObj->c.radius = TO_FX_FRAC(newObj->c.sprite->w, 2 * TEX_WIDTH); + newObj->c.spriteMirrored = false; // Add it to the linked list push(&ray->enemies, newObj); + + // If the boss was spawned, play the theme + if (OBJ_ENEMY_BOSS == type) + { + bzrPlayBgm(&ray->songs[6], BZR_STEREO); + } } /** @@ -266,10 +295,10 @@ void rayCreateEnemy(ray_t* ray, rayMapCellType_t type, int32_t id, int32_t x, in * @param ray The entire game state * @param type The type of object to spawn * @param id The ID for this object - * @param x The X cell position for this object - * @param y The Y cell position for this object + * @param x The X position for this object + * @param y The Y position for this object */ -void rayCreateCommonObj(ray_t* ray, rayMapCellType_t type, int32_t id, int32_t x, int32_t y) +void rayCreateCommonObj(ray_t* ray, rayMapCellType_t type, int32_t id, q24_8 x, q24_8 y) { rayObjCommon_t* newObj = (rayObjCommon_t*)heap_caps_calloc(1, sizeof(rayObjCommon_t), MALLOC_CAP_SPIRAM); @@ -279,8 +308,8 @@ void rayCreateCommonObj(ray_t* ray, rayMapCellType_t type, int32_t id, int32_t x newObj->id = id; // Set spatial values - newObj->posX = TO_FX(x) + TO_FX_FRAC(1, 2); - newObj->posY = TO_FX(y) + TO_FX_FRAC(1, 2); + newObj->posX = x; + newObj->posY = y; newObj->radius = TO_FX_FRAC(newObj->sprite->w, 2 * TEX_WIDTH); // Add it to the linked list diff --git a/main/modes/ray/ray_map.h b/main/modes/ray/ray_map.h index 8f60dabeb..8563a773f 100644 --- a/main/modes/ray/ray_map.h +++ b/main/modes/ray/ray_map.h @@ -3,11 +3,11 @@ #include "mode_ray.h" -void loadRayMap(const char* name, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bool spiRam); +void loadRayMap(int32_t mapId, ray_t* ray, q24_8* pStartX, q24_8* pStartY, bool spiRam); void freeRayMap(rayMap_t* map); bool isPassableCell(rayMapCell_t* cell); void markTileVisited(rayMap_t* map, int16_t x, int16_t y); -void rayCreateEnemy(ray_t* ray, rayMapCellType_t type, int32_t id, int32_t x, int32_t y); -void rayCreateCommonObj(ray_t* ray, rayMapCellType_t type, int32_t id, int32_t x, int32_t y); +void rayCreateEnemy(ray_t* ray, rayMapCellType_t type, int32_t id, q24_8 x, q24_8 y); +void rayCreateCommonObj(ray_t* ray, rayMapCellType_t type, int32_t id, q24_8 x, q24_8 y); #endif \ No newline at end of file diff --git a/main/modes/ray/ray_object.c b/main/modes/ray/ray_object.c index dc07b9062..3d1c1ccd6 100644 --- a/main/modes/ray/ray_object.c +++ b/main/modes/ray/ray_object.c @@ -16,57 +16,12 @@ //============================================================================== static bool objectsIntersect(const rayObjCommon_t* obj1, const rayObjCommon_t* obj2); -static void moveRayBullets(ray_t* ray, int32_t elapsedUs); +static void moveRayBullets(ray_t* ray, uint32_t elapsedUs); //============================================================================== // Functions //============================================================================== -/** - * @brief Initialize templates for enemy creation. This also loads enemy sprites - * - * @param ray The entire game state - */ -void initEnemyTemplates(ray_t* ray) -{ - // The names of the different types of enemies - const char* eTypes[] = { - "NORMAL", "STRONG", "ARMORED", "FLAMING", "HIDDEN", "BOSS", - }; - - // The types of enemies - const rayMapCellType_t types[] = { - OBJ_ENEMY_NORMAL, OBJ_ENEMY_STRONG, OBJ_ENEMY_ARMORED, OBJ_ENEMY_FLAMING, OBJ_ENEMY_HIDDEN, OBJ_ENEMY_BOSS, - }; - - // An empty buffer to build strings - char buf[64] = {0}; - - // For each enemy type - for (int32_t eIdx = 0; eIdx < ARRAY_SIZE(ray->eTemplates); eIdx++) - { - // Set the type - ray->eTemplates[eIdx].c.type = types[eIdx]; - - // Set the time for each animation state - ray->eTemplates[eIdx].animTimerLimit = 250000; - - // Load textures for all states - for (int32_t frIdx = 0; frIdx < ARRAY_SIZE(ray->eTemplates->hurtSprites); frIdx++) - { - // Load the textures - snprintf(buf, sizeof(buf) - 1, "E_%s_WALK_%" PRId32 ".wsg", eTypes[eIdx], frIdx); - ray->eTemplates[eIdx].walkSprites[frIdx] = loadTexture(ray, buf, EMPTY); - snprintf(buf, sizeof(buf) - 1, "E_%s_SHOOT_%" PRId32 ".wsg", eTypes[eIdx], frIdx); - ray->eTemplates[eIdx].shootSprites[frIdx] = loadTexture(ray, buf, EMPTY); - snprintf(buf, sizeof(buf) - 1, "E_%s_HURT_%" PRId32 ".wsg", eTypes[eIdx], frIdx); - ray->eTemplates[eIdx].hurtSprites[frIdx] = loadTexture(ray, buf, EMPTY); - } - // Set initial texture - ray->eTemplates[eIdx].c.sprite = ray->eTemplates[eIdx].walkSprites[0]; - } -} - /** * @brief Create a bullet with an owner, type, position, and velocity * @@ -121,7 +76,7 @@ void rayCreateBullet(ray_t* ray, rayMapCellType_t bulletType, q24_8 posX, q24_8 * @param ray The entire game state * @param elapsedUs The elapsed time since this function was last called */ -void moveRayObjects(ray_t* ray, int32_t elapsedUs) +void moveRayObjects(ray_t* ray, uint32_t elapsedUs) { moveRayBullets(ray, elapsedUs); rayEnemiesMoveAnimate(ray, elapsedUs); @@ -133,7 +88,7 @@ void moveRayObjects(ray_t* ray, int32_t elapsedUs) * @param ray The entire game state * @param elapsedUs The elapsed time since this function was last called */ -static void moveRayBullets(ray_t* ray, int32_t elapsedUs) +static void moveRayBullets(ray_t* ray, uint32_t elapsedUs) { // For convenience int32_t rayMapId = ray->p.mapId; @@ -147,66 +102,110 @@ static void moveRayBullets(ray_t* ray, int32_t elapsedUs) { // Update the bullet's position // TODO justify the scaling factor, assuming velXY is a unit vector - obj->c.posX += (obj->velX * elapsedUs) / 100000; - obj->c.posY += (obj->velY * elapsedUs) / 100000; + obj->c.posX += (obj->velX * (int32_t)elapsedUs) / 100000; + obj->c.posY += (obj->velY * (int32_t)elapsedUs) / 100000; // Get the cell the bullet is in now rayMapCell_t* cell = &ray->map.tiles[FROM_FX(obj->c.posX)][FROM_FX(obj->c.posY)]; - // If a wall is it - if (CELL_IS_TYPE(cell->type, BG | WALL)) + // If the bullet hit something + if (!isPassableCell(cell)) { - // Check for shot wall scripts - if (checkScriptShootWall(ray, FROM_FX(obj->c.posX), FROM_FX(obj->c.posY))) + // If this is a player's bullet + if (1 == obj->c.id) { - // Script warped, return - return; - } - - // Destroy this bullet - memset(obj, 0, sizeof(rayBullet_t)); - obj->c.id = -1; - } - // Else if a door is hit - else if (CELL_IS_TYPE(cell->type, BG | DOOR)) - { - // If the door is closed - if (0 == cell->doorOpen) - { - // Check if the bullet type can open the door - if ((BG_DOOR == cell->type) // Normal doors are openable by anything - || (BG_DOOR_CHARGE == cell->type && OBJ_BULLET_CHARGE == obj->c.type) - // || (BG_DOOR_SCRIPT == cell->type) // Script doors aren't openable with bullets - || (BG_DOOR_MISSILE == cell->type && OBJ_BULLET_MISSILE == obj->c.type) - || (BG_DOOR_ICE == cell->type && OBJ_BULLET_ICE == obj->c.type) - || (BG_DOOR_XRAY == cell->type && OBJ_BULLET_XRAY == obj->c.type) - || (BG_DOOR_KEY_A == cell->type && KEY == ray->p.i.keys[rayMapId][0]) - || (BG_DOOR_KEY_B == cell->type && KEY == ray->p.i.keys[rayMapId][1]) - || (BG_DOOR_KEY_C == cell->type && KEY == ray->p.i.keys[rayMapId][2])) + // If it hit a wall + if (CELL_IS_TYPE(cell->type, BG | WALL)) { - // Start opening the door - cell->openingDirection = 1; - // Destroy this bullet - memset(obj, 0, sizeof(rayBullet_t)); - obj->c.id = -1; - - // If this door requires a key - if ((BG_DOOR_KEY_A == cell->type) || // - (BG_DOOR_KEY_B == cell->type) || // - (BG_DOOR_KEY_C == cell->type)) - { - // Use the key - ray->p.i.keys[rayMapId][cell->type - BG_DOOR_KEY_A] = OPEN_KEY; - } + // Check wall scripts + checkScriptShootWall(ray, FROM_FX(obj->c.posX), FROM_FX(obj->c.posY)); } - else + // If it hit a door + else if (CELL_IS_TYPE(cell->type, BG | DOOR)) { - // Shot a closed door it couldn't open - // destroy this bullet - memset(obj, 0, sizeof(rayBullet_t)); - obj->c.id = -1; + // If the door is closed + if (0 == cell->doorOpen) + { + bool opened = false; + switch (cell->type) + { + case BG_DOOR: + { + opened = true; + break; + } + case BG_DOOR_CHARGE: + { + opened = (OBJ_BULLET_CHARGE == obj->c.type); + break; + } + case BG_DOOR_MISSILE: + { + opened = (OBJ_BULLET_MISSILE == obj->c.type); + break; + } + case BG_DOOR_ICE: + { + opened = (OBJ_BULLET_ICE == obj->c.type); + break; + } + case BG_DOOR_XRAY: + { + opened = (OBJ_BULLET_XRAY == obj->c.type); + break; + } + case BG_DOOR_SCRIPT: + { + // Script doors aren't openable by bullets + break; + } + case BG_DOOR_KEY_A: + case BG_DOOR_KEY_B: + case BG_DOOR_KEY_C: + { + // Open the door if the player has the appropriate key + opened = (KEY == ray->p.i.keys[rayMapId][cell->type - BG_DOOR_KEY_A]); + if (opened) + { + // Mark the key as used + ray->p.i.keys[rayMapId][cell->type - BG_DOOR_KEY_A] = OPEN_KEY; + } + break; + } + case BG_DOOR_ARTIFACT: + { + // Check if all artifacts have been collected + opened = true; + for (int16_t aIdx = 0; aIdx < ARRAY_SIZE(ray->p.i.artifacts); aIdx++) + { + if (!ray->p.i.artifacts[aIdx]) + { + opened = false; + break; + } + } + break; + } + default: + { + // Not a door, somehow + break; + } + } + + // If the door was opened + if (opened) + { + // Start opening the door + cell->openingDirection = 1; + } + } } } + + // Destroy this bullet + memset(obj, 0, sizeof(rayBullet_t)); + obj->c.id = -1; } } } @@ -250,7 +249,42 @@ void checkRayCollisions(ray_t* ray) // An enemy's bullet if (objectsIntersect(&player, &bullet->c)) { - // TODO Player got shot, apply damage + // Determine the damage per-bullet + int32_t dmg = 0; + switch (bullet->c.type) + { + case OBJ_BULLET_NORMAL: + { + dmg = 5; + break; + } + case OBJ_BULLET_CHARGE: + { + dmg = 10; + break; + } + case OBJ_BULLET_MISSILE: + { + dmg = 15; + break; + } + case OBJ_BULLET_ICE: + { + dmg = 20; + break; + } + case OBJ_BULLET_XRAY: + { + dmg = 25; + break; + } + default: + { + break; + } + } + // Player got shot, apply damage + rayPlayerDecrementHealth(ray, dmg); // De-allocate the bullet bullet->c.id = -1; } @@ -271,11 +305,7 @@ void checkRayCollisions(ray_t* ray) // Touch the item rayPlayerTouchItem(ray, item->type, ray->p.mapId, item->id); // Check scripts - if (checkScriptGet(ray, item->id, item->sprite)) - { - // Script warped, return - return; - } + checkScriptGet(ray, item->id, item->sprite); // Mark this item for removal toRemove = currentNode; @@ -305,8 +335,6 @@ void checkRayCollisions(ray_t* ray) { // Get a pointer from the linked list rayEnemy_t* enemy = ((rayEnemy_t*)currentNode->val); - // Used for iterating - bool enemyWasKilled = false; // Iterate through all bullets for (uint16_t bIdx = 0; bIdx < MAX_RAY_BULLETS; bIdx++) @@ -317,44 +345,16 @@ void checkRayCollisions(ray_t* ray) // A player's bullet if (objectsIntersect(&enemy->c, &bullet->c)) { + // Decrease HP based on the shot and enemy type + rayEnemyGetShot(ray, enemy, bullet->c.type); // De-allocate the bullet bullet->c.id = -1; - - // Enemy got shot, apply damage - if (rayEnemyGetShot(ray, enemy, bullet->c.type)) - { - // Enemy was killed - checkScriptKill(ray, enemy->c.id, enemy->walkSprites[0]); - - // save the next node - node_t* nextNode = currentNode->next; - - // Remove the lock - if (ray->targetedObj == enemy) - { - ray->targetedObj = NULL; - } - - // Unlink and free - removeEntry(&ray->enemies, currentNode); - free(enemy); - - // Set the next node - currentNode = nextNode; - enemyWasKilled = true; - - break; - } } } } - // If the enemy was killed, iteration already happened - if (!enemyWasKilled) - { - // Iterate to the next node - currentNode = currentNode->next; - } + // Iterate to the next + currentNode = currentNode->next; } // Check if a bullet or the player touches scenery @@ -367,11 +367,7 @@ void checkRayCollisions(ray_t* ray) // Check if the player touches scenery if (objectsIntersect(&player, scenery)) { - if (checkScriptTouch(ray, scenery->id, scenery->sprite)) - { - // Script warped, return - return; - } + checkScriptTouch(ray, scenery->id, scenery->sprite); } // Iterate through all bullets @@ -387,11 +383,7 @@ void checkRayCollisions(ray_t* ray) bullet->c.id = -1; // Scenery was shot - if (checkScriptShootObjs(ray, scenery->id, scenery->sprite)) - { - // Script warped, return - return; - } + checkScriptShootObjs(ray, scenery->id, scenery->sprite); } } } diff --git a/main/modes/ray/ray_object.h b/main/modes/ray/ray_object.h index eabce0d71..bdd78a5bf 100644 --- a/main/modes/ray/ray_object.h +++ b/main/modes/ray/ray_object.h @@ -3,10 +3,9 @@ #include "mode_ray.h" -void initEnemyTemplates(ray_t* ray); void rayCreateBullet(ray_t* ray, rayMapCellType_t bulletType, q24_8 posX, q24_8 posY, q24_8 velX, q24_8 velY, bool isPlayer); -void moveRayObjects(ray_t* ray, int32_t elapsedUs); +void moveRayObjects(ray_t* ray, uint32_t elapsedUs); void checkRayCollisions(ray_t* ray); #endif diff --git a/main/modes/ray/ray_pause.c b/main/modes/ray/ray_pause.c index 751efe974..c203b3c73 100644 --- a/main/modes/ray/ray_pause.c +++ b/main/modes/ray/ray_pause.c @@ -3,12 +3,7 @@ //============================================================================== #include "ray_pause.h" - -//============================================================================== -// Defines -//============================================================================== - -#define PAUSE_BLINK_US 500000 +#include "ray_tex_manager.h" //============================================================================== // Enums @@ -113,6 +108,7 @@ const rayWorldMapLine_t warpLines[] = { static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs); static void rayPauseRenderWorldMap(ray_t* ray, uint32_t elapsedUs); +static void drawPlayerIndicator(ray_t* ray, int16_t cX, int16_t cY); //============================================================================== // Functions @@ -164,6 +160,9 @@ void rayPauseCheckButtons(ray_t* ray) */ void rayPauseRender(ray_t* ray, uint32_t elapsedUs) { + // Clear to black first + clearPxTft(); + // Render based on the displayed screen switch (ray->pauseScreen) { @@ -179,14 +178,6 @@ void rayPauseRender(ray_t* ray, uint32_t elapsedUs) break; } } - - // Run a timer to blink things - ray->pauseBlinkTimer += elapsedUs; - if (ray->pauseBlinkTimer > PAUSE_BLINK_US) - { - ray->pauseBlinkTimer -= PAUSE_BLINK_US; - ray->pauseBlink = !ray->pauseBlink; - } } /** @@ -197,12 +188,15 @@ void rayPauseRender(ray_t* ray, uint32_t elapsedUs) */ static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs) { + int16_t tWidth = textWidth(&ray->ibm, rayMapNames[ray->p.mapId]); + drawText(&ray->ibm, rayMapColors[ray->p.mapId], rayMapNames[ray->p.mapId], (TFT_WIDTH - tWidth) / 2, 2); + // Figure out the largest cell size to draw the whole map centered on the screen int16_t cellSizeW = TFT_WIDTH / ray->map.w; - int16_t cellSizeH = TFT_HEIGHT / ray->map.h; + int16_t cellSizeH = (TFT_HEIGHT - (ray->ibm.height + 4)) / ray->map.h; int16_t cellSize = MIN(cellSizeW, cellSizeH); int16_t cellOffX = (TFT_WIDTH - (ray->map.w * cellSize)) / 2; - int16_t cellOffY = (TFT_HEIGHT - (ray->map.h * cellSize)) / 2; + int16_t cellOffY = (ray->ibm.height + 4) + (((TFT_HEIGHT - (ray->ibm.height + 4)) - (ray->map.h * cellSize)) / 2); // For each cell for (int16_t y = 0; y < ray->map.h; y++) @@ -232,17 +226,17 @@ static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs) } case BG_DOOR_CHARGE: { - color = c234; + color = c404; break; } case BG_DOOR_MISSILE: { - color = c400; + color = c500; break; } case BG_DOOR_ICE: { - color = c004; + color = c005; break; } case BG_DOOR_XRAY: @@ -250,7 +244,7 @@ static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs) // Hide XRAY doors until the player gets the xrayLoadOut if (ray->p.i.xrayLoadOut) { - color = c020; + color = c050; } else { @@ -265,10 +259,23 @@ static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs) break; } case BG_DOOR_KEY_A: + { + color = c541; + break; + } case BG_DOOR_KEY_B: + { + color = c423; + break; + } case BG_DOOR_KEY_C: { - color = c440; + color = c234; + break; + } + case BG_DOOR_ARTIFACT: + { + color = c245; break; } default: @@ -315,13 +322,64 @@ static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs) } } + // Look through scenery for warp points + node_t* currentNode = ray->scenery.first; + while (currentNode != NULL) + { + // Get a pointer from the linked list + rayObjCommon_t* obj = ((rayObjCommon_t*)currentNode->val); + + // Look for portals + if (OBJ_SCENERY_PORTAL == obj->type) + { + // Found a portal, look for corresponding script + node_t* scriptNode = ray->scripts[TOUCH].first; + while (NULL != scriptNode) + { + rayScript_t* scr = scriptNode->val; + // If this is the right script for this object + if ((TOUCH == scr->ifOp) && (WARP == scr->thenOp) && (scr->ifArgs.idList.ids[0] == obj->id)) + { + // And the player has visited the other end of the warp + if (ray->p.mapsVisited[scr->thenArgs.warpDest.mapId]) + { + // Draw a number indicating the warp destination + char num[8]; + snprintf(num, sizeof(num) - 1, "%1d", scr->thenArgs.warpDest.mapId + 1); + tWidth = textWidth(&ray->ibm, num); + drawText(&ray->ibm, c555, num, + cellOffX + (cellSize * FROM_FX(obj->posX)) + (cellSize - tWidth) / 2, + cellOffY + (cellSize * FROM_FX(obj->posY)) + (cellSize - ray->ibm.height) / 2); + } + break; + } + scriptNode = scriptNode->next; + } + } + + // Iterate to the next node + currentNode = currentNode->next; + } + // The player's location blinks, so draw it when appropriate - if (ray->pauseBlink) + int16_t cX = cellOffX + FROM_FX(cellSize * ray->p.posX); + int16_t cY = cellOffY + FROM_FX(cellSize * ray->p.posY); + drawPlayerIndicator(ray, cX, cY); +} + +/** + * @brief Draw the blinking player indicator on the pause screen + * + * @param ray The entire game state + * @param cX The X pixel to center on + * @param cY The Y pixel to center on + */ +static void drawPlayerIndicator(ray_t* ray, int16_t cX, int16_t cY) +{ + if (ray->blink) { // Draw a circle for the player - int16_t cX = cellOffX + FROM_FX(cellSize * ray->p.posX); - int16_t cY = cellOffY + FROM_FX(cellSize * ray->p.posY); - int16_t cR = cellSize / 2; + int16_t cR = 6; drawCircle(cX, cY, cR, c145); // Draw a line for the player's direction @@ -340,12 +398,10 @@ static void rayPauseRenderLocalMap(ray_t* ray, uint32_t elapsedUs) */ static void rayPauseRenderWorldMap(ray_t* ray, uint32_t elapsedUs) { - fillDisplayArea(0, 0, TFT_WIDTH, TFT_HEIGHT, c100); - -#define MAP_X_MARGIN 16 -#define MAP_Y_MARGIN 36 -#define MAP_Y_MIDGAP 24 -#define MAP_SIZE 72 +#define MAP_X_MARGIN 16 +#define MAP_Y_MARGIN 36 +#define MAP_Y_MID_GAP 24 +#define MAP_SIZE 72 #define MAP_ROWS 2 #define MAP_COLS 3 @@ -355,6 +411,11 @@ static void rayPauseRenderWorldMap(ray_t* ray, uint32_t elapsedUs) // Save coordinates for where to draw warp lines int16_t warpCoords[NUM_MAPS][NUM_MAP_CORNERS][2]; + // A texture to draw after artifacts are acquired + wsg_t* artifactWsg = getTexByType(ray, OBJ_ITEM_ARTIFACT); + int16_t aAOffX = (MAP_SIZE - artifactWsg->w) / 2; + int16_t aAOffY = (MAP_SIZE - artifactWsg->h) / 2; + // For all six maps in a 3x2 grid for (int16_t mapY = 0; mapY < MAP_ROWS; mapY++) { @@ -368,14 +429,20 @@ static void rayPauseRenderWorldMap(ray_t* ray, uint32_t elapsedUs) { // Find the box coordinates int16_t startX = MAP_X_MARGIN + mapX * (MAP_SIZE + MAP_X_MARGIN); - int16_t startY = MAP_Y_MARGIN + mapY * (MAP_SIZE + MAP_Y_MIDGAP); + int16_t startY = MAP_Y_MARGIN + mapY * (MAP_SIZE + MAP_Y_MID_GAP); int16_t endX = startX + MAP_SIZE; int16_t endY = startY + MAP_SIZE; - // Draw the black box, outlined - fillDisplayArea(startX + 1, startY + 1, endX - 1, endY - 1, c000); + // Draw the dark red box, outlined + fillDisplayArea(startX + 1, startY + 1, endX - 1, endY - 1, c100); drawRect(startX, startY, endX, endY, rayMapColors[mapId]); + // Draw an artifact if was acquired here + if (ray->p.i.artifacts[mapId]) + { + drawWsgSimple(artifactWsg, startX + aAOffX, startY + aAOffY); + } + // Draw the name, either above or below the box int16_t tWidth = textWidth(&ray->ibm, rayMapNames[mapId]); if (0 == mapY) @@ -398,6 +465,14 @@ static void rayPauseRenderWorldMap(ray_t* ray, uint32_t elapsedUs) warpCoords[mapId][BOTTOM_LEFT][0] = startX + WARP_POINT_INDENT; warpCoords[mapId][BOTTOM_LEFT][1] = endY - WARP_POINT_INDENT - 1; + + if (ray->p.mapId == mapId) + { + int16_t pPosX = startX + (MAP_SIZE * ray->p.posX) / TO_FX(ray->map.w); + int16_t pPosY = startY + (MAP_SIZE * ray->p.posY) / TO_FX(ray->map.h); + + drawPlayerIndicator(ray, pPosX, pPosY); + } } } } diff --git a/main/modes/ray/ray_player.c b/main/modes/ray/ray_player.c index 11f7ceb71..848e800f8 100644 --- a/main/modes/ray/ray_player.c +++ b/main/modes/ray/ray_player.c @@ -10,6 +10,8 @@ #include "ray_map.h" #include "ray_pause.h" #include "ray_script.h" +#include "ray_death_screen.h" +#include "ray_enemy.h" //============================================================================== // Functions @@ -71,6 +73,20 @@ bool initializePlayer(ray_t* ray) ray->planeX = -MUL_FX(TO_FX(2) / 3, ray->p.dirY); ray->planeY = MUL_FX(TO_FX(2) / 3, ray->p.dirX); + // Always start with at least one missile to not get locked behind doors + if (ray->p.i.missileLoadOut) + { + if (0 == ray->p.i.numMissiles) + { + ray->p.i.numMissiles++; + } + } + + // Set the next loadout to current, to not swap + ray->nextLoadout = ray->p.loadout; + ray->loadoutChangeTimer = 0; + ray->forceLoadoutSwap = 0; + return initFromScratch; } @@ -136,7 +152,7 @@ void rayPlayerCheckButtons(ray_t* ray, rayObjCommon_t* centeredSprite, uint32_t // Set strafe to true ray->isStrafing = true; // If there is a centered sprite - if (centeredSprite) + if (centeredSprite && !CELL_IS_TYPE(centeredSprite->type, (OBJ | BULLET))) { // Lock onto it ray->targetedObj = centeredSprite; @@ -226,6 +242,14 @@ void rayPlayerCheckButtons(ray_t* ray, rayObjCommon_t* centeredSprite, uint32_t // Strafing is either locked or unlocked if (ray->isStrafing) { + if (NULL != ray->targetedObj) + { + // lock on the target before moving + pDirX = ray->targetedObj->posX - pPosX; + pDirY = ray->targetedObj->posY - pPosY; + fastNormVec(&pDirX, &pDirY); + } + if (ray->btnState & PB_RIGHT) { // Strafe right @@ -241,46 +265,46 @@ void rayPlayerCheckButtons(ray_t* ray, rayObjCommon_t* centeredSprite, uint32_t } else { - // Assume rightward rotation, 5 degrees every 40000uS - int32_t rotateDeg = ((5 * (int32_t)elapsedUs) / 40000) % 360; - if (ray->btnState & PB_RIGHT) - { - // Rotate right, leave as-is - } - else if (ray->btnState & PB_LEFT) + if (ray->btnState & (PB_RIGHT | PB_LEFT)) { - // Rotate left, reverse direction - rotateDeg = 360 - rotateDeg; - } - else - { - // No rotation, zero it out - rotateDeg = 0; - } - - // If we should rotate - if (rotateDeg) - { - // Do trig functions, only once - int32_t sinVal = getSin1024(rotateDeg); - int32_t cosVal = getCos1024(rotateDeg); - // Find the rotated X and Y vectors - q24_8 newX = (pDirX * cosVal) - (pDirY * sinVal); - q24_8 newY = (pDirX * sinVal) + (pDirY * cosVal); - // Normalize the vector - fastNormVec(&newX, &newY); - - // Recompute the camera plane, orthogonal to the direction vector and scaled to 2/3 - ray->planeX = -MUL_FX(TO_FX(2) / 3, newY); - ray->planeY = MUL_FX(TO_FX(2) / 3, newX); + // Assume rightward rotation, 1 degree every 8000uS + int32_t rotateDeg = 0; + ray->pRotationTimer -= elapsedUs; + while (0 >= ray->pRotationTimer) + { + ray->pRotationTimer += 8000; + rotateDeg++; + } - // Save new direction vector - ray->p.dirX = newX; - ray->p.dirY = newY; + if (0 != rotateDeg) + { + if (ray->btnState & PB_LEFT) + { + // Rotate left, reverse direction + rotateDeg = 360 - rotateDeg; + } - // Also update the local copy - pDirX = newX; - pDirY = newY; + // Do trig functions, only once + int32_t sinVal = getSin1024(rotateDeg); + int32_t cosVal = getCos1024(rotateDeg); + // Find the rotated X and Y vectors + q24_8 newX = (pDirX * cosVal) - (pDirY * sinVal); + q24_8 newY = (pDirX * sinVal) + (pDirY * cosVal); + // Normalize the vector + fastNormVec(&newX, &newY); + + // Save new direction vector + ray->p.dirX = newX; + ray->p.dirY = newY; + + // Recompute the camera plane, orthogonal to the direction vector and scaled to 2/3 + ray->planeX = -MUL_FX(TO_FX(2) / 3, ray->p.dirY); + ray->planeY = MUL_FX(TO_FX(2) / 3, ray->p.dirX); + + // Also update the local copy + pDirX = newX; + pDirY = newY; + } } } @@ -316,9 +340,9 @@ void rayPlayerCheckButtons(ray_t* ray, rayObjCommon_t* centeredSprite, uint32_t // If the player is in water if (isInWater) { - // Slow down movement by a fourth - deltaX /= 4; - deltaY /= 4; + // Slow down movement by a 8x + deltaX /= 8; + deltaY /= 8; } // Boundary checks are longer than the move dist to not get right up on the wall @@ -355,29 +379,25 @@ void rayPlayerCheckButtons(ray_t* ray, rayObjCommon_t* centeredSprite, uint32_t markTileVisited(&ray->map, newCellX, newCellY); // Check scripts when entering cells - if (checkScriptEnter(ray, newCellX, newCellY)) - { - // Script warped, return - return; - } + checkScriptEnter(ray, newCellX, newCellY); } + } - // After moving position, recompute direction to targeted object - if (ray->isStrafing && ray->targetedObj) - { - // Re-lock on the target after moving - pDirX = ray->targetedObj->posX - pPosX; - pDirY = ray->targetedObj->posY - pPosY; - fastNormVec(&pDirX, &pDirY); - - // Set the player's direction - ray->p.dirX = pDirX; - ray->p.dirY = pDirY; - - // Recompute the 2d rayCaster version of camera plane, orthogonal to the direction vector and scaled to 2/3 - ray->planeX = -MUL_FX(TO_FX(2) / 3, pDirY); - ray->planeY = MUL_FX(TO_FX(2) / 3, pDirX); - } + // Finally, if there is a targeted object, orient towards it + if (ray->targetedObj) + { + // Re-lock on the target + pDirX = ray->targetedObj->posX - pPosX; + pDirY = ray->targetedObj->posY - pPosY; + fastNormVec(&pDirX, &pDirY); + + // Set the player's direction + ray->p.dirX = pDirX; + ray->p.dirY = pDirY; + + // Recompute the 2d rayCaster version of camera plane, orthogonal to the direction vector and scaled to 2/3 + ray->planeX = -MUL_FX(TO_FX(2) / 3, ray->p.dirY); + ray->planeY = MUL_FX(TO_FX(2) / 3, ray->p.dirX); } } @@ -451,6 +471,16 @@ void rayPlayerCheckJoystick(ray_t* ray, uint32_t elapsedUs) { if (ray->p.loadout != ray->nextLoadout) { + // If swapping to or from XRAY, also swap hidden enemy sprites + if (LO_XRAY == ray->nextLoadout) + { + switchEnemiesToXray(ray, true); + } + else if (LO_XRAY == ray->p.loadout) + { + switchEnemiesToXray(ray, false); + } + // Swap the loadout ray->p.loadout = ray->nextLoadout; // Set the timer for the load in @@ -654,14 +684,41 @@ void rayPlayerCheckLava(ray_t* ray, uint32_t elapsedUs) if (ray->lavaTimer <= US_PER_LAVA_DAMAGE) { ray->lavaTimer -= US_PER_LAVA_DAMAGE; - if (ray->p.i.health) - { - ray->p.i.health--; - if (0 == ray->p.i.health) - { - // TODO game over - } - } + rayPlayerDecrementHealth(ray, 1); + } + } +} + +/** + * @brief Decrement player health and check for death + * + * @param ray The entire game state + * @param health The amount of health to decrement + */ +void rayPlayerDecrementHealth(ray_t* ray, int32_t health) +{ + // Decrement health + ray->p.i.health -= health; + // Check for death + if (0 >= ray->p.i.health) + { + // If the player already has the artifact + if (ray->p.i.artifacts[ray->p.mapId]) + { + // load the last save + rayStartGame(); } + else + { + // Player does not have this artifact, load the backup from when the map was entered + mempcpy(&ray->p, &ray->p_backup, sizeof(rayPlayer_t)); + raySavePlayer(ray); + // Clear visited tiles too + memset(ray->map.visitedTiles, NOT_VISITED, ray->map.w * ray->map.h * sizeof(rayTileState_t)); + raySaveVisitedTiles(ray); + } + + // Show the death screen + rayShowDeathScreen(ray); } } diff --git a/main/modes/ray/ray_player.h b/main/modes/ray/ray_player.h index e427d24a0..e6b69d46e 100644 --- a/main/modes/ray/ray_player.h +++ b/main/modes/ray/ray_player.h @@ -10,5 +10,6 @@ void rayPlayerTouchItem(ray_t* ray, rayMapCellType_t type, int32_t mapId, int32_ void rayPlayerCheckLava(ray_t* ray, uint32_t elapsedUs); void raySavePlayer(ray_t* ray); void raySaveVisitedTiles(ray_t* ray); +void rayPlayerDecrementHealth(ray_t* ray, int32_t health); #endif \ No newline at end of file diff --git a/main/modes/ray/ray_renderer.c b/main/modes/ray/ray_renderer.c index 966b90949..7276eeaa2 100644 --- a/main/modes/ray/ray_renderer.c +++ b/main/modes/ray/ray_renderer.c @@ -97,7 +97,7 @@ void castFloorCeiling(ray_t* ray, int32_t firstRow, int32_t lastRow) // Set a pointer for textures later paletteColor_t* texture = NULL; // The ceiling texture is always this - paletteColor_t* ceilTexture = getTexByType(ray, BG_CEILING)->px; + paletteColor_t* ceilTexture = ray->envTex[ray->p.mapId % NUM_ENVS][TX_CEILING].px; // Save these to not resolve pointers later uint32_t mapW = ray->map.w; @@ -197,21 +197,16 @@ void castFloorCeiling(ray_t* ray, int32_t firstRow, int32_t lastRow) { // Get the next cell texture rayMapCellType_t type = ray->map.tiles[cellX][cellY].type; - // Always draw floor under doors - if (CELL_IS_TYPE(type, BG | DOOR)) - { - type = BG_FLOOR; - } -#ifdef TEST_TEX - if ((ray->p.mapId < 4) && (BG_FLOOR_LAVA != type) && (BG_FLOOR_WATER != type)) + // Water and lava are special + if ((BG_FLOOR_LAVA == type) || (BG_FLOOR_WATER == type)) { - texture = ray->testTextures[ray->p.mapId * 2].px; + texture = getTexByType(ray, type)->px; } else -#endif { - texture = getTexByType(ray, type)->px; + // Otherwise draw normal floor for this map + texture = ray->envTex[ray->p.mapId % NUM_ENVS][TX_FLOOR].px; } } else @@ -513,22 +508,18 @@ void castWalls(ray_t* ray) // Pick the texture based on the map tile paletteColor_t* tex; + rayMapCellType_t type = ray->map.tiles[mapX][mapY].type; if (xrayOverride) { - tex = getTexByType(ray, BG_WALL_1)->px; + tex = ray->envTex[ray->p.mapId % NUM_ENVS][TX_WALL_1].px; + } + else if (BG_WALL_1 <= type && type <= BG_WALL_5) + { + tex = ray->envTex[ray->p.mapId % NUM_ENVS][type - BG_WALL_1].px; } else { -#ifdef TEST_TEX - if (ray->p.mapId < 4 && !CELL_IS_TYPE(ray->map.tiles[mapX][mapY].type, BG | DOOR)) - { - tex = ray->testTextures[ray->p.mapId * 2 + 1].px; - } - else -#endif - { - tex = getTexByType(ray, ray->map.tiles[mapX][mapY].type)->px; - } + tex = getTexByType(ray, type)->px; } // Draw a vertical strip @@ -780,6 +771,9 @@ rayObjCommon_t* castSprites(ray_t* ray) // Sort the sprites by distance qsort(allObjs, allObjsIdx, sizeof(objDist_t), objDistComparator); + // Calculate the width modifier for 'rotated' items + int32_t widthMod = getSin1024(ray->itemRotateDeg); + // after sorting the sprites, do the projection and draw them for (int i = 0; i < allObjsIdx; i++) { @@ -836,6 +830,22 @@ rayObjCommon_t* castSprites(ray_t* ray) spriteWidth = -spriteWidth; } + // If this is an item that should rotate + if (CELL_IS_TYPE(obj->type, OBJ | ITEM) && (OBJ_ITEM_ENERGY_TANK != obj->type) + && (OBJ_ITEM_PICKUP_ENERGY != obj->type) && (OBJ_ITEM_PICKUP_MISSILE != obj->type)) + { + // Scale this item's width according to current rotation + spriteWidth = (spriteWidth * widthMod) / 1024; + // If this scales to 0, don't draw it + if (0 == spriteWidth) + { + // If this sprite has zero width, don't draw it + continue; + } + // Copy the global mirror to this object + obj->spriteMirrored = ray->itemRotateMirror; + } + // This is the texture step per-screen-pixel q16_16 texXDelta = (tWidth << 16) / spriteWidth; // This is the inital texture X coordinate @@ -891,6 +901,25 @@ rayObjCommon_t* castSprites(ray_t* ray) // Find the pixel Y coordinate where the sprite draw starts. It may be negative int32_t drawStartY = (-spriteHeight + TFT_HEIGHT) / 2 + spritePosZ; + + // If this is an enemy + bool drawWarpLine = false; + if (CELL_IS_TYPE(obj->type, OBJ | ENEMY)) + { + rayEnemy_t* enemy = (rayEnemy_t*)obj; + // And the enemy is warping in + if (0 < enemy->warpTimer) + { + // Start drawing at an offset Y + int32_t offset = ((enemy->warpTimer * spriteHeight) / E_WARP_TIME); + drawStartY += offset; + // Start drawing within the sprite + initialTexY = texYDelta * (offset); + + drawWarpLine = true; + } + } + if (drawStartY < 0) { // Advance the initial texture Y coordinate by the difference @@ -923,6 +952,20 @@ rayObjCommon_t* castSprites(ray_t* ray) // Reset the texture Y coordinate q16_16 texY = initialTexY; + // If an enemy is warping in + if (drawWarpLine) + { + // Draw a line above the partial sprite + if (0 <= drawStartY - 1) + { + TURBO_SET_PIXEL(stripe, drawStartY - 1, c303); + if (0 <= drawStartY - 2) + { + TURBO_SET_PIXEL(stripe, drawStartY - 2, c550); + } + } + } + // for every pixel of the current stripe for (int32_t y = drawStartY; y < drawEndY; y++) { @@ -980,27 +1023,34 @@ void drawHud(ray_t* ray) drawWsgSimple(gun, TFT_WIDTH - gun->w, yOffset); } - // If the player has missiles - if (ray->p.i.missileLoadOut) - { - // Draw a count of missiles - char missileStr[16] = {0}; - snprintf(missileStr, sizeof(missileStr) - 1, "M:%02" PRId32 "/%02" PRId32, ray->p.i.numMissiles, - ray->p.i.maxNumMissiles); - drawText(&ray->ibm, c555, missileStr, 40, TFT_HEIGHT - ray->ibm.height); - } - - // Draw a count of Keys - char keyStr[16] = "K:"; + // Draw Keys + int16_t xOff = 40; + const rayMapCellType_t kTypes[] = {OBJ_ITEM_KEY_A, OBJ_ITEM_KEY_B, OBJ_ITEM_KEY_C}; for (int16_t kIdx = 0; kIdx < NUM_KEYS; kIdx++) { if (KEY == ray->p.i.keys[ray->p.mapId][kIdx]) { - char thisKeyStr[] = {'0' + kIdx, 0}; - strcat(keyStr, thisKeyStr); + wsg_t* keyTex = getTexByType(ray, kTypes[kIdx]); + drawWsgSimpleHalf(keyTex, xOff, TFT_HEIGHT - (keyTex->h / 2)); + xOff += (keyTex->w / 2) - 4; } } - drawText(&ray->ibm, c555, keyStr, 100, TFT_HEIGHT - ray->ibm.height); + + // If the player has missiles + if (ray->p.i.missileLoadOut) + { + xOff = 103; + // Get texture and string + wsg_t* mTex = &ray->missileHUDicon; + char missileStr[16] = {0}; + snprintf(missileStr, sizeof(missileStr) - 1, "%02" PRId32, ray->p.i.numMissiles); + + // Draw missile icon + drawWsgSimple(mTex, xOff, TFT_HEIGHT - mTex->h); + xOff += (mTex->w) + 2; + // Draw a count of missiles + drawText(&ray->ibm, c555, missileStr, xOff, TFT_HEIGHT - (mTex->h + ray->ibm.height) / 2); + } #define BAR_END_MARGIN 40 #define BAR_SIDE_MARGIN 8 @@ -1055,6 +1105,73 @@ void drawHud(ray_t* ray) TFT_WIDTH - BAR_SIDE_MARGIN, // chargeIndicatorStart, // sideBarColor); + + // Draw target if locked on + if (NULL != ray->targetedObj) + { + // Pick color based on loadout + paletteColor_t color = c114; + switch (ray->p.loadout) + { + default: + case LO_NORMAL: + { + color = c114; + break; + } + case LO_MISSILE: + { + color = c401; + break; + } + case LO_ICE: + { + color = c135; + break; + } + case LO_XRAY: + { + color = c130; + break; + } + } +#define T_SIZE 20 +#define T_THICK 3 +#define T_ARM 7 + // Draw some brackets + fillDisplayArea(TFT_WIDTH / 2 - T_SIZE, // + TFT_HEIGHT / 2 - T_SIZE, // + TFT_WIDTH / 2 - T_SIZE + T_THICK, // + TFT_HEIGHT / 2 + T_SIZE, // + color); + fillDisplayArea(TFT_WIDTH / 2 + T_SIZE - T_THICK, // + TFT_HEIGHT / 2 - T_SIZE, // + TFT_WIDTH / 2 + T_SIZE, // + TFT_HEIGHT / 2 + T_SIZE, // + color); + + fillDisplayArea(TFT_WIDTH / 2 - T_SIZE + T_THICK, // + TFT_HEIGHT / 2 - T_SIZE, // + TFT_WIDTH / 2 - T_SIZE + T_THICK + T_ARM, // + TFT_HEIGHT / 2 - T_SIZE + T_THICK, // + color); + fillDisplayArea(TFT_WIDTH / 2 - T_SIZE + T_THICK, // + TFT_HEIGHT / 2 + T_SIZE - T_THICK, // + TFT_WIDTH / 2 - T_SIZE + T_THICK + T_ARM, // + TFT_HEIGHT / 2 + T_SIZE, // + color); + + fillDisplayArea(TFT_WIDTH / 2 + T_SIZE - T_THICK - T_ARM, // + TFT_HEIGHT / 2 - T_SIZE, // + TFT_WIDTH / 2 + T_SIZE - T_THICK, // + TFT_HEIGHT / 2 - T_SIZE + T_THICK, // + color); + fillDisplayArea(TFT_WIDTH / 2 + T_SIZE - T_THICK - T_ARM, // + TFT_HEIGHT / 2 + T_SIZE - T_THICK, // + TFT_WIDTH / 2 + T_SIZE - T_THICK, // + TFT_HEIGHT / 2 + T_SIZE, // + color); + } } /** @@ -1163,4 +1280,19 @@ void runEnvTimers(ray_t* ray, uint32_t elapsedUs) } } } + + // "Rotate" item renders by one degree every 16384uS + ray->itemRotateTimer -= elapsedUs; + while (ray->itemRotateTimer <= 0) + { + ray->itemRotateTimer += 16384; + ray->itemRotateDeg++; + // Only rotate between 0 and 179 degrees + if (180 <= ray->itemRotateDeg) + { + ray->itemRotateDeg = 0; + // Mirror sprites every rotation to account for asymmetry + ray->itemRotateMirror = !ray->itemRotateMirror; + } + } } diff --git a/main/modes/ray/ray_script.c b/main/modes/ray/ray_script.c index 06f54774d..7837dd0cc 100644 --- a/main/modes/ray/ray_script.c +++ b/main/modes/ray/ray_script.c @@ -5,9 +5,9 @@ #include "ray_map.h" #include "ray_warp_screen.h" -static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait); -static bool checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* portrait); -static bool checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y); +static void executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait); +static void checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* portrait); +static void checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y); static void freeScript(rayScript_t* script); /** @@ -27,8 +27,8 @@ void loadScripts(ray_t* ray, const uint8_t* fileData, uint32_t fileSize, uint32_ // For each script for (int sIdx = 0; sIdx < numScripts; sIdx++) { - // Read the script length - uint8_t scriptLen = (fileData[fileIdx] << 8) | (fileData[fileIdx + 1]); + // Read the script length (don't actually care) + // uint8_t scriptLen = (fileData[fileIdx] << 8) | (fileData[fileIdx + 1]); fileIdx += 2; // Allocate a script @@ -285,9 +285,8 @@ static void freeScript(rayScript_t* script) * @param scriptList The list of scripts to check * @param id The ID to check * @param portrait A portrait to draw on dialogs - * @return true if the script warped and memory is unsafe to use */ -static bool checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* portrait) +static void checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* portrait) { // Iterate over all nodes node_t* currentNode = scriptList->first; @@ -366,14 +365,10 @@ static bool checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* por if (shouldExecute) { // Do it - if (executeScriptEvent(ray, script, portrait)) - { - // Script warped, return - return true; - } + executeScriptEvent(ray, script, portrait); - // Mark it as inactive if this is a one time script - script->isActive = (ALWAYS == script->ifArgs.idList.oneTime); + // Mark it as inactive if this is a one time script and the reset timer is inactive + script->isActive = (ALWAYS == script->ifArgs.idList.oneTime) && (0 == script->resetTimerSec); // Reset the triggered IDs memset(script->ifArgs.idList.idsTriggered, false, sizeof(bool) * script->ifArgs.idList.numIds); @@ -383,7 +378,6 @@ static bool checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* por // Move to the next script currentNode = currentNode->next; } - return false; } /** @@ -392,11 +386,10 @@ static bool checkScriptId(ray_t* ray, list_t* scriptList, int32_t id, wsg_t* por * @param ray The entire game state * @param id The ID of the shot object * @param portrait A portrait to draw on dialogs - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptShootObjs(ray_t* ray, int32_t id, wsg_t* portrait) +void checkScriptShootObjs(ray_t* ray, int32_t id, wsg_t* portrait) { - return checkScriptId(ray, &ray->scripts[SHOOT_OBJS], id, portrait); + checkScriptId(ray, &ray->scripts[SHOOT_OBJS], id, portrait); } /** @@ -405,11 +398,10 @@ bool checkScriptShootObjs(ray_t* ray, int32_t id, wsg_t* portrait) * @param ray The entire game state * @param id The ID of the dead enemy * @param portrait A portrait to draw on dialogs - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptKill(ray_t* ray, int32_t id, wsg_t* portrait) +void checkScriptKill(ray_t* ray, int32_t id, wsg_t* portrait) { - return checkScriptId(ray, &ray->scripts[KILL], id, portrait); + checkScriptId(ray, &ray->scripts[KILL], id, portrait); } /** @@ -418,11 +410,10 @@ bool checkScriptKill(ray_t* ray, int32_t id, wsg_t* portrait) * @param ray The entire game state * @param id The ID of the item obtained * @param portrait A portrait to draw on dialogs - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptGet(ray_t* ray, int32_t id, wsg_t* portrait) +void checkScriptGet(ray_t* ray, int32_t id, wsg_t* portrait) { - return checkScriptId(ray, &ray->scripts[GET], id, portrait); + checkScriptId(ray, &ray->scripts[GET], id, portrait); } /** @@ -431,11 +422,10 @@ bool checkScriptGet(ray_t* ray, int32_t id, wsg_t* portrait) * @param ray The entire game state * @param id The ID of the object touched * @param portrait A portrait to draw on dialogs - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptTouch(ray_t* ray, int32_t id, wsg_t* portrait) +void checkScriptTouch(ray_t* ray, int32_t id, wsg_t* portrait) { - return checkScriptId(ray, &ray->scripts[TOUCH], id, portrait); + checkScriptId(ray, &ray->scripts[TOUCH], id, portrait); } /** @@ -445,9 +435,8 @@ bool checkScriptTouch(ray_t* ray, int32_t id, wsg_t* portrait) * @param scriptList The list of scripts to check * @param x The X coordinate of the cell * @param y The Y coordinate of the cell - * @return true if the script warped and memory is unsafe to use */ -static bool checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y) +static void checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y) { // Iterate over all nodes node_t* currentNode = scriptList->first; @@ -526,14 +515,10 @@ static bool checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y if (shouldExecute) { // Do it - if (executeScriptEvent(ray, script, &ray->portrait)) - { - // Script warped, return - return true; - } + executeScriptEvent(ray, script, &ray->portrait); - // Mark it as inactive if this is a one time script - script->isActive = (ALWAYS == script->ifArgs.cellList.oneTime); + // Mark it as inactive if this is a one time script and the reset timer is inactive + script->isActive = (ALWAYS == script->ifArgs.cellList.oneTime) && (0 == script->resetTimerSec); // Reset the triggered cells memset(script->ifArgs.cellList.cellsTriggered, false, sizeof(bool) * script->ifArgs.cellList.numCells); @@ -543,7 +528,6 @@ static bool checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y // Move to the next script currentNode = currentNode->next; } - return false; } /** @@ -552,11 +536,10 @@ static bool checkScriptCell(ray_t* ray, list_t* scriptList, int32_t x, int32_t y * @param ray The entire game state * @param x The X coordinate of the shot wall * @param y The Y coordinate of the shot wall - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptShootWall(ray_t* ray, int32_t x, int32_t y) +void checkScriptShootWall(ray_t* ray, int32_t x, int32_t y) { - return checkScriptCell(ray, &ray->scripts[SHOOT_WALLS], x, y); + checkScriptCell(ray, &ray->scripts[SHOOT_WALLS], x, y); } /** @@ -565,11 +548,10 @@ bool checkScriptShootWall(ray_t* ray, int32_t x, int32_t y) * @param ray The entire game state * @param x The X coordinate of the cell entered * @param y The Y coordinate of the cell entered - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptEnter(ray_t* ray, int32_t x, int32_t y) +void checkScriptEnter(ray_t* ray, int32_t x, int32_t y) { - return checkScriptCell(ray, &ray->scripts[ENTER], x, y); + checkScriptCell(ray, &ray->scripts[ENTER], x, y); } /** @@ -577,9 +559,8 @@ bool checkScriptEnter(ray_t* ray, int32_t x, int32_t y) * * @param ray The entire game state * @param elapsedUs The time since this function was called last, in microseconds - * @return true if the script warped and memory is unsafe to use */ -bool checkScriptTime(ray_t* ray, uint32_t elapsedUs) +void checkScriptTime(ray_t* ray, uint32_t elapsedUs) { // Keep track of elapsed microseconds ray->scriptTimer += elapsedUs; @@ -604,11 +585,8 @@ bool checkScriptTime(ray_t* ray, uint32_t elapsedUs) if (script->ifArgs.time <= ray->secondsSinceStart) { // Do it - if (executeScriptEvent(ray, script, &ray->portrait)) - { - // Script warped, return - return true; - } + executeScriptEvent(ray, script, &ray->portrait); + // Mark it as inactive script->isActive = false; } @@ -617,8 +595,35 @@ bool checkScriptTime(ray_t* ray, uint32_t elapsedUs) // Move to the next currentNode = currentNode->next; } + + // For all script types + for (int16_t sIdx = 0; sIdx < ARRAY_SIZE(ray->scripts); sIdx++) + { + // For all scripts + currentNode = ray->scripts[sIdx].first; + while (currentNode != NULL) + { + // Get the script + rayScript_t* script = currentNode->val; + + // If there is time on the timer + if (0 < script->resetTimerSec) + { + // Decrement + script->resetTimerSec--; + // If it expired + if (0 == script->resetTimerSec) + { + // Reactivate the script + script->isActive = true; + } + } + + // Move to the next + currentNode = currentNode->next; + } + } } - return false; } /** @@ -627,9 +632,8 @@ bool checkScriptTime(ray_t* ray, uint32_t elapsedUs) * @param ray The entire game state * @param script The script which should be executed * @param portrait A portrait to draw on dialogs - * @param true if the player warped and scripts reloaded, false if there was no warp */ -static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) +static void executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) { switch (script->thenOp) { @@ -660,6 +664,8 @@ static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) } case SPAWN: { + // Start timer to not re-trigger immediately + script->resetTimerSec = 60; // Create objects for (int32_t sIdx = 0; sIdx < script->thenArgs.spawnList.numSpawns; sIdx++) { @@ -693,7 +699,7 @@ static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) if (!enemyExists) { - rayCreateEnemy(ray, type, id, x, y); + rayCreateEnemy(ray, type, id, TO_FX(x) + TO_FX_FRAC(1, 2), TO_FX(y) + TO_FX_FRAC(1, 2)); } } else if (ITEM == (type & 0x60)) @@ -719,7 +725,7 @@ static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) if (!itemExists) { - rayCreateCommonObj(ray, type, id, x, y); + rayCreateCommonObj(ray, type, id, TO_FX(x) + TO_FX_FRAC(1, 2), TO_FX(y) + TO_FX_FRAC(1, 2)); } } else if (SCENERY == (type & 0x60)) @@ -745,7 +751,7 @@ static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) if (!sceneryExists) { - rayCreateCommonObj(ray, type, id, x, y); + rayCreateCommonObj(ray, type, id, TO_FX(x) + TO_FX_FRAC(1, 2), TO_FX(y) + TO_FX_FRAC(1, 2)); } } } @@ -811,7 +817,6 @@ static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) // Save parameters because scripts will be freed in the process setWarpDestination(ray, script->thenArgs.warpDest.mapId, script->thenArgs.warpDest.pos.x, script->thenArgs.warpDest.pos.y); - return true; } break; } @@ -821,5 +826,4 @@ static bool executeScriptEvent(ray_t* ray, rayScript_t* script, wsg_t* portrait) break; } } - return false; } diff --git a/main/modes/ray/ray_script.h b/main/modes/ray/ray_script.h index 2d10bf1e0..c892dab31 100644 --- a/main/modes/ray/ray_script.h +++ b/main/modes/ray/ray_script.h @@ -6,12 +6,12 @@ void loadScripts(ray_t* ray, const uint8_t* fileData, uint32_t fileSize, uint32_t caps); void freeScripts(ray_t* ray); -bool checkScriptShootObjs(ray_t* ray, int32_t id, wsg_t* portrait); -bool checkScriptKill(ray_t* ray, int32_t id, wsg_t* portrait); -bool checkScriptGet(ray_t* ray, int32_t id, wsg_t* portrait); -bool checkScriptTouch(ray_t* ray, int32_t id, wsg_t* portrait); -bool checkScriptShootWall(ray_t* ray, int32_t x, int32_t y); -bool checkScriptEnter(ray_t* ray, int32_t x, int32_t y); -bool checkScriptTime(ray_t* ray, uint32_t elapsedUs); +void checkScriptShootObjs(ray_t* ray, int32_t id, wsg_t* portrait); +void checkScriptKill(ray_t* ray, int32_t id, wsg_t* portrait); +void checkScriptGet(ray_t* ray, int32_t id, wsg_t* portrait); +void checkScriptTouch(ray_t* ray, int32_t id, wsg_t* portrait); +void checkScriptShootWall(ray_t* ray, int32_t x, int32_t y); +void checkScriptEnter(ray_t* ray, int32_t x, int32_t y); +void checkScriptTime(ray_t* ray, uint32_t elapsedUs); #endif \ No newline at end of file diff --git a/main/modes/ray/ray_tex_manager.c b/main/modes/ray/ray_tex_manager.c index 3b92a969b..e20521b43 100644 --- a/main/modes/ray/ray_tex_manager.c +++ b/main/modes/ray/ray_tex_manager.c @@ -14,10 +14,9 @@ //============================================================================== /** - * The maximum number of loaded sprites. - * TODO pick a better number for all textures + * The maximum number of loaded named textures */ -#define MAX_LOADED_TEXTURES 128 +#define MAX_LOADED_TEXTURES 64 /// Helper macro to load textures #define LOAD_TEXTURE(r, t) loadTexture(r, #t ".wsg", t) @@ -26,11 +25,6 @@ // Functions //============================================================================== -#ifdef TEST_TEX -const char* const testTexNames[] = {"ICE_FLOOR.wsg", "ICE_WALL.wsg", "JUN_FLOOR.wsg", "JUN_WALL.wsg", - "LAV_FLOOR.wsg", "LAV_WALL.wsg", "SPA_FLOOR.wsg", "SPA_WALL.wsg"}; -#endif - /** * @brief Allocate memory and preload all environment textures * @@ -38,6 +32,7 @@ const char* const testTexNames[] = {"ICE_FLOOR.wsg", "ICE_WALL.wsg", "JUN_FLOOR. */ void loadEnvTextures(ray_t* ray) { + // Load a portrait for dialogs loadWsg("CHO_PORTRAIT.wsg", &ray->portrait, true); // Load HUD textures @@ -45,29 +40,76 @@ void loadEnvTextures(ray_t* ray) loadWsg("GUN_MISSILE.wsg", &ray->guns[LO_MISSILE], true); loadWsg("GUN_ICE.wsg", &ray->guns[LO_ICE], true); loadWsg("GUN_XRAY.wsg", &ray->guns[LO_XRAY], true); - -#ifdef TEST_TEX - for (int tIdx = 0; tIdx < ARRAY_SIZE(testTexNames); tIdx++) - { - loadWsg(testTexNames[tIdx], &ray->testTextures[tIdx], true); - } -#endif + loadWsg("HUD_MISSILE.wsg", &ray->missileHUDicon, true); // Allocate space for the textures ray->loadedTextures = heap_caps_calloc(MAX_LOADED_TEXTURES, sizeof(namedTexture_t), MALLOC_CAP_SPIRAM); // Types are 8 bit, non sequential, so allocate a 256 element array. Probably too many ray->typeToIdxMap = heap_caps_calloc(256, sizeof(uint8_t), MALLOC_CAP_SPIRAM); - // Load everything by type! - LOAD_TEXTURE(ray, BG_FLOOR); + // String buffer to load filenames + char fName[32]; + + // MUST be in the same order as rayEnv_t + const char* const env_types[] = {"BASE", "JUNGLE", "CAVE"}; + // MUST be in the same order as rayEnvTex_t + const char* const env_texes[] = {"WALL_1", "WALL_2", "WALL_3", "WALL_4", "WALL_5", "FLOOR", "CEILING"}; + // Load all environment textures + for (rayEnv_t e = 0; e < NUM_ENVS; e++) + { + for (rayEnvTex_t t = 0; t < NUM_ENV_TEXES; t++) + { + snprintf(fName, sizeof(fName) - 1, "BG_%s_%s.wsg", env_types[e], env_texes[t]); + loadWsg(fName, &ray->envTex[e][t], true); + } + } + + // MUST be in teh same order as rayMapCellType_t + const char* const enemyTypes[] = {"NORMAL", "STRONG", "ARMORED", "FLAMING", "HIDDEN", "BOSS"}; + // MUST be int the same order as rayEnemyState_t + const char* const enemyAnimations[] = {"WALK_0", "WALK_1", "SHOOT", "HURT", "BLOCK", "DEAD"}; + // Load all enemy textures + for (int32_t eIdx = 0; eIdx < NUM_ENEMIES; eIdx++) + { + for (rayEnemyState_t aIdx = 0; aIdx < E_NUM_STATES; aIdx++) + { + for (int32_t fIdx = 0; fIdx < NUM_ANIM_FRAMES; fIdx++) + { + snprintf(fName, sizeof(fName) - 1, "E_%s_%s_%" PRId32 ".wsg", enemyTypes[eIdx], enemyAnimations[aIdx], + fIdx); + loadWsg(fName, &ray->enemyTex[eIdx][aIdx][fIdx], true); + + // Also load alt-textures for the hidden enemy + if (eIdx == (OBJ_ENEMY_HIDDEN - OBJ_ENEMY_NORMAL)) + { + snprintf(fName, sizeof(fName) - 1, "E_%s_%s_%" PRId32 "_X.wsg", enemyTypes[eIdx], + enemyAnimations[aIdx], fIdx); + loadWsg(fName, &ray->hiddenXRTex[aIdx][fIdx], true); + } + // Also load alt-textures for the boss + else if (eIdx == (OBJ_ENEMY_BOSS - OBJ_ENEMY_NORMAL)) + { + snprintf(fName, sizeof(fName) - 1, "E_%s_%s_%" PRId32 "_M.wsg", enemyTypes[eIdx], + enemyAnimations[aIdx], fIdx); + loadWsg(fName, &ray->bossTex[B_MISSILE][aIdx][fIdx], true); + + snprintf(fName, sizeof(fName) - 1, "E_%s_%s_%" PRId32 "_I.wsg", enemyTypes[eIdx], + enemyAnimations[aIdx], fIdx); + loadWsg(fName, &ray->bossTex[B_ICE][aIdx][fIdx], true); + + snprintf(fName, sizeof(fName) - 1, "E_%s_%s_%" PRId32 "_X.wsg", enemyTypes[eIdx], + enemyAnimations[aIdx], fIdx); + loadWsg(fName, &ray->bossTex[B_XRAY][aIdx][fIdx], true); + } + } + } + } + + // Special floor tiles LOAD_TEXTURE(ray, BG_FLOOR_WATER); LOAD_TEXTURE(ray, BG_FLOOR_LAVA); - LOAD_TEXTURE(ray, BG_CEILING); - LOAD_TEXTURE(ray, BG_WALL_1); - LOAD_TEXTURE(ray, BG_WALL_2); - LOAD_TEXTURE(ray, BG_WALL_3); - LOAD_TEXTURE(ray, BG_WALL_4); - LOAD_TEXTURE(ray, BG_WALL_5); + + // Doors LOAD_TEXTURE(ray, BG_DOOR); LOAD_TEXTURE(ray, BG_DOOR_CHARGE); LOAD_TEXTURE(ray, BG_DOOR_MISSILE); @@ -77,6 +119,9 @@ void loadEnvTextures(ray_t* ray) LOAD_TEXTURE(ray, BG_DOOR_KEY_A); LOAD_TEXTURE(ray, BG_DOOR_KEY_B); LOAD_TEXTURE(ray, BG_DOOR_KEY_C); + LOAD_TEXTURE(ray, BG_DOOR_ARTIFACT); + + // Items LOAD_TEXTURE(ray, OBJ_ITEM_BEAM); LOAD_TEXTURE(ray, OBJ_ITEM_CHARGE_BEAM); LOAD_TEXTURE(ray, OBJ_ITEM_MISSILE); @@ -91,13 +136,25 @@ void loadEnvTextures(ray_t* ray) LOAD_TEXTURE(ray, OBJ_ITEM_ARTIFACT); LOAD_TEXTURE(ray, OBJ_ITEM_PICKUP_ENERGY); LOAD_TEXTURE(ray, OBJ_ITEM_PICKUP_MISSILE); + + // Bullets LOAD_TEXTURE(ray, OBJ_BULLET_NORMAL); LOAD_TEXTURE(ray, OBJ_BULLET_CHARGE); LOAD_TEXTURE(ray, OBJ_BULLET_ICE); LOAD_TEXTURE(ray, OBJ_BULLET_MISSILE); LOAD_TEXTURE(ray, OBJ_BULLET_XRAY); + + // Scenery LOAD_TEXTURE(ray, OBJ_SCENERY_TERMINAL); LOAD_TEXTURE(ray, OBJ_SCENERY_PORTAL); + + // Friends + LOAD_TEXTURE(ray, OBJ_SCENERY_F1); + LOAD_TEXTURE(ray, OBJ_SCENERY_F2); + LOAD_TEXTURE(ray, OBJ_SCENERY_F3); + LOAD_TEXTURE(ray, OBJ_SCENERY_F4); + LOAD_TEXTURE(ray, OBJ_SCENERY_F5); + LOAD_TEXTURE(ray, OBJ_SCENERY_F6); } /** @@ -170,13 +227,40 @@ void freeAllTex(ray_t* ray) freeWsg(&ray->guns[LO_MISSILE]); freeWsg(&ray->guns[LO_ICE]); freeWsg(&ray->guns[LO_XRAY]); + freeWsg(&ray->missileHUDicon); -#ifdef TEST_TEX - for (int tIdx = 0; tIdx < ARRAY_SIZE(testTexNames); tIdx++) + // Free all environment textures + for (rayEnv_t e = 0; e < NUM_ENVS; e++) { - freeWsg(&ray->testTextures[tIdx]); + for (rayEnvTex_t t = 0; t < NUM_ENV_TEXES; t++) + { + freeWsg(&ray->envTex[e][t]); + } + } + + // Free all enemy textures + for (int32_t eIdx = 0; eIdx < NUM_ENEMIES; eIdx++) + { + for (int32_t aIdx = 0; aIdx < E_NUM_STATES; aIdx++) + { + for (int32_t fIdx = 0; fIdx < NUM_ANIM_FRAMES; fIdx++) + { + freeWsg(&ray->enemyTex[eIdx][aIdx][fIdx]); + + // Also free alt-textures for the hidden enemy + if (eIdx == (OBJ_ENEMY_HIDDEN - OBJ_ENEMY_NORMAL)) + { + freeWsg(&ray->hiddenXRTex[aIdx][fIdx]); + } + else if (eIdx == (OBJ_ENEMY_BOSS - OBJ_ENEMY_NORMAL)) + { + freeWsg(&ray->bossTex[B_MISSILE][aIdx][fIdx]); + freeWsg(&ray->bossTex[B_ICE][aIdx][fIdx]); + freeWsg(&ray->bossTex[B_XRAY][aIdx][fIdx]); + } + } + } } -#endif if (ray->loadedTextures) { diff --git a/main/modes/ray/ray_warp_screen.c b/main/modes/ray/ray_warp_screen.c index 5e323f188..df64c8d07 100644 --- a/main/modes/ray/ray_warp_screen.c +++ b/main/modes/ray/ray_warp_screen.c @@ -52,6 +52,8 @@ void rayWarpScreenRender(ray_t* ray, uint32_t elapsedUs) ray->screen = RAY_GAME; // Don't warp again ray->warpTimerUs = 0; + // Play music + bzrPlayBgm(&ray->songs[ray->p.mapId], BZR_STEREO); } } @@ -72,6 +74,9 @@ void setWarpDestination(ray_t* ray, int32_t mapId, int16_t posX, int16_t posY) // Set the warp timer ray->warpTimerUs = 4000000; + + // Stop BGM when warping + bzrStop(true); } /** @@ -90,12 +95,12 @@ void warpToDestination(ray_t* ray) // Free the scripts rayFreeCurrentState(ray); - // Construct the map name - char mapName[] = "0.rmh"; - mapName[0] = '0' + ray->warpDestMapId; // Load the new map q24_8 pStartX = 0, pStartY = 0; - loadRayMap(mapName, ray, &pStartX, &pStartY, true); + loadRayMap(ray->warpDestMapId, ray, &pStartX, &pStartY, true); + + // Stop BGM when warping + bzrStop(true); // Set the map ID ray->p.mapId = ray->warpDestMapId; @@ -121,9 +126,21 @@ void warpToDestination(ray_t* ray) ray->planeX = -MUL_FX(TO_FX(2) / 3, ray->p.dirY); ray->planeY = MUL_FX(TO_FX(2) / 3, ray->p.dirX); + // Give the player one missile after warping to not get stuck behind doors + if (ray->p.i.missileLoadOut) + { + if (0 == ray->p.i.numMissiles) + { + ray->p.i.numMissiles++; + } + } + // Save after warping raySavePlayer(ray); + // Save a backup of the player state to restore in case of death + memcpy(&ray->p_backup, &ray->p, sizeof(rayPlayer_t)); + // Mark the starting tile as visited markTileVisited(&ray->map, FROM_FX(ray->p.posX), FROM_FX(ray->p.posY)); } diff --git a/tools/rayMapEditor/README.md b/tools/rayMapEditor/README.md index 95d081b3e..821e3b9de 100644 --- a/tools/rayMapEditor/README.md +++ b/tools/rayMapEditor/README.md @@ -109,6 +109,12 @@ These are the objects that can be spawned | `OBJ_ITEM_PICKUP_MISSILE` | Pickup, missiles | | `OBJ_SCENERY_TERMINAL` | Scenery, computer terminal | | `OBJ_SCENERY_PORTAL` | Scenery, portal | +| `OBJ_SCENERY_F1` | Scenery, a friend | +| `OBJ_SCENERY_F2` | Scenery, a friend | +| `OBJ_SCENERY_F3` | Scenery, a friend | +| `OBJ_SCENERY_F4` | Scenery, a friend | +| `OBJ_SCENERY_F5` | Scenery, a friend | +| `OBJ_SCENERY_F6` | Scenery, a friend | ### Script Examples diff --git a/tools/rayMapEditor/openAll.sh b/tools/rayMapEditor/openAll.sh index 1f5b2dd65..1f23518b7 100755 --- a/tools/rayMapEditor/openAll.sh +++ b/tools/rayMapEditor/openAll.sh @@ -1,8 +1,8 @@ #!/bin/bash -python ray_map_editor.py ../../assets/ray/0.rmd & -python ray_map_editor.py ../../assets/ray/1.rmd & -python ray_map_editor.py ../../assets/ray/2.rmd & -python ray_map_editor.py ../../assets/ray/3.rmd & -python ray_map_editor.py ../../assets/ray/4.rmd & -python ray_map_editor.py ../../assets/ray/5.rmd & +python ray_map_editor.py ../../assets/ray/maps/0.rmd & +python ray_map_editor.py ../../assets/ray/maps/1.rmd & +python ray_map_editor.py ../../assets/ray/maps/2.rmd & +python ray_map_editor.py ../../assets/ray/maps/3.rmd & +python ray_map_editor.py ../../assets/ray/maps/4.rmd & +python ray_map_editor.py ../../assets/ray/maps/5.rmd & diff --git a/tools/rayMapEditor/rme_model.py b/tools/rayMapEditor/rme_model.py index cd4ed05cb..2e0d70a1f 100644 --- a/tools/rayMapEditor/rme_model.py +++ b/tools/rayMapEditor/rme_model.py @@ -74,13 +74,13 @@ def getNextId(self): self.currentId = (self.currentId + 1) % 256 def getPaletteType(self, x, y): - if x < len(objTiles): + if x < len(bgTiles): # backgrounds in the first columns if 0 <= y and y < len(bgTiles[x]): return bgTiles[x][y] else: # objects i the other columns - x = x - len(objTiles) + x = x - len(bgTiles) if 0 <= y and y < len(objTiles[x]): return objTiles[x][y] return None diff --git a/tools/rayMapEditor/rme_script_editor.py b/tools/rayMapEditor/rme_script_editor.py index 308b6b164..ecad52fb1 100644 --- a/tools/rayMapEditor/rme_script_editor.py +++ b/tools/rayMapEditor/rme_script_editor.py @@ -382,10 +382,11 @@ def toBytes(self) -> bytearray: for id in self.thenArgs[kIds]: bytes.append(id) if kText in self.thenArgs.keys(): - textLen = len(self.thenArgs[kText]) + unescapedText = self.thenArgs[kText].replace('\\n', '\n').encode() + textLen = len(unescapedText) bytes.append((textLen >> 8) & 255) bytes.append((textLen >> 0) & 255) - bytes.extend(self.thenArgs[kText].encode()) + bytes.extend(unescapedText) if kMap in self.thenArgs.keys(): bytes.append(self.thenArgs[kMap]) if kCell in self.thenArgs.keys(): @@ -494,7 +495,8 @@ def fromBytes(self, bytes: bytearray): (bytes[idx + 1]) idx = idx + 2 # Read text - self.thenArgs[kText] = str(bytes[idx:idx + textLen], 'ascii') + self.thenArgs[kText] = str( + bytes[idx:idx + textLen], 'ascii').replace('\n', '\\n') idx = idx + textLen elif thenOpType.WARP == self.thenOp: # Read the map diff --git a/tools/rayMapEditor/rme_tiles.py b/tools/rayMapEditor/rme_tiles.py index 173b7596f..598a4dce5 100644 --- a/tools/rayMapEditor/rme_tiles.py +++ b/tools/rayMapEditor/rme_tiles.py @@ -40,6 +40,7 @@ class tileType(Enum): BG_DOOR_KEY_A = (BG | DOOR | 7) BG_DOOR_KEY_B = (BG | DOOR | 8) BG_DOOR_KEY_C = (BG | DOOR | 9) + BG_DOOR_ARTIFACT = (BG | DOOR | 10) # Self and Enemies OBJ_ENEMY_START_POINT = (OBJ | ENEMY | 1) OBJ_ENEMY_NORMAL = (OBJ | ENEMY | 2) @@ -74,6 +75,12 @@ class tileType(Enum): # Scenery OBJ_SCENERY_TERMINAL = (OBJ | SCENERY | 1) OBJ_SCENERY_PORTAL = (OBJ | SCENERY | 2) + OBJ_SCENERY_F1 = (OBJ | SCENERY | 3) + OBJ_SCENERY_F2 = (OBJ | SCENERY | 4) + OBJ_SCENERY_F3 = (OBJ | SCENERY | 5) + OBJ_SCENERY_F4 = (OBJ | SCENERY | 6) + OBJ_SCENERY_F5 = (OBJ | SCENERY | 7) + OBJ_SCENERY_F6 = (OBJ | SCENERY | 8) bgTiles: list[list[tileType]] = [ [ @@ -96,6 +103,7 @@ class tileType(Enum): tileType.BG_DOOR_KEY_A, tileType.BG_DOOR_KEY_B, tileType.BG_DOOR_KEY_C, + tileType.BG_DOOR_ARTIFACT, ] ] @@ -124,8 +132,16 @@ class tileType(Enum): tileType.OBJ_ITEM_SUIT_LAVA, tileType.OBJ_ITEM_ENERGY_TANK, tileType.OBJ_ITEM_ARTIFACT, + ], + [ tileType.OBJ_SCENERY_TERMINAL, tileType.OBJ_SCENERY_PORTAL, + tileType.OBJ_SCENERY_F1, + tileType.OBJ_SCENERY_F2, + tileType.OBJ_SCENERY_F3, + tileType.OBJ_SCENERY_F4, + tileType.OBJ_SCENERY_F5, + tileType.OBJ_SCENERY_F6, tileType.DELETE ] ] diff --git a/tools/rayMapEditor/rme_view.py b/tools/rayMapEditor/rme_view.py index f585b4985..84495a80a 100644 --- a/tools/rayMapEditor/rme_view.py +++ b/tools/rayMapEditor/rme_view.py @@ -116,7 +116,7 @@ def __init__(self): # Set up the canvasses self.paletteCanvas: tk.Canvas = tk.Canvas( - content, background=elemBgColor, width=self.paletteCellSize * 4, height=self.paletteCellSize * 8, + content, background=elemBgColor, width=self.paletteCellSize * 5, height=self.paletteCellSize * 8, highlightthickness=borderThickness, highlightbackground=borderColor) self.mapCanvas: tk.Canvas = tk.Canvas( content, background=elemBgColor, highlightthickness=borderThickness, highlightbackground=borderColor) @@ -222,21 +222,21 @@ def loadAllTextures(self): # Load all textures self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.BG_FLOOR, '../../assets/ray/env/BG_FLOOR.png') + tileType.BG_FLOOR, '../../assets/ray/env/BASE/BG_BASE_FLOOR.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.BG_FLOOR_WATER, '../../assets/ray/env/BG_FLOOR_WATER.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.BG_FLOOR_LAVA, '../../assets/ray/env/BG_FLOOR_LAVA.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.BG_WALL_1, '../../assets/ray/env/BG_WALL_1.png') + tileType.BG_WALL_1, '../../assets/ray/env/BASE/BG_BASE_WALL_1.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.BG_WALL_2, '../../assets/ray/env/BG_WALL_2.png') + tileType.BG_WALL_2, '../../assets/ray/env/BASE/BG_BASE_WALL_2.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.BG_WALL_3, '../../assets/ray/env/BG_WALL_3.png') + tileType.BG_WALL_3, '../../assets/ray/env/BASE/BG_BASE_WALL_3.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.BG_WALL_4, '../../assets/ray/env/BG_WALL_4.png') + tileType.BG_WALL_4, '../../assets/ray/env/BASE/BG_BASE_WALL_4.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.BG_WALL_5, '../../assets/ray/env/BG_WALL_5.png') + tileType.BG_WALL_5, '../../assets/ray/env/BASE/BG_BASE_WALL_5.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.BG_DOOR, '../../assets/ray/doors/BG_DOOR.png') self.loadTexture(self.texMapPalette, self.texMapMap, @@ -255,20 +255,22 @@ def loadAllTextures(self): tileType.BG_DOOR_KEY_B, '../../assets/ray/doors/BG_DOOR_KEY_B.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.BG_DOOR_KEY_C, '../../assets/ray/doors/BG_DOOR_KEY_C.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.BG_DOOR_ARTIFACT, '../../assets/ray/doors/BG_DOOR_ARTIFACT.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.OBJ_ENEMY_START_POINT, 'imgs/OBJ_ENEMY_START_POINT.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.OBJ_ENEMY_NORMAL, '../../assets/ray/enemies/NORMAL/E_NORMAL_WALK_0.png') + tileType.OBJ_ENEMY_NORMAL, '../../assets/ray/enemies/NORMAL/E_NORMAL_WALK_0_0.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.OBJ_ENEMY_STRONG, '../../assets/ray/enemies/STRONG/E_STRONG_WALK_0.png') + tileType.OBJ_ENEMY_STRONG, '../../assets/ray/enemies/STRONG/E_STRONG_WALK_0_0.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.OBJ_ENEMY_ARMORED, '../../assets/ray/enemies/ARMORED/E_ARMORED_WALK_0.png') + tileType.OBJ_ENEMY_ARMORED, '../../assets/ray/enemies/ARMORED/E_ARMORED_WALK_0_0.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.OBJ_ENEMY_FLAMING, '../../assets/ray/enemies/FLAMING/E_FLAMING_WALK_0.png') + tileType.OBJ_ENEMY_FLAMING, '../../assets/ray/enemies/FLAMING/E_FLAMING_WALK_0_0.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.OBJ_ENEMY_HIDDEN, '../../assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0.png') + tileType.OBJ_ENEMY_HIDDEN, '../../assets/ray/enemies/HIDDEN/E_HIDDEN_WALK_0_0.png') self.loadTexture(self.texMapPalette, self.texMapMap, - tileType.OBJ_ENEMY_BOSS, '../../assets/ray/enemies/BOSS/E_BOSS_WALK_0.png') + tileType.OBJ_ENEMY_BOSS, '../../assets/ray/enemies/BOSS/E_BOSS_WALK_0_0.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.OBJ_ITEM_BEAM, '../../assets/ray/items/OBJ_ITEM_BEAM.png') self.loadTexture(self.texMapPalette, self.texMapMap, @@ -301,6 +303,18 @@ def loadAllTextures(self): tileType.OBJ_SCENERY_TERMINAL, '../../assets/ray/scenery/OBJ_SCENERY_TERMINAL.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.OBJ_SCENERY_PORTAL, '../../assets/ray/scenery/OBJ_SCENERY_PORTAL.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.OBJ_SCENERY_F1, '../../assets/ray/friends/OBJ_SCENERY_F1.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.OBJ_SCENERY_F2, '../../assets/ray/friends/OBJ_SCENERY_F2.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.OBJ_SCENERY_F3, '../../assets/ray/friends/OBJ_SCENERY_F3.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.OBJ_SCENERY_F4, '../../assets/ray/friends/OBJ_SCENERY_F4.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.OBJ_SCENERY_F5, '../../assets/ray/friends/OBJ_SCENERY_F5.png') + self.loadTexture(self.texMapPalette, self.texMapMap, + tileType.OBJ_SCENERY_F6, '../../assets/ray/friends/OBJ_SCENERY_F6.png') self.loadTexture(self.texMapPalette, self.texMapMap, tileType.DELETE, 'imgs/DELETE.png') diff --git a/tools/spiffs_file_preprocessor/.gitignore b/tools/spiffs_file_preprocessor/.gitignore index 441f7fce0..b80db4651 100644 --- a/tools/spiffs_file_preprocessor/.gitignore +++ b/tools/spiffs_file_preprocessor/.gitignore @@ -1 +1,3 @@ -spiffs_file_preprocessor \ No newline at end of file +spiffs_file_preprocessor +in +out \ No newline at end of file diff --git a/tools/spiffs_file_preprocessor/.vscode/c_cpp_properties.json b/tools/spiffs_file_preprocessor/.vscode/c_cpp_properties.json index 16b60fc25..405ce14c4 100644 --- a/tools/spiffs_file_preprocessor/.vscode/c_cpp_properties.json +++ b/tools/spiffs_file_preprocessor/.vscode/c_cpp_properties.json @@ -6,11 +6,9 @@ "${workspaceFolder}/**" ], "defines": [], - "windowsSdkVersion": "10.0.18362.0", - "compilerPath": "C:/msys64/usr/bin/gcc.exe", "cStandard": "c17", "cppStandard": "c++17", - "intelliSenseMode": "windows-gcc-x64" + "intelliSenseMode": "gcc-x64" } ], "version": 4 diff --git a/tools/spiffs_file_preprocessor/.vscode/launch.json b/tools/spiffs_file_preprocessor/.vscode/launch.json index 6bfe2f111..a983e64f3 100644 --- a/tools/spiffs_file_preprocessor/.vscode/launch.json +++ b/tools/spiffs_file_preprocessor/.vscode/launch.json @@ -11,16 +11,15 @@ "program": "${workspaceFolder}/spiffs_file_preprocessor", "args": [ "-i", - "../../assets", + "./in", "-o", - "../../spiffs_image" + "./out" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", - "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", "setupCommands": [ { "description": "Enable pretty-printing for gdb", diff --git a/tools/spiffs_file_preprocessor/src/midi_processor.c b/tools/spiffs_file_preprocessor/src/midi_processor.c index 018f9d574..cf7735cfe 100644 --- a/tools/spiffs_file_preprocessor/src/midi_processor.c +++ b/tools/spiffs_file_preprocessor/src/midi_processor.c @@ -246,7 +246,7 @@ void process_midi(const char* inFile, const char* outDir) notes[trackIdx] = calloc((2 * track->nbOfNotes) + 1, sizeof(musicalNote_t)); /* For each note */ - unsigned long int lastNoteStart = track->notes[0].timeBeforeAppear; + unsigned long int lastNoteStart = 0; // track->notes[0].timeBeforeAppear; unsigned long int introRest = lastNoteStart; for (int midiNoteIdx = 0; midiNoteIdx < track->nbOfNotes; midiNoteIdx++) @@ -414,10 +414,10 @@ static void checkMidiEvents(MidiParser* midiParser, unsigned long int lastNoteSt for (int evtIdx = 0; evtIdx < track->nbOfEvents; evtIdx++) { Event* evt = &track->events[evtIdx]; - // printf("%5d: %s\n", evt->timeToAppear, evt->type); + // printf("%5d: %d\n", evt->timeToAppear, evt->type); // If this event occurs between the notes we're at, process it - if ((0 == lastNoteStart && 0 == thisNoteStart && 0 == evt->timeToAppear) + if ((0 == lastNoteStart && 0 == evt->timeToAppear) || (lastNoteStart < evt->timeToAppear && evt->timeToAppear <= thisNoteStart)) { switch (evt->type) @@ -426,7 +426,7 @@ static void checkMidiEvents(MidiParser* midiParser, unsigned long int lastNoteSt { // printf( // " Tempo infos: time signature %i/%i 1/4 note is %i ticks %i\n", - // , + // ((unsigned char *)evt->infos)[0], // ((unsigned char *)evt->infos)[1], // ((unsigned char *)evt->infos)[2], // ((unsigned char *)evt->infos)[3]