/** * @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 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) { le_t *le; int entnum, state, playerNum; NET_ReadFormat(msg, self->formatString, &entnum, &state, &playerNum); /* get les */ 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 */ FLOOR(le) = NULL; le->state = state; /* count spotted aliens */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); /* play animation */ LE_SetThink(le, NULL); 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 { 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 HUD_DisplayMessage(_("A civilian was killed.")); break; case (TEAM_ALIEN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("An alien was stunned.")); else HUD_DisplayMessage(_("An alien was killed.")); break; case (TEAM_PHALANX): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A soldier was stunned.")); 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 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); VectorCopy(player_dead_maxs, le->maxs); if (!LE_IsStunned(le)) le->contents = CONTENTS_DEADACTOR; CL_ActorRemoveFromTeamList(le); /* update pathing as we maybe can walk onto the dead actor now */ CL_ActorConditionalMoveCalc(selActor); LE_Unlock(le); }
void LE_AddProjectile (const fireDef_t* fd, int flags, const vec3_t muzzle, const vec3_t impact, int normal, le_t* leVictim) { /* add le */ le_t* le = LE_Add(0); if (!le) return; LE_SetInvisible(le); /* bind particle */ le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle); if (!le->ptl) { le->inuse = false; return; } /* calculate parameters */ vec3_t delta; VectorSubtract(impact, muzzle, delta); const float dist = VectorLength(delta); VecToAngles(delta, le->ptl->angles); /* direction - bytedirs index */ le->angle = normal; le->fd = fd; /* infinite speed projectile? */ if (!fd->speed) { le->inuse = false; le->ptl->size[0] = dist; VectorMA(muzzle, 0.5, delta, le->ptl->s); if ((flags & (SF_IMPACT | SF_BODY)) || (fd->splrad && !fd->bounce)) { ptl_t* ptl = nullptr; const float* dir = bytedirs[le->angle]; if (flags & SF_BODY) { if (fd->hitBodySound != nullptr) { S_LoadAndPlaySample(fd->hitBodySound, le->origin, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (fd->hitBody != nullptr) ptl = CL_ParticleSpawn(fd->hitBody, 0, impact, dir); /* 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 (leVictim) { if (fd->obj->dmgtype != csi.damStunGas) LE_ActorBodyHit(leVictim, impact, le->angle); if (fd->damage[0] >= 0) CL_ActorPlaySound(leVictim, SND_HURT); } } else { if (fd->impactSound != nullptr) { S_LoadAndPlaySample(fd->impactSound, le->origin, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (fd->impact != nullptr) ptl = CL_ParticleSpawn(fd->impact, 0, impact, dir); } if (ptl) VecToAngles(dir, ptl->angles); } return; } /* particle properties */ VectorScale(delta, fd->speed / dist, le->ptl->v); le->endTime = cl.time + 1000 * dist / fd->speed; /* think function */ if (flags & SF_BODY) { le->ref1 = fd->hitBody; le->ref2 = fd->hitBodySound; le->ref3 = leVictim; } else if ((flags & SF_IMPACT) || (fd->splrad && !fd->bounce)) { le->ref1 = fd->impact; le->ref2 = fd->impactSound; } else { le->ref1 = nullptr; if (flags & SF_BOUNCING) le->ref2 = fd->bounceSound; } LE_SetThink(le, LET_Projectile); LE_ExecuteThink(le); }