#include "g_Skeletons.h" #include "g_teleport.h" #include "m_player.h" //#include "p_types.h" #include "player.h" #include "p_animactor.h" #include "p_anims.h" #include "p_ctrl.h" #include "p_funcs.h" #include "p_main.h" #include "p_weapon.h" #include "angles.h" #include "fx.h" #include "random.h" #include "vector.h" #include "utilities.h" #include "g_playstats.h" static edict_t *current_player; static gclient_t *current_client; float xyspeed; float bobmove; int bobcycle; // odd cycles are right foot going forward float bobfracsin; // sin(bobfrac*M_PI) extern void Cmd_WeapPrev_f (edict_t *ent); extern void Cmd_Use_f (edict_t *ent, char *s); extern void PrintLocalBuoyInfo(vec3_t org); // ** setup a looping sound on the client void G_set_looping_sound(edict_t *self, int sound_num) { self->s.sound = sound_num; self->s.sound_data = (255 & ENT_VOL_MASK) | ATTN_NORM; } // ************************************************************************************************ // ClientServerRand // ---------------- // ************************************************************************************************ static int ClientServerRand(playerinfo_t *playerinfo,int mn,int mx) { int t; if (mn>=mx) return(mn); t=(int)(playerinfo->leveltime*10.0f); t=(t>>7)^(t>>10)^(t>>5); t%=(1+mx-mn); return(t+mn); } // ************************************************************************************************ // InitPlayerinfo // -------------- // ************************************************************************************************ void InitPlayerinfo(edict_t *ent) { // Client side function callbacks (approximating functionality of server function callbacks). ent->client->playerinfo.CL_Sound=NULL; ent->client->playerinfo.CL_Trace=NULL; ent->client->playerinfo.CL_CreateEffect=NULL; // Server (game) function callbacks (approximating functionality of client-side function callbacks). ent->client->playerinfo.G_L_Sound=G_set_looping_sound; ent->client->playerinfo.G_Sound=gi.soundevent; ent->client->playerinfo.G_Trace=gi.trace; ent->client->playerinfo.G_CreateEffect=gi.CreateEffectEvent; ent->client->playerinfo.G_RemoveEffects=gi.RemoveEffectsEvent; // Server (game) function callbacks that have no client side equivalent. ent->client->playerinfo.G_SoundIndex=gi.soundindex; ent->client->playerinfo.G_SoundRemove=gi.soundremove; ent->client->playerinfo.G_UseTargets=G_UseTargets; ent->client->playerinfo.G_GetEntityStatePtr=G_GetEntityStatePtr; ent->client->playerinfo.G_BranchLwrClimbing=G_BranchLwrClimbing; ent->client->playerinfo.G_PlayerActionCheckRopeGrab=G_PlayerActionCheckRopeGrab; ent->client->playerinfo.G_PlayerClimbingMoveFunc=G_PlayerClimbingMoveFunc; ent->client->playerinfo.G_PlayerActionUsePuzzle=G_PlayerActionUsePuzzle; ent->client->playerinfo.G_PlayerActionCheckPuzzleGrab=G_PlayerActionCheckPuzzleGrab; ent->client->playerinfo.G_PlayerActionTakePuzzle=G_PlayerActionTakePuzzle; ent->client->playerinfo.G_PlayerActionCheckPushPull_Ent=G_PlayerActionCheckPushPull_Ent; ent->client->playerinfo.G_PlayerActionMoveItem=G_PlayerActionMoveItem; ent->client->playerinfo.G_PlayerActionCheckPushButton=G_PlayerActionCheckPushButton; ent->client->playerinfo.G_PlayerActionPushButton=G_PlayerActionPushButton; ent->client->playerinfo.G_PlayerActionCheckPushLever=G_PlayerActionCheckPushLever; ent->client->playerinfo.G_PlayerActionPushLever=G_PlayerActionPushLever; ent->client->playerinfo.G_HandleTeleport=G_HandleTeleport; ent->client->playerinfo.G_PlayerActionShrineEffect=G_PlayerActionShrineEffect; ent->client->playerinfo.G_PlayerActionChickenBite=G_PlayerActionChickenBite; ent->client->playerinfo.G_PlayerFallingDamage=G_PlayerFallingDamage; ent->client->playerinfo.G_PlayerSpellShieldAttack=G_PlayerSpellShieldAttack; ent->client->playerinfo.G_PlayerSpellStopShieldAttack=G_PlayerSpellStopShieldAttack; ent->client->playerinfo.G_PlayerVaultKick = G_PlayerVaultKick; ent->client->playerinfo.G_PlayerActionCheckRopeMove=G_PlayerActionCheckRopeMove; ent->client->playerinfo.G_gamemsg_centerprintf=gi.gamemsg_centerprintf; ent->client->playerinfo.G_levelmsg_centerprintf=gi.levelmsg_centerprintf; ent->client->playerinfo.G_WeapNext=Cmd_WeapPrev_f; ent->client->playerinfo.G_UseItem=Cmd_Use_f; // Common client & server (game) function callbacks. ent->client->playerinfo.PointContents=gi.pointcontents; ent->client->playerinfo.SetJointAngles=G_SetJointAngles; ent->client->playerinfo.ResetJointAngles=G_ResetJointAngles; ent->client->playerinfo.PlayerActionSwordAttack=G_PlayerActionSwordAttack; ent->client->playerinfo.PlayerActionSpellFireball=G_PlayerActionSpellFireball; ent->client->playerinfo.PlayerActionSpellBlast=G_PlayerActionSpellBlast; ent->client->playerinfo.PlayerActionSpellArray=G_PlayerActionSpellArray; ent->client->playerinfo.PlayerActionSpellSphereCreate=G_PlayerActionSpellSphereCreate; ent->client->playerinfo.PlayerActionSpellBigBall=G_PlayerActionSpellBigBall; ent->client->playerinfo.PlayerActionSpellFirewall=G_PlayerActionSpellFirewall; ent->client->playerinfo.PlayerActionRedRainBowAttack=G_PlayerActionRedRainBowAttack; ent->client->playerinfo.PlayerActionPhoenixBowAttack=G_PlayerActionPhoenixBowAttack; ent->client->playerinfo.PlayerActionHellstaffAttack=G_PlayerActionHellstaffAttack; ent->client->playerinfo.PlayerActionSpellDefensive=G_PlayerActionSpellDefensive; ent->client->playerinfo.G_EntIsAButton=G_EntIsAButton; ent->client->playerinfo.irand=ClientServerRand; // So we know we are server (game) side. ent->client->playerinfo.isclient=false; ent->client->playerinfo.ishistory=false; } // ************************************************************************************************ // SetupPlayerinfo // --------------- // ************************************************************************************************ void SetupPlayerinfo(edict_t *ent) { int i; // ******************************************************************************************** // Inputs only. // ******************************************************************************************** // Pointer to the associated player's edict_t. ent->client->playerinfo.self=ent; // Game .dll variables. ent->client->playerinfo.leveltime=level.time; // Server variables. ent->client->playerinfo.sv_gravity=sv_gravity->value; ent->client->playerinfo.sv_cinematicfreeze=sv_cinematicfreeze->value; ent->client->playerinfo.sv_jumpcinematic=sv_jumpcinematic->value; // From edict_t. ent->client->playerinfo.ideal_yaw=ent->ideal_yaw; ent->client->playerinfo.groundentity=ent->groundentity; // Pointer to entity_state_t of player's enemy edict. if(ent->enemy) ent->client->playerinfo.enemystate=&ent->enemy->s; else ent->client->playerinfo.enemystate=NULL; // Spell / weapon aiming direction. VectorCopy(ent->client->aimangles,ent->client->playerinfo.aimangles); // Deathmatch flags - only set this if we are in death match. if (deathmatch->value) ent->client->playerinfo.dmflags = DF_DEATHMATCH_SET | (int)dmflags->value; // Send the high bit if deathmatch. else ent->client->playerinfo.dmflags=0; ent->client->playerinfo.advancedstaff = (int)(advancedstaff->value); // ******************************************************************************************** // Inputs & outputs. // ******************************************************************************************** // If we are in a cinematic, remove certain commands from the ucmd_t the server received from // the client. NOTE: THIS IS HIGHLY SUBJECTIVE. REQUIRES VIGOUROUS TESTING. // Basically, just killing all buttons pressed while a cinematic is running - Probably not the best way to do this // Jake 9/28/98 // need to reget this constantly, since it changes on the fly. memcpy(&ent->client->playerinfo.pcmd,&ent->client->pcmd,sizeof(usercmd_t)); if (sv_cinematicfreeze->value) { ent->client->pcmd.buttons = 0; ent->client->pcmd.sidemove = 0; ent->client->pcmd.forwardmove = 0; ent->client->playerinfo.pcmd.buttons = 0; ent->client->playerinfo.pcmd.sidemove = 0; ent->client->playerinfo.pcmd.forwardmove = 0; ent->client->playerinfo.buttons = 0; ent->client->playerinfo.remember_buttons = 0; } // From edict_t. VectorCopy(ent->s.origin,ent->client->playerinfo.origin); VectorCopy(ent->s.angles,ent->client->playerinfo.angles); VectorCopy(ent->velocity,ent->client->playerinfo.velocity); VectorCopy(ent->mins,ent->client->playerinfo.mins); VectorCopy(ent->maxs,ent->client->playerinfo.maxs); ent->client->playerinfo.enemy=ent->enemy; ent->client->playerinfo.target=ent->target; ent->client->playerinfo.targetEnt=ent->targetEnt; ent->client->playerinfo.target_ent=ent->target_ent; ent->client->playerinfo.nextthink=ent->nextthink; ent->client->playerinfo.viewheight=ent->viewheight; ent->client->playerinfo.watertype=ent->watertype; ent->client->playerinfo.waterlevel=ent->waterlevel; ent->client->playerinfo.deadflag=ent->deadflag; ent->client->playerinfo.movetype=ent->movetype; ent->client->playerinfo.edictflags=ent->flags; // From entity_state_t. ent->client->playerinfo.frame=ent->s.frame, ent->client->playerinfo.swapFrame=ent->s.swapFrame; ent->client->playerinfo.effects=ent->s.effects; ent->client->playerinfo.renderfx=ent->s.renderfx; ent->client->playerinfo.skinnum=ent->s.skinnum; ent->client->playerinfo.clientnum=ent->s.clientnum; for(i=0;iclient->playerinfo.fmnodeinfo[i]=ent->s.fmnodeinfo[i]; } // From pmove_state_t. ent->client->playerinfo.pm_flags=ent->client->ps.pmove.pm_flags; ent->client->playerinfo.pm_w_flags=ent->client->ps.pmove.w_flags; } // ************************************************************************************************ // WritePlayerinfo // --------------- // ************************************************************************************************ void WritePlayerinfo(edict_t *ent) { int i; // ******************************************************************************************** // Inputs & outputs. // ******************************************************************************************** memcpy(&ent->client->pcmd,&ent->client->playerinfo.pcmd,sizeof(usercmd_t)); // From edict_t. VectorCopy(ent->client->playerinfo.origin,ent->s.origin); VectorCopy(ent->client->playerinfo.angles,ent->s.angles); VectorCopy(ent->client->playerinfo.velocity,ent->velocity); VectorCopy(ent->client->playerinfo.mins,ent->mins); VectorCopy(ent->client->playerinfo.maxs,ent->maxs); ent->enemy=ent->client->playerinfo.enemy; ent->targetEnt=ent->client->playerinfo.targetEnt; ent->target_ent=ent->client->playerinfo.target_ent; ent->target=ent->client->playerinfo.target; ent->nextthink=ent->client->playerinfo.nextthink; ent->viewheight=ent->client->playerinfo.viewheight; ent->watertype=ent->client->playerinfo.watertype; ent->waterlevel=ent->client->playerinfo.waterlevel; ent->deadflag=ent->client->playerinfo.deadflag; ent->movetype=ent->client->playerinfo.movetype; ent->flags=ent->client->playerinfo.edictflags; // From entity_state_t. ent->s.frame=ent->client->playerinfo.frame, ent->s.swapFrame=ent->client->playerinfo.swapFrame; ent->s.effects=ent->client->playerinfo.effects; ent->s.renderfx=ent->client->playerinfo.renderfx; ent->s.skinnum=ent->client->playerinfo.skinnum; ent->s.clientnum=ent->client->playerinfo.clientnum; for(i=0;is.fmnodeinfo[i]=ent->client->playerinfo.fmnodeinfo[i]; } // From pmove_state_t. ent->client->ps.pmove.pm_flags=ent->client->playerinfo.pm_flags; ent->client->ps.pmove.w_flags=ent->client->playerinfo.pm_w_flags; // ******************************************************************************************** // Outputs only. // ******************************************************************************************** // From playerstate_t. VectorCopy(ent->client->playerinfo.offsetangles,ent->client->ps.offsetangles); } // ************************************************************************************************ // SetupPlayerinfo_effects // ----------------------- // ************************************************************************************************ void SetupPlayerinfo_effects(edict_t *ent) { int i; ent->client->playerinfo.effects = ent->s.effects; ent->client->playerinfo.renderfx = ent->s.renderfx; ent->client->playerinfo.skinnum = ent->s.skinnum; ent->client->playerinfo.clientnum = ent->s.clientnum; for(i = 0; i < MAX_FM_MESH_NODES; i++) { ent->client->playerinfo.fmnodeinfo[i] = ent->s.fmnodeinfo[i]; } } // ************************************************************************************************ // WritePlayerinfo_effects // ----------------------- // ************************************************************************************************ void WritePlayerinfo_effects(edict_t *ent) { int i; ent->s.effects = ent->client->playerinfo.effects; ent->s.renderfx = ent->client->playerinfo.renderfx; ent->s.skinnum = ent->client->playerinfo.skinnum; ent->s.clientnum = ent->client->playerinfo.clientnum; for(i = 0; i < MAX_FM_MESH_NODES; i++) { ent->s.fmnodeinfo[i] = ent->client->playerinfo.fmnodeinfo[i]; } } // ************************************************************************************************ // PlayerTimerUpdate // ----------------- // Deal with incidental player stuff, like setting the personal light to OFF if its should be. // ************************************************************************************************ void PlayerTimerUpdate(edict_t *ent) { playerinfo_t *playerinfo; playerinfo=&ent->client->playerinfo; // Disable light when we should. if (playerinfo->light_timer < level.time) playerinfo->effects &= ~EF_LIGHT_ENABLED; // Disable speed when we should. if (playerinfo->speed_timer < level.time) playerinfo->effects &= ~EF_SPEED_ACTIVE; // Disable max speed when we should. if (playerinfo->knockbacktime < level.time) playerinfo->effects &= ~EF_HIGH_MAX; // Disable powerup when we should. if (playerinfo->powerup_timer < level.time) playerinfo->effects &= ~EF_POWERUP_ENABLED; // Disable relection when we should. if (playerinfo->reflect_timer < level.time) { // Were we relfective last frame? if (playerinfo->renderfx &RF_REFLECTION) { playerinfo->renderfx &= ~RF_REFLECTION; // Unset the skin. P_PlayerUpdateModelAttributes(&ent->client->playerinfo); } } // Disable ghosting when we should. if (playerinfo->ghost_timer < level.time) playerinfo->renderfx &= ~RF_TRANS_GHOST; } /* =============== P_DamageFeedback Handles color blends and view kicks =============== */ void P_DamageFeedback (edict_t *player) { gclient_t *client; int count; client=player->client; // Flash the backgrounds behind the status numbers. client->ps.stats[STAT_FLASHES]=0; if (client->damage_blood) client->ps.stats[STAT_FLASHES]|=1; // Total up the points of damage shot at the player this frame. if((count=client->damage_blood)==0) { // Didn't take any damage. return; } //Check for gasssss damage if (player->pain_debounce_time < level.time && client->damage_gas) { if ( client->playerinfo.loweridle && client->playerinfo.upperidle ) P_PlayerAnimSetLowerSeq(&client->playerinfo, ASEQ_PAIN_A); P_PlayerPlayPain(&client->playerinfo, 1); } else if (((!irand(0, 4)) || count > 8) && (player->pain_debounce_time < level.time)) // Play pain animation. { if ( client->playerinfo.loweridle && client->playerinfo.upperidle ) P_PlayerAnimSetLowerSeq(&client->playerinfo, ASEQ_PAIN_A); if (count <= 4) P_PlayerPlayPain(&client->playerinfo, 2); else P_PlayerPlayPain(&client->playerinfo, 0); player->pain_debounce_time = level.time + 0.5; } // Reset the player's pain_debounce_time. if (level.time > player->pain_debounce_time) player->pain_debounce_time = level.time + 0.7; // Clear damage totals. client->damage_blood = 0; client->damage_knockback = 0; } /* ============= P_WorldEffects ============= */ void P_WorldEffects (void) { int waterlevel,old_waterlevel; vec3_t Origin, Dir; if (current_player->client->playerinfo.deadflag > DEAD_NO) return; // If we are in no clip, or we have special lungs, we don't need air. if(current_player->movetype==PHYSICSTYPE_NOCLIP) { current_player->air_finished=level.time+HOLD_BREATH_TIME; if(current_player->movetype==PHYSICSTYPE_NOCLIP) return; } waterlevel=current_player->waterlevel; old_waterlevel=current_client->old_waterlevel; current_client->old_waterlevel=waterlevel; // // If the current player just entered a water volume, play a sound and start a water-ripple // client effect. // if(!old_waterlevel&&waterlevel) { // Clear damage_debounce, so the pain sound will play immediately. current_player->damage_debounce_time=level.time-1; if (current_player->watertype & CONTENTS_LAVA) { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/inlava.wav"), 1, ATTN_NORM, 0); current_player->flags |= FL_INLAVA; } else if (current_player->watertype & CONTENTS_SLIME) { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/muckin.wav"), 1, ATTN_NORM, 0); current_player->flags |= FL_INSLIME; } else { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/Water Enter.wav"),1,ATTN_NORM,0); } current_player->flags|=FL_INWATER; VectorCopy(current_player->s.origin,Origin); Origin[2]+=current_player->client->playerinfo.waterheight; // Fixme: Need to determine the actual water surface normal - if we have any sloping water?? Dir[0]=0.0; Dir[1]=0.0; Dir[2]=1.0; VectorCopy(Origin,current_player->client->playerinfo.LastWatersplashPos); gi.CreateEffect(NULL, FX_WATER_ENTRYSPLASH, CEF_FLAG7, Origin, "bd", 128|96, // FIXME: Size propn. to entry velocity? Dir); } else if (old_waterlevel&&!waterlevel) { // // If the current player just completely exited a water volume, play a sound. // // INWATER is set whether in lava, slime or water. if (current_player->flags & FL_INLAVA) { gi.sound (current_player, CHAN_BODY, gi.soundindex("player/inlava.wav"), 1, ATTN_NORM, 0); current_player->flags &= ~FL_INLAVA; } else if (current_player->flags & FL_INSLIME) { gi.sound (current_player, CHAN_BODY, gi.soundindex("player/muckexit.wav"), 1, ATTN_NORM, 0); current_player->flags &= ~FL_INSLIME; } else { gi.sound (current_player, CHAN_BODY, gi.soundindex("player/Water Exit.wav"), 1, ATTN_NORM, 0); } current_player->flags&=~FL_INWATER; VectorCopy(current_player->s.origin,Origin); Origin[2]=current_player->client->playerinfo.LastWatersplashPos[2]; // Fixme: Need to determine the actual water surface normal - if we have any sloping water?? Dir[0]=0.0; Dir[1]=0.0; Dir[2]=1.0; VectorCopy(Origin,current_player->client->playerinfo.LastWatersplashPos); gi.CreateEffect(NULL, FX_WATER_ENTRYSPLASH, 0, Origin, "bd", 96, // FIXME: Size propn. to exit velocity. Dir); } // // Start a waterwake effect if the current player has been in water and still is in water. // if(waterlevel && (old_waterlevel&&waterlevel<3) && (VectorLength(current_player->velocity)!=0.0)) { // no ripples while in cinematics if (sv_cinematicfreeze->value) return; if((((int)(current_client->bobtime+bobmove))!=bobcycle) && (!sv_cinematicfreeze->value)) { // FIXME: Doing more work then we need to here???? How about re-writing this so that it // is always active on the client and does water tests itself? We'll see - currently not // enough info. is available on the client to try this. vec3_t Angles, Temp; byte angle_byte; VectorCopy(current_player->velocity,Temp); VectorNormalize(Temp); vectoangles(Temp,Angles); VectorCopy(current_player->s.origin,Origin); Origin[2]+=current_player->client->playerinfo.waterheight; angle_byte = Q_ftol(((Angles[YAW] + DEGREE_180)/360.0) * 255.0); gi.CreateEffect(NULL, FX_WATER_WAKE, 0, Origin, "sbv", (short)current_player->s.number, angle_byte, current_player->velocity); } } // // Check for head just coming out of water. // if (old_waterlevel == 3 && waterlevel != 3) { if (current_player->air_finished < level.time) { // Gasp for air. if (irand(0,1)) gi.sound (current_player, CHAN_BODY, gi.soundindex("*gasp1.wav"), 1, ATTN_NORM, 0); else gi.sound (current_player, CHAN_BODY, gi.soundindex("*gasp2.wav"), 1, ATTN_NORM, 0); } else if (current_player->air_finished < level.time + 11) { // Broke surface, low on air gi.sound (current_player, CHAN_BODY, gi.soundindex("*waterresurface.wav"), 1, ATTN_NORM, 0); } } // ******************************************************************************************** // Handle drowning. // ******************************************************************************************** if (waterlevel == 3) { if(current_player->watertype & CONTENTS_SLIME) { // Slime should kill really quick. current_player->dmg=25; // Play a gurp sound instead of a normal pain sound. if (irand(0, 1)) gi.sound (current_player, CHAN_VOICE, gi.soundindex("*drowning1.wav"), 1, ATTN_NORM, 0); else gi.sound (current_player, CHAN_VOICE, gi.soundindex("*drowning2.wav"), 1, ATTN_NORM, 0); current_player->pain_debounce_time = level.time; T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_SUFFOCATION, MOD_SLIME); } else if ((current_player->air_finished + current_player->client->playerinfo.lungs_timer) < level.time) { // If out of air, start drowning. if (current_player->client->next_drown_time < level.time && current_player->health > 0) { current_player->client->next_drown_time = level.time + 1; // Take more damage the longer underwater. current_player->dmg += 2; if (current_player->dmg > 15) current_player->dmg = 15; // Play a gurp sound instead of a normal pain sound. if (irand(0, 1)) gi.sound (current_player, CHAN_VOICE, gi.soundindex("*drowning1.wav"), 1, ATTN_NORM, 0); else gi.sound (current_player, CHAN_VOICE, gi.soundindex("*drowning2.wav"), 1, ATTN_NORM, 0); current_player->pain_debounce_time = level.time; T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_SUFFOCATION, MOD_WATER); } } // else, we aren't drowning yet, but we may well need to decrement the amount of extra lungs we have from shrines // since we still have lungs, reset air finished till we don't anymore. else if (current_player->client->playerinfo.lungs_timer) { // floatinf point inaccuracy never lets us equal zero by ourselves if (current_player->client->playerinfo.lungs_timer < 0.1) current_player->client->playerinfo.lungs_timer = 0.0; else { current_player->client->playerinfo.lungs_timer -= 0.1; current_player->air_finished = level.time + HOLD_BREATH_TIME; } } if (old_waterlevel != 3) { // We weren't underwater before this, so play a submerged sound gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/underwater.wav"), 1, ATTN_IDLE, 0); } // Play an underwater sound every 4 seconds! if (((int)(level.time/4.0))*4.0 == level.time) { // Underwater ambient // Play local only? gi.sound (current_player, CHAN_BODY, gi.soundindex("player/underwater.wav"), 1, ATTN_IDLE, 0); } } else { current_player->air_finished = level.time + HOLD_BREATH_TIME; current_player->dmg = 2; } // ******************************************************************************************** // Handle lava sizzle damage. // ******************************************************************************************** if (waterlevel && (current_player->watertype&(CONTENTS_LAVA)) ) { if (current_player->health > 0 && current_player->pain_debounce_time <= level.time) { gi.sound (current_player, CHAN_VOICE, gi.soundindex("player/lavadamage.wav"), 1, ATTN_NORM, 0); current_player->pain_debounce_time = level.time + 1; } T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, (waterlevel>2)?25:(3*waterlevel), 0, DAMAGE_LAVA, MOD_LAVA); } } /* =============== G_SetClientSound =============== */ void G_SetClientSound (edict_t *ent) { char *weap; if (ent->client->playerinfo.pers.weapon) weap = ent->client->playerinfo.pers.weapon->classname; else weap = ""; // this seems like a silly thing to do ? /// ent->s.sound = 0; } // ************************************************************************************************ // ClientEndServerFrame // -------------------- // Called for each player at the end of the server frame and right after spawning. // ************************************************************************************************ void ClientEndServerFrame (edict_t *ent) { float bobtime; int i,index; current_player = ent; current_client = ent->client; // ******************************************************************************************** // If end of unit layout is displayed, don't give the player any normal movement attributes. // ******************************************************************************************** if (level.intermissiontime) { current_client->ps.fov=75; G_SetStats(ent); return; } // ******************************************************************************************** // Apply world effect, e.g. burn from lava, etc. // ******************************************************************************************** P_WorldEffects (); // ******************************************************************************************** // Set the player entity's model angles. // ******************************************************************************************** if(ent->deadflag==DEAD_NO) { // PITCH. if((ent->client->ps.pmove.w_flags & (WF_DIVING | WF_SWIMFREE))) { if(ent->client->v_angle[PITCH] > 180.0) ent->s.angles[PITCH] = -(-360.0 + ent->client->v_angle[PITCH]); else ent->s.angles[PITCH] = -ent->client->v_angle[PITCH]; } else { ent->s.angles[PITCH] = 0.0; } // YAW and ROLL. ent->s.angles[YAW] = ent->client->v_angle[YAW]; ent->s.angles[ROLL] = 0.0; } // ******************************************************************************************** // Handle calcs for cyclic effects like walking / swimming. // ******************************************************************************************** xyspeed = sqrt(ent->velocity[0] * ent->velocity[0] + ent->velocity[1] * ent->velocity[1]); if (xyspeed < 5) { // Start at beginning of cycle again. bobmove = 0; current_client->bobtime = 0; } else if(ent->groundentity && !current_player->waterlevel) { // So bobbing only cycles when on ground. if(xyspeed > 210) bobmove = 0.25; else if(xyspeed > 100) bobmove = 0.125; else bobmove = 0.0625; } else if(current_player->waterlevel) { // So bobbing only cycles when in water. if(xyspeed > 100) bobmove = 1.0; else if(xyspeed > 50) bobmove = 0.5; else bobmove = 0.25; } bobtime = (current_client->bobtime += bobmove); bobcycle = (int)bobtime; bobfracsin = Q_fabs(sin(bobtime * M_PI)); // ******************************************************************************************** // Calculate damage (if any) from hitting the floor and apply the damage taken this frame from // ALL sources. // ******************************************************************************************** SetupPlayerinfo(ent); P_PlayerFallingDamage(&ent->client->playerinfo); P_DamageFeedback(ent); WritePlayerinfo(ent); // ******************************************************************************************** // Generate client-side status display data // ******************************************************************************************** G_SetStats(ent); G_SetClientSound(ent); // ******************************************************************************************** // Handle player animation. // ******************************************************************************************** SetupPlayerinfo(ent); P_PlayerUpdateCmdFlags(&ent->client->playerinfo); if(BUOY_DEBUG) // Note this is a bit of a hack { if(ent->client->playerinfo.seqcmd[ACMDL_ACTION] == true) { PrintLocalBuoyInfo(ent->s.origin); } } P_PlayerUpdate(&ent->client->playerinfo); P_AnimUpdateFrame(&ent->client->playerinfo); PlayerTimerUpdate(ent); WritePlayerinfo(ent); // ******************************************************************************************** // Save velocity and viewangles away for use next game frame. // ******************************************************************************************** VectorCopy(ent->velocity,ent->client->playerinfo.oldvelocity); VectorCopy(ent->client->ps.viewangles,ent->client->oldviewangles); // ******************************************************************************************** // If the deathmatch scoreboard is up then update it. // ******************************************************************************************** if(ent->client->playerinfo.showscores && deathmatch->value && (!(level.framenum&31))) { DeathmatchScoreboardMessage(ent,ent->enemy,false); gi.unicast(ent,false); } // ******************************************************************************************** // Reflect remote camera views(s) in the client's playerstate. // ******************************************************************************************** if(current_client->RemoteCameraLockCount>0) current_client->ps.remote_id=current_client->RemoteCameraNumber; else current_client->ps.remote_id=-1; // ******************************************************************************************** // Reflect inventory changes in the client's playetstate. // ******************************************************************************************** current_client->ps.NoOfItems=i=0; for(index=0;indexplayerinfo.pers.inventory.Items[index]!=current_client->playerinfo.pers.old_inventory.Items[index]) { current_client->ps.inventory_changes[i]=index; current_client->ps.inventory_remaining[i]=current_client->playerinfo.pers.inventory.Items[index]; current_client->playerinfo.pers.old_inventory.Items[index]=current_client->playerinfo.pers.inventory.Items[index]; i++; } } current_client->ps.NoOfItems=i; // ******************************************************************************************** // Reflect changes to the client's origin and velocity due to the current player animation, in // the client's playerstate. // ******************************************************************************************** current_client->ps.pmove.origin[0]=ent->s.origin[0]*8.0; current_client->ps.pmove.origin[1]=ent->s.origin[1]*8.0; current_client->ps.pmove.origin[2]=ent->s.origin[2]*8.0; current_client->ps.pmove.velocity[0]=ent->velocity[0]*8.0; current_client->ps.pmove.velocity[1]=ent->velocity[1]*8.0; current_client->ps.pmove.velocity[2]=ent->velocity[2]*8.0; // ******************************************************************************************** // Reflect viewheight changes in client's playerstate. // ******************************************************************************************** current_client->ps.viewheight=ent->viewheight; // ******************************************************************************************** // Write all the shit that animation system modifies out to the playerstate (for prediction). // ******************************************************************************************** current_client->ps.maxs[0]=current_client->playerinfo.maxs[0]; current_client->ps.maxs[1]=current_client->playerinfo.maxs[1]; current_client->ps.maxs[2]=current_client->playerinfo.maxs[2]; current_client->ps.mins[0]=current_client->playerinfo.mins[0]; current_client->ps.mins[1]=current_client->playerinfo.mins[1]; current_client->ps.mins[2]=current_client->playerinfo.mins[2]; current_client->ps.NonNullgroundentity=(byte)(current_client->playerinfo.groundentity?1:0); current_client->ps.GroundPlane=current_client->playerinfo.GroundPlane; current_client->ps.GroundContents=current_client->playerinfo.GroundContents; current_client->ps.GroundSurface.flags= (current_client->playerinfo.GroundSurface!=NULL)?current_client->playerinfo.GroundSurface->flags:0; current_client->ps.watertype=current_client->playerinfo.watertype; current_client->ps.waterlevel=current_client->playerinfo.waterlevel; current_client->ps.waterheight=current_client->playerinfo.waterheight; VectorCopy(current_client->playerinfo.grabloc,current_client->ps.grabloc); current_client->ps.grabangle=current_client->playerinfo.grabangle; current_client->ps.fwdvel=current_client->playerinfo.fwdvel; current_client->ps.sidevel=current_client->playerinfo.sidevel; current_client->ps.upvel=current_client->playerinfo.upvel; current_client->ps.flags=current_client->playerinfo.flags; current_client->ps.edictflags=current_client->playerinfo.edictflags; current_client->ps.oldvelocity_z=current_client->playerinfo.oldvelocity[2]; current_client->ps.upperseq=current_client->playerinfo.upperseq; current_client->ps.lowerseq=current_client->playerinfo.lowerseq; current_client->ps.upperframe=current_client->playerinfo.upperframe; current_client->ps.lowerframe=current_client->playerinfo.lowerframe; current_client->ps.upperidle=(byte)((current_client->playerinfo.upperidle==true)?1:0); current_client->ps.loweridle=(byte)((current_client->playerinfo.loweridle==true)?1:0); current_client->ps.uppermove_index=current_client->playerinfo.uppermove_index; current_client->ps.lowermove_index=current_client->playerinfo.lowermove_index; current_client->ps.weapon=(byte)ITEM_INDEX(current_client->playerinfo.pers.weapon); current_client->ps.defense=(byte)ITEM_INDEX(current_client->playerinfo.pers.defence); current_client->ps.lastweapon=(byte)ITEM_INDEX(current_client->playerinfo.pers.lastweapon); current_client->ps.lastdefense=(byte)ITEM_INDEX(current_client->playerinfo.pers.lastdefence); current_client->ps.weaponready=(byte)current_client->playerinfo.pers.weaponready; current_client->ps.switchtoweapon=(byte)current_client->playerinfo.switchtoweapon; current_client->ps.newweapon=(byte)ITEM_INDEX(current_client->playerinfo.pers.newweapon); current_client->ps.weap_ammo_index=(byte)current_client->playerinfo.weap_ammo_index; current_client->ps.def_ammo_index=(byte)current_client->playerinfo.def_ammo_index; current_client->ps.weaponcharge=(byte)current_client->playerinfo.weaponcharge; current_client->ps.armortype=(byte)current_client->playerinfo.pers.armortype; current_client->ps.bowtype=(byte)current_client->playerinfo.pers.bowtype; current_client->ps.stafflevel=(byte)current_client->playerinfo.pers.stafflevel; current_client->ps.helltype=(byte)current_client->playerinfo.pers.helltype; current_client->ps.meteor_count=(byte)current_client->playerinfo.meteor_count; current_client->ps.handfxtype=(byte)current_client->playerinfo.pers.handfxtype; current_client->ps.plaguelevel=(byte)current_client->playerinfo.plaguelevel; current_client->ps.skintype=(byte)current_client->playerinfo.pers.skintype; current_client->ps.altparts=(byte)current_client->playerinfo.pers.altparts; current_client->ps.deadflag=current_client->playerinfo.deadflag; current_client->ps.ideal_yaw=ent->ideal_yaw; current_client->ps.leveltime=level.time; current_client->ps.idletime=current_client->playerinfo.idletime; current_client->ps.quickturnEndTime=current_client->playerinfo.quickturnEndTime; current_client->ps.powerup_timer=current_client->playerinfo.powerup_timer; current_client->ps.quickturn_rate=current_client->playerinfo.quickturn_rate; current_client->ps.dmflags=current_client->playerinfo.dmflags; current_client->ps.advancedstaff=current_client->playerinfo.advancedstaff; current_client->ps.cinematicfreeze=current_client->playerinfo.sv_cinematicfreeze; }