Beispiel #1
0
/**
 * @brief Chain together all entities with a matching team field.
 * All but the first will have the FL_GROUPSLAVE flag set.
 * All but the last will have the groupchain field set to the next one
 */
static void G_FindEdictGroups (void)
{
	Edict* ent = G_EdictsGetFirst(); /* the first edict is always a world edict that can be skipped */

	while ((ent = G_EdictsGetNextInUse(ent))) {
		/* no group at all */
		if (!ent->group)
			continue;
		/* already marked as slave in another group */
		if (ent->flags & FL_GROUPSLAVE)
			continue;
		Edict* chain = ent;
		ent->groupMaster = ent;
		Edict* groupMember = ent;
		/* search only the remainder of the entities */
		while ((groupMember = G_EdictsGetNextInUse(groupMember))) {
			/* no group at all */
			if (!groupMember->group)
				continue;
			/* already marked as slave in another group */
			if (groupMember->flags & FL_GROUPSLAVE)
				continue;
			/* same group as the master? */
			if (Q_streq(ent->group, groupMember->group)) {
				chain->groupChain = groupMember;
				groupMember->groupMaster = ent;
				chain = groupMember;
				groupMember->flags |= FL_GROUPSLAVE;
			}
		}
	}
}
Beispiel #2
0
/**
 * @brief Find valid actor spawn fields for this player.
 * @note Already used spawn-point are not found because ent->type is changed in G_ClientTeamInfo.
 * @param[in] player The player to spawn the actors for.
 * @param[in] spawnType The type of spawn-point so search for (ET_ACTORSPAWN or ET_ACTOR2x2SPAWN)
 * @return A pointer to a found spawn point or NULL if nothing was found or on error.
 */
static edict_t *G_ClientGetFreeSpawnPoint (const player_t * player, int spawnType)
{
	edict_t *ent = NULL;

	/* Abort for non-spawnpoints */
	assert(spawnType == ET_ACTORSPAWN || spawnType == ET_ACTOR2x2SPAWN);

	if (level.noRandomSpawn) {
		while ((ent = G_EdictsGetNextInUse(ent)))
			if (ent->type == spawnType && player->pers.team == ent->team) {
				if (G_EdictsGetLivingActorFromPos(ent->pos))
					continue;
				return ent;
			}
	} else {
		edict_t *list[MAX_EDICTS];
		int count = 0;
		while ((ent = G_EdictsGetNextInUse(ent)))
			if (ent->type == spawnType && player->pers.team == ent->team) {
				if (G_EdictsGetLivingActorFromPos(ent->pos))
					continue;
				list[count++] = ent;
			}

		if (count)
			return list[rand() % count];
	}

	return NULL;
}
Beispiel #3
0
/**
 * @brief Chain together all entities with a matching team field.
 * All but the first will have the FL_GROUPSLAVE flag set.
 * All but the last will have the groupchain field set to the next one
 */
static void G_FindEdictGroups (void)
{
	edict_t *ent = G_EdictsGetFirst(); /* the first edict is always a world edict that can be skipped */

	while ((ent = G_EdictsGetNextInUse(ent))) {
		edict_t *ent2, *chain;

		if (!ent->group)
			continue;
		if (ent->flags & FL_GROUPSLAVE)
			continue;
		chain = ent;
		ent->groupMaster = ent;
		ent2 = ent;			/* search only the remainder of the entities */
		while ((ent2 = G_EdictsGetNextInUse(ent2))) {
			if (!ent2->group)
				continue;
			if (ent2->flags & FL_GROUPSLAVE)
				continue;
			if (Q_streq(ent->group, ent2->group)) {
				chain->groupChain = ent2;
				ent2->groupMaster = ent;
				chain = ent2;
				ent2->flags |= FL_GROUPSLAVE;
			}
		}
	}
}
Beispiel #4
0
static void testSpawnAndConnect (void)
{
	char userinfo[MAX_INFO_STRING];
	player_t *player;
	const char *name = "name";
	bool day = true;
	byte *buf;
	/* this entity string may not contain any inline models, we don't have the bsp tree loaded here */
	const int size = FS_LoadFile("game/entity.txt", &buf);
	edict_t *e = NULL;
	int cnt = 0;

	CU_ASSERT_NOT_EQUAL_FATAL(size, -1);
	CU_ASSERT_FATAL(size > 0);

	SV_InitGameProgs();
	/* otherwise we can't link the entities */
	SV_ClearWorld();

	player = G_PlayerGetNextHuman(0);
	svs.ge->SpawnEntities(name, day, (const char *)buf);
	CU_ASSERT_TRUE(svs.ge->ClientConnect(player, userinfo, sizeof(userinfo)));
	CU_ASSERT_FALSE(svs.ge->RunFrame());

	while ((e = G_EdictsGetNextInUse(e))) {
		Com_Printf("entity %i: %s\n", cnt, e->classname);
		cnt++;
	}

	CU_ASSERT_EQUAL(cnt, 45);

	SV_ShutdownGameProgs();
	FS_FreeFile(buf);
}
Beispiel #5
0
/**
 * @brief Test if point is "visible" from team.
 * @param[in] team A team to test.
 * @param[in] point A point to check.
 * @return true if point is "visible"
 */
static bool G_TeamPointVis (int team, const vec3_t point)
{
	Edict *from = nullptr;
	vec3_t eye;

	/* test if point is visible from team */
	while ((from = G_EdictsGetNextLivingActorOfTeam(from, team))) {
		if (G_FrustumVis(from, point)) {
			/* get viewers eye height */
			G_ActorGetEyeVector(from, eye);

			/* line of sight */
			if (!G_TestLine(eye, point)) {
				const float distance = VectorDist(from->origin, point);
				bool blocked = false;
				/* check visibility in the smoke */
				if (distance >= UNIT_SIZE) {
					Edict *e = nullptr;
					while ((e = G_EdictsGetNextInUse(e))) {
						if (G_IsSmoke(e) && RayIntersectAABB(eye, point, e->absmin, e->absmax)) {
								blocked = true;
								break;
						}
					}
				}
				if (!blocked)
					return true;
			}
		}
	}

	/* not visible */
	return false;
}
Beispiel #6
0
/**
 * @brief Searches an edict that is not of the given types at the given grid location.
 * @param pos The grid location to look for an edict.
 * @param n The amount of given entity_type_t values that are given via variadic arguments to this function.
 * @return @c NULL if nothing was found, otherwise the entity located at the given grid position.
 */
edict_t *G_GetEdictFromPosExcluding (const pos3_t pos, const int n, ...)
{
    edict_t *ent = NULL;
    entity_type_t types[ET_MAX];
    va_list ap;
    int i;

    assert(n > 0);
    assert(n < sizeof(types));

    va_start(ap, n);

    for (i = 0; i < n; i++) {
        types[i] = va_arg(ap, entity_type_t);
    }

    while ((ent = G_EdictsGetNextInUse(ent))) {
        for (i = 0; i < n; i++)
            if (ent->type == types[i])
                break;
        if (i != n)
            continue;
        if (VectorCompare(pos, ent->pos))
            return ent;
    }
    /* nothing found at this pos */
    return NULL;
}
Beispiel #7
0
/**
 * @brief Call the reset function for those triggers that are no longer touched (left the trigger zone)
 * @param ent The edict that is leaving the trigger area
 * @param touched The edicts that the activating ent currently touches
 * @param num The amount of edicts in the @c touched list
 */
static void G_ResetTriggers (edict_t *ent, edict_t **touched, int num)
{
    edict_t *trigger = NULL;

    /* check all edicts to find all triggers */
    while ((trigger = G_EdictsGetNextInUse(trigger))) {
        if (trigger->solid == SOLID_TRIGGER) {
            /* check if our edict is among the known triggerers of this trigger */
            if (G_TriggerIsInList(trigger, ent)) {
                /* if so, check if it still touches it */
                int i;
                for (i = 0; i < num; i++) {
                    if (touched[i] == trigger)
                        break;	/* Yes ! */
                }
                if (i == num) {	/* No ! */
                    G_TriggerRemoveFromList(trigger, ent);
                    /* the ent left the trigger area */
                    if (trigger->reset != NULL)
                        trigger->reset(trigger, ent);
                }
            }
        }
    }
}
Beispiel #8
0
TEST_F(GameTest, SpawnAndConnect)
{
	char userinfo[MAX_INFO_STRING];
	player_t* player;
	const char* name = "name";
	bool day = true;
	byte* buf;
	/* this entity string may not contain any inline models, we don't have the bsp tree loaded here */
	const int size = FS_LoadFile("game/entity.txt", &buf);
	Edict* e = nullptr;
	int cnt = 0;

	ASSERT_NE(size, -1) << "could not load game/entity.txt.";
	ASSERT_TRUE(size > 0) << "game/entity.txt is empty.";

	SV_InitGameProgs();
	/* otherwise we can't link the entities */
	SV_ClearWorld();

	player = G_PlayerGetNextHuman(0);
	svs.ge->SpawnEntities(name, day, (const char*)buf);
	ASSERT_TRUE(svs.ge->ClientConnect(player, userinfo, sizeof(userinfo))) << "Failed to connect the client";
	ASSERT_FALSE(svs.ge->RunFrame()) << "Failed to run the server logic frame tick";

	while ((e = G_EdictsGetNextInUse(e))) {
		Com_Printf("entity %i: %s\n", cnt, e->classname);
		cnt++;
	}

	ASSERT_EQ(cnt, 43);

	FS_FreeFile(buf);
}
Beispiel #9
0
/**
 * @brief Build the forbidden list for the pathfinding (server side).
 * @param[in] team The team number if the list should be calculated from the eyes of that team. Use 0 to ignore team.
 * @param[in] movingActor The moving actor to build the forbidden list for. If this is an AI actor, everything other actor will be
 * included in the forbidden list - even the invisible ones. This is needed to ensure that they are not walking into each other
 * (civilians <=> aliens, aliens <=> civilians)
 * @sa G_MoveCalc
 * @sa Grid_CheckForbidden
 * @sa CL_BuildForbiddenList <- shares quite some code
 * @note This is used for pathfinding.
 * It is a list of where the selected unit can not move to because others are standing there already.
 */
static void G_BuildForbiddenList (int team, const edict_t *movingActor)
{
    edict_t *ent = NULL;
    int visMask;

    forbiddenListLength = 0;

    /* team visibility */
    if (team)
        visMask = G_TeamToVisMask(team);
    else
        visMask = TEAM_ALL;

    while ((ent = G_EdictsGetNextInUse(ent))) {
        /* Dead 2x2 unit will stop walking, too. */
        if (G_IsBlockingMovementActor(ent) && (G_IsAI(movingActor) || (ent->visflags & visMask))) {
            forbiddenList[forbiddenListLength++] = ent->pos;
            forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize;
        } else if (ent->type == ET_SOLID) {
            int j;
            for (j = 0; j < ent->forbiddenListSize; j++) {
                forbiddenList[forbiddenListLength++] = ent->forbiddenListPos[j];
                forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize;
            }
        }
    }

    if (forbiddenListLength > MAX_FORBIDDENLIST)
        gi.Error("G_BuildForbiddenList: list too long\n");
}
Beispiel #10
0
TEST_F(GameTest, DoorTrigger)
{
	const char* mapName = "test_game";
	ASSERT_NE(-1, FS_CheckFile("maps/%s.bsp", mapName)) << "Map resource '" << mapName << ".bsp' for test is missing.";
	Edict* e = nullptr;
	int cnt = 0;
	int doors = 0;

	SV_Map(true, mapName, nullptr);
	while ((e = G_EdictsGetNextInUse(e))) {
		cnt++;
		if (e->type != ET_DOOR)
			continue;
		if (Q_streq(e->targetname, "left-0")) {
			ASSERT_TRUE(e->doorState) << "this one is triggered by an actor standing inside of a trigger_touch";
		} else if (Q_streq(e->targetname, "right-0")) {
			ASSERT_FALSE(e->doorState) << "this one has a trigger_touch, too - but nobody is touching that trigger yet";
		} else {
			ASSERT_TRUE(false) << "both of the used doors have a targetname set";
		}
		doors++;
	}

	ASSERT_TRUE(cnt > 0);
	ASSERT_TRUE(doors == 2);
}
Beispiel #11
0
static void G_SendBoundingBoxes (void)
{
	if (sv_send_edicts->integer) {
		edict_t *ent = G_EdictsGetFirst();	/* skip the world */
		while ((ent = G_EdictsGetNextInUse(ent)))
			G_EventSendEdict(ent);
	}
}
Beispiel #12
0
/**
 * @sa G_RecalcRouting
 */
void G_CompleteRecalcRouting (void)
{
    edict_t *ent = NULL;

    while ((ent = G_EdictsGetNextInUse(ent)))
        if (IS_BMODEL(ent))
            G_RecalcRouting(ent->model);
}
Beispiel #13
0
/**
 * @brief Reset the visflags for all edicts in the global list for the
 * given team - and only for the given team
 */
void G_VisFlagsClear (int team)
{
	Edict* ent = nullptr;
	const teammask_t teamMask = ~G_TeamToVisMask(team);
	while ((ent = G_EdictsGetNextInUse(ent))) {
		ent->visflags &= teamMask;
	}
}
Beispiel #14
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)
{
	edict_t *ent = NULL;
	int newMorale;

	while ((ent = G_EdictsGetNextInUse(ent))) {
		/* this only applies to ET_ACTOR but not to ET_ACTOR2x2 */
		if (ent->type == ET_ACTOR && ent->team == team && !G_IsDead(ent)) {
			/* civilians have a 1:1 chance to randomly run away in multiplayer */
			if (sv_maxclients->integer >= 2 && level.activeTeam == TEAM_CIVILIAN && 0.5 > frand())
				G_MoralePanic(ent, qfalse);
			/* multiplayer needs enabled sv_enablemorale */
			/* singleplayer has this in every case */
			if (G_IsMoraleEnabled()) {
				/* if panic, determine what kind of panic happens: */
				if (ent->morale <= mor_panic->value && !G_IsPaniced(ent) && !G_IsRaged(ent)) {
					qboolean sanity;
					if ((float) ent->morale / mor_panic->value > (m_sanity->value * frand()))
						sanity = qtrue;
					else
						sanity = qfalse;
					if ((float) ent->morale / mor_panic->value > (m_rage->value * frand()))
						G_MoralePanic(ent, sanity);
					else
						G_MoraleRage(ent, sanity);
					/* if shaken, well .. be shaken; */
				} else if (ent->morale <= mor_shaken->value && !G_IsPaniced(ent)
						&& !G_IsRaged(ent)) {
					/* shaken is later reset along with reaction fire */
					G_SetShaken(ent);
					G_SetState(ent, STATE_REACTION);
					G_EventSendState(G_VisToPM(ent->visflags), ent);
					G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s is currently shaken.\n"),
							ent->chr.name);
				} else {
					if (G_IsPaniced(ent))
						G_MoraleStopPanic(ent);
					else if (G_IsRaged(ent))
						G_MoraleStopRage(ent);
				}
			}

			G_ActorSetMaxs(ent);

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

			/* send phys data and state: */
			G_SendStats(ent);
			gi.EndEvents();
		}
	}
}
Beispiel #15
0
/**
 * @brief Iterate through the living actor entities
 * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
 */
Edict* G_EdictsGetNextLivingActor (Edict* lastEnt)
{
	Edict* ent = lastEnt;

	while ((ent = G_EdictsGetNextInUse(ent))) {
		if (G_IsLivingActor(ent))
			break;
	}
	return ent;
}
Beispiel #16
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);
	}
}
Beispiel #17
0
/**
 * @brief Iterator through all the trigger_nextmap edicts
 * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
 */
Edict* G_EdictsGetTriggerNextMaps (Edict* lastEnt)
{
	Edict* ent = lastEnt;

	while ((ent = G_EdictsGetNextInUse(ent))) {
		if (G_IsTriggerNextMap(ent))
			break;
	}
	return ent;
}
Beispiel #18
0
/**
 * @brief Sets visible edict on player spawn
 * @sa G_ClientStartMatch
 * @sa G_CheckVisTeam
 * @sa G_AppearPerishEvent
 */
void G_CheckVisPlayer (Player& player, const vischeckflags_t visFlags)
{
	Edict* ent = nullptr;

	/* check visibility */
	while ((ent = G_EdictsGetNextInUse(ent))) {
		/* check if he's visible */
		G_DoTestVis(player.getTeam(), ent, visFlags, G_PlayerToPM(player), nullptr);
	}
}
Beispiel #19
0
/**
 * @brief Do @c G_CheckVisTeam for all entities
 * ent is the one that is looking at the others
 */
int G_CheckVisTeamAll (const int team, const vischeckflags_t visFlags, const Edict* ent)
{
	Edict* chk = nullptr;
	int status = 0;

	while ((chk = G_EdictsGetNextInUse(chk))) {
		status |= G_CheckVisTeam(team, chk, visFlags, ent);
	}
	return status;
}
Beispiel #20
0
/**
 * @brief Reset the visflags for all edicts in the global list for the
 * given team - and only for the given team
 */
void G_VisFlagsClear (int team)
{
	edict_t *ent = NULL;
	vismask_t mask;

	mask = ~G_TeamToVisMask(team);
	while ((ent = G_EdictsGetNextInUse(ent))) {
		ent->visflags &= mask;
	}
}
Beispiel #21
0
/**
 * @brief Make everything visible to anyone who can't already see it
 */
void G_VisMakeEverythingVisible (void)
{
	edict_t *ent = NULL;
	while ((ent = G_EdictsGetNextInUse(ent))) {
		const int playerMask = G_VisToPM(ent->visflags);
		G_AppearPerishEvent(~playerMask, true, ent, NULL);
		if (G_IsActor(ent))
			G_SendInventory(~G_TeamToPM(ent->team), ent);
	}
}
Beispiel #22
0
/**
 * @brief Do @c G_CheckVisTeam for all entities
 */
int G_CheckVisTeamAll (const int team, bool perish, const edict_t *ent)
{
	edict_t *chk = NULL;
	int status = 0;

	while ((chk = G_EdictsGetNextInUse(chk))) {
		status |= G_CheckVisTeam(team, chk, perish, ent);
	}
	return status;
}
Beispiel #23
0
/**
 * @brief creates an entity list
 * @param[out] entList A list of all active inline model entities
 * @sa G_RecalcRouting
 * @sa G_LineVis
 */
void G_GenerateEntList (const char *entList[MAX_EDICTS])
{
    int i = 0;
    edict_t *ent = NULL;

    /* generate entity list */
    while ((ent = G_EdictsGetNextInUse(ent)))
        if (IS_BMODEL(ent))
            entList[i++] = ent->model;
    entList[i] = NULL;
}
Beispiel #24
0
static void SVCmd_ShowAll_f (void)
{
	Edict* ent = nullptr;

	/* Make everything visible to anyone who can't already see it */
	while ((ent = G_EdictsGetNextInUse(ent))) {
		G_AppearPerishEvent(~G_VisToPM(ent->visflags), 1, *ent, nullptr);
		G_VisFlagsAdd(*ent, ~ent->visflags);
	}
	gi.DPrintf("All items and creatures revealed to all sides\n");
}
Beispiel #25
0
/**
 * @brief Iterate through the actor entities (even the dead! - but skips the invisible)
 * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
 */
Edict* G_EdictsGetNextActor (Edict* lastEnt)
{
	Edict* ent = lastEnt;

	assert(lastEnt < &g_edicts[globals.num_edicts]);

	while ((ent = G_EdictsGetNextInUse(ent))) {
		if (G_IsActor(ent))
			break;
	}
	return ent;
}
Beispiel #26
0
/**
 * @brief Searches the edict that has the given target as @c targetname set
 * @param target The target name of the edict that you are searching
 * @return @c nullptr if no edict with the given target name was found, otherwise
 * the edict that has the targetname set you were looking for.
 */
Edict* G_EdictsFindTargetEntity (const char* target)
{
	Edict* ent = nullptr;

	while ((ent = G_EdictsGetNextInUse(ent))) {
		const char* n = ent->targetname;
		if (n && Q_streq(n, target))
			return ent;
	}

	return nullptr;
}
Beispiel #27
0
/**
 * @brief Sets visible edict on player spawn
 * @sa G_ClientStartMatch
 * @sa G_CheckVisTeam
 * @sa G_AppearPerishEvent
 */
int G_CheckVisPlayer (player_t* player, bool perish)
{
	int status = 0;
	edict_t* ent = NULL;

	/* check visibility */
	while ((ent = G_EdictsGetNextInUse(ent))) {
		/* check if he's visible */
		status |= G_DoTestVis(player->pers.team, ent, perish, G_PlayerToPM(player), NULL);
	}

	return status;
}
Beispiel #28
0
/**
 * @brief Iterate through the living actor entities
 * @param lastEnt The entity found in the previous iteration; if nullptr, we start at the beginning
 */
Actor* G_EdictsGetNextLivingActor (Actor* lastEnt)
{
	Edict* ent = lastEnt;

	while ((ent = G_EdictsGetNextInUse(ent))) {
		if (G_IsLivingActor(ent)) {
			Actor* actor = static_cast<Actor*>(ent);
			if (actor)
				return actor;
			Sys_Error("dynamic_cast to Actor failed.");
		}
	}
	return nullptr;
}
Beispiel #29
0
/**
 * @brief Searches all active entities for the next one that holds
 * the matching string at fieldofs (use the offsetof() macro) in the structure.
 *
 * @param[in] from If this is @c NULL all edicts will be searched, otherwise the given
 * edict will be the start of the search
 * @param[in] fieldofs The field offset of the struct member (returned by the offsetof macro)
 * of the field to search the given value for
 * @param[in] match The value of the field
 * @note Searches beginning at the edict after from, or the beginning if NULL
 * @code
 * while ((spot = G_Find(spot, FOFS(classname), "misc_whatever")) != NULL) {
 *   [..]
 * }
 * @endcode
 * @return NULL will be returned if the end of the list is reached.
 */
edict_t *G_Find (edict_t * from, int fieldofs, char *match)
{
    edict_t *ent = from;

    while ((ent = G_EdictsGetNextInUse(ent))) {
        const char *s = *(const char **) ((byte *) ent + fieldofs);
        if (!s)
            continue;
        if (!Q_strcasecmp(s, match))
            return ent;
    }

    return NULL;
}
Beispiel #30
0
/**
 * @brief Searches an edict of the given type at the given grid location.
 * @param pos The grid location to look for an edict.
 * @param type The type of the edict to look for or @c -1 to look for any type in the search.
 * @return @c NULL if nothing was found, otherwise the entity located at the given grid position.
 */
edict_t *G_GetEdictFromPos (const pos3_t pos, const entity_type_t type)
{
    edict_t *ent = NULL;

    while ((ent = G_EdictsGetNextInUse(ent))) {
        if (type > ET_NULL && ent->type != type)
            continue;
        if (!VectorCompare(pos, ent->pos))
            continue;

        return ent;
    }
    /* nothing found at this pos */
    return NULL;
}