GameBoy (Color) port of the GTA San Andreas arcade game Duality
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

obj.c 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. * obj.c
  3. * Duality
  4. *
  5. * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * See <http://www.gnu.org/licenses/>.
  18. */
  19. #include <gbdk/platform.h>
  20. #include <stdint.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include "gb/hardware.h"
  24. #include "sprites.h"
  25. #include "game.h"
  26. #include "obj.h"
  27. /*
  28. * sprite budget: TODO
  29. *
  30. * fixed:
  31. * ship: 4 + 5
  32. * thruster: 1 - 1
  33. * health: 4
  34. * power: 4
  35. * --> 17 fixed
  36. *
  37. * hardware tiles: 40 - 13 = 27 - 4 = 23
  38. *
  39. * dynamic:
  40. * shot: 1
  41. * light: 4
  42. * dark: 4
  43. * --> 2x dark & 2x light = 16
  44. * --> 5x shot & 6x small = 11
  45. * --> 16 + 11 = 27
  46. */
  47. #define MAX_DARK 2
  48. #define MAX_LIGHT 2
  49. #define MAX_SHOT 5
  50. #define MAX_SHOT_DARK 3
  51. #define MAX_SHOT_LIGHT 3
  52. #define MAX_OBJ ((4 * MAX_DARK) + (4 * MAX_LIGHT) + MAX_SHOT + MAX_SHOT_DARK + MAX_SHOT_LIGHT)
  53. #define MAX_TRAVEL 128
  54. #define GRAVITY_RANGE (24 << POS_SCALE_OBJS)
  55. #define GRAVITY_SHIFT (POS_SCALE_OBJS + 4)
  56. #define DAMAGE_RANGE (14 << POS_SCALE_OBJS)
  57. #define DAMAGE_INC 5
  58. #define HEALTH_RANGE (12 << POS_SCALE_OBJS)
  59. #define HEALTH_INC HEALTH_MAX
  60. #define PICKUP_SMALL_RANGE (12 << POS_SCALE_OBJS)
  61. #define SHOT_RANGE (10 << POS_SCALE_OBJS)
  62. #define SCORE_SMALL 5
  63. #define SCORE_LARGE 10
  64. #define DESPAWN_RANGE (250 << POS_SCALE_OBJS)
  65. struct obj {
  66. uint8_t active;
  67. enum SPRITES sprite;
  68. int16_t off_x;
  69. int16_t off_y;
  70. int16_t spd_x;
  71. int16_t spd_y;
  72. uint8_t travel;
  73. };
  74. static struct obj objs[MAX_OBJ];
  75. void obj_init(void) NONBANKED {
  76. memset(objs, 0, sizeof(objs));
  77. }
  78. enum OBJ_STATE obj_add(enum SPRITES sprite, int16_t off_x, int16_t off_y, int16_t spd_x, int16_t spd_y) NONBANKED {
  79. uint8_t obj_cnt = 0xFF;
  80. for (uint8_t i = 0; i < MAX_OBJ; i++) {
  81. if (!objs[i].active) {
  82. obj_cnt = i;
  83. break;
  84. }
  85. }
  86. if (obj_cnt >= MAX_OBJ) {
  87. return OBJ_LIST_FULL;
  88. }
  89. objs[obj_cnt].active = 1;
  90. objs[obj_cnt].sprite = sprite;
  91. objs[obj_cnt].off_x = off_x << POS_SCALE_OBJS;
  92. objs[obj_cnt].off_y = off_y << POS_SCALE_OBJS;
  93. objs[obj_cnt].spd_x = spd_x;
  94. objs[obj_cnt].spd_y = spd_y;
  95. objs[obj_cnt].travel = 0;
  96. obj_cnt += 1;
  97. return OBJ_ADDED;
  98. }
  99. int16_t obj_do(int16_t *spd_off_x, int16_t *spd_off_y, int32_t *score, uint8_t *hiwater) NONBANKED {
  100. int16_t damage = 0;
  101. // initial speed
  102. int16_t spd_x = *spd_off_x;
  103. int16_t spd_y = *spd_off_y;
  104. for (uint8_t i = 0; i < MAX_OBJ; i++) {
  105. if (!objs[i].active) {
  106. continue;
  107. }
  108. // move objects by their speed and compensate for movement of the background / ship
  109. objs[i].off_x = objs[i].off_x + objs[i].spd_x - spd_x;
  110. objs[i].off_y = objs[i].off_y + objs[i].spd_y - spd_y;
  111. if (objs[i].off_x > POS_OBJS_MAX) {
  112. objs[i].off_x -= POS_OBJS_MAX - POS_OBJS_MIN + 1;
  113. } else if (objs[i].off_x < POS_OBJS_MIN) {
  114. objs[i].off_x += POS_OBJS_MAX - POS_OBJS_MIN + 1;
  115. }
  116. if (objs[i].off_y > POS_OBJS_MAX) {
  117. objs[i].off_y -= POS_OBJS_MAX - POS_OBJS_MIN + 1;
  118. } else if (objs[i].off_y < POS_OBJS_MIN) {
  119. objs[i].off_y += POS_OBJS_MAX - POS_OBJS_MIN + 1;
  120. }
  121. // only update travel time if we're actually moving
  122. if ((objs[i].spd_x != 0) || (objs[i].spd_y != 0)) {
  123. objs[i].travel += 1;
  124. }
  125. // remove objects that have traveled for too long
  126. if (objs[i].travel >= MAX_TRAVEL) {
  127. objs[i].active = 0;
  128. continue;
  129. }
  130. int abs_off_x = abs(objs[i].off_x);
  131. int abs_off_y = abs(objs[i].off_y);
  132. // handle collision
  133. switch (objs[i].sprite) {
  134. case SPR_DARK:
  135. if ((abs_off_x >= DESPAWN_RANGE) || (abs_off_y >= DESPAWN_RANGE)) {
  136. // TODO find new (random) position
  137. //objs[i].active = 0;
  138. }
  139. if ((abs_off_x <= GRAVITY_RANGE) && (abs_off_y <= GRAVITY_RANGE)) {
  140. if (objs[i].off_x > 0) {
  141. *spd_off_x += (GRAVITY_RANGE - objs[i].off_x) >> GRAVITY_SHIFT;
  142. } else if (objs[i].off_x < 0) {
  143. *spd_off_x += (-GRAVITY_RANGE - objs[i].off_x) >> GRAVITY_SHIFT;
  144. }
  145. if (objs[i].off_y > 0) {
  146. *spd_off_y += (GRAVITY_RANGE - objs[i].off_y) >> GRAVITY_SHIFT;
  147. } else if (objs[i].off_y < 0) {
  148. *spd_off_y += (-GRAVITY_RANGE - objs[i].off_y) >> GRAVITY_SHIFT;
  149. }
  150. }
  151. if ((abs_off_x <= DAMAGE_RANGE) && (abs_off_y <= DAMAGE_RANGE)) {
  152. damage += DAMAGE_INC;
  153. }
  154. break;
  155. case SPR_LIGHT:
  156. if ((abs_off_x >= DESPAWN_RANGE) || (abs_off_y >= DESPAWN_RANGE)) {
  157. // TODO find new (random) position
  158. //objs[i].active = 0;
  159. }
  160. if ((abs_off_x <= GRAVITY_RANGE) && (abs_off_y <= GRAVITY_RANGE)) {
  161. if (objs[i].off_x > 0) {
  162. *spd_off_x -= (GRAVITY_RANGE - objs[i].off_x) >> GRAVITY_SHIFT;
  163. } else if (objs[i].off_x < 0) {
  164. *spd_off_x -= (-GRAVITY_RANGE - objs[i].off_x) >> GRAVITY_SHIFT;
  165. }
  166. if (objs[i].off_y > 0) {
  167. *spd_off_y -= (GRAVITY_RANGE - objs[i].off_y) >> GRAVITY_SHIFT;
  168. } else if (objs[i].off_y < 0) {
  169. *spd_off_y -= (-GRAVITY_RANGE - objs[i].off_y) >> GRAVITY_SHIFT;
  170. }
  171. }
  172. if ((abs_off_x <= HEALTH_RANGE) && (abs_off_y <= HEALTH_RANGE)) {
  173. damage -= HEALTH_INC;
  174. }
  175. break;
  176. case SPR_SHOT_DARK:
  177. if ((abs_off_x >= DESPAWN_RANGE) || (abs_off_y >= DESPAWN_RANGE)) {
  178. // TODO find new (random) position
  179. //objs[i].active = 0;
  180. }
  181. if ((abs_off_x <= PICKUP_SMALL_RANGE) && (abs_off_y <= PICKUP_SMALL_RANGE)) {
  182. (*score) -= SCORE_SMALL;
  183. objs[i].active = 0;
  184. }
  185. break;
  186. case SPR_SHOT_LIGHT:
  187. if ((abs_off_x >= DESPAWN_RANGE) || (abs_off_y >= DESPAWN_RANGE)) {
  188. // TODO find new (random) position
  189. //objs[i].active = 0;
  190. }
  191. if ((abs_off_x <= PICKUP_SMALL_RANGE) && (abs_off_y <= PICKUP_SMALL_RANGE)) {
  192. (*score) += SCORE_SMALL;
  193. objs[i].active = 0;
  194. }
  195. break;
  196. case SPR_SHOT:
  197. for (uint8_t j = 0; j < MAX_OBJ; j++) {
  198. if ((!objs[j].active) || ((objs[j].sprite != SPR_LIGHT) && (objs[j].sprite != SPR_DARK))) {
  199. continue;
  200. }
  201. if ((abs(objs[i].off_x - objs[j].off_x) <= SHOT_RANGE)
  202. && (abs(objs[i].off_y - objs[j].off_y) <= SHOT_RANGE)) {
  203. objs[i].active = 0;
  204. objs[j].active = 0;
  205. if (objs[j].sprite == SPR_LIGHT) {
  206. (*score) += SCORE_LARGE;
  207. } else {
  208. (*score) -= SCORE_LARGE;
  209. }
  210. break;
  211. }
  212. }
  213. break;
  214. default:
  215. break;
  216. }
  217. spr_draw(objs[i].sprite, FLIP_NONE, objs[i].off_x >> POS_SCALE_OBJS, objs[i].off_y >> POS_SCALE_OBJS, 0, hiwater);
  218. }
  219. return damage;
  220. }