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 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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:
  29. *
  30. * fixed:
  31. * ship: 4
  32. * thruster: 1
  33. * health: 4
  34. * power: 4
  35. * --> 13 fixed
  36. *
  37. * hardware tiles: 40 - 13 = 27
  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 32
  54. #define GRAVITY_RANGE (32 << POS_SCALE_OBJS)
  55. #define GRAVITY_SHIFT (POS_SCALE_OBJS + 4)
  56. #define DAMAGE_RANGE (16 << POS_SCALE_OBJS)
  57. #define DAMAGE_INC 3
  58. struct obj {
  59. uint8_t active;
  60. enum SPRITES sprite;
  61. int16_t off_x;
  62. int16_t off_y;
  63. int16_t spd_x;
  64. int16_t spd_y;
  65. uint8_t travel;
  66. };
  67. static struct obj objs[MAX_OBJ];
  68. void obj_init(void) {
  69. memset(objs, 0, sizeof(objs));
  70. }
  71. static uint8_t cnt_sprite(enum SPRITES sprite) {
  72. uint8_t cnt = 0;
  73. for (uint8_t i = 0; i < MAX_OBJ; i++) {
  74. if (!objs[i].active) {
  75. continue;
  76. }
  77. if (objs[i].sprite == sprite) {
  78. cnt++;
  79. }
  80. }
  81. return cnt;
  82. }
  83. enum OBJ_STATE obj_add(enum SPRITES sprite, int16_t off_x, int16_t off_y, int16_t spd_x, int16_t spd_y) {
  84. uint8_t obj_cnt = 0xFF;
  85. for (uint8_t i = 0; i < MAX_OBJ; i++) {
  86. if (!objs[i].active) {
  87. obj_cnt = i;
  88. break;
  89. }
  90. }
  91. if (obj_cnt >= MAX_OBJ) {
  92. return OBJ_LIST_FULL;
  93. }
  94. if (((sprite == SPR_DARK) && (cnt_sprite(sprite) >= MAX_DARK))
  95. || ((sprite == SPR_LIGHT) && (cnt_sprite(sprite) >= MAX_LIGHT))
  96. || ((sprite == SPR_SHOT) && (cnt_sprite(sprite) >= MAX_SHOT))
  97. || ((sprite == SPR_SHOT_DARK) && (cnt_sprite(sprite) >= MAX_SHOT_DARK))
  98. || ((sprite == SPR_SHOT_LIGHT) && (cnt_sprite(sprite) >= MAX_SHOT_LIGHT))) {
  99. return OBJ_TYPE_FULL;
  100. }
  101. objs[obj_cnt].active = 1;
  102. objs[obj_cnt].sprite = sprite;
  103. objs[obj_cnt].off_x = off_x << POS_SCALE_OBJS;
  104. objs[obj_cnt].off_y = off_y << POS_SCALE_OBJS;
  105. objs[obj_cnt].spd_x = spd_x;
  106. objs[obj_cnt].spd_y = spd_y;
  107. objs[obj_cnt].travel = 0;
  108. obj_cnt += 1;
  109. return OBJ_ADDED;
  110. }
  111. uint8_t obj_act(int16_t *spd_off_x, int16_t *spd_off_y) {
  112. uint8_t damage = 0;
  113. for (uint8_t i = 0; i < MAX_OBJ; i++) {
  114. if (!objs[i].active) {
  115. continue;
  116. }
  117. switch (objs[i].sprite) {
  118. case SPR_DARK: {
  119. if ((abs(objs[i].off_x) <= GRAVITY_RANGE) && (abs(objs[i].off_y) <= GRAVITY_RANGE)) {
  120. if (objs[i].off_x > 0) {
  121. *spd_off_x += (GRAVITY_RANGE - objs[i].off_x) >> GRAVITY_SHIFT;
  122. } else if (objs[i].off_x < 0) {
  123. *spd_off_x += (-GRAVITY_RANGE - objs[i].off_x) >> GRAVITY_SHIFT;
  124. }
  125. if (objs[i].off_y > 0) {
  126. *spd_off_y += (GRAVITY_RANGE - objs[i].off_y) >> GRAVITY_SHIFT;
  127. } else if (objs[i].off_y < 0) {
  128. *spd_off_y += (-GRAVITY_RANGE - objs[i].off_y) >> GRAVITY_SHIFT;
  129. }
  130. }
  131. if ((abs(objs[i].off_x) <= DAMAGE_RANGE) && (abs(objs[i].off_y) <= DAMAGE_RANGE)) {
  132. damage += DAMAGE_INC;
  133. }
  134. } break;
  135. default:
  136. break;
  137. }
  138. }
  139. return damage;
  140. }
  141. void obj_draw(int16_t spd_x, int16_t spd_y, uint8_t *hiwater) {
  142. for (uint8_t i = 0; i < MAX_OBJ; i++) {
  143. if (!objs[i].active) {
  144. continue;
  145. }
  146. // move objects by their speed and compensate for movement of the background / ship
  147. objs[i].off_x += objs[i].spd_x - spd_x;
  148. objs[i].off_y += objs[i].spd_y - spd_y;
  149. // only update travel time if we're actually moving
  150. if ((objs[i].spd_x != 0) || (objs[i].spd_y != 0)) {
  151. objs[i].travel += 1;
  152. }
  153. // remove objects that have traveled for too long
  154. if (objs[i].travel >= MAX_TRAVEL) {
  155. objs[i].active = 0;
  156. }
  157. spr_draw(objs[i].sprite, FLIP_NONE, objs[i].off_x >> POS_SCALE_OBJS, objs[i].off_y >> POS_SCALE_OBJS, hiwater);
  158. }
  159. }