// called every time when adding a bot or bringing the bot from previous map void Bot_Spawn_And_Begin (client_t *cl) { int i; edict_t *ent = cl->edict; // set colormap, name, entgravity and maxspeed SetUpClientEdict (cl, ent); cl->state = cs_spawned; // copy spawn parms out of the client_t for (i=0 ; i< NUM_SPAWN_PARMS ; i++) (&PR_GLOBAL(parm1))[i] = cl->spawn_parms[i]; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (PR_GLOBAL(ClientConnect)); // actually spawn the player pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (PR_GLOBAL(PutClientInServer)); cl->sendinfo = true; }
/* ================ SV_SaveSpawnparms Grabs the current state of the progs serverinfo flags and each client for saving across the transition to another level ================ */ static void SV_SaveSpawnparms (void) { int i, j; if (!sv.state) return; // no progs loaded yet // serverflags is the only game related thing maintained svs.serverflags = PR_GLOBAL(serverflags); for (i=0, sv_client = svs.clients ; i<MAX_CLIENTS ; i++, sv_client++) { if (sv_client->state != cs_spawned) continue; // needs to reconnect sv_client->state = cs_connected; // call the progs to get default spawn parms for the new client pr_global_struct->self = EDICT_TO_PROG(sv_client->edict); PR_GameSetChangeParms(); for (j=0 ; j<NUM_SPAWN_PARMS ; j++) sv_client->spawn_parms[j] = (&PR_GLOBAL(parm1))[j]; } }
/* ================== SV_Impact Two entities have touched, so run their touch functions ================== */ void SV_Impact (edict_t *e1, edict_t *e2) { int old_self, old_other; old_self = PR_GLOBAL(self); old_other = PR_GLOBAL(other); PR_GLOBAL(time) = sv.time; if (e1->v.touch && e1->v.solid != SOLID_NOT) { PR_GLOBAL(self) = EDICT_TO_PROG(e1); PR_GLOBAL(other) = EDICT_TO_PROG(e2); PR_ExecuteProgram (e1->v.touch); } if (e2->v.touch && e2->v.solid != SOLID_NOT) { PR_GLOBAL(self) = EDICT_TO_PROG(e2); PR_GLOBAL(other) = EDICT_TO_PROG(e1); PR_ExecuteProgram (e2->v.touch); } PR_GLOBAL(self) = old_self; PR_GLOBAL(other) = old_other; }
/* ================ SV_Physics ================ */ void SV_Physics (void) { int i; edict_t *ent; if (sv.state != ss_game) return; if (sv.old_time) { // don't bother running a frame if sv_mintic seconds haven't passed sv_frametime = sv.time - sv.old_time; if (sv_frametime < sv_mintic.value) return; if (sv_frametime > sv_maxtic.value) sv_frametime = sv_maxtic.value; sv.old_time = sv.time; } else sv_frametime = 0.1; // initialization frame if (pr_nqprogs) NQP_Reset (); PR_GLOBAL(frametime) = sv_frametime; SV_ProgStartFrame (); // // treat each object in turn // even the world gets a chance to think // ent = sv.edicts; for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent)) { if (!ent->inuse) continue; if (PR_GLOBAL(force_retouch)) SV_LinkEdict (ent, true); // force retouch even for stationary if (i > 0 && i <= MAX_CLIENTS) continue; // clients are run directly from packets SV_RunEntity (ent); SV_RunNewmis (); } if (PR_GLOBAL(force_retouch)) PR_GLOBAL(force_retouch)--; SV_RunBots (); }
/* ================ SV_Physics_Pusher ================ */ void SV_Physics_Pusher (edict_t *ent) { float thinktime; float oldltime; float movetime; oldltime = ent->v.ltime; #ifdef _DEBUG if (!ent->v.classname && ent->v.think) movetime = 0; #endif thinktime = ent->v.nextthink; if (thinktime < ent->v.ltime + host_frametime) { movetime = thinktime - ent->v.ltime; if (movetime < 0) movetime = 0; } else movetime = host_frametime; if (movetime) { #ifdef HEXEN2_SUPPORT if (hexen2 && (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])) { SV_PushRotate (ent, movetime); } else #endif SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked } if (thinktime > oldltime && thinktime <= ent->v.ltime) { ent->v.nextthink = 0; PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); PR_GLOBAL(other) = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); if (ent->free) return; } }
void SV_RemoveBot (client_t *cl) { if (cl->state == cs_spawned) { if (!cl->spectator) { // call the prog function for removing a client // this will set the body to a dead frame, among other things pr_global_struct->self = EDICT_TO_PROG(cl->edict); PR_ExecuteProgram (PR_GLOBAL(ClientDisconnect)); } else if (SpectatorDisconnect) { // call the prog function for removing a client // this will set the body to a dead frame, among other things pr_global_struct->self = EDICT_TO_PROG(cl->edict); PR_ExecuteProgram (SpectatorDisconnect); } } Com_DPrintf ("Bot %s removed\n", cl->name.c_str()); cl->state = cs_free; // we don't have zombie bots :) cl->bot = false; cl->old_frags = 0; cl->name = ""; // cl->edict->inuse = false; cl->edict->v.frags = 0; cl->userinfo.clear(); SV_FreeDelayedPackets (cl); // send notification to all remaining clients SV_FullClientUpdate (cl, &sv.reliable_datagram); }
void SV_ProgStartFrame (void) { // let the progs know that a new frame has started pr_global_struct->self = EDICT_TO_PROG(sv.edicts); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); pr_global_struct->time = sv.time; PR_ExecuteProgram (PR_GLOBAL(StartFrame)); }
void PR1_GamePutClientInServer(int spec) { if (spec) { // none... } else { PR_ExecuteProgram(PR_GLOBAL(PutClientInServer)); } }
void PR1_GameClientPreThink(int spec) { if (spec) { // none... } else { PR_ExecuteProgram(PR_GLOBAL(PlayerPreThink)); } }
void PR1_GameClientPostThink(int spec) { if (spec) { if (mod_SpectatorThink) PR_ExecuteProgram(mod_SpectatorThink); } else { PR_ExecuteProgram(PR_GLOBAL(PlayerPostThink)); } }
void PR1_GameClientConnect(int spec) { if (spec) { if (mod_SpectatorConnect) PR_ExecuteProgram(mod_SpectatorConnect); } else { PR_ExecuteProgram(PR_GLOBAL(ClientConnect)); } }
/* ============= SV_RunThink Runs thinking code if time. There is some play in the exact time the think function will be called, because it is called before any movement is done in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. ============= */ qboolean SV_RunThink (edict_t *ent) { float thinktime; int oldcount; thinktime = ent->v.nextthink; if (thinktime <= 0 || thinktime > sv.time + host_frametime) return true; if (thinktime < sv.time) thinktime = sv.time; // don't let things stay in the past. // it is possible to start that way // by a trigger with a local time. ent->v.nextthink = 0; PR_GLOBAL(time) = thinktime; PR_GLOBAL(self) = EDICT_TO_PROG(ent); PR_GLOBAL(other) = EDICT_TO_PROG(sv.edicts); // JDH: the extra 1 added to total_monsters by monster_fish happens // in swimmonster_start_go, during the first frame (sv.time = 1). // But fix it only if total_monsters was increased when fish were // spawned (ie. if sv.fish_counted is true) if ((sv.time == 1.0) && sv_fishfix.value && sv.fish_counted && !strcmp(pr_strings + ent->v.classname, "monster_fish") && !strcmp(pr_functions[ent->v.think].s_name, "swimmonster_start_go")) { oldcount = PR_GLOBAL(total_monsters); } else oldcount = -1; PR_ExecuteProgram (ent->v.think); if (oldcount != -1) { if ((int)PR_GLOBAL(total_monsters) - oldcount == 1) { PR_GLOBAL(total_monsters) -= 1; if (sv.fish_counted == 1) { Con_Print ("Detected fish-count bug in progs.dat; monster count has been adjusted\n"); sv.fish_counted++; } } } return !ent->free; }
//Writes a SAVEGAME_COMMENT_LENGTH character comment void SV_SavegameComment (char *buffer) { int i; char kills[20]; char *mapname = sv.mapname; if (!mapname || !*mapname) mapname = "Unnamed_Level"; for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) buffer[i] = ' '; memcpy (buffer, mapname, min(strlen(mapname), 21)); snprintf (kills, sizeof (kills), "kills:%3i/%-3i", (int)PR_GLOBAL(killed_monsters), (int)PR_GLOBAL(total_monsters)); memcpy (buffer + 22, kills, strlen(kills)); // convert space to _ to make stdio happy for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) if (buffer[i] == ' ') buffer[i] = '_'; buffer[SAVEGAME_COMMENT_LENGTH] = 0; }
/* ============ SV_PushMove ============ */ void SV_PushMove (edict_t *pusher, float movetime) { int i, e; edict_t *check, *block; vec3_t mins, maxs, move; vec3_t entorig, pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) { pusher->v.ltime += movetime; return; } for (i=0 ; i<3 ; i++) { move[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorCopy (pusher->v.origin, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definately be moved if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher)) { if (check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2]) continue; // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.origin); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { PR_GLOBAL(self) = EDICT_TO_PROG(pusher); PR_GLOBAL(other) = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i=0 ; i<num_moved ; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); SV_LinkEdict (moved_edict[i], false); } return; } } }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is only called from the SV_Map_f() function. ================ */ void SV_SpawnServer (char *mapname, qbool devmap, char* entityfile) { extern func_t ED_FindFunctionOffset (char *name); edict_t *ent; int i; extern cvar_t sv_loadentfiles, sv_loadentfiles_dir; char *entitystring; char oldmap[MAP_NAME_LEN]; extern qbool sv_allow_cheats; extern cvar_t sv_cheats, sv_paused, sv_bigcoords; #ifndef SERVERONLY extern void CL_ClearState (void); #endif // store old map name snprintf (oldmap, MAP_NAME_LEN, "%s", sv.mapname); Con_DPrintf ("SpawnServer: %s\n",mapname); #ifndef SERVERONLY // As client+server we do it here. // As serveronly we do it in NET_Init(). NET_InitServer(); #endif SV_SaveSpawnparms (); SV_LoadAccounts(); #ifdef USE_PR2 // remove bot clients for (i = 0; i < MAX_CLIENTS; i++) { if( sv_vm && svs.clients[i].isBot ) { svs.clients[i].old_frags = 0; svs.clients[i].edict->v.frags = 0.0; svs.clients[i].name[0] = 0; svs.clients[i].state = cs_free; Info_RemoveAll(&svs.clients[i]._userinfo_ctx_); Info_RemoveAll(&svs.clients[i]._userinfoshort_ctx_); SV_FullClientUpdate(&svs.clients[i], &sv.reliable_datagram); svs.clients[i].isBot = 0; } } #endif // Shutdown game. PR_GameShutDown(); PR_UnLoadProgs(); svs.spawncount++; // any partially connected client will be restarted #ifndef SERVERONLY com_serveractive = false; #endif sv.state = ss_dead; sv.paused = false; Cvar_SetROM(&sv_paused, "0"); Host_ClearMemory(); #ifdef FTE_PEXT_FLOATCOORDS if (sv_bigcoords.value) { msg_coordsize = 4; msg_anglesize = 2; } else { msg_coordsize = 2; msg_anglesize = 1; } #endif if ((int)coop.value) Cvar_Set (&deathmatch, "0"); current_skill = (int) (skill.value + 0.5); if (current_skill < 0) current_skill = 0; Cvar_Set (&skill, va("%d", current_skill)); if (current_skill > 3) current_skill = 3; if ((sv_cheats.value || devmap) && !sv_allow_cheats) { sv_allow_cheats = true; Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); } else if ((!sv_cheats.value && !devmap) && sv_allow_cheats) { sv_allow_cheats = false; Info_SetValueForStarKey (svs.info, "*cheats", "", MAX_SERVERINFO_STRING); } // wipe the entire per-level structure // NOTE: this also set sv.mvdrecording to false, so calling SV_MVD_Record() at end of function memset (&sv, 0, sizeof(sv)); sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.data = sv.datagram_buf; sv.datagram.allowoverflow = true; sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); sv.reliable_datagram.data = sv.reliable_datagram_buf; sv.multicast.maxsize = sizeof(sv.multicast_buf); sv.multicast.data = sv.multicast_buf; sv.signon.maxsize = sizeof(sv.signon_buffers[0]); sv.signon.data = sv.signon_buffers[0]; sv.num_signon_buffers = 1; sv.time = 1.0; // load progs to get entity field count // which determines how big each edict is // and allocate edicts PR_LoadProgs (); #ifdef WITH_NQPROGS PR_InitPatchTables(); #endif PR_InitProg(); for (i = 0; i < MAX_EDICTS; i++) { ent = EDICT_NUM(i); ent->e = &sv.sv_edicts[i]; // assigning ->e field in each edict_t ent->e->entnum = i; ent->e->area.ed = ent; // yeah, pretty funny, but this help to find which edict_t own this area (link_t) } fofs_items2 = ED_FindFieldOffset ("items2"); // ZQ_ITEMS2 extension fofs_maxspeed = ED_FindFieldOffset ("maxspeed"); fofs_gravity = ED_FindFieldOffset ("gravity"); fofs_movement = ED_FindFieldOffset ("movement"); fofs_vw_index = ED_FindFieldOffset ("vw_index"); fofs_hideentity = ED_FindFieldOffset ("hideentity"); fofs_trackent = ED_FindFieldOffset ("trackent"); // find optional QC-exported functions. // we have it here, so we set it to NULL in case of PR2 progs. mod_SpectatorConnect = ED_FindFunctionOffset ("SpectatorConnect"); mod_SpectatorThink = ED_FindFunctionOffset ("SpectatorThink"); mod_SpectatorDisconnect = ED_FindFunctionOffset ("SpectatorDisconnect"); mod_ChatMessage = ED_FindFunctionOffset ("ChatMessage"); mod_UserInfo_Changed = ED_FindFunctionOffset ("UserInfo_Changed"); mod_ConsoleCmd = ED_FindFunctionOffset ("ConsoleCmd"); mod_UserCmd = ED_FindFunctionOffset ("UserCmd"); mod_localinfoChanged = ED_FindFunctionOffset ("localinfoChanged"); GE_ClientCommand = ED_FindFunctionOffset ("GE_ClientCommand"); GE_PausedTic = ED_FindFunctionOffset ("GE_PausedTic"); GE_ShouldPause = ED_FindFunctionOffset ("GE_ShouldPause"); // leave slots at start for clients only sv.num_edicts = MAX_CLIENTS+1; for (i=0 ; i<MAX_CLIENTS ; i++) { ent = EDICT_NUM(i+1); // restore client name. ent->v.netname = PR_SetString(svs.clients[i].name); // reserve edict. svs.clients[i].edict = ent; //ZOID - make sure we update frags right svs.clients[i].old_frags = 0; } // fill sv.mapname and sv.modelname with new map name strlcpy (sv.mapname, mapname, sizeof(sv.mapname)); snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", sv.mapname); #ifndef SERVERONLY // set cvar Cvar_ForceSet (&host_mapname, mapname); #endif if (!(sv.worldmodel = CM_LoadMap (sv.modelname, false, &sv.map_checksum, &sv.map_checksum2))) // true if bad map { Con_Printf ("Cant load map %s, falling back to %s\n", mapname, oldmap); // fill mapname, sv.mapname and sv.modelname with old map name strlcpy (sv.mapname, oldmap, sizeof(sv.mapname)); snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", sv.mapname); mapname = oldmap; // and re-load old map sv.worldmodel = CM_LoadMap (sv.modelname, false, &sv.map_checksum, &sv.map_checksum2); // this should never happen if (!sv.worldmodel) SV_Error ("CM_LoadMap: bad map"); } sv.map_checksum2 = Com_TranslateMapChecksum (sv.mapname, sv.map_checksum2); SV_ClearWorld (); // clear physics interaction links #ifdef USE_PR2 if ( sv_vm ) { sv.sound_precache[0] = ""; sv.model_precache[0] = ""; } else #endif { sv.sound_precache[0] = pr_strings; sv.model_precache[0] = pr_strings; } sv.model_precache[1] = sv.modelname; sv.models[1] = sv.worldmodel; for (i=1 ; i< CM_NumInlineModels() ; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = CM_InlineModel (localmodels[i]); } //check player/eyes models for hacks sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); sv.model_newplayer_checksum = SV_CheckModel("progs/newplayer.mdl"); sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; #ifndef SERVERONLY com_serveractive = true; #endif ent = EDICT_NUM(0); ent->e->free = false; ent->v.model = PR_SetString(sv.modelname); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; // information about the server ent->v.netname = PR_SetString(VersionStringFull()); ent->v.targetname = PR_SetString(SERVER_NAME); ent->v.impulse = VERSION_NUM; ent->v.items = pr_numbuiltins - 1; PR_GLOBAL(mapname) = PR_SetString(sv.mapname); // serverflags are for cross level information (sigils) PR_GLOBAL(serverflags) = svs.serverflags; if (pr_nqprogs) { pr_globals[35] = deathmatch.value; pr_globals[36] = coop.value; pr_globals[37] = teamplay.value; NQP_Reset (); } if (pr_nqprogs) { // register the cvars that NetQuake provides for mod use const char **var, *nqcvars[] = {"gamecfg", "scratch1", "scratch2", "scratch3", "scratch4", "saved1", "saved2", "saved3", "saved4", "savedgamecfg", "temp1", NULL}; for (var = nqcvars; *var; var++) Cvar_Create((char *)/*stupid const warning*/ *var, "0", 0); } // run the frame start qc function to let progs check cvars if (!pr_nqprogs) SV_ProgStartFrame (); // ********* External Entity support (.ent file(s) in gamedir/maps) pinched from ZQuake ********* // load and spawn all other entities entitystring = NULL; if ((int)sv_loadentfiles.value) { char ent_path[1024] = {0}; if (!entityfile || !entityfile[0]) entityfile = sv.mapname; // first try maps/sv_loadentfiles_dir/ if (sv_loadentfiles_dir.string[0]) { snprintf(ent_path, sizeof(ent_path), "maps/%s/%s.ent", sv_loadentfiles_dir.string, entityfile); entitystring = (char *) FS_LoadHunkFile(ent_path, NULL); } // try maps/ if not loaded yet. if (!entitystring) { snprintf(ent_path, sizeof(ent_path), "maps/%s.ent", entityfile); entitystring = (char *) FS_LoadHunkFile(ent_path, NULL); } if (entitystring) { Con_DPrintf ("Using entfile %s\n", ent_path); } } if (!entitystring) { entitystring = CM_EntityString(); } PR_LoadEnts(entitystring); // ********* End of External Entity support code ********* // look up some model indexes for specialized message compression SV_FindModelNumbers (); // all spawning is completed, any further precache statements // or prog writes to the signon message are errors sv.state = ss_active; // run two frames to allow everything to settle SV_Physics (); sv.time += 0.1; SV_Physics (); sv.time += 0.1; sv.old_time = sv.time; // save movement vars SV_SetMoveVars(); // create a baseline for more efficient communications SV_CreateBaseline (); sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; Info_SetValueForKey (svs.info, "map", sv.mapname, MAX_SERVERINFO_STRING); // calltimeofday. { extern void PF_calltimeofday (void); pr_global_struct->time = sv.time; pr_global_struct->self = 0; PF_calltimeofday(); } Con_DPrintf ("Server spawned.\n"); // we change map - clear whole demo struct and sent initial state to all dest if any (for QTV only I thought) SV_MVD_Record(NULL, true); #ifndef SERVERONLY CL_ClearState (); #endif }
/* ============ SV_CycleWeaponReverse (JDH: copy of weapons.qc function) ============ */ void SV_CycleWeaponReverse (edict_t *ent) { int it, weapon; qboolean has_ammo; dfunction_t *func; it = (int) ent->v.items; weapon = (int) ent->v.weapon; ent->v.impulse = 0; while (1) { has_ammo = true; switch (weapon) { case IT_LIGHTNING: weapon = IT_ROCKET_LAUNCHER; if (ent->v.ammo_rockets < 1) has_ammo = false; break; case IT_ROCKET_LAUNCHER: weapon = IT_GRENADE_LAUNCHER; if (ent->v.ammo_rockets < 1) has_ammo = false; break; case IT_GRENADE_LAUNCHER: weapon = IT_SUPER_NAILGUN; if (ent->v.ammo_nails < 2) has_ammo = false; break; case IT_SUPER_NAILGUN: weapon = IT_NAILGUN; if (ent->v.ammo_nails < 1) has_ammo = false; break; case IT_NAILGUN: weapon = IT_SUPER_SHOTGUN; if (ent->v.ammo_shells < 2) has_ammo = false; break; case IT_SUPER_SHOTGUN: weapon = IT_SHOTGUN; if (ent->v.ammo_shells < 1) has_ammo = false; break; case IT_SHOTGUN: weapon = IT_AXE; break; case IT_AXE: weapon = IT_LIGHTNING; if (ent->v.ammo_cells < 1) has_ammo = false; break; } if ((it & weapon) && has_ammo) { func = PR_FindFunction ("W_SetCurrentAmmo", PRFF_NOBUILTINS); if (func) { // W_SetCurrentAmmo usually has no params, but for lthsp2-lthsp5 // it expects "self" as an argument if (func->numparms == 1) ((int *)pr_globals)[OFS_PARM0] = PR_GLOBAL(self); ent->v.weapon = weapon; PR_ExecuteProgram (func - pr_functions); } return; } } };
/* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { client_t *cl; vec3_t v; qboolean was_angle_set; cl = &svs.clients[num-1]; if (!cl->active) return; // unconnected slot // call standard client pre-think PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); PR_ExecuteProgram (PR_GLOBAL(PlayerPreThink)); // for cutscene hack (see below) if (isDedicated || (num != 1)) was_angle_set = false; // do it on local client only else was_angle_set = (ent->v.fixangle != 0); // do a move SV_CheckVelocity (ent); // decide which move function to call switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink(ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink(ent)) return; if (!SV_CheckWater(ent) && !((int)ent->v.flags & FL_WATERJUMP)) SV_AddGravity (ent); SV_CheckStuck (ent); SV_WalkMove (ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: #ifdef HEXEN2_SUPPORT case MOVETYPE_SWIM: #endif if (!SV_RunThink(ent)) return; SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink(ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // JDH: hack for cutscenes made by Darin McNeil's Cutscene Construction Kit: // (note that the extra precision is noticeable only if the viewangles // are sent from server to client as 2-byte values; hence the addition // of the new svc_setpreciseangle message code) if (was_angle_set && (ent->v.view_ofs[2] == 0) && host_cutscenehack.value && !strcmp (pr_strings + ent->v.classname, "camera")) { // - when camera changes back to player, classname remains "camera" for // 1 frame, but movedir is no longer valid. So as an additional check, // I verify that view_ofs[2] is still 0 // - early version(s?) of Cutscene Construction Kit don't move the camera, // so movedir is not used. I determine the version by checking *when* // the viewangle is set: early version does it in the .think function; // later ones in PlayerPreThink. was_angle_set will be true only if // it was changed in PlayerPreThink //if (!sv_oldprotocol.value) { v[0] = ent->v.movedir[0] - ent->v.origin[0]; v[1] = ent->v.movedir[1] - ent->v.origin[1]; v[2] = ent->v.origin[2] - ent->v.movedir[2]; //vectoangles (v, ent->v.angles); vectoangles (v, cl->cutscene_viewangles); } if (!cl->in_cutscene) { int i; edict_t *ed; // by this time, the player's viewangles have already been changed. // But the dummy entity spawned in place of the player has the values for (i = 1 ; i < sv.num_edicts ; i++) { // get the current server version ed = EDICT_NUM(i); if (ed->free) continue; if (!strcmp(pr_strings + ed->v.classname, "dummy")) { VectorCopy (ed->v.angles, cl->prev_viewangles); break; } } cl->in_cutscene = true; } //sv.found_cutscene = true; } else { if (cl->in_cutscene) { // I'm not sure why, but last viewangle while in_cutscene isn't final angle ent->v.fixangle = 1; VectorCopy (cl->prev_viewangles, ent->v.angles); cl->in_cutscene = false; } } SV_LinkEdict (ent, true); PR_GLOBAL(time) = sv.time; PR_GLOBAL(self) = EDICT_TO_PROG(ent); // JDH: another hack, this time for progs that lack CycleWeaponReverse if ((ent->v.impulse == 12.0) && ((sv_imp12hack.value >= 2) || (sv_imp12hack.value && !pr_handles_imp12)) && !ent->v.deadflag && (ent->v.view_ofs[0] || ent->v.view_ofs[1] || ent->v.view_ofs[2])) { eval_t *val = GETEDICTFIELD(ent, eval_attack_finished); if (val && (sv.time >= val->_float)) { SV_CycleWeaponReverse (ent); } } // call standard player post-think PR_ExecuteProgram (PR_GLOBAL(PlayerPostThink)); }
/* ================ SV_Physics ================ */ void SV_Physics (void) { int i; edict_t *ent; // let the progs know that a new frame has started #ifdef HEXEN2_SUPPORT edict_t *ent2; vec3_t oldOrigin, oldAngle; int originMoved, c; #endif PR_GLOBAL(self) = EDICT_TO_PROG(sv.edicts); PR_GLOBAL(other) = EDICT_TO_PROG(sv.edicts); PR_GLOBAL(time) = sv.time; PR_ExecuteProgram (PR_GLOBAL(StartFrame)); //SV_CheckAllEnts (); // treat each object in turn ent = sv.edicts; for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; #ifndef RQM_SV_ONLY if (!isDedicated && ((i+1) % 100 == 0)) S_ExtraUpdateTime (); // BJP: Improve sound when many entities #endif #ifdef HEXEN2_SUPPORT if (hexen2) { ent2 = PROG_TO_EDICT (ent->v.movechain); if (ent2 != sv.edicts) { VectorCopy (ent->v.origin, oldOrigin); VectorCopy (ent->v.angles, oldAngle); } } #endif if (pr_global_ptrs.force_retouch && *pr_global_ptrs.force_retouch) SV_LinkEdict (ent, true); // force retouch even for stationary if (i > 0 && i <= svs.maxclients) SV_Physics_Client (ent, i); else if (ent->v.movetype == MOVETYPE_PUSH) SV_Physics_Pusher (ent); else if (ent->v.movetype == MOVETYPE_NONE) SV_Physics_None (ent); else if (ent->v.movetype == MOVETYPE_NOCLIP) SV_Physics_Noclip (ent); else if (ent->v.movetype == MOVETYPE_STEP) SV_Physics_Step (ent); #ifdef HEXEN2_SUPPORT else if ((hexen2) && (ent->v.movetype == MOVETYPE_PUSHPULL)) SV_Physics_Step (ent); #endif else if (ent->v.movetype == MOVETYPE_TOSS || ent->v.movetype == MOVETYPE_BOUNCE #ifdef HEXEN2_SUPPORT || ((hexen2) && ((ent->v.movetype == MOVETYPE_BOUNCEMISSILE) || (ent->v.movetype == MOVETYPE_SWIM))) #endif || ent->v.movetype == MOVETYPE_FLY || ent->v.movetype == MOVETYPE_FLYMISSILE) SV_Physics_Toss (ent); else Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); #ifdef HEXEN2_SUPPORT if ((hexen2) && (ent2 != sv.edicts)) { originMoved = !VectorCompare(ent->v.origin, oldOrigin); if (originMoved || !VectorCompare(ent->v.angles, oldAngle)) { VectorSubtract(ent->v.origin, oldOrigin, oldOrigin); VectorSubtract(ent->v.angles, oldAngle, oldAngle); for (c=0; c<10; c++) { // chain a max of 10 objects if (ent2->free) break; VectorAdd(oldOrigin, ent2->v.origin, ent2->v.origin); if ((int) ent2->v.flags & FL_MOVECHAIN_ANGLE) { VectorAdd(oldAngle, ent2->v.angles, ent2->v.angles); } if (originMoved && ent2->v.chainmoved) { // callback function *pr_global_ptrs.self = EDICT_TO_PROG(ent2); *pr_global_ptrs.other = EDICT_TO_PROG(ent); PR_ExecuteProgram(ent2->v.chainmoved); } ent2 = PROG_TO_EDICT( ent2->v.movechain ); if (ent2 == sv.edicts) break; } } } #endif } if (pr_global_ptrs.force_retouch && *pr_global_ptrs.force_retouch) PR_GLOBAL(force_retouch)--; sv.time += host_frametime; }