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