示例#1
0
//===========================================================================
// P_FuzzySpawn
//  Try to spawn close to the mapspot. Returns false if no clear spot 
//  was found.
//===========================================================================
boolean P_FuzzySpawn(mapthing_t * spot, int playernum, boolean doTeleSpark)
{
	int     i, k, x, y;
	int     offset = 33;		// Player radius = 16
	mapthing_t place;

	// Try some spots in the vicinity.
	for(i = 0; i < 9; i++)
	{
		memcpy(&place, spot, sizeof(*spot));
		if(i != 0)
		{
			k = (i == 4 ? 0 : i);
			// Move a bit.
			x = k % 3 - 1;
			y = k / 3 - 1;
			place.x += x * offset;
			place.y += y * offset;
		}
		if(P_CheckSpot(playernum, &place, doTeleSpark))
		{
			// This is good!
			P_SpawnPlayer(&place, playernum);
			return true;
		}
	}

	// No success. Just spawn at the specified spot.
	P_SpawnPlayer(spot, playernum);
	return false;
}
示例#2
0
//*****************************************************************************
//
void COOP_SpawnVoodooDollsForPlayerIfNecessary ( const ULONG ulPlayer, const bool bSpawnEvenIfPlayerIsNotIngame )
{
	// [BB] Only the server spawns voodoo dolls.
	if ( NETWORK_GetState() != NETSTATE_SERVER )
		return;

	// [BB] The current game mode doesn't need voodoo dolls.
	if ( COOP_VoodooDollsSelectedByGameMode() == false )
		return;

	// [BB] The map doesn't have any voodoo doll starts for this player.
	if ( AllPlayerStarts[ulPlayer].Size() <= 1 )
		return;

	// [BB] In case of unassigned voodoo dolls, on some maps it may be better only
	// to spawn the voodoo dolls for the first N players.
	if ( ( sv_coopunassignedvoodoodolls == true ) && ( static_cast<LONG>(ulPlayer) >= sv_coopunassignedvoodoodollsfornplayers ) )
		return;

	// [BB] To enforce the spawning, we have to set playeringame to true.
	const bool bPlayerInGame = playeringame[ulPlayer];
	if ( bSpawnEvenIfPlayerIsNotIngame )
		playeringame[ulPlayer] = true;

	// [BB] Every start except for the last, has to spawn a voodoo doll.
	for ( ULONG ulIdx = 0; ulIdx < AllPlayerStarts[ulPlayer].Size() - 1; ulIdx++ )
	{
		APlayerPawn *pDoll = P_SpawnPlayer ( &(AllPlayerStarts[ulPlayer][ulIdx]), false, NULL );
		// [BB] Mark the voodoo doll as spawned by the map.
		// P_SpawnPlayer won't spawn anything for a player not in game, therefore we need to check if pDoll is NULL.
		if ( pDoll )
		{
			pDoll->ulSTFlags |= STFL_LEVELSPAWNED;

			// [BB] If we would just set the player pointer to NULL, a lot of things wouldn't work
			// at all for the voodoo dolls (e.g. floor scrollers), so we set it do a pointer to a
			// dummy player to get past all the ( player != NULL ) checks. This will require special
			// handling wherever the code assumes that non-NULL player pointers have a valid mo.
			if ( sv_coopunassignedvoodoodolls )
				pDoll->player = &DummyPlayer;
		}
	}

	// [BB] Now that the spawning is done, we have to restore the proper playeringame value.
	playeringame[ulPlayer] = bPlayerInGame;
	// [BB] If we have spawned the doll for a player not in the game, we have to clear a few pointers.
	// Otherwise we'll run into nasty problems after a map change.
	if ( bPlayerInGame == false )
	{
		players[ulPlayer].mo = NULL;
		players[ulPlayer].ReadyWeapon = NULL;
		players[ulPlayer].PendingWeapon = NULL;
	}
}
示例#3
0
void G_DeathMatchSpawnPlayer(int playernum) {
    int i, j;
    int selections;

    selections = deathmatch_p - deathmatchstarts;
    if(selections < 4) {
        I_Error("G_DeathMatchSpawnPlayer: Only %i deathmatch spots, 4 required", selections);
    }

    for(j = 0; j < 20; j++) {
        i = P_Random(pr_dmspawn) % selections;
        if(G_CheckSpot(playernum, &deathmatchstarts[i])) {
            deathmatchstarts[i].type = playernum+1;
            P_SpawnPlayer(&deathmatchstarts[i]);
            return;
        }
    }

    // no good spot, so the player will probably get stuck
    P_SpawnPlayer(&playerstarts[playernum]);
}
示例#4
0
void G_DoReborn (int playernum) 
{ 
	int 		i; 
	 
	if (!netgame)
	{
		gameaction = ga_died;			/* reload the level from scratch  */
		return;
	}

/*	 */
/* respawn this player while the other players keep going */
/* */
	players[playernum].mo->player = NULL;   /* dissasociate the corpse  */
		
	/* spawn at random spot if in death match  */
	if (netgame == gt_deathmatch) 
	{ 
		G_DeathMatchSpawnPlayer (playernum); 
		return; 
	} 
		
	if (G_CheckSpot (playernum, &playerstarts[playernum]) ) 
	{ 
		P_SpawnPlayer (&playerstarts[playernum]); 
		return; 
	} 
	/* try to spawn at one of the other players spots  */
	for (i=0 ; i<MAXPLAYERS ; i++) 
		if (G_CheckSpot (playernum, &playerstarts[i]) ) 
		{ 
			playerstarts[i].type = playernum+1;		/* fake as other player  */
			P_SpawnPlayer (&playerstarts[i]); 
			playerstarts[i].type = i+1;             /* restore  */
			return; 
		} 
	/* he's going to be inside something.  Too bad.  */
	P_SpawnPlayer (&playerstarts[playernum]); 
} 
示例#5
0
void G_DoReborn(int playernum)
{
    int i;

    // quit demo unless -demoextend
    if (!demoextend && G_CheckDemoStatus())
        return;
    if (!netgame)
        gameaction = ga_loadlevel;      // reload the level from scratch
    else
    {                           // respawn at the start
        players[playernum].mo->player = NULL;   // dissasociate the corpse

        // spawn at random spot if in death match
        if (deathmatch)
        {
            G_DeathMatchSpawnPlayer(playernum);
            return;
        }

        if (G_CheckSpot(playernum, &playerstarts[playernum]))
        {
            P_SpawnPlayer(&playerstarts[playernum]);
            return;
        }
        // try to spawn at one of the other players spots
        for (i = 0; i < MAXPLAYERS; i++)
            if (G_CheckSpot(playernum, &playerstarts[i]))
            {
                playerstarts[i].type = playernum + 1;   // fake as other player
                P_SpawnPlayer(&playerstarts[i]);
                playerstarts[i].type = i + 1;   // restore
                return;
            }
        // he's going to be inside something.  Too bad.
        P_SpawnPlayer(&playerstarts[playernum]);
    }
}
示例#6
0
void G_DoReborn(int playernum) {
    if(!netgame) {
        gameaction = ga_loadlevel;    // reload the level from scratch
    }
    else {   // respawn at the start
        int i;

        // first dissasociate the corpse
        if(players[playernum].mo == NULL) {
            I_Error("G_DoReborn: Player start #%i not found!", playernum+1);
        }

        players[playernum].mo->player = NULL;

        // spawn at random spot if in death match
        if(deathmatch) {
            G_DeathMatchSpawnPlayer(playernum);
            return;
        }

        if(G_CheckSpot(playernum, &playerstarts[playernum])) {
            P_SpawnPlayer(&playerstarts[playernum]);
            return;
        }

        // try to spawn at one of the other players spots
        for(i = 0; i < MAXPLAYERS; i++) {
            if(G_CheckSpot(playernum, &playerstarts[i])) {
                playerstarts[i].type = playernum+1;    // fake as other player
                P_SpawnPlayer(&playerstarts[i]);
                playerstarts[i].type = i+1;            // restore
                return;
            }
            // he's going to be inside something.  Too bad.
        }
        P_SpawnPlayer(&playerstarts[playernum]);
    }
}
示例#7
0
//==========================================================================
// P_SpawnPlayers
//  Spawns all players, using the method appropriate for current game mode.
//  Called during level setup.
//==========================================================================
void P_SpawnPlayers(void)
{
	int     i;

	// If deathmatch, randomly spawn the active players.
	if(deathmatch)
	{
		for(i = 0; i < MAXPLAYERS; i++)
			if(players[i].plr->ingame)
			{
				players[i].plr->mo = NULL;
				G_DeathMatchSpawnPlayer(i);
			}
	}
	else
	{
#ifdef __JDOOM__
		if(!IS_NETGAME)
		{
			mapthing_t *it;

			// Spawn all unused player starts. This will create 'zombies'.
			// FIXME: Also in netgames?
			for(it = playerstarts; it != playerstart_p; it++)
				if(players[0].startspot != it - playerstarts && it->type == 1)
				{
					P_SpawnPlayer(it, 0);
				}
		}
#endif
		// Spawn everybody at their assigned places.
		// Might get messy if there aren't enough starts.
		for(i = 0; i < MAXPLAYERS; i++)
			if(players[i].plr->ingame)
			{
				ddplayer_t *ddpl = players[i].plr;

				if(!P_FuzzySpawn
				   (&playerstarts[players[i].startspot], i, false))
				{
					// Gib anything at the spot.
					P_Telefrag(ddpl->mo);
				}
			}
	}
}
示例#8
0
static void SpawnExtraPlayers()
{
	// If there are more players now than there were in the savegame,
	// be sure to spawn the extra players.
	int i;

	if (deathmatch)
	{
		return;
	}

	for (i = 0; i < MAXPLAYERS; ++i)
	{
		if (playeringame[i] && players[i].mo == NULL)
		{
			players[i].playerstate = PST_ENTER;
			P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
		}
	}
}
示例#9
0
static void SpawnExtraPlayers ()
{
	// If there are more players now than there were in the savegame,
	// be sure to spawn the extra players.
	int i;

	if (deathmatch)
	{
		return;
	}

	for (i = 0; i < MAXPLAYERS; ++i)
	{
		if (playeringame[i] && players[i].mo == NULL)
		{
			players[i].playerstate = PST_ENTER;
			P_SpawnPlayer (&playerstarts[i], false, &players[i]);
		}
	}
}
示例#10
0
/**
 * Changes the class of the given player. Will not work if the player
 * is currently morphed.
 */
void P_PlayerChangeClass(player_t *player, playerclass_t newClass)
{
    int i;

    DENG_ASSERT(player != 0);

    if(newClass < PCLASS_FIRST || newClass >= NUM_PLAYER_CLASSES)
        return;

    // Don't change if morphed.
    if(player->morphTics) return;
    if(!PCLASS_INFO(newClass)->userSelectable) return;

    player->class_ = newClass;
    cfg.playerClass[player - players] = newClass;
    P_ClassForPlayerWhenRespawning(player - players, true /*clear change request*/);

    // Take away armor.
    for(i = 0; i < NUMARMOR; ++i)
    {
        player->armorPoints[i] = 0;
    }
    player->update |= PSF_ARMOR_POINTS;

    P_PostMorphWeapon(player, WT_FIRST);

    if(player->plr->mo)
    {
        // Respawn the player and destroy the old mobj.
        mobj_t* oldMo = player->plr->mo;

        P_SpawnPlayer(player - players, newClass, oldMo->origin[VX],
                      oldMo->origin[VY], oldMo->origin[VZ], oldMo->angle, 0,
                      P_MobjIsCamera(oldMo), true);
        P_MobjRemove(oldMo, true);
    }
}
示例#11
0
void G_FinishTravel ()
{
	TThinkerIterator<APlayerPawn> it (STAT_TRAVELLING);
	APlayerPawn *pawn, *pawndup, *oldpawn, *next;
	AInventory *inv;

	next = it.Next ();
	while ( (pawn = next) != NULL)
	{
		next = it.Next ();
		pawn->ChangeStatNum (STAT_PLAYER);
		pawndup = pawn->player->mo;
		assert (pawn != pawndup);
		if (pawndup == NULL)
		{ // Oh no! there was no start for this player!
			pawn->flags |= MF_NOSECTOR|MF_NOBLOCKMAP;
			pawn->Destroy ();
		}
		else
		{
			oldpawn = pawndup;

			// The player being spawned here is a short lived dummy and
			// must not start any ENTER script or big problems will happen.
			pawndup = P_SpawnPlayer (&playerstarts[pawn->player - players], int(pawn->player - players), SPF_TEMPPLAYER);
			if (!(changeflags & CHANGELEVEL_KEEPFACING))
			{
				pawn->angle = pawndup->angle;
				pawn->pitch = pawndup->pitch;
			}
			pawn->x = pawndup->x;
			pawn->y = pawndup->y;
			pawn->z = pawndup->z;
			pawn->velx = pawndup->velx;
			pawn->vely = pawndup->vely;
			pawn->velz = pawndup->velz;
			pawn->Sector = pawndup->Sector;
			pawn->floorz = pawndup->floorz;
			pawn->ceilingz = pawndup->ceilingz;
			pawn->dropoffz = pawndup->dropoffz;
			pawn->floorsector = pawndup->floorsector;
			pawn->floorpic = pawndup->floorpic;
			pawn->ceilingsector = pawndup->ceilingsector;
			pawn->ceilingpic = pawndup->ceilingpic;
			pawn->floorclip = pawndup->floorclip;
			pawn->waterlevel = pawndup->waterlevel;
			pawn->target = NULL;
			pawn->lastenemy = NULL;
			pawn->player->mo = pawn;
			pawn->player->camera = pawn;
			DObject::StaticPointerSubstitution (oldpawn, pawn);
			oldpawn->Destroy();
			pawndup->Destroy ();
			pawn->LinkToWorld ();
			pawn->AddToHash ();
			pawn->SetState(pawn->SpawnState);
			pawn->player->SendPitchLimits();

			for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
			{
				inv->ChangeStatNum (STAT_INVENTORY);
				inv->LinkToWorld ();
				inv->Travelled ();
			}
			if (ib_compatflags & BCOMPATF_RESETPLAYERSPEED)
			{
				pawn->Speed = pawn->GetDefault()->Speed;
			}
			if (level.FromSnapshot)
			{
				FBehavior::StaticStartTypedScripts (SCRIPT_Return, pawn, true);
			}
		}
	}
}
示例#12
0
//===========================================================================
// P_CheckSpot
//  Returns false if the player cannot be respawned
//  at the given mapthing_t spot because something is occupying it 
//  FIXME: Quite a mess!
//===========================================================================
boolean P_CheckSpot(int playernum, mapthing_t * mthing, boolean doTeleSpark)
{
	fixed_t x;
	fixed_t y;
	unsigned an;
	mobj_t *mo;

#if __JDOOM__ || __JHEXEN__
	subsector_t *ss;
#endif
#if __JDOOM__
	int     i;
#endif
#if __JHERETIC__ || __JHEXEN__
	mapthing_t faraway;
	boolean using_dummy = false;
#endif

#if __JDOOM__
	if(!players[playernum].plr->mo)
	{
		// first spawn of level, before corpses
		for(i = 0; i < playernum; i++)
		{
			if(players[i].plr->mo &&
			   players[i].plr->mo->x == mthing->x << FRACBITS &&
			   players[i].plr->mo->y == mthing->y << FRACBITS)
				return false;
		}
		return true;
	}
#endif

	x = mthing->x << FRACBITS;
	y = mthing->y << FRACBITS;

#if __JHERETIC__ || __JHEXEN__
	if(!players[playernum].plr->mo)
	{
		// The player doesn't have a mobj. Let's create a dummy.
		faraway.x = faraway.y = DDMAXSHORT;
		P_SpawnPlayer(&faraway, playernum);
		using_dummy = true;
	}
	players[playernum].plr->mo->flags2 &= ~MF2_PASSMOBJ;
#endif

	if(!P_CheckPosition(players[playernum].plr->mo, x, y))
	{
#if __JHERETIC__ || __JHEXEN__
		players[playernum].plr->mo->flags2 |= MF2_PASSMOBJ;
		if(using_dummy)
		{
			P_RemoveMobj(players[playernum].plr->mo);
			players[playernum].plr->mo = NULL;
		}
#endif
		return false;
	}

#if __JHERETIC__
	players[playernum].plr->mo->flags2 |= MF2_PASSMOBJ;
#endif

#if __JHERETIC__ || __JHEXEN__
	if(using_dummy)
	{
		P_RemoveMobj(players[playernum].plr->mo);
		players[playernum].plr->mo = NULL;
	}
#endif

#if __JDOOM__
	G_QueueBody(players[playernum].plr->mo);
#endif

	if(doTeleSpark)
	{
		// spawn a teleport fog 
		an = (ANG45 * (mthing->angle / 45)) >> ANGLETOFINESHIFT;

#if __JDOOM__ || __JHEXEN__
		ss = R_PointInSubsector(x, y);
		mo = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an],
						 ss->sector->floorheight, MT_TFOG);
#else							// __JHERETIC__
		mo = P_SpawnTeleFog(x + 20 * finecosine[an], y + 20 * finesine[an]);
#endif

		// don't start sound on first frame
		if(players[consoleplayer].plr->viewz != 1)
		{
#ifdef __JHEXEN__
			S_StartSound(SFX_TELEPORT, mo);
#else
			S_StartSound(sfx_telept, mo);
#endif
		}
	}

	return true;
}
示例#13
0
void G_FinishTravel ()
{
	TThinkerIterator<APlayerPawn> it (STAT_TRAVELLING);
	APlayerPawn *pawn, *pawndup, *oldpawn, *next;
	AInventory *inv;
	FPlayerStart *start;
	int pnum;

	next = it.Next ();
	while ( (pawn = next) != NULL)
	{
		next = it.Next ();
		pnum = int(pawn->player - players);
		pawn->ChangeStatNum (STAT_PLAYER);
		pawndup = pawn->player->mo;
		start = NULL;
		assert (pawn != pawndup);
		if (pawndup == NULL)
		{ // Oh no! there was no start for this player!
			start = G_PickPlayerStart(pnum, PPS_FORCERANDOM); 
			if (start != NULL) pawndup = P_SpawnPlayer(start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
			if (pawndup == NULL)
			{
				pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP;
				pawn->Destroy();
				continue;
			}
		}

		if (start == NULL)
		{
			start = G_PickPlayerStart(pnum, 0);
			if (start == NULL)
			{
				Printf(TEXTCOLOR_RED "No player %d start to travel to!\n", pnum + 1);
				// Move to the coordinates this player had when they left the level.
				pawn->SetXYZ(pawndup->Pos());
			}
		}
		oldpawn = pawndup;

		// The player being spawned here is a short lived dummy and
		// must not start any ENTER script or big problems will happen.
		pawndup = P_SpawnPlayer(start, pnum, SPF_TEMPPLAYER);
		if (pawndup != NULL)
		{
			if (!(changeflags & CHANGELEVEL_KEEPFACING))
			{
				pawn->Angles = pawndup->Angles;
			}
			pawn->SetXYZ(pawndup->Pos());
			pawn->Vel = pawndup->Vel;
			pawn->Sector = pawndup->Sector;
			pawn->floorz = pawndup->floorz;
			pawn->ceilingz = pawndup->ceilingz;
			pawn->dropoffz = pawndup->dropoffz;
			pawn->floorsector = pawndup->floorsector;
			pawn->floorpic = pawndup->floorpic;
			pawn->floorterrain = pawndup->floorterrain;
			pawn->ceilingsector = pawndup->ceilingsector;
			pawn->ceilingpic = pawndup->ceilingpic;
			pawn->Floorclip = pawndup->Floorclip;
			pawn->waterlevel = pawndup->waterlevel;
		}
		else
		{
			P_FindFloorCeiling(pawn);
		}
		pawn->target = NULL;
		pawn->lastenemy = NULL;
		pawn->player->mo = pawn;
		pawn->player->camera = pawn;
		pawn->player->viewheight = pawn->ViewHeight;
		pawn->flags2 &= ~MF2_BLASTED;
		DObject::StaticPointerSubstitution (oldpawn, pawn);
		oldpawn->Destroy();
		if (pawndup != NULL)
		{
			pawndup->Destroy();
		}
		pawn->LinkToWorld ();
		pawn->ClearInterpolation();
		pawn->AddToHash ();
		pawn->SetState(pawn->SpawnState);
		pawn->player->SendPitchLimits();

		for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
		{
			inv->ChangeStatNum (STAT_INVENTORY);
			inv->LinkToWorld ();
			inv->Travelled ();
		}
		if (ib_compatflags & BCOMPATF_RESETPLAYERSPEED)
		{
			pawn->Speed = pawn->GetDefault()->Speed;
		}
		if (level.FromSnapshot)
		{
			FBehavior::StaticStartTypedScripts (SCRIPT_Return, pawn, true);
		}
	}

	bglobal.FinishTravel ();
}
示例#14
0
文件: p_mobj.c 项目: jaguar64/samples
void P_SpawnMapThing (mapthing_t *mthing)
{
    int			i, bit;
    mobj_t		*mobj;
    fixed_t		x,y,z;

    /* count deathmatch start positions */
    if (mthing->type == 11)
    {
        if (deathmatch_p < &deathmatchstarts[10])
            D_memcpy (deathmatch_p, mthing, sizeof(*mthing));
        deathmatch_p++;
        return;
    }

    /* check for players specially */

#if 0
    if (mthing->type > 4)
        return;	/*DEBUG */
#endif

    if (mthing->type <= 4)
    {
        /* save spots for respawning in network games */
        if (mthing->type <= MAXPLAYERS)
        {
            playerstarts[mthing->type-1] = *mthing;
            if (netgame != gt_deathmatch)
                P_SpawnPlayer (mthing);
        }
        return;
    }

    /* check for apropriate skill level */
    if ( (netgame != gt_deathmatch) && (mthing->options & 16) )
        return;

    if (gameskill == sk_baby)
        bit = 1;
    else if (gameskill == sk_nightmare)
        bit = 4;
    else
        bit = 1<<(gameskill-1);
    if (!(mthing->options & bit) )
        return;

#ifdef MARS
    /* hack player corpses into something else, because player graphics */
    /* aren't included */
    if (mthing->type == 10 || mthing->type == 12)	/* player corpse */
        mthing->type = 18;		/* possessed human corpse */
#endif


    /* find which type to spawn */
    for (i=0 ; i< NUMMOBJTYPES ; i++)
        if (mthing->type == mobjinfo[i].doomednum)
            break;

    if (i==NUMMOBJTYPES)
        I_Error ("P_SpawnMapThing: Unknown type %i at (%i, %i)",mthing->type
                 , mthing->x, mthing->y);



    /* don't spawn keycards and players in deathmatch */
    if (netgame == gt_deathmatch && mobjinfo[i].flags & (MF_NOTDMATCH|MF_COUNTKILL) )
        return;


    /* spawn it */

    x = mthing->x << FRACBITS;
    y = mthing->y << FRACBITS;
    if (mobjinfo[i].flags & MF_SPAWNCEILING)
        z = ONCEILINGZ;
    else
        z = ONFLOORZ;
    mobj = P_SpawnMobj (x,y,z, i);
    if (mobj->tics > 0)
        mobj->tics = 1 + (P_Random () % mobj->tics);
    if (mobj->flags & MF_COUNTKILL)
        totalkills++;
    if (mobj->flags & MF_COUNTITEM)
        totalitems++;

    mobj->angle = ANG45 * (mthing->angle/45);
    if (mthing->options & MTF_AMBUSH)
        mobj->flags |= MF_AMBUSH;

    mobj->spawnx = mthing->x;
    mobj->spawny = mthing->y;
    mobj->spawntype = mthing->type;
    mobj->spawnangle = mthing->angle;
}
示例#15
0
void G_FinishTravel ()
{
	TThinkerIterator<APlayerPawn> it (STAT_TRAVELLING);
	APlayerPawn *pawn, *pawndup, *oldpawn, *next;
	AInventory *inv;
	FPlayerStart *start;
	int pnum;

	next = it.Next ();
	while ( (pawn = next) != NULL)
	{
		next = it.Next ();
		pnum = int(pawn->player - players);
		pawn->ChangeStatNum (STAT_PLAYER);
		pawndup = pawn->player->mo;
		assert (pawn != pawndup);

		start = G_PickPlayerStart(pnum, 0);
		if (start == NULL)
		{
			if (pawndup != nullptr)
			{
				Printf(TEXTCOLOR_RED "No player %d start to travel to!\n", pnum + 1);
				// Move to the coordinates this player had when they left the level.
				pawn->SetXYZ(pawndup->Pos());
			}
			else
			{
				// Could not find a start for this player at all. This really should never happen but if it does, let's better abort.
				DThinker::DestroyThinkersInList(STAT_TRAVELLING);
				I_Error ("No player %d start to travel to!\n", pnum + 1);
			}
		}
		oldpawn = pawndup;

		// The player being spawned here is a short lived dummy and
		// must not start any ENTER script or big problems will happen.
		pawndup = P_SpawnPlayer(start, pnum, SPF_TEMPPLAYER);
		if (pawndup != NULL)
		{
			if (!(changeflags & CHANGELEVEL_KEEPFACING))
			{
				pawn->Angles = pawndup->Angles;
			}
			pawn->SetXYZ(pawndup->Pos());
			pawn->Vel = pawndup->Vel;
			pawn->Sector = pawndup->Sector;
			pawn->floorz = pawndup->floorz;
			pawn->ceilingz = pawndup->ceilingz;
			pawn->dropoffz = pawndup->dropoffz;
			pawn->floorsector = pawndup->floorsector;
			pawn->floorpic = pawndup->floorpic;
			pawn->floorterrain = pawndup->floorterrain;
			pawn->ceilingsector = pawndup->ceilingsector;
			pawn->ceilingpic = pawndup->ceilingpic;
			pawn->Floorclip = pawndup->Floorclip;
			pawn->waterlevel = pawndup->waterlevel;
		}
		else
		{
			P_FindFloorCeiling(pawn);
		}
		pawn->target = NULL;
		pawn->lastenemy = NULL;
		pawn->player->mo = pawn;
		pawn->player->camera = pawn;
		pawn->player->viewheight = pawn->ViewHeight;
		pawn->flags2 &= ~MF2_BLASTED;
		if (oldpawn != nullptr)
		{
			DObject::StaticPointerSubstitution (oldpawn, pawn);
			oldpawn->Destroy();
		}
		if (pawndup != NULL)
		{
			pawndup->Destroy();
		}
		pawn->LinkToWorld (nullptr);
		pawn->ClearInterpolation();
		pawn->AddToHash ();
		pawn->SetState(pawn->SpawnState);
		pawn->player->SendPitchLimits();

		for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
		{
			inv->ChangeStatNum (STAT_INVENTORY);
			inv->LinkToWorld (nullptr);
			inv->Travelled ();
		}
		if (ib_compatflags & BCOMPATF_RESETPLAYERSPEED)
		{
			pawn->Speed = pawn->GetDefault()->Speed;
		}
		if (level.FromSnapshot)
		{
			FBehavior::StaticStartTypedScripts (SCRIPT_Return, pawn, true);

			// [Nash] run REOPEN scripts upon map re-entry
			FBehavior::StaticStartTypedScripts(SCRIPT_Reopen, NULL, false);
		}
	}

	bglobal.FinishTravel ();

	// make sure that, after travelling has completed, no travelling thinkers are left.
	// Since this list is excluded from regular thinker cleaning, anything that may survive through here
	// will endlessly multiply and severely break the following savegames or just simply crash on broken pointers.
	DThinker::DestroyThinkersInList(STAT_TRAVELLING);
}