Esempio n. 1
0
/**
 * @brief Spawn a singleplayer 2x2 unit.
 */
static void G_Actor2x2Spawn (Edict* ent)
{
	/* set properties */
	level.num_2x2spawnpoints[ent->team]++;
	ent->classname = "ugv";
	ent->type = ET_ACTOR2x2SPAWN;
	ent->fieldSize = ACTOR_SIZE_2x2;

	/* Spawning has already calculated the pos from the origin ( = center of the cell). Perfect for normal size actors.
	 * For 2x2 actors, the origin(of the info_ box) is in the middle of the four cells. Using VecToPos on that origin
	 * results in the upper right cell being the pos of the actor. But we want the lower left cell to be the pos of the
	 * 2x2 actor because routing and pathfinding rely on that. So compensate for that. */
	ent->pos[0]--;
	ent->pos[1]--;

	/* Fall to ground */
	if (ent->pos[2] >= PATHFINDING_HEIGHT)
		ent->pos[2] = PATHFINDING_HEIGHT - 1;
	ent->pos[2] = gi.GridFall(ent->fieldSize, ent->pos);
	if (ent->pos[2] >= PATHFINDING_HEIGHT)
		gi.DPrintf("G_Actor2x2Spawn: Warning: z level is out of bounds: %i\n", ent->pos[2]);
	G_EdictCalcOrigin(ent);

	/* link it for collision detection */
	ent->dir = AngleToDir(ent->angle);
	assert(ent->dir < CORE_DIRECTIONS);
	ent->solid = SOLID_BBOX;

	/* Set bounding box. Maybe this is already set in one of the spawn functions? */
	if (ent->maxs[0] == 0)
		VectorSet(ent->maxs, PLAYER2x2_WIDTH, PLAYER2x2_WIDTH, PLAYER_STAND);
	if (ent->mins[0] == 0)
		VectorSet(ent->mins, -PLAYER2x2_WIDTH, -PLAYER2x2_WIDTH, PLAYER_MIN);
}
Esempio n. 2
0
/**
 * @brief Let an actor fall down if e.g. the func_breakable the actor was standing on was destroyed.
 * @param[in,out] ent The actor that should fall down
 * @todo Handle cases where the grid position the actor would fall to is occupied by another actor already.
 */
void G_ActorFall (edict_t *ent)
{
    edict_t* entAtPos;
    const int oldZ = ent->pos[2];

    ent->pos[2] = gi.GridFall(gi.routingMap, ent->fieldSize, ent->pos);

    if (oldZ == ent->pos[2])
        return;

    entAtPos = G_GetEdictFromPos(ent->pos, ET_NULL);
    if (entAtPos != NULL && (G_IsBreakable(entAtPos) || G_IsBlockingMovementActor(entAtPos))) {
        const int diff = oldZ - ent->pos[2];
        G_TakeDamage(entAtPos, (int)(FALLING_DAMAGE_FACTOR * (float)diff));
    }

    G_EdictCalcOrigin(ent);
    gi.LinkEdict(ent);

    G_CheckVis(ent, true);

    G_EventActorFall(ent);

    gi.EndEvents();
}
Esempio n. 3
0
/**
 * @brief Spawn a singleplayer 2x2 unit.
 */
static void G_Actor2x2Spawn (edict_t *ent)
{
	/* set properties */
	level.num_2x2spawnpoints[ent->team]++;
	ent->classname = "ugv";
	ent->type = ET_ACTOR2x2SPAWN;
	ent->fieldSize = ACTOR_SIZE_2x2;

	/* Fall to ground */
	if (ent->pos[2] >= PATHFINDING_HEIGHT)
		ent->pos[2] = PATHFINDING_HEIGHT - 1;
	ent->pos[2] = gi.GridFall(gi.routingMap, ent->fieldSize, ent->pos);
	if (ent->pos[2] >= PATHFINDING_HEIGHT)
		gi.DPrintf("G_Actor2x2Spawn: Warning: z level is out of bounds: %i\n", ent->pos[2]);
	G_EdictCalcOrigin(ent);

	/* link it for collision detection */
	ent->dir = AngleToDir(ent->angle);
	assert(ent->dir < CORE_DIRECTIONS);
	ent->solid = SOLID_BBOX;

	/* Set bounding box. Maybe this is already set in one of the spawn functions? */
	if (ent->maxs[0] == 0)
		VectorSet(ent->maxs, PLAYER2x2_WIDTH, PLAYER2x2_WIDTH, PLAYER_STAND);
	if (ent->mins[0] == 0)
		VectorSet(ent->mins, -PLAYER2x2_WIDTH, -PLAYER2x2_WIDTH, PLAYER_MIN);
}
Esempio n. 4
0
/**
 * @brief Spawns a new entity at the floor
 * @note This is e.g. used to place dropped weapons/items at the floor
 */
Edict* G_SpawnFloor (const pos3_t pos)
{
	Edict* floorItem;

	floorItem = G_Spawn("item");
	floorItem->type = ET_ITEM;
	/* make sure that the item is always on a field that even the smallest actor can reach */
	floorItem->fieldSize = ACTOR_SIZE_NORMAL;
	VectorCopy(pos, floorItem->pos);
	floorItem->pos[2] = gi.GridFall(floorItem->fieldSize, floorItem->pos);
	G_EdictCalcOrigin(floorItem);
	return floorItem;
}
Esempio n. 5
0
/**
 * @brief Spawns a new entity at the floor
 * @note This is e.g. used to place dropped weapons/items at the floor
 */
edict_t *G_SpawnFloor (const pos3_t pos)
{
	edict_t *floor;

	floor = G_Spawn();
	floor->classname = "item";
	floor->type = ET_ITEM;
	/* make sure that the item is always on a field that even the smallest actor can reach */
	floor->fieldSize = ACTOR_SIZE_NORMAL;
	VectorCopy(pos, floor->pos);
	floor->pos[2] = gi.GridFall(gi.routingMap, floor->fieldSize, floor->pos);
	G_EdictCalcOrigin(floor);
	return floor;
}
Esempio n. 6
0
/**
 * @brief info_civilian_start (0 1 1) (-16 -16 -24) (16 16 32)
 * Way point for a civilian.
 * @sa SP_civilian_start
 * @todo These waypoints should be placeable by the human player (e.g. spawn a special particle on the waypoint)
 * to direct the civilians to a special location
 */
static void SP_civilian_target (edict_t *ent)
{
	/* target point for which team */
	ent->team = TEAM_CIVILIAN;
	ent->classname = "civtarget";
	ent->type = ET_CIVILIANTARGET;
	ent->fieldSize = ACTOR_SIZE_NORMAL; /* to let the grid fall function work */

	/* add the edict to the list of known waypoints */
	G_AddToWayPointList(ent);

	/* fall to ground */
	if (ent->pos[2] >= PATHFINDING_HEIGHT)
		ent->pos[2] = PATHFINDING_HEIGHT - 1;
	ent->pos[2] = gi.GridFall(gi.routingMap, ent->fieldSize, ent->pos);
	G_EdictCalcOrigin(ent);
}
Esempio n. 7
0
static void G_SpawnField (edict_t *ent, const char *classname, entity_type_t type, solid_t solid)
{
	vec3_t particleOrigin;

	ent->classname = classname;
	ent->type = type;
	ent->fieldSize = ACTOR_SIZE_NORMAL;
	ent->solid = solid;
	VectorSet(ent->maxs, UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2);
	VectorSet(ent->mins, -UNIT_SIZE / 2, -UNIT_SIZE / 2, -UNIT_HEIGHT / 2);
	G_EdictCalcOrigin(ent);
	ent->think = Think_SmokeAndFire;
	ent->nextthink = 1;
	ent->time = level.actualRound;

	gi.LinkEdict(ent);

	VectorCopy(ent->origin, particleOrigin);
	particleOrigin[2] -= GROUND_DELTA;
	ent->particleLink = G_SpawnParticle(particleOrigin, ent->spawnflags, ent->particle);
}
Esempio n. 8
0
static void G_SpawnSmoke (const vec3_t vec, const char *particle, int rounds)
{
	pos3_t pos;
	edict_t *ent;

	VecToPos(vec, pos);

	ent = G_GetEdictFromPos(pos, ET_SMOKE);
	if (ent == NULL) {
		pos_t z = gi.GridFall(gi.routingMap, ACTOR_SIZE_NORMAL, pos);
		if (z != pos[2])
			return;

		ent = G_Spawn();
		VectorCopy(pos, ent->pos);
		G_EdictCalcOrigin(ent);
		ent->spawnflags = G_GetLevelFlagsFromPos(pos);
		ent->particle = particle;
		SP_misc_smoke(ent);
	}

	ent->count = rounds;
}
Esempio n. 9
0
/**
 * @brief Generates the client events that are send over the netchannel to move an actor
 * @param[in] player Player who is moving an actor
 * @param[in] visTeam The team to check the visibility for - if this is 0 we build the forbidden list
 * above all edicts - for the human controlled actors this would mean that clicking to a grid
 * position that is not reachable because an invisible actor is standing there would not result in
 * a single step - as the movement is aborted before. For AI movement this is in general @c 0 - but
 * not if they e.g. hide.
 * @param[in] ent Edict to move
 * @param[in] to The grid position to walk to
 * @sa CL_ActorStartMove
 * @sa PA_MOVE
 */
void G_ClientMove (const player_t * player, int visTeam, edict_t* ent, const pos3_t to)
{
    int status, initTU;
    dvec_t dvtab[MAX_DVTAB];
    int dir;
    byte numdv, length;
    pos3_t pos;
    float div;
    int oldState;
    int oldHP;
    bool autoCrouchRequired = false;
    byte crouchingState;

    if (VectorCompare(ent->pos, to))
        return;

    /* check if action is possible */
    if (!G_ActionCheckForCurrentTeam(player, ent, TU_MOVE_STRAIGHT))
        return;

    crouchingState = G_IsCrouched(ent) ? 1 : 0;
    oldState = oldHP = 0;

    /* calculate move table */
    G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
    length = gi.MoveLength(level.pathingMap, to, crouchingState, false);

    /* length of ROUTING_NOT_REACHABLE means not reachable */
    if (length && length >= ROUTING_NOT_REACHABLE)
        return;

    /* Autostand: check if the actor is crouched and player wants autostanding...*/
    if (crouchingState && player->autostand) {
        /* ...and if this is a long walk... */
        if (SHOULD_USE_AUTOSTAND(length)) {
            /* ...make them stand first. If the player really wants them to walk a long
             * way crouched, he can move the actor in several stages.
             * Uses the threshold at which standing, moving and crouching again takes
             * fewer TU than just crawling while crouched. */
            G_ClientStateChange(player, ent, STATE_CROUCHED, true); /* change to stand state */
            crouchingState = G_IsCrouched(ent) ? 1 : 0;
            if (!crouchingState) {
                G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
                length = gi.MoveLength(level.pathingMap, to, crouchingState, false);
                autoCrouchRequired = true;
            }
        }
    }

    /* this let the footstep sounds play even over network */
    ent->think = G_PhysicsStep;
    ent->nextthink = level.time;

    /* assemble dvec-encoded move data */
    VectorCopy(to, pos);
    initTU = ent->TU;

    numdv = G_FillDirectionTable(dvtab, lengthof(dvtab), crouchingState, pos);

    /* make sure to end any other pending events - we rely on EV_ACTOR_MOVE not being active anymore */
    gi.EndEvents();

    /* everything ok, found valid route? */
    if (VectorCompare(pos, ent->pos)) {
        byte* stepAmount = NULL;
        int usedTUs = 0;
        /* no floor inventory at this point */
        FLOOR(ent) = NULL;

        while (numdv > 0) {
            /* A flag to see if we needed to change crouch state */
            int crouchFlag;
            const byte oldDir = ent->dir;
            int dvec;

            /* get next dvec */
            numdv--;
            dvec = dvtab[numdv];
            /* This is the direction to make the step into */
            dir = getDVdir(dvec);

            /* turn around first */
            status = G_ActorDoTurn(ent, dir);
            if (status & VIS_STOP) {
                autoCrouchRequired = false;
                if (ent->moveinfo.steps == 0)
                    usedTUs += TU_TURN;
                break;
            }

            if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv)) {
                /* don't autocrouch if new enemy becomes visible */
                autoCrouchRequired = false;
                /* if something appears on our route that didn't trigger a VIS_STOP, we have to
                 * send the turn event if this is our first step */
                if (oldDir != ent->dir && ent->moveinfo.steps == 0) {
                    G_EventActorTurn(ent);
                    usedTUs += TU_TURN;
                }
                break;
            }

            /* decrease TUs */
            div = gi.GetTUsForDirection(dir, G_IsCrouched(ent));
            if ((int) (usedTUs + div) > ent->TU)
                break;
            usedTUs += div;

            /* This is now a flag to indicate a change in crouching - we need this for
             * the stop in mid move call(s), because we need the updated entity position */
            crouchFlag = 0;
            /* Calculate the new position after the decrease in TUs, otherwise the game
             * remembers the false position if the time runs out */
            PosAddDV(ent->pos, crouchFlag, dvec);

            /* slower if crouched */
            if (G_IsCrouched(ent))
                ent->speed = ACTOR_SPEED_CROUCHED;
            else
                ent->speed = ACTOR_SPEED_NORMAL;
            ent->speed *= g_actorspeed->value;

            if (crouchFlag == 0) { /* No change in crouch */
                edict_t* clientAction;
                int contentFlags;
                vec3_t pointTrace;

                G_EdictCalcOrigin(ent);
                VectorCopy(ent->origin, pointTrace);
                pointTrace[2] += PLAYER_MIN;

                contentFlags = gi.PointContents(pointTrace);

                /* link it at new position - this must be done for every edict
                 * movement - to let the server know about it. */
                gi.LinkEdict(ent);

                /* Only the PHALANX team has these stats right now. */
                if (ent->chr.scoreMission) {
                    float truediv = gi.GetTUsForDirection(dir, 0);		/* regardless of crouching ! */
                    if (G_IsCrouched(ent))
                        ent->chr.scoreMission->movedCrouched += truediv;
                    else
                        ent->chr.scoreMission->movedNormal += truediv;
                }
                /* write the step to the net */
                G_WriteStep(ent, &stepAmount, dvec, contentFlags);

                /* check if player appears/perishes, seen from other teams */
                G_CheckVis(ent, true);

                /* check for anything appearing, seen by "the moving one" */
                status = G_CheckVisTeamAll(ent->team, false, ent);

                /* Set ent->TU because the reaction code relies on ent->TU being accurate. */
                G_ActorSetTU(ent, initTU - usedTUs);

                clientAction = ent->clientAction;
                oldState = ent->state;
                oldHP = ent->HP;
                /* check triggers at new position */
                if (G_TouchTriggers(ent)) {
                    if (!clientAction)
                        status |= VIS_STOP;
                }

                G_TouchSolids(ent, 10.0f);

                /* state has changed - maybe we walked on a trigger_hurt */
                if (oldState != ent->state)
                    status |= VIS_STOP;
                else if (oldHP != ent->HP)
                    status |= VIS_STOP;
            } else if (crouchFlag == 1) {
                /* Actor is standing */
                G_ClientStateChange(player, ent, STATE_CROUCHED, true);
            } else if (crouchFlag == -1) {
                /* Actor is crouching and should stand up */
                G_ClientStateChange(player, ent, STATE_CROUCHED, false);
            }

            /* check for reaction fire */
            if (G_ReactionFireOnMovement(ent)) {
                status |= VIS_STOP;

                autoCrouchRequired = false;
            }

            /* check for death */
            if (((oldHP != 0 && oldHP != ent->HP) || (oldState != ent->state)) && !G_IsDazed(ent)) {
                /** @todo Handle dazed via trigger_hurt */
                /* maybe this was due to rf - then the G_ActorDie was already called */
                if (!G_IsDead(ent)) {
                    G_CheckDeathOrKnockout(ent, NULL, NULL, oldHP - ent->HP);
                }
                return;
            }

            if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv - 1)) {
                /* don't autocrouch if new enemy becomes visible */
                autoCrouchRequired = false;
                break;
            }

            /* Restore ent->TU because the movement code relies on it not being modified! */
            G_ActorSetTU(ent, initTU);
        }

        /* submit the TUs / round down */
        if (g_notu != NULL && g_notu->integer)
            G_ActorSetTU(ent, initTU);
        else
            G_ActorSetTU(ent, initTU - usedTUs);

        G_SendStats(ent);

        /* end the move */
        G_GetFloorItems(ent);
        gi.EndEvents();
    }

    if (autoCrouchRequired) {
        /* toggle back to crouched state */
        G_ClientStateChange(player, ent, STATE_CROUCHED, true);
    }
}
Esempio n. 10
0
/**
 * @brief Creates a server's entity / program execution context
 * by parsing textual entity definitions out of an ent file.
 * @sa CM_EntityString
 * @sa SV_SpawnServer
 */
void G_SpawnEntities (const char *mapname, bool day, const char *entities)
{
	int entnum;

	G_FreeTags(TAG_LEVEL);

	OBJZERO(level);
	level.pathingMap = (pathing_t *)G_TagMalloc(sizeof(*level.pathingMap), TAG_LEVEL);

	G_EdictsReset();

	/* initialize reactionFire data */
	G_ReactionFireTargetsInit();

	Q_strncpyz(level.mapname, mapname, sizeof(level.mapname));
	level.day = day;

	G_ResetClientData();

	level.activeTeam = TEAM_NO_ACTIVE;
	level.actualRound = 1;
	level.hurtAliens = sv_hurtaliens->integer;
	ai_waypointList = NULL;

	/* parse ents */
	entnum = 0;
	while (1) {
		edict_t *ent;
		/* parse the opening brace */
		const char *token = Com_Parse(&entities);
		if (!entities)
			break;
		if (token[0] != '{')
			gi.Error("ED_LoadFromFile: found %s when expecting {", token);

		ent = G_Spawn();

		entities = ED_ParseEdict(entities, ent);

		ent->mapNum = entnum++;

		/* Set the position of the entity */
		VecToPos(ent->origin, ent->pos);

		/* Call this entity's specific initializer (sets ent->type) */
		ED_CallSpawn(ent);

		/* if this entity is an bbox (e.g. actor), then center its origin based on its position */
		if (ent->solid == SOLID_BBOX)
			G_EdictCalcOrigin(ent);
	}

	/* spawn ai players, if needed */
	if (level.num_spawnpoints[TEAM_CIVILIAN]) {
		if (AI_CreatePlayer(TEAM_CIVILIAN) == NULL)
			gi.DPrintf("Could not create civilian\n");
	}

	if ((sv_maxclients->integer == 1 || ai_numactors->integer) && level.num_spawnpoints[TEAM_ALIEN]) {
		if (AI_CreatePlayer(TEAM_ALIEN) == NULL)
			gi.DPrintf("Could not create alien\n");
	}

	Com_Printf("Used inventory slots after ai spawn: %i\n", game.i.GetUsedSlots(&game.i));

	G_FindEdictGroups();
}