예제 #1
0
파일: g_client.c 프로젝트: mecwerks/revamp
/*
===========
PlayerSpawn

Called every time a player is placed fresh in the world:
after the first PlayerBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void PlayerSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gplayer_t	*player;
	int		i;
	playerPersistant_t	saved;
	playerSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	gentity_t *tent;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
	int 	startWeapon, startAmmo;

	index = ent - g_entities;
	player = ent->player;

	VectorClear(spawn_origin);

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this player
	if ( player->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else if (g_gametype.integer >= GT_CTF ) {
		// all base oriented team games use the CTF spawn points
		spawnPoint = SelectCTFSpawnPoint ( 
						player->sess.sessionTeam, 
						player->pers.teamState.state, 
						spawn_origin, spawn_angles,
						!!(ent->r.svFlags & SVF_BOT));
	}
	else
	{
		// the first spawn should be at a good looking spot
		if ( player->pers.initialSpawn && player->pers.localClient )
		{
			spawnPoint = SelectInitialSpawnPoint(spawn_origin, spawn_angles,
							     !!(ent->r.svFlags & SVF_BOT));
		}
		else
		{
			// don't spawn near existing origin if possible
			spawnPoint = SelectSpawnPoint ( 
				player->ps.origin, 
				spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT));
		}
	}
	player->pers.teamState.state = TEAM_ACTIVE;

	// always clear the kamikaze flag
	ent->s.eFlags &= ~EF_KAMIKAZE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->player->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = player->pers;
	savedSess = player->sess;
	savedPing = player->ps.ping;
//	savedAreaBits = player->areabits;
	accuracy_hits = player->accuracy_hits;
	accuracy_shots = player->accuracy_shots;
	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		persistant[i] = player->ps.persistant[i];
	}
	eventSequence = player->ps.eventSequence;

	Com_Memset (player, 0, sizeof(*player));

	player->pers = saved;
	player->sess = savedSess;
	player->ps.ping = savedPing;
//	player->areabits = savedAreaBits;
	player->accuracy_hits = accuracy_hits;
	player->accuracy_shots = accuracy_shots;
	player->lastkilled_player = -1;

	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		player->ps.persistant[i] = persistant[i];
	}
	player->ps.eventSequence = eventSequence;
	// increment the spawncount so the client will detect the respawn
	player->ps.persistant[PERS_SPAWN_COUNT]++;
	player->ps.persistant[PERS_TEAM] = player->sess.sessionTeam;

	player->airOutTime = level.time + 12000;

	// set max health
	player->pers.maxHealth = PlayerHandicap( player );
	// clear entity values
	player->ps.stats[STAT_MAX_HEALTH] = player->pers.maxHealth;
	player->ps.eFlags = flags;
	player->ps.contents = CONTENTS_BODY;
	player->ps.collisionType = ( g_playerCapsule.integer == 1 ) ? CT_CAPSULE : CT_AABB;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->player = &level.players[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	ent->player->headless = qfalse;
	
	VectorCopy (playerMins, player->ps.mins);
	VectorCopy (playerMaxs, player->ps.maxs);

	player->ps.playerNum = index;

	if ( g_instaGib.integer == 1 ) {
		startWeapon = WP_RAILGUN;
		startAmmo = 50;
	} else if ( g_instaGib.integer == 2 ) {
		startWeapon = WP_ROCKET_LAUNCHER;
		startAmmo = 50;
	} else {
		startWeapon = WP_MACHINEGUN;
		startAmmo = 100;
	}

	player->ps.stats[STAT_WEAPONS] = ( 1 << startWeapon );

	player->ps.ammo[startWeapon] = startAmmo;

	player->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
	player->ps.ammo[WP_GAUNTLET] = -1;
	player->ps.ammo[WP_GRAPPLING_HOOK] = -1;

	// health will count down towards max_health
	ent->health = player->ps.stats[STAT_HEALTH] = player->ps.stats[STAT_MAX_HEALTH] = MAX_HEALTH;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, player->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	player->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( player - level.players, &ent->player->pers.cmd );
	SetPlayerViewAngle( ent, spawn_angles );
	// don't allow full run speed for a bit
	player->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	player->ps.pm_time = 100;

	player->respawnTime = level.time;
	player->inactivityTime = level.time + g_inactivity.integer * 1000;
	player->latched_buttons = 0;

	// set default animations
	player->ps.torsoAnim = TORSO_STAND;
	player->ps.legsAnim = LEGS_IDLE;

	if (!level.intermissiontime) {
		if (ent->player->sess.sessionTeam != TEAM_SPECTATOR) {
			G_KillBox(ent);
			// force the base weapon up
			player->ps.weapon = startWeapon;
			player->ps.weaponstate = WEAPON_READY;
			// fire the targets of the spawn point
			G_UseTargets(spawnPoint, ent);
			// select the highest weapon number available, after any spawn given items have fired
			player->ps.weapon = 1;

			for (i = WP_NUM_WEAPONS - 1 ; i > 0 ; i--) {
				if (player->ps.stats[STAT_WEAPONS] & (1 << i)) {
					player->ps.weapon = i;
					break;
				}
			}
			// positively link the player, even if the command times are weird
			VectorCopy(ent->player->ps.origin, ent->r.currentOrigin);

			tent = G_TempEntity(ent->player->ps.origin, EV_PLAYER_TELEPORT_IN);
			tent->s.playerNum = ent->s.playerNum;

			trap_LinkEntity (ent);
		}
	} else {
		// move players to intermission
		MovePlayerToIntermission(ent);
	}
	// run a player frame to drop exactly to the floor,
	// initialize animations and other things
	player->ps.commandTime = level.time - 100;
	ent->player->pers.cmd.serverTime = level.time;
	PlayerThink( ent-g_entities );
	// run the presend to set anything else, follow spectators wait
	// until all players have been reconnected after map_restart
	if ( ent->player->sess.spectatorState != SPECTATOR_FOLLOW ) {
		PlayerEndFrame( ent );
	}

	// clear entity state values
	BG_PlayerStateToEntityState( &player->ps, &ent->s, qtrue );

	// we don't want players being backward-reconciled to the place they died
	G_ResetHistory( ent );
}
예제 #2
0
/*
===========
G_ReachedPath
===========
*/
qboolean G_ReachedPath(gentity_t *ent, qboolean check)
{
	gentity_t	*point;
	qboolean	backward;

	if (!(ent->s.eFlags & EF_PATHMODE)) {
		return qfalse;
	}

	if (ent->player) {
		backward = (ent->player->ps.eFlags & EF_TRAINBACKWARD);
	} else {
		backward = (ent->s.eFlags & EF_TRAINBACKWARD);
	}

	if (backward) {
		point = ent->prevTrain;
	} else {
		point = ent->nextTrain;
	}

	if (!point && ent->player) {
		// Previous point
		if (backward)
			point = ent->nextTrain;
		else
			point = ent->prevTrain;

		// Stop train
		return qfalse;
	} else if (!point && !ent->player) {
		// end of path
		if (!(ent->spawnflags & 1)) {
			// Stop train
			return qfalse;
		}

		// Previous point
		if (backward)
			point = ent->nextTrain;
		else
			point = ent->prevTrain;

		// Go back the way you came
		backward = !backward;
		ent->s.eFlags ^= EF_TRAINBACKWARD;

		// Next point
		if (backward)
			point = ent->prevTrain;
		else
			point = ent->nextTrain;

		if (!point) {
			return qfalse;
		}
	}

	if ((!backward && point == point->nextTrain) || (backward && point == point->prevTrain)) {
		// Entity points to self...
		G_Printf("DEBUG: Entity points to self!\n");
		return qfalse;
	}

	// ZTM: Check if we have made it to the next train
	//               Doesn't work with PATHF_AXIS!
	if (check)
	{
		vec3_t targetPos;
		vec3_t origin;
		vec_t dist;

		VectorCopy(point->s.origin, targetPos);

		if (ent->player)
			VectorCopy(ent->player->ps.origin, origin);
		else
			VectorCopy(ent->s.origin, origin);

		if (ent->player && ent->player->ps.pathMode == PATHMODE_SIDE) {
			// "2D" path
			origin[2] = targetPos[2] = 0; // Don't compare Z
		}

		dist = Distance(origin, targetPos);

		if (dist > 20.0f) {
			return qfalse;
		}
	}

	// fire all other targets
	G_UseTargets( point, ent );

	// Setup next move
	if (backward) {
		ent->prevTrain = point->prevTrain;
		if (ent->prevTrain) {
			ent->nextTrain = point;
		}
	} else {
		ent->nextTrain = point->nextTrain;
		if (ent->nextTrain) {
			ent->prevTrain = point;
		}
	}

	// set the new trajectory
	if (ent->prevTrain) {
		VectorCopy( ent->prevTrain->s.origin, ent->pos1 );
	}
	if (ent->nextTrain) {
		VectorCopy( ent->nextTrain->s.origin, ent->pos2 );
	}

	if (ent->s.eType == ET_MOVER)
	{
		float			speed;
		vec3_t			move;
		float			length;

		// if the path_corner has a speed, use that
		if ( point->speed ) {
			speed = point->speed;
		} else {
			// otherwise use the train's speed
			speed = ent->speed;
		}
		if ( speed < 1 ) {
			speed = 1;
		}

		// calculate duration
		VectorSubtract( ent->pos2, ent->pos1, move );
		length = VectorLength( move );

		ent->s.pos.trDuration = length * 1000 / speed;

		// Tequila comment: Be sure to send to clients after any fast move case
		ent->r.svFlags &= ~SVF_NOCLIENT;

		// Tequila comment: Fast move case
		if(ent->s.pos.trDuration<1) {
			// Tequila comment: As trDuration is used later in a division, we need to avoid that case now
			// With null trDuration,
			// the calculated rocks bounding box becomes infinite and the engine think for a short time
			// any entity is riding that mover but not the world entity... In rare case, I found it
			// can also stuck every map entities after func_door are used.
			// The desired effect with very very big speed is to have instant move, so any not null duration
			// lower than a frame duration should be sufficient.
			// Afaik, the negative case don't have to be supported.
			ent->s.pos.trDuration=1;

			// Tequila comment: Don't send entity to clients so it becomes really invisible 
			ent->r.svFlags |= SVF_NOCLIENT;
		}

		// looping sound
		ent->s.loopSound = point->soundLoop;

		// start it going
		if (backward)
			SetMoverState( ent, MOVER_2TO1, level.time );
		else
			SetMoverState( ent, MOVER_1TO2, level.time );

		// if there is a "wait" value on the target, don't start moving yet
		if ( point->wait ) {
			ent->nextthink = level.time + point->wait * 1000;
			ent->think = Think_BeginMoving;
			ent->s.pos.trType = TR_STATIONARY;
		}
	}
	else if (ent->player)
	{
		vec3_t dir;
		vec3_t viewAngles;
		int mode;

		// Set path movement style
		mode = point->moverState;

		// Unset style means use pervious style.
		if (!mode) {
			mode = ent->player->ps.pathMode;
		}

		VectorCopy( ent->pos1, ent->player->ps.grapplePoint );
		VectorCopy( ent->pos2, ent->player->ps.nextPoint );

		if (backward)
			VectorSubtract( ent->pos1, ent->player->ps.origin, dir );
		else
			VectorSubtract( ent->pos2, ent->player->ps.origin, dir );

		vectoangles( dir, viewAngles );
		viewAngles[ROLL] = ent->player->ps.viewangles[ROLL];
		viewAngles[PITCH] = ent->player->ps.viewangles[PITCH];
		SetPlayerViewAngle(ent, viewAngles);

		if (mode == PATHMODE_SIDE) {
			if (backward) {
				ent->player->ps.stats[STAT_DEAD_YAW] = viewAngles[YAW]-90;
			} else {
				ent->player->ps.stats[STAT_DEAD_YAW] = viewAngles[YAW]+90;
			}
		} else {
			ent->player->ps.stats[STAT_DEAD_YAW] = viewAngles[YAW];
		}

		if (mode != ent->player->ps.pathMode) {
			// ZTM: Do we need to do anything when the mode changes?
			ent->player->ps.pathMode = mode;
		}
	}

	return qtrue;
}