Esempio n. 1
0
void G_EventSendState (unsigned int playerMask, const edict_t *ent)
{
	G_EventActorStateChange(playerMask & G_TeamToPM(ent->team), ent);

	G_EventAdd(playerMask & ~G_TeamToPM(ent->team), EV_ACTOR_STATECHANGE, ent->number);
	gi.WriteShort(ent->state & STATE_PUBLIC);
	G_EventEnd();
}
Esempio n. 2
0
void G_EventSendState (playermask_t playerMask, const Edict& ent)
{
	G_EventActorStateChange(playerMask & G_TeamToPM(ent.team), ent);

	G_EventAdd(playerMask & ~G_TeamToPM(ent.team), EV_ACTOR_STATECHANGE, ent.number);
	gi.WriteShort(ent.state & STATE_PUBLIC);
	G_EventEnd();
}
Esempio n. 3
0
/**
 * @brief Informs the client that an interaction with the world is possible
 * @note It's assumed that the clientAction is already set
 * @param[in] ent The edict that can execute the action (an actor)
 */
void G_EventSetClientAction (const Edict& ent)
{
	assert(ent.clientAction);
	assert(ent.clientAction->flags & FL_CLIENTACTION);

	/* tell the hud to show the door buttons */
	G_EventAdd(G_TeamToPM(ent.team), EV_CLIENT_ACTION, ent.number);
	gi.WriteShort(ent.clientAction->number);
	G_EventEnd();
}
Esempio n. 4
0
/**
 * @brief Send stats to network buffer
 * @sa CL_ActorStats
 */
void G_SendStats (Edict& ent)
{
	/* extra sanity checks */
	assert(ent.TU >= 0);
	ent.HP = std::max(ent.HP, 0);
	ent.STUN = std::min(ent.STUN, 255);
	ent.morale = std::max(ent.morale, 0);

	G_EventActorStats(ent, G_TeamToPM(ent.team));
}
Esempio n. 5
0
/**
 * @brief Guess! Reset all "shaken" states on end of turn?
 * @note Normally called on end of turn.
 * @sa G_ClientStateChange
 * @param[in] team Index of team to loop through.
 */
void G_ReactionFireReset (int team)
{
	edict_t *ent = NULL;

	while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team))) {
		/** @todo why do we send the state here and why do we change the "shaken"
		 * state? - see G_MoraleBehaviour */
		G_RemoveShaken(ent);

		G_EventActorStateChange(G_TeamToPM(ent->team), ent);
	}
}
Esempio n. 6
0
/**
 * @brief Checks whether an edict appear/perishes for a specific team - also
 * updates the visflags each edict carries
 * @sa G_TestVis
 * @param[in] team Team to check the vis for
 * @param[in] check The edict that you want to check (and which maybe will appear
 * or perish for the given team). If this is NULL every edict will be checked.
 * @param[in] perish Also check whether the edict (the actor) is going to become
 * invisible for the given team
 * @param[in] ent The edict that is (maybe) seeing other edicts
 * @return If an actor get visible who's no civilian VIS_STOP is added to the
 * bit mask, VIS_YES means, he is visible, VIS_CHANGE means that the actor
 * flipped its visibility (invisible to visible or vice versa), VIS_PERISH means
 * that the actor (the edict) is getting invisible for the queried team
 * @note the edict may not only be actors, but also items of course
 * @sa G_TeamToPM
 * @sa G_TestVis
 * @sa G_AppearPerishEvent
 * @sa G_CheckVisPlayer
 * @sa G_CheckVisTeamAll
 * @note If something appears, the needed information for those clients that are affected
 * are also send in @c G_AppearPerishEvent
 */
int G_CheckVisTeam (const int team, edict_t *check, bool perish, const edict_t *ent)
{
	int status = 0;

	/* check visibility */
	if (check->inuse) {
		/* check if he's visible */
		status |= G_DoTestVis(team, check, perish, G_TeamToPM(team), ent);
	}

	return status;
}
Esempio n. 7
0
/**
 * @brief Checks whether an edict appear/perishes for a specific team - also
 * updates the visflags each edict carries
 * @sa G_TestVis
 * @param[in] team Team to check the vis for
 * @param[in] check The edict that you want to check (and which maybe will appear
 * or perish for the given team). If this is nullptr every edict will be checked.
 * @param visFlags Modifiers for the checks
 * @param[in] ent The edict that is (maybe) seeing other edicts
 * @return If an actor get visible who's no civilian VIS_STOP is added to the
 * bit mask, VIS_APPEAR means, that the actor is becoming visible for the queried
 * team, VIS_PERISH means that the actor (the edict) is getting invisible
 * @note the edict may not only be actors, but also items of course
 * @sa G_TeamToPM
 * @sa G_TestVis
 * @sa G_AppearPerishEvent
 * @sa G_CheckVisPlayer
 * @sa G_CheckVisTeamAll
 * @note If something appears, the needed information for those clients that are affected
 * are also send in @c G_AppearPerishEvent
 */
static int G_CheckVisTeam (const int team, Edict* check, const vischeckflags_t visFlags, const Edict* ent)
{
	int status = 0;

	/* check visibility */
	if (check->inuse) {
		/* check if he's visible */
		status |= G_DoTestVis(team, check, visFlags, G_TeamToPM(team), ent);
	}

	return status;
}
Esempio n. 8
0
void G_EventActorAppear (playermask_t playerMask, const Edict& check, const Edict* ent)
{
	const int mask = G_TeamToPM(check.team) & playerMask;

	/* parsed in CL_ActorAppear */
	G_EventAdd(playerMask, EV_ACTOR_APPEAR, check.number);
	gi.WriteShort(ent && ent->number > 0 ? ent->number : SKIP_LOCAL_ENTITY);
	gi.WriteByte(check.team);
	gi.WriteByte(check.chr.teamDef ? check.chr.teamDef->idx : NONE);
	gi.WriteByte(check.chr.gender);
	gi.WriteShort(check.chr.ucn);
	gi.WriteByte(check.pnum);
	gi.WriteGPos(check.pos);
	gi.WriteByte(check.dir);
	if (check.getRightHandItem()) {
		gi.WriteShort(check.getRightHandItem()->def()->idx);
	} else {
		gi.WriteShort(NONE);
	}

	if (check.getLeftHandItem()) {
		gi.WriteShort(check.getLeftHandItem()->def()->idx);
	} else {
		gi.WriteShort(NONE);
	}

	if (check.body == 0 || check.head == 0) {
		gi.Error("invalid body and/or head model indices");
	}
	gi.WriteShort(check.body);
	gi.WriteShort(check.head);
	gi.WriteByte(check.chr.bodySkin);
	gi.WriteByte(check.chr.headSkin);
	/* strip the server private states */
	gi.WriteShort(check.state & STATE_PUBLIC);
	gi.WriteByte(check.fieldSize);
	/* get the max values for TU and morale */
	gi.WriteByte(G_ActorCalculateMaxTU(&check));
	gi.WriteByte(std::min(MAX_SKILL, GET_MORALE(check.chr.score.skills[ABILITY_MIND])));
	gi.WriteShort(check.chr.maxHP);
	G_EventEnd();

	if (mask) {
		G_EventActorStateChange(mask, check);
		G_SendInventory(mask, check);
	}
}
Esempio n. 9
0
/**
 * @brief Deals splash damage to a target and its surroundings.
 * @param[in] ent The shooting actor
 * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is.
 * @param[in] impact The impact vector where the grenade is exploding
 * @param[in,out] mock pseudo shooting - only for calculating mock values - NULL for real shots
 * @param[in] tr The trace where the grenade hits something (or not)
 */
static void G_SplashDamage (edict_t *ent, const fireDef_t *fd, vec3_t impact, shot_mock_t *mock, const trace_t* tr)
{
	edict_t *check = NULL;
	vec3_t center;
	float dist;
	int damage;

	const bool shock = (fd->obj->dmgtype == gi.csi->damShock);

	assert(fd->splrad > 0.0);

	while ((check = G_EdictsGetNextInUse(check))) {
		/* If we use a blinding weapon we skip the target if it's looking
		 * away from the impact location. */
		if (shock && !G_FrustumVis(check, impact))
			continue;

		if (G_IsBrushModel(check) && G_IsBreakable(check))
			VectorCenterFromMinsMaxs(check->absmin, check->absmax, center);
		else if (G_IsLivingActor(check) || G_IsBreakable(check))
			VectorCopy(check->origin, center);
		else
			continue;

		/* check for distance */
		dist = VectorDist(impact, center);
		dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0;
		if (dist > fd->splrad)
			continue;

		if (fd->irgoggles) {
			if (G_IsActor(check)) {
				/* check whether this actor (check) is in the field of view of the 'shooter' (ent) */
				if (G_FrustumVis(ent, check->origin)) {
					if (!mock) {
						const unsigned int playerMask = G_TeamToPM(ent->team) ^ G_VisToPM(check->visflags);
						G_AppearPerishEvent(playerMask, true, check, ent);
						G_VisFlagsAdd(check, G_PMToVis(playerMask));
					}
				}
			}
			continue;
		}

		/* check for walls */
		if (G_IsLivingActor(check) && !G_ActorVis(impact, ent, check, false))
			continue;

		/* do damage */
		if (shock)
			damage = 0;
		else
			damage = fd->spldmg[0] * (1.0 - dist / fd->splrad);

		if (mock)
			mock->allow_self = true;
		G_Damage(check, fd, damage, ent, mock, NULL);
		if (mock)
			mock->allow_self = false;
	}

	/** @todo splash might also hit other surfaces and the trace doesn't handle that */
	if (tr && G_FireAffectedSurface(tr->surface, fd)) {
		/* move a little away from the impact vector */
		VectorMA(impact, 1, tr->plane.normal, impact);
		G_SpawnParticle(impact, tr->contentFlags >> 8, "burning");
	}
Esempio n. 10
0
/**
 * @brief Moves an item inside an inventory. Floors are handled special.
 * @param[in] actor The pointer to the selected/used edict/soldier.
 * @param[in] fromContType The container (-id) the item should be moved from.
 * @param[in] fItem The item you want to move.
 * @param[in] toContType The container (-def) the item should be moved to.
 * @param[in] tx x position where you want the item to go in the destination container
 * @param[in] ty y position where you want the item to go in the destination container
 * @param[in] checkaction Set this to true if you want to check for TUs, otherwise false.
 * @sa event PA_INVMOVE
 * @sa AI_ActorThink
 */
bool G_ActorInvMove (Edict* actor, const invDef_t* fromContType, Item* fItem, const invDef_t* toContType, int tx, int ty, bool checkaction)
{
	Edict* floor;
	bool newFloor;
	Item* tc;
	playermask_t mask;
	inventory_action_t ia;
	Item fromItemBackup, toItemBackup;
	int fx, fy;
	int originalTU, reservedTU = 0;
	Player& player = actor->getPlayer();

	assert(fItem);
	assert(fItem->def());

	/* Store the location/item of 'from' BEFORE actually moving items with moveInInventory. */
	fromItemBackup = *fItem;

	/* Store the location of 'to' BEFORE actually moving items with moveInInventory
	 * so in case we swap ammo the client can be updated correctly */
	tc = actor->chr.inv.getItemAtPos(toContType, tx, ty);
	if (tc)
		toItemBackup = *tc;
	else
		toItemBackup = *fItem;

	/* Get first used bit in item. */
	fItem->getFirstShapePosition(&fx, &fy);
	fx += fItem->getX();
	fy += fItem->getY();

	/* Check if action is possible */
	/* TUs are 1 here - but this is only a dummy - the real TU check is done in the inventory functions below */
	if (checkaction && !G_ActionCheckForCurrentTeam(player, actor, 1))
		return false;

	if (!actor->chr.inv.canHoldItemWeight(fromContType->id, toContType->id, *fItem, actor->chr.score.skills[ABILITY_POWER])) {
		G_ClientPrintf(player, PRINT_HUD, _("This soldier can not carry anything else."));
		return false;
	}

	/* "get floor ready" - searching for existing floor-edict */
	floor = G_GetFloorItems(actor);
	if (toContType->isFloorDef() && !floor) {
		/* We are moving to the floor, but no existing edict for this floor-tile found -> create new one */
		floor = G_SpawnFloor(actor->pos);
		newFloor = true;
	} else if (fromContType->isFloorDef() && !floor) {
		/* We are moving from the floor, but no existing edict for this floor-tile found -> this should never be the case. */
		gi.DPrintf("G_ClientInvMove: No source-floor found.\n");
		return false;
	} else {
		/* There already exists an edict for this floor-tile. */
		newFloor = false;
	}

	/* search for space */
	Item* item2;
	if (tx == NONE) {
		item2 = actor->chr.inv.getItemAtPos(fromContType, fItem->getX(), fItem->getY());
		if (item2)
			actor->chr.inv.findSpace(toContType, item2, &tx, &ty, fItem);
		if (tx == NONE)
			return false;
	}

	/** @todo what if we don't have enough TUs after subtracting the reserved ones? */
	/* Because moveInInventory don't know anything about character_t and it updates actor->TU,
	 * we need to save original actor->TU for the sake of checking TU reservations. */
	originalTU = actor->TU;
	reservedTU = G_ActorGetReservedTUs(actor);
	/* Temporary decrease actor->TU to make moveInInventory do what expected. */
	G_ActorUseTU(actor, reservedTU);
	/* Try to actually move the item and check the return value after restoring valid actor->TU. */
	ia = game.i.moveInInventory(&actor->chr.inv, fromContType, fItem, toContType, tx, ty, checkaction ? &actor->TU : nullptr, &item2);
	/* Now restore the original actor->TU and decrease it for TU used for inventory move. */
	G_ActorSetTU(actor, originalTU - (originalTU - reservedTU - actor->TU));

	switch (ia) {
	case IA_NONE:
		/* No action possible - abort */
		return false;
	case IA_NOTIME:
		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not enough TUs!"));
		return false;
	case IA_NORELOAD:
		G_ClientPrintf(player, PRINT_HUD,
				_("Can't perform action - weapon already fully loaded with the same ammunition!"));
		return false;
	default:
		/* Continue below. */
		break;
	}

	/* successful inventory change; remove the item in clients */
	if (fromContType->isFloorDef()) {
		/* We removed an item from the floor - check how the client
		 * needs to be updated. */
		assert(!newFloor);
		if (actor->getFloor()) {
			/* There is still something on the floor. */
			floor->setFloor(actor);
			/* Delay this if swapping ammo, otherwise the le will be removed in the client before we can add back
			 * the current ammo because removeNextFrame is set in LE_PlaceItem() if the floor le has no items */
			if (ia != IA_RELOAD_SWAP)
				G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy);
		} else {
			/* Floor is empty, remove the edict (from server + client) if we are
			 * not moving to it. */
			if (!toContType->isFloorDef()) {
				G_EventPerish(*floor);
				G_FreeEdict(floor);
			} else
				G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy);
		}
	} else {
		G_EventInventoryDelete(*actor, G_TeamToPM(actor->team), fromContType->id, fx, fy);
	}

	/* send tu's */
	G_SendStats(*actor);

	assert(item2);
	Item item = *item2;

	if (ia == IA_RELOAD || ia == IA_RELOAD_SWAP) {
		/* reload */
		if (toContType->isFloorDef())
			mask = G_VisToPM(floor->visflags);
		else
			mask = G_TeamToPM(actor->team);

		G_EventInventoryReload(toContType->isFloorDef() ? *floor : *actor, mask, &item, toContType, item2);

		if (ia == IA_RELOAD) {
			return true;
		} else { /* ia == IA_RELOAD_SWAP */
			item.setAmmoLeft(NONE_AMMO);
			item.setAmmoDef(nullptr);
			item.setDef(toItemBackup.ammoDef());
			item.rotated = fromItemBackup.rotated;
			item.setAmount(toItemBackup.getAmount());
			toContType = fromContType;
			if (toContType->isFloorDef()) {
				/* moveInInventory placed the swapped ammo in an available space, check where it was placed
				 * so we can place it at the same place in the client, otherwise since fItem hasn't been removed
				 * this could end in a different place in the client - will cause an error if trying to use it again */
				item2 = actor->chr.inv.findInContainer(toContType->id, &item);
				assert(item2);
				fromItemBackup = item;
				fromItemBackup.setX(item2->getX());
				fromItemBackup.setY(item2->getY());
			}
			tx = fromItemBackup.getX();
			ty = fromItemBackup.getY();
		}
	}

	/* We moved an item to the floor - check how the client needs to be updated. */
	if (toContType->isFloorDef()) {
		/* we have to link the temp floor container to the new floor edict or add
		 * the item to an already existing floor edict - the floor container that
		 * is already linked might be from a different entity (this might happen
		 * in case of a throw by another actor) */
		floor->setFloor(actor);

		/* A new container was created for the floor. */
		if (newFloor) {
			/* Send item info to the clients */
			G_CheckVis(floor);
		} else {
			/* use the backup item to use the old amount values, because the clients have to use the same actions
			 * on the original amount. Otherwise they would end in a different amount of items as the server (+1) */
			G_EventInventoryAdd(*floor, G_VisToPM(floor->visflags), 1);
			G_WriteItem(fromItemBackup, toContType->id, tx, ty);
			G_EventEnd();
			/* Couldn't remove it before because that would remove the le from the client and would cause battlescape to crash
			 * when trying to add back the swapped ammo above */
			if (ia == IA_RELOAD_SWAP)
				G_EventInventoryDelete(*floor, G_VisToPM(floor->visflags), fromContType->id, fx, fy);
		}
	} else {
		G_EventInventoryAdd(*actor, G_TeamToPM(actor->team), 1);
		G_WriteItem(item, toContType->id, tx, ty);
		G_EventEnd();
	}

	G_ReactionFireSettingsUpdate(actor, actor->chr.RFmode.getFmIdx(), actor->chr.RFmode.getHand(), actor->chr.RFmode.getWeapon());

	/* Other players receive weapon info only. */
	mask = G_VisToPM(actor->visflags) & ~G_TeamToPM(actor->team);
	if (mask) {
		if (fromContType->isRightDef() || fromContType->isLeftDef()) {
			G_EventInventoryDelete(*actor, mask, fromContType->id, fx, fy);
		}
		if (toContType->isRightDef() || toContType->isLeftDef()) {
			G_EventInventoryAdd(*actor, mask, 1);
			G_WriteItem(item, toContType->id, tx, ty);
			G_EventEnd();
		}
	}

	return true;
}
Esempio n. 11
0
/**
 * @brief Deals splash damage to a target and its surroundings.
 * @param[in] ent The shooting actor
 * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is.
 * @param[in] impact The impact vector where the grenade is exploding
 * @param[in,out] mock pseudo shooting - only for calculating mock values - nullptr for real shots
 * @param[in] tr The trace where the grenade hits something (or not)
 */
static void G_SplashDamage (Actor* ent, const fireDef_t* fd, vec3_t impact, shot_mock_t* mock, const trace_t* tr)
{
	assert(fd->splrad > 0.0f);

	const bool shock = (fd->obj->dmgtype == gi.csi->damShock);

	Edict* check = nullptr;
	while ((check = G_EdictsGetNextInUse(check))) {
		/* If we use a blinding weapon we skip the target if it's looking
		 * away from the impact location. */
		if (shock && !G_FrustumVis(check, impact))
			continue;

		const bool isActor = G_IsLivingActor(check);
		vec3_t center;
		if (G_IsBrushModel(check) && G_IsBreakable(check))
			check->absBox.getCenter(center);
		else if (isActor || G_IsBreakable(check))
			VectorCopy(check->origin, center);
		else
			continue;

		/* check for distance */
		float dist = VectorDist(impact, center);
		dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0.0f;
		if (dist > fd->splrad)
			continue;

		if (fd->irgoggles) {
			if (isActor) {
				/* check whether this actor (check) is in the field of view of the 'shooter' (ent) */
				if (G_FrustumVis(ent, check->origin)) {
					if (!mock) {
						vec3_t eyeEnt;
						G_ActorGetEyeVector(ent, eyeEnt);
						if (!G_SmokeVis(eyeEnt, check)) {
							const unsigned int playerMask = G_TeamToPM(ent->getTeam()) ^ G_VisToPM(check->visflags);
							G_AppearPerishEvent(playerMask, true, *check, ent);
							G_VisFlagsAdd(*check, G_PMToVis(playerMask));
						}
					}
				}
			}
			continue;
		}

		/* check for walls */
		if (isActor && G_TestLine(impact, check->origin))
			continue;

		/* do damage */
		const int damage = shock ? 0 : fd->spldmg[0] * (1.0f - dist / fd->splrad);

		if (mock)
			mock->allow_self = true;
		/* Send hurt sounds for actors, but only if they'll recieve damage from this attack */
		if (G_Damage(check, fd, damage, ent, mock, nullptr) && isActor
				&& (G_ApplyProtection(check, fd->dmgweight,  damage) > 0) && !shock) {
			const teamDef_t* teamDef = check->chr.teamDef;
			const int gender = check->chr.gender;
			const char* sound = teamDef->getActorSound(gender, SND_HURT);
			G_EventSpawnSound(G_VisToPM(check->visflags), *check, nullptr, sound);
		}
		if (mock)
			mock->allow_self = false;
	}

	/** @todo splash might also hit other surfaces and the trace doesn't handle that */
	if (tr && G_FireAffectedSurface(tr->surface, fd)) {
		/* move a little away from the impact vector */
		VectorMA(impact, 1, tr->plane.normal, impact);
		G_SpawnParticle(impact, tr->contentFlags >> 8, "burning");
	}