static void godcurse(void) { region *r; for (r = regions; r; r = r->next) { if (is_cursed(r->attribs, C_CURSED_BY_THE_GODS, 0)) { unit *u; for (u = r->units; u; u = u->next) { skill *sv = u->skills; while (sv != u->skills + u->skill_size) { int weeks = 1 + rng_int() % 3; reduce_skill(u, sv, weeks); ++sv; } } if (fval(r->terrain, SEA_REGION)) { ship *sh; for (sh = r->ships; sh;) { ship *shn = sh->next; double dmg = config_get_flt("rules.ship.damage.godcurse", 0.1); damage_ship(sh, dmg); if (sh->damage >= sh->size * DAMAGE_SCALE) { unit *u = ship_owner(sh); if (u) ADDMSG(&u->faction->msgs, msg_message("godcurse_destroy_ship", "ship", sh)); remove_ship(&sh->region->ships, sh); } sh = shn; } } } } }
/* updates positions of fires and also checks for collisions */ void update_fire(void) { int i, j; /* first loop to update all fires */ for (i = 0; i < MAX_FIRES; i++) { if (fires[i].surface) { /* if the laser has expired, free the surface and set it as off, otherwise, update its information */ if (current_time > fires[i].expire_time) { /* fire has expired */ free_fire(i); } else { float factor = (((float)loop_length / 1000.0f) * (float)fires[i].velocity); fires[i].world_x += get_cos(fires[i].angle) * factor; fires[i].world_y += get_neg_sin(fires[i].angle) * factor; /* update the fire, hasnt expired yet */ fires[i].screen_x = (short int)((int)fires[i].world_x - camera_x); fires[i].screen_y = (short int)((int)fires[i].world_y - camera_y); } } } /* second loop to check for collisions */ for (i = 0; i < MAX_FIRES; i++) { if (fires[i].surface) { for (j = 0; j < MAX_SHIPS; j++) { if (ships[j]) { if ((current_time >= ships[j]->creation_delay) && (!ships[j]->landed) && (ships[j]->hull_strength > 0)) { if (fires[i].owner != ships[j]) { if (get_distance_sqrd(fires[i].world_x, fires[i].world_y, ships[j]->world_x, ships[j]->world_y) < (ships[j]->model->radius * ships[j]->model->radius)) { /* collision has occured */ damage_ship(ships[j], &fires[i]); free_fire(i); /* do a check, it's possible the ship was destroyed and freed (and now null) in damage_ship() */ if (ships[j]) ship_was_fired_upon(ships[j]); j = num_ships; /* dont need to continue checking for more collisions, one is enough */ } } } } } } /* need to recheck as a destroyed fire could have occured and 'i' may have been incremented */ if (fires[i].surface) { for (j = 0; j < MAX_ASTEROIDS; j++) { if (asteroids[j].on) { if (get_distance_sqrd(fires[i].world_x, fires[i].world_y, asteroids[j].x, asteroids[j].y) < 2500) { /* collision has occured */ damage_asteroid(j, fires[i].weapon->strength); free_fire(i); j = MAX_SHIPS; /* dont need to continue checking for more collisions, one is enough */ } } } } /* need to recheck as a destroyed fire could have occured and 'i' may have been incremented */ if (fires[i].surface) { if (fires[i].owner != player.ship) { if (get_distance_from_player_sqrd(fires[i].world_x, fires[i].world_y) < (player.ship->model->radius * player.ship->model->radius)) { damage_ship(player.ship, &fires[i]); free_fire(i); } } } } }
static void move_iceberg(region * r) { attrib *a; direction_t dir; region *rc; a = a_find(r->attribs, &at_iceberg); if (!a) { dir = (direction_t) (rng_int() % MAXDIRECTIONS); a = a_add(&r->attribs, make_iceberg(dir)); } else { if (rng_int() % 100 < 20) { dir = (direction_t) (rng_int() % MAXDIRECTIONS); a->data.i = dir; } else { dir = (direction_t) a->data.i; } } rc = rconnect(r, dir); if (rc && !fval(rc->terrain, ARCTIC_REGION)) { if (fval(rc->terrain, SEA_REGION)) { /* Eisberg treibt */ ship *sh, *shn; unit *u; int x, y; for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next) if (!fval(u->faction, FFL_SELECT)) { fset(u->faction, FFL_SELECT); ADDMSG(&u->faction->msgs, msg_message("iceberg_drift", "region dir", r, dir)); } x = r->x; y = r->y; runhash(r); runhash(rc); r->x = rc->x; r->y = rc->y; rc->x = x; rc->y = y; rhash(rc); rhash(r); /* rc ist der Ozean (Ex-Eisberg), r der Eisberg (Ex-Ozean) */ /* Schiffe aus dem Zielozean werden in den Eisberg transferiert * und nehmen Schaden. */ for (sh = r->ships; sh; sh = sh->next) freset(sh, SF_SELECT); for (sh = r->ships; sh; sh = sh->next) { /* Meldung an Kapitän */ float dmg = get_param_flt(global.parameters, "rules.ship.damage.intoiceberg", 0.10F); damage_ship(sh, dmg); fset(sh, SF_SELECT); } /* Personen, Schiffe und Gebäude verschieben */ while (rc->buildings) { rc->buildings->region = r; translist(&rc->buildings, &r->buildings, rc->buildings); } while (rc->ships) { float dmg = get_param_flt(global.parameters, "rules.ship.damage.withiceberg", 0.10F); fset(rc->ships, SF_SELECT); damage_ship(rc->ships, dmg); move_ship(rc->ships, rc, r, NULL); } while (rc->units) { building *b = rc->units->building; u = rc->units; u->building = 0; /* prevent leaving in move_unit */ move_unit(rc->units, r, NULL); u_set_building(u, b); /* undo leave-prevention */ } /* Beschädigte Schiffe können sinken */ for (sh = r->ships; sh;) { shn = sh->next; if (fval(sh, SF_SELECT)) { u = ship_owner(sh); if (sh->damage >= sh->size * DAMAGE_SCALE) { if (u != NULL) { ADDMSG(&u->faction->msgs, msg_message("overrun_by_iceberg_des", "ship", sh)); } remove_ship(&sh->region->ships, sh); } else if (u != NULL) { ADDMSG(&u->faction->msgs, msg_message("overrun_by_iceberg", "ship", sh)); } } sh = shn; } } else if (rng_int() % 100 < 20) { /* Eisberg bleibt als Gletscher liegen */ unit *u; rsetterrain(r, T_GLACIER); a_remove(&r->attribs, a); for (u = r->units; u; u = u->next) freset(u->faction, FFL_SELECT); for (u = r->units; u; u = u->next) if (!fval(u->faction, FFL_SELECT)) { fset(u->faction, FFL_SELECT); ADDMSG(&u->faction->msgs, msg_message("iceberg_land", "region", r)); } } } }
void chaos(region * r) { if (rng_int() % 100 < 8) { switch (rng_int() % 3) { case 0: /* Untote */ if (!fval(r->terrain, SEA_REGION)) { unit *u = random_unit(r); if (u && playerrace(u_race(u))) { ADDMSG(&u->faction->msgs, msg_message("chaos_disease", "unit", u)); u_setfaction(u, get_monsters()); u_setrace(u, get_race(RC_GHOUL)); } } break; case 1: /* Drachen */ if (random_unit(r)) { int mfac = 0; unit *u; switch (rng_int() % 3) { case 0: mfac = 100; u = createunit(r, get_monsters(), rng_int() % 8 + 1, get_race(RC_FIREDRAGON)); break; case 1: mfac = 500; u = createunit(r, get_monsters(), rng_int() % 4 + 1, get_race(RC_DRAGON)); break; default: mfac = 1000; u = createunit(r, get_monsters(), rng_int() % 2 + 1, get_race(RC_WYRM)); break; } if (mfac) set_money(u, u->number * (rng_int() % mfac)); fset(u, UFL_ISNEW | UFL_MOVED); } case 2: /* Terrainveränderung */ if (!fval(r->terrain, FORBIDDEN_REGION)) { if (!fval(r->terrain, SEA_REGION)) { direction_t dir; for (dir = 0; dir != MAXDIRECTIONS; ++dir) { region *rn = rconnect(r, dir); if (rn && fval(rn->terrain, SEA_REGION)) break; } if (dir != MAXDIRECTIONS) { ship *sh = r->ships; unit **up; while (sh) { ship *nsh = sh->next; float dmg = get_param_flt(global.parameters, "rules.ship.damage.atlantis", 0.50); damage_ship(sh, dmg); if (sh->damage >= sh->size * DAMAGE_SCALE) { remove_ship(&sh->region->ships, sh); } sh = nsh; } for (up = &r->units; *up;) { unit *u = *up; if (u_race(u) != get_race(RC_SPELL) && u->ship == 0 && !canfly(u)) { ADDMSG(&u->faction->msgs, msg_message("tidalwave_kill", "region unit", r, u)); remove_unit(up, u); } if (*up == u) up = &u->next; } ADDMSG(&r->msgs, msg_message("tidalwave", "region", r)); while (r->buildings) { remove_building(&r->buildings, r->buildings); } terraform_region(r, newterrain(T_OCEAN)); } } else { direction_t dir; for (dir = 0; dir != MAXDIRECTIONS; ++dir) { region *rn = rconnect(r, dir); if (rn && fval(rn->terrain, SEA_REGION)) break; } if (dir != MAXDIRECTIONS) { terraform_region(r, chaosterrain()); } } } } } }
static void tick_physics(struct GameState *game_state, float dt) { // Projectile kinematics. for (uint32 i = 0; i < game_state->projectile_count; ++i) { struct Projectile *projectile = &game_state->projectiles[i]; // r = r0 + (v*t) + (a*t^2)/2 projectile->position = vec2_add(projectile->position, vec2_mul(projectile->velocity, dt)); } // Ship kinematics. for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; vec2 move_acceleration = vec2_zero(); // v = v0 + (a*t) ship->move_velocity = vec2_add(ship->move_velocity, vec2_mul(move_acceleration, dt)); // r = r0 + (v*t) + (a*t^2)/2 ship->position = vec2_add(vec2_add(ship->position, vec2_mul(ship->move_velocity, dt)), vec2_div(vec2_mul(move_acceleration, dt * dt), 2.0f)); } // // TODO: spatial hashing // // Projectile collision. for (uint32 i = 0; i < game_state->projectile_count; ++i) { struct Projectile *projectile = &game_state->projectiles[i]; struct AABB projectile_aabb = aabb_from_transform(projectile->position, projectile->size); // Projectile-building collision. for (uint32 j = 0; j < game_state->building_count; ++j) { struct Building *building = &game_state->buildings[j]; struct AABB building_aabb = aabb_from_transform(building->position, building->size); if (aabb_aabb_intersection(projectile_aabb, building_aabb)) { // TODO: damage building if not friendly destroy_projectile(game_state, projectile); break; } } // NOTE: owner may be dead and destroyed at this point, so checking for NULL might be required. struct Ship *owner = get_ship_by_id(game_state, projectile->owner); // Projectile-ship collision. for (uint32 j = 0; j < game_state->ship_count; ++j) { struct Ship *ship = &game_state->ships[j]; if (ship->id == projectile->owner) continue; #if 0 // Allow projectiles to pass through teammates. if (ship->team == projectile->team) continue; #endif struct AABB ship_aabb = aabb_from_transform(ship->position, ship->size); if (aabb_aabb_intersection(projectile_aabb, ship_aabb)) { // Disable friendly fire. if (ship->team != projectile->team) damage_ship(game_state, ship, projectile->damage); destroy_projectile(game_state, projectile); break; } } } // TODO: optimize // Ship collision. if (game_state->ship_count >= 2) { for (uint32 i = 0; i < game_state->ship_count - 1; ++i) { struct Ship *a = &game_state->ships[i]; struct AABB a_aabb = aabb_from_transform(a->position, a->size); vec2 a_center = vec2_div(vec2_add(a_aabb.min, a_aabb.max), 2.0f); vec2 a_half_extents = vec2_div(vec2_sub(a_aabb.max, a_aabb.min), 2.0f); // Ship-building collision. for (uint32 j = 0; j < game_state->building_count; ++j) { struct Building *building = &game_state->buildings[j]; struct AABB b_aabb = aabb_from_transform(building->position, building->size); if (aabb_aabb_intersection(a_aabb, b_aabb)) { vec2 b_center = vec2_div(vec2_add(b_aabb.min, b_aabb.max), 2.0f); vec2 b_half_extents = vec2_div(vec2_sub(b_aabb.max, b_aabb.min), 2.0f); vec2 intersection = vec2_sub(vec2_abs(vec2_sub(b_center, a_center)), vec2_add(a_half_extents, b_half_extents)); if (intersection.x > intersection.y) { a->move_velocity.x = 0.0f; if (a->position.x < building->position.x) a->position.x += intersection.x/2.0f; else a->position.x -= intersection.x/2.0f; } else { a->move_velocity.y = 0.0f; if (a->position.y < building->position.y) a->position.y += intersection.y/2.0f; else a->position.y -= intersection.y/2.0f; } } } // Ship-ship collision. for (uint32 j = i + 1; j < game_state->ship_count; ++j) { struct Ship *b = &game_state->ships[j]; struct AABB b_aabb = aabb_from_transform(b->position, b->size); if (aabb_aabb_intersection(a_aabb, b_aabb)) { vec2 b_center = vec2_div(vec2_add(b_aabb.min, b_aabb.max), 2.0f); vec2 b_half_extents = vec2_div(vec2_sub(b_aabb.max, b_aabb.min), 2.0f); vec2 intersection = vec2_sub(vec2_abs(vec2_sub(b_center, a_center)), vec2_add(a_half_extents, b_half_extents)); if (intersection.x > intersection.y) { if (abs_float(a->move_velocity.x) > abs_float(b->move_velocity.x)) a->move_velocity.x = 0.0f; else b->move_velocity.x = 0.0f; if (a->position.x < b->position.x) { a->position.x += intersection.x/2.0f; b->position.x -= intersection.x/2.0f; } else { a->position.x -= intersection.x/2.0f; b->position.x += intersection.x/2.0f; } } else { if (abs_float(a->move_velocity.y) > abs_float(b->move_velocity.y)) a->move_velocity.y = 0.0f; else b->move_velocity.y = 0.0f; if (a->position.y < b->position.y) { a->position.y += intersection.y/2.0f; b->position.y -= intersection.y/2.0f; } else { a->position.y -= intersection.y/2.0f; b->position.y += intersection.y/2.0f; } } } } } } }