void CL_ActorStateChange (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, state; character_t *chr; NET_ReadFormat(msg, self->formatString, &entnum, &state); le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsActor(le)) { Com_Printf("StateChange message ignored... LE is no actor (number: %i, state: %i, type: %i)\n", entnum, state, le->type); return; } /* If standing up or crouching down remove the reserved-state for crouching. */ if (((state & STATE_CROUCHED) && !LE_IsCrouched(le)) || (!(state & STATE_CROUCHED) && LE_IsCrouched(le))) { if (CL_ActorUsableTUs(le) < TU_CROUCH && CL_ActorReservedTUs(le, RES_CROUCH) >= TU_CROUCH) { /* We have not enough non-reserved TUs, * but some reserved for crouching/standing up. * i.e. we only reset the reservation for crouching if it's the very last attempt. */ CL_ActorReserveTUs(le, RES_CROUCH, 0); /* Reset reserved TUs (0 TUs) */ } } /* killed by the server: no animation is played, etc. */ if ((state & STATE_DEAD) && LE_IsLivingActor(le)) { le->state = state; FLOOR(le) = NULL; LE_SetThink(le, NULL); VectorCopy(player_dead_maxs, le->maxs); CL_ActorRemoveFromTeamList(le); return; } else { le->state = state; LE_SetThink(le, LET_StartIdle); } /* save those states that the actor should also carry over to other missions */ chr = CL_ActorGetChr(le); if (!chr) return; chr->state = (le->state & STATE_REACTION); /* change reaction button state */ if (!(le->state & STATE_REACTION)) { UI_ExecuteConfunc("disable_reaction"); } else { UI_ExecuteConfunc("startreaction"); } /* state change may have affected move length */ CL_ActorConditionalMoveCalc(le); }
/** * @brief Network event function for TU reservation. Responsible for updating the HUD with the information * that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from */ void CL_ActorReservationChange (const eventRegister_t* self, dbuffer* msg) { int entnum, reaction, shot, crouch; NET_ReadFormat(msg, self->formatString, &entnum, &reaction, &shot, &crouch); const le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); character_t* chr = CL_ActorGetChr(le); if (!chr) return; chr->reservedTus.reaction = reaction; chr->reservedTus.shot = shot; chr->reservedTus.crouch = crouch; }
/** * @brief Network event function for reaction fire mode changes. Responsible for updating * the HUD with the information that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from * @sa HUD_UpdateReactionFiremodes */ void CL_ActorReactionFireChange (const eventRegister_t* self, dbuffer* msg) { actorHands_t hand; int entnum, fmIdx, odIdx; NET_ReadFormat(msg, self->formatString, &entnum, &fmIdx, &hand, &odIdx); const le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); character_t* chr = CL_ActorGetChr(le); if (!chr) return; const objDef_t* od = INVSH_GetItemByIDX(odIdx); chr->RFmode.set(hand, fmIdx, od); UI_ExecuteConfunc("reactionfire_updated"); }
/** * @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 Calculates chance to hit if the actor has a fire mode activated. * @param[in] actor The local entity of the actor to calculate the hit probability for. * @todo The hit probability should work somewhat differently for splash damage weapons. * Since splash damage weapons can deal damage even when they don't directly hit an actor, * the hit probability should be defined as the predicted percentage of the maximum splash * damage of the firemode, assuming the projectile explodes at the desired location. This * means that a percentage should be displayed for EVERY actor in the predicted blast * radius. This will likely require specialized code. */ int CL_GetHitProbability (const le_t* actor) { vec3_t shooter, target; float distance, pseudosin, width, height, acc, perpX, perpY, hitchance, stdevupdown, stdevleftright, crouch, commonfactor; int distx, disty, n; le_t *le; const character_t *chr; pos3_t toPos; assert(actor); assert(actor->fd); if (IS_MODE_FIRE_PENDING(actor->actorMode)) VectorCopy(actor->mousePendPos, toPos); else VectorCopy(mousePos, toPos); /** @todo use LE_FindRadius */ le = LE_GetFromPos(toPos); if (!le) return 0; /* or suicide attempted */ if (le->selected && !FIRESH_IsMedikit(le->fd)) return 0; VectorCopy(actor->origin, shooter); VectorCopy(le->origin, target); /* Calculate HitZone: */ distx = fabs(shooter[0] - target[0]); disty = fabs(shooter[1] - target[1]); distance = sqrt(distx * distx + disty * disty); if (distx > disty) pseudosin = distance / distx; else pseudosin = distance / disty; width = 2 * PLAYER_WIDTH * pseudosin; height = LE_IsCrouched(le) ? PLAYER_CROUCHING_HEIGHT : PLAYER_STANDING_HEIGHT; chr = CL_ActorGetChr(actor); if (!chr) Com_Error(ERR_DROP, "No character given for local entity"); acc = GET_ACC(chr->score.skills[ABILITY_ACCURACY], actor->fd->weaponSkill ? chr->score.skills[actor->fd->weaponSkill] : 0.0) * CL_ActorInjuryModifier(actor, MODIFIER_ACCURACY); crouch = (LE_IsCrouched(actor) && actor->fd->crouch) ? actor->fd->crouch : 1.0; commonfactor = crouch * torad * distance * GET_INJURY_MULT(chr->score.skills[ABILITY_MIND], actor->HP, actor->maxHP); stdevupdown = (actor->fd->spread[0] * (WEAPON_BALANCE + SKILL_BALANCE * acc)) * commonfactor; stdevleftright = (actor->fd->spread[1] * (WEAPON_BALANCE + SKILL_BALANCE * acc)) * commonfactor; hitchance = (stdevupdown > LOOKUP_EPSILON ? CL_LookupErrorFunction(height * 0.3536f / stdevupdown) : 1.0f) * (stdevleftright > LOOKUP_EPSILON ? CL_LookupErrorFunction(width * 0.3536f / stdevleftright) : 1.0f); /* 0.3536=sqrt(2)/4 */ /* Calculate cover: */ n = 0; height = height / 18; width = width / 18; target[2] -= UNIT_HEIGHT / 2; target[2] += height * 9; perpX = disty / distance * width; perpY = 0 - distx / distance * width; target[0] += perpX; perpX *= 2; target[1] += perpY; perpY *= 2; target[2] += 6 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[0] += perpX; target[1] += perpY; target[2] -= 6 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[0] += perpX; target[1] += perpY; target[2] += 4 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[2] += 4 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[0] -= perpX * 3; target[1] -= perpY * 3; target[2] -= 12 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[0] -= perpX; target[1] -= perpY; target[2] += 6 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[0] -= perpX; target[1] -= perpY; target[2] -= 4 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; target[0] -= perpX; target[1] -= perpY; target[2] += 10 * height; if (!CL_TestLine(shooter, target, TL_FLAG_NONE)) n++; return 100 * (hitchance * (0.125) * n); }
/** * @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); }