/**
 * @brief Moves actor.
 * @param[in] self Pointer to the event structure that is currently executed
 * @param[in] msg The netchannel message
 * @sa LET_PathMove
 * @note EV_ACTOR_MOVE
 */
void CL_ActorDoMove (const eventRegister_t* self, dbuffer* msg)
{
	const int number = NET_ReadShort(msg);

	/* get le */
	le_t* le = LE_Get(number);
	if (!le)
		LE_NotFoundError(number);

	if (!LE_IsActor(le))
		Com_Error(ERR_DROP, "Can't move, LE doesn't exist or is not an actor (entnum: %i, type: %i)\n",
			number, le->type);

	if (LE_IsDead(le))
		Com_Error(ERR_DROP, "Can't move, actor on team %i dead (entnum: %i)", le->team, number);

	/* lock this le for other events, the corresponding unlock is in LE_DoEndPathMove() */
	LE_Lock(le);
	if (le->isMoving()) {
		if (le->pathLength == le->pathPos) {
			LE_DoEndPathMove(le);
		} else {
			Com_Error(ERR_DROP, "Actor (entnum: %i) on team %i is still moving (%i steps left).  Times: %i, %i, %i",
					le->entnum, le->team, le->pathLength - le->pathPos, le->startTime, le->endTime, cl.time);
		}
	}

	int i = 0;
	/* the end of this event is marked with a 0 */
	while (NET_PeekLong(msg) != 0) {
		NET_ReadByte(msg);
		le->dvtab[i] = NET_ReadShort(msg); /** Don't adjust dv values here- the whole thing is needed to move the actor! */
		le->speed[i] = NET_ReadShort(msg);
		le->pathContents[i] = NET_ReadShort(msg);
		i++;
	}
	le->pathLength = i;

	if (le->pathLength > MAX_ROUTE)
		Com_Error(ERR_DROP, "Overflow in pathLength (entnum: %i)", number);

	/* skip the end of move marker */
	NET_ReadLong(msg);
	/* Also get the final position */
	NET_ReadGPos(msg, le->newPos);

	if (VectorCompare(le->newPos, le->pos))
		Com_Error(ERR_DROP, "start and end pos are the same (entnum: %i)", number);

	/* activate PathMove function */
	le->resetFloor();
	if (LE_IsInvisible(le))
		/* Hack: this relies on the visibility events interrupting the EV_ACTOR_MOVE event */
		LE_SetThink(le, LET_HiddenMove);
	else
		LE_SetThink(le, LET_StartPathMove);
	le->pathPos = 0;
	le->startTime = cl.time;
	le->endTime = cl.time;
}
Exemple #2
0
/**
 * @brief Callback for EV_DOOR_CLOSE event - rotates the inline model and recalc routing
 * @sa EV_DOOR_CLOSE
 * @sa G_ClientUseEdict
 * @sa Touch_DoorTrigger
 */
void CL_DoorClose (const eventRegister_t *self, struct dbuffer *msg)
{
    /* get local entity */
    int number;
    le_t *le;

    NET_ReadFormat(msg, self->formatString, &number);

    le = LE_Get(number);
    if (!le)
        LE_NotFoundError(number);

    if (le->type == ET_DOOR) {
        if (le->dir & DOOR_OPEN_REVERSE)
            le->angles[le->dir & 3] += DOOR_ROTATION_ANGLE;
        else
            le->angles[le->dir & 3] -= DOOR_ROTATION_ANGLE;

        CM_SetInlineModelOrientation(cl.mapTiles, le->inlineModelName, le->origin, le->angles);
        CL_RecalcRouting(le);
    } else if (le->type == ET_DOOR_SLIDING) {
        LE_SetThink(le, LET_DoorSlidingClose);
        le->think(le);
    } else {
        Com_Error(ERR_DROP, "Invalid door entity found of type: %i", le->type);
    }
}
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 reaction fire target handling. 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_ActorReactionFireTargetUpdate (const eventRegister_t* self, dbuffer* msg)
{
	int shooterEntNum;
	int targetEntNum;
	// if these TUs have arrived at 0, the reaction fire can be triggered
	int tusUntilTriggered;
	int unused;

	NET_ReadFormat(msg, self->formatString, &shooterEntNum, &targetEntNum, &tusUntilTriggered, &unused);

	const le_t* shooter = LE_Get(shooterEntNum);
	if (!shooter)
		LE_NotFoundError(shooterEntNum);

	const le_t* target = LE_Get(targetEntNum);
	if (!target)
		LE_NotFoundError(targetEntNum);

	const bool outOfRange = CL_ActorIsReactionFireOutOfRange(shooter, target);
	UI_ExecuteConfunc("reactionfire_updatetarget %i %i %i %i", shooterEntNum, target->entnum, tusUntilTriggered, outOfRange);
}
Exemple #5
0
/**
 * @brief Called whenever an entity disappears from view
 * @sa CL_EntAppear
 */
void CL_EntPerish (const eventRegister_t *self, struct dbuffer *msg)
{
	int		entnum;
	le_t	*le, *actor;

	NET_ReadFormat(msg, self->formatString, &entnum);

	le = LE_Get(entnum);

	if (!le)
		LE_NotFoundError(entnum);

	/* decrease the count of spotted aliens (also stunned) */
	cl.numEnemiesSpotted = CL_CountVisibleEnemies();

	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 an 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;
}
/**
 * @brief Network event function for reaction fire target handling. 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_ActorReactionFireRemoveTarget (const eventRegister_t* self, dbuffer* msg)
{
	int shooterEntNum;
	int targetEntNum;
	int unused;

	NET_ReadFormat(msg, self->formatString, &shooterEntNum, &targetEntNum, &unused);

	const le_t* target = LE_Get(targetEntNum);
	if (!target)
		LE_NotFoundError(targetEntNum);

	UI_ExecuteConfunc("reactionfire_removetarget %i %i", shooterEntNum, target->entnum);
}
/**
 * @sa CL_InvDel
 * @sa G_SendInventory
 * @sa EV_INV_ADD
 */
void CL_InvAdd (const eventRegister_t *self, dbuffer *msg)
{
	const int number = NET_ReadShort(msg);
	le_t *le = LE_Get(number);
	int nr = NET_ReadShort(msg);

	if (!le)
		LE_NotFoundError(number);

	le->removeNextFrame = false;

	for (; nr-- > 0;) {
		item_t item;
		containerIndex_t container;
		int x, y;
		CL_NetReceiveItem(msg, &item, &container, &x, &y);

		if (LE_IsItem(le)) {
			if (container != csi.idFloor)
				Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container);
		} else if (INVDEF(container)->temp) {
			Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container);
		}

		if (cls.i.AddToInventory(&cls.i, &le->i, &item, INVDEF(container), x, y, item.amount) == NULL)
			Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i",
					item.amount, item.item->id, container);

		if (container == csi.idRight)
			le->right = item.item->idx;
		else if (container == csi.idLeft)
			le->left = item.item->idx;
		else if (container == csi.idExtension)
			le->extension = item.item->idx;
		else if (container == csi.idHeadgear)
			le->headgear = item.item->idx;
	}

	switch (le->type) {
	case ET_ACTOR:
	case ET_ACTOR2x2:
		LE_SetThink(le, LET_StartIdle);
		break;
	case ET_ITEM:
		LE_PlaceItem(le);
		break;
	default:
		break;
	}
}
Exemple #8
0
/**
 * @sa CL_InvDel
 * @sa G_SendInventory
 * @sa EV_INV_ADD
 */
void CL_InvAdd (const eventRegister_t* self, dbuffer* msg)
{
	const int number = NET_ReadShort(msg);
	le_t* le = LE_Get(number);
	int nr = NET_ReadShort(msg);

	if (!le)
		LE_NotFoundError(number);

	le->flags &= ~LE_REMOVE_NEXT_FRAME;

	for (; nr-- > 0;) {
		Item item;
		containerIndex_t container;
		int x, y;
		CL_NetReceiveItem(msg, &item, &container, &x, &y);

		if (LE_IsItem(le)) {
			if (container != CID_FLOOR)
				Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container);
		} else if (INVDEF(container)->temp) {
			Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container);
		}

		if (cls.i.addToInventory(&le->inv, &item, INVDEF(container), x, y, item.getAmount()) == nullptr)
			Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i",
					item.getAmount(), item.def()->id, container);

		if (container == CID_RIGHT)
			le->right = item.def()->idx;
		else if (container == CID_LEFT)
			le->left = item.def()->idx;
		else if (container == CID_HEADGEAR)
			le->headgear = item.def()->idx;
	}

	switch (le->type) {
	case ET_ACTOR:
	case ET_ACTOR2x2:
		if (LE_IsSelected(le))
			Cmd_ExecuteString("hud_updateactorload");
		LE_SetThink(le, LET_StartIdle);
		break;
	case ET_ITEM:
		LE_PlaceItem(le);
		break;
	default:
		break;
	}
}
/**
 * @brief Reads the entity number for client interaction
 * @sa EV_CLIENT_ACTION
 * @sa Touch_DoorTrigger
 * @sa CL_ActorUse
 * @todo Hud should have a button that should be activated now
 */
void CL_ActorClientAction (const eventRegister_t *self, dbuffer *msg)
{
	le_t* le;
	int number, actionEntityNumber;

	/* read data */
	NET_ReadFormat(msg, self->formatString, &number, &actionEntityNumber);

	/* get actor le */
	le = LE_Get(number);
	if (!le)
		LE_NotFoundError(number);

	/* set client action entity */
	le->clientAction = LE_Get(actionEntityNumber);
	if (!le->clientAction)
		LE_NotFoundError(actionEntityNumber);

	UI_ExecuteConfunc("enable_clientaction");

	Com_DPrintf(DEBUG_CLIENT, "CL_ActorClientAction: Set entity number: %i (for actor with entnum %i)\n",
			actionEntityNumber, number);
}
/**
 * @note e.g. func_breakable or func_door with health
 * @sa EV_MODEL_EXPLODE
 */
void CL_Explode (const eventRegister_t *self, struct dbuffer *msg)
{
	const int entnum = NET_ReadShort(msg);
	le_t *le = LE_Get(entnum);
	if (!le)
		LE_NotFoundError(entnum);

	le->inuse = false;
	if (le->modelnum1 > 0)
		cl.model_clip[le->modelnum1] = NULL;

	/* Recalc the client routing table because this le (and the inline model) is now gone */
	CL_RecalcRouting(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;
}
int CL_ActorReactionFireRemoveTargetTime (const eventRegister_t* self, dbuffer* msg, eventTiming_t* eventTiming)
{
	int targetEntNum;
	int unused;
	int step;

	NET_ReadFormat(msg, self->formatString, &unused, &targetEntNum, &step);

	const le_t* target = LE_Get(targetEntNum);
	if (!target)
		LE_NotFoundError(targetEntNum);
	if (step >= MAX_ROUTE)
		return eventTiming->nextTime;
	const int stepTime = CL_GetStepTime(eventTiming, target, step);
	if (eventTiming->shootTime > stepTime)
		return eventTiming->impactTime;
	return stepTime;
}
/**
 * @brief Turns actor.
 * @param[in] self Pointer to the event structure that is currently executed
 * @param[in] msg The netchannel message
 */
void CL_ActorDoTurn (const eventRegister_t* self, dbuffer* msg)
{
	int entnum, dir;
	NET_ReadFormat(msg, self->formatString, &entnum, &dir);

	/* get le */
	le_t* le = LE_Get(entnum);
	if (!le)
		LE_NotFoundError(entnum);

	if (!LE_IsActor(le))
		Com_Error(ERR_DROP, "Can't turn, LE doesn't exist or is not an actor (number: %i, type: %i)\n",
				entnum, le->type);

	if (LE_IsDead(le))
		Com_Error(ERR_DROP, "Can't turn, actor dead\n");

	le->angle = dir;
	le->angles[YAW] = directionAngles[le->angle];
}
Exemple #14
0
int CL_SoundEventTime (const struct eventRegister_s* self, dbuffer* msg, eventTiming_t* eventTiming)
{
	char sound[MAX_QPATH];
	vec3_t origin;
	int number;
	int step;

	/* read data */
	NET_ReadFormat(msg, self->formatString, &number, &origin, &step, &sound, sizeof(sound));

	const le_t* le = LE_Get(number);
	if (!le)
		LE_NotFoundError(number);
	if (step >= MAX_ROUTE)
		return eventTiming->nextTime;
	const int stepTime = CL_GetStepTime(eventTiming, le, step);
	if (eventTiming->shootTime > stepTime)
		return eventTiming->impactTime;
	return stepTime;
}
/**
 * @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 Decides if following events should be delayed. The delay is the amount of time the actor needs to walk
 * from the start to the end pos.
 */
int CL_ActorDoMoveTime (const eventRegister_t *self, dbuffer *msg, eventTiming_t *eventTiming)
{
	int time = 0;

	const int eventTime = eventTiming->nextTime;
	const int number = NET_ReadShort(msg);

	/* get le */
	const le_t *le = LE_Get(number);
	if (!le)
		LE_NotFoundError(number);

	pos3_t pos;
	VectorCopy(le->pos, pos);
	byte crouchingState = LE_IsCrouched(le) ? 1 : 0;

	/* the end of this event is marked with a 0 */
	while (NET_PeekLong(msg) != 0) {
		const dvec_t dvec = NET_ReadShort(msg);
		const byte dir = getDVdir(dvec);
		pos3_t oldPos;
		VectorCopy(pos, oldPos);
		PosAddDV(pos, crouchingState, dvec);
		time += LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg));
		NET_ReadShort(msg);
	}

	/* skip the end of move marker */
	NET_ReadLong(msg);

	/* Also skip the final position */
	NET_ReadByte(msg);
	NET_ReadByte(msg);
	NET_ReadByte(msg);

	assert(NET_PeekByte(msg) == EV_NULL);

	eventTiming->nextTime += time + 400;

	return eventTime;
}
Exemple #17
0
/**
 * @brief Callback for EV_DOOR_CLOSE event - rotates the inline model and recalc routing
 * @sa EV_DOOR_CLOSE
 * @sa G_ClientUseEdict
 * @sa Touch_DoorTrigger
 */
void CL_DoorClose (const eventRegister_t* self, dbuffer* msg)
{
	/* get local entity */
	int number;

	NET_ReadFormat(msg, self->formatString, &number);

	le_t* le = LE_Get(number);
	if (!le)
		LE_NotFoundError(number);

	if (le->type == ET_DOOR) {
		LE_SetThink(le, LET_DoorRotatingClose);
		le->think(le);
	} else if (le->type == ET_DOOR_SLIDING) {
		LE_SetThink(le, LET_DoorSlidingClose);
		le->think(le);
	} else {
		Com_Error(ERR_DROP, "Invalid door entity found of type: %i", le->type);
	}
}
/**
 * @brief Let a particle appear for the client
 * @param[in] self Pointer to the event structure that is currently executed
 * @param[in] msg holds the network data
 * @sa CL_ParticleSpawn
 * @sa EV_PARTICLE_APPEAR
 */
void CL_ParticleAppear (const eventRegister_t* self, dbuffer* msg)
{
	char particle[MAX_VAR];
	int entnum, levelflags;
	vec3_t origin;

	/* read data */
	NET_ReadFormat(msg, self->formatString, &entnum, &levelflags, origin, particle, sizeof(particle));

	le_t* le = LE_Get(entnum);
	if (!le)
		LE_NotFoundError(entnum);

	/* particles don't have a model to add to the scene - we mark them as invisible and
	 * only render the particle */
	LE_SetInvisible(le);
	le->levelflags = levelflags;
	le->particleID = Mem_PoolStrDup(particle, cl_genericPool, 0);
	le->ptl = CL_ParticleSpawn(le->particleID, le->levelflags, origin);
	if (!le->ptl)
		Com_Printf("Could not spawn particle: '%s'\n", le->particleID);
}
/**
 * @brief Parses the actor stats that comes from the netchannel
 * @sa CL_ParseEvent
 * @sa G_SendStats
 */
void CL_ActorStats (const eventRegister_t *self, dbuffer *msg)
{
	le_t *le;
	int entnum, oldTUs = 0;

	entnum = NET_ReadShort(msg);
	le = LE_Get(entnum);
	if (!le)
		LE_NotFoundError(entnum);

	switch (le->type) {
	case ET_ACTORHIDDEN:
	case ET_ACTOR:
	case ET_ACTOR2x2:
		break;
	default:
		Com_Printf("CL_ActorStats: LE (%i) not an actor (type: %i)\n", entnum, le->type);
		return;
	}

	if (LE_IsSelected(le))
		oldTUs = le->TU;

	NET_ReadFormat(msg, self->formatString, &le->TU, &le->HP, &le->STUN, &le->morale);

	if (le->TU > le->maxTU)
		le->maxTU = le->TU;
	if (le->HP > le->maxHP)
		le->maxHP = le->HP;
	if (le->morale > le->maxMorale)
		le->maxMorale = le->morale;

	/* if selActor's timeunits have changed, update movelength */
	if (le->TU != oldTUs && LE_IsSelected(le))
		CL_ActorResetMoveLength(le);
}
/**
 * @brief Parses the actor wound stats that come from the netchannel
 * @sa CL_ParseEvent
 * @sa G_SendStats
 */
void CL_ActorWound (const eventRegister_t *self, struct dbuffer *msg)
{
	le_t *le;
	int entnum, bodyPart, wounds, treatment;

	NET_ReadFormat(msg, self->formatString, &entnum, &bodyPart, &wounds, &treatment);

	le = LE_Get(entnum);
	if (!le)
		LE_NotFoundError(entnum);

	switch (le->type) {
	case ET_ACTORHIDDEN:
	case ET_ACTOR:
	case ET_ACTOR2x2:
		break;
	default:
		Com_Printf("CL_ActorWound: LE (%i) not an actor (type: %i)\n", entnum, le->type);
		return;
	}

	le->wounds.woundLevel[bodyPart] = wounds;
	le->wounds.treatmentLevel[bodyPart] = treatment;
}
/**
 * @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);
}
Exemple #22
0
/**
 * @brief Calculates the time the event should get executed. If two events return the same time,
 * they are going to be executed in the order the were parsed.
 * @param[in] eType The event type
 * @param[in,out] msg The message buffer that can be modified to get the event time
 * @param[in] dt Delta time in msec since the last event was parsed
 */
int CL_GetEventTime (const event_t eType, struct dbuffer *msg, const int dt)
{
	const eventRegister_t *eventData = CL_GetEvent(eType);

#ifdef OLDEVENTTIME
	/* the time the event should be executed. This value is used to sort the
	 * event chain to determine which event must be executed at first. This
	 * value also ensures, that the events are executed in the correct
	 * order. E.g. @c impactTime is used to delay some events in case the
	 * projectile needs some time to reach its target. */
	int eventTime;

	if (eType == EV_RESET) {
		parsedDeath = qfalse;
		nextTime = 0;
		shootTime = 0;
		impactTime = 0;
	} else if (eType == EV_ACTOR_DIE)
		parsedDeath = qtrue;

	/* get event time */
	if (nextTime < cl.time)
		nextTime = cl.time;
	if (impactTime < cl.time)
		impactTime = cl.time;

	if (eType == EV_ACTOR_DIE || eType == EV_MODEL_EXPLODE)
		eventTime = impactTime;
	else if (eType == EV_ACTOR_SHOOT || eType == EV_ACTOR_SHOOT_HIDDEN)
		eventTime = shootTime;
	else if (eType == EV_RESULTS)
		eventTime = nextTime + 1400;
	else
		eventTime = nextTime;

	if (eType == EV_ENT_APPEAR || eType == EV_INV_ADD || eType == EV_PARTICLE_APPEAR || eType == EV_PARTICLE_SPAWN) {
		if (parsedDeath) { /* drop items after death (caused by impact) */
			eventTime = impactTime + 400;
			/* EV_INV_ADD messages are the last events sent after a death */
			if (eType == EV_INV_ADD)
				parsedDeath = qfalse;
		} else if (impactTime > cl.time) { /* item thrown on the ground */
			eventTime = impactTime + 75;
		}
	}

	/* calculate time interval before the next event */
	switch (eType) {
	case EV_ACTOR_APPEAR:
		if (cl.actTeam != cls.team)
			nextTime += 600;
		break;
	case EV_INV_RELOAD:
		/* let the reload sound play */
		nextTime += 600;
		break;
	case EV_ACTOR_START_SHOOT:
		nextTime += 300;
		shootTime = nextTime;
		break;
	case EV_ACTOR_SHOOT_HIDDEN:
		{
			int first;
			int objIdx;
			const objDef_t *obj;
			weaponFireDefIndex_t weapFdsIdx;
			fireDefIndex_t fireDefIndex;

			NET_ReadFormat(msg, eventData->formatString, &first, &objIdx, &weapFdsIdx, &fireDefIndex);

			obj = INVSH_GetItemByIDX(objIdx);
			if (first) {
				nextTime += 500;
				impactTime = shootTime = nextTime;
			} else {
				const fireDef_t *fd = FIRESH_GetFiredef(obj, weapFdsIdx, fireDefIndex);
				/* impact right away - we don't see it at all
				 * bouncing is not needed here, too (we still don't see it) */
				impactTime = shootTime;
				nextTime = shootTime + 1400;
				if (fd->delayBetweenShots > 0.0)
					shootTime += 1000 / fd->delayBetweenShots;
			}
			parsedDeath = qfalse;
		}
		break;
	case EV_ACTOR_MOVE:
		{
			le_t *le;
			int number, i;
			int time = 0;
			int pathLength;
			byte crouchingState;
			pos3_t pos, oldPos;

			number = NET_ReadShort(msg);
			/* get le */
			le = LE_Get(number);
			if (!le)
				LE_NotFoundError(number);

			pathLength = NET_ReadByte(msg);

			/* Also skip the final position */
			NET_ReadByte(msg);
			NET_ReadByte(msg);
			NET_ReadByte(msg);

			VectorCopy(le->pos, pos);
			crouchingState = LE_IsCrouched(le) ? 1 : 0;

			for (i = 0; i < pathLength; i++) {
				const dvec_t dvec = NET_ReadShort(msg);
				const byte dir = getDVdir(dvec);
				VectorCopy(pos, oldPos);
				PosAddDV(pos, crouchingState, dvec);
				time += LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg));
				NET_ReadShort(msg);
			}
			nextTime += time + 400;
		}
		break;
	case EV_ACTOR_SHOOT:
		{
			const fireDef_t	*fd;
			int flags, dummy;
			int objIdx, surfaceFlags;
			objDef_t *obj;
			int weap_fds_idx, fd_idx;
			shoot_types_t shootType;
			vec3_t muzzle, impact;

			/* read data */
			NET_ReadFormat(msg, eventData->formatString, &dummy, &dummy, &dummy, &objIdx, &weap_fds_idx, &fd_idx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &dummy);

			obj = INVSH_GetItemByIDX(objIdx);
			fd = FIRESH_GetFiredef(obj, weap_fds_idx, fd_idx);

			if (!(flags & SF_BOUNCED)) {
				/* shooting */
				if (fd->speed > 0.0 && !CL_OutsideMap(impact, UNIT_SIZE * 10)) {
					impactTime = shootTime + 1000 * VectorDist(muzzle, impact) / fd->speed;
				} else {
					impactTime = shootTime;
				}
				if (cl.actTeam != cls.team)
					nextTime = impactTime + 1400;
				else
					nextTime = impactTime + 400;
				if (fd->delayBetweenShots > 0.0)
					shootTime += 1000 / fd->delayBetweenShots;
			} else {
				/* only a bounced shot */
				eventTime = impactTime;
				if (fd->speed > 0.0) {
					impactTime += 1000 * VectorDist(muzzle, impact) / fd->speed;
					nextTime = impactTime;
				}
			}
			parsedDeath = qfalse;
		}
		break;
	case EV_ACTOR_THROW:
		nextTime += NET_ReadShort(msg);
		impactTime = shootTime = nextTime;
		parsedDeath = qfalse;
		break;
	default:
		break;
	}

	Com_DPrintf(DEBUG_EVENTSYS, "%s => eventTime: %i, nextTime: %i, impactTime: %i, shootTime: %i\n",
			eventData->name, eventTime, nextTime, impactTime, shootTime);

	return eventTime;
#else
	if (!eventData->timeCallback)
		return cl.time;

	return eventData->timeCallback(eventData, msg, dt);
#endif
}
/**
 * @brief Decides if following events should be delayed. The delay is the amount of time the actor needs to walk
 * from the start to the end pos.
 */
int CL_ActorDoMoveTime (const eventRegister_t* self, dbuffer* msg, eventTiming_t* eventTiming)
{
	int time = 0;

	const int eventTime = eventTiming->nextTime;
	const int number = NET_ReadShort(msg);
	/* get le */
	le_t* le = LE_Get(number);
	if (!le)
		LE_NotFoundError(number);

	pos3_t pos;
	VectorCopy(le->pos, pos);
	byte crouchingState = LE_IsCrouched(le) ? 1 : 0;

	leStep_t* newStep = Mem_AllocType(leStep_t);
	if (le->stepList == nullptr) {
		le->stepList = newStep;
		le->stepIndex = 0;
	} else {
		/* append to the list */
		leStep_t* step = le->stepList;
		while (step) {
			if (step->next == nullptr) {
				step->next = newStep;
				le->stepIndex++;
				break;
			}
			step = step->next;
		}
	}

	/* the end of this event is marked with a 0 */
	while (NET_PeekLong(msg) != 0) {
		newStep->steps = NET_ReadByte(msg);
		const dvec_t dvec = NET_ReadShort(msg);
		const byte dir = getDVdir(dvec);
		pos3_t oldPos;
		VectorCopy(pos, oldPos);
		PosAddDV(pos, crouchingState, dvec);
		const int stepTime = LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg));
		newStep->stepTimes[newStep->steps] = stepTime;
		time += stepTime;
		NET_ReadShort(msg);
	}
	++newStep->steps;

	if (newStep->steps > MAX_ROUTE)
		Com_Error(ERR_DROP, "route length overflow: %i", newStep->steps);

	/* skip the end of move marker */
	NET_ReadLong(msg);

	/* Also skip the final position */
	NET_ReadByte(msg);
	NET_ReadByte(msg);
	NET_ReadByte(msg);

	assert(NET_PeekByte(msg) == EV_NULL);

	eventTiming->nextTime += time + 400;
	newStep->lastMoveTime = eventTime;
	newStep->lastMoveDuration = time;
	return eventTime;
}
/**
 * @sa CL_ActorAddToTeamList
 * @sa G_AppearPerishEvent
 * @sa CL_ActorAdd
 * @note EV_ACTOR_APPEAR
 */
void CL_ActorAppear (const eventRegister_t *self, struct dbuffer *msg)
{
	le_t *le, *leResponsible;
	int entnum, entnumResponsible, modelnum1, modelnum2;
	int teamDefID = -1;

	/* check if the actor is already visible */
	entnum = NET_ReadShort(msg);
	entnumResponsible = NET_ReadShort(msg);
	le = LE_Get(entnum);
	leResponsible = LE_Get(entnumResponsible);

	if (entnumResponsible != SKIP_LOCAL_ENTITY && !leResponsible)
		LE_NotFoundError(entnumResponsible);

	/* mission start - no actor is spawned yet - so create it */
	if (!le)
		le = LE_Add(entnum);

	/* Locking should be unnecessary if CL_CheckDefault filters this call, since this event starts and
	 * ends in this function only.  Adding lock/unlock just to be sure. */
	LE_Lock(le);

	/* maybe added via CL_ActorAdd before */
	le->invis = false;

	/* get the info */
	NET_ReadFormat(msg, self->formatString,
			&le->team, &teamDefID, &le->gender, &le->ucn, &le->pnum, &le->pos,
			&le->angle, &le->right, &le->left,
			&modelnum1, &modelnum2, &le->bodySkin, &le->headSkin,
			&le->state, &le->fieldSize,
			&le->maxTU, &le->maxMorale, &le->maxHP);

	if (teamDefID < 0 || teamDefID > csi.numTeamDefs)
		Com_Printf("CL_ActorAppear: Invalid teamDef index\n");
	else
		le->teamDef = &csi.teamDef[teamDefID];

	switch (le->fieldSize) {
	case ACTOR_SIZE_NORMAL:
		le->addFunc = CL_AddActor;
		le->type = ET_ACTOR;
		break;
	case ACTOR_SIZE_2x2:
		le->addFunc = CL_AddUGV;
		le->type = ET_ACTOR2x2;
		break;
	default:
		Com_Error(ERR_DROP, "Unknown fieldSize for le in CL_ActorAppear (EV_ACTOR_APPEAR)");
	}
	le->modelnum1 = modelnum1;
	le->modelnum2 = modelnum2;
	le->model1 = LE_GetDrawModel(modelnum1);
	le->model2 = LE_GetDrawModel(modelnum2);
	Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, le->origin);
	le->angles[YAW] = directionAngles[le->angle];

	if (LE_IsDead(le) && !LE_IsStunned(le))
		le->contents = CONTENTS_DEADACTOR;
	else
		le->contents = CONTENTS_ACTOR;
	VectorCopy(player_mins, le->mins);
	if (LE_IsDead(le))
		VectorCopy(player_dead_maxs, le->maxs);
	else
		VectorCopy(player_maxs, le->maxs);

	LE_SetThink(le, LET_StartIdle);

	/* count spotted aliens (also stunned) */
	cl.numEnemiesSpotted = CL_CountVisibleEnemies();

	if (LE_IsLivingActor(le)) {
		if (cl.actTeam != cls.team) {
			/* center view (if wanted) */
			LE_CenterView(le);
		}

		/* draw line of sight */
		if (le->team != cls.team) {
			if (leResponsible)
				CL_DrawLineOfSight(leResponsible, le);

			/* message */
			if (le->team != TEAM_CIVILIAN) {
				if (GAME_TeamIsKnown(le->teamDef)) {
					char tmpbuf[128];
					Com_sprintf(tmpbuf, sizeof(tmpbuf), _("Enemy spotted: %s!"), _(le->teamDef->name));
					HUD_DisplayMessage(tmpbuf);
				} else
					HUD_DisplayMessage(_("Unknown enemy spotted!"));
			} else
				HUD_DisplayMessage(_("Civilian spotted."));

			/* update pathing as new actor could block path */
			CL_ActorConditionalMoveCalc(leResponsible ? leResponsible : selActor);
		}
	}

	/* add team members to the actor list */
	CL_ActorAddToTeamList(le);
	LE_Unlock(le);
}
/**
 * @brief Shoot with weapon.
 * @sa CL_ActorShoot
 * @sa CL_ActorShootHidden
 * @todo Improve detection of left- or right animation.
 * @sa EV_ACTOR_SHOOT
 */
void CL_ActorDoShoot (const eventRegister_t* self, dbuffer* msg)
{
	vec3_t muzzle, impact;
	int flags, normal, shooterEntnum, victimEntnum;
	int objIdx;
	int first;
	weaponFireDefIndex_t weapFdsIdx;
	fireDefIndex_t fdIdx;
	int surfaceFlags;
	shoot_types_t shootType;

	/* read data */
	NET_ReadFormat(msg, self->formatString, &shooterEntnum, &victimEntnum, &first, &objIdx, &weapFdsIdx, &fdIdx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &normal);

	le_t* leVictim;
	if (victimEntnum != SKIP_LOCAL_ENTITY) {
		leVictim = LE_Get(victimEntnum);
		if (!leVictim)
			LE_NotFoundError(victimEntnum);
	} else {
		leVictim = nullptr;
	}

	/* get shooter le */
	le_t* leShooter = LE_Get(shooterEntnum);

	/* get the fire def */
	const objDef_t* obj = INVSH_GetItemByIDX(objIdx);
	const fireDef_t* fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx);

	CL_ActorGetMuzzle(leShooter, muzzle, shootType);

	/* add effect le */
	LE_AddProjectile(fd, flags, muzzle, impact, normal, leVictim);

	/* start the sound */
	if ((first || !fd->soundOnce) && fd->fireSound != nullptr && !(flags & SF_BOUNCED))
		S_LoadAndPlaySample(fd->fireSound, muzzle, fd->fireAttenuation, SND_VOLUME_WEAPONS);

	/* do actor related stuff */
	if (!leShooter)
		return; /* maybe hidden or inuse is false? */

	if (!LE_IsActor(leShooter))
		Com_Error(ERR_DROP, "Can't shoot, LE not an actor (type: %i)", leShooter->type);

	/* no animations for hidden actors */
	if (leShooter->type == ET_ACTORHIDDEN)
		return;

	if (LE_IsDead(leShooter)) {
		Com_DPrintf(DEBUG_CLIENT, "Can't shoot, actor dead or stunned.\n");
		return;
	}

	/* Animate - we have to check if it is right or left weapon usage. */
	if (IS_SHOT_RIGHT(shootType)) {
		R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->right, leShooter->left, leShooter->state));
		R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->right, leShooter->left, leShooter->state));
	} else if (IS_SHOT_LEFT(shootType)) {
		R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->left, leShooter->right, leShooter->state));
		R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->left, leShooter->right, leShooter->state));
	} else if (IS_SHOT_HEADGEAR(shootType)) {
		if (fd->irgoggles) {
			leShooter->state |= RF_IRGOGGLESSHOT;
			if (LE_IsSelected(leShooter))
				refdef.rendererFlags |= RDF_IRGOGGLES;
		}
	} else {
		/* no animation for headgear (yet) */
		Com_Error(ERR_DROP, "CL_ActorDoShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, shooterEntnum);
	}
}
Exemple #26
0
/**
 * @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);
}