/* * @brief */ static void G_BeginIntermission(const char *map) { int32_t i; g_edict_t *ent, *client; if (g_level.intermission_time) return; // already activated g_level.intermission_time = g_level.time; // respawn any dead clients for (i = 0; i < sv_max_clients->integer; i++) { client = g_game.edicts + 1 + i; if (!client->in_use) continue; if (client->locals.health <= 0) G_ClientRespawn(client, false); } // find an intermission spot ent = G_Find(NULL, EOFS(class_name), "info_player_intermission"); if (!ent) { // map does not have an intermission point ent = G_Find(NULL, EOFS(class_name), "info_player_start"); if (!ent) ent = G_Find(NULL, EOFS(class_name), "info_player_deathmatch"); } VectorCopy(ent->s.origin, g_level.intermission_origin); VectorCopy(ent->s.angles, g_level.intermission_angle); // move all clients to the intermission point for (i = 0; i < sv_max_clients->integer; i++) { client = g_game.edicts + 1 + i; if (!client->in_use) continue; G_ClientToIntermission(client); } // play a dramatic sound effect gi.PositionedSound(g_level.intermission_origin, g_game.edicts, gi.SoundIndex("weapons/bfg/hit"), ATTEN_NORM); // stay on same level if not provided g_level.changemap = map && *map ? map : g_level.name; }
/** * @brief Support function to allow chaingun maps to work nicely */ static void G_weapon_chaingun(g_entity_t *ent) { // see if we already have one ready, this is just to keep this BS self-contained g_entity_t *cg = NULL; while ((cg = G_Find(cg, EOFS(class_name), "weapon_chaingun"))) { if (cg->locals.Think) { return; // think will destroy us later } } ent->locals.Think = G_weapon_chaingun_Think; ent->locals.next_think = g_level.time + 1; // do it after spawnentities ent->locals.move_type = MOVE_TYPE_THINK; }
/** * @brief Actually does the magic */ static void G_weapon_chaingun_Think(g_entity_t *ent) { g_entity_t *cg = NULL; while ((cg = G_Find(cg, EOFS(class_name), "weapon_chaingun"))) { // spawn a lightning gun where we are g_entity_t *lg = G_AllocEntity_(g_media.items.weapons[WEAPON_LIGHTNING]->class_name); VectorCopy(cg->s.origin, lg->s.origin); VectorCopy(cg->s.angles, lg->s.angles); lg->locals.spawn_flags = cg->locals.spawn_flags; G_SpawnItem(lg, g_media.items.weapons[WEAPON_LIGHTNING]); // replace nearby bullets with bolts g_entity_t *ammo = NULL; while ((ammo = G_FindRadius(ammo, lg->s.origin, 128.0))) { if (ammo->locals.item && ammo->locals.item == g_media.items.ammo[AMMO_BULLETS]) { // hello bolts g_entity_t *bolts = G_AllocEntity_(g_media.items.ammo[AMMO_BOLTS]->class_name); VectorCopy(ammo->s.origin, bolts->s.origin); VectorCopy(ammo->s.angles, bolts->s.angles); bolts->locals.spawn_flags = ammo->locals.spawn_flags; G_SpawnItem(bolts, g_media.items.ammo[AMMO_BOLTS]); // byebye bullets G_FreeEntity(ammo); } } // byebye chaingun G_FreeEntity(cg); } }
/** * @brief Set up the list of spawn points. */ static void G_InitSpawnPoints(void) { // first, set up all of the deathmatch points GSList *dm_spawns = NULL; g_entity_t *spot = NULL; while ((spot = G_Find(spot, EOFS(class_name), "info_player_deathmatch")) != NULL) { dm_spawns = g_slist_prepend(dm_spawns, spot); } spot = NULL; // for legacy maps if (!g_slist_length(dm_spawns)) { while ((spot = G_Find(spot, EOFS(class_name), "info_player_start")) != NULL) { dm_spawns = g_slist_prepend(dm_spawns, spot); } } // find the team points, if we have any explicit ones in the map. // start by finding the flags for (int32_t t = 0; t < MAX_TEAMS; t++) { g_teamlist[t].flag_entity = G_Find(NULL, EOFS(class_name), g_teamlist[t].flag); } GSList *team_spawns[MAX_TEAMS]; memset(team_spawns, 0, sizeof(team_spawns)); spot = NULL; while ((spot = G_Find(spot, EOFS(class_name), "info_player_team_any")) != NULL) { for (int32_t t = 0; t < MAX_TEAMS; t++) { team_spawns[t] = g_slist_prepend(team_spawns[t], spot); } } for (int32_t t = 0; t < MAX_TEAMS; t++) { spot = NULL; g_team_t *team = &g_teamlist[t]; while ((spot = G_Find(spot, EOFS(class_name), team->spawn)) != NULL) { team_spawns[t] = g_slist_prepend(team_spawns[t], spot); } team->spawn_points.count = g_slist_length(team_spawns[t]); } // only one team if (!!g_team_red->spawn_points.count != !!g_team_blue->spawn_points.count) { gi.Error("Map has spawns for only a single team. Use info_player_deathmatch for these!\n"); } g_level.spawn_points.count = g_slist_length(dm_spawns); GSList *point = NULL; // in the odd case that the map only has team spawns, we'll use them if (!g_level.spawn_points.count) { for (int32_t t = 0; t < MAX_TEAMS; t++) { for (point = team_spawns[t]; point; point = point->next) { dm_spawns = g_slist_prepend(dm_spawns, (g_entity_t *) point->data); } } g_level.spawn_points.count = g_slist_length(dm_spawns); if (!g_level.spawn_points.count) { gi.Error("Map has no spawn points! You need some info_player_deathmatch's (or info_player_team1/2/3/4/_any for teamplay maps).\n"); } } // if we have team spawns, copy them over if (!g_team_red->spawn_points.count) { // none in the map, let's make some! G_CreateTeamSpawnPoints(&dm_spawns, &team_spawns[TEAM_RED], &team_spawns[TEAM_BLUE]); // re-calculate final values since the above may change them for (int32_t t = 0; t < MAX_TEAMS; t++) { g_teamlist[t].spawn_points.count = g_slist_length(team_spawns[t]); } g_level.spawn_points.count = g_slist_length(dm_spawns); } // copy all the data in! size_t i; for (int32_t t = 0; t < MAX_TEAMS; t++) { g_teamlist[t].spawn_points.spots = gi.Malloc(sizeof(g_entity_t *) * g_teamlist[t].spawn_points.count, MEM_TAG_GAME_LEVEL); for (i = 0, point = team_spawns[t]; point; point = point->next, i++) { g_teamlist[t].spawn_points.spots[i] = (g_entity_t *) point->data; } g_slist_free(team_spawns[t]); } g_level.spawn_points.spots = gi.Malloc(sizeof(g_entity_t *) * g_level.spawn_points.count, MEM_TAG_GAME_LEVEL); for (i = 0, point = dm_spawns; point; point = point->next, i++) { g_level.spawn_points.spots[i] = (g_entity_t *) point->data; } g_slist_free(dm_spawns); G_InitNumTeams(); }
F_INT, F_FLOAT, F_STRING, // string on disk, pointer in memory, TAG_LEVEL F_VECTOR, F_ANGLE } g_field_type_t; typedef struct g_field_s { char *name; ptrdiff_t ofs; g_field_type_t type; int32_t flags; } g_field_t; static const g_field_t fields[] = { { "classname", EOFS(class_name), F_STRING, 0 }, { "model", EOFS(model), F_STRING, 0 }, { "spawnflags", LOFS(spawn_flags), F_INT, 0 }, { "speed", LOFS(speed), F_FLOAT, 0 }, { "accel", LOFS(accel), F_FLOAT, 0 }, { "decel", LOFS(decel), F_FLOAT, 0 }, { "target", LOFS(target), F_STRING, 0 }, { "targetname", LOFS(target_name), F_STRING, 0 }, { "pathtarget", LOFS(path_target), F_STRING, 0 }, { "killtarget", LOFS(kill_target), F_STRING, 0 }, { "message", LOFS(message), F_STRING, 0 }, { "team", LOFS(team), F_STRING, 0 }, { "command", LOFS(command), F_STRING, 0 }, { "script", LOFS(script), F_STRING, 0 }, { "wait", LOFS(wait), F_FLOAT, 0 }, { "delay", LOFS(delay), F_FLOAT, 0 },