|
| 1 | + |
| 2 | +#include "nx.h" |
| 3 | +#include "common/llist.h" |
| 4 | +#include "ObjManager.h" |
| 5 | +#include "ObjManager.fdh" |
| 6 | + |
| 7 | +static Object ZERO_OBJECT; |
| 8 | +static Player ZERO_PLAYER; |
| 9 | + |
| 10 | +Object *firstobject = NULL, *lastobject = NULL; |
| 11 | +Object *lowestobject = NULL, *highestobject = NULL; |
| 12 | + |
| 13 | +/* |
| 14 | +void c------------------------------() {} |
| 15 | +*/ |
| 16 | + |
| 17 | +Object *CreateObject(int x, int y, int type, int xinertia, int yinertia, |
| 18 | + int dir, Object *linkedobject, uint32_t createflags) |
| 19 | +{ |
| 20 | +Object *o; |
| 21 | + |
| 22 | + // create the structure |
| 23 | + if (type != OBJ_PLAYER) |
| 24 | + { |
| 25 | + o = new Object; |
| 26 | + *o = ZERO_OBJECT; // safely clears all members |
| 27 | + } |
| 28 | + else |
| 29 | + { |
| 30 | + Player *p = new Player; |
| 31 | + *p = ZERO_PLAYER; |
| 32 | + o = (Object *)p; |
| 33 | + } |
| 34 | + |
| 35 | + // initialize |
| 36 | + o->SetType(type); |
| 37 | + o->flags = objprop[type].defaultflags; |
| 38 | + o->DamageText = new FloatText(SPR_REDNUMBERS); |
| 39 | + |
| 40 | + o->x = x - (sprites[o->sprite].spawn_point.x << CSF); |
| 41 | + o->y = y - (sprites[o->sprite].spawn_point.y << CSF); |
| 42 | + o->dir = dir; |
| 43 | + o->xinertia = xinertia; |
| 44 | + o->yinertia = yinertia; |
| 45 | + o->linkedobject = linkedobject; |
| 46 | + |
| 47 | + // add into list |
| 48 | + LL_ADD_END(o, prev, next, firstobject, lastobject); |
| 49 | + LL_ADD_END(o, lower, higher, lowestobject, highestobject); |
| 50 | + |
| 51 | + // set it's initial blocked states, but do not update blockedstates on objects starting |
| 52 | + // with nullsprite-- the reason is for objects whose sprite is set after being spawned |
| 53 | + if (o->sprite != SPR_NULL) |
| 54 | + o->UpdateBlockStates(ALLDIRMASK); |
| 55 | + |
| 56 | + if (!(createflags & CF_NO_SPAWN_EVENT)) |
| 57 | + o->OnSpawn(); |
| 58 | + |
| 59 | + return o; |
| 60 | +} |
| 61 | + |
| 62 | +Object *CreateObject(int x, int y, int type) |
| 63 | +{ |
| 64 | + return CreateObject(x, y, type, 0, 0, RIGHT, NULL, CF_DEFAULT); |
| 65 | +} |
| 66 | + |
| 67 | +/* |
| 68 | +void c------------------------------() {} |
| 69 | +*/ |
| 70 | + |
| 71 | +// update the blocked states of all objects |
| 72 | +void Objects::UpdateBlockStates(void) |
| 73 | +{ |
| 74 | + Object *o = firstobject; |
| 75 | + while(o) |
| 76 | + { |
| 77 | + o->lastblockl = o->blockl; |
| 78 | + o->lastblockr = o->blockr; |
| 79 | + o->lastblocku = o->blocku; |
| 80 | + o->lastblockd = o->blockd; |
| 81 | + |
| 82 | + o->UpdateBlockStates(ALLDIRMASK); |
| 83 | + o = o->next; |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +// returns true if the bounding boxes of the two given objects are touching |
| 88 | +bool hitdetect(Object *o1, Object *o2) |
| 89 | +{ |
| 90 | +SIFSprite *s1, *s2; |
| 91 | +int32_t rect1x1, rect1y1, rect1x2, rect1y2; |
| 92 | +int32_t rect2x1, rect2y1, rect2x2, rect2y2; |
| 93 | + |
| 94 | + // get the sprites used by the two objects |
| 95 | + s1 = o1->Sprite(); |
| 96 | + s2 = o2->Sprite(); |
| 97 | + |
| 98 | + // get the bounding rectangle of the first object |
| 99 | + rect1x1 = o1->x + (s1->bbox.x1 << CSF); |
| 100 | + rect1x2 = o1->x + (s1->bbox.x2 << CSF); |
| 101 | + rect1y1 = o1->y + (s1->bbox.y1 << CSF); |
| 102 | + rect1y2 = o1->y + (s1->bbox.y2 << CSF); |
| 103 | + |
| 104 | + // get the bounding rectangle of the second object |
| 105 | + rect2x1 = o2->x + (s2->bbox.x1 << CSF); |
| 106 | + rect2x2 = o2->x + (s2->bbox.x2 << CSF); |
| 107 | + rect2y1 = o2->y + (s2->bbox.y1 << CSF); |
| 108 | + rect2y2 = o2->y + (s2->bbox.y2 << CSF); |
| 109 | + |
| 110 | + // find out if the rectangles overlap |
| 111 | + if ((rect1x1 < rect2x1) && (rect1x2 < rect2x1)) return false; |
| 112 | + if ((rect1x1 > rect2x2) && (rect1x2 > rect2x2)) return false; |
| 113 | + if ((rect1y1 < rect2y1) && (rect1y2 < rect2y1)) return false; |
| 114 | + if ((rect1y1 > rect2y2) && (rect1y2 > rect2y2)) return false; |
| 115 | + |
| 116 | + return true; |
| 117 | +} |
| 118 | + |
| 119 | +// returns true if the solidity boxes of the two given objects are touching |
| 120 | +bool solidhitdetect(Object *o1, Object *o2) |
| 121 | +{ |
| 122 | +SIFSprite *s1, *s2; |
| 123 | +int32_t rect1x1, rect1y1, rect1x2, rect1y2; |
| 124 | +int32_t rect2x1, rect2y1, rect2x2, rect2y2; |
| 125 | + |
| 126 | + // get the sprites used by the two objects |
| 127 | + s1 = o1->Sprite(); |
| 128 | + s2 = o2->Sprite(); |
| 129 | + |
| 130 | + // get the bounding rectangle of the first object |
| 131 | + rect1x1 = o1->x + (s1->solidbox.x1 << CSF); |
| 132 | + rect1x2 = o1->x + (s1->solidbox.x2 << CSF); |
| 133 | + rect1y1 = o1->y + (s1->solidbox.y1 << CSF); |
| 134 | + rect1y2 = o1->y + (s1->solidbox.y2 << CSF); |
| 135 | + |
| 136 | + // get the bounding rectangle of the second object |
| 137 | + rect2x1 = o2->x + (s2->solidbox.x1 << CSF); |
| 138 | + rect2x2 = o2->x + (s2->solidbox.x2 << CSF); |
| 139 | + rect2y1 = o2->y + (s2->solidbox.y1 << CSF); |
| 140 | + rect2y2 = o2->y + (s2->solidbox.y2 << CSF); |
| 141 | + |
| 142 | + // find out if the rectangles overlap |
| 143 | + if ((rect1x1 < rect2x1) && (rect1x2 < rect2x1)) return false; |
| 144 | + if ((rect1x1 > rect2x2) && (rect1x2 > rect2x2)) return false; |
| 145 | + if ((rect1y1 < rect2y1) && (rect1y2 < rect2y1)) return false; |
| 146 | + if ((rect1y1 > rect2y2) && (rect1y2 > rect2y2)) return false; |
| 147 | + |
| 148 | + return true; |
| 149 | +} |
| 150 | + |
| 151 | +/* |
| 152 | +void c------------------------------() {} |
| 153 | +*/ |
| 154 | + |
| 155 | +// runs all entity AI routines |
| 156 | +void Objects::RunAI(void) |
| 157 | +{ |
| 158 | +Object *o; |
| 159 | + |
| 160 | + // because we handle objects in order of their creation and have a separate list |
| 161 | + // for display order, we can't ever run AI twice in a frame because of z-order |
| 162 | + // rearrangement, and 2) objects created by other objects are added to the end of |
| 163 | + // the list and given a chance to run their AI routine before being displayed. |
| 164 | + FOREACH_OBJECT(o) |
| 165 | + { |
| 166 | + if (!o->deleted) |
| 167 | + o->RunAI(); |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | + |
| 172 | +// the most important thing it does is apply x/y inertia to the objects. |
| 173 | +void Objects::PhysicsSim(void) |
| 174 | +{ |
| 175 | +Object *o; |
| 176 | +int xinertia, yinertia; |
| 177 | + |
| 178 | + FOREACH_OBJECT(o) |
| 179 | + { |
| 180 | + if (o != player && !o->deleted) // player is moved in PDoPhysics |
| 181 | + { |
| 182 | + if (!(o->flags & FLAG_IGNORE_SOLID) && \ |
| 183 | + !(o->nxflags & NXFLAG_NO_RESET_YINERTIA)) |
| 184 | + { |
| 185 | + if (o->blockd && o->yinertia > 0) o->yinertia = 0; |
| 186 | + if (o->blocku && o->yinertia < 0) o->yinertia = 0; |
| 187 | + } |
| 188 | + |
| 189 | + // apply inertia to X,Y position |
| 190 | + xinertia = o->xinertia; |
| 191 | + yinertia = o->yinertia; |
| 192 | + if (o->shaketime) |
| 193 | + { |
| 194 | + if (o->nxflags & NXFLAG_SLOW_X_WHEN_HURT) xinertia >>= 1; |
| 195 | + if (o->nxflags & NXFLAG_SLOW_Y_WHEN_HURT) yinertia >>= 1; |
| 196 | + } |
| 197 | + |
| 198 | + o->apply_xinertia(xinertia); |
| 199 | + o->apply_yinertia(yinertia); |
| 200 | + |
| 201 | + // flag_solid_brick objects push player as they move |
| 202 | + if (o->flags & FLAG_SOLID_BRICK) |
| 203 | + { |
| 204 | + o->PushPlayerOutOfWay(xinertia, yinertia); |
| 205 | + } |
| 206 | + else if (o->damage > 0) |
| 207 | + { |
| 208 | + // have enemies hurt you when you touch them |
| 209 | + // (solid-brick objects do this in PHandleSolidBrickObjects) |
| 210 | + if (hitdetect(o, player)) |
| 211 | + o->DealContactDamage(); |
| 212 | + } |
| 213 | + } |
| 214 | + } |
| 215 | +} |
| 216 | + |
| 217 | +/* |
| 218 | +void c------------------------------() {} |
| 219 | +*/ |
| 220 | + |
| 221 | +// returns how many objects exist of the given type |
| 222 | +int Objects::CountType(int objtype) |
| 223 | +{ |
| 224 | + int count = 0; |
| 225 | + Object *o; |
| 226 | + |
| 227 | + FOREACH_OBJECT(o) |
| 228 | + { |
| 229 | + if (o->type == objtype) |
| 230 | + count++; |
| 231 | + } |
| 232 | + |
| 233 | + return count; |
| 234 | +} |
| 235 | + |
| 236 | +// returns the first object of type objtype or NULL |
| 237 | +Object *Objects::FindByType(int objtype) |
| 238 | +{ |
| 239 | + Object *o; |
| 240 | + FOREACH_OBJECT(o) |
| 241 | + { |
| 242 | + if (o->type == objtype) |
| 243 | + return o; |
| 244 | + } |
| 245 | + |
| 246 | + return NULL; |
| 247 | +} |
| 248 | + |
| 249 | +/* |
| 250 | +void c------------------------------() {} |
| 251 | +*/ |
| 252 | + |
| 253 | +// free objects deleted earlier via ObjDel |
| 254 | +void Objects::CullDeleted(void) |
| 255 | +{ |
| 256 | +Object *o, *next; |
| 257 | + |
| 258 | + o = firstobject; |
| 259 | + while(o) |
| 260 | + { |
| 261 | + next = o->next; |
| 262 | + |
| 263 | + if (o->deleted) |
| 264 | + { |
| 265 | + o->Destroy(); |
| 266 | + } |
| 267 | + |
| 268 | + o = next; |
| 269 | + } |
| 270 | +} |
| 271 | + |
| 272 | +// deletes all objects. if delete_player is true, also deletes the player. |
| 273 | +// used by load_pxe to reset the game in preperation for loading a new maplayer-> |
| 274 | +void Objects::DestroyAll(bool delete_player) |
| 275 | +{ |
| 276 | +Object *o, *next; |
| 277 | + |
| 278 | + o = firstobject; |
| 279 | + while(o) |
| 280 | + { |
| 281 | + next = o->next; |
| 282 | + |
| 283 | + if (o != player) |
| 284 | + { |
| 285 | + o->Destroy(); |
| 286 | + } |
| 287 | + |
| 288 | + o = next; |
| 289 | + } |
| 290 | + |
| 291 | + // must do this last to avoid crashes as player ptr gets invalidated |
| 292 | + if (delete_player) |
| 293 | + { |
| 294 | + player->Destroy(); |
| 295 | + } |
| 296 | + |
| 297 | + memset(ID2Lookup, 0, sizeof(ID2Lookup)); |
| 298 | +} |
| 299 | + |
| 300 | + |
0 commit comments