area.c (9316B)
1 #include "engine/area.h" 2 3 #include <math.h> 4 #include <stdlib.h> 5 #include <string.h> 6 7 const PhAreaDef * 8 ph_area_catalog_area(const PhAreaCatalog *catalog, int area_id) 9 { 10 int i; 11 12 for (i = 0; i < catalog->area_count; ++i) 13 if (catalog->areas[i].id == area_id) return &catalog->areas[i]; 14 return NULL; 15 } 16 17 const PhMarkerDef * 18 ph_area_catalog_marker(const PhAreaCatalog *catalog, int area_id, 19 unsigned char symbol) 20 { 21 int i; 22 23 for (i = 0; i < catalog->marker_count; ++i) 24 if (catalog->markers[i].area_id == area_id && 25 catalog->markers[i].symbol == symbol) return &catalog->markers[i]; 26 return NULL; 27 } 28 29 int 30 ph_area_catalog_load(PhArea *area, const PhAreaCatalog *catalog, int area_id) 31 { 32 const PhAreaDef *def = ph_area_catalog_area(catalog, area_id); 33 34 if (!def || ph_area_load(area, def->path) < 0) return -1; 35 area->tile_defs = catalog->tiles; 36 area->tile_def_count = catalog->tile_count; 37 return 0; 38 } 39 40 int 41 ph_world_start(PhWorld *world, const PhWorldSetup *setup) 42 { 43 PhArea area; 44 int player = -1; 45 int i; 46 47 if (!setup || !setup->areas || setup->equipment_count < 0 || 48 (setup->equipment_count && !setup->equipment) || 49 ph_area_catalog_load(&area, setup->areas, setup->start_area_id) < 0) 50 return -1; 51 if (ph_world_init(world, area, setup->start_area_id, setup->content) < 0) { 52 ph_area_free(&area); 53 return -1; 54 } 55 if (ph_world_prepare_area(world, &world->area, setup->areas, 56 setup->start_area_id, &player) < 0 || player < 0) goto fail; 57 for (i = 0; i < setup->equipment_count; ++i) 58 if (setup->equipment[i] && 59 ph_world_equip_item(world, player, setup->equipment[i]) < 0) 60 goto fail; 61 ph_world_set_player(world, player); 62 return 0; 63 64 fail: 65 ph_area_free(&world->area); 66 return -1; 67 } 68 69 static PhVec2 70 ph_marker_pos(const PhAreaMarker *marker) 71 { 72 return (PhVec2){ PH_TILE_CENTER(marker->tile_x), 73 PH_TILE_CENTER(marker->tile_y) }; 74 } 75 76 static int 77 ph_world_add_area_content(PhWorld *world, const PhArea *area, 78 const PhAreaCatalog *catalog, int area_id, int *player) 79 { 80 int i; 81 82 for (i = 0; i < area->marker_count; ++i) { 83 const PhAreaMarker *marker = &area->markers[i]; 84 const PhMarkerDef *def = ph_area_catalog_marker(catalog, area_id, 85 marker->symbol); 86 int entity; 87 88 if (!def) return -1; 89 if (def->kind == PH_MARKER_ITEM) { 90 if (ph_world_drop_item(world, def->content_id, ph_marker_pos(marker), 91 def->amount, area_id) < 0) return -1; 92 continue; 93 } 94 if (def->kind != PH_MARKER_ENTITY && def->kind != PH_MARKER_PLAYER) 95 continue; 96 if (def->kind == PH_MARKER_PLAYER && *player >= 0) return -1; 97 entity = ph_world_spawn_entity(world, def->content_id, 98 ph_marker_pos(marker), def->territory_radius, area_id); 99 if (entity < 0) return -1; 100 if (def->kind == PH_MARKER_PLAYER) *player = entity; 101 } 102 return 0; 103 } 104 105 static PhAreaState * 106 ph_world_area_state(PhWorld *world, int area_id) 107 { 108 int low = 0; 109 int high = world->initialized_area_count; 110 111 while (low < high) { 112 int middle = low + (high - low) / 2; 113 if (world->area_states[middle].area_id < area_id) low = middle + 1; 114 else high = middle; 115 } 116 if (low < world->initialized_area_count && 117 world->area_states[low].area_id == area_id) return &world->area_states[low]; 118 return NULL; 119 } 120 121 static PhAreaState * 122 ph_world_add_area_state(PhWorld *world, int area_id) 123 { 124 PhAreaState *states; 125 int capacity; 126 int position = 0; 127 128 if (ph_world_area_state(world, area_id)) return NULL; 129 if (world->initialized_area_count >= world->area_state_capacity) { 130 capacity = world->area_state_capacity ? world->area_state_capacity * 2 : 8; 131 states = realloc(world->area_states, (size_t)capacity * sizeof(*states)); 132 if (!states) return NULL; 133 world->area_states = states; 134 world->area_state_capacity = capacity; 135 } 136 while (position < world->initialized_area_count && 137 world->area_states[position].area_id < area_id) ++position; 138 memmove(&world->area_states[position + 1], &world->area_states[position], 139 (size_t)(world->initialized_area_count - position) * sizeof(*states)); 140 states = &world->area_states[position]; 141 ++world->initialized_area_count; 142 memset(states, 0, sizeof(*states)); 143 states->area_id = area_id; 144 return states; 145 } 146 147 int 148 ph_world_capture_area(PhWorld *world) 149 { 150 PhAreaState *state; 151 PhEntity *entities = NULL; 152 PhGroundItem *items = NULL; 153 int entity_count = 0; 154 int item_count = 0; 155 int i; 156 157 if (!world) return -1; 158 state = ph_world_area_state(world, world->area_id); 159 if (!state && !(state = ph_world_add_area_state(world, world->area_id))) 160 return -1; 161 for (i = 0; i < world->entity_count; ++i) 162 if (i != world->player_index && world->entities[i].active && 163 world->entities[i].area_id == world->area_id) ++entity_count; 164 for (i = 0; i < world->ground_item_count; ++i) 165 if (world->ground_items[i].active && 166 world->ground_items[i].area_id == world->area_id) ++item_count; 167 if (entity_count && !(entities = malloc((size_t)entity_count * sizeof(*entities)))) 168 return -1; 169 if (item_count && !(items = malloc((size_t)item_count * sizeof(*items)))) { 170 free(entities); 171 return -1; 172 } 173 entity_count = 0; 174 item_count = 0; 175 for (i = 0; i < world->entity_count; ++i) 176 if (i != world->player_index && world->entities[i].active && 177 world->entities[i].area_id == world->area_id) 178 entities[entity_count++] = world->entities[i]; 179 for (i = 0; i < world->ground_item_count; ++i) 180 if (world->ground_items[i].active && 181 world->ground_items[i].area_id == world->area_id) 182 items[item_count++] = world->ground_items[i]; 183 free(state->entities); 184 free(state->ground_items); 185 state->entities = entities; 186 state->ground_items = items; 187 state->entity_count = entity_count; 188 state->ground_item_count = item_count; 189 return 0; 190 } 191 192 static void 193 ph_world_clear_area_content(PhWorld *world, int *player) 194 { 195 PhEntity saved_player; 196 int has_player = world->player_index >= 0 && 197 world->player_index < world->entity_count; 198 199 if (has_player) saved_player = world->entities[world->player_index]; 200 memset(world->entities, 0, sizeof(world->entities)); 201 memset(world->ground_items, 0, sizeof(world->ground_items)); 202 world->entity_count = 0; 203 world->ground_item_count = 0; 204 world->player_index = -1; 205 *player = -1; 206 if (has_player) { 207 world->entities[0] = saved_player; 208 world->entity_count = 1; 209 world->player_index = *player = 0; 210 } 211 } 212 213 static int 214 ph_world_restore_area(PhWorld *world, PhAreaState *state, int *player) 215 { 216 if (!state || state->entity_count > PH_MAX_ENTITIES - world->entity_count || 217 state->ground_item_count > PH_MAX_GROUND_ITEMS) return -1; 218 if (state->entity_count) { 219 memcpy(&world->entities[world->entity_count], state->entities, 220 (size_t)state->entity_count * sizeof(*state->entities)); 221 world->entity_count += state->entity_count; 222 } 223 if (state->ground_item_count) { 224 memcpy(world->ground_items, state->ground_items, 225 (size_t)state->ground_item_count * sizeof(*state->ground_items)); 226 world->ground_item_count = state->ground_item_count; 227 } 228 *player = world->player_index; 229 return 0; 230 } 231 232 int 233 ph_world_prepare_area(PhWorld *world, const PhArea *area, 234 const PhAreaCatalog *catalog, int area_id, int *player) 235 { 236 PhAreaState *state; 237 int entity_count = world->entity_count; 238 int ground_item_count = world->ground_item_count; 239 int old_player = *player; 240 241 state = ph_world_area_state(world, area_id); 242 if (state && area_id == world->area_id) return 0; 243 if (state) return ph_world_restore_area(world, state, player); 244 if (ph_world_add_area_content(world, area, catalog, area_id, player) < 0 || 245 !ph_world_add_area_state(world, area_id)) { 246 world->entity_count = entity_count; 247 world->ground_item_count = ground_item_count; 248 *player = old_player; 249 return -1; 250 } 251 return 0; 252 } 253 254 int 255 ph_world_update_portal(PhWorld *world, const PhAreaCatalog *catalog) 256 { 257 const PhEntity *player = ph_world_player(world); 258 int tile_x; 259 int tile_y; 260 int i; 261 262 if (!player) return -1; 263 tile_x = (int)floorf(player->pos.x / PH_TILE_SIZE); 264 tile_y = (int)floorf(player->pos.y / PH_TILE_SIZE); 265 for (i = 0; i < world->area.marker_count; ++i) { 266 const PhAreaMarker *marker = &world->area.markers[i]; 267 const PhMarkerDef *portal = ph_area_catalog_marker(catalog, world->area_id, 268 marker->symbol); 269 PhArea area; 270 PhVec2 destination; 271 int player_index = world->player_index; 272 int j; 273 274 if (!portal || portal->kind != PH_MARKER_PORTAL || 275 marker->tile_x != tile_x || marker->tile_y != tile_y) continue; 276 if (ph_area_catalog_load(&area, catalog, portal->destination_area_id) < 0) 277 return -1; 278 for (j = 0; j < area.marker_count; ++j) { 279 const PhMarkerDef *arrival = ph_area_catalog_marker(catalog, 280 portal->destination_area_id, area.markers[j].symbol); 281 282 if (area.markers[j].symbol == portal->destination_symbol && arrival && 283 arrival->kind == PH_MARKER_ARRIVAL) break; 284 } 285 if (j == area.marker_count || ph_world_capture_area(world) < 0) { 286 ph_area_free(&area); 287 return -1; 288 } 289 ph_world_clear_area_content(world, &player_index); 290 if (ph_world_prepare_area(world, &area, catalog, 291 portal->destination_area_id, &player_index) < 0) { 292 PhAreaState *old = ph_world_area_state(world, world->area_id); 293 294 ph_world_clear_area_content(world, &player_index); 295 ph_world_restore_area(world, old, &player_index); 296 ph_area_free(&area); 297 return -1; 298 } 299 destination = ph_marker_pos(&area.markers[j]); 300 ph_world_enter_area(world, area, portal->destination_area_id, destination); 301 return 1; 302 } 303 return 0; 304 }