/* * G_ClientCorpse */ static void G_ClientCorpse(g_edict_t *self) { const float f = frand(); g_edict_t *ent = G_Spawn(); ent->class_name = "corpse"; ent->move_type = MOVE_TYPE_TOSS; ent->solid = SOLID_NOT; VectorScale(PM_MINS, PM_SCALE, ent->mins); VectorScale(PM_MAXS, PM_SCALE, ent->maxs); VectorCopy(self->velocity, ent->velocity); memcpy(&ent->s, &self->s, sizeof(ent->s)); ent->s.number = ent - g_game.edicts; ent->s.model2 = 0; ent->s.model3 = 0; ent->s.model4 = 0; if (f < 0.33) G_SetAnimation(ent, ANIM_BOTH_DEATH1, true); else if (f < 0.66) G_SetAnimation(ent, ANIM_BOTH_DEATH2, true); else G_SetAnimation(ent, ANIM_BOTH_DEATH3, true); ent->think = G_ClientCorpse_think; ent->next_think = g_level.time + 1.0; gi.LinkEntity(ent); }
/* * G_ClientRespawn_ * * The grunt work of putting the client into the server on [re]spawn. */ static void G_ClientRespawn_(g_edict_t *ent) { vec3_t spawn_origin, spawn_angles, old_angles; float height; g_client_t *cl; g_client_persistent_t locals; int i; // find a spawn point G_SelectSpawnPoint(ent, spawn_origin, spawn_angles); cl = ent->client; // retain last angles for delta VectorCopy(ent->client->cmd_angles, old_angles); // reset inventory, health, etc G_InitClientLocals(cl); // clear everything but locals locals = cl->persistent; memset(cl, 0, sizeof(*cl)); cl->persistent = locals; // clear entity values VectorScale(PM_MINS, PM_SCALE, ent->mins); VectorScale(PM_MAXS, PM_SCALE, ent->maxs); height = ent->maxs[2] - ent->mins[2]; ent->ground_entity = NULL; ent->take_damage = true; ent->move_type = MOVE_TYPE_WALK; ent->view_height = ent->mins[2] + (height * 0.75); ent->class_name = "player"; ent->mass = 200.0; ent->solid = SOLID_BOX; ent->dead = false; ent->clip_mask = MASK_PLAYER_SOLID; ent->pain = G_ClientPain; ent->die = G_ClientDie; ent->water_level = 0; ent->water_type = 0; ent->sv_flags = 0; // copy these back once they have been set in locals ent->health = ent->client->persistent.health; ent->max_health = ent->client->persistent.max_health; VectorClear(ent->velocity); ent->velocity[2] = 150.0; // clear player state values memset(&ent->client->ps, 0, sizeof(cl->ps)); cl->ps.pmove.origin[0] = spawn_origin[0] * 8.0; cl->ps.pmove.origin[1] = spawn_origin[1] * 8.0; cl->ps.pmove.origin[2] = spawn_origin[2] * 8.0; // clear entity state values ent->s.effects = 0; ent->s.model1 = 0xff; // use the client info model ent->s.model2 = 0; ent->s.model3 = 0; ent->s.model4 = 0; ent->s.client = ent - g_game.edicts - 1; G_SetAnimation(ent, ANIM_TORSO_STAND1, true); G_SetAnimation(ent, ANIM_LEGS_JUMP1, true); VectorCopy(spawn_origin, ent->s.origin); VectorCopy(ent->s.origin, ent->s.old_origin); // set the delta angle of the spawn point for (i = 0; i < 3; i++) { cl->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - old_angles[i]); } VectorClear(cl->cmd_angles); VectorClear(cl->angles); VectorClear(ent->s.angles); // spawn a spectator if (cl->persistent.spectator) { cl->chase_target = NULL; cl->persistent.weapon = NULL; cl->persistent.team = NULL; cl->persistent.ready = false; ent->move_type = MOVE_TYPE_NO_CLIP; ent->solid = SOLID_NOT; ent->sv_flags |= SVF_NO_CLIENT; ent->take_damage = false; gi.LinkEntity(ent); return; } // or spawn a player ent->s.event = EV_TELEPORT; // hold in place briefly cl->ps.pmove.pm_flags = PMF_TIME_TELEPORT; cl->ps.pmove.pm_time = 20; cl->persistent.match_num = g_level.match_num; cl->persistent.round_num = g_level.round_num; gi.UnlinkEntity(ent); G_KillBox(ent); // telefrag anyone in our spot gi.LinkEntity(ent); // force the current weapon up cl->new_weapon = cl->persistent.weapon; G_ChangeWeapon(ent); }
/* * @brief Sets the animation sequences for the specified entity. This is called * towards the end of each frame, after our ground entity and water level have * been resolved. */ static void G_ClientAnimation(g_entity_t *ent) { if (ent->s.model1 != MODEL_CLIENT) return; // corpses animate to their final resting place if (ent->solid == SOLID_DEAD) { if (g_level.time >= ent->client->locals.respawn_time) { switch (ent->s.animation1) { case ANIM_BOTH_DEATH1: case ANIM_BOTH_DEATH2: case ANIM_BOTH_DEATH3: G_SetAnimation(ent, ent->s.animation1 + 1, false); break; default: break; } } return; } // no-clippers do not animate if (ent->locals.move_type == MOVE_TYPE_NO_CLIP) { G_SetAnimation(ent, ANIM_TORSO_STAND1, false); G_SetAnimation(ent, ANIM_LEGS_JUMP1, false); return; } // check for falling g_client_locals_t *cl = &ent->client->locals; if (!ent->locals.ground_entity) { // not on the ground if (g_level.time - cl->jump_time > 400) { if (ent->locals.water_level == 3 && cl->speed > 10.0) { // swimming G_SetAnimation(ent, ANIM_LEGS_SWIM, false); return; } if (ent->client->ps.pm_state.flags & PMF_DUCKED) { // ducking G_SetAnimation(ent, ANIM_LEGS_IDLECR, false); return; } } _Bool jumping = G_IsAnimation(ent, ANIM_LEGS_JUMP1); jumping |= G_IsAnimation(ent, ANIM_LEGS_JUMP2); if (!jumping) G_SetAnimation(ent, ANIM_LEGS_JUMP1, false); return; } // duck, walk or run after landing if (g_level.time - 400 > cl->land_time && g_level.time - 50 > cl->ground_time) { if (ent->client->ps.pm_state.flags & PMF_DUCKED) { // ducked if (cl->speed < 1.0) G_SetAnimation(ent, ANIM_LEGS_IDLECR, false); else G_SetAnimation(ent, ANIM_LEGS_WALKCR, false); return; } if (cl->speed < 1.0 && !cl->cmd.forward && !cl->cmd.right && !cl->cmd.up) { G_SetAnimation(ent, ANIM_LEGS_IDLE, false); return; } vec3_t angles, forward; VectorSet(angles, 0.0, ent->s.angles[YAW], 0.0); AngleVectors(angles, forward, NULL, NULL); if (DotProduct(ent->locals.velocity, forward) < -0.1) G_SetAnimation(ent, ANIM_LEGS_BACK, false); else if (cl->speed < 200.0) G_SetAnimation(ent, ANIM_LEGS_WALK, false); else G_SetAnimation(ent, ANIM_LEGS_RUN, false); return; } }
/* * G_ClientThink * * This will be called once for each client frame, which will usually be a * couple times for each server frame. */ void G_ClientThink(g_edict_t *ent, user_cmd_t *ucmd) { g_client_t *client; g_edict_t *other; int i, j; g_level.current_entity = ent; client = ent->client; if (g_level.intermission_time) { client->ps.pmove.pm_type = PM_FREEZE; return; } if (client->chase_target) { // ensure chase is valid client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; if (!client->chase_target->in_use || client->chase_target->client->persistent.spectator) { other = client->chase_target; G_ChaseNext(ent); if (client->chase_target == other) { // no one to chase client->chase_target = NULL; } } } if (!client->chase_target) { // set up for pmove pm_move_t pm; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; if (ent->move_type == MOVE_TYPE_NO_CLIP) client->ps.pmove.pm_type = PM_SPECTATOR; else if (ent->s.model1 != 255 || ent->dead) client->ps.pmove.pm_type = PM_DEAD; else client->ps.pmove.pm_type = PM_NORMAL; client->ps.pmove.gravity = g_level.gravity; memset(&pm, 0, sizeof(pm)); pm.s = client->ps.pmove; for (i = 0; i < 3; i++) { pm.s.origin[i] = ent->s.origin[i] * 8.0; pm.s.velocity[i] = ent->velocity[i] * 8.0; } pm.cmd = *ucmd; pm.Trace = G_ClientMoveTrace; // adds default params pm.PointContents = gi.PointContents; // perform a pmove gi.Pmove(&pm); // save results of pmove client->ps.pmove = pm.s; for (i = 0; i < 3; i++) { ent->s.origin[i] = pm.s.origin[i] * 0.125; ent->velocity[i] = pm.s.velocity[i] * 0.125; } VectorCopy(pm.mins, ent->mins); VectorCopy(pm.maxs, ent->maxs); client->cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); // check for jump, play randomized sound if (ent->ground_entity && !pm.ground_entity && (pm.cmd.up >= 10) && (pm.water_level == 0) && client->jump_time < g_level.time - 0.2) { vec3_t angles, forward, velocity; float speed; VectorSet(angles, 0.0, ent->s.angles[YAW], 0.0); AngleVectors(angles, forward, NULL, NULL); VectorCopy(ent->velocity, velocity); velocity[2] = 0.0; speed = VectorNormalize(velocity); if (DotProduct(velocity, forward) < 0.0 && speed > 200.0) G_SetAnimation(ent, ANIM_LEGS_JUMP2, true); else G_SetAnimation(ent, ANIM_LEGS_JUMP1, true); ent->s.event = EV_CLIENT_JUMP; client->jump_time = g_level.time; } ent->view_height = pm.view_height; ent->water_level = pm.water_level; ent->water_type = pm.water_type; ent->ground_entity = pm.ground_entity; if (ent->ground_entity) ent->ground_entity_link_count = ent->ground_entity->link_count; VectorCopy(pm.angles, client->angles); VectorCopy(pm.angles, client->ps.angles); gi.LinkEntity(ent); // touch jump pads, hurt brushes, etc.. if (ent->move_type != MOVE_TYPE_NO_CLIP && ent->health > 0) G_TouchTriggers(ent); // touch other objects for (i = 0; i < pm.num_touch; i++) { other = pm.touch_ents[i]; for (j = 0; j < i; j++) if (pm.touch_ents[j] == other) break; if (j != i) continue; // duplicated if (!other->touch) continue; other->touch(other, ent, NULL, NULL); } } client->old_buttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->old_buttons; // fire weapon if requested if (client->latched_buttons & BUTTON_ATTACK) { if (client->persistent.spectator) { client->latched_buttons = 0; if (client->chase_target) { // toggle chase camera client->chase_target = NULL; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; } else { G_ChaseTarget(ent); } } else if (client->weapon_think_time < g_level.time) { G_WeaponThink(ent); } } // update chase camera if being followed for (i = 1; i <= sv_max_clients->integer; i++) { other = g_game.edicts + i; if (other->in_use && other->client->chase_target == ent) { G_ChaseThink(other); } } G_ClientInventoryThink(ent); }
/* * @brief Sets the animation sequences for the specified entity. This is called * towards the end of each frame, after our ground entity and water level have * been resolved. */ static void G_ClientAnimation(g_edict_t *ent) { if (ent->sv_flags & SVF_NO_CLIENT) return; // no-clippers do not animate if (ent->locals.move_type == MOVE_TYPE_NO_CLIP) { G_SetAnimation(ent, ANIM_TORSO_STAND1, false); G_SetAnimation(ent, ANIM_LEGS_JUMP1, false); return; } // check for falling g_client_locals_t *cl = &ent->client->locals; if (!ent->locals.ground_entity) { // not on the ground if (g_level.time - cl->jump_time > 400) { if (ent->locals.water_level == 3 && cl->speed > 10.0) { // swimming G_SetAnimation(ent, ANIM_LEGS_SWIM, false); return; } if (ent->client->ps.pm_state.flags & PMF_DUCKED) { // ducking G_SetAnimation(ent, ANIM_LEGS_IDLECR, false); return; } } _Bool jumping = G_IsAnimation(ent, ANIM_LEGS_JUMP1); jumping |= G_IsAnimation(ent, ANIM_LEGS_JUMP2); if (!jumping) G_SetAnimation(ent, ANIM_LEGS_JUMP1, false); return; } // duck, walk or run after landing if (g_level.time - 400 > cl->land_time && g_level.time - 50 > cl->ground_time) { if (ent->client->ps.pm_state.flags & PMF_DUCKED) { // ducked if (cl->speed < 1.0) G_SetAnimation(ent, ANIM_LEGS_IDLECR, false); else G_SetAnimation(ent, ANIM_LEGS_WALKCR, false); return; } if (cl->speed < 1.0 && !cl->cmd.forward && !cl->cmd.right && !cl->cmd.up) { G_SetAnimation(ent, ANIM_LEGS_IDLE, false); return; } vec3_t angles, forward; VectorSet(angles, 0.0, ent->s.angles[YAW], 0.0); AngleVectors(angles, forward, NULL, NULL); if (DotProduct(ent->locals.velocity, forward) < -0.1) G_SetAnimation(ent, ANIM_LEGS_BACK, false); else if (cl->speed < 200.0) G_SetAnimation(ent, ANIM_LEGS_WALK, false); else G_SetAnimation(ent, ANIM_LEGS_RUN, false); return; } }