/* The entities are directly placed in the array, rather than allocated with ED_Alloc, because otherwise an error loading the map would have entity number references out of order. Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. Used for both fresh maps and savegame loads. A fresh map would also need to call ED_CallSpawnFunctions () to let the objects initialize themselves. */ void ED_LoadFromFile(char *data) { ServerEntity_t *eEntity = NULL; for(;;) { // parse the opening brace data = COM_Parse(data); if(!data) break; if(com_token[0] != '{') Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token); if(!eEntity) eEntity = EDICT_NUM(0); else eEntity = ED_Alloc(); data = ED_ParseEdict(data,eEntity); // Immediately call spawn function if(!Game->Server_SpawnEntity(eEntity)) { if(developer.value) Edict_Print(eEntity); ED_Free(eEntity); continue; } } }
static void G_ParseString(void) { const char *entities = level.entstring; edict_t *ent; int inhibit = 0; char *token; // parse ents while (1) { // parse the opening brace token = COM_Parse(&entities); if (!entities) break; if (token[0] != '{') gi.error("%s: found %s when expecting {", __func__, token); ent = G_Spawn(); ED_ParseEdict(&entities, ent); // remove things from different skill levels or deathmatch if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) { G_FreeEdict(ent); inhibit++; continue; } ent->spawnflags &= ~INHIBIT_MASK; ED_CallSpawn(ent); } gi.dprintf("%i entities inhibited\n", inhibit); }
/* DESCRIPTION: ED_LoadFromFile // LOCATION: pr_edict.c, but it's conspicuously absent in FTEQW. // PATH: SV_LoadEntities // // To quote the text block that came with it: // The entities are directly placed in the array, rather than allocated with // ED_Alloc, because otherwise an error loading the map would have entity // number references out of order. // // Creates a server's entity / program execution context by // parsing textual entity definitions out of an ent file. // // Used for both fresh maps and savegame loads. A fresh map would also need // to call ED_CallSpawnFunctions () to let the objects initialize themselves. */ void ED_LoadFromFile(const char *data) { edict_t *ent; int inhibit; ent = NULL; inhibit = 0; gGlobalVariables.time = global_sv.time0c; //parse ents while(1) { // parse the opening brace data = COM_Parse(data); if(data == NULL) { break; } if(global_com_token[0] != '{') { Sys_Error ("%s: found %s when expecting {", __FUNCTION__, global_com_token); } if(ent == NULL) { ent = &(global_sv.edicts[0]); ReleaseEntityDLLFields(ent); InitEntityDLLFields(ent); } else { ent = ED_Alloc(); } data = ED_ParseEdict(data, ent); // remove things from different skill levels or deathmatch if(cvar_deathmatch.value != 0) { if((ent->v.spawnflags & 0x800) != 0) { //SPAWNFLAG_NOT_DEATHMATCH ED_Free(ent); inhibit++; continue; } } // immediately call spawn function if(ent->v.classname == 0) { Con_Printf("%s: Ent with no classname.\n", __FUNCTION__); ED_Free(ent); continue; } if(gEntityInterface.pfnSpawn(ent) < 0 || ent->v.flags & FL_KILLME) { ED_Free(ent); } } Con_Printf("%i entities inhibited\n", inhibit); }
/* =============== Host_Loadgame_f =============== */ void Host_Loadgame_f (void) { char name[MAX_OSPATH], temp[MAX_OSPATH]; vfsfile_t *f; char mapname[MAX_QPATH]; float time, tfloat; char str[32768], *start, *c; int i, r, len, n; edict_t *ent; int entnum; int version; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load <savename> : load a game\n"); return; } cls.demonum = -1; // stop demo loop in case this fails // sprintf (name, "%s", Cmd_Argv(1)); strcpy (name, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con_Printf ("Loading game from %s...\n", name); // f = fopen (name, "r"); f = FS_OpenVFS(name, "rb", FS_GAMEONLY); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } // fscanf (f, "%i\n", &version); version = atoi(VFS_GETS(f, name, sizeof(name))); if (version != SAVEGAME_VERSION) { // fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); VFS_CLOSE (f); return; } // fscanf (f, "%s\n", str); VFS_GETS(f, str, sizeof(str)); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) spawn_parms[i] = atof(VFS_GETS(f, temp, sizeof(temp))); // fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values // fscanf (f, "%f\n", &tfloat); tfloat = atof(VFS_GETS(f, temp, sizeof(temp))); // temp buf current_skill = (int)(tfloat + 0.1); Cvar_SetValue ("skill", (float)current_skill); #ifdef QUAKE2 Cvar_SetValue ("deathmatch", 0); Cvar_SetValue ("coop", 0); Cvar_SetValue ("teamplay", 0); #endif // fscanf (f, "%s\n",mapname); // fscanf (f, "%f\n",&time); VFS_GETS(f, mapname, sizeof(mapname)); for (c = mapname+strlen(mapname)-1; c>=mapname && (*c == '\n' || *c == '\r'); *c--='\0'); time = atof(VFS_GETS(f, temp, sizeof(temp))); CL_Disconnect_f (); allowcheats = sv_cheats.value != 0; // sv_cheats // if the video isn't initialized already, it needs to be Host_InitVideo(); #ifdef QUAKE2 SV_SpawnServer (mapname, NULL); #else SV_SpawnServer (mapname); #endif if (!sv.active) { Con_Printf ("Couldn't load map\n"); VFS_CLOSE (f); return; } sv.paused = true; // pause until all clients connect sv.loadgame = true; // load the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { // fscanf (f, "%s\n", str); VFS_GETS(f, str, sizeof(str)); // sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1); // this is no longer necessary, changed to straight array for (c = str+strlen(str)-1; c>=str && (*c == '\n' || *c == '\r'); *c--='\0'); strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals len = VFS_GETLEN(f); n = 0; // while (!feof(f)) for(;;) { for (i=0 ; i<sizeof(str)-1 ; i++) { // r = fgetc (f); r = VFS_GETC(f); if (r == -1 || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (r == -1 || !r) break; if (i == sizeof(str)-1) Sys_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Sys_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; n++; } sv.num_edicts = entnum; sv.time = time; // fclose (f); VFS_CLOSE (f); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (char *mapname, char *entities, char *spawnpoint) { edict_t *ent = NULL; int inhibit = 0; char *com_token; int i; float skill_level; //AQ2:TNG New Location Code char locfile[MAX_QPATH], line[256]; FILE *f; int readmore, x, y, z, rx, ry, rz, count; char *locationstr, *param; cvar_t *game_cvar; int u; // placedata_t temp; // Reset teamplay stuff for(i = TEAM1; i < TEAM_TOP; i++) { teams[i].score = teams[i].total = 0; teams[i].ready = teams[i].locked = 0; teams[i].pauses_used = teams[i].wantReset = 0; gi.cvar_forceset(teams[i].teamscore->name, "0"); } matchtime = 0; day_cycle_at = 0; team_round_going = team_game_going = team_round_countdown = 0; lights_camera_action = holding_on_tie_check = 0; timewarning = fragwarning = 0; teamCount = 2; if (ctf->value) { // Make sure teamplay is enabled if (!teamplay->value) { gi.dprintf ("CTF Enabled - Forcing teamplay on\n"); gi.cvar_forceset(teamplay->name, "1"); } if (use_3teams->value) { gi.dprintf ("CTF Enabled - Forcing 3Teams off\n"); gi.cvar_forceset(use_3teams->name, "0"); } if(teamdm->value) { gi.dprintf ("CTF Enabled - Forcing Team DM off\n"); gi.cvar_forceset(teamdm->name, "0"); } if (use_tourney->value) { gi.dprintf ("CTF Enabled - Forcing Tourney off\n"); gi.cvar_forceset(use_tourney->name, "0"); } if (!((int) (dmflags->value) & DF_NO_FRIENDLY_FIRE)) { gi.dprintf ("CTF Enabled - Forcing Friendly Fire off\n"); gi.cvar_forceset(dmflags->name, va("%i", (int)dmflags->value | DF_NO_FRIENDLY_FIRE)); } strcpy(teams[TEAM1].name, "RED"); strcpy(teams[TEAM2].name, "BLUE"); strcpy(teams[TEAM1].skin, "male/ctf_r"); strcpy(teams[TEAM2].skin, "male/ctf_b"); strcpy(teams[TEAM1].skin_index, "../players/male/ctf_r_i"); strcpy(teams[TEAM2].skin_index, "../players/male/ctf_b_i"); if(ctf->value == 2) gi.cvar_forceset(ctf->name, "1"); //for now } else if(teamdm->value) { if (!teamplay->value) { gi.dprintf ("Team Deathmatch Enabled - Forcing teamplay on\n"); gi.cvar_forceset(teamplay->name, "1"); } if (use_3teams->value) { gi.dprintf ("Team Deathmatch Enabled - Forcing 3Teams off\n"); gi.cvar_forceset(use_3teams->name, "0"); } if (use_tourney->value) { gi.dprintf ("Team Deathmatch Enabled - Forcing Tourney off\n"); gi.cvar_forceset(use_tourney->name, "0"); } } else if (use_3teams->value) { teamCount = 3; if (!teamplay->value) { gi.dprintf ("3 Teams Enabled - Forcing teamplay on\n"); gi.cvar_forceset(teamplay->name, "1"); } if (use_tourney->value) { gi.dprintf ("3 Teams Enabled - Forcing Tourney off\n"); gi.cvar_forceset(use_tourney->name, "0"); } if (!use_oldspawns->value) { gi.dprintf ("3 Teams Enabled - Forcing use_oldspawns on\n"); gi.cvar_forceset(use_oldspawns->name, "1"); } } else if (matchmode->value) { if (!teamplay->value) { gi.dprintf ("Matchmode Enabled - Forcing teamplay on\n"); gi.cvar_forceset(teamplay->name, "1"); } if (use_tourney->value) { gi.dprintf ("Matchmode Enabled - Forcing Tourney off\n"); gi.cvar_forceset(use_tourney->name, "0"); } } else if (use_tourney->value) { if (!teamplay->value) { gi.dprintf ("Tourney Enabled - Forcing teamplay on\n"); gi.cvar_forceset(teamplay->name, "1"); } } skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset ("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof (level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); Q_strncpyz(level.mapname, mapname, sizeof(level.mapname)); Q_strncpyz(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)); // set client fields on player ents for (i = 0; i < game.maxclients; i++) g_edicts[i + 1].client = game.clients + i; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {", com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp (level.mapname, "command") && !Q_stricmp (ent->classname, "trigger_once") && !Q_stricmp (ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) { G_FreeEdict (ent); inhibit++; continue; } } else { if ( /* ((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */ ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD))) { G_FreeEdict (ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn (ent); } gi.dprintf ("%i entities inhibited\n", inhibit); // AQ2:TNG Igor adding .flg files // CTF configuration if(ctf->value) { if(!CTFLoadConfig(level.mapname)) { if ((!G_Find(NULL, FOFS (classname), "item_flag_team1") || !G_Find(NULL, FOFS (classname), "item_flag_team2"))) { gi.dprintf ("No native CTF map, loading flag positions from file\n"); if (LoadFlagsFromFile (level.mapname)) ChangePlayerSpawns (); } } } // AQ2:TNG End adding .flg files G_FindTeams (); PlayerTrail_Init (); // TNG:Freud - Ghosts num_ghost_players = 0; //FIREBLADE if (!teamplay->value || teamdm->value || ctf->value == 2) { //FIREBLADE //zucc for special items SetupSpecSpawn (); } else if (teamplay->value) { GetSpawnPoints (); //TNG:Freud - New spawning system if(!use_oldspawns->value) NS_GetSpawnPoints(); } //AQ2:TNG Slicer - New location code memset (ml_build, 0, sizeof (ml_build)); memset (ml_creator, 0, sizeof (ml_creator)); game_cvar = gi.cvar ("game", "", 0); if (!*game_cvar->string) Com_sprintf(locfile, sizeof(locfile), "%s/tng/%s.aqg", GAMEVERSION, level.mapname); else Com_sprintf(locfile, sizeof(locfile), "%s/tng/%s.aqg", game_cvar->string, level.mapname); f = fopen (locfile, "rt"); if (!f) { ml_count = 0; gi.dprintf ("No location file for %s\n", level.mapname); return; } gi.dprintf ("Location file: %s\n", level.mapname); readmore = 1; count = 0; while (readmore) { readmore = (fgets (line, 256, f) != NULL); param = strtok (line, " :\r\n\0"); if (line[0] == '#' && line[2] == 'C') { u = 0; for (i = 10; line[i] != '\n'; i++) { if (line[i] == '\r') continue; ml_creator[u] = line[i]; u++; } } if (line[0] == '#' && line[2] == 'B') { u = 0; for (i = 8; line[i] != '\n'; i++) { if (line[i] != ' ' && line[i] != '\r') { ml_build[u] = line[i]; u++; } } } ml_build[5] = 0; ml_creator[100] = 0; // TODO: better support for file comments if (!param || param[0] == '#') continue; x = atoi (param); param = strtok (NULL, " :\r\n\0"); if (!param) continue; y = atoi (param); param = strtok (NULL, " :\r\n\0"); if (!param) continue; z = atoi (param); param = strtok (NULL, " :\r\n\0"); if (!param) continue; rx = atoi (param); param = strtok (NULL, " :\r\n\0"); if (!param) continue; ry = atoi (param); param = strtok (NULL, " :\r\n\0"); if (!param) continue; rz = atoi (param); param = strtok (NULL, "\r\n\0"); if (!param) continue; locationstr = param; locationbase[count].x = x; locationbase[count].y = y; locationbase[count].z = z; locationbase[count].rx = rx; locationbase[count].ry = ry; locationbase[count].rz = rz; Q_strncpyz (locationbase[count].desc, locationstr, sizeof(locationbase[count].desc)); count++; if (count >= MAX_LOCATIONS_IN_BASE) { gi.dprintf ("Cannot read more than %d locations.\n", MAX_LOCATIONS_IN_BASE); break; } } ml_count = count; fclose (f); gi.dprintf ("Found %d locations.\n", count); }
/* * SpawnEntities * * Creates a server's entity / program execution context by * parsing textual entity definitions out of an ent file. */ void G_InitLevel( char *mapname, char *entities, int entstrlen, unsigned int levelTime, unsigned int serverTime, unsigned int realTime ) { char *mapString = NULL; char name[MAX_CONFIGSTRING_CHARS]; int i; edict_t *ent; char *token; const gsitem_t *item; G_asGarbageCollect( true ); GT_asCallShutdown(); G_asCallMapExit(); G_asShutdownMapScript(); GT_asShutdownScript(); G_FreeCallvotes(); game.serverTime = serverTime; game.realtime = realTime; game.levelSpawnCount++; GClip_ClearWorld(); // clear areas links if( !entities ) G_Error( "G_SpawnLevel: NULL entities string\n" ); // make a copy of the raw entities string so it's not freed with the pool mapString = ( char * )G_Malloc( entstrlen + 1 ); memcpy( mapString, entities, entstrlen ); Q_strncpyz( name, mapname, sizeof( name ) ); // clear old data G_LevelInitPool( strlen( mapname ) + 1 + ( entstrlen + 1 ) * 2 + G_LEVELPOOL_BASE_SIZE ); G_StringPoolInit(); memset( &level, 0, sizeof( level_locals_t ) ); memset( &gs.gameState, 0, sizeof( gs.gameState ) ); level.spawnedTimeStamp = game.realtime; level.time = levelTime; level.gravity = g_gravity->value; // get the strings back Q_strncpyz( level.mapname, name, sizeof( level.mapname ) ); level.mapString = ( char * )G_LevelMalloc( entstrlen + 1 ); level.mapStrlen = entstrlen; memcpy( level.mapString, mapString, entstrlen ); G_Free( mapString ); mapString = NULL; // make a copy of the raw entities string for parsing level.map_parsed_ents = ( char * )G_LevelMalloc( entstrlen + 1 ); level.map_parsed_ents[0] = 0; if( !level.time ) memset( game.edicts, 0, game.maxentities * sizeof( game.edicts[0] ) ); else { G_FreeEdict( world ); for( i = gs.maxclients + 1; i < game.maxentities; i++ ) { if( game.edicts[i].r.inuse ) G_FreeEdict( game.edicts + i ); } } game.numentities = gs.maxclients + 1; // link client fields on player ents for( i = 0; i < gs.maxclients; i++ ) { game.edicts[i+1].s.number = i+1; game.edicts[i+1].r.client = &game.clients[i]; game.edicts[i+1].r.inuse = ( trap_GetClientState( i ) >= CS_CONNECTED ) ? true : false; memset( &game.clients[i].level, 0, sizeof( game.clients[0].level ) ); game.clients[i].level.timeStamp = level.time; } // initialize game subsystems trap_ConfigString( CS_MAPNAME, level.mapname ); trap_ConfigString( CS_SKYBOX, "" ); trap_ConfigString( CS_AUDIOTRACK, "" ); trap_ConfigString( CS_STATNUMS, va( "%i %i %i", STAT_SCORE, STAT_HEALTH, STAT_LAST_KILLER ) ); trap_ConfigString( CS_POWERUPEFFECTS, va( "%i %i %i %i", EF_QUAD, EF_SHELL, EF_CARRIER, EF_REGEN ) ); trap_ConfigString( CS_SCB_PLAYERTAB_LAYOUT, "" ); trap_ConfigString( CS_SCB_PLAYERTAB_TITLES, "" ); trap_ConfigString( CS_MATCHNAME, "" ); trap_ConfigString( CS_MATCHSCORE, "" ); // reset map messages for( i = 0; i < MAX_HELPMESSAGES; i++ ) { trap_ConfigString( CS_HELPMESSAGES + i, "" ); } G_InitGameCommands(); G_MapLocations_Init(); G_CallVotes_Init(); G_SpawnQueue_Init(); G_Teams_Init(); // load map script G_asLoadMapScript( level.mapname ); G_Gametype_Init(); // ch : this would be the location to "transfer ratings" G_PrecacheItems(); // set configstrings for items (gametype must be initialized) G_PrecacheMedia(); G_PrecacheGameCommands(); // adding commands after this point won't update them to the client AI_InitLevel(); // load navigation file of the current map // start spawning entities level.canSpawnEntities = true; G_InitBodyQueue(); // reserve some spots for dead player bodies entities = level.mapString; i = 0; ent = NULL; while( 1 ) { level.spawning_entity = NULL; // parse the opening brace token = COM_Parse( &entities ); if( !entities ) break; if( token[0] != '{' ) G_Error( "G_SpawnMapEntities: found %s when expecting {", token ); if( !ent ) { ent = world; G_InitEdict( world ); } else ent = G_Spawn(); ent->spawnString = entities; // keep track of string definition of this entity entities = ED_ParseEdict( entities, ent ); if( !ent->classname ) { i++; // racesow - introducing the freestyle map bug again in // order to make some freestyle maps work if( !level.gametype.freestyleMapFix ) G_FreeEdict( ent ); // !racesow G_FreeEdict( ent ); continue; } if( !G_CanSpawnEntity( ent ) ) { i++; G_FreeEdict( ent ); continue; } if( !G_CallSpawn( ent ) ) { i++; G_FreeEdict( ent ); continue; } // check whether an item is allowed to spawn if( ( item = ent->item ) ) { // not pickable items aren't spawnable if( item->flags & ITFLAG_PICKABLE ) { if( G_Gametype_CanSpawnItem( item ) ) { // override entity's classname with whatever item specifies ent->classname = item->classname; PrecacheItem( item ); continue; } } i++; G_FreeEdict( ent ); continue; } } G_FindTeams(); // is the parsing string sane? assert( (int)level.map_parsed_len < entstrlen ); level.map_parsed_ents[level.map_parsed_len] = 0; // make sure server got the edicts data trap_LocateEntities( game.edicts, sizeof( game.edicts[0] ), game.numentities, game.maxentities ); // items need brush model entities spawned before they are linked G_Items_FinishSpawningItems(); // // initialize game subsystems which require entities initialized // // call gametype specific GT_asCallSpawn(); // call map specific G_asCallMapInit(); AI_InitEntitiesData(); // always start in warmup match state and let the thinking code // revert it to wait state if empty ( so gametype based item masks are setup ) G_Match_LaunchState( MATCH_STATE_WARMUP ); G_asGarbageCollect( true ); // racesow RS_Init(); }
/** * @brief Creates a server's entity / program execution context * by parsing textual entity definitions out of an ent file. * @sa CM_EntityString * @sa SV_SpawnServer */ void G_SpawnEntities (const char *mapname, bool day, const char *entities) { int entnum; G_FreeTags(TAG_LEVEL); OBJZERO(level); level.pathingMap = (pathing_t *)G_TagMalloc(sizeof(*level.pathingMap), TAG_LEVEL); G_EdictsReset(); /* initialize reactionFire data */ G_ReactionFireTargetsInit(); Q_strncpyz(level.mapname, mapname, sizeof(level.mapname)); level.day = day; G_ResetClientData(); level.activeTeam = TEAM_NO_ACTIVE; level.actualRound = 1; level.hurtAliens = sv_hurtaliens->integer; ai_waypointList = NULL; /* parse ents */ entnum = 0; while (1) { edict_t *ent; /* parse the opening brace */ const char *token = Com_Parse(&entities); if (!entities) break; if (token[0] != '{') gi.Error("ED_LoadFromFile: found %s when expecting {", token); ent = G_Spawn(); entities = ED_ParseEdict(entities, ent); ent->mapNum = entnum++; /* Set the position of the entity */ VecToPos(ent->origin, ent->pos); /* Call this entity's specific initializer (sets ent->type) */ ED_CallSpawn(ent); /* if this entity is an bbox (e.g. actor), then center its origin based on its position */ if (ent->solid == SOLID_BBOX) G_EdictCalcOrigin(ent); } /* spawn ai players, if needed */ if (level.num_spawnpoints[TEAM_CIVILIAN]) { if (AI_CreatePlayer(TEAM_CIVILIAN) == NULL) gi.DPrintf("Could not create civilian\n"); } if ((sv_maxclients->integer == 1 || ai_numactors->integer) && level.num_spawnpoints[TEAM_ALIEN]) { if (AI_CreatePlayer(TEAM_ALIEN) == NULL) gi.DPrintf("Could not create alien\n"); } Com_Printf("Used inventory slots after ai spawn: %i\n", game.i.GetUsedSlots(&game.i)); G_FindEdictGroups(); }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (const char *mapname, char *entities, const char *spawnpoint) { edict_t *ent; int inhibit; const char *com_token; int i; float skill_level; int oldmaxent; skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof(level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); strncpy (level.mapname, mapname, sizeof(level.mapname)-1); strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); // set client fields on player ents for (i=0 ; i<game.maxclients ; i++) g_edicts[i+1].client = game.clients + i; ent = NULL; inhibit = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); ent->spawnflags2 = 0; entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if ( (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) || (ent->spawnflags2 & SPAWNFLAG2_NOT_SINGLE) ) { G_FreeEdict (ent); inhibit++; continue; } } #if 0 // FIXME: DG: coop stuff from rogue else if (coop->value) { if (ent->spawnflags & SPAWNFLAG_NOT_COOP) { G_FreeEdict(ent); inhibit++; continue; } /* stuff marked !easy & !med & !hard are coop only, all levels */ if (!((ent->spawnflags & SPAWNFLAG_NOT_EASY) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM) && (ent->spawnflags & SPAWNFLAG_NOT_HARD))) { if (((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD))) { G_FreeEdict(ent); inhibit++; continue; } } } #endif // 0 else { if (((!coop->value) && (ent->spawnflags2 & SPAWNFLAG2_NOT_SINGLE)) || ((coop->value) && (ent->spawnflags2 & SPAWNFLAG2_NOT_COOP)) || ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn (ent); } oldmaxent = globals.num_edicts; gi.dprintf("%i entities created\n", globals.num_edicts); gi.dprintf ("%i entities inhibited\n", inhibit); G_FindTeams (); PlayerTrail_Init (); Z_SpawnDMItems(); }
void SV_LoadGame_f (void) { char name[MAX_OSPATH], mapname[MAX_QPATH], str[32 * 1024], *start; FILE *f; float time, tfloat, spawn_parms[NUM_SPAWN_PARMS]; edict_t *ent; int entnum, version, r; unsigned int i; if (Cmd_Argc() != 2) { Com_Printf ("Usage: %s <savename> : load a game\n", Cmd_Argv(0)); return; } snprintf (name, sizeof (name), "%s/save/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); Com_Printf ("Loading game from %s...\n", name); if (!(f = fopen (name, "rb"))) { Com_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Com_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i = 0; i < NUM_SPAWN_PARMS; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_Set (&skill, va("%i", current_skill)); Cvar_SetValue (&deathmatch, 0); Cvar_SetValue (&coop, 0); Cvar_SetValue (&teamplay, 0); Cvar_SetValue (&maxclients, 1); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &time); Host_EndGame(); CL_BeginLocalConnection (); SV_SpawnServer (mapname, false); if (sv.state != ss_active) { Com_Printf ("Couldn't load map\n"); return; } Cvar_ForceSet (&sv_paused, "1"); // pause until all clients connect sv.loadgame = true; // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = (char *) Hunk_Alloc (strlen(str) + 1); strlcpy (sv.lightstyles[i], str, strlen(str) + 1); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { for (i = 0; i < sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Host_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Host_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (char *mapname, char *entities, char *spawnpoint) { edict_t *ent; int inhibit; char *com_token; int i; float skill_level; skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof(level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); strncpy (level.mapname, mapname, sizeof(level.mapname)-1); strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); // set client fields on player ents for (i=0 ; i<game.maxclients ; i++) g_edicts[i+1].client = game.clients + i; ent = NULL; inhibit = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; continue; } } else { if ( /* ((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */ ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn (ent); } gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); PlayerTrail_Init (); }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (char *mapname, char *entities, char *spawnpoint) { edict_t *ent; int inhibit; char *com_token; int i; float skill_level; extern int max_modelindex; extern int max_soundindex; extern int lastgibframe; if (developer->value) gi.dprintf("====== SpawnEntities ========\n"); skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof(level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); // Lazarus: these are used to track model and sound indices // in g_main.c: max_modelindex = 0; max_soundindex = 0; // Lazarus: last frame a gib was spawned in lastgibframe = 0; strncpy (level.mapname, mapname, sizeof(level.mapname)-1); strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); // set client fields on player ents for (i=0 ; i<game.maxclients ; i++) g_edicts[i+1].client = game.clients + i; ent = NULL; inhibit = 0; // Knightamre- load the entity alias script file LoadAliasData(); //gi.dprintf ("Size of alias data: %i\n", alias_data_size); // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; continue; } } else if (coop->value) // Knightmare added- not in coop flag support { if ( ent->spawnflags & SPAWNFLAG_NOT_COOP ) { G_FreeEdict (ent); inhibit++; continue; } // NOT_EASY && NOT_MEDIUM && NOT_HARD && NOT_DM == COOP_ONLY if ( (ent->spawnflags & SPAWNFLAG_NOT_EASY) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM) && (ent->spawnflags & SPAWNFLAG_NOT_HARD) && (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) ) goto removeflags; if( ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || ((skill->value >= 2) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } else // single player { if( ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || ((skill->value >= 2) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } removeflags: // Knightmare- remove no coop flag ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_DEATHMATCH|SPAWNFLAG_NOT_COOP); } ED_CallSpawn (ent); ent->s.renderfx |= RF_IR_VISIBLE; // ir goggles flag } // Knightmare- unload the alias script file if (alias_data) { // If no alias file was loaded, don't bother #ifdef KMQUAKE2_ENGINE_MOD // use new engine function instead gi.FreeFile(alias_data); #else if (alias_from_pak) gi.TagFree(alias_data); else free(&alias_data); #endif } // Knightmare- unload the replacement entity data /* if (newents) // If no alias file was loaded, don't bother #ifdef KMQUAKE2_ENGINE_MOD // use new engine function instead gi.FreeFile(newents); #else free(&newents); #endif*/ gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); // DWH G_FindCraneParts(); // Get origin offsets (mainly for brush models w/o origin brushes) for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++) { VectorAdd(ent->absmin,ent->absmax,ent->origin_offset); VectorScale(ent->origin_offset,0.5,ent->origin_offset); VectorSubtract(ent->origin_offset,ent->s.origin,ent->origin_offset); } // end DWH PlayerTrail_Init (); //ZOID CTFSpawn(); // Knightmare added if (deathmatch->value && !ctf->value) CTFSetupTechSpawn(); //ZOID if (!deathmatch->value) SetupHintPaths(); for(i=1, ent=g_edicts+i; i < globals.num_edicts; i++, ent++) { if(!ent->movewith) continue; if(ent->movewith_ent) continue; ent->movewith_ent = G_Find(NULL,FOFS(targetname),ent->movewith); // Make sure that we can really "movewith" this guy. This check // allows us to have movewith parent with same targetname as // other entities while(ent->movewith_ent && (Q_stricmp(ent->movewith_ent->classname,"func_train") && Q_stricmp(ent->movewith_ent->classname,"model_train") && Q_stricmp(ent->movewith_ent->classname,"func_door") && Q_stricmp(ent->movewith_ent->classname,"func_vehicle") && Q_stricmp(ent->movewith_ent->classname,"func_tracktrain") )) ent->movewith_ent = G_Find(ent->movewith_ent,FOFS(targetname),ent->movewith); if(ent->movewith_ent) movewith_init(ent->movewith_ent); } /* for(i=1, ent=g_edicts+i; i < globals.num_edicts; i++, ent++) { gi.dprintf("%s:%s - movewith=%s, movewith_ent=%s:%s, movewith_next=%s:%s\n====================\n", ent->classname, (ent->targetname ? ent->targetname : "noname"), (ent->movewith ? ent->movewith : "N/A"), (ent->movewith_ent ? ent->movewith_ent->classname : "N/A"), (ent->movewith_ent ? (ent->movewith_ent->targetname ? ent->movewith_ent->targetname : "noname") : "N/A"), (ent->movewith_next ? ent->movewith_next->classname : "N/A"), (ent->movewith_next ? (ent->movewith_next->targetname ? ent->movewith_next->targetname : "noname") : "N/A")); } */ if(game.transition_ents) LoadTransitionEnts(); actor_files(); }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void G_SpawnEntities(const char *mapname, const char *entities, const char *spawnpoint) { edict_t *ent; gclient_t *client; int i; client_persistant_t pers; char *token; char playerskin[MAX_QPATH]; #if USE_SQLITE G_OpenDatabase(); G_LogClients(); #endif gi.FreeTags(TAG_LEVEL); memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); Q_strlcpy(level.mapname, mapname, sizeof(level.mapname)); G_LoadScores(); // set client fields on player ents for (i = 0; i < game.maxclients; i++) { ent = &g_edicts[i + 1]; client = &game.clients[i]; ent->client = client; ent->inuse = qfalse; if (!client->pers.connected) { continue; } // clear everything but the persistant data pers = client->pers; memset(client, 0, sizeof(*client)); client->pers = pers; client->edict = ent; client->clientNum = i; client->pers.connected = CONN_CONNECTED; // combine name and skin into a configstring Q_concat(playerskin, sizeof(playerskin), client->pers.netname, "\\", client->pers.skin, NULL); gi.configstring(CS_PLAYERSKINS + i, playerskin); gi.configstring(CS_PLAYERNAMES + i, client->pers.netname); } // parse worldspawn token = COM_Parse(&entities); if (!entities) gi.error("%s: empty entity string", __func__); if (token[0] != '{') gi.error("%s: found %s when expecting {", __func__, token); ent = g_edicts; ED_ParseEdict(&entities, ent); ED_CallSpawn(ent); level.entstring = entities; G_ParseString(); G_FindTeams(); //G_UpdateItemBans(); // find spawnpoints ent = NULL; while ((ent = G_Find(ent, FOFS(classname), "info_player_deathmatch")) != NULL) { level.spawns[level.numspawns++] = ent; if (level.numspawns == MAX_SPAWNS) { break; } } gi.dprintf("%d spawn points\n", level.numspawns); }
/* =============== SV_LoadGame_f =============== */ void SV_LoadGame_f (void) { char name[MAX_OSPATH]; FILE *f; char mapname[MAX_QPATH]; float time, tfloat; char str[32768], *start; int i, r; edict_t *ent; int entnum; int version; float spawn_parms[NUM_SPAWN_PARMS]; qbool save_disabled_for_loading; if (Cmd_Argc() != 2) { Com_Printf ("load <savename> : load a game\n"); return; } sprintf (name, "%s/save/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Com_Printf ("Loading game from %s...\n", name); f = fopen (name, "r"); if (!f) { Com_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Com_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_Set (&skill, va("%i", current_skill)); Cvar_SetValue (&deathmatch, 0); Cvar_SetValue (&coop, 0); Cvar_SetValue (&teamplay, 0); Cvar_SetValue (&maxclients, 1); fscanf (f, "%s\n",mapname); fscanf (f, "%f\n",&time); save_disabled_for_loading = scr_disabled_for_loading; Host_EndGame (); // Host_EndGame disables the loading plaque, restore it scr_disabled_for_loading = save_disabled_for_loading; CL_BeginLocalConnection (); SV_SpawnServer (mapname, false); if (sv.state != ss_active) { Com_Printf ("Couldn't load map\n"); return; } Cvar_ForceSet (&sv_paused, "1"); // pause until all clients connect sv.loadgame = true; // load the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1); strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { for (i=0 ; i<sizeof(str)-1 ; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Host_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Host_Error ("First token isn't a brace"); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->inuse = true; ED_ParseEdict (start, ent); // link it into the bsp tree if (ent->inuse) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); // FIXME: this assumes the player is using client slot #0 for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; }
int LoadGamestate(char *level, char *startspot) { char name[MAX_OSPATH]; FILE *f; char mapname[MAX_QPATH]; float time, sk; char str[32768], *start; int i, r; edict_t *ent; int entnum; int version; // float spawn_parms[NUM_SPAWN_PARMS]; sprintf (name, "%s/%s.gip", com_gamedir, level); Con_Printf ("Loading game from %s...\n", name); f = fopen (name, "r"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return -1; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return -1; } fscanf (f, "%s\n", str); // for (i=0 ; i<NUM_SPAWN_PARMS ; i++) // fscanf (f, "%f\n", &spawn_parms[i]); fscanf (f, "%f\n", &sk); Cvar_SetValue ("skill", sk); fscanf (f, "%s\n",mapname); fscanf (f, "%f\n",&time); SV_SpawnServer (mapname, startspot); if (!sv.active) { Con_Printf ("Couldn't load map\n"); return -1; } // load the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1); strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file while (!feof(f)) { fscanf (f, "%i\n",&entnum); for (i=0 ; i<sizeof(str)-1 ; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == sizeof(str)-1) Sys_Error ("Loadgame buffer overflow"); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Sys_Error ("First token isn't a brace"); // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } // sv.num_edicts = entnum; sv.time = time; fclose (f); // for (i=0 ; i<NUM_SPAWN_PARMS ; i++) // svs.clients->spawn_parms[i] = spawn_parms[i]; return 0; }
void ParseEntityString (qboolean respawn) { edict_t *ent; int inhibit; const char *com_token; const char *entities; int i; entities = level.entity_string; ent = NULL; inhibit = 0; i = 0; //mark all the spawned_entities as inuse so G_Spawn doesn't try to pick them during //trigger spawning etc for (i = 0; i < num_spawned_entities; i++) spawned_entities[i]->inuse = true; i = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (respawn) { ent = spawned_entities[i]; if (ent != world) { //double check we aren't overwriting stuff that we shouldn't be if (ent->enttype == ENT_DOOR_TRIGGER || ent->enttype == ENT_PLAT_TRIGGER) TDM_Error ("ParseEntityString: Trying to reuse an already spawned ent!"); G_FreeEdict (ent); G_InitEdict (ent); } i++; } else { if (!ent) ent = g_edicts; else ent = G_Spawn (); } entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; //have to keep num_spawned in sync for respawn if (!respawn) spawned_entities[num_spawned_entities++] = ent; continue; } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } else if (respawn) continue; ED_CallSpawn (ent); if (!respawn) { if (num_spawned_entities == MAX_EDICTS * 4) TDM_Error ("Exceeded MAX_EDICTS * 4 entities during entity string parse"); spawned_entities[num_spawned_entities++] = ent; } else { //an elevator or something respawned on top of a player? don't do it for match start, since //players will be respawning anyway. if (tdm_match_status < MM_PLAYING && (ent->solid == SOLID_BBOX || ent->solid == SOLID_BSP)) { edict_t *touch[MAX_EDICTS]; int count; int j; //r1: fix 00000196, can't killbox during map spawn due to risk of killing half-spawned //map entities. do a pseudo-killbox that only whacks players instead. count = gi.BoxEdicts (ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID); for (j = 0; j < count; j++) { if (touch[j] == ent) continue; //no point killing anything that won't clip (eg corpses) if (touch[j]->solid == SOLID_NOT || touch[j]->enttype == ENT_BODYQUE) continue; if (!touch[j]->client) continue; if (touch[j]->inuse) T_Damage (touch[j], ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); } } } } //gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); }
/* ================ ED_LoadFromFile The entities are directly placed in the array, rather than allocated with ED_Alloc, because otherwise an error loading the map would have entity number references out of order. Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. Used for both fresh maps and savegame loads. A fresh map would also need to call ED_CallSpawnFunctions () to let the objects initialize themselves. ================ */ void ED_LoadFromFile (char *data) { extern cvar_t cl_curlybraces; edict_t *ent; int inhibit; dfunction_t *func; float curlybraces_oldvalue = cl_curlybraces.value; if (curlybraces_oldvalue) { Cvar_SetValue(&cl_curlybraces, 0); } ent = NULL; inhibit = 0; pr_global_struct->time = sv.time; // parse ents while (1) { // parse the opening brace data = COM_Parse (data); if (!data) break; if (com_token[0] != '{') SV_Error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = EDICT_NUM(0); else ent = ED_Alloc (); data = ED_ParseEdict (data, ent); // remove things from different skill levels or deathmatch if ((int)deathmatch.value) { if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { ED_Free (ent); inhibit++; continue; } } else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) { ED_Free (ent); inhibit++; continue; } // // immediately call spawn function // if (!ent->v.classname) { Con_Printf ("No classname for:\n"); ED_Print (ent); ED_Free (ent); continue; } // look for the spawn function func = ED_FindFunction ( PR_GetString(ent->v.classname) ); if (!func) { Con_Printf ("No spawn function for:\n"); ED_Print (ent); ED_Free (ent); continue; } pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (func - pr_functions); SV_FlushSignon(); } Con_DPrintf ("%i entities inhibited\n", inhibit); if (curlybraces_oldvalue) { Cvar_SetValue(&cl_curlybraces, curlybraces_oldvalue); } }
static int LoadGamestate (const char *level, const char *startspot, int ClientsMode) { FILE *f; char mapname[MAX_QPATH]; float playtime, sk; char str[32768]; const char *start; int i, r; edict_t *ent; int entnum; int version; // float spawn_parms[NUM_SPAWN_PARMS]; qboolean auto_correct = false; if (ClientsMode == 1) /* for RestoreClients() only: map must be active */ { if (!sv.active) { Con_Printf ("%s: server not active\n", __thisfunc__); return -1; } FS_MakePath_BUF (FS_USERDIR, &r, savename, sizeof(savename), "clients.gip"); if (r) { Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } else { FS_MakePath_VABUF (FS_USERDIR, &r, savename, sizeof(savename), "%s.gip", level); if (r) { Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } if (ClientsMode != 2 && ClientsMode != 3) Con_Printf ("Loading game from %s...\n", savename); } f = fopen (savename, "r"); if (!f) { if (ClientsMode == 2) Con_Printf ("%s: ERROR: couldn't open savefile\n", __thisfunc__); return -1; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return -1; } if (ClientsMode != 1) { fscanf (f, "%s\n", str); // for (i = 0; i < NUM_SPAWN_PARMS; i++) // fscanf (f, "%f\n", &spawn_parms[i]); fscanf (f, "%f\n", &sk); Cvar_SetValue ("skill", sk); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &playtime); SV_SpawnServer (mapname, startspot); if (!sv.active) { fclose (f); Con_Printf ("Couldn't load map\n"); return -1; } // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles"); } SV_LoadEffects (f); } // load the edicts out of the savegame file while (!feof(f)) { fscanf (f, "%i\n", &entnum); for (i = 0; i < (int) sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == (int) sizeof(str) - 1) { fclose (f); Host_Error ("%s: Loadgame buffer overflow", __thisfunc__); } str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) { fclose (f); Host_Error ("%s: First token isn't a brace", __thisfunc__); } // parse an edict if (entnum == -1) { ED_ParseGlobals (start); // Need to restore this *sv_globals.startspot = PR_SetEngineString(sv.startspot); } else { ent = EDICT_NUM(entnum); /* default to active edict: ED_ParseEdict() set it * to free if it really is free. cf. ED_Write() */ ent->free = false; /* ED_ParseEdict() will always memset ent->v to 0, * because SaveGamestate() doesn't write entnum 0 */ ED_ParseEdict (start, ent); if (ClientsMode == 1 || ClientsMode == 2 || ClientsMode == 3) ent->v.stats_restored = true; // link it into the bsp tree if (!ent->free) { if (entnum >= sv.num_edicts) { /* This is necessary to restore "generated" edicts which were * not available during the map parsing by ED_LoadFromFile(). * This includes items dropped by monsters, items "dropped" by * an item_spawner such as the "prizes" in the Temple of Mars * (romeric5), a health sphere generated by the Crusader's * Holy Strength ability, or a respawning-candidate killed * monster in the expansion pack's nightmare mode. -- THOMAS */ /* Moved this into the if (!ent->free) construct: less debug * chatter. Even if this skips a free edict in between, the * skipped free edict wasn't parsed by ED_LoadFromFile() and * it will remain as a freed edict. (There is no harm because * we are dealing with extra edicts not originally present in * the map.) -- O.S. */ Con_DPrintf("%s: entnum %d >= sv.num_edicts (%d)\n", __thisfunc__, entnum, sv.num_edicts); sv.num_edicts = entnum + 1; } SV_LinkEdict (ent, false); if (ent->v.modelindex && ent->v.model) { i = SV_ModelIndex(PR_GetString(ent->v.model)); if (i != ent->v.modelindex) { ent->v.modelindex = i; auto_correct = true; } } } } } fclose (f); if (ClientsMode == 0) { sv.time = playtime; sv.paused = true; *sv_globals.serverflags = svs.serverflags; RestoreClients (0); } else if (ClientsMode == 2) { sv.time = playtime; } else if (ClientsMode == 3) { sv.time = playtime; *sv_globals.serverflags = svs.serverflags; RestoreClients (3); } if (ClientsMode != 1 && auto_correct) { Con_DPrintf("*** Auto-corrected model indexes!\n"); } // for (i = 0; i < NUM_SPAWN_PARMS; i++) // svs.clients->spawn_parms[i] = spawn_parms[i]; return 0; }
/* =============== Host_Loadgame_f =============== */ void Host_Loadgame_f (void) { char name[MAX_OSPATH]; FILE *f; char mapname[MAX_QPATH]; float time, tfloat; char str[32768]; const char *start; int i, r; edict_t *ent; int entnum; int version; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load <savename> : load a game\n"); return; } cls.demonum = -1; // stop demo loop in case this fails q_snprintf (name, sizeof(name), "%s/%s", com_gamedir, Cmd_Argv(1)); COM_AddExtension (name, ".sav", sizeof(name)); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con_Printf ("Loading game from %s...\n", name); f = fopen (name, "r"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i = 0; i < NUM_SPAWN_PARMS; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_SetValue ("skill", (float)current_skill); fscanf (f, "%s\n",mapname); fscanf (f, "%f\n",&time); CL_Disconnect_f (); SV_SpawnServer (mapname); if (!sv.active) { fclose (f); Con_Printf ("Couldn't load map\n"); return; } sv.paused = true; // pause until all clients connect sv.loadgame = true; // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles"); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals while (!feof(f)) { qboolean inside_string = false; for (i = 0; i < (int) sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '"') { inside_string = !inside_string; } else if (r == '}' && !inside_string) // only handle } characters outside of quoted strings { i++; break; } } if (i == (int) sizeof(str) - 1) { fclose (f); Sys_Error ("Loadgame buffer overflow"); } str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) { fclose (f); Sys_Error ("First token isn't a brace"); } if (entnum == -1) { // parse the global vars ED_ParseGlobals (start); } else { // parse an edict ent = EDICT_NUM(entnum); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i = 0; i < NUM_SPAWN_PARMS; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } }
/* =============== Host_Loadgame_f =============== */ void Host_Loadgame_f (void) { char name[MAX_OSPATH]; FILE *f; char mapname[MAX_QPATH]; float time, tfloat; char str[32768], *start; int i, r, line; // Current line # in savegame file edict_t *ent; int entnum, numedicts_save; int version; float spawn_parms[NUM_SPAWN_PARMS]; qboolean Quote; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load <savename> : load a game\n"); return; } cls.demonum = -1; // stop demo loop in case this fails sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con_SafePrintf ("Loading game from %s...\n", name); f = fopen (name, "rb"); // Use binary mode to avoid EOF issues in savegame files if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } // Kludge to read saved games with newlines in title do fscanf (f, "%s\n", str); while (!feof(f) && !strstr(str, "kills:")); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tfloat); current_skill = (int)(tfloat + 0.1); Cvar_SetValue ("skill", (float)current_skill); fscanf (f, "%s\n",mapname); fscanf (f, "%f\n",&time); CL_Disconnect_f (); #ifdef QUAKE2 SV_SpawnServer (mapname, NULL); #else SV_SpawnServer (mapname); #endif if (!sv.active) { Con_Printf ("Couldn't load map\n"); return; } sv.paused = true; // pause until all clients connect sv.loadgame = true; // load the light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = Hunk_Alloc (strlen(str)+1); strcpy (sv.lightstyles[i], str); } // load the edicts out of the savegame file entnum = -1; // -1 is the globals // Hack to avoid validation errors while parsing savegame file numedicts_save = sv.num_edicts; sv.num_edicts = MAX_EDICTS; line = 86; // 85 lines before globals in savegame while (!feof(f)) { for (i=0, Quote = false ; i<sizeof(str)-1 ; i++) { r = fgetc (f); if (r == EOF || !r) break; if (r == '\n') ++line; str[i] = r; // Handle multiline quoted strings containing '}' if (!Quote) { if (r == '\"') Quote = true; else if (r == '}') { i++; break; } } else if (Quote) { if (r == '\"') Quote = false; } } if (i == sizeof(str)-1) Sys_Error ("Loadgame buffer overflow (%d, max = %d) on line %d", i + 1, sizeof(str) - 1, line); if (Quote) Sys_Error ("Host_Loadgame_f: %s in quoted string on line %d", r == EOF ? "EOF" : "EOS", line); str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) Sys_Error ("First token (%s) isn't a brace on line %d", com_token, line); if (entnum == -1) { // parse the global vars ED_ParseGlobals (start, line); } else { // parse an edict ent = EDICT_NUM("Host_Loadgame_f1", entnum); if (!pr_free[entnum]) ED_Free (ent); // Unlink from world memset (&ent->v, 0, progs->entityfields * 4); pr_free[entnum] = false; ED_ParseEdict (start, ent, line); // link it into the bsp tree if (!pr_free[entnum]) { int mindx = ent->v.modelindex; model_t *model = sv.models[mindx]; char *mname = pr_String ("Host_Loadgame_f1", ent->v.model); char *cname = pr_String ("Host_Loadgame_f2", ent->v.classname); // Check for missing/invalid models (except e.g. player/eyes RoS switch) if (mindx != 0 && (model == NULL || *mname != 0 && strcmp(model->name, mname) && strcmp(cname, "player"))) { Con_Printf ("\x02Host_Loadgame_f: "); if (model == NULL) Con_Printf ("missing model"); else Con_Printf ("invalid model (%s)", model->name); Con_Printf (" for edict %d (%s)\n", entnum, ED_DbgEdict(&ent->v)); } SV_LinkEdict (ent, false); } } entnum++; } sv.num_edicts = numedicts_save; // Free edicts not present in the savegame, might // otherwise cause odd SV_TouchLinks errors for (i = entnum; i < sv.num_edicts; ++i) { if (!pr_free[i]) { ent = EDICT_NUM("Host_Loadgame_f2", i); // Don't warn if edict_reuse is disabled if (ent->area.prev && edict_reuse.value) { Con_Printf ("\002Host_Loadgame_f: "); Con_Printf ("invalid touched edict (%d, max = %d)\n", i, entnum); } ED_Free (ent); // Unlink from world } } sv.num_edicts = entnum; // Set new edict amount from savegame // Count active edicts for (i = sv.active_edicts = 0; i < sv.num_edicts; ++i) { if (!pr_free[i]) ++sv.active_edicts; } ED_ChkEdict (true, false); sv.time = time; fclose (f); for (i=0 ; i<NUM_SPAWN_PARMS ; i++) svs.clients->spawn_parms[i] = spawn_parms[i]; // Chack for different saved game mode if (deathmatch.value != pr_global_struct->deathmatch || coop.value != pr_global_struct->coop || teamplay.value != pr_global_struct->teamplay) { Con_Printf ("\002Host_Loadgame_f: "); Con_Printf ("saved game mode different (dm=%g/%g, coop=%g/%g, team=%g/%g)\n", deathmatch.value, pr_global_struct->deathmatch, coop.value, pr_global_struct->coop, teamplay.value, pr_global_struct->teamplay); } if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } }
/* * Creates a server's entity / program execution context by * parsing textual entity definitions out of an ent file. */ void SpawnEntities(char *mapname, char *entities, char *spawnpoint) { edict_t *ent; int inhibit; char *com_token; int i; float skill_level; skill_level = floor(skill->value); if (skill_level < 0) { skill_level = 0; } if (skill_level > 3) { skill_level = 3; } if (skill->value != skill_level) { gi.cvar_forceset("skill", va("%f", skill_level)); } SaveClientData(); gi.FreeTags(TAG_LEVEL); memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); strncpy(level.mapname, mapname, sizeof(level.mapname) - 1); strncpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint) - 1); /* set client fields on player ents */ for (i = 0; i < game.maxclients; i++) { g_edicts[i + 1].client = game.clients + i; } ent = NULL; inhibit = 0; /* parse ents */ while (1) { /* parse the opening brace */ com_token = COM_Parse(&entities); if (!entities) { break; } if (com_token[0] != '{') { gi.error("ED_LoadFromFile: found %s when expecting {", com_token); } if (!ent) { ent = g_edicts; } else { ent = G_Spawn(); } entities = ED_ParseEdict(entities, ent); /* yet another map hack */ if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) { ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; } /* remove things (except the world) from different skill levels or deathmatch */ if (ent != g_edicts) { if (deathmatch->value) { if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) { G_FreeEdict(ent); inhibit++; continue; } } else { if (((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict(ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn(ent); } gi.dprintf("%i entities inhibited.\n", inhibit); G_FindTeams(); PlayerTrail_Init(); CTFSpawn(); }
/* * Creates a server's entity / program execution context by * parsing textual entity definitions out of an ent file. */ void SpawnEntities(const char *mapname, char *entities, const char *spawnpoint) { edict_t *ent; int inhibit; const char *com_token; int i; float skill_level; static qboolean monster_count_city3 = false; if (!mapname || !entities || !spawnpoint) { return; } skill_level = floor(skill->value); if (skill_level < 0) { skill_level = 0; } if (skill_level > 3) { skill_level = 3; } if (skill->value != skill_level) { gi.cvar_forceset("skill", va("%f", skill_level)); } SaveClientData(); gi.FreeTags(TAG_LEVEL); memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); Q_strlcpy(level.mapname, mapname, sizeof(level.mapname)); Q_strlcpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)); /* set client fields on player ents */ for (i = 0; i < game.maxclients; i++) { g_edicts[i + 1].client = game.clients + i; } ent = NULL; inhibit = 0; /* parse ents */ while (1) { /* parse the opening brace */ com_token = COM_Parse(&entities); if (!entities) { break; } if (com_token[0] != '{') { gi.error("ED_LoadFromFile: found %s when expecting {", com_token); } if (!ent) { ent = g_edicts; } else { ent = G_Spawn(); } entities = ED_ParseEdict(entities, ent); /* yet another map hack */ if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) { ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; } /* * The 'monsters' count in city3.bsp is wrong. * There're two monsters triggered in a hidden * and unreachable room next to the security * pass. * * We need to make sure that this hack is only * applied once! */ if(!Q_stricmp(level.mapname, "city3") && !monster_count_city3) { level.total_monsters = level.total_monsters - 2; monster_count_city3 = true; } /* remove things (except the world) from different skill levels or deathmatch */ if (ent != g_edicts) { if (deathmatch->value) { if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) { G_FreeEdict(ent); inhibit++; continue; } } else { if (((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict(ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn(ent); } gi.dprintf("%i entities inhibited.\n", inhibit); G_FindTeams(); PlayerTrail_Init(); }