#include "eruta.h" #include "area.h" #include "tilemap.h" #include "bump.h" #include "thing.h" #include "mem.h" #include "flags.h" #include "draw.h" #include "sprite.h" #include "callrb.h" #include "monolog.h" #include "thing_struct.h" /** Scale factor. */ #define AREA_SCALE_BY (32.0lf) /** Scales int by scale factor */ #define AREA_SCALE_INT(I) ((double)((INT) I)>>5)) /* An area is an in game region that corresponds with a single loaded tile map and that keeps track of the physics and location of all in-game objects. An area consists of a chipmunk cpSpace that simulates the dynamics, and a list of Things, that is, in game objects. It also contains a reference to a SpriteList (that is owned by state). Division of the data locations: visual and physics engine: in C. Logic/game/character data: in scripting engine. */ /* Start with space for 20000 things. */ #define AREA_THINGS 20000 struct Area_ { BumpWorld * world; int lastid; Thing * things[AREA_THINGS]; Thing * things_todraw[AREA_THINGS]; BumpTilemap * bumpmap; int draw_physics; }; /** Gets the amount of possible things for this area */ int area_maxthings(Area * area) { (void) area; return AREA_THINGS; } /** Gets the thing for the given thing ID. */ Thing * area_thing(Area * area, int index) { if (index < 0) return NULL; if (index > AREA_THINGS) return NULL; if (!area) return NULL; return area->things[index]; } /** Sets the thing for the given thing ID. */ Thing * area_thing_(Area * area, int index, Thing * set) { if (index < 0) return NULL; if (index > AREA_THINGS) return NULL; if (!area) return NULL; return area->things[index] = set; } /** Returns the first unused Thing ID. Internally walks over * the known things and finds an empty cell. Returns negative on error. */ int area_get_unused_thing_id(Area * self) { int index, stop; if (!self) return -1; stop = area_maxthings(self); for (index = 0; index < stop; index++) { Thing * thing = area_thing(self, index); if(!thing) { return index; } } return -3; } /** Puts a thing inside the area. Returns NULL on error. returns the thing set if ok. Sets the thing's id to it's position in the internal array for things the area has. */ Thing * area_add_thing(Area * area, int index, Thing * thing) { if (!thing) return NULL; if (index < 0 ) return NULL; if (index > AREA_THINGS) return NULL; if(!area_thing_(area, index, thing)) return NULL; thing_id_(thing, index); /* Set last used id to index if needed */ if (area->lastid < index) { area->lastid = index; } /* Don't forget to add physical body and hull to the space if needed. */ bumpworld_add_body(area->world, thing->physical); bumpworld_add_hull(area->world, thing->hull); /* Fix up drawing array. */ area_update_things(area); return thing; } /** Set the tile map to use for this area. */ Tilemap * area_addmap(Area * area, Tilemap * map) { return bumpworld_tilemap_(area->world, map); } /** Returns the static body that this area uses for static things. * Removed, since not needed because bump uses real tile maps and not static bodies. */ /* Removes the thing from the area, but does not deallocate it. * Returns thing if sucessful. */ Thing * area_remove_thing(Area * area, Thing * thing) { if (!thing) return NULL; if (thing->id<0) return NULL; if (thing->kind == THING_UNUSED) return NULL; bumpworld_remove_hull(area->world, thing->hull); bumpworld_remove_body_only(area->world, thing->physical); bumpworld_delete_hulls_for(area->world, thing->physical); area_thing_(area, thing->id, NULL); /* Fix up drawing array. */ area_update_things(area); return thing; } /* Removes a thing from the area, selected by ID and deallocates it. */ int area_delete_thing(Area * area, int index) { Thing * thing = area_thing(area, index); Thing * del = area_remove_thing(area, thing); if(thing && del) { thing_free(thing); } return 0; } /* Cleans up the things of the area */ Area * area_cleanupthings(Area * self) { int index, stop; if(!self) return NULL; stop = area_maxthings(self); for (index= 0; index < stop; index++) { area_delete_thing(self, index); } return self; } /* Empties the things of an area. Does not destroy them! */ Area * area_emptythings(Area * self) { int index, stop; if (!self) return NULL; stop = area_maxthings(self); for (index= 0; index < stop; index++) { area_thing_(self, index, NULL); } return self; } /** Deinitializes an area and returns self. */ Area * area_done(Area * self) { if(!self) return self; if(!self->world) return self; area_cleanupthings(self); bumpworld_free(self->world); self->world = NULL; self->draw_physics = FALSE; return self; } /** Frees an area. Returns NULL. */ Area * area_free(Area * self) { if(area_done(self)) { mem_free(self); } return NULL; } /** Allocates an area. */ Area * area_alloc() { return STRUCT_ALLOC(Area); } /** Initializes an area. */ Area * area_init(Area * self) { if(!self) return NULL; self->lastid = 0; self->world = bumpworld_new(); if (!self->world) goto out_of_memory; area_emptythings(self); self->bumpmap = NULL; self->draw_physics = FALSE; // area_setup_default_collision_handlers(self, self); return self; out_of_memory: area_done(self); return NULL; } /** Updates the things in the area, must be called whan adding or deleting a thing too. */ void area_update_things(Area * self) { int index; int subindex = 0; /* Empty the draw queue to avoid stale references after deleting a thing * or it's physical body. */ for(index = 0; index < AREA_THINGS; index++) { self->things_todraw[index] = NULL; } for(index = 0; index < AREA_THINGS; index++) { Thing * thing = area_thing(self, index); if (thing && thing->physical) { self->things_todraw[subindex] = thing; subindex++; } } /** Sort drawing array so the painter's algorihm can be used. */ qsort(self->things_todraw, subindex, sizeof(Thing*), thing_compare_for_drawing); } /** Sets the area so it will draw its physics * bounds boxes when it is drawn or not. */ void area_draw_physics_(Area * self, int draw) { self->draw_physics = draw; } /** Checks if the area draws its physics or not. */ int area_draw_physics(Area * self) { return self->draw_physics; } /** Makes a new area. */ Area * area_new() { return area_init(area_alloc()); } /** Returns the BumpWorld that the area uses for dynamics modelling. */ BumpWorld * area_world(Area * self) { if (!self) return NULL; return self->world; } /** Makes a new thing and adds it to the area. Return it or NULL on error. */ Thing * area_new_thing(Area * self, int kind, int x, int y, int z, int w, int h) { Thing * thing; int index; index = area_get_unused_thing_id(self); thing = thing_new_dynamic(self, index, kind, x, y, z, w, h); if (!area_add_thing(self, index, thing)) { return thing_free(thing); } return thing; } /** Makes a new thing and returns it's ID. */ int area_new_thing_id(Area * self, int kind, int x, int y, int z, int w, int h) { Thing * thing; thing = area_new_thing(self, kind, x, y, z, w, h); return thing_id(thing); } /** Sets the current tile map of the area. */ ERES area_tilemap_(Area * self, Tilemap * map) { int wide, high; if (map) { wide = tilemap_gridwide(map); high = tilemap_gridhigh(map); } else { /* No map so this is whatever, I think. */ wide = 640; high = 480; } bumpworld_tilemap_(self->world, map); return ERES_OK; } /** Draws all things in an area taking the camera into account. */ void area_draw(Area * self, Camera * camera) { int index; for(index = 0; index < (self->lastid + 1); index++) { Thing * thing = self->things_todraw[index]; /* If we encounter a NULL, we're all done drawing. */ if(!thing) break; thing_draw(thing, camera); } #ifdef ERUTA_DEBUG_DRAW bumpworld_draw_debug(self->world); #endif } /** Draws all things in an area taking the camera and layer into account. */ void area_draw_layer (Area * self, Camera * camera, int layer) { int index; for (index = 0; index < (self->lastid + 1); index++) { Thing * thing = self->things_todraw[index]; /* If we encounter a NULL, we're all done drawing. */ if(!thing) break; /* Only draw current layer. */ if(thing_z(thing) != layer) { continue; } thing_draw(thing, camera); } if (self->draw_physics) { bumpworld_draw_debug(self->world); } } /** Updates the area */ void area_update(Area * self, double dt) { int subindex = 0; int index; bumpworld_update(self->world, dt); area_update_things(self); for(index = 0; index < (self->lastid + 1); index++) { Thing * thing = area_thing(self, index); /* this is a bandaid, somehow uninitialized things are stored in the area. */ if (thing && thing->physical) { thing_update(thing, dt); } else if (thing && (thing->id > AREA_THINGS)) { LOG_WARNING("Corrupted thing detected! %p", thing); } } } /* Helper for callback wrapper. */ struct area_find_hulls_helper { int (* callback)(Thing * thing, void * extra); void * extra; }; /* Callback wrapper. */ static int area_find_hulls_callback(BumpHull * hull, void * extra) { struct area_find_hulls_helper * helper = extra; Thing * thing = bumphull_thing(hull); if (thing) { return helper->callback(thing, helper->extra); } return 0; } /** Finds all things in a given rectangle and calls the callback for each of them. * Returns 0. If the callback returns nonzero returns this in stead immediately. */ int area_find_things(Area * self, int x, int y, int w, int h, void * extra, int callback(Thing * thing, void * extra)) { struct area_find_hulls_helper helper; BumpAABB bounds = bumpaabb_make_int(x, y, w, h); helper.callback = callback; helper.extra = extra; return bumpworld_find_hulls(self->world, bounds, &helper, area_find_hulls_callback); } /* Avoid allocation in the function below. */ static BumpHull * area_hulls[AREA_THINGS]; /** Finds all things in a given rectangle and returns * up to max_things of them in the array things. * Returns the amount of things found or negative on error. */ int area_find_and_fetch_things(Area * self, int x, int w, int y, int h, Thing ** things, int max_things) { int index, result; BumpAABB bounds = bumpaabb_make_int(x, y, w, h); int amount = bumpworld_find_and_fetch_hulls(self->world, bounds, area_hulls, AREA_THINGS); int stop = amount; if (stop < max_things) stop = max_things; result = 0; for (index = 0; index < stop; index ++) { Thing * thing = bumphull_thing(area_hulls[index]); if (thing) { things[result] = thing; result++; } } return result; } /** Gets the thing at index index and adds a new hull to it */ BumpHull * area_add_hull(Area * self, int index, int kind, int x, int y, int z, int w, int h) { BumpHull * hull; Thing * thing; BumpAABB aabb = bumpaabb_make_int(x, y, w, h); thing = area_thing(self, index); return bumpworld_new_hull(self->world, thing->physical, bevec(x,y), aabb, z, kind); } /* Removes a hull from the area and also from the body it was attached * to. */ Area * area_delete_hull(Area * self, int index) { if (bumpworld_delete_hull_index(self->world, index)) return self; return NULL; } /* Removes a body from the area and also all hulls attached to it from the area. */ Area * area_delete_body(Area * self, int index) { if(bumpworld_delete_body_index(self->world, index)) return self; return NULL; } /** Sets a flag on the main hull of the Thing indicated by index. */ int area_set_thing_hull_flag(Area * me, int index, int flag) { Thing * thing; if (!me) return 0; thing = area_thing(me, index); return thing_set_hull_flag(thing, flag); } /** Unsets a flag for the main hull of the thing. */ int area_unset_thing_hull_flag(Area * me, int index, int flag) { Thing * thing; if (!me) return 0; thing = area_thing(me, index); return thing_unset_hull_flag(thing, flag); } /** Sets all flags for the main hull of the thing. */ int area_thing_hull_flags_(Area * me, int index, int flags) { Thing * thing; if (!me) return 0; thing = area_thing(me, index); return thing_hull_flags_(thing, flags); } /** Gets all flags for the main hull of the thing. */ int area_thing_hull_flags(Area * me, int index) { Thing * thing; if (!me) return 0; thing = area_thing(me, index); return thing_hull_flags(thing); } /** Sets a flag on the hull indicated by index. */ int area_set_hull_flag(Area * me, int index, int flag) { if (!me) return 0; return bumpworld_set_hull_flag(me->world, index, flag); } /** Unsets a flag for the hull indicated by index. */ int area_unset_hull_flag(Area * me, int index, int flag) { if (!me) return 0; return bumpworld_unset_hull_flag(me->world, index, flag); } /** Sets all flags for the hull indicated by index. */ int area_hull_flags_(Area * me, int index, int flags) { if (!me) return 0; return bumpworld_hull_flags_(me->world, index, flags); } /** Gets all flags for the hull indicated by index. */ int area_hull_flags(Area * me, int index) { if (!me) return 0; return bumpworld_hull_flags(me->world, index); } int area_thing_hull_group(Area * me , int index) { if (!me) return -1; return thing_hull_group(area_thing(me, index)); } int area_thing_hull_group_(Area * me, int index, int group) { if (!me) return -1; return thing_hull_group_(area_thing(me, index), group); } /** Returns the thing related to the hull's body, if any. * This is in area.c for dependency reasons. */ Thing * bumphull_thing(BumpHull * hull) { return bumphull_body_data(hull); } #define THING_TRACK_DELTA 32.0 * 4 #ifdef COMMENT_ /** A tracker function for tracking a Thing. Only works with dynamic things. */ int thing_track(Tracker * tracker, void * data) { Thing * thing = NULL; if(!tracker || !tracker->camera) return TRACKER_ERROR; thing = (Thing *) tracker->target; if(thing_static_p(thing)) return TRACKER_ERROR; // TODO: correct with half width and half height camera_centerdelta_(tracker->camera, thing_p(thing), THING_TRACK_DELTA); return TRACKER_DONE; } #endif