/** * @todo Use this function to enable footsteps over network * @note Only play the water sounds if actor is not in STATE_CROUCHED mode * we can assume, that moving slower and more carefully would also not produce * the water sounds */ void G_PhysicsStep (edict_t *ent) { /** * @todo don't play foot step sounds for flying units. */ if (ent->moveinfo.currentStep < ent->moveinfo.steps) { const int contentFlags = ent->contentFlags; const vismask_t visflags = ent->moveinfo.visflags[ent->moveinfo.currentStep]; /* Send the sound effect to everyone how's not seeing the actor */ if (!G_IsCrouched(ent)) { if (contentFlags & CONTENTS_WATER) { if (ent->moveinfo.contentFlags[ent->moveinfo.currentStep] & CONTENTS_WATER) { /* looks like we already are in the water */ /* send water moving sound */ G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_under"); } else { /* send water entering sound */ G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_in"); } } else if (ent->contentFlags & CONTENTS_WATER) { /* send water leaving sound */ G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_out"); } else { trace_t trace; vec3_t from, to; VectorCopy(ent->origin, to); VectorCopy(ent->origin, from); /* we should really hit the ground with this */ to[2] -= UNIT_HEIGHT; trace = G_Trace(from, to, NULL, MASK_SOLID); if (trace.surface) { const char *snd = gi.GetFootstepSound(trace.surface->name); if (snd) G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, snd); } } } /* and now save the new contents */ ent->contentFlags = ent->moveinfo.contentFlags[ent->moveinfo.currentStep]; ent->moveinfo.currentStep++; /* Immediately re-think */ ent->nextthink = (level.framenum + 3) * SERVER_FRAME_SECONDS; } else { ent->moveinfo.currentStep = 0; ent->moveinfo.steps = 0; ent->think = NULL; } }
static bool Destroy_Breakable (edict_t *self) { vec3_t origin; const char *model = self->model; VectorCenterFromMinsMaxs(self->absmin, self->absmax, origin); /* the HP value is used to decide whether this was a triggered call or a * call during a fight - a triggered call will be handled differently in * terms of timing and the related particle effects in the client code */ if (self->HP == 0) G_EventModelExplodeTriggered(self); else G_EventModelExplode(self); if (self->particle) G_SpawnParticle(origin, self->spawnflags, self->particle); switch (self->material) { case MAT_GLASS: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakglass+"); break; case MAT_METAL: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakmetal+"); break; case MAT_ELECTRICAL: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakelectric+"); break; case MAT_WOOD: G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakwood+"); break; case MAT_MAX: break; } G_TouchEdicts(self, 10.0f); /* destroy the door trigger */ if (self->child) G_FreeEdict(self->child); /* now we can destroy the edict completely */ G_FreeEdict(self); AABB oldAABB(vec3_origin, vec3_origin); gi.GetInlineModelAABB(model, oldAABB); GridBox rerouteOldBox(oldAABB); G_RecalcRouting(model, rerouteOldBox); return true; }
/** * @brief Hurt trigger * @sa SP_trigger_hurt */ bool Touch_HurtTrigger (Edict* self, Edict* activator) { /* Dead actors should really not be able to trigger this - they can't be hurt anymore anyway */ if (!G_IsLivingActor(activator)) return false; /* If no damage is dealt don't count it as triggered */ const int damage = G_ApplyProtection(activator, self->dmgtype, self->dmg); if (damage == 0) return false; const bool stunEl = (self->dmgtype == gi.csi->damStunElectro); const bool stunGas = (self->dmgtype == gi.csi->damStunGas); const bool shock = (self->dmgtype == gi.csi->damShock); const bool isRobot = activator->chr.teamDef->robot; Actor* actor = makeActor(activator); if (stunEl || (stunGas && !isRobot)) { actor->addStun(damage); } else if (shock) { /** @todo Handle dazed via trigger_hurt */ } else { G_TakeDamage(actor, damage); } /* Play hurt sound unless this is shock damage -- it doesn't do anything * because we don't actually handle it above yet */ if (!shock) { const teamDef_t* teamDef = activator->chr.teamDef; const int gender = activator->chr.gender; const char* sound = teamDef->getActorSound(gender, SND_HURT); G_EventSpawnSound(G_PlayerToPM(activator->getPlayer()), *activator, nullptr, sound); } G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage); return true; }
/** * @brief Opens/closes a door * @note Use function for func_door * @todo Check if the door can be opened or closed - there should not be * anything in the way (e.g. an actor) */ static bool Door_Use (edict_t *door, edict_t *activator) { if (door->doorState == STATE_CLOSED) { door->doorState = STATE_OPENED; /* change rotation/origin and relink */ if (door->type == ET_DOOR) { if (door->dir & DOOR_OPEN_REVERSE) door->angles[door->dir & 3] -= DOOR_ROTATION_ANGLE; else door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE; } else if (door->type == ET_DOOR_SLIDING) { Door_SlidingUse(door); } gi.LinkEdict(door); /* maybe the server called this because the door starts opened */ if (G_MatchIsRunning()) { /* let everybody know, that the door opens */ G_EventDoorOpen(door); if (door->noise[0] != '\0') G_EventSpawnSound(PM_ALL, false, door, door->origin, door->noise); } } else if (door->doorState == STATE_OPENED) { door->doorState = STATE_CLOSED; /* change rotation and relink */ if (door->type == ET_DOOR) { if (door->dir & DOOR_OPEN_REVERSE) door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE; else door->angles[door->dir & 3] -= DOOR_ROTATION_ANGLE; } else if (door->type == ET_DOOR_SLIDING) { Door_SlidingUse(door); } gi.LinkEdict(door); /* closed is the standard, opened is handled above - we need an active * team here already */ if (G_MatchIsRunning()) { /* let everybody know, that the door closes */ G_EventDoorClose(door); if (door->noise[0] != '\0') G_EventSpawnSound(PM_ALL, false, door, door->origin, door->noise); } } else return false; /* Update model orientation */ gi.SetInlineModelOrientation(door->model, door->origin, door->angles); Com_DPrintf(DEBUG_GAME, "Server processed door movement.\n"); /* Update path finding table */ G_RecalcRouting(door->model); if (activator && G_IsLivingActor(activator)) { /* Check if the player appears/perishes, seen from other teams. */ G_CheckVis(activator, true); /* Calc new vis for the activator. */ G_CheckVisTeamAll(activator->team, false, activator); } return true; }
/** * @brief Opens/closes a door * @note Use function for func_door * @todo Check if the door can be opened or closed - there should not be * anything in the way (e.g. an actor) */ static bool Door_Use (edict_t *door, edict_t *activator) { int opening = 1; if (door->doorState == STATE_CLOSED) { door->doorState = STATE_OPENED; opening = 1; } else if (door->doorState == STATE_OPENED) { door->doorState = STATE_CLOSED; opening = -1; } else return false; /* remember the old location */ AABB oldAABB; gi.GetInlineModelAABB(door->model, oldAABB); GridBox rerouteOldBox(oldAABB); /* change rotation and relink */ if (door->type == ET_DOOR) { if (door->dir & DOOR_OPEN_REVERSE) opening *= -1; door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE * opening; } else if (door->type == ET_DOOR_SLIDING) { Door_SlidingUse(door); } gi.LinkEdict(door); /* maybe the server called this because the door starts opened */ if (G_MatchIsRunning()) { /* let everybody know, that the door moves */ if (door->doorState == STATE_OPENED) G_EventDoorOpen(door); else G_EventDoorClose(door); if (door->noise[0] != '\0') { const playermask_t playerMask = G_GetClosePlayerMask(door->origin, UNIT_SIZE * 10); G_EventSpawnSound(playerMask, false, door, door->origin, door->noise); } } /* Update model orientation */ gi.SetInlineModelOrientation(door->model, door->origin, door->angles); AABB newAabb; gi.GetInlineModelAABB(door->model, newAabb); GridBox rerouteNewBox(newAabb); Com_DPrintf(DEBUG_GAME, "Server processed door movement.\n"); /* Update path finding table for the new location of the model */ G_RecalcRouting(door->model, rerouteOldBox); /* Update path finding table */ G_RecalcRouting(door->model, rerouteNewBox); if (activator && G_IsLivingActor(activator)) { /* Check if the player appears/perishes, seen from other teams. */ G_CheckVis(activator); /* Calc new vis for the activator. */ G_CheckVisTeamAll(activator->team, 0, activator); } return true; }
/** * @brief Deals splash damage to a target and its surroundings. * @param[in] ent The shooting actor * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is. * @param[in] impact The impact vector where the grenade is exploding * @param[in,out] mock pseudo shooting - only for calculating mock values - nullptr for real shots * @param[in] tr The trace where the grenade hits something (or not) */ static void G_SplashDamage (Actor* ent, const fireDef_t* fd, vec3_t impact, shot_mock_t* mock, const trace_t* tr) { assert(fd->splrad > 0.0f); const bool shock = (fd->obj->dmgtype == gi.csi->damShock); Edict* check = nullptr; while ((check = G_EdictsGetNextInUse(check))) { /* If we use a blinding weapon we skip the target if it's looking * away from the impact location. */ if (shock && !G_FrustumVis(check, impact)) continue; const bool isActor = G_IsLivingActor(check); vec3_t center; if (G_IsBrushModel(check) && G_IsBreakable(check)) check->absBox.getCenter(center); else if (isActor || G_IsBreakable(check)) VectorCopy(check->origin, center); else continue; /* check for distance */ float dist = VectorDist(impact, center); dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0.0f; if (dist > fd->splrad) continue; if (fd->irgoggles) { if (isActor) { /* check whether this actor (check) is in the field of view of the 'shooter' (ent) */ if (G_FrustumVis(ent, check->origin)) { if (!mock) { vec3_t eyeEnt; G_ActorGetEyeVector(ent, eyeEnt); if (!G_SmokeVis(eyeEnt, check)) { const unsigned int playerMask = G_TeamToPM(ent->getTeam()) ^ G_VisToPM(check->visflags); G_AppearPerishEvent(playerMask, true, *check, ent); G_VisFlagsAdd(*check, G_PMToVis(playerMask)); } } } } continue; } /* check for walls */ if (isActor && G_TestLine(impact, check->origin)) continue; /* do damage */ const int damage = shock ? 0 : fd->spldmg[0] * (1.0f - dist / fd->splrad); if (mock) mock->allow_self = true; /* Send hurt sounds for actors, but only if they'll recieve damage from this attack */ if (G_Damage(check, fd, damage, ent, mock, nullptr) && isActor && (G_ApplyProtection(check, fd->dmgweight, damage) > 0) && !shock) { const teamDef_t* teamDef = check->chr.teamDef; const int gender = check->chr.gender; const char* sound = teamDef->getActorSound(gender, SND_HURT); G_EventSpawnSound(G_VisToPM(check->visflags), *check, nullptr, sound); } if (mock) mock->allow_self = false; } /** @todo splash might also hit other surfaces and the trace doesn't handle that */ if (tr && G_FireAffectedSurface(tr->surface, fd)) { /* move a little away from the impact vector */ VectorMA(impact, 1, tr->plane.normal, impact); G_SpawnParticle(impact, tr->contentFlags >> 8, "burning"); }