/**
 * @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;
}
void CL_InvAmmo (const eventRegister_t *self, dbuffer *msg)
{
    invList_t	*ic;
    le_t	*le;
    int		number;
    int		ammo, type, x, y;
    containerIndex_t container;

    NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y);

    le = LE_Get(number);
    if (!le) {
        Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n");
        return;
    }

    if (le->team != cls.team)
        return;

    assert(container >= 0);
    assert(container < MAX_INVDEFS);
    ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y);
    if (!ic)
        return;

    /* set new ammo */
    ic->item.ammoLeft = ammo;
    ic->item.ammo = INVSH_GetItemByIDX(type);
}
Exemple #3
0
/**
 * @brief Adds server side edicts to the client for displaying them
 * @sa EV_ADD_EDICT
 * @sa CL_EntAppear
 */
void CL_AddEdict (const eventRegister_t *self, struct dbuffer * msg)
{
	le_t *le;
	int entnum;
	entity_type_t type;
	vec3_t mins, maxs;

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

	/* use an offset to ensure that we don't conflict with any other solid edict that is already spawned */
	le = LE_Get(entnum + MAX_EDICTS);
	if (!le) {
		le = LE_Add(entnum + MAX_EDICTS);
	} else {
		Com_DPrintf(DEBUG_CLIENT, "CL_AddEdict: Entity appearing already visible... overwriting the old one\n");
		le->inuse = qtrue;
	}

	VectorCopy(mins, le->mins);
	VectorCopy(maxs, le->maxs);
	le->addFunc = CL_AddEdictFunc;
	le->type = type;

	Com_DPrintf(DEBUG_CLIENT, "CL_AddEdict: entnum: %i - type: %i\n", entnum, type);
}
/**
 * @brief Adds an hidden actor to the list of le's
 * @sa CL_ActorAppear
 * @note Actor is invisible until CL_ActorAppear (EV_ACTOR_APPEAR) was triggered
 * @note EV_ACTOR_ADD
 * @sa G_SendInvisible
 */
void CL_ActorAdd (const eventRegister_t* self, dbuffer* msg)
{
	/* check if the actor is already visible */
	const int entnum = NET_ReadShort(msg);
	le_t* le = LE_Get(entnum);
	if (le) {
		Com_Printf("CL_ActorAdd: Actor with number %i already exists\n", entnum);
		NET_SkipFormat(msg, self->formatString);
		return;
	}
	le = LE_Add(entnum);

	/* get the info */
	int teamDefID;
	NET_ReadFormat(msg, self->formatString,
				&le->team, &teamDefID,
				&le->gender, &le->pnum, &le->pos,
				&le->state, &le->fieldSize);

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

	/*Com_Printf("CL_ActorAdd: Add number: %i\n", entnum);*/

	le->type = ET_ACTORHIDDEN;

	Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin);
	le->flags = LE_INVISIBLE;
}
Exemple #5
0
void CL_InvAmmo (const eventRegister_t* self, dbuffer* msg)
{
	int		number;
	int		ammo, type, x, y;
	containerIndex_t container;

	NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y);

	le_t* le = LE_Get(number);
	if (!le) {
		Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n");
		return;
	}

	if (le->team != cls.team)
		return;

	assert(container >= 0);
	assert(container < MAX_INVDEFS);
	Item* item = le->inv.getItemAtPos(INVDEF(container), x, y);
	if (!item)
		return;

	/* set new ammo */
	item->setAmmoLeft(ammo);
	item->setAmmoDef(INVSH_GetItemByIDX(type));
}
/**
 * @brief Adds a camera edicts to the client for displaying them
 * @sa EV_CAMERA_APPEAR
 */
void CL_CameraAppear (const eventRegister_t *self, dbuffer *msg)
{
	int entnum;
	int team;
	int levelflags;
	int dir;
	int rotate;
	vec3_t origin;
	camera_type_t cameraType;

	NET_ReadFormat(msg, self->formatString, &entnum, &origin, &team, &dir, &cameraType, &levelflags, &rotate);

	le_t *le = LE_Get(entnum);
	if (!le) {
		le = LE_Add(entnum);
	} else {
		le->inuse = true;
	}

	VectorCopy(origin, le->origin);
	le->type = ET_CAMERA;
	le->team = team;
	le->angles[YAW] = directionAngles[le->angle];
	le->flags |= LE_CHECK_LEVELFLAGS;
	le->levelflags = levelflags;
	le->model1 = R_FindModel(va("objects/cameras/camera%i", cameraType));
	if (rotate) {
		const char *rotateAnim = "rotate";
		R_AnimChange(&le->as, le->model1, rotateAnim);
	}

	Com_DPrintf(DEBUG_CLIENT, "CL_CameraAppear: entnum: %i\n", entnum);
}
Exemple #7
0
/**
 * @brief Play a sound on the client side
 * @param[in] self Pointer to the event structure that is currently executed
 * @param[in] msg holds the network data
 * @sa EV_SOUND
 * @note if the last character of the sound file string that was sent by the
 * server is a '+', we will select a random sound file by replacing the '+'
 * character with a number between 01..99
 */
void CL_SoundEvent (const eventRegister_t* self, dbuffer* msg)
{
	char sound[MAX_QPATH];
	vec3_t origin;
	int number;
	int step;

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

	le_t* le = LE_Get(number);
	if (le) {
		if (LE_IsLivingActor(le) && le->team != cls.team) {
			/** @todo render */
		} else if (LE_IsDoor(le) || LE_IsBreakable(le)) {
			/** @todo render */
		}
	}

	const char* file = CL_ConvertSoundFromEvent(sound, sizeof(sound));
	Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", file, origin[0], origin[1], origin[2]);
	if (step >= 0 && step < MAX_ROUTE) {
		le_t* closest = CL_ActorGetClosest(origin, cls.team);
		if (closest != nullptr) {
			vec3_t tmp;
			VectorCopy(cl.cam.camorg, tmp);
			VectorCopy(closest->origin, cl.cam.camorg);
			S_LoadAndPlaySample(file, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT);
			VectorCopy(tmp, cl.cam.camorg);
		}
		return;
	}
	S_LoadAndPlaySample(file, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT);
}
Exemple #8
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);
    }
}
/**
 * @brief Let an entity appear - like an item on the ground that just got visible
 * @sa EV_ENT_APPEAR
 * @sa CL_EntPerish
 * @sa CL_AddEdict
 */
void CL_EntAppear (const eventRegister_t* self, dbuffer* msg)
{
	int		entnum;
	entity_type_t type;
	pos3_t	pos;

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

	/* check if the ent is already visible */
	le_t* le = LE_Get(entnum);
	if (!le) {
		le = LE_Add(entnum);
	} else {
		Com_DPrintf(DEBUG_CLIENT, "CL_EntAppear: Entity appearing already visible... overwriting the old one\n");
		le->inuse = true;
	}

	le->type = type;

	/* the default is invisible - another event will follow which spawns not
	 * only the le, but also the particle. The visibility is set there, too */
	if (le->type == ET_PARTICLE)
		LE_SetInvisible(le);

	VectorCopy(pos, le->pos);
	Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin);
}
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);
}
/**
 * @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();
}
/**
 * @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;
	}
}
/**
 * @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 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);
}
Exemple #17
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 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;
}
Exemple #20
0
/**
 * @sa CL_InvAdd
 */
void CL_InvDel (const eventRegister_t *self, dbuffer *msg)
{
	le_t	*le;
	int		number;
	int		x, y;
	containerIndex_t container;
	invList_t	*ic;

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

	le = LE_Get(number);
	if (!le)
		Com_Error(ERR_DROP, "InvDel message ignored... LE not found\n");

	/* update the local entity to ensure that the correct weapon/item is rendered in the battlescape */
	if (container == csi.idRight)
		le->right = NONE;
	else if (container == csi.idLeft)
		le->left = NONE;
	else if (container == csi.idExtension)
		le->extension = NONE;
	else if (container == csi.idHeadgear)
		le->headgear = NONE;

	if (le->type == ET_ACTOR || le->type == ET_ACTOR2x2)
		LE_SetThink(le, LET_StartIdle);

	ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y);
	/* ic can be null for other team actors - we don't the full inventory of them, only
	 * the object index */
	if (!ic)
		return;

	if (!cls.i.RemoveFromInventory(&cls.i, &le->i, INVDEF(container), ic))
		Com_Error(ERR_DROP, "CL_InvDel: No item was removed from container %i", container);

	if (le == selActor)
		Cmd_ExecuteString("hud_updateactorload");

	/* update the rendered item after it was removed from the floor container */
	if (LE_IsItem(le))
		LE_PlaceItem(le);
}
Exemple #21
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 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];
}
/**
 * @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 #25
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 Starts shooting with actor.
 * @param[in] self Pointer to the event structure that is currently executed
 * @param[in] msg The netchannel message
 * @sa CL_ActorShootHidden
 * @sa CL_ActorShoot
 * @sa CL_ActorDoShoot
 * @todo Improve detection of left- or right animation.
 * @sa EV_ACTOR_START_SHOOT
 */
void CL_ActorStartShoot (const eventRegister_t *self, dbuffer *msg)
{
	le_t *le;
	pos3_t from, target;
	int entnum;
	shoot_types_t shootType;

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

	/* shooting actor */
	le = LE_Get(entnum);

	/* center view (if wanted) */
	if (cl.actTeam != cls.team)
		CL_CameraRoute(from, target);

	/* actor dependent stuff following */
	if (!le)
		/* it's OK, the actor is not visible */
		return;

	if (!LE_IsLivingActor(le) || LE_IsStunned(le)) {
		Com_Printf("CL_ActorStartShoot: LE (%i) not a living actor (type: %i)\n", entnum, le->type);
		return;
	}

	/* ET_ACTORHIDDEN actors don't have a model yet */
	if (le->type == ET_ACTORHIDDEN)
		return;

	/* Animate - we have to check if it is right or left weapon usage. */
	if (IS_SHOT_RIGHT(shootType)) {
		R_AnimChange(&le->as, le->model1, LE_GetAnim("move", le->right, le->left, le->state));
	} else if (IS_SHOT_LEFT(shootType)) {
		R_AnimChange(&le->as, le->model1, LE_GetAnim("move", le->left, le->right, le->state));
	} else if (!IS_SHOT_HEADGEAR(shootType)) {
		/* no animation for headgear (yet) */
		Com_Error(ERR_DROP, "CL_ActorStartShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, entnum);
	}
}
/**
 * @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);
}
void CL_InvReload (const eventRegister_t* self, dbuffer* msg)
{
	int		number;
	int		ammo, type, x, y;
	containerIndex_t container;

	NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y);

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

	if (le->team != cls.team)
		return;

	assert(container >= 0);
	assert(container < MAX_INVDEFS);
	Item* ic = le->inv.getItemAtPos(INVDEF(container), x, y);
	if (!ic)
		return;

	S_LoadAndPlaySample(ic->def()->reloadSound, le->origin, ic->def()->reloadAttenuation, SND_VOLUME_WEAPONS);

	/* if the displaced clip had any remaining bullets
	 * store them as loose, unless the removed clip was full */
	equipDef_t* ed = GAME_GetEquipmentDefinition();
	if (ed && ic->getAmmoLeft() > 0 && ic->getAmmoLeft() != ic->def()->ammo) {
		assert(ammo == ic->def()->ammo);
		/* Accumulate loose ammo into clips (only accessible post-mission) */
		ed->addClip(ic);
	}

	/* set new ammo */
	ic->setAmmoLeft(ammo);
	ic->setAmmoDef(INVSH_GetItemByIDX(type));

	if (le == selActor)
		Cmd_ExecuteString("hud_updateactorload");
}
Exemple #29
0
/**
 * @brief Play a sound on the client side
 * @param[in] self Pointer to the event structure that is currently executed
 * @param[in] msg holds the network data
 * @sa EV_SOUND
 * @note if the last character of the sound file string that was sent by the
 * server is a '+', we will select a random sound file by replacing the '+'
 * character with a number between 01..99
 */
void CL_SoundEvent (const eventRegister_t *self, struct dbuffer *msg)
{
	char sound[MAX_QPATH];
	vec3_t origin;
	int number;
	le_t *le;
	size_t length;

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

	le = LE_Get(number);
	if (le) {
		if (LE_IsLivingActor(le) && le->team != cls.team) {
			/** @todo render */
		} else if (LE_IsDoor(le) || LE_IsBreakable(le)) {
			/** @todo render */
		}
	}

	length = strlen(sound) - 1;
	if (sound[length] == '+') {
		int i;

		sound[length] = '\0';
		for (i = 1; i <= 99; i++) {
			if (FS_CheckFile("sounds/%s%02i", sound, i) == -1)
				break;
		}

		Com_sprintf(sound + length, sizeof(sound) - length, "%02i", rand() % i + 1);
	}

	Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", sound, origin[0], origin[1], origin[2]);
	S_LoadAndPlaySample(sound, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT);
}
/**
 * @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;
}