//=========================================================================== // 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; }
//***************************************************************************** // 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; } }
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]); }
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]); }
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]); } }
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]); } }
//========================================================================== // 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); } } } }
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); } } }
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]); } } }
/** * 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); } }
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); } } } }
//=========================================================================== // 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; }
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 (); }
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; }
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); }