// Copyright (C) 1997 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // DESCRIPTION: // Base class for worldspawn objects. This should be subclassed whenever // a DLL has new game behaviour that needs to be initialized before any other // entities are created, or before any entity thinks each frame. Also controls // spawning of clients. // #include "g_local.h" #include "entity.h" #include "scriptmaster.h" #include "worldspawn.h" #include "surface.h" #include "console.h" #include "deadbody.h" #include "gravpath.h" #include "earthquake.h" //### #include "spritegun.h" // added for spritegun #include "checkpoints.h" //### extern void CreateMissionComputer( void ); WorldPtr world; //### added some options to the worldspawn /*****************************************************************************/ /*SINED worldspawn (0 0 0) ? CINEMATIC x BIKESONLY BODIESFADE Only used for the world. "sky" environment map name "skyaxis" vector axis for rotating sky "skyrotate" speed of rotation in degrees/second "soundtrack" the music file to use "cdtrack" music cd track number "gravity" 800 is default gravity "message" text to print at user logon "skipthread" thread that is activated to skip this level (if cinematic) "watercolor" specifies the view blend color while in water. "wateralpha" specifies the alpha of the view blend while in water. "lavacolor" specifies the view blend color while in lava. "lavaalpha" specifies the alpha of the view blend while in lava. "lightcolor" specifies the view blend color while in light. "lightalpha" specifies the alpha of the view blend while in light. "lavadamage" sets the amount of damage laval should do per waterheight level. Default is 10. "cpsounds" sets the script file to get the hoverbike checkpoint sounds from. Default is "global/default_cp_sounds.scr" BIKESONLY all players spawn in hoverbikes and can't get off. If the bike dies, they die. BODIESFADE makes dead player bodies fade shortly after dying. Good for big open maps. /*****************************************************************************/ #define CINEMATIC 1 //### #define BIKESONLY 4 #define BODIESFADE 8 //### CLASS_DECLARATION( Entity, World, "worldspawn" ); ResponseDef World::Responses[] = { { NULL, NULL } }; World::World() { const char *text; str mapname; int i; Vector water_color; Vector lightvolume_color; Vector lava_color; Event *ev; //### added for spritegun world = this; setMoveType( MOVETYPE_NONE ); setSolidType( SOLID_BSP ); // world model is always index 1 edict->s.modelindex = 1; model = "*1"; // Anything that modifies configstrings, or spawns things is ignored when loading savegames if ( LoadingSavegame ) { return; } // inform the client that this is deathmatch, and we should // draw deathmatch stats. This goes around what the CS_STATUSBAR // used to be used for since we moved all HUDS to the client. if ( deathmatch->value ) { gi.configstring( CS_STATUSBAR, "DEATHMATCH" ); } else { gi.configstring( CS_STATUSBAR, "SINGLE_PLAYER" ); } // // see if the level has a soundtrack associated withit // text = G_GetSpawnArg( "soundtrack" ); if ( text ) { gi.configstring( CS_SOUNDTRACK, text ); } text = G_GetSpawnArg( "sky" ); if ( text ) { gi.configstring( CS_SKY, text ); } else { gi.configstring( CS_SKY, "sky_" ); } text = G_GetSpawnArg( "skyrotate" ); gi.configstring( CS_SKYROTATE, text ? text : "0" ); text = G_GetSpawnArg( "skyaxis" ); gi.configstring( CS_SKYAXIS, text ? text : "0 0 0" ); text = G_GetSpawnArg( "cdtrack" ); gi.configstring( CS_CDTRACK, text ? text : "0" ); gi.configstring (CS_MAXCLIENTS, va("%i", (int)(maxclients->value) ) ); text = G_GetSpawnArg( "gravity" ); if ( !text ) { gi.cvar_set( "sv_gravity", "800" ); } else { gi.cvar_set( "sv_gravity", text ); } // get skipthread skipthread = G_GetStringArg( "skipthread" ); // the world takes blast marks and sparks by default flags |= FL_BLASTMARK; flags |= FL_SPARKS; // Reserve some space for dead bodies InitializeBodyQueue(); // // see if this is a cinematic level // if ( spawnflags & CINEMATIC ) { level.cinematic = true; } else { level.cinematic = false; } level.nextmap = G_GetStringArg( "nextmap" ); // make some data visible to the server text = G_GetSpawnArg( "message" ); if ( text ) { gi.configstring( CS_NAME, text ); level.level_name = text; } else { level.level_name = level.mapname; } // Set up script text = G_GetSpawnArg( "script" ); if ( !text ) { // No script specified. Try using the mapname as the script name mapname = "maps/"; mapname += level.mapname; for( i = mapname.length() - 1; i >= 0; i-- ) { if ( mapname[ i ] == '.' ) { mapname[ i ] = 0; break; } } mapname += ".scr"; text = &mapname[ 5 ]; // If there isn't a script with the same name as the map, then don't try to load script if ( gi.LoadFile( mapname.c_str(), NULL, 0 ) == -1 ) { text = NULL; } } if ( text ) { gi.dprintf( "Adding script: '%s'\n", text ); mapname = va( "maps/%s", text ); // just set the script, we will start it in G_Spawn ScriptLib.SetGameScript( mapname.c_str() ); } //### // read in sprite file after everything's spawned ev = new Event("spg_load"); Spritecontrol.PostEvent(ev, 0.1); // setup checkpoints sounds level.cp_sounds_script = G_GetStringArg("cpsounds", "global/default_cp_sounds.scr"); //set this here for when there's no checkpoints gi.configstring(CS_CHECKPOINTS, "NONE"); // init the checkpoint counter level.cp_anyorder = false; level.cp_num = 0; if(spawnflags & BIKESONLY) { level.bikesonly = true; } else { level.bikesonly = false; } if(spawnflags & BODIESFADE) { level.bodiesfade = true; } else { level.bodiesfade = false; } level.lavadamage = G_GetIntArg("lavadamage", 10); // remove any time limit offset reset_timeofs = 0; //### // Set the color for the blends. water_color = G_GetVectorArg("watercolor",Vector(0,0,1)); level.water_alpha = G_GetFloatArg("wateralpha",0.1); lightvolume_color = G_GetVectorArg("lightcolor",Vector(1,1,1)); level.lightvolume_alpha = G_GetFloatArg("lightalpha",0.5); lava_color = G_GetVectorArg("lavacolor",Vector(1.0,0.3,0)); level.lava_alpha = G_GetFloatArg("lavaalpha",0.6); level.water_color = water_color; level.lightvolume_color = lightvolume_color; level.lava_color = lava_color; // // reset the earthquake // level.earthquake = 0; } TargetList * World::GetTargetList( str &targetname ) { TargetList * targetlist; int i; for( i = 1; i <= targetList.NumObjects(); i++ ) { targetlist = targetList.ObjectAt( i ); if ( targetname == targetlist->targetname) return targetlist; } targetlist = new TargetList( targetname ); targetList.AddObject( targetlist ); return targetlist; } void World::AddTargetEntity( str &targetname, Entity * ent ) { TargetList * targetlist; targetlist = GetTargetList( targetname ); targetlist->AddEntity( ent ); } void World::RemoveTargetEntity( str &targetname, Entity * ent ) { TargetList * targetlist; targetlist = GetTargetList( targetname ); targetlist->RemoveEntity( ent ); } Entity * World::GetNextEntity( str &targetname, Entity * ent ) { TargetList * targetlist; targetlist = GetTargetList( targetname ); return targetlist->GetNextEntity( ent ); } //### added so I can trigger stuff easily from code void World::ActivateTarget(str &targetname) { TargetList * targetlist; Event *event; Entity *ent; int i, num; targetlist = GetTargetList( targetname ); num = targetlist->list.NumObjects(); for ( i = 1; i <= num; i++ ) { ent = targetlist->list.ObjectAt( i ); assert( ent ); event = new Event( EV_Activate ); event->AddEntity( world ); ent->ProcessEvent( event ); } } //### World::~World() { FreeTargetList(); } void World::FreeTargetList ( void ) { int i; int num; num = targetList.NumObjects(); for( i = 1; i <= num; i++ ) { delete targetList.ObjectAt( i ); } targetList.FreeObjectList(); } // // List stuff for targets // CLASS_DECLARATION( Class, TargetList, NULL ); ResponseDef TargetList::Responses[] = { { NULL, NULL } }; TargetList::TargetList() { } TargetList::TargetList( str &tname ) { targetname = tname; } TargetList::~TargetList() { } void TargetList::AddEntity( Entity * ent ) { if ( !list.ObjectInList( ent ) ) { list.AddObject( ent ); } } void TargetList::RemoveEntity( Entity * ent ) { if ( list.ObjectInList( ent ) ) { list.RemoveObject( ent ); } } Entity * TargetList::GetNextEntity( Entity * ent ) { int index; index = 0; if ( ent ) index = list.IndexOfObject( ent ); index++; if ( index > list.NumObjects() ) return NULL; else return list.ObjectAt( index ); }