Example #1
0
static void G_UseEdict_f (void)
{
	Edict* e;
	int i;

	if (gi.Cmd_Argc() < 2) {
		gi.DPrintf("Usage: %s <entnum>\n", gi.Cmd_Argv(0));
		return;
	}

	i = atoi(gi.Cmd_Argv(1));
	if (!G_EdictsIsValidNum(i)) {
		gi.DPrintf("No entity with number %i\n", i);
		return;
	}

	e = G_EdictsGetByNum(i);
	if (!e->use) {
		gi.DPrintf("No use function for entity %s\n", e->classname);
		return;
	}

	gi.DPrintf("Call use function for %s\n", e->classname);
	e->use(e, nullptr);
}
Example #2
0
void G_EventReactionFireTargetUpdate (const Edict& shooter, const Edict& target, int tus, int step)
{
	gi.QueueEvent(G_PlayerToPM(shooter.getPlayer()), EV_ACTOR_REACTIONFIRETARGETUPDATE, shooter.getIdNum());
	gi.QueueWriteShort(target.getIdNum());
	gi.QueueWriteByte(tus);
	gi.QueueWriteByte(step);
}
Example #3
0
void G_EventSendState (playermask_t playerMask, const Edict& ent)
{
	G_EventActorStateChange(playerMask & G_TeamToPM(ent.getTeam()), ent);

	G_EventAdd(playerMask & ~G_TeamToPM(ent.getTeam()), EV_ACTOR_STATECHANGE, ent.getIdNum());
	gi.WriteShort(ent.state & STATE_PUBLIC);
	G_EventEnd();
}
Example #4
0
/**
 * @brief Announce the actor die event for the clients that are seeing the actor
 * @param[in] ent The actor that is dying
 * @param[in] attacker Whether the actor was killed or just died
 */
void G_EventActorDie (const Edict& ent, bool attacker)
{
	G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_DIE, ent.getIdNum());
	gi.WriteShort(ent.state);
	gi.WriteByte(ent.getPlayerNum());
	gi.WriteByte(attacker);
	G_EventEnd();
}
Example #5
0
void G_EventActorStats (const Edict& ent, playermask_t playerMask)
{
	G_EventAdd(playerMask, EV_ACTOR_STATS, ent.getIdNum());
	gi.WriteByte(ent.TU);
	gi.WriteShort(ent.HP);
	gi.WriteByte(ent.getStun());
	gi.WriteByte(ent.morale);
	G_EventEnd();
}
Example #6
0
TEST_F(GameTest, InventoryForDiedAlien)
{
	const char* mapName = "test_game";
	ASSERT_NE(-1, FS_CheckFile("maps/%s.bsp", mapName)) << "Map resource '" << mapName << ".bsp' for test is missing.";
	Actor* diedEnt;
	Actor* actor;
	Edict* floorItems;
	Item* invlist;
	int count;
	SV_Map(true, mapName, nullptr);
	level.activeTeam = TEAM_ALIEN;

	/* first alien that should die and drop its inventory */
	diedEnt = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN);
	ASSERT_TRUE(nullptr != diedEnt);
	diedEnt->HP = 0;
	ASSERT_TRUE(G_ActorDieOrStun(diedEnt, nullptr));
	ASSERT_TRUE(diedEnt->isDead());

	/* now try to collect the inventory with a second alien */
	actor = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN);
	ASSERT_TRUE(nullptr != actor);

	/* move to the location of the first died alien to drop the inventory into the same floor container */
	Player& player = actor->getPlayer();
	ASSERT_TRUE(G_IsAIPlayer(&player));
	G_ClientMove(player, 0, actor, diedEnt->pos);
	ASSERT_TRUE(VectorCompare(actor->pos, diedEnt->pos));

	floorItems = G_GetFloorItems(actor);
	ASSERT_TRUE(nullptr != floorItems);
	ASSERT_EQ(floorItems->getFloor(), actor->getFloor());

	/* drop everything to floor to make sure we have space in the backpack */
	G_InventoryToFloor(actor);
	ASSERT_EQ(0, GAMETEST_GetItemCount(actor, CID_BACKPACK));

	invlist = actor->getContainer(CID_BACKPACK);
	ASSERT_TRUE(nullptr == invlist);
	count = GAMETEST_GetItemCount(actor, CID_FLOOR);
	if (count > 0) {
		Item* entryToMove = actor->getFloor();
		int tx, ty;
		actor->chr.inv.findSpace(INVDEF(CID_BACKPACK), entryToMove, &tx, &ty, entryToMove);
		if (tx == NONE)
			return;
		Com_Printf("trying to move item %s from floor into backpack to pos %i:%i\n", entryToMove->def()->name, tx, ty);
		ASSERT_TRUE(G_ActorInvMove(actor, INVDEF(CID_FLOOR), entryToMove, INVDEF(CID_BACKPACK), tx, ty, false));
		ASSERT_EQ(count - 1, GAMETEST_GetItemCount(actor, CID_FLOOR)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack";
		Com_Printf("item %s was removed from floor\n", entryToMove->def()->name);
		ASSERT_EQ(1, GAMETEST_GetItemCount(actor, CID_BACKPACK)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack";
		Com_Printf("item %s was moved successfully into the backpack\n", entryToMove->def()->name);
		invlist = actor->getContainer(CID_BACKPACK);
		ASSERT_TRUE(nullptr != invlist);
	}
}
Example #7
0
/**
 * @brief Make everything visible to anyone who can't already see it
 */
void G_VisMakeEverythingVisible (void)
{
	Edict* ent = nullptr;
	while ((ent = G_EdictsGetNextInUse(ent))) {
		const int playerMask = G_VisToPM(ent->visflags);
		G_AppearPerishEvent(~playerMask, true, *ent, nullptr);
		if (G_IsActor(ent))
			G_SendInventory(~G_TeamToPM(ent->getTeam()), *ent);
	}
}
Example #8
0
/**
 * @brief Send info about an actor's wounds to the client.
 * @param[in] ent The actor whose wound status we are sending.
 * @param[in] bodyPart The body part index we are sending wound info about.
 * @note This event is sent to the player this actor belongs to
 */
void G_EventActorWound (const Edict& ent, const int bodyPart)
{
	const int mask = G_PlayerToPM(ent.getPlayer());
	G_EventAdd(mask, EV_ACTOR_WOUND, ent.getIdNum());
	gi.WriteByte(bodyPart);
	const woundInfo_t& wounds = ent.chr.wounds;
	gi.WriteByte(wounds.woundLevel[bodyPart]);
	gi.WriteByte(wounds.treatmentLevel[bodyPart]);
	G_EventEnd();
}
Example #9
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.getTeam()), EV_CLIENT_ACTION, ent.getIdNum());
	gi.WriteShort(ent.clientAction->getIdNum());
	G_EventEnd();
}
Example #10
0
/**
 * @brief Will inform the player about the real TU reservation
 * @param ent The actors edict.
 */
void G_EventActorSendReservations (const Edict& ent)
{
	G_EventAdd(G_PlayerToPM(ent.getPlayer()), EV_ACTOR_RESERVATIONCHANGE, ent.getIdNum());
	const chrReservations_t& reservedTUs = ent.chr.reservedTus;
	gi.WriteShort(reservedTUs.reaction);
	gi.WriteShort(reservedTUs.shot);
	gi.WriteShort(reservedTUs.crouch);

	G_EventEnd();
}
Example #11
0
/**
 * @brief Applies morale behaviour on actors
 * @note only called when mor_panic is not zero
 * @sa G_MoralePanic
 * @sa G_MoraleRage
 * @sa G_MoraleStopRage
 * @sa G_MoraleStopPanic
 */
void G_MoraleBehaviour (int team)
{
	bool enabled = G_IsMoraleEnabled(team);
	if (!enabled)
		return;

	Edict* ent = nullptr;
	while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team)) != nullptr) {
		/* this only applies to ET_ACTOR but not to ET_ACTOR2x2 */
		if (ent->type != ET_ACTOR || CHRSH_IsTeamDefRobot(ent->chr.teamDef))
			continue;

		/* if panic, determine what kind of panic happens: */
		if (!G_IsPanicked(ent) && !G_IsRaged(ent)) {
			if (ent->morale <= mor_panic->integer) {
				const float ratio = (float) ent->morale / mor_panic->value;
				const bool sanity = ratio > (m_sanity->value * frand());
				if (!sanity)
					G_SetInsane(ent);
				if (ratio > (m_rage->value * frand()))
					G_MoralePanic(ent);
				else
					G_MoraleRage(ent);
				/* if shaken, well .. be shaken; */
			} else if (ent->morale <= mor_shaken->integer) {
				/* shaken is later reset along with reaction fire */
				G_SetShaken(ent);
				G_ClientStateChange(ent->getPlayer(), ent, STATE_REACTION, false);
				G_EventSendState(G_VisToPM(ent->visflags), *ent);
				G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("%s is currently shaken."),
						ent->chr.name);
				G_PrintStats("%s is shaken (entnum %i).", ent->chr.name, ent->getIdNum());
			}
		} else {
			if (G_IsPanicked(ent))
				G_MoraleStopPanic(ent);
			else if (G_IsRaged(ent))
				G_MoraleStopRage(ent);
		}

		G_ActorSetMaxs(ent);

		/* morale-regeneration, capped at max: */
		int newMorale = ent->morale + MORALE_RANDOM(mor_regeneration->value);
		const int maxMorale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]);
		if (newMorale > maxMorale)
			ent->morale = maxMorale;
		else
			ent->morale = newMorale;

		/* send phys data and state: */
		G_SendStats(*ent);
	}
}
Example #12
0
void G_EventReactionFireChange (const Edict& ent)
{
	const FiremodeSettings& fireMode = ent.chr.RFmode;
	const objDef_t* od = fireMode.getWeapon();

	G_EventAdd(G_PlayerToPM(ent.getPlayer()), EV_ACTOR_REACTIONFIRECHANGE, ent.getIdNum());
	gi.WriteByte(fireMode.getFmIdx());
	gi.WriteByte(fireMode.getHand());
	gi.WriteShort(od ? od->idx : NONE);

	G_EventEnd();
}
Example #13
0
/**
 * @brief Send an appear event to the client.
 * @param playerMask The players to send the event to
 * @param ent The camera that should appear to the players included in the given mask.
 */
void G_EventCameraAppear (playermask_t playerMask, const Edict& ent)
{
	G_EventAdd(playerMask, EV_CAMERA_APPEAR, ent.getIdNum());
	gi.WritePos(ent.origin);
	gi.WriteByte(ent.getTeam());
	gi.WriteByte(ent.dir);
	gi.WriteByte(ent.camera.cameraType);
	/* strip the higher bits - only send levelflags */
	gi.WriteByte(ent.spawnflags & 0xFF);
	gi.WriteByte(ent.camera.rotate);
	G_EventEnd();
}
Example #14
0
/**
 * @sa CL_ActorAdd
 */
void G_EventActorAdd (playermask_t playerMask, const Edict& ent)
{
	G_EventAdd(playerMask, EV_ACTOR_ADD, ent.getIdNum());
	gi.WriteByte(ent.getTeam());
	gi.WriteByte(ent.chr.teamDef ? ent.chr.teamDef->idx : NONE);
	gi.WriteByte(ent.chr.gender);
	gi.WriteByte(ent.getPlayerNum());
	gi.WriteGPos(ent.pos);
	gi.WriteShort(ent.state & STATE_PUBLIC);
	gi.WriteByte(ent.fieldSize);
	G_EventEnd();
}
Example #15
0
void G_EventModelExplode (const Edict& ent, const char* sound)
{
	assert(ent.inuse);
	G_EventAdd(PM_ALL, EV_MODEL_EXPLODE, ent.getIdNum());
	gi.WriteString(sound);
	G_EventEnd();
}
Example #16
0
/**
 * @brief Send disappear event
 * @param[in] playerMask The bitmask to determine the clients this event is send to
 * @param[in,out] ent The edict that perished
 */
void G_EventEdictPerish (playermask_t playerMask, const Edict& ent)
{
	assert(ent.inuse);
	G_EventAdd(playerMask, EV_ENT_PERISH, ent.getIdNum());
	gi.WriteByte(ent.type);
	G_EventEnd();
}
Example #17
0
/**
 * @brief Send an appear event to the client.
 * @param playerMask The players to send the event to
 * @param ent The edict that should appear to the players included in the given mask.
 * @note Each following event that is relying on the fact that this edict must already
 * be known in the client, must also adopt the client side parsing of the event times.
 */
void G_EventEdictAppear (playermask_t playerMask, const Edict& ent)
{
	G_EventAdd(playerMask, EV_ENT_APPEAR, ent.getIdNum());
	gi.WriteByte(ent.type);
	gi.WriteGPos(ent.pos);
	G_EventEnd();
}
Example #18
0
void G_EventReactionFireAddTarget (const Edict& shooter, const Edict& target, int tus, int step)
{
	gi.QueueEvent(G_PlayerToPM(shooter.getPlayer()), EV_ACTOR_REACTIONFIREADDTARGET, shooter.number);
	gi.QueueWriteShort(target.number);
	gi.QueueWriteByte(tus);
	gi.QueueWriteByte(step);
}
Example #19
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);
	}
}
Example #20
0
/**
 * @brief Called every frame to let the edicts tick
 */
void G_EdictsThink (void)
{
	/* treat each object in turn */
	/* even the world gets a chance to think */
	Edict* ent = nullptr;
	while ((ent = G_EdictsGetNextInUse(ent))) {
		if (!ent->think)
			continue;
		if (ent->nextthink <= 0)
			continue;
		if (ent->nextthink > level.time + 0.001f)
			continue;

		ent->nextthink = level.time + SERVER_FRAME_SECONDS;
		ent->think(ent);
	}
}
Example #21
0
void G_EventSpawnFootstepSound (const Edict& ent, const char* sound)
{
	const playermask_t playerMask = ~G_VisToPM(ent.visflags);
	gi.QueueEvent(playerMask, EV_SOUND, ent.getIdNum());
	gi.QueueWritePos(ent.origin);
	gi.QueueWriteByte(ent.moveinfo.steps);
	gi.QueueWriteString(sound);
}
Example #22
0
/**
 * Send a particle spawn event to the client
 * @param[in] playerMask The clients that should see the particle
 * @param[in] ent The particle to spawn
 */
void G_EventSendParticle (playermask_t playerMask, const Edict& ent)
{
	G_EventAdd(playerMask, EV_PARTICLE_APPEAR, ent.getIdNum());
	gi.WriteShort(ent.spawnflags);
	gi.WritePos(ent.origin);
	gi.WriteString(ent.particle);
	G_EventEnd();
}
Example #23
0
/**
 * @brief Start the shooting event
 * @param ent The entity that starts the shooting
 * @param teamMask the vis mask of the teams to determine the clients from this event is send to
 * @param shootType The type of the shoot
 * @param at The grid position to target to
 */
void G_EventStartShoot (const Edict& ent, teammask_t teamMask, shoot_types_t shootType, const pos3_t at)
{
	G_EventAdd(G_VisToPM(teamMask), EV_ACTOR_START_SHOOT, ent.getIdNum());
	gi.WriteByte(shootType);
	gi.WriteGPos(ent.pos);
	gi.WriteGPos(at);
	G_EventEnd();
}
Example #24
0
/**
 * @brief Tell the client to remove the item from the container
 * @param[in] ent Pointer to entity having given inventory.
 * @param[in] playerMask The player mask to determine which clients should receive the event (e.g. @c G_VisToPM(ent->visflags))
 * @param[in] containerId Id of the container the item is in.
 * @param[in] x,y Position of item in container.
 */
void G_EventInventoryDelete (const Edict& ent, playermask_t playerMask, const containerIndex_t containerId, int x, int y)
{
	G_EventAdd(playerMask, EV_INV_DEL, ent.getIdNum());
	gi.WriteByte(containerId);
	gi.WriteByte(x);
	gi.WriteByte(y);
	G_EventEnd();
}
Example #25
0
/**
 * @brief Either finds a free edict, or allocates a new one.
 * @note Try to avoid reusing an entity that was recently freed, because it
 * can cause the player to think the entity morphed into something else
 * instead of being removed and recreated, which can cause interpolated
 * angles and bad trails.
 * @sa G_FreeEdict
 */
Edict* G_Spawn (const char* classname)
{
	Edict* ent = G_EdictsGetNewEdict();

	if (!ent)
		gi.Error("G_Spawn: no free edicts");

	ent->inuse = true;
	ent->number = G_EdictsGetNumber(ent);
	if (classname)
		ent->classname = classname;
	else
		ent->classname = "noclass";
	ent->fieldSize = ACTOR_SIZE_NORMAL;
	ent->setActive();		/* only used by camera */
	return ent;
}
Example #26
0
/**
 * @brief Send the bounding box info to the client.
 * @param[in] ent The edict to send the bounding box for
 */
void G_EventSendEdict (const Edict& ent)
{
	G_EventAdd(PM_ALL, EV_ADD_EDICT, ent.getIdNum());
	gi.WriteByte(ent.type);
	gi.WritePos(ent.absBox.mins);
	gi.WritePos(ent.absBox.maxs);
	G_EventEnd();
}
Example #27
0
/**
 * @brief Triggers the end of the game. Will be executed in the next server (or game) frame.
 * @param[in] team The winning team
 * @param[in] timeGap Second to wait before really ending the game. Useful if you want to allow a last view on the scene
 */
void G_MatchEndTrigger (int team, int timeGap)
{
	bool foundNextMap = false;
	Edict* ent = nullptr;

	while ((ent = G_EdictsGetTriggerNextMaps(ent)) != nullptr) {
		if (ent->getTeam() == team) {
			ent->think = Think_NextMapTrigger;
			ent->nextthink = 1;
			foundNextMap = true;
		}
	}

	if (!foundNextMap) {
		const int realTimeGap = timeGap > 0 ? level.time + timeGap : 1;
		level.winningTeam = team;
		level.intermissionTime = realTimeGap;
	}
}
Example #28
0
void G_EventInventoryReload (const Edict& ent, playermask_t playerMask, const Item* item, const invDef_t* invDef, const Item* ic)
{
	G_EventAdd(playerMask, EV_INV_RELOAD, ent.getIdNum());
	gi.WriteByte(item->def()->ammo);
	gi.WriteByte(item->ammoDef()->idx);
	gi.WriteByte(invDef->id);
	gi.WriteByte(ic->getX());
	gi.WriteByte(ic->getY());
	G_EventEnd();
}
Example #29
0
/**
 * @brief Mission trigger use function
 */
bool G_MissionUse (Edict* self, Edict* activator)
{
	Edict* target = G_EdictsFindTargetEntity(self->target);
	if (!target) {
		gi.DPrintf("Target '%s' wasn't found for misc_mission\n", self->target);
		G_FreeEdict(self);
		return false;
	}

	if (target->destroy) {
		/* set this to zero to determine that this is a triggered destroy call */
		target->HP = 0;
		target->destroy(target);
		/* freed when the level changes */
		self->target = nullptr;
		self->use = nullptr;
	} else if (target->use)
		target->use(target, activator);

	return true;
}
Example #30
0
void G_EventActorFall (const Edict& ent)
{
	G_EventAdd(G_VisToPM(ent.visflags), EV_ACTOR_MOVE, ent.getIdNum());
	gi.WriteByte(1);
	gi.WriteByte(ent.pos[0]);
	gi.WriteByte(ent.pos[1]);
	gi.WriteByte(ent.pos[2]);
	gi.WriteByte(makeDV(DIRECTION_FALL, ent.pos[2]));
	gi.WriteShort(GRAVITY);
	gi.WriteShort(0);
	G_EventEnd();
}