Beispiel #1
0
/**
 * @brief Returns the penalty to the given stat caused by the actor wounds.
 * @param[in] ent Pointer to the actor we want to calculate the penalty for.
 * @param[in] type The stat we want to calculate the penalty for.
 * @return The given penalty for this actor.
 */
float G_ActorGetInjuryPenalty (const Edict* const ent, const modifier_types_t type)
{
	float penalty = 0;

	const teamDef_t* const teamDef = ent->chr.teamDef;
	for (int bodyPart = 0; bodyPart < teamDef->bodyTemplate->numBodyParts(); ++bodyPart) {
		const int threshold = ent->chr.maxHP * teamDef->bodyTemplate->woundThreshold(bodyPart);
		const int injury = (ent->chr.wounds.woundLevel[bodyPart] + ent->chr.wounds.treatmentLevel[bodyPart] * 0.5);
		if (injury > threshold)
			penalty += 2 * teamDef->bodyTemplate->penalty(bodyPart, type) * injury / ent->chr.maxHP;
	}

	switch (type) {
	case MODIFIER_REACTION:
		penalty += G_ActorGetInjuryPenalty(ent, MODIFIER_SHOOTING);
		break;
	case MODIFIER_SHOOTING:
	case MODIFIER_ACCURACY:
		++penalty;
		break;
	case MODIFIER_TU:
	case MODIFIER_SIGHT:
		penalty = 1 - penalty;
		break;
	case MODIFIER_MOVEMENT:
		penalty = ceil(penalty);
		break;
	default:
		gi.DPrintf("G_ActorGetInjuryPenalty: Unknown modifier type %i\n", type);
		penalty = 0;
		break;
	}

	return penalty;
}
Beispiel #2
0
int G_ActorCalculateMaxTU (const Edict* ent)
{
	const int invWeight = ent->chr.inv.getWeight();
	const int currentMaxTU = GET_TU(ent->chr.score.skills[ABILITY_SPEED], GET_ENCUMBRANCE_PENALTY(invWeight,
			ent->chr.score.skills[ABILITY_POWER])) * G_ActorGetInjuryPenalty(ent, MODIFIER_TU);

	return std::min(currentMaxTU, MAX_TU);
}
Beispiel #3
0
int G_VisCheckDist (const Edict* const ent)
{
	if (G_IsActiveCamera(ent))
		return MAX_SPOT_DIST_CAMERA;

	if (G_IsActor(ent))
		return MAX_SPOT_DIST * G_ActorGetInjuryPenalty(ent, MODIFIER_SIGHT);

	return MAX_SPOT_DIST;
}
Beispiel #4
0
/**
* @brief Return the needed TUs to walk to a given position
* @param ent Edict to calculate move length for
* @param path Pointer to pathing table
* @param to Position to walk to
* @param stored Use the stored mask (the cached move) of the routing data
* @return ROUTING_NOT_REACHABLE if the move isn't possible, length of move otherwise (TUs)
*/
byte G_ActorMoveLength (const edict_t *ent, const pathing_t *path, const pos3_t to, bool stored)
{
	byte crouchingState = G_IsCrouched(ent) ? 1 : 0;
	const int length = gi.MoveLength(path, to, crouchingState, stored);
	int dvec, numSteps = 0;
	pos3_t pos;

	if (!length || length == ROUTING_NOT_REACHABLE)
		return length;

	VectorCopy(to, pos);
	while ((dvec = gi.MoveNext(level.pathingMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
		++numSteps;
		PosSubDV(pos, crouchingState, dvec); /* We are going backwards to the origin. */
	}

	return std::min(ROUTING_NOT_REACHABLE, length + static_cast<int>(numSteps *
			G_ActorGetInjuryPenalty(ent, MODIFIER_MOVEMENT)));
}
Beispiel #5
0
/**
 * @brief Generates the client events that are send over the netchannel to move an actor
 * @param[in] player Player who is moving an actor
 * @param[in] visTeam The team to check the visibility for - if this is 0 we build the forbidden list
 * above all edicts - for the human controlled actors this would mean that clicking to a grid
 * position that is not reachable because an invisible actor is standing there would not result in
 * a single step - as the movement is aborted before. For AI movement this is in general @c 0 - but
 * not if they e.g. hide.
 * @param[in] ent Edict to move
 * @param[in] to The grid position to walk to
 * @sa CL_ActorStartMove
 * @sa PA_MOVE
 */
void G_ClientMove (const player_t * player, int visTeam, edict_t* ent, const pos3_t to)
{
	int status, initTU;
	dvec_t dvtab[MAX_ROUTE];
	byte numdv, length;
	pos3_t pos;
	float div;
	int oldState;
	int oldHP;
	int oldSTUN;
	bool autoCrouchRequired = false;
	byte crouchingState;

	if (VectorCompare(ent->pos, to))
		return;

	/* check if action is possible */
	if (!G_ActionCheckForCurrentTeam(player, ent, TU_MOVE_STRAIGHT))
		return;

	crouchingState = G_IsCrouched(ent) ? 1 : 0;
	oldState = oldHP = oldSTUN = 0;

	/* calculate move table */
	G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
	length = G_ActorMoveLength(ent, level.pathingMap, to, false);

	/* length of ROUTING_NOT_REACHABLE means not reachable */
	if (length && length >= ROUTING_NOT_REACHABLE)
		return;

	/* Autostand: check if the actor is crouched and player wants autostanding...*/
	if (crouchingState && player->autostand) {
		/* ...and if this is a long walk... */
		if (SHOULD_USE_AUTOSTAND(length)) {
			/* ...make them stand first. If the player really wants them to walk a long
			 * way crouched, he can move the actor in several stages.
			 * Uses the threshold at which standing, moving and crouching again takes
			 * fewer TU than just crawling while crouched. */
			G_ClientStateChange(player, ent, STATE_CROUCHED, true); /* change to stand state */
			crouchingState = G_IsCrouched(ent) ? 1 : 0;
			if (!crouchingState) {
				G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
				length = G_ActorMoveLength(ent, level.pathingMap, to, false);
				autoCrouchRequired = true;
			}
		}
	}

	/* this let the footstep sounds play even over network */
	ent->think = G_PhysicsStep;
	ent->nextthink = level.time;

	/* assemble dvec-encoded move data */
	VectorCopy(to, pos);
	initTU = ent->TU;

	numdv = G_FillDirectionTable(dvtab, lengthof(dvtab), crouchingState, pos);

	/* make sure to end any other pending events - we rely on EV_ACTOR_MOVE not being active anymore */
	G_EventEnd();

	/* everything ok, found valid route? */
	if (VectorCompare(pos, ent->pos)) {
		byte* stepAmount = NULL;
		int usedTUs = 0;
		/* no floor inventory at this point */
		FLOOR(ent) = NULL;
		const int movingModifier = G_ActorGetInjuryPenalty(ent, MODIFIER_MOVEMENT);

		while (numdv > 0) {
			/* A flag to see if we needed to change crouch state */
			int crouchFlag;
			const byte oldDir = ent->dir;

			/* get next dvec */
			numdv--;
			const int dvec = dvtab[numdv];
			/* This is the direction to make the step into */
			const int dir = getDVdir(dvec);

			/* turn around first */
			status = G_ActorDoTurn(ent, dir);
			if (status & VIS_STOP) {
				autoCrouchRequired = false;
				if (ent->moveinfo.steps == 0)
					usedTUs += TU_TURN;
				break;
			}

			if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv)) {
				/* don't autocrouch if new enemy becomes visible */
				autoCrouchRequired = false;
				/* if something appears on our route that didn't trigger a VIS_STOP, we have to
				 * send the turn event if this is our first step */
				if (oldDir != ent->dir && ent->moveinfo.steps == 0) {
					G_EventActorTurn(ent);
					usedTUs += TU_TURN;
				}
				break;
			}

			/* decrease TUs */
			div = gi.GetTUsForDirection(dir, G_IsCrouched(ent));
			if ((int) (usedTUs + div + movingModifier) > ent->TU)
				break;
			usedTUs += div + movingModifier;

			/* This is now a flag to indicate a change in crouching - we need this for
			 * the stop in mid move call(s), because we need the updated entity position */
			crouchFlag = 0;
			/* Calculate the new position after the decrease in TUs, otherwise the game
			 * remembers the false position if the time runs out */
			PosAddDV(ent->pos, crouchFlag, dvec);

			/* slower if crouched */
			if (G_IsCrouched(ent))
				ent->speed = ACTOR_SPEED_CROUCHED;
			else
				ent->speed = ACTOR_SPEED_NORMAL;
			ent->speed *= g_actorspeed->value;

			if (crouchFlag == 0) { /* No change in crouch */
				G_EdictCalcOrigin(ent);

				const int contentFlags = G_ActorGetContentFlags(ent->origin);

				/* link it at new position - this must be done for every edict
				 * movement - to let the server know about it. */
				gi.LinkEdict(ent);

				/* Only the PHALANX team has these stats right now. */
				if (ent->chr.scoreMission) {
					float truediv = gi.GetTUsForDirection(dir, 0);		/* regardless of crouching ! */
					if (G_IsCrouched(ent))
						ent->chr.scoreMission->movedCrouched += truediv;
					else
						ent->chr.scoreMission->movedNormal += truediv;
				}
				/* write the step to the net */
				G_WriteStep(ent, &stepAmount, dvec, contentFlags);

				status = 0;

				/* Set ent->TU because the reaction code relies on ent->TU being accurate. */
				G_ActorSetTU(ent, initTU - usedTUs);

				edict_t* clientAction = ent->clientAction;
				oldState = ent->state;
				oldHP = ent->HP;
				oldSTUN = ent->STUN;
				/* check triggers at new position */
				if (G_TouchTriggers(ent)) {
					if (!clientAction)
						status |= VIS_STOP;
				}

				/* check if player appears/perishes, seen from other teams */
				G_CheckVis(ent);

				/* check for anything appearing, seen by "the moving one" */
				status |= G_CheckVisTeamAll(ent->team, 0, ent);

				G_TouchSolids(ent, 10.0f);

				/* state has changed - maybe we walked on a trigger_hurt */
				if (oldState != ent->state || oldHP != ent->HP || oldSTUN != ent->STUN)
					status |= VIS_STOP;
			} else if (crouchFlag == 1) {
				/* Actor is standing */
				G_ClientStateChange(player, ent, STATE_CROUCHED, true);
			} else if (crouchFlag == -1) {
				/* Actor is crouching and should stand up */
				G_ClientStateChange(player, ent, STATE_CROUCHED, false);
			}

			/* check for reaction fire */
			if (G_ReactionFireOnMovement(ent)) {
				status |= VIS_STOP;

				autoCrouchRequired = false;
			}

			/* check for death */
			if (((oldHP != 0 && (oldHP != ent->HP || oldSTUN != ent->STUN)) || (oldState != ent->state)) && !G_IsDazed(ent)) {
				/** @todo Handle dazed via trigger_hurt */
				/* maybe this was due to rf - then the G_ActorDie was already called */
				if (!G_IsDead(ent)) {
					G_CheckDeathOrKnockout(ent, NULL, NULL, (oldHP - ent->HP) + (ent->STUN - oldSTUN));
				}
				return;
			}

			if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv - 1)) {
				/* don't autocrouch if new enemy becomes visible */
				autoCrouchRequired = false;
				break;
			}

			/* Restore ent->TU because the movement code relies on it not being modified! */
			G_ActorSetTU(ent, initTU);
		}

		/* submit the TUs / round down */
		G_ActorSetTU(ent, initTU - usedTUs);

		G_SendStats(ent);

		/* end the move */
		G_GetFloorItems(ent);
		G_EventEnd();
	}

	if (autoCrouchRequired) {
		/* toggle back to crouched state */
		G_ClientStateChange(player, ent, STATE_CROUCHED, true);
	}
}
Beispiel #6
0
int G_ActorGetModifiedTimeForFiredef (const Edict* const ent, const fireDef_t* const fd, const bool reaction)
{
	return fd->time * G_ActorGetInjuryPenalty(ent, reaction ? MODIFIER_REACTION : MODIFIER_SHOOTING);
}