Exemplo n.º 1
0
const char *cht_Morph (player_t *player, PClassPlayerPawn *morphclass, bool quickundo)
{
    if (player->mo == NULL)
    {
        return "";
    }
    PClassPlayerPawn *oldclass = player->mo->GetClass();

    // Set the standard morph style for the current game
    int style = MORPH_UNDOBYTOMEOFPOWER;
    if (gameinfo.gametype == GAME_Hexen) style |= MORPH_UNDOBYCHAOSDEVICE;

    if (player->morphTics)
    {
        if (P_UndoPlayerMorph (player, player))
        {
            if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style))
            {
                return GStrings("TXT_STRANGER");
            }
            return GStrings("TXT_NOTSTRANGE");
        }
    }
    else if (P_MorphPlayer (player, player, morphclass, 0, style))
    {
        return GStrings("TXT_STRANGE");
    }
    return "";
}
Exemplo n.º 2
0
END_DEFAULTS

bool AArtiTeleport::Use (bool pickup)
{
	fixed_t destX;
	fixed_t destY;
	angle_t destAngle;

	if (deathmatch)
	{
		unsigned int selections = deathmatchstarts.Size ();
		unsigned int i = pr_tele() % selections;
		destX = deathmatchstarts[i].x << FRACBITS;
		destY = deathmatchstarts[i].y << FRACBITS;
		destAngle = ANG45 * (deathmatchstarts[i].angle/45);
	}
	else
	{
		destX = playerstarts[Owner->player - players].x << FRACBITS;
		destY = playerstarts[Owner->player - players].y << FRACBITS;
		destAngle = ANG45 * (playerstarts[Owner->player - players].angle/45);
	}
	P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false);
	if (gameinfo.gametype == GAME_Hexen && Owner->player->morphTics)
	{ // Teleporting away will undo any morph effects (pig)
		P_UndoPlayerMorph (Owner->player);
	}
	if (gameinfo.gametype == GAME_Heretic)
	{ // Full volume laugh
		S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_NONE);
	}
	return true;
}
Exemplo n.º 3
0
bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth)
{
	// May be a morphed player
	if ((actor->player) &&
		(actor->player->morphTics) &&
		(actor->player->MorphStyle & MORPH_UNDOBYDEATH) &&
		(actor->player->mo) &&
		(actor->player->mo->tracer))
	{
		AActor *realme = actor->player->mo->tracer;
		int realstyle = actor->player->MorphStyle;
		int realhealth = actor->health;
		if (P_UndoPlayerMorph(actor->player, actor->player, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
		{
			*morphed = realme;
			*morphedstyle = realstyle;
			*morphedhealth = realhealth;
			return true;
		}
		return false;
	}

	// May be a morphed monster
	if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
	{
		AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
		AActor *realme = fakeme->UnmorphedMe;
		if (realme != NULL)
		{
			if ((fakeme->UnmorphTime) &&
				(fakeme->MorphStyle & MORPH_UNDOBYDEATH))
			{
				int realstyle = fakeme->MorphStyle;
				int realhealth = fakeme->health;
				if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
				{
					*morphed = realme;
					*morphedstyle = realstyle;
					*morphedhealth = realhealth;
					return true;
				}
			}
			if (realme->flags4 & MF4_BOSSDEATH)
			{
				realme->health = 0;	// make sure that A_BossDeath considers it dead.
				// FIXME: Use the caller's stack once the whole chain is scriptable.
				VMFrameStack stack;
				VMValue params[3] = { realme, realme, VMValue(NULL, ATAG_STATE) };
				stack.Call(A_BossDeath_VMPtr, params, countof(params), NULL, 0, NULL);
			}
		}
		fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
		return false;
	}

	// Not a morphed player or monster
	return false;
}
Exemplo n.º 4
0
bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth)
{
	// May be a morphed player
	if ((actor->player) &&
		(actor->player->morphTics) &&
		(actor->player->MorphStyle & MORPH_UNDOBYDEATH) &&
		(actor->player->mo) &&
		(actor->player->mo->tracer))
	{
		AActor *realme = actor->player->mo->tracer;
		int realstyle = actor->player->MorphStyle;
		int realhealth = actor->health;
		if (P_UndoPlayerMorph(actor->player, actor->player, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
		{
			*morphed = realme;
			*morphedstyle = realstyle;
			*morphedhealth = realhealth;
			return true;
		}
		return false;
	}

	// May be a morphed monster
	if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
	{
		AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
		AActor *realme = fakeme->UnmorphedMe;
		if (realme != NULL)
		{
			if ((fakeme->UnmorphTime) &&
				(fakeme->MorphStyle & MORPH_UNDOBYDEATH))
			{
				int realstyle = fakeme->MorphStyle;
				int realhealth = fakeme->health;
				if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
				{
					*morphed = realme;
					*morphedstyle = realstyle;
					*morphedhealth = realhealth;
					return true;
				}
			}
			if (realme->flags4 & MF4_BOSSDEATH)
			{
				realme->health = 0;	// make sure that A_BossDeath considers it dead.
				CALL_ACTION(A_BossDeath, realme);
			}
		}
		fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
		return false;
	}

	// Not a morphed player or monster
	return false;
}
Exemplo n.º 5
0
bool AArtiTomeOfPower::Use (bool pickup)
{
	if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYTOMEOFPOWER))
	{ // Attempt to undo chicken
		if (!P_UndoPlayerMorph (Owner->player, Owner->player, MORPH_UNDOBYTOMEOFPOWER))
		{ // Failed
			if (!(Owner->player->MorphStyle & MORPH_FAILNOTELEFRAG))
			{
				P_DamageMobj (Owner, NULL, NULL, TELEFRAG_DAMAGE, NAME_Telefrag);
			}
		}
		else
		{ // Succeeded
			S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_IDLE);
		}
		return true;
	}
	else
	{
		return Super::Use (pickup);
	}
}
Exemplo n.º 6
0
bool AArtiTeleport::Use (bool pickup)
{
	fixed_t destX;
	fixed_t destY;
	angle_t destAngle;

	if (deathmatch)
	{
		unsigned int selections = deathmatchstarts.Size ();
		unsigned int i = pr_tele() % selections;
		destX = deathmatchstarts[i].x;
		destY = deathmatchstarts[i].y;
		destAngle = ANG45 * (deathmatchstarts[i].angle/45);
	}
	else
	{
		destX = playerstarts[Owner->player - players].x;
		destY = playerstarts[Owner->player - players].y;
		destAngle = ANG45 * (playerstarts[Owner->player - players].angle/45);
	}
	P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false);
	bool canlaugh = true;
 	if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYCHAOSDEVICE))
 	{ // Teleporting away will undo any morph effects (pig)
		if (!P_UndoPlayerMorph (Owner->player, Owner->player, MORPH_UNDOBYCHAOSDEVICE)
			&& (Owner->player->MorphStyle & MORPH_FAILNOLAUGH))
		{
			canlaugh = false;
		}
 	}
	if (canlaugh)
 	{ // Full volume laugh
 		S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_NONE);
 	}
	return true;
}
Exemplo n.º 7
0
void cht_DoCheat (player_t *player, int cheat)
{
    static const char * const BeholdPowers[9] =
    {
        "PowerInvulnerable",
        "PowerStrength",
        "PowerInvisibility",
        "PowerIronFeet",
        "MapRevealer",
        "PowerLightAmp",
        "PowerShadow",
        "PowerMask",
        "PowerTargeter",
    };
    PClassActor *type;
    AInventory *item;
    const char *msg = "";
    char msgbuild[32];
    int i;

    switch (cheat)
    {
    case CHT_IDDQD:
        if (!(player->cheats & CF_GODMODE) && player->playerstate == PST_LIVE)
        {
            if (player->mo)
                player->mo->health = deh.GodHealth;

            player->health = deh.GodHealth;
        }
    // fall through to CHT_GOD
    case CHT_GOD:
        player->cheats ^= CF_GODMODE;
        if (player->cheats & CF_GODMODE)
            msg = GStrings("STSTR_DQDON");
        else
            msg = GStrings("STSTR_DQDOFF");
        ST_SetNeedRefresh();
        break;

    case CHT_BUDDHA:
        player->cheats ^= CF_BUDDHA;
        if (player->cheats & CF_BUDDHA)
            msg = GStrings("TXT_BUDDHAON");
        else
            msg = GStrings("TXT_BUDDHAOFF");
        break;

    case CHT_GOD2:
        player->cheats ^= CF_GODMODE2;
        if (player->cheats & CF_GODMODE2)
            msg = GStrings("STSTR_DQD2ON");
        else
            msg = GStrings("STSTR_DQD2OFF");
        ST_SetNeedRefresh();
        break;

    case CHT_BUDDHA2:
        player->cheats ^= CF_BUDDHA2;
        if (player->cheats & CF_BUDDHA2)
            msg = GStrings("TXT_BUDDHA2ON");
        else
            msg = GStrings("TXT_BUDDHA2OFF");
        break;

    case CHT_NOCLIP:
        player->cheats ^= CF_NOCLIP;
        if (player->cheats & CF_NOCLIP)
            msg = GStrings("STSTR_NCON");
        else
            msg = GStrings("STSTR_NCOFF");
        break;

    case CHT_NOCLIP2:
        player->cheats ^= CF_NOCLIP2;
        if (player->cheats & CF_NOCLIP2)
        {
            player->cheats |= CF_NOCLIP;
            msg = GStrings("STSTR_NC2ON");
        }
        else
        {
            player->cheats &= ~CF_NOCLIP;
            msg = GStrings("STSTR_NCOFF");
        }
        if (player->mo->Vel.X == 0) player->mo->Vel.X = MinVel;	// force some lateral movement so that internal variables are up to date
        break;

    case CHT_NOVELOCITY:
        player->cheats ^= CF_NOVELOCITY;
        if (player->cheats & CF_NOVELOCITY)
            msg = GStrings("TXT_LEADBOOTSON");
        else
            msg = GStrings("TXT_LEADBOOTSOFF");
        break;

    case CHT_FLY:
        if (player->mo != NULL)
        {
            player->mo->flags7 ^= MF7_FLYCHEAT;
            if (player->mo->flags7 & MF7_FLYCHEAT)
            {
                player->mo->flags |= MF_NOGRAVITY;
                player->mo->flags2 |= MF2_FLY;
                msg = GStrings("TXT_LIGHTER");
            }
            else
            {
                player->mo->flags &= ~MF_NOGRAVITY;
                player->mo->flags2 &= ~MF2_FLY;
                msg = GStrings("TXT_GRAVITY");
            }
        }
        break;

    case CHT_MORPH:
        msg = cht_Morph (player, static_cast<PClassPlayerPawn *>(PClass::FindClass (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer)), true);
        break;

    case CHT_NOTARGET:
        player->cheats ^= CF_NOTARGET;
        if (player->cheats & CF_NOTARGET)
            msg = "notarget ON";
        else
            msg = "notarget OFF";
        break;

    case CHT_ANUBIS:
        player->cheats ^= CF_FRIGHTENING;
        if (player->cheats & CF_FRIGHTENING)
            msg = "\"Quake with fear!\"";
        else
            msg = "No more ogre armor";
        break;

    case CHT_CHASECAM:
        player->cheats ^= CF_CHASECAM;
        if (player->cheats & CF_CHASECAM)
            msg = "chasecam ON";
        else
            msg = "chasecam OFF";
        R_ResetViewInterpolation ();
        break;

    case CHT_CHAINSAW:
        if (player->mo != NULL && player->health >= 0)
        {
            type = PClass::FindActor("Chainsaw");
            if (player->mo->FindInventory (type) == NULL)
            {
                player->mo->GiveInventoryType (type);
            }
            msg = GStrings("STSTR_CHOPPERS");
        }
        // [RH] The original cheat also set powers[pw_invulnerability] to true.
        // Since this is a timer and not a boolean, it effectively turned off
        // the invulnerability powerup, although it looks like it was meant to
        // turn it on.
        break;

    case CHT_POWER:
        if (player->mo != NULL && player->health >= 0)
        {
            item = player->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
            if (item != NULL)
            {
                item->Destroy ();
                msg = GStrings("TXT_CHEATPOWEROFF");
            }
            else
            {
                player->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2));
                msg = GStrings("TXT_CHEATPOWERON");
            }
        }
        break;

    case CHT_IDKFA:
        cht_Give (player, "backpack");
        cht_Give (player, "weapons");
        cht_Give (player, "ammo");
        cht_Give (player, "keys");
        cht_Give (player, "armor");
        msg = GStrings("STSTR_KFAADDED");
        break;

    case CHT_IDFA:
        cht_Give (player, "backpack");
        cht_Give (player, "weapons");
        cht_Give (player, "ammo");
        cht_Give (player, "armor");
        msg = GStrings("STSTR_FAADDED");
        break;

    case CHT_BEHOLDV:
    case CHT_BEHOLDS:
    case CHT_BEHOLDI:
    case CHT_BEHOLDR:
    case CHT_BEHOLDA:
    case CHT_BEHOLDL:
    case CHT_PUMPUPI:
    case CHT_PUMPUPM:
    case CHT_PUMPUPT:
        i = cheat - CHT_BEHOLDV;

        if (i == 4)
        {
            level.flags2 ^= LEVEL2_ALLMAP;
        }
        else if (player->mo != NULL && player->health >= 0)
        {
            item = player->mo->FindInventory(PClass::FindActor(BeholdPowers[i]));
            if (item == NULL)
            {
                if (i != 0)
                {
                    cht_Give(player, BeholdPowers[i]);
                    if (cheat == CHT_BEHOLDS)
                    {
                        P_GiveBody (player->mo, -100);
                    }
                }
                else
                {
                    // Let's give the item here so that the power doesn't need colormap information.
                    cht_Give(player, "InvulnerabilitySphere");
                }
            }
            else
            {
                item->Destroy ();
            }
        }
        msg = GStrings("STSTR_BEHOLDX");
        break;

    case CHT_MASSACRE:
    {
        int killcount = P_Massacre ();
        // killough 3/22/98: make more intelligent about plural
        // Ty 03/27/98 - string(s) *not* externalized
        mysnprintf (msgbuild, countof(msgbuild), "%d Monster%s Killed", killcount, killcount==1 ? "" : "s");
        msg = msgbuild;
    }
    break;

    case CHT_HEALTH:
        if (player->mo != NULL && player->playerstate == PST_LIVE)
        {
            player->health = player->mo->health = player->mo->GetDefault()->health;
            msg = GStrings("TXT_CHEATHEALTH");
        }
        break;

    case CHT_KEYS:
        cht_Give (player, "keys");
        msg = GStrings("TXT_CHEATKEYS");
        break;

    // [GRB]
    case CHT_RESSURECT:
        if (player->playerstate != PST_LIVE && player->mo != nullptr)
        {
            if (player->mo->IsKindOf(RUNTIME_CLASS(APlayerChunk)))
            {
                Printf("Unable to resurrect. Player is no longer connected to its body.\n");
            }
            else
            {
                player->playerstate = PST_LIVE;
                player->health = player->mo->health = player->mo->GetDefault()->health;
                player->viewheight = ((APlayerPawn *)player->mo->GetDefault())->ViewHeight;
                player->mo->flags = player->mo->GetDefault()->flags;
                player->mo->flags2 = player->mo->GetDefault()->flags2;
                player->mo->flags3 = player->mo->GetDefault()->flags3;
                player->mo->flags4 = player->mo->GetDefault()->flags4;
                player->mo->flags5 = player->mo->GetDefault()->flags5;
                player->mo->flags6 = player->mo->GetDefault()->flags6;
                player->mo->flags7 = player->mo->GetDefault()->flags7;
                player->mo->renderflags &= ~RF_INVISIBLE;
                player->mo->Height = player->mo->GetDefault()->Height;
                player->mo->radius = player->mo->GetDefault()->radius;
                player->mo->special1 = 0;	// required for the Hexen fighter's fist attack.
                // This gets set by AActor::Die as flag for the wimpy death and must be reset here.
                player->mo->SetState (player->mo->SpawnState);
                if (!(player->mo->flags2 & MF2_DONTTRANSLATE))
                {
                    player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players));
                }
                player->mo->DamageType = NAME_None;
                if (player->ReadyWeapon != nullptr)
                {
                    P_SetPsprite(player, PSP_WEAPON, player->ReadyWeapon->GetUpState());
                }

                if (player->morphTics > 0)
                {
                    P_UndoPlayerMorph(player, player);
                }

            }
        }
        break;

    case CHT_GIMMIEA:
        cht_Give (player, "ArtiInvulnerability");
        msg = "Valador's Ring of Invunerability";
        break;

    case CHT_GIMMIEB:
        cht_Give (player, "ArtiInvisibility");
        msg = "Shadowsphere";
        break;

    case CHT_GIMMIEC:
        cht_Give (player, "ArtiHealth");
        msg = "Quartz Flask";
        break;

    case CHT_GIMMIED:
        cht_Give (player, "ArtiSuperHealth");
        msg = "Mystic Urn";
        break;

    case CHT_GIMMIEE:
        cht_Give (player, "ArtiTomeOfPower");
        msg = "Tyketto's Tome of Power";
        break;

    case CHT_GIMMIEF:
        cht_Give (player, "ArtiTorch");
        msg = "Torch";
        break;

    case CHT_GIMMIEG:
        cht_Give (player, "ArtiTimeBomb");
        msg = "Delmintalintar's Time Bomb of the Ancients";
        break;

    case CHT_GIMMIEH:
        cht_Give (player, "ArtiEgg");
        msg = "Torpol's Morph Ovum";
        break;

    case CHT_GIMMIEI:
        cht_Give (player, "ArtiFly");
        msg = "Inhilicon's Wings of Wrath";
        break;

    case CHT_GIMMIEJ:
        cht_Give (player, "ArtiTeleport");
        msg = "Darchala's Chaos Device";
        break;

    case CHT_GIMMIEZ:
        for (int i=0; i<16; i++)
        {
            cht_Give (player, "artifacts");
        }
        msg = "All artifacts!";
        break;

    case CHT_TAKEWEAPS:
        if (player->morphTics || player->mo == NULL || player->mo->health <= 0)
        {
            return;
        }
        {
            // Take away all weapons that are either non-wimpy or use ammo.
            AInventory **invp = &player->mo->Inventory, **lastinvp;
            for (item = *invp; item != NULL; item = *invp)
            {
                lastinvp = invp;
                invp = &(*invp)->Inventory;
                if (item->IsKindOf (RUNTIME_CLASS(AWeapon)))
                {
                    AWeapon *weap = static_cast<AWeapon *> (item);
                    if (!(weap->WeaponFlags & WIF_WIMPY_WEAPON) ||
                            weap->AmmoType1 != NULL)
                    {
                        item->Destroy ();
                        invp = lastinvp;
                    }
                }
            }
        }
        msg = GStrings("TXT_CHEATIDKFA");
        break;

    case CHT_NOWUDIE:
        cht_Suicide (player);
        msg = GStrings("TXT_CHEATIDDQD");
        break;

    case CHT_ALLARTI:
        for (int i=0; i<25; i++)
        {
            cht_Give (player, "artifacts");
        }
        msg = GStrings("TXT_CHEATARTIFACTS3");
        break;

    case CHT_PUZZLE:
        cht_Give (player, "puzzlepieces");
        msg = GStrings("TXT_CHEATARTIFACTS3");
        break;

    case CHT_MDK:
        if (player->mo == NULL)
        {
            Printf ("What do you want to kill outside of a game?\n");
        }
        else if (!deathmatch)
        {
            // Don't allow this in deathmatch even with cheats enabled, because it's
            // a very very cheap kill.
            P_LineAttack (player->mo, player->mo->Angles.Yaw, PLAYERMISSILERANGE,
                          P_AimLineAttack (player->mo, player->mo->Angles.Yaw, PLAYERMISSILERANGE), TELEFRAG_DAMAGE,
                          NAME_MDK, NAME_BulletPuff);
        }
        break;

    case CHT_DONNYTRUMP:
        cht_Give (player, "HealthTraining");
        msg = GStrings("TXT_MIDASTOUCH");
        break;

    case CHT_LEGO:
        if (player->mo != NULL && player->health >= 0)
        {
            int oldpieces = ASigil::GiveSigilPiece (player->mo);
            item = player->mo->FindInventory (RUNTIME_CLASS(ASigil));

            if (item != NULL)
            {
                if (oldpieces == 5)
                {
                    item->Destroy ();
                }
                else
                {
                    player->PendingWeapon = static_cast<AWeapon *> (item);
                }
            }
        }
        break;

    case CHT_PUMPUPH:
        cht_Give (player, "MedPatch");
        cht_Give (player, "MedicalKit");
        cht_Give (player, "SurgeryKit");
        msg = GStrings("TXT_GOTSTUFF");
        break;

    case CHT_PUMPUPP:
        cht_Give (player, "AmmoSatchel");
        msg = GStrings("TXT_GOTSTUFF");
        break;

    case CHT_PUMPUPS:
        cht_Give (player, "UpgradeStamina", 10);
        cht_Give (player, "UpgradeAccuracy");
        msg = GStrings("TXT_GOTSTUFF");
        break;

    case CHT_CLEARFROZENPROPS:
        player->cheats &= ~(CF_FROZEN|CF_TOTALLYFROZEN);
        msg = "Frozen player properties turned off";
        break;

    case CHT_FREEZE:
        bglobal.changefreeze ^= 1;
        if (bglobal.freeze ^ bglobal.changefreeze)
        {
            msg = GStrings("TXT_FREEZEON");
        }
        else
        {
            msg = GStrings("TXT_FREEZEOFF");
        }
        break;
    }

    if (!*msg)              // [SO] Don't print blank lines!
        return;

    if (player == &players[consoleplayer])
        Printf ("%s\n", msg);
    else if (cheat != CHT_CHASECAM)
        Printf ("%s cheats: %s\n", player->userinfo.GetName(), msg);
}
Exemplo n.º 8
0
bool AArtiTeleport::Use (bool pickup)
{
	fixed_t destX;
	fixed_t destY;
	angle_t destAngle;

	// [BC] Let the server decide where we go.
	if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) ||
		( CLIENTDEMO_IsPlaying( )))
	{
		return ( true );
	}

	// [BB] If this is a team game and there are valid team starts for the team
	// the owner is on, teleport to one of the team starts.
	const ULONG ownerTeam = Owner->player ? Owner->player->ulTeam : teams.Size( );
	if ( ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_PLAYERSONTEAMS )
	     && TEAM_CheckIfValid ( ownerTeam )
	     && ( teams[ownerTeam].TeamStarts.Size( ) > 0 ) )
	{
		unsigned int selections = teams[ownerTeam].TeamStarts.Size ();
		unsigned int i = pr_tele() % selections;
		destX = teams[ownerTeam].TeamStarts[i].x;
		destY = teams[ownerTeam].TeamStarts[i].y;
		destAngle = ANG45 * (teams[ownerTeam].TeamStarts[i].angle/45);
	}
	else if (deathmatch)
	{
		unsigned int selections = deathmatchstarts.Size ();
		unsigned int i = pr_tele() % selections;
		destX = deathmatchstarts[i].x;
		destY = deathmatchstarts[i].y;
		destAngle = ANG45 * (deathmatchstarts[i].angle/45);
	}
	else
	{
		FMapThing *pSpot = NULL;
		// [BB] If there is a designated start for this player use it.
		if ( playerstarts[Owner->player - players].type != 0 )
			pSpot = &playerstarts[Owner->player - players];
		// [BB] Otherwise we just have to select a start at random from all available player starts.
		else
			pSpot = SelectRandomCooperativeSpot( Owner->player - players );

		if ( pSpot != NULL )
		{
			destX = pSpot->x;
			destY = pSpot->y;
			destAngle = ANG45 * (pSpot->angle/45);
		}
		else
			I_Error( "ArtiTeleport: No player start found!" );
	}
	P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false);
	bool canlaugh = true;
 	if (Owner->player->morphTics && (Owner->player->MorphStyle & MORPH_UNDOBYCHAOSDEVICE))
 	{ // Teleporting away will undo any morph effects (pig)
		if (!P_UndoPlayerMorph (Owner->player, Owner->player, MORPH_UNDOBYCHAOSDEVICE)
			&& (Owner->player->MorphStyle & MORPH_FAILNOLAUGH))
		{
			canlaugh = false;
		}
 	}
	if (canlaugh)
 	{ // Full volume laugh
 		S_Sound (Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_NONE);

		// [BC] Play the laugh for clients.
		if ( NETWORK_GetState( ) == NETSTATE_SERVER )
			SERVERCOMMANDS_SoundActor( Owner, CHAN_VOICE, "*evillaugh", 1, ATTN_NONE );
 	}
	return true;
}