/** * @note Think function */ static void LET_Projectile (le_t* le) { if (cl.time >= le->endTime) { vec3_t impact; VectorCopy(le->origin, impact); CL_ParticleFree(le->ptl); /* don't run the think function again */ le->inuse = false; if (Q_strvalid(le->ref1)) { VectorCopy(le->ptl->s, impact); le->ptl = CL_ParticleSpawn(le->ref1, 0, impact, bytedirs[le->angle]); VecToAngles(bytedirs[le->state], le->ptl->angles); } if (Q_strvalid(le->ref2)) { S_LoadAndPlaySample(le->ref2, impact, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (le->ref3) { /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */ /** @todo Special particles for stun attack (mind you that there is * electrical and gas/chemical stunning)? */ if (le->fd->obj->dmgtype != csi.damStunGas) LE_ActorBodyHit(le->ref3, impact, le->angle); CL_ActorPlaySound(le->ref3, SND_HURT); } } else if (CL_OutsideMap(le->ptl->s, UNIT_SIZE * 10)) { le->endTime = cl.time; CL_ParticleFree(le->ptl); /* don't run the think function again */ le->inuse = false; } }
/** * @brief Free a particle and all it's children * @param[in] p the particle to free * @sa CL_ParticleSpawn */ void CL_ParticleFree (ptl_t *p) { ptl_t *c; p->inuse = false; p->invis = true; for (c = p->children; c; c = c->next) { CL_ParticleFree(c); } }
/** * @brief Called whenever an entity disappears from view * @sa CL_EntAppear */ void CL_EntPerish (const eventRegister_t *self, struct dbuffer *msg) { int entnum; int type; le_t *le, *actor; NET_ReadFormat(msg, self->formatString, &entnum, &type); le = LE_Get(entnum); if (!le) LE_NotFoundWithTypeError(entnum, type); switch (le->type) { case ET_ITEM: cls.i.EmptyContainer(&cls.i, &le->i, INVDEF(csi.idFloor)); /* search owners (there can be many, some of them dead) */ actor = NULL; while ((actor = LE_GetNextInUse(actor))) { if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2) && VectorCompare(actor->pos, le->pos)) { Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: le of type ET_ITEM hidden\n"); FLOOR(actor) = NULL; } } break; case ET_ACTOR: case ET_ACTOR2x2: cls.i.DestroyInventory(&cls.i, &le->i); break; #ifdef DEBUG case ET_ACTORHIDDEN: Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: It should not happen that we perish a hidden actor\n"); return; #endif case ET_PARTICLE: CL_ParticleFree(le->ptl); le->ptl = NULL; break; case ET_BREAKABLE: case ET_DOOR: case ET_DOOR_SLIDING: break; default: break; } le->invis = qtrue; /* decrease the count of spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); }
/** * @brief checks whether a particle is still active in the current round * @note also calls the round function of each particle (if defined) * @sa CL_ParticleFunction */ void CL_ParticleCheckRounds (void) { ptl_t *p; int i; for (i = 0, p = r_particleArray; i < r_numParticles; i++, p++) if (p->inuse) { /* run round function */ CL_ParticleFunction(p, p->ctrl->round); if (p->rounds) { p->roundsCnt--; if (p->roundsCnt <= 0) CL_ParticleFree(p); } } }
/** * @brief Revitalizes a stunned actor (all that is needed is the local entity state set). * @param[in] msg The netchannel message * @param[in] self Pointer to the event structure that is currently executed */ void CL_ActorRevitalised (const eventRegister_t* self, dbuffer* msg) { int entnum, state; NET_ReadFormat(msg, self->formatString, &entnum, &state); /* get les */ le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsStunned(le) && !LE_IsLivingActor(le)) Com_Error(ERR_DROP, "CL_ActorRevitalised: Can't revitalise, LE is not a dead or stunned actor"); LE_Lock(le); /* link any floor container into the actor temp floor container */ le_t* floor = LE_Find(ET_ITEM, le->pos); if (floor) le->setFloor(floor); le->state = state; /* play animation */ LE_SetThink(le, LET_StartIdle); /* Print some info. */ if (le->team == cls.team) { const character_t* chr = CL_ActorGetChr(le); if (chr) { char tmpbuf[128]; Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was revitalised\n"), chr->name); HUD_DisplayMessage(tmpbuf); } } else { switch (le->team) { case (TEAM_CIVILIAN): HUD_DisplayMessage(_("A civilian was revitalised.")); break; case (TEAM_ALIEN): HUD_DisplayMessage(_("An alien was revitalised.")); break; case (TEAM_PHALANX): HUD_DisplayMessage(_("A soldier was revitalised.")); break; default: HUD_DisplayMessage(va(_("A member of team %i was revitalised."), le->team)); break; } } le->aabb.setMaxs(player_maxs); if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } /* add team members to the actor list */ CL_ActorAddToTeamList(le); /* update pathing as we maybe not can walk onto this actor anymore */ CL_ActorConditionalMoveCalc(selActor); LE_Unlock(le); }
/** * @brief Prepares the particle rendering, calculate new position, velocity and all the * other particle values that are needed to display it * @sa CL_ParticleRun * @param[in,out] p The particle to handle */ static void CL_ParticleRun2 (ptl_t *p) { /* advance time */ p->dt = cls.frametime; p->t = (cl.time - p->startTime) * 0.001f; p->lastThink += p->dt; p->lastFrame += p->dt; if (p->rounds && !p->roundsCnt) p->roundsCnt = p->rounds; /* test for end of life */ if (p->life && p->t >= p->life && !p->parent) { CL_ParticleFree(p); return; /* don't play the weather particles if a user don't want them there can * be a lot of weather particles - which might slow the computer down */ } else if (p->weather && !cl_particleweather->integer) { CL_ParticleFree(p); return; } /* kinematics */ if (p->style != STYLE_LINE) { VectorMA(p->s, 0.5 * p->dt * p->dt, p->a, p->s); VectorMA(p->s, p->dt, p->v, p->s); VectorMA(p->v, p->dt, p->a, p->v); VectorMA(p->angles, p->dt, p->omega, p->angles); } /* basic 'physics' for particles */ if (p->physics) { vec3_t mins, maxs; const float size = std::max(p->size[0], p->size[1]); if (p->hitSolid && p->bounce) { VectorCopy(p->oldV, p->v); VectorNegate(p->a, p->a); p->hitSolid = false; } /* if the particle hit a solid already and is sticking to the surface, no further * traces are needed */ if (p->hitSolid && p->stick) return; VectorSet(mins, -size, -size, -size); VectorSet(maxs, size, size, size); const trace_t tr = PTL_Trace(p, mins, maxs); /* hit something solid */ if (tr.fraction < 1.0 || tr.startsolid) { p->hitSolid = true; /* now execute the physics handler */ if (p->ctrl->physics) CL_ParticleFunction(p, p->ctrl->physics); /* let them stay on the ground until they fade out or die */ if (!p->stayalive) { CL_ParticleFree(p); return; } else if (p->bounce) { /* bounce */ vec3_t temp; VectorCopy(p->v, p->oldV); VectorScale(tr.plane.normal, -DotProduct(tr.plane.normal, p->v), temp); VectorAdd(temp, p->v, temp); VectorAdd(temp, temp, p->v); VectorNegate(p->a, p->a); } else { VectorClear(p->v); } VectorCopy(tr.endpos, p->s); } } /* run */ CL_ParticleFunction(p, p->ctrl->run); /* think */ while (p->tps && p->lastThink * p->tps >= 1) { CL_ParticleFunction(p, p->ctrl->think); p->lastThink -= 1.0 / p->tps; } /* animate */ while (p->fps && p->lastFrame * p->fps >= 1) { /* advance frame */ p->frame++; if (p->frame > p->endFrame) p->frame = 0; p->lastFrame -= 1.0 / p->fps; /* load next frame */ assert(p->pic); p->pic = CL_ParticleGetArt(p->pic->name, p->frame, ART_PIC); } /* fading */ if (p->thinkFade || p->frameFade) { const bool onlyAlpha = (p->blend == BLEND_BLEND); if (!onlyAlpha) Vector4Set(p->color, 1.0f, 1.0f, 1.0f, 1.0f); else p->color[3] = 1.0; if (p->thinkFade) CL_Fading(p->color, p->thinkFade, p->lastThink * p->tps, onlyAlpha); if (p->frameFade) CL_Fading(p->color, p->frameFade, p->lastFrame * p->fps, onlyAlpha); } /* this is useful for particles like weather effects that are on top of * some other brushes in higher level but should be visible in lower ones */ if (p->autohide) { const int z = (int)p->s[2] / UNIT_HEIGHT; if (z > cl_worldlevel->integer) { p->invis = true; return; } else if (z < 0) { CL_ParticleFree(p); return; } } /* add light to the scene */ if (VectorNotEmpty(p->lightColor)) { const float intensity = 0.5 + p->lightIntensity; if (p->lightSustain) R_AddSustainedLight(p->s, intensity * PTL_INTENSITY_TO_RADIUS, p->lightColor, p->lightSustain); else R_AddLight(p->s, intensity * PTL_INTENSITY_TO_RADIUS, p->lightColor); } /* set the new origin */ VectorCopy(p->s, p->origin); p->invis = false; }
static void CL_ParticleFunction (ptl_t *p, ptlCmd_t *cmd) { int stackIdx; ptrdiff_t e; int type; int i, j, n; void *cmdData; float arg; ptl_t *pnew; /* test for null cmd */ if (!cmd) return; /* run until finding PC_END */ for (stackIdx = 0, e = 0; cmd->cmd != PC_END; cmd++) { if (cmd->ref > RSTACK) cmdData = CL_ParticleCommandGetDataLocation(p, cmd); else { if (!stackIdx) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); /* pop an element off the stack */ e = (byte *) stackPtr[--stackIdx] - cmdStack; i = RSTACK - cmd->ref; if (!i) { /* normal stack reference */ cmdData = stackPtr[stackIdx]; cmd->type = stackType[stackIdx]; } else { /* stack reference to element of vector */ if ((1 << stackType[stackIdx]) & V_VECS) { cmd->type = V_FLOAT; cmdData = (float *) stackPtr[stackIdx] + (i - 1); } else { Com_Error(ERR_DROP, "CL_ParticleFunction: can't get components of a non-vector type (particle %s)", p->ctrl->name); } } } switch (cmd->cmd) { case PC_PUSH: /* check for stack overflow */ if (stackIdx >= MAX_STACK_DEPTH) Com_Error(ERR_DROP, "CL_ParticleFunction: stack overflow"); /* store the value in the stack */ stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; e += Com_SetValue(stackPtr[stackIdx++], cmdData, (valueTypes_t)cmd->type, 0, 0); break; case PC_POP: case PC_KPOP: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); /* get pics and models */ if (offsetof(ptl_t, pic) == -cmd->ref) { if (stackType[--stackIdx] != V_STRING) Com_Error(ERR_DROP, "Bad type '%s' for pic (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); p->pic = CL_ParticleGetArt((char *) stackPtr[stackIdx], p->frame, ART_PIC); e = (byte *) stackPtr[stackIdx] - cmdStack; break; } if (offsetof(ptl_t, model) == -cmd->ref) { if (stackType[--stackIdx] != V_STRING) Com_Error(ERR_DROP, "Bad type '%s' for model (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); p->model = CL_ParticleGetArt((char *) stackPtr[stackIdx], p->frame, ART_MODEL); e = (byte *) stackPtr[stackIdx] - cmdStack; break; } if (offsetof(ptl_t, program) == -cmd->ref) { if (stackType[--stackIdx] != V_STRING) Com_Error(ERR_DROP, "Bad type '%s' for program (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); p->program = R_LoadProgram((char *) stackPtr[stackIdx], R_InitParticleProgram, R_UseParticleProgram); if (p->program) p->program->userdata = p; e = (byte *) stackPtr[stackIdx] - cmdStack; break; } /* get different data */ if (cmd->cmd == PC_POP) e -= Com_SetValue(cmdData, stackPtr[--stackIdx], (valueTypes_t)cmd->type, 0, 0); else Com_SetValue(cmdData, stackPtr[stackIdx - 1], (valueTypes_t)cmd->type, 0, 0); break; case PC_ADD: case PC_SUB: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); type = stackType[stackIdx - 1]; if (!((1 << type) & V_VECS)) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); /* float based vector addition */ if (type != cmd->type) Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for add/sub (particle %s)", p->ctrl->name); n = type - V_FLOAT + 1; for (i = 0; i < n; i++) { if (cmd->cmd == PC_SUB) arg = -(*((float *) cmdData + i)); else arg = *((float *) cmdData + i); *((float *) stackPtr[stackIdx - 1] + i) += arg; } break; case PC_MUL: case PC_DIV: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); type = stackType[stackIdx - 1]; if (!((1 << type) & V_VECS)) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); n = type - V_FLOAT + 1; if (type > V_FLOAT && cmd->type > V_FLOAT) { /* component wise multiplication */ if (type != cmd->type) Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name); for (i = 0; i < n; i++) { if (cmd->cmd == PC_DIV) arg = 1.0 / (*((float *) cmdData + i)); else arg = *((float *) cmdData + i); *((float *) stackPtr[stackIdx - 1] + i) *= arg; } break; } if (cmd->type > V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name); /* scalar multiplication with scalar in second argument */ if (cmd->cmd == PC_DIV) arg = 1.0 / (*(float *) cmdData); else arg = *(float *) cmdData; for (i = 0; i < n; i++) *((float *) stackPtr[stackIdx - 1] + i) *= arg; break; case PC_SIN: if (cmd->type != V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for sin (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI)); e += sizeof(float); break; case PC_COS: if (cmd->type != V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for cos (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI)); e += sizeof(float); break; case PC_TAN: if (cmd->type != V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for tan (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI)); e += sizeof(float); break; case PC_RAND: case PC_CRAND: stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; n = cmd->type - V_FLOAT + 1; if (cmd->cmd == PC_RAND) for (i = 0; i < n; i++) *((float *) stackPtr[stackIdx] + i) = *((float *) cmdData + i) * frand(); else for (i = 0; i < n; i++) *((float *) stackPtr[stackIdx] + i) = *((float *) cmdData + i) * crand(); e += n * sizeof(float); stackIdx++; break; case PC_V2: case PC_V3: case PC_V4: n = cmd->cmd - PC_V2 + 2; j = 0; if (stackIdx < n) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); for (i = 0; i < n; i++) { if (!((1 << stackType[--stackIdx]) & V_VECS)) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for vector creation (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); j += stackType[stackIdx] - V_FLOAT + 1; } if (j > 4) Com_Error(ERR_DROP, "CL_ParticleFunction: created vector with dim > 4 (particle %s)", p->ctrl->name); stackType[stackIdx++] = V_FLOAT + j - 1; break; case PC_KILL: CL_ParticleFree(p); return; case PC_SPAWN: pnew = CL_ParticleSpawn((const char *) cmdData, p->levelFlags, p->s, p->v, p->a); if (!pnew) Com_Printf("PC_SPAWN: Could not spawn child particle for '%s' (%s)\n", p->ctrl->name, (const char *) cmdData); break; case PC_TNSPAWN: /* check for stack underflow */ if (stackIdx < 2) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); /* pop elements off the stack */ /* amount of timed particles */ type = stackType[--stackIdx]; if (type != V_INT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); n = *(int *) stackPtr[stackIdx]; /* delta time */ type = stackType[--stackIdx]; if (type != V_INT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); i = *(int *) stackPtr[stackIdx]; /** @todo make the children boolean configurable */ CL_ParticleSpawnTimed((const char *) cmdData, p, true, i, n); e -= 2 * sizeof(int); break; case PC_NSPAWN: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); type = stackType[--stackIdx]; if (type != V_INT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for nspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); n = *(int *) stackPtr[stackIdx]; e -= sizeof(int); for (i = 0; i < n; i++) { pnew = CL_ParticleSpawn((const char *) cmdData, p->levelFlags, p->s, p->v, p->a); if (!pnew) Com_Printf("PC_NSPAWN: Could not spawn child particle for '%s'\n", p->ctrl->name); } break; case PC_CHILD: pnew = CL_ParticleSpawn((const char *)cmdData, p->levelFlags, p->s, p->v, p->a); if (pnew) { pnew->next = p->children; pnew->parent = p; p->children = pnew; } else { Com_Printf("PC_CHILD: Could not spawn child particle for '%s'\n", p->ctrl->name); } break; default: Com_Error(ERR_DROP, "CL_ParticleFunction: unknown cmd type %i", cmd->type); break; } } }
/** * @brief Called whenever an entity disappears from view * @sa CL_EntAppear */ void CL_EntPerish (const eventRegister_t* self, dbuffer* msg) { int entnum; int type; NET_ReadFormat(msg, self->formatString, &entnum, &type); le_t* le = LE_Get(entnum); if (!le) LE_NotFoundWithTypeError(entnum, type); le_t* actor = nullptr; switch (le->type) { case ET_ITEM: cls.i.emptyContainer(&le->inv, CID_FLOOR); /* search owners (there can be many, some of them dead) */ while ((actor = LE_GetNextInUse(actor))) { if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2) && VectorCompare(actor->pos, le->pos)) { Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: le of type ET_ITEM hidden\n"); actor->resetFloor(); } } break; case ET_ACTOR: case ET_ACTOR2x2: if (!cls.isOurRound() && le->team != TEAM_CIVILIAN) LE_CenterView(le); cls.i.destroyInventory(&le->inv); if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } /* Clear anim data to prevent actor "jumping" to new animation when it reappears, or worse animation issues. */ OBJZERO(le->as); break; #ifdef DEBUG case ET_ACTORHIDDEN: Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: It should not happen that we perish a hidden actor\n"); return; #endif case ET_PARTICLE: if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } else { Com_Printf("CL_EntPerish: Particle is nullptr for entnum %i!\n", entnum); } break; case ET_BREAKABLE: case ET_DOOR: case ET_DOOR_SLIDING: break; default: break; } le->flags |= LE_INVISIBLE; /* decrease the count of spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); Cvar_SetValue("mn_numaliensspotted", cl.numEnemiesSpotted); }
/** * @brief Kills an actor (all that is needed is the local entity state set to STATE_DEAD). * @note Also changes the animation to a random death sequence and appends the dead animation * @param[in] msg The netchannel message * @param[in] self Pointer to the event structure that is currently executed */ void CL_ActorDie (const eventRegister_t* self, dbuffer* msg) { int entnum, state, playerNum, attacker; NET_ReadFormat(msg, self->formatString, &entnum, &state, &playerNum, &attacker); /* get les */ le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsLivingActor(le)) Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, LE is not an actor (type: %i)", le->type); if (!LE_IsStunned(le) && LE_IsDead(le)) Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, actor already dead"); LE_Lock(le); /* set relevant vars */ le->resetFloor(); le->state = state; /* count spotted aliens */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); Cvar_SetValue("mn_numaliensspotted", cl.numEnemiesSpotted); /* play animation */ LE_SetThink(le, nullptr); R_AnimChange(&le->as, le->model1, va("death%i", LE_GetAnimationIndexForDeath(le))); R_AnimAppend(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le))); /* Print some info about the death or stun. */ if (le->team == cls.team) { if (playerNum != le->pnum) { const char* playerName = CL_PlayerGetName(playerNum); char tmpbuf[128]; Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s lost a soldier\n"), playerName); HUD_DisplayMessage(tmpbuf); } else { const character_t* chr = CL_ActorGetChr(le); if (chr) { char tmpbuf[128]; if (LE_IsStunned(le)) { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was stunned\n"), chr->name); } else if (!attacker) { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s has died\n"), chr->name); } else { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was killed\n"), chr->name); } HUD_DisplayMessage(tmpbuf); } } } else { switch (le->team) { case (TEAM_CIVILIAN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A civilian was stunned.")); else if (!attacker) HUD_DisplayMessage(_("A civilian has died.")); else HUD_DisplayMessage(_("A civilian was killed.")); break; case (TEAM_ALIEN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("An alien was stunned.")); else if (!attacker) HUD_DisplayMessage(_("An alien has died.")); else HUD_DisplayMessage(_("An alien was killed.")); break; case (TEAM_PHALANX): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A soldier was stunned.")); else if (!attacker) HUD_DisplayMessage(_("A soldier has died.")); else HUD_DisplayMessage(_("A soldier was killed.")); break; default: if (LE_IsStunned(le)) HUD_DisplayMessage(va(_("A member of team %i was stunned."), le->team)); else if (!attacker) HUD_DisplayMessage(va(_("A member of team %i has died."), le->team)); else HUD_DisplayMessage(va(_("A member of team %i was killed."), le->team)); break; } } /** * @todo CHRSH_IsTeamDefRobot: spawn smoke particles for robots */ CL_ActorPlaySound(le, SND_DEATH); le->aabb.setMaxs(player_dead_maxs); if (!LE_IsStunned(le)) le->contents = CONTENTS_DEADACTOR; if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } CL_ActorRemoveFromTeamList(le); /* update pathing as we maybe can walk onto the dead actor now */ CL_ActorConditionalMoveCalc(selActor); LE_Unlock(le); }