raze/source/games/sw/src/sprite.cpp
2021-11-29 00:56:14 +01:00

7319 lines
200 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
This file is part of Shadow Warrior version 1.2
Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "build.h"
#include "names2.h"
#include "panel.h"
#include "misc.h"
#include "tags.h"
#include "ai.h"
#include "light.h"
#include "break.h"
#include "network.h"
#include "pal.h"
#include "sounds.h"
#include "interpolate.h"
#include "interpso.h"
#include "sprite.h"
#include "weapon.h"
#include "jsector.h"
#include "misc.h"
#include "player.h"
#include "quotemgr.h"
#include "v_text.h"
#include "gamecontrol.h"
#include "gamefuncs.h"
BEGIN_SW_NS
int SetupCoolie(DSWActor*);
int SetupNinja(DSWActor*);
int SetupGoro(DSWActor*);
int SetupCoolg(DSWActor*);
int SetupEel(DSWActor*);
int SetupSumo(DSWActor*);
int SetupZilla(DSWActor*);
int SetupToiletGirl(DSWActor*);
int SetupWashGirl(DSWActor*);
int SetupCarGirl(DSWActor*);
int SetupMechanicGirl(DSWActor*);
int SetupSailorGirl(DSWActor*);
int SetupPruneGirl(DSWActor*);
int SetupTrashCan(DSWActor*);
int SetupBunny(DSWActor*);
int SetupRipper(DSWActor*);
int SetupRipper2(DSWActor*);
int SetupSerp(DSWActor*);
int SetupLava(DSWActor* actor);
int SetupSkel(DSWActor*);
int SetupHornet(DSWActor*);
int SetupSkull(DSWActor*);
int SetupBetty(DSWActor*);
int SetupPachinkoLight(DSWActor*);
int SetupPachinko1(DSWActor*);
int SetupPachinko2(DSWActor*);
int SetupPachinko3(DSWActor*);
int SetupPachinko4(DSWActor*);
int SetupGirlNinja(DSWActor*);
ANIMATOR DoVator, DoVatorAuto;
ANIMATOR DoRotator;
ANIMATOR DoSlidor;
ANIMATOR DoSpike, DoSpikeAuto;
ANIMATOR DoLavaErupt;
int DoSlidorInstantClose(DSWActor*);
void InitWeaponRocket(PLAYERp);
void InitWeaponUzi(PLAYERp);
bool FAF_Sector(short sectnum);
int MoveSkip4, MoveSkip2, MoveSkip8;
int MinEnemySkill;
extern STATE s_CarryFlag[];
extern STATE s_CarryFlagNoDet[];
static int globhiz, globloz, globhihit, globlohit;
short wait_active_check_offset;
int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust;
void SetSectorWallBits(short sectnum, int bit_mask, bool set_sectwall, bool set_nextwall);
int DoActorDebris(DSWActor* actor);
void ActorWarpUpdatePos(DSWActor*,short sectnum);
void ActorWarpType(DSWActor* sp, DSWActor* act_warp);
int MissileZrange(DSWActor*);
#define ACTIVE_CHECK_TIME (3*120)
TRACK Track[MAX_TRACKS];
SECTOR_OBJECT SectorObject[MAX_SECTOR_OBJECTS];
int DirArr[] = {NORTH, NE, EAST, SE, SOUTH, SW, WEST, NW, NORTH, NE, EAST, SE, SOUTH, SW, WEST, NW};
#define SCROLL_RATE 20
#define SCROLL_FIRE_RATE 20
STATE s_DebrisNinja[] =
{
{NINJA_DIE + 3, 100, DoActorDebris, &s_DebrisNinja[0]},
};
STATE s_DebrisRat[] =
{
{750, 100, DoActorDebris, &s_DebrisRat[0]},
};
STATE s_DebrisCrab[] =
{
{423, 100, DoActorDebris, &s_DebrisCrab[0]},
};
STATE s_DebrisStarFish[] =
{
{426, 100, DoActorDebris, &s_DebrisStarFish[0]},
};
ANIMATOR DoGet, DoKey, DoSpriteFade;
// temporary
#define ICON_REPAIR_KIT 1813
#define REPAIR_KIT_RATE 1100
STATE s_RepairKit[] =
{
{ICON_REPAIR_KIT + 0, REPAIR_KIT_RATE, DoGet, &s_RepairKit[0]}
};
STATE s_GoldSkelKey[] =
{
{GOLD_SKELKEY, 100, DoGet, &s_GoldSkelKey[0]}
};
STATE s_BlueKey[] =
{
{BLUE_KEY, 100, DoGet, &s_BlueKey[0]}
};
STATE s_BlueCard[] =
{
{BLUE_CARD, 100, DoGet, &s_BlueCard[0]}
};
STATE s_SilverSkelKey[] =
{
{SILVER_SKELKEY, 100, DoGet, &s_SilverSkelKey[0]}
};
STATE s_RedKey[] =
{
{RED_KEY, 100, DoGet, &s_RedKey[0]}
};
STATE s_RedCard[] =
{
{RED_CARD, 100, DoGet, &s_RedCard[0]}
};
STATE s_BronzeSkelKey[] =
{
{BRONZE_SKELKEY, 100, DoGet, &s_BronzeSkelKey[0]}
};
STATE s_GreenKey[] =
{
{GREEN_KEY, 100, DoGet, &s_GreenKey[0]}
};
STATE s_GreenCard[] =
{
{GREEN_CARD, 100, DoGet, &s_GreenCard[0]}
};
STATE s_RedSkelKey[] =
{
{RED_SKELKEY, 100, DoGet, &s_RedSkelKey[0]}
};
STATE s_YellowKey[] =
{
{YELLOW_KEY, 100, DoGet, &s_YellowKey[0]}
};
STATE s_YellowCard[] =
{
{YELLOW_CARD, 100, DoGet, &s_YellowCard[0]}
};
STATEp s_Key[] =
{
s_RedKey,
s_BlueKey,
s_GreenKey,
s_YellowKey,
s_RedCard,
s_BlueCard,
s_GreenCard,
s_YellowCard,
s_GoldSkelKey,
s_SilverSkelKey,
s_BronzeSkelKey,
s_RedSkelKey
};
#define KEY_RATE 25
#if 0
STATE s_BlueKey[] =
{
{BLUE_KEY + 0, KEY_RATE, DoKey, &s_BlueKey[0]}
};
STATE s_RedKey[] =
{
{RED_KEY + 0, KEY_RATE, DoKey, &s_RedKey[0]}
};
STATE s_GreenKey[] =
{
{GREEN_KEY + 0, KEY_RATE, DoKey, &s_GreenKey[0]}
};
STATE s_YellowKey[] =
{
{YELLOW_KEY + 0, KEY_RATE, DoKey, &s_YellowKey[0]}
};
STATEp s_Key[] =
{
s_RedKey,
s_BlueKey,
s_GreenKey,
s_YellowKey,
s_RedCard,
s_BlueCard,
s_GreenCard,
s_YellowCard,
s_GoldSkelKey,
s_SilverSkelKey,
s_BronzeSkelKey,
s_RedSkelKey
};
#endif
#if 0
STATE s_BlueKeyStatue[] =
{
{BLUE_KEY_STATUE + 0, KEY_RATE, DoSpriteFade, &s_BlueKeyStatue[0]}
};
STATE s_RedKeyStatue[] =
{
{RED_KEY_STATUE + 0, KEY_RATE, DoSpriteFade, &s_RedKeyStatue[0]}
};
STATE s_GreenKeyStatue[] =
{
{GREEN_KEY_STATUE + 0, KEY_RATE, DoSpriteFade, &s_GreenKeyStatue[0]}
};
STATE s_YellowKeyStatue[] =
{
{YELLOW_KEY_STATUE + 0, KEY_RATE, DoSpriteFade, &s_YellowKeyStatue[0]}
};
STATEp s_KeyStatue[] =
{
s_RedKeyStatue,
s_BlueKeyStatue,
s_GreenKeyStatue,
s_YellowKeyStatue,
};
#endif
#define Red_COIN 2440
#define Yellow_COIN 2450
#define Green_COIN 2460
#define RED_COIN_RATE 10
#define YELLOW_COIN_RATE 8
#define GREEN_COIN_RATE 6
ANIMATOR DoCoin;
STATE s_RedCoin[] =
{
{Red_COIN + 0, RED_COIN_RATE, DoCoin, &s_RedCoin[1]},
{Red_COIN + 1, RED_COIN_RATE, DoCoin, &s_RedCoin[2]},
{Red_COIN + 2, RED_COIN_RATE, DoCoin, &s_RedCoin[3]},
{Red_COIN + 3, RED_COIN_RATE, DoCoin, &s_RedCoin[4]},
{Red_COIN + 4, RED_COIN_RATE, DoCoin, &s_RedCoin[5]},
{Red_COIN + 5, RED_COIN_RATE, DoCoin, &s_RedCoin[6]},
{Red_COIN + 6, RED_COIN_RATE, DoCoin, &s_RedCoin[7]},
{Red_COIN + 7, RED_COIN_RATE, DoCoin, &s_RedCoin[0]},
};
// !JIM! Frank, I made coins go progressively faster
STATE s_YellowCoin[] =
{
{Yellow_COIN + 0, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[1]},
{Yellow_COIN + 1, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[2]},
{Yellow_COIN + 2, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[3]},
{Yellow_COIN + 3, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[4]},
{Yellow_COIN + 4, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[5]},
{Yellow_COIN + 5, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[6]},
{Yellow_COIN + 6, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[7]},
{Yellow_COIN + 7, YELLOW_COIN_RATE, DoCoin, &s_YellowCoin[0]},
};
STATE s_GreenCoin[] =
{
{Green_COIN + 0, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[1]},
{Green_COIN + 1, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[2]},
{Green_COIN + 2, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[3]},
{Green_COIN + 3, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[4]},
{Green_COIN + 4, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[5]},
{Green_COIN + 5, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[6]},
{Green_COIN + 6, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[7]},
{Green_COIN + 7, GREEN_COIN_RATE, DoCoin, &s_GreenCoin[0]},
};
ANIMATOR DoFireFly;
#if 0
STATE s_FireFly[] =
{
{FIRE_FLY0, 120 * 3, DoFireFly, &s_FireFly[1]},
{FIRE_FLY1, 20, DoFireFly, &s_FireFly[2]},
{FIRE_FLY2, 20, DoFireFly, &s_FireFly[3]},
{FIRE_FLY3, 20, DoFireFly, &s_FireFly[4]},
{FIRE_FLY4, 60, DoFireFly, &s_FireFly[0]},
};
#else
STATE s_FireFly[] =
{
{FIRE_FLY0, FIRE_FLY_RATE * 4, DoFireFly, &s_FireFly[0]}
};
#endif
STATE s_IconStar[] =
{
{ICON_STAR, 100, DoGet, &s_IconStar[0]}
};
STATE s_IconUzi[] =
{
{ICON_UZI, 100, DoGet, &s_IconUzi[0]}
};
STATE s_IconLgUziAmmo[] =
{
{ICON_LG_UZI_AMMO, 100, DoGet, &s_IconLgUziAmmo[0]}
};
STATE s_IconUziFloor[] =
{
{ICON_UZIFLOOR, 100, DoGet, &s_IconUziFloor[0]}
};
STATE s_IconRocket[] =
{
{ICON_ROCKET, 100, DoGet, &s_IconRocket[0]}
};
STATE s_IconLgRocket[] =
{
{ICON_LG_ROCKET, 100, DoGet, &s_IconLgRocket[0]}
};
STATE s_IconShotgun[] =
{
{ICON_SHOTGUN, 100, DoGet, &s_IconShotgun[0]}
};
STATE s_IconLgShotshell[] =
{
{ICON_LG_SHOTSHELL, 100, DoGet, &s_IconLgShotshell[0]}
};
STATE s_IconAutoRiot[] =
{
{ICON_AUTORIOT, 100, DoGet, &s_IconAutoRiot[0]}
};
STATE s_IconGrenadeLauncher[] =
{
{ICON_GRENADE_LAUNCHER, 100, DoGet, &s_IconGrenadeLauncher[0]}
};
STATE s_IconLgGrenade[] =
{
{ICON_LG_GRENADE, 100, DoGet, &s_IconLgGrenade[0]}
};
STATE s_IconLgMine[] =
{
{ICON_LG_MINE, 100, DoGet, &s_IconLgMine[0]}
};
STATE s_IconGuardHead[] =
{
{ICON_GUARD_HEAD + 0, 15, DoGet, &s_IconGuardHead[0]},
// {ICON_GUARD_HEAD + 1, 15, DoGet, &s_IconGuardHead[2]},
// {ICON_GUARD_HEAD + 2, 15, DoGet, &s_IconGuardHead[0]}
};
#define FIREBALL_LG_AMMO_RATE 12
STATE s_IconFireballLgAmmo[] =
{
{ICON_FIREBALL_LG_AMMO + 0, FIREBALL_LG_AMMO_RATE, DoGet, &s_IconFireballLgAmmo[1]},
{ICON_FIREBALL_LG_AMMO + 1, FIREBALL_LG_AMMO_RATE, DoGet, &s_IconFireballLgAmmo[2]},
{ICON_FIREBALL_LG_AMMO + 2, FIREBALL_LG_AMMO_RATE, DoGet, &s_IconFireballLgAmmo[0]},
};
STATE s_IconHeart[] =
{
{ICON_HEART + 0, 25, DoGet, &s_IconHeart[1]},
{ICON_HEART + 1, 25, DoGet, &s_IconHeart[0]},
};
#define HEART_LG_AMMO_RATE 12
STATE s_IconHeartLgAmmo[] =
{
{ICON_HEART_LG_AMMO + 0, HEART_LG_AMMO_RATE, DoGet, &s_IconHeartLgAmmo[1]},
{ICON_HEART_LG_AMMO + 1, HEART_LG_AMMO_RATE, DoGet, &s_IconHeartLgAmmo[0]},
};
STATE s_IconMicroGun[] =
{
{ICON_MICRO_GUN, 100, DoGet, &s_IconMicroGun[0]}
};
STATE s_IconMicroBattery[] =
{
{ICON_MICRO_BATTERY, 100, DoGet, &s_IconMicroBattery[0]}
};
// !JIM! Added rail crap
STATE s_IconRailGun[] =
{
{ICON_RAIL_GUN, 100, DoGet, &s_IconRailGun[0]}
};
STATE s_IconRailAmmo[] =
{
{ICON_RAIL_AMMO, 100, DoGet, &s_IconRailAmmo[0]}
};
STATE s_IconElectro[] =
{
{ICON_ELECTRO + 0, 25, DoGet, &s_IconElectro[1]},
{ICON_ELECTRO + 1, 25, DoGet, &s_IconElectro[0]},
};
#define ICON_SPELL_RATE 8
STATE s_IconSpell[] =
{
{ICON_SPELL + 0, ICON_SPELL_RATE, DoGet, &s_IconSpell[1]},
{ICON_SPELL + 1, ICON_SPELL_RATE, DoGet, &s_IconSpell[2]},
{ICON_SPELL + 2, ICON_SPELL_RATE, DoGet, &s_IconSpell[3]},
{ICON_SPELL + 3, ICON_SPELL_RATE, DoGet, &s_IconSpell[4]},
{ICON_SPELL + 4, ICON_SPELL_RATE, DoGet, &s_IconSpell[5]},
{ICON_SPELL + 5, ICON_SPELL_RATE, DoGet, &s_IconSpell[6]},
{ICON_SPELL + 6, ICON_SPELL_RATE, DoGet, &s_IconSpell[7]},
{ICON_SPELL + 7, ICON_SPELL_RATE, DoGet, &s_IconSpell[8]},
{ICON_SPELL + 8, ICON_SPELL_RATE, DoGet, &s_IconSpell[9]},
{ICON_SPELL + 9, ICON_SPELL_RATE, DoGet, &s_IconSpell[10]},
{ICON_SPELL + 10, ICON_SPELL_RATE, DoGet, &s_IconSpell[11]},
{ICON_SPELL + 11, ICON_SPELL_RATE, DoGet, &s_IconSpell[12]},
{ICON_SPELL + 12, ICON_SPELL_RATE, DoGet, &s_IconSpell[13]},
{ICON_SPELL + 13, ICON_SPELL_RATE, DoGet, &s_IconSpell[14]},
{ICON_SPELL + 14, ICON_SPELL_RATE, DoGet, &s_IconSpell[15]},
{ICON_SPELL + 15, ICON_SPELL_RATE, DoGet, &s_IconSpell[0]},
};
STATE s_IconArmor[] =
{
{ICON_ARMOR + 0, 15, DoGet, &s_IconArmor[0]},
};
STATE s_IconMedkit[] =
{
{ICON_MEDKIT + 0, 15, DoGet, &s_IconMedkit[0]},
};
STATE s_IconChemBomb[] =
{
{ICON_CHEMBOMB, 15, DoGet, &s_IconChemBomb[0]},
};
STATE s_IconFlashBomb[] =
{
{ICON_FLASHBOMB, 15, DoGet, &s_IconFlashBomb[0]},
};
STATE s_IconNuke[] =
{
{ICON_NUKE, 15, DoGet, &s_IconNuke[0]},
};
STATE s_IconCaltrops[] =
{
{ICON_CALTROPS, 15, DoGet, &s_IconCaltrops[0]},
};
#define ICON_SM_MEDKIT 1802
STATE s_IconSmMedkit[] =
{
{ICON_SM_MEDKIT + 0, 15, DoGet, &s_IconSmMedkit[0]},
};
#define ICON_BOOSTER 1810
STATE s_IconBooster[] =
{
{ICON_BOOSTER + 0, 15, DoGet, &s_IconBooster[0]},
};
#define ICON_HEAT_CARD 1819
STATE s_IconHeatCard[] =
{
{ICON_HEAT_CARD + 0, 15, DoGet, &s_IconHeatCard[0]},
};
#if 0
STATE s_IconEnvironSuit[] =
{
{ICON_ENVIRON_SUIT + 0, 20, DoGet, &s_IconEnvironSuit[0]},
};
#endif
STATE s_IconCloak[] =
{
// {ICON_CLOAK + 0, 20, DoGet, &s_IconCloak[1]},
// {ICON_CLOAK + 1, 20, DoGet, &s_IconCloak[2]},
{ICON_CLOAK + 0, 20, DoGet, &s_IconCloak[0]},
};
STATE s_IconFly[] =
{
{ICON_FLY + 0, 20, DoGet, &s_IconFly[1]},
{ICON_FLY + 1, 20, DoGet, &s_IconFly[2]},
{ICON_FLY + 2, 20, DoGet, &s_IconFly[3]},
{ICON_FLY + 3, 20, DoGet, &s_IconFly[4]},
{ICON_FLY + 4, 20, DoGet, &s_IconFly[5]},
{ICON_FLY + 5, 20, DoGet, &s_IconFly[6]},
{ICON_FLY + 6, 20, DoGet, &s_IconFly[7]},
{ICON_FLY + 7, 20, DoGet, &s_IconFly[0]}
};
STATE s_IconNightVision[] =
{
{ICON_NIGHT_VISION + 0, 20, DoGet, &s_IconNightVision[0]},
};
STATE s_IconFlag[] =
{
{ICON_FLAG + 0, 32, DoGet, &s_IconFlag[1]},
{ICON_FLAG + 1, 32, DoGet, &s_IconFlag[2]},
{ICON_FLAG + 2, 32, DoGet, &s_IconFlag[0]}
};
void SetOwner(DSWActor* ownr, DSWActor* child, bool flag)
{
SPRITEp cp = &child->s();
if (flag && ownr != nullptr && ownr->hasU())
{
SET(ownr->u()->Flags2, SPR2_CHILDREN);
}
cp->owner = ownr? ownr->GetSpriteIndex() : -1;
}
DSWActor* GetOwner(DSWActor* child)
{
if (!child || child->s().owner < 0) return nullptr;
return &swActors[child->s().owner];
}
void ClearOwner(DSWActor* child)
{
if (child) child->s().owner = -1;
}
void SetOwner(int a, int b)
{
if (a >= 0 && b >= 0) SetOwner(&swActors[a], &swActors[b]);
else if (b >= 0) ClearOwner(&swActors[b]);
}
void SetAttach(DSWActor* ownr, DSWActor* child)
{
if (child && child->hasU() && ownr->hasU())
{
USERp cu = child->u();
SET(ownr->u()->Flags2, SPR2_CHILDREN);
cu->attachActor = ownr;
}
}
void KillActor(DSWActor* actor)
{
KillSprite(actor->GetSpriteIndex());
}
void KillSprite(int16_t SpriteNum)
{
auto actor = &swActors[SpriteNum];
SPRITEp sp = &actor->s();
USERp u = actor->u();
int i;
unsigned stat;
short statnum,sectnum;
//extern short Zombies;
ASSERT(!Prediction);
ASSERT(sp->statnum < MAXSTATUS);
//////////////////////////////////////////////
// Check sounds list to kill attached sounds
DeleteNoSoundOwner(actor);
DeleteNoFollowSoundOwner(SpriteNum);
//////////////////////////////////////////////
if (u)
{
PLAYERp pp;
short pnum;
// doing a MissileSetPos - don't allow killing
if (TEST(u->Flags, SPR_SET_POS_DONT_KILL))
return;
// for attached sprites that are getable make sure they don't have
// any Anims attached
AnimDelete(ANIM_Userz, 0, actor);
AnimDelete(ANIM_Spritez, 0, actor);
StopInterpolation(SpriteNum, Interp_Sprite_Z);
//if (TEST(u->Flags2, SPR2_DONT_TARGET_OWNER))
// Zombies--;
// adjust sprites attached to sector objects
if (TEST(u->Flags, SPR_SO_ATTACHED))
{
SECTOR_OBJECTp sop;
short sn, FoundSpriteNdx = -1;
for (sop = SectorObject; sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++)
{
for (sn = 0; sop->so_actors[sn] != nullptr; sn++)
{
if (sop->so_actors[sn] == actor)
{
FoundSpriteNdx = sn;
}
}
if (FoundSpriteNdx >= 0)
{
// back up sn so it points to the last valid sprite num
sn--;
ASSERT(sop->so_actors[sn] != nullptr);
so_stopspriteinterpolation(sop, actor);
// replace the one to be deleted with the last ndx
sop->so_actors[FoundSpriteNdx] = sop->so_actors[sn];
// the last ndx is not -1
sop->so_actors[sn] = nullptr;
break;
}
}
ASSERT(FoundSpriteNdx >= 0);
}
// if a player is dead and watching this sprite
// reset it.
TRAVERSE_CONNECT(pnum)
{
pp = Player + pnum;
if (pp->KillerActor != nullptr)
{
if (pp->KillerActor == actor)
{
pp->KillerActor = nullptr;;
}
}
}
// if on a track and died reset the track to non-occupied
if (sp->statnum == STAT_ENEMY)
{
if (u->track != -1)
{
if (Track[u->track].flags)
RESET(Track[u->track].flags, TF_TRACK_OCCUPIED);
}
}
// if missile is heading for the sprite, the missile need to know
// that it is already dead
if (TEST(sp->extra, SPRX_PLAYER_OR_ENEMY))
{
USERp mu;
static short MissileStats[] = {STAT_MISSILE, STAT_MISSILE_SKIP4};
for (stat = 0; stat < SIZ(MissileStats); stat++)
{
SWStatIterator it(MissileStats[stat]);
while (auto actor = it.Next())
{
if (!actor->hasU()) continue;
mu = actor->u();
if (mu->WpnGoalActor == actor)
{
mu->WpnGoalActor = nullptr;
}
}
}
}
// much faster
if (TEST(u->Flags2, SPR2_CHILDREN))
//if (TEST(sp->extra, SPRX_CHILDREN))
{
// check for children and alert them that the Owner is dead
// don't bother th check if you've never had children
for (stat = 0; stat < STAT_DONT_DRAW; stat++)
{
SWStatIterator it(stat);
while (auto itActor = it.Next())
{
if (GetOwner(itActor) == actor)
{
ClearOwner(itActor);
}
if (itActor->hasU() && itActor->u()->attachActor == actor)
{
itActor->u()->attachActor = nullptr;
}
}
}
}
if (sp->statnum == STAT_ENEMY)
{
SWStatIterator it(STAT_ENEMY);
while (auto itActor = it.Next())
{
if (itActor->hasU() && itActor->u()->targetActor == actor)
{
DoActorPickClosePlayer(itActor);
}
}
}
if (u->flameActor != nullptr)
{
SetSuicide(u->flameActor);
}
User[SpriteNum].Clear();
}
FVector3 pos = GetSoundPos(&actor->s().pos);
soundEngine->RelinkSound(SOURCE_Actor, &actor->s(), nullptr, &pos);
deletesprite(actor->GetSpriteIndex());
// shred your garbage - but not statnum
statnum = sp->statnum;
sectnum = sp->sectnum;
memset(sp, 0xCC, sizeof(SPRITE));
sp->statnum = statnum;
sp->sectnum = sectnum;
// Kill references in all users - slow but unavoidable if we don't want the game to crash on stale pointers.
for (auto& u : User)
{
if (u.Data())
{
if (u->highActor == actor) u->highActor = nullptr;
if (u->lowActor == actor) u->lowActor = nullptr;
if (u->targetActor == actor) u->targetActor = nullptr;
}
}
}
void ChangeState(DSWActor* actor, STATEp statep)
{
if (!actor->hasU())
return;
USERp u = actor->u();
u->Tics = 0;
u->State = u->StateStart = statep;
// Just in case
PicAnimOff(u->State->Pic);
}
void change_actor_stat(DSWActor* actor, int stat, bool quick)
{
changespritestat(actor->GetSpriteIndex(), stat);
if (actor->hasU() && !quick)
{
USERp u = actor->u();
RESET(u->Flags, SPR_SKIP2|SPR_SKIP4);
if (stat >= STAT_SKIP4_START && stat <= STAT_SKIP4_END)
SET(u->Flags, SPR_SKIP4);
if (stat >= STAT_SKIP2_START && stat <= STAT_SKIP2_END)
SET(u->Flags, SPR_SKIP2);
switch (stat)
{
//case STAT_DEAD_ACTOR:
//case STAT_ITEM:
//case STAT_SKIP4:
#if 0
case STAT_PLAYER0:
case STAT_PLAYER1:
case STAT_PLAYER2:
case STAT_PLAYER3:
case STAT_PLAYER4:
case STAT_PLAYER5:
case STAT_PLAYER6:
case STAT_PLAYER7:
#endif
case STAT_ENEMY_SKIP4:
case STAT_ENEMY:
// for enemys - create offsets so all enemys don't call FAFcansee at once
wait_active_check_offset += ACTORMOVETICS*3;
if (wait_active_check_offset > ACTIVE_CHECK_TIME)
wait_active_check_offset = 0;
u->wait_active_check = wait_active_check_offset;
// don't do a break here
SET(u->Flags, SPR_SHADOW);
break;
}
}
}
USERp SpawnUser(DSWActor* actor, short id, STATEp state)
{
SPRITEp sp = &actor->s();
USERp u;
ASSERT(!Prediction);
User[actor->GetSpriteIndex()].Clear(); // make sure to delete old, stale content first!
User[actor->GetSpriteIndex()].Alloc();
u = actor->u();
PRODUCTION_ASSERT(u != nullptr);
// be careful State can be nullptr
u->State = u->StateStart = state;
change_actor_stat(actor, sp->statnum);
u->ID = id;
u->Health = 100;
u->WpnGoalActor = nullptr;
u->attachActor = nullptr;
u->track = -1;
u->targetActor = Player[0].Actor();
u->Radius = 220;
u->Sibling = -1;
u->SpriteP = &actor->s();
u->SpriteNum = actor->GetSpriteIndex();
u->WaitTics = 0;
u->OverlapZ = Z(4);
u->bounce = 0;
u->motion_blur_num = 0;
u->motion_blur_dist = 256;
sp->backuppos();
u->oz = sp->oz;
u->active_range = MIN_ACTIVE_RANGE;
// default
// based on clipmove z of 48 pixels off the floor
u->floor_dist = Z(48) - Z(28);
u->ceiling_dist = Z(8);
// Problem with sprites spawned really close to white sector walls
// cant do a getzrange there
// Just put in some valid starting values
u->loz = sector[sp->sectnum].floorz;
u->hiz = sector[sp->sectnum].ceilingz;
u->lowActor = nullptr;
u->highActor = nullptr;
u->lo_sectp = &sector[sp->sectnum];
u->hi_sectp = &sector[sp->sectnum];
return u;
}
SECT_USERp GetSectUser(short sectnum)
{
SECT_USERp sectu;
if (SectUser[sectnum].Data())
return SectUser[sectnum].Data();
SectUser[sectnum].Alloc();
sectu = SectUser[sectnum].Data();
ASSERT(sectu != nullptr);
return sectu;
}
int16_t SpawnSprite(short stat, short id, STATEp state, short sectnum, int x, int y, int z, int init_ang, int vel)
{
SPRITEp sp;
USERp u;
if (sectnum < 0)
return -1;
ASSERT(!Prediction);
auto spawnedActor = InsertActor(sectnum, stat);
sp = &spawnedActor->s();
sp->pal = 0;
sp->x = x;
sp->y = y;
sp->z = z;
sp->cstat = 0;
u = SpawnUser(spawnedActor, id, state);
// be careful State can be nullptr
if (u->State)
{
sp->picnum = u->State->Pic;
PicAnimOff(sp->picnum);
}
sp->shade = 0;
sp->xrepeat = 64;
sp->yrepeat = 64;
sp->ang = NORM_ANGLE(init_ang);
sp->xvel = vel;
sp->zvel = 0;
sp->lotag = 0L;
sp->hitag = 0L;
sp->extra = 0;
sp->xoffset = 0;
sp->yoffset = 0;
sp->clipdist = 0;
return spawnedActor->GetSpriteIndex();
}
DSWActor* SpawnActor(short stat, short id, STATEp state, short sectnum, int x, int y, int z, int ang, int vel)
{
int s = SpawnSprite(stat, id, state, sectnum, x, y, z, ang, vel);
return s >= 0 ? &swActors[s] : nullptr;
}
void PicAnimOff(short picnum)
{
short anim_type = TEST(picanm[picnum].sf, PICANM_ANIMTYPE_MASK) >> PICANM_ANIMTYPE_SHIFT;
ASSERT(picnum >= 0 && picnum < MAXTILES);
if (!anim_type)
return;
RESET(picanm[picnum].sf, PICANM_ANIMTYPE_MASK);
}
bool IconSpawn(SPRITEp sp)
{
// if multi item and not a modem game
if (TEST(sp->extra, SPRX_MULTI_ITEM))
{
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
return false;
}
sp->cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN | CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_ALIGNMENT_FLOOR);
return true;
}
bool ActorTestSpawn(DSWActor* actor)
{
auto sp = &actor->s();
if (sp->statnum == STAT_DEFAULT && sp->lotag == TAG_SPAWN_ACTOR)
{
auto actorNew = InsertActor(sp->sectnum, STAT_DEFAULT);
int t = actorNew->s().time; // must be preserved!
actorNew->s() = *sp;
actorNew->s().time = t;
change_actor_stat(actorNew, STAT_SPAWN_TRIGGER);
RESET(actorNew->s().cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
return false;
}
// Countermeasure for mods that leave the lower skills unpopulated.
int spawnskill = TEST(sp->extra, SPRX_SKILL);
if (MinEnemySkill > 0 && spawnskill == MinEnemySkill) spawnskill = 0;
// Skill ranges from -1 (No Monsters) to 3.
if (spawnskill > Skill)
{
// JBF: hack to fix Wanton Destruction's missing Sumos, Serpents, and Zilla on Skill < 2
if (((sp->picnum == SERP_RUN_R0 || sp->picnum == SUMO_RUN_R0) && sp->lotag > 0 &&
sp->lotag != TAG_SPAWN_ACTOR && sp->extra > 0) || sp->picnum == ZILLA_RUN_R0)
{
const char *c;
// NOTE: Wanton's $boat.map has two sumos, neither of which actually activate
// anything but are spawned in, and one of them has a skill level 2 mask. This
// hack however forces both sumos to appear on the easy levels. Bummer.
switch (sp->picnum)
{
case SERP_RUN_R0: c = "serpent"; break;
case SUMO_RUN_R0: c = "sumo"; break;
case ZILLA_RUN_R0: c = "zilla"; break;
default: c = "?"; break;
}
Printf("WARNING: skill-masked %s at %d,%d,%d not being killed because it "
"activates something\n", c, sp->x, sp->y, sp->z);
return true;
}
//always spawn girls in addons
if ((sp->picnum == TOILETGIRL_R0 ||
sp->picnum == WASHGIRL_R0 ||
sp->picnum == MECHANICGIRL_R0 ||
sp->picnum == CARGIRL_R0 ||
sp->picnum == PRUNEGIRL_R0 ||
sp->picnum == SAILORGIRL_R0) && (g_gameType & GAMEFLAG_ADDON)) return true;
// spawn Bouncing Betty (mine) in TD map 09 Warehouse
if (sp->picnum == 817 && (currentLevel->flags & LEVEL_SW_SPAWNMINES))
return true;
return false;
}
return true;
}
int EnemyCheckSkill()
{
SWStatIterator it(STAT_DEFAULT);
int maxskill = INT_MAX;
while (auto actor = it.Next())
{
auto sp = &actor->s();
switch (sp->picnum)
{
case COOLIE_RUN_R0:
case NINJA_RUN_R0:
case NINJA_CRAWL_R0:
case GORO_RUN_R0:
case 1441:
case COOLG_RUN_R0:
case EEL_RUN_R0:
case SUMO_RUN_R0:
case ZILLA_RUN_R0:
case RIPPER_RUN_R0:
case RIPPER2_RUN_R0:
case SERP_RUN_R0:
case LAVA_RUN_R0:
case SKEL_RUN_R0:
case HORNET_RUN_R0:
case SKULL_R0:
case BETTY_R0:
case GIRLNINJA_RUN_R0:
{
int myskill = sp->extra & SPRX_SKILL;
if (myskill < maxskill) maxskill = myskill;
}
}
}
if (maxskill < 0 || maxskill == INT_MAX) maxskill = 0;
return maxskill;
}
bool ActorSpawn(DSWActor* actor)
{
bool ret = true;
int picnum = actor->s().picnum;
switch (picnum)
{
case COOLIE_RUN_R0:
{
//PreCacheCoolie();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupCoolie(actor);
break;
}
case NINJA_RUN_R0:
case NINJA_CRAWL_R0:
{
//PreCacheNinja();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupNinja(actor);
break;
}
case GORO_RUN_R0:
{
//PreCacheGuardian();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupGoro(actor);
break;
}
case 1441:
case COOLG_RUN_R0:
{
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupCoolg(actor);
break;
}
case EEL_RUN_R0:
{
//PreCacheEel();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupEel(actor);
break;
}
case SUMO_RUN_R0:
{
//PreCacheSumo();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupSumo(actor);
break;
}
case ZILLA_RUN_R0:
{
//PreCacheSumo();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupZilla(actor);
break;
}
case TOILETGIRL_R0:
{
//PreCacheToiletGirl();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupToiletGirl(actor);
break;
}
case WASHGIRL_R0:
{
//PreCacheWashGirl();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupWashGirl(actor);
break;
}
case CARGIRL_R0:
{
//PreCacheCarGirl();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupCarGirl(actor);
break;
}
case MECHANICGIRL_R0:
{
//PreCacheMechanicGirl();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupMechanicGirl(actor);
break;
}
case SAILORGIRL_R0:
{
//PreCacheSailorGirl();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupSailorGirl(actor);
break;
}
case PRUNEGIRL_R0:
{
//PreCachePruneGirl();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupPruneGirl(actor);
break;
}
case TRASHCAN:
{
//PreCacheTrash();
PicAnimOff(picnum);
SetupTrashCan(actor);
break;
}
case BUNNY_RUN_R0:
{
//PreCacheBunny();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupBunny(actor);
break;
}
case RIPPER_RUN_R0:
{
//PreCacheRipper();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupRipper(actor);
break;
}
case RIPPER2_RUN_R0:
{
//PreCacheRipper2();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupRipper2(actor);
break;
}
case SERP_RUN_R0:
{
//PreCacheSerpent();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupSerp(actor);
break;
}
case LAVA_RUN_R0:
{
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupLava(actor);
break;
}
case SKEL_RUN_R0:
{
//PreCacheSkel();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupSkel(actor);
break;
}
case HORNET_RUN_R0:
{
//PreCacheHornet();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupHornet(actor);
break;
}
case SKULL_R0:
{
//PreCacheSkull();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupSkull(actor);
break;
}
case BETTY_R0:
{
//PreCacheBetty();
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupBetty(actor);
break;
}
case 623: // Pachinko win light
{
//PreCachePachinko();
PicAnimOff(picnum);
SetupPachinkoLight(actor);
break;
}
case PACHINKO1:
{
//PreCachePachinko();
PicAnimOff(picnum);
SetupPachinko1(actor);
break;
}
case PACHINKO2:
{
//PreCachePachinko();
PicAnimOff(picnum);
SetupPachinko2(actor);
break;
}
case PACHINKO3:
{
//PreCachePachinko();
PicAnimOff(picnum);
SetupPachinko3(actor);
break;
}
case PACHINKO4:
{
//PreCachePachinko();
PicAnimOff(picnum);
SetupPachinko4(actor);
break;
}
case GIRLNINJA_RUN_R0:
{
if (!ActorTestSpawn(actor))
{
KillActor(actor);
return false;
}
PicAnimOff(picnum);
SetupGirlNinja(actor);
break;
}
default:
ret = false;
break;
}
return ret;
}
void IconDefault(DSWActor* actor)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
//if (sp->statnum == STAT_ITEM)
change_actor_stat(actor, STAT_ITEM);
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
u->Radius = 650;
DoActorZrange(actor);
}
void PreMapCombineFloors(void)
{
const int MAX_FLOORS = 32;
SPRITEp sp;
int i, j, k;
int base_offset;
int dx, dy;
int dasect, startwall, endwall, nextsector;
short pnum;
typedef struct
{
SPRITEp offset;
} BOUND_LIST;
BOUND_LIST BoundList[MAX_FLOORS];
memset(BoundList, 0, MAX_FLOORS * sizeof(BOUND_LIST));
SWStatIterator it(0);
while (auto actor = it.Next())
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
if (sp->picnum != ST1)
continue;
if (sp->hitag == BOUND_FLOOR_OFFSET || sp->hitag == BOUND_FLOOR_BASE_OFFSET)
{
ASSERT(sp->lotag < MAX_FLOORS);
BoundList[sp->lotag].offset = sp;
change_actor_stat(actor, STAT_FAF);
}
}
for (i = base_offset = 0; i < MAX_FLOORS; i++)
{
// blank so continue
if (!BoundList[i].offset)
continue;
if (BoundList[i].offset->hitag == BOUND_FLOOR_BASE_OFFSET)
{
base_offset = i;
continue;
}
dx = BoundList[base_offset].offset->x - BoundList[i].offset->x;
dy = BoundList[base_offset].offset->y - BoundList[i].offset->y;
BFSSearch search(numsectors, BoundList[i].offset->sectnum);
for (unsigned dasect; (dasect = search.GetNext()) != BFSSearch::EOL;)
{
SWSectIterator it(dasect);
while (auto jActor = it.Next())
{
jActor->s().x += dx;
jActor->s().y += dy;
}
for (auto& wal : wallsofsector(dasect))
{
wal.x += dx;
wal.y += dy;
nextsector = wal.nextsector;
if (nextsector >= 0)
search.Add(nextsector);
}
}
TRAVERSE_CONNECT(pnum)
{
PLAYERp pp = &Player[pnum];
dasect = pp->cursectnum;
search.Rewind();
for (unsigned itsect; (itsect = search.GetNext()) != BFSSearch::EOL;)
{
if (itsect == dasect)
{
pp->posx += dx;
pp->posy += dy;
pp->oposx = pp->oldposx = pp->posx;
pp->oposy = pp->oldposy = pp->posy;
break;
}
}
}
}
// get rid of the sprites used
it.Reset(STAT_FAF);
while (auto actor = it.Next())
{
KillActor(actor);
}
}
#if 0
// example of way to traverse through sectors from closest to farthest
void TraverseSectors(short start_sect)
{
int i, j, k;
short sectlist[M AXSECTORS];
short sectlistplc, sectlistend, sect, startwall, endwall, nextsector;
sectlist[0] = start_sect;
sectlistplc = 0; sectlistend = 1;
while (sectlistplc < sectlistend)
{
sect = sectlist[sectlistplc++];
startwall = sector[sect].wallptr;
endwall = startwall + sector[sect].wallnum;
for (j=startwall; j<endwall; j++)
{
nextsector = wall[j].nextsector;
if (nextsector < 0)
continue;
// make sure its not on the list
for (k = sectlistend-1; k >= 0; k--)
{
if (sectlist[k] == nextsector)
break;
}
// if its not on the list add it to the end
if (k < 0)
sectlist[sectlistend++] = nextsector;
}
}
// list is finished - can now traverse it
#if 0
sect = pp->cursectnum;
for (j=0; j<sectlistend; j++)
{
if (sectlist[j] == sect)
{
break;
}
}
#endif
}
#endif
void SpriteSetupPost(void)
{
SPRITEp ds;
USERp u;
int cz,fz;
// Post processing of some sprites after gone through the main SpriteSetup()
// routine
SWStatIterator it(STAT_FLOOR_PAN);
while (auto iActor = it.Next())
{
SWSectIterator it2(iActor->s().sectnum);
while (auto jActor = it.Next())
{
ds = &jActor->s();
if (ds->picnum == ST1)
continue;
if (TEST(ds->cstat, CSTAT_SPRITE_ALIGNMENT_WALL|CSTAT_SPRITE_ALIGNMENT_FLOOR))
continue;
if (jActor->hasU())
continue;
getzsofslope(ds->sectnum, ds->x, ds->y, &cz, &fz);
if (labs(ds->z - fz) > Z(4))
continue;
u = SpawnUser(jActor, 0, nullptr);
change_actor_stat(jActor, STAT_NO_STATE);
u->ceiling_dist = Z(4);
u->floor_dist = -Z(2);
u->ActorActionFunc = DoActorDebris;
SET(ds->cstat, CSTAT_SPRITE_BREAKABLE);
SET(ds->extra, SPRX_BREAKABLE);
}
}
}
void SpriteSetup(void)
{
short num;
int cz,fz;
MinEnemySkill = EnemyCheckSkill();
// special case for player
PicAnimOff(PLAYER_NINJA_RUN_R0);
// Clear Sprite Extension structure
// Clear all extra bits - they are set by sprites
for (int i = 0; i < numsectors; i++)
{
SectUser[i].Clear();
sector[i].extra = 0;
}
// Clear PARALLAX_LEVEL overrides
parallaxyscale_override = 0;
pskybits_override = -1;
// Call my little sprite setup routine first
JS_SpriteSetup();
int minEnemySkill = EnemyCheckSkill();
SWStatIterator it(STAT_DEFAULT);
while (auto actor = it.Next())
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
// not used yetv
getzsofslope(sp->sectnum, sp->x, sp->y, &cz, &fz);
if (sp->z > DIV2(cz + fz))
{
// closer to a floor
SET(sp->cstat, CSTAT_SPRITE_CLOSE_FLOOR);
}
// CSTAT_SPIN is insupported - get rid of it
if (TEST(sp->cstat, CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB)
RESET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_SLAB);
// if BLOCK is set set BLOCK_HITSCAN
// Hope this doesn't screw up anything
if (TEST(sp->cstat, CSTAT_SPRITE_BLOCK))
SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
////////////////////////////////////////////
//
// BREAKABLE CHECK
//
////////////////////////////////////////////
// USER SETUP - TAGGED BY USER
// Non ST1 sprites that are tagged like them
if (TEST_BOOL1(sp) && sp->picnum != ST1)
{
RESET(sp->extra,
SPRX_BOOL4|
SPRX_BOOL5|
SPRX_BOOL6|
SPRX_BOOL7|
SPRX_BOOL8|
SPRX_BOOL9|
SPRX_BOOL10);
switch (sp->hitag)
{
case BREAKABLE:
// need something that tells missiles to hit them
// but allows actors to move through them
sp->clipdist = SPRITEp_SIZE_X(sp);
SET(sp->extra, SPRX_BREAKABLE);
SET(sp->cstat, CSTAT_SPRITE_BREAKABLE);
break;
}
}
else
{
// BREAK SETUP TABLE AUTOMATED
SetupSpriteForBreak(actor);
}
if (sp->lotag == TAG_SPRITE_HIT_MATCH)
{
// if multi item and not a modem game
if (TEST(sp->extra, SPRX_MULTI_ITEM))
{
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
{
KillActor(actor);
continue;
}
}
// crack sprite
if (sp->picnum == 80)
{
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE);;
}
else
{
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE);;
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);;
}
if (TEST(SP_TAG8(sp), BIT(0)))
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE); ;
if (TEST(SP_TAG8(sp), BIT(1)))
RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
change_actor_stat(actor, STAT_SPRITE_HIT_MATCH);
continue;
}
if (sp->picnum >= TRACK_SPRITE &&
sp->picnum <= TRACK_SPRITE + MAX_TRACKS)
{
short track_num;
// skip this sprite, just for numbering walls/sectors
if (TEST(sp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL))
continue;
track_num = sp->picnum - TRACK_SPRITE + 0;
change_actor_stat(actor, STAT_TRACK + track_num);
continue;
}
if (ActorSpawn(actor))
continue;
switch (sp->picnum)
{
case ST_QUICK_JUMP:
change_actor_stat(actor, STAT_QUICK_JUMP);
break;
case ST_QUICK_JUMP_DOWN:
change_actor_stat(actor, STAT_QUICK_JUMP_DOWN);
break;
case ST_QUICK_SUPER_JUMP:
change_actor_stat(actor, STAT_QUICK_SUPER_JUMP);
break;
case ST_QUICK_SCAN:
change_actor_stat(actor, STAT_QUICK_SCAN);
break;
case ST_QUICK_EXIT:
change_actor_stat(actor, STAT_QUICK_EXIT);
break;
case ST_QUICK_OPERATE:
change_actor_stat(actor, STAT_QUICK_OPERATE);
break;
case ST_QUICK_DUCK:
change_actor_stat(actor, STAT_QUICK_DUCK);
break;
case ST_QUICK_DEFEND:
change_actor_stat(actor, STAT_QUICK_DEFEND);
break;
case ST1:
{
SECT_USERp sectu;
short tag;
short bit;
// get rid of defaults
if (SP_TAG3(sp) == 32)
SP_TAG3(sp) = 0;
tag = sp->hitag;
RESET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
// for bounding sector objects
if ((tag >= 500 && tag < 600) || tag == SECT_SO_CENTER)
{
// NOTE: These will get deleted by the sector object
// setup code
change_actor_stat(actor, STAT_ST1);
break;
}
if (tag < 16)
{
bit = 1 << (tag);
SET(sector[sp->sectnum].extra, bit);
if (TEST(bit, SECTFX_SINK))
{
sectu = GetSectUser(sp->sectnum);
sectu->depth_fixed = IntToFixed(sp->lotag);
KillActor(actor);
}
else if (TEST(bit, SECTFX_OPERATIONAL))
{
KillActor(actor);
}
else if (TEST(bit, SECTFX_CURRENT))
{
sectu = GetSectUser(sp->sectnum);
sectu->speed = sp->lotag;
sectu->ang = sp->ang;
KillActor(actor);
}
else if (TEST(bit, SECTFX_NO_RIDE))
{
change_actor_stat(actor, STAT_NO_RIDE);
}
else if (TEST(bit, SECTFX_DIVE_AREA))
{
sectu = GetSectUser(sp->sectnum);
sectu->number = sp->lotag;
change_actor_stat(actor, STAT_DIVE_AREA);
}
else if (TEST(bit, SECTFX_UNDERWATER))
{
sectu = GetSectUser(sp->sectnum);
sectu->number = sp->lotag;
change_actor_stat(actor, STAT_UNDERWATER);
}
else if (TEST(bit, SECTFX_UNDERWATER2))
{
sectu = GetSectUser(sp->sectnum);
sectu->number = sp->lotag;
if (sp->clipdist == 1)
SET(sectu->flags, SECTFU_CANT_SURFACE);
change_actor_stat(actor, STAT_UNDERWATER2);
}
}
else
{
switch (tag)
{
#if 0
case MULTI_PLAYER_START:
change_actor_stat(actor, STAT_MULTI_START + sp->lotag);
break;
case MULTI_COOPERATIVE_START:
change_actor_stat(actor, STAT_CO_OP_START + sp->lotag);
break;
#endif
case SECT_MATCH:
sectu = GetSectUser(sp->sectnum);
sectu->number = sp->lotag;
KillActor(actor);
break;
case SLIDE_SECTOR:
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_SLIDE_SECTOR);
sectu->speed = SP_TAG2(sp);
KillActor(actor);
break;
case SECT_DAMAGE:
{
sectu = GetSectUser(sp->sectnum);
if (TEST_BOOL1(sp))
SET(sectu->flags, SECTFU_DAMAGE_ABOVE_SECTOR);
sectu->damage = sp->lotag;
KillActor(actor);
break;
}
case PARALLAX_LEVEL:
{
parallaxyscale_override = 8192;
pskybits_override = sp->lotag;
if (SP_TAG4(sp) > 2048)
parallaxyscale_override = SP_TAG4(sp);
KillActor(actor);
break;
}
case BREAKABLE:
// used for wall info
change_actor_stat(actor, STAT_BREAKABLE);
break;
case SECT_DONT_COPY_PALETTE:
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_DONT_COPY_PALETTE);
KillActor(actor);
break;
}
case SECT_FLOOR_PAN:
{
// if moves with SO
if (TEST_BOOL1(sp))
sp->xvel = 0;
else
sp->xvel = sp->lotag;
StartInterpolation(sp->sectnum, Interp_Sect_FloorPanX);
StartInterpolation(sp->sectnum, Interp_Sect_FloorPanY);
change_actor_stat(actor, STAT_FLOOR_PAN);
break;
}
case SECT_CEILING_PAN:
{
// if moves with SO
if (TEST_BOOL1(sp))
sp->xvel = 0;
else
sp->xvel = sp->lotag;
StartInterpolation(sp->sectnum, Interp_Sect_CeilingPanX);
StartInterpolation(sp->sectnum, Interp_Sect_CeilingPanY);
change_actor_stat(actor, STAT_CEILING_PAN);
break;
}
case SECT_WALL_PAN_SPEED:
{
vec3_t hit_pos = { sp->x, sp->y, sp->z - Z(8) };
hitdata_t hitinfo;
hitscan(&hit_pos, sp->sectnum, // Start position
bcos(sp->ang), // X vector of 3D ang
bsin(sp->ang), // Y vector of 3D ang
0, // Z vector of 3D ang
&hitinfo, CLIPMASK_MISSILE);
if (hitinfo.wall == -1)
{
KillActor(actor);
break;
}
sp->owner = hitinfo.wall;
// if moves with SO
if (TEST_BOOL1(sp))
sp->xvel = 0;
else
sp->xvel = sp->lotag;
sp->ang = SP_TAG6(sp);
// attach to the sector that contains the wall
ChangeActorSect(actor, hitinfo.sect);
StartInterpolation(hitinfo.wall, Interp_Wall_PanX);
StartInterpolation(hitinfo.wall, Interp_Wall_PanY);
change_actor_stat(actor, STAT_WALL_PAN);
break;
}
case WALL_DONT_STICK:
{
vec3_t hit_pos = { sp->x, sp->y, sp->z - Z(8) };
hitdata_t hitinfo;
hitscan(&hit_pos, sp->sectnum, // Start position
bcos(sp->ang), // X vector of 3D ang
bcos(sp->ang), // Y vector of 3D ang
0, // Z vector of 3D ang
&hitinfo, CLIPMASK_MISSILE);
if (hitinfo.wall == -1)
{
KillActor(actor);
break;
}
SET(wall[hitinfo.wall].extra, WALLFX_DONT_STICK);
KillActor(actor);
break;
}
case TRIGGER_SECTOR:
{
SET(sector[sp->sectnum].extra, SECTFX_TRIGGER);
change_actor_stat(actor, STAT_TRIGGER);
break;
}
case DELETE_SPRITE:
{
change_actor_stat(actor, STAT_DELETE_SPRITE);
break;
}
case SPAWN_ITEMS:
{
if (TEST(sp->extra, SPRX_MULTI_ITEM))
{
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
{
KillActor(actor);
break;
}
}
change_actor_stat(actor, STAT_SPAWN_ITEMS);
break;
}
case CEILING_FLOOR_PIC_OVERRIDE:
{
// block hitscans depending on translucency
if (SP_TAG7(sp) == 0 || SP_TAG7(sp) == 1)
{
if (SP_TAG3(sp) == 0)
SET(sector[sp->sectnum].ceilingstat, CEILING_STAT_FAF_BLOCK_HITSCAN);
else
SET(sector[sp->sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN);
}
else if (TEST_BOOL1(sp))
{
if (SP_TAG3(sp) == 0)
SET(sector[sp->sectnum].ceilingstat, CEILING_STAT_FAF_BLOCK_HITSCAN);
else
SET(sector[sp->sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN);
}
// copy tag 7 to tag 6 and pre-shift it
SP_TAG6(sp) = SP_TAG7(sp);
SP_TAG6(sp) <<= 7;
change_actor_stat(actor, STAT_CEILING_FLOOR_PIC_OVERRIDE);
break;
}
case QUAKE_SPOT:
{
change_actor_stat(actor, STAT_QUAKE_SPOT);
//SP_TAG13(sp) = (SP_TAG6(sp)*10L) * 120L;
SET_SP_TAG13(sp, ((SP_TAG6(sp)*10L) * 120L));
break;
}
case SECT_CHANGOR:
{
change_actor_stat(actor, STAT_CHANGOR);
break;
}
#if 0
case SECT_DEBRIS_SEWER:
{
ANIMATOR DoGenerateSewerDebris;
u = SpawnUser(actor, 0, nullptr);
ASSERT(u != nullptr);
u->RotNum = 0;
u->WaitTics = sp->lotag * 120;
u->ActorActionFunc = DoGenerateSewerDebris;
change_actor_stat(actor, STAT_NO_STATE);
break;
}
#endif
case SECT_VATOR:
{
SECTORp sectp = &sector[sp->sectnum];
SECT_USERp sectu;
short speed,vel,time,type,start_on,floor_vator;
u = SpawnUser(actor, 0, nullptr);
// vator already set - ceiling AND floor vator
if (TEST(sectp->extra, SECTFX_VATOR))
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_VATOR_BOTH);
}
SET(sectp->extra, SECTFX_VATOR);
SetSectorWallBits(sp->sectnum, WALLFX_DONT_STICK, true, true);
SET(sector[sp->sectnum].extra, SECTFX_DYNAMIC_AREA);
// don't step on toes of other sector settings
if (sectp->lotag == 0 && sectp->hitag == 0)
sectp->lotag = TAG_VATOR;
type = SP_TAG3(sp);
speed = SP_TAG4(sp);
vel = SP_TAG5(sp);
time = SP_TAG9(sp);
start_on = !!TEST_BOOL1(sp);
floor_vator = true;
if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
floor_vator = false;
u->jump_speed = u->vel_tgt = speed;
u->vel_rate = vel;
u->WaitTics = time*15; // 1/8 of a sec
u->Tics = 0;
SET(u->Flags, SPR_ACTIVE);
switch (type)
{
case 0:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoVator;
break;
case 1:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoVator;
break;
case 2:
u->ActorActionFunc = DoVatorAuto;
break;
case 3:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoVatorAuto;
break;
}
if (floor_vator)
{
// start off
u->sz = sectp->floorz;
u->z_tgt = sp->z;
if (start_on)
{
int amt;
amt = sp->z - sectp->floorz;
// start in the on position
//sectp->floorz = sp->z;
sectp->floorz += amt;
u->z_tgt = u->sz;
MoveSpritesWithSector(sp->sectnum, amt, false); // floor
}
// set orig z
u->oz = sp->oz = sectp->floorz;
}
else
{
// start off
u->sz = sectp->ceilingz;
u->z_tgt = sp->z;
if (start_on)
{
int amt;
amt = sp->z - sectp->ceilingz;
// starting in the on position
//sectp->ceilingz = sp->z;
sectp->ceilingz += amt;
u->z_tgt = u->sz;
MoveSpritesWithSector(sp->sectnum, amt, true); // ceiling
}
// set orig z
u->oz = sp->oz = sectp->ceilingz;
}
change_actor_stat(actor, STAT_VATOR);
break;
}
case SECT_ROTATOR_PIVOT:
{
change_actor_stat(actor, STAT_ROTATOR_PIVOT);
break;
}
case SECT_ROTATOR:
{
SECTORp sectp = &sector[sp->sectnum];
short time,type;
short wallcount,startwall,endwall,w;
u = SpawnUser(actor, 0, nullptr);
SetSectorWallBits(sp->sectnum, WALLFX_DONT_STICK, true, true);
// need something for this
sectp->lotag = TAG_ROTATOR;
sectp->hitag = sp->lotag;
type = SP_TAG3(sp);
time = SP_TAG9(sp);
u->WaitTics = time*15; // 1/8 of a sec
u->Tics = 0;
startwall = sector[sp->sectnum].wallptr;
endwall = startwall + sector[sp->sectnum].wallnum - 1;
// count walls of sector
for (w = startwall, wallcount = 0; w <= endwall; w++)
wallcount++;
u->rotator.Alloc();
u->rotator->open_dest = SP_TAG5(sp);
u->rotator->speed = SP_TAG7(sp);
u->rotator->vel = SP_TAG8(sp);
u->rotator->pos = 0; // closed
u->rotator->tgt = u->rotator->open_dest; // closed
u->rotator->SetNumWalls(wallcount);
u->rotator->orig_speed = u->rotator->speed;
for (w = startwall, wallcount = 0; w <= endwall; w++)
{
u->rotator->origX[wallcount] = wall[w].x;
u->rotator->origY[wallcount] = wall[w].y;
wallcount++;
}
SET(u->Flags, SPR_ACTIVE);
switch (type)
{
case 0:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoRotator;
break;
case 1:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoRotator;
break;
}
change_actor_stat(actor, STAT_ROTATOR);
break;
}
case SECT_SLIDOR:
{
SECTORp sectp = &sector[sp->sectnum];
short time,type;
u = SpawnUser(actor, 0, nullptr);
SetSectorWallBits(sp->sectnum, WALLFX_DONT_STICK, true, true);
// need something for this
sectp->lotag = TAG_SLIDOR;
sectp->hitag = sp->lotag;
type = SP_TAG3(sp);
time = SP_TAG9(sp);
u->WaitTics = time*15; // 1/8 of a sec
u->Tics = 0;
u->rotator.Alloc();
u->rotator->open_dest = SP_TAG5(sp);
u->rotator->speed = SP_TAG7(sp);
u->rotator->vel = SP_TAG8(sp);
u->rotator->pos = 0; // closed
u->rotator->tgt = u->rotator->open_dest; // closed
u->rotator->ClearWalls();
u->rotator->orig_speed = u->rotator->speed;
SET(u->Flags, SPR_ACTIVE);
switch (type)
{
case 0:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoSlidor;
break;
case 1:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoSlidor;
break;
}
if (TEST_BOOL5(sp))
{
DoSlidorInstantClose(actor);
}
change_actor_stat(actor, STAT_SLIDOR);
break;
}
case SECT_SPIKE:
{
short speed,vel,time,type,start_on,floor_vator;
int floorz,ceilingz,trash;
u = SpawnUser(actor, 0, nullptr);
SetSectorWallBits(sp->sectnum, WALLFX_DONT_STICK, false, true);
SET(sector[sp->sectnum].extra, SECTFX_DYNAMIC_AREA);
type = SP_TAG3(sp);
speed = SP_TAG4(sp);
vel = SP_TAG5(sp);
time = SP_TAG9(sp);
start_on = !!TEST_BOOL1(sp);
floor_vator = true;
if (TEST(sp->cstat, CSTAT_SPRITE_YFLIP))
floor_vator = false;
u->jump_speed = u->vel_tgt = speed;
u->vel_rate = vel;
u->WaitTics = time*15; // 1/8 of a sec
u->Tics = 0;
SET(u->Flags, SPR_ACTIVE);
switch (type)
{
case 0:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoSpike;
break;
case 1:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoSpike;
break;
case 2:
u->ActorActionFunc = DoSpikeAuto;
break;
case 3:
RESET(u->Flags, SPR_ACTIVE);
u->ActorActionFunc = DoSpikeAuto;
break;
}
getzrangepoint(sp->x, sp->y, sp->z, sp->sectnum, &ceilingz, &trash, &floorz, &trash);
if (floor_vator)
{
u->zclip = floorz;
// start off
u->sz = u->zclip;
u->z_tgt = sp->z;
if (start_on)
{
// start in the on position
u->zclip = sp->z;
u->z_tgt = u->sz;
SpikeAlign(actor);
}
// set orig z
u->oz = sp->oz = u->zclip;
}
else
{
u->zclip = ceilingz;
// start off
u->sz = u->zclip;
u->z_tgt = sp->z;
if (start_on)
{
// starting in the on position
u->zclip = sp->z;
u->z_tgt = u->sz;
SpikeAlign(actor);
}
// set orig z
u->oz = sp->oz = u->zclip;
}
change_actor_stat(actor, STAT_SPIKE);
break;
}
case LIGHTING:
{
short w, startwall, endwall;
short wallcount;
int8_t* wall_shade;
USERp u;
LIGHT_Tics(sp) = 0;
if (LIGHT_ShadeInc(sp) == 0)
LIGHT_ShadeInc(sp) = 1;
// save off original floor and ceil shades
LIGHT_FloorShade(sp) = sector[sp->sectnum].floorshade;
LIGHT_CeilingShade(sp) = sector[sp->sectnum].ceilingshade;
startwall = sector[sp->sectnum].wallptr;
endwall = startwall + sector[sp->sectnum].wallnum - 1;
// count walls of sector
for (w = startwall, wallcount = 0; w <= endwall; w++)
{
wallcount++;
if (TEST_BOOL5(sp))
{
if (wall[w].nextwall >= 0)
wallcount++;
}
}
u = SpawnUser(actor, 0, nullptr);
u->WallShade.Resize(wallcount);
wall_shade = u->WallShade.Data();
// save off original wall shades
for (w = startwall, wallcount = 0; w <= endwall; w++)
{
wall_shade[wallcount] = wall[w].shade;
wallcount++;
if (TEST_BOOL5(sp))
{
uint16_t const nextwall = wall[w].nextwall;
if (validWallIndex(nextwall))
{
wall_shade[wallcount] = wall[wall[w].nextwall].shade;
wallcount++;
}
}
}
u->spal = sp->pal;
// DON'T USE COVER function
change_actor_stat(actor, STAT_LIGHTING, true);
break;
}
case LIGHTING_DIFFUSE:
{
short w, startwall, endwall;
short wallcount;
int8_t* wall_shade;
USERp u;
LIGHT_Tics(sp) = 0;
// save off original floor and ceil shades
LIGHT_FloorShade(sp) = sector[sp->sectnum].floorshade;
LIGHT_CeilingShade(sp) = sector[sp->sectnum].ceilingshade;
startwall = sector[sp->sectnum].wallptr;
endwall = startwall + sector[sp->sectnum].wallnum - 1;
// count walls of sector
for (w = startwall, wallcount = 0; w <= endwall; w++)
{
wallcount++;
if (TEST_BOOL5(sp))
{
if (wall[w].nextwall >= 0)
wallcount++;
}
}
// !LIGHT
// make an wall_shade array and put it in User
u = SpawnUser(actor, 0, nullptr);
u->WallShade.Resize(wallcount);
wall_shade = u->WallShade.Data();
// save off original wall shades
for (w = startwall, wallcount = 0; w <= endwall; w++)
{
wall_shade[wallcount] = wall[w].shade;
wallcount++;
if (TEST_BOOL5(sp))
{
uint16_t const nextwall = wall[w].nextwall;
if (validWallIndex(nextwall))
{
wall_shade[wallcount] = wall[wall[w].nextwall].shade;
wallcount++;
}
}
}
// DON'T USE COVER function
change_actor_stat(actor, STAT_LIGHTING_DIFFUSE, true);
break;
}
case SECT_VATOR_DEST:
change_actor_stat(actor, STAT_VATOR);
break;
case SO_WALL_DONT_MOVE_UPPER:
change_actor_stat(actor, STAT_WALL_DONT_MOVE_UPPER);
break;
case SO_WALL_DONT_MOVE_LOWER:
change_actor_stat(actor, STAT_WALL_DONT_MOVE_LOWER);
break;
case FLOOR_SLOPE_DONT_DRAW:
change_actor_stat(actor, STAT_FLOOR_SLOPE_DONT_DRAW);
break;
case DEMO_CAMERA:
sp->yvel = sp->zvel = 100; //attempt horiz control
change_actor_stat(actor, STAT_DEMO_CAMERA);
break;
case LAVA_ERUPT:
{
u = SpawnUser(actor, ST1, nullptr);
change_actor_stat(actor, STAT_NO_STATE);
u->ActorActionFunc = DoLavaErupt;
// interval between erupts
if (SP_TAG10(sp) == 0)
SP_TAG10(sp) = 20;
// interval in seconds
u->WaitTics = RandomRange(SP_TAG10(sp)) * 120;
// time to erupt
if (SP_TAG9(sp) == 0)
SP_TAG9(sp) = 10;
sp->z += Z(30);
break;
}
case SECT_EXPLODING_CEIL_FLOOR:
{
SECTORp sectp = &sector[sp->sectnum];
SetSectorWallBits(sp->sectnum, WALLFX_DONT_STICK, false, true);
if (TEST(sectp->floorstat, FLOOR_STAT_SLOPE))
{
SP_TAG5(sp) = sectp->floorheinum;
RESET(sectp->floorstat, FLOOR_STAT_SLOPE);
sectp->floorheinum = 0;
}
if (TEST(sectp->ceilingstat, CEILING_STAT_SLOPE))
{
SP_TAG6(sp) = sectp->ceilingheinum;
RESET(sectp->ceilingstat, CEILING_STAT_SLOPE);
sectp->ceilingheinum = 0;
}
SP_TAG4(sp) = abs(sectp->ceilingz - sectp->floorz)>>8;
sectp->ceilingz = sectp->floorz;
change_actor_stat(actor, STAT_EXPLODING_CEIL_FLOOR);
break;
}
case SECT_COPY_SOURCE:
change_actor_stat(actor, STAT_COPY_SOURCE);
break;
case SECT_COPY_DEST:
{
SetSectorWallBits(sp->sectnum, WALLFX_DONT_STICK, false, true);
change_actor_stat(actor, STAT_COPY_DEST);
break;
}
case SECT_WALL_MOVE:
change_actor_stat(actor, STAT_WALL_MOVE);
break;
case SECT_WALL_MOVE_CANSEE:
change_actor_stat(actor, STAT_WALL_MOVE_CANSEE);
break;
case SPRI_CLIMB_MARKER:
{
// setup climb marker
change_actor_stat(actor, STAT_CLIMB_MARKER);
// make a QUICK_LADDER sprite automatically
auto ns = InsertActor(sp->sectnum, STAT_QUICK_LADDER);
auto np = &ns->s();
np->cstat = 0;
np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = sp->z;
np->ang = NORM_ANGLE(sp->ang + 1024);
np->picnum = sp->picnum;
np->x += MOVEx(256+128, sp->ang);
np->y += MOVEy(256+128, sp->ang);
break;
}
case SO_AUTO_TURRET:
#if 0
switch (gNet.MultiGameType)
{
case MULTI_GAME_NONE:
change_actor_stat(actor, STAT_ST1);
break;
case MULTI_GAME_COMMBAT:
KillActor(actor);
break;
case MULTI_GAME_COOPERATIVE:
change_actor_stat(actor, STAT_ST1);
break;
}
#else
change_actor_stat(actor, STAT_ST1);
#endif
break;
case SO_DRIVABLE_ATTRIB:
case SO_SCALE_XY_MULT:
case SO_SCALE_INFO:
case SO_SCALE_POINT_INFO:
case SO_TORNADO:
case SO_FLOOR_MORPH:
case SO_AMOEBA:
case SO_SET_SPEED:
case SO_ANGLE:
case SO_SPIN:
case SO_SPIN_REVERSE:
case SO_BOB_START:
case SO_BOB_SPEED:
case SO_TURN_SPEED:
case SO_SYNC1:
case SO_SYNC2:
case SO_LIMIT_TURN:
case SO_MATCH_EVENT:
case SO_MAX_DAMAGE:
case SO_RAM_DAMAGE:
case SO_SLIDE:
case SO_KILLABLE:
case SECT_SO_SPRITE_OBJ:
case SECT_SO_DONT_ROTATE:
case SECT_SO_CLIP_DIST:
{
// NOTE: These will get deleted by the sector
// object
// setup code
change_actor_stat(actor, STAT_ST1);
break;
}
case SOUND_SPOT:
//SP_TAG13(sp) = SP_TAG4(sp);
SET_SP_TAG13(sp, SP_TAG4(sp));
change_actor_stat(actor, STAT_SOUND_SPOT);
break;
case STOP_SOUND_SPOT:
change_actor_stat(actor, STAT_STOP_SOUND_SPOT);
break;
case SPAWN_SPOT:
if (!actor->hasU())
u = SpawnUser(actor, ST1, nullptr);
if (SP_TAG14(sp) == ((64<<8)|64))
//SP_TAG14(sp) = 0;
SET_SP_TAG14(sp, 0);
change_actor_stat(actor, STAT_SPAWN_SPOT);
break;
case VIEW_THRU_CEILING:
case VIEW_THRU_FLOOR:
{
// make sure there is only one set per level of these
SWStatIterator it(STAT_FAF);
while (auto itActor = it.Next())
{
auto ispr = &itActor->s();
if (ispr->hitag == sp->hitag && ispr->lotag == sp->lotag)
{
I_Error("Two VIEW_THRU_ tags with same match found on level\n1: x %d, y %d \n2: x %d, y %d", sp->x, sp->y, ispr->x, ispr->y);
}
}
change_actor_stat(actor, STAT_FAF);
break;
}
case VIEW_LEVEL1:
case VIEW_LEVEL2:
case VIEW_LEVEL3:
case VIEW_LEVEL4:
case VIEW_LEVEL5:
case VIEW_LEVEL6:
{
change_actor_stat(actor, STAT_FAF);
break;
}
case PLAX_GLOB_Z_ADJUST:
{
SET(sector[sp->sectnum].extra, SECTFX_Z_ADJUST);
PlaxCeilGlobZadjust = SP_TAG2(sp);
PlaxFloorGlobZadjust = SP_TAG3(sp);
KillActor(actor);
break;
}
case CEILING_Z_ADJUST:
{
//SET(sector[sp->sectnum].ceilingstat, CEILING_STAT_FAF_BLOCK_HITSCAN);
SET(sector[sp->sectnum].extra, SECTFX_Z_ADJUST);
change_actor_stat(actor, STAT_ST1);
break;
}
case FLOOR_Z_ADJUST:
{
//SET(sector[sp->sectnum].floorstat, FLOOR_STAT_FAF_BLOCK_HITSCAN);
SET(sector[sp->sectnum].extra, SECTFX_Z_ADJUST);
change_actor_stat(actor, STAT_ST1);
break;
}
case WARP_TELEPORTER:
{
short start_wall, wall_num;
short sectnum = sp->sectnum;
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
SET(sector[sp->sectnum].extra, SECTFX_WARP_SECTOR);
change_actor_stat(actor, STAT_WARP);
// if just a destination teleporter
// don't set up flags
if (SP_TAG10(sp) == 1)
break;
// move the the next wall
wall_num = start_wall = sector[sectnum].wallptr;
// Travel all the way around loop setting wall bits
do
{
// DO NOT TAG WHITE WALLS!
if (validWallIndex(wall[wall_num].nextwall))
{
SET(wall[wall_num].cstat, CSTAT_WALL_WARP_HITSCAN);
}
wall_num = wall[wall_num].point2;
}
while (wall_num != start_wall);
break;
}
case WARP_CEILING_PLANE:
case WARP_FLOOR_PLANE:
{
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
SET(sector[sp->sectnum].extra, SECTFX_WARP_SECTOR);
change_actor_stat(actor, STAT_WARP);
break;
}
case WARP_COPY_SPRITE1:
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
SET(sector[sp->sectnum].extra, SECTFX_WARP_SECTOR);
change_actor_stat(actor, STAT_WARP_COPY_SPRITE1);
break;
case WARP_COPY_SPRITE2:
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
SET(sector[sp->sectnum].extra, SECTFX_WARP_SECTOR);
change_actor_stat(actor, STAT_WARP_COPY_SPRITE2);
break;
case FIREBALL_TRAP:
case BOLT_TRAP:
case SPEAR_TRAP:
{
u = SpawnUser(actor, 0, nullptr);
ClearOwner(actor);
change_actor_stat(actor, STAT_TRAP);
break;
}
case SECT_SO_DONT_BOB:
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_SO_DONT_BOB);
KillActor(actor);
break;
}
case SECT_LOCK_DOOR:
{
sectu = GetSectUser(sp->sectnum);
sectu->number = sp->lotag;
sectu->stag = SECT_LOCK_DOOR;
KillActor(actor);
break;
}
case SECT_SO_SINK_DEST:
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_SO_SINK_DEST);
sectu->number = sp->lotag; // acually the offset Z
// value
KillActor(actor);
break;
}
case SECT_SO_DONT_SINK:
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_SO_DONT_SINK);
KillActor(actor);
break;
}
case SO_SLOPE_FLOOR_TO_POINT:
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_SO_SLOPE_FLOOR_TO_POINT);
SET(sector[sp->sectnum].extra, SECTFX_DYNAMIC_AREA);
KillActor(actor);
break;
}
case SO_SLOPE_CEILING_TO_POINT:
{
sectu = GetSectUser(sp->sectnum);
SET(sectu->flags, SECTFU_SO_SLOPE_CEILING_TO_POINT);
SET(sector[sp->sectnum].extra, SECTFX_DYNAMIC_AREA);
KillActor(actor);
break;
}
case SECT_SO_FORM_WHIRLPOOL:
{
sectu = GetSectUser(sp->sectnum);
sectu->stag = SECT_SO_FORM_WHIRLPOOL;
sectu->height = sp->lotag;
KillActor(actor);
break;
}
case SECT_ACTOR_BLOCK:
{
short start_wall, wall_num;
short sectnum = sp->sectnum;
// move the the next wall
wall_num = start_wall = sector[sectnum].wallptr;
// Travel all the way around loop setting wall bits
do
{
SET(wall[wall_num].cstat, CSTAT_WALL_BLOCK_ACTOR);
uint16_t const nextwall = wall[wall_num].nextwall;
if (validWallIndex(nextwall))
SET(wall[nextwall].cstat, CSTAT_WALL_BLOCK_ACTOR);
wall_num = wall[wall_num].point2;
}
while (wall_num != start_wall);
KillActor(actor);
break;
}
}
}
}
break;
case RED_CARD:
num = 4;
goto KeyMain;
case RED_KEY:
num = 0;
goto KeyMain;
case BLUE_CARD:
num = 5;
goto KeyMain;
case BLUE_KEY:
num = 1;
goto KeyMain;
case GREEN_CARD:
num = 6;
goto KeyMain;
case GREEN_KEY:
num = 2;
goto KeyMain;
case YELLOW_CARD:
num = 7;
goto KeyMain;
case YELLOW_KEY:
num = 3;
goto KeyMain;
case GOLD_SKELKEY:
num = 8;
goto KeyMain;
case SILVER_SKELKEY:
num = 9;
goto KeyMain;
case BRONZE_SKELKEY:
num = 10;
goto KeyMain;
case RED_SKELKEY:
num = 11;
KeyMain:
{
if (gNet.MultiGameType == MULTI_GAME_COMMBAT || gNet.MultiGameType == MULTI_GAME_AI_BOTS)
{
KillActor(actor);
break;
}
u = SpawnUser(actor, 0, nullptr);
ASSERT(u != nullptr);
sp->picnum = u->ID = sp->picnum;
u->spal = sp->pal; // Set the palette from build
//SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
ChangeState(actor, s_Key[num]);
RESET(picanm[sp->picnum].sf, PICANM_ANIMTYPE_MASK);
RESET(picanm[sp->picnum + 1].sf, PICANM_ANIMTYPE_MASK);
change_actor_stat(actor, STAT_ITEM);
RESET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN | CSTAT_SPRITE_ONE_SIDED);
u->Radius = 500;
sp->hitag = LUMINOUS; //Set so keys over ride colored lighting
DoActorZrange(actor);
}
break;
// Used for multiplayer locks
case 1846:
case 1850:
case 1852:
case 2470:
if (TEST(sp->extra, SPRX_MULTI_ITEM))
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
{
KillActor(actor);
}
break;
case FIRE_FLY0:
/*
* u = SpawnUser(actor, FIRE_FLY0, nullptr);
*
* u->State = u->StateStart = &s_FireFly[0]; u->RotNum = 0;
*
* sp->ang = 0; sp->xvel = 4;
*
* if (labs(sp->z - sector[sp->sectnum].floorz) < Z(32)) sp->z =
* sector[sp->sectnum].floorz - Z(32);
*
* u->sz = sp->z;
*
* change_actor_stat(actor, STAT_MISC);
*/
break;
case ICON_REPAIR_KIT:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_REPAIR_KIT, s_RepairKit);
IconDefault(actor);
break;
case ICON_STAR:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_STAR, s_IconStar);
IconDefault(actor);
break;
case ICON_LG_MINE:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_LG_MINE, s_IconLgMine);
IconDefault(actor);
break;
case ICON_MICRO_GUN:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_MICRO_GUN, s_IconMicroGun);
IconDefault(actor);
break;
case ICON_MICRO_BATTERY:
NUKE_REPLACEMENT:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_MICRO_BATTERY, s_IconMicroBattery);
IconDefault(actor);
break;
case ICON_UZI:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_UZI, s_IconUzi);
IconDefault(actor);
break;
case ICON_UZIFLOOR:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_UZIFLOOR, s_IconUziFloor);
IconDefault(actor);
break;
case ICON_LG_UZI_AMMO:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_LG_UZI_AMMO, s_IconLgUziAmmo);
IconDefault(actor);
break;
case ICON_GRENADE_LAUNCHER:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_GRENADE_LAUNCHER, s_IconGrenadeLauncher);
IconDefault(actor);
break;
case ICON_LG_GRENADE:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_LG_GRENADE, s_IconLgGrenade);
IconDefault(actor);
break;
case ICON_RAIL_GUN:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_RAIL_GUN, s_IconRailGun);
IconDefault(actor);
break;
case ICON_RAIL_AMMO:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_RAIL_AMMO, s_IconRailAmmo);
IconDefault(actor);
break;
case ICON_ROCKET:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_ROCKET, s_IconRocket);
IconDefault(actor);
break;
case ICON_LG_ROCKET:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_LG_ROCKET, s_IconLgRocket);
IconDefault(actor);
break;
case ICON_SHOTGUN:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_SHOTGUN, s_IconShotgun);
u->Radius = 350; // Shotgun is hard to pick up for some reason.
IconDefault(actor);
break;
case ICON_LG_SHOTSHELL:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_LG_SHOTSHELL, s_IconLgShotshell);
IconDefault(actor);
break;
case ICON_AUTORIOT:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_AUTORIOT, s_IconAutoRiot);
IconDefault(actor);
break;
case ICON_GUARD_HEAD:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_GUARD_HEAD, s_IconGuardHead);
IconDefault(actor);
break;
case ICON_FIREBALL_LG_AMMO:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_FIREBALL_LG_AMMO, s_IconFireballLgAmmo);
IconDefault(actor);
break;
case ICON_HEART:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_HEART, s_IconHeart);
IconDefault(actor);
break;
case ICON_HEART_LG_AMMO:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_HEART_LG_AMMO, s_IconHeartLgAmmo);
IconDefault(actor);
break;
#if 0
case ICON_ELECTRO:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_ELECTRO, s_IconElectro);
IconDefault(actor);
break;
#endif
case ICON_SPELL:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_SPELL, s_IconSpell);
IconDefault(actor);
PicAnimOff(sp->picnum);
break;
case ICON_ARMOR:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_ARMOR, s_IconArmor);
if (sp->pal != PALETTE_PLAYER3)
sp->pal = u->spal = PALETTE_PLAYER1;
else
sp->pal = u->spal = PALETTE_PLAYER3;
IconDefault(actor);
break;
case ICON_MEDKIT:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_MEDKIT, s_IconMedkit);
IconDefault(actor);
break;
case ICON_SM_MEDKIT:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_SM_MEDKIT, s_IconSmMedkit);
IconDefault(actor);
break;
case ICON_CHEMBOMB:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_CHEMBOMB, s_IconChemBomb);
IconDefault(actor);
break;
case ICON_FLASHBOMB:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_FLASHBOMB, s_IconFlashBomb);
IconDefault(actor);
break;
case ICON_NUKE:
if (gNet.MultiGameType)
{
if (!gNet.Nuke)
{
goto NUKE_REPLACEMENT;
break;
}
}
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_NUKE, s_IconNuke);
IconDefault(actor);
break;
case ICON_CALTROPS:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_CALTROPS, s_IconCaltrops);
IconDefault(actor);
break;
case ICON_BOOSTER:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_BOOSTER, s_IconBooster);
IconDefault(actor);
break;
case ICON_HEAT_CARD:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_HEAT_CARD, s_IconHeatCard);
IconDefault(actor);
break;
#if 0
case ICON_ENVIRON_SUIT:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_ENVIRON_SUIT, s_IconEnvironSuit);
IconDefault(actor);
PicAnimOff(sp->picnum);
break;
#endif
case ICON_CLOAK:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_CLOAK, s_IconCloak);
IconDefault(actor);
PicAnimOff(sp->picnum);
break;
case ICON_FLY:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_FLY, s_IconFly);
IconDefault(actor);
PicAnimOff(sp->picnum);
break;
case ICON_NIGHT_VISION:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_NIGHT_VISION, s_IconNightVision);
IconDefault(actor);
PicAnimOff(sp->picnum);
break;
case ICON_FLAG:
if (!IconSpawn(sp))
{
KillActor(actor);
break;
}
u = SpawnUser(actor, ICON_FLAG, s_IconFlag);
u->spal = sp->pal;
sector[sp->sectnum].hitag = 9000; // Put flag's color in sect containing it
sector[sp->sectnum].lotag = u->spal;
IconDefault(actor);
PicAnimOff(sp->picnum);
break;
#if 0
case 380:
case 396:
case 430:
case 512:
case 521:
case 541:
case 2720:
#endif
case 3143:
case 3157:
{
u = SpawnUser(actor, sp->picnum, nullptr);
change_actor_stat(actor, STAT_STATIC_FIRE);
u->ID = FIREBALL_FLAMES;
u->Radius = 200;
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
RESET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
sp->hitag = LUMINOUS; //Always full brightness
sp->shade = -40;
break;
}
// blades
case BLADE1:
case BLADE2:
case BLADE3:
case 5011:
{
u = SpawnUser(actor, sp->picnum, nullptr);
change_actor_stat(actor, STAT_DEFAULT);
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
SET(sp->cstat, CSTAT_SPRITE_BLOCK_HITSCAN);
SET(sp->extra, SPRX_BLADE);
break;
}
case BREAK_LIGHT:
case BREAK_BARREL:
case BREAK_PEDISTAL:
case BREAK_BOTTLE1:
case BREAK_BOTTLE2:
case BREAK_MUSHROOM:
//if (TEST(sp->extra, SPRX_BREAKABLE))
// break;
u = SpawnUser(actor, sp->picnum, nullptr);
sp->clipdist = SPRITEp_SIZE_X(sp);
SET(sp->cstat, CSTAT_SPRITE_BREAKABLE);
SET(sp->extra, SPRX_BREAKABLE);
break;
// switches
case 581:
case 582:
case 558:
case 559:
case 560:
case 561:
case 562:
case 563:
case 564:
case 565:
case 566:
case 567:
case 568:
case 569:
case 570:
case 571:
case 572:
case 573:
case 574:
case 551:
case 552:
case 575:
case 576:
case 577:
case 578:
case 579:
case 589:
case 583:
case 584:
case 553:
case 554:
{
if (TEST(sp->extra, SPRX_MULTI_ITEM))
{
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
{
KillActor(actor);
break;
}
}
SET(sp->cstat, CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
break;
}
}
}
}
bool ItemSpotClear(DSWActor* actor, short statnum, short id)
{
bool found = false;
int i;
auto sip = &actor->s();
if (TEST_BOOL2(sip))
{
SWSectIterator it(sip->sectnum);
while (auto itActor = it.Next())
{
if (itActor->s().statnum == statnum && itActor->u()->ID == id)
{
found = true;
break;
}
}
}
return !found;
}
void SetupItemForJump(DSWActor* spawner, DSWActor* actor)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
auto sip = &spawner->s();
// setup item for jumping
if (SP_TAG7(sip))
{
change_actor_stat(actor, STAT_SKIP4);
u->ceiling_dist = Z(6);
u->floor_dist = Z(0);
u->Counter = 0;
sp->xvel = (int)SP_TAG7(sip)<<2;
sp->zvel = -(((int)SP_TAG8(sip))<<5);
u->xchange = MOVEx(sp->xvel, sp->ang);
u->ychange = MOVEy(sp->xvel, sp->ang);
u->zchange = sp->zvel;
}
}
int ActorCoughItem(DSWActor* actor)
{
SPRITEp sp = &actor->s();
USERp u = actor->u();
short choose;
SPRITEp np;
DSWActor* actorNew = nullptr;
switch (u->ID)
{
case SAILORGIRL_R0:
ASSERT(sp->sectnum >= 0);
actorNew = InsertActor(sp->sectnum, STAT_SPAWN_ITEMS);
np = &actorNew->s();
np->cstat = np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = SPRITEp_MID(sp);
np->ang = 0;
np->extra = 0;
// vel
SP_TAG7(np) = 1;
// zvel
SP_TAG8(np) = 40;
choose = RANDOM_P2(1024);
if (choose > 854)
SP_TAG3(np) = 91; // Match number
else if (choose > 684)
SP_TAG3(np) = 48; // Match number
else if (choose > 514)
SP_TAG3(np) = 58; // Match number
else if (choose > 344)
SP_TAG3(np) = 60; // Match number
else if (choose > 174)
SP_TAG3(np) = 62; // Match number
else
SP_TAG3(np) = 68; // Match number
// match
SP_TAG2(np) = -1;
// kill
RESET_BOOL1(np);
SpawnItemsMatch(-1);
break;
case GORO_RUN_R0:
if (RANDOM_P2(1024) < 700)
return 0;
ASSERT(sp->sectnum >= 0);
actorNew = InsertActor(sp->sectnum, STAT_SPAWN_ITEMS);
np = &actorNew->s();
np->cstat = np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = SPRITEp_MID(sp);
np->ang = 0;
np->extra = 0;
// vel
SP_TAG7(np) = 1;
// zvel
SP_TAG8(np) = 40;
SP_TAG3(np) = 69; // Match number
// match
SP_TAG2(np) = -1;
// kill
RESET_BOOL1(np);
SpawnItemsMatch(-1);
break;
case RIPPER2_RUN_R0:
if (RANDOM_P2(1024) < 700)
return 0;
ASSERT(sp->sectnum >= 0);
actorNew = InsertActor(sp->sectnum, STAT_SPAWN_ITEMS);
np = &actorNew->s();
np->cstat = np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = SPRITEp_MID(sp);
np->ang = 0;
np->extra = 0;
// vel
SP_TAG7(np) = 1;
// zvel
SP_TAG8(np) = 40;
SP_TAG3(np) = 70; // Match number
// match
SP_TAG2(np) = -1;
// kill
RESET_BOOL1(np);
SpawnItemsMatch(-1);
break;
case NINJA_RUN_R0:
if (u->PlayerP)
{
if (RANDOM_P2(1024) > 200)
return 0;
ASSERT(sp->sectnum >= 0);
actorNew = InsertActor(sp->sectnum, STAT_SPAWN_ITEMS);
np = &actorNew->s();
np->cstat = 0;
np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = SPRITEp_MID(sp);
np->ang = 0;
np->extra = 0;
// vel
SP_TAG7(np) = 1;
// zvel
SP_TAG8(np) = 40;
switch (u->WeaponNum)
{
case WPN_UZI:
SP_TAG3(np) = 0;
break;
case WPN_SHOTGUN:
SP_TAG3(np) = 51;
break;
case WPN_STAR:
if (u->PlayerP->WpnAmmo[WPN_STAR] < 9)
break;
SP_TAG3(np) = 41;
break;
case WPN_MINE:
if (u->PlayerP->WpnAmmo[WPN_MINE] < 5)
break;
SP_TAG3(np) = 42;
break;
case WPN_MICRO:
case WPN_ROCKET:
SP_TAG3(np) = 43;
break;
case WPN_GRENADE:
SP_TAG3(np) = 45;
break;
case WPN_RAIL:
SP_TAG3(np) = 47;
break;
case WPN_HEART:
SP_TAG3(np) = 55;
break;
case WPN_HOTHEAD:
SP_TAG3(np) = 53;
break;
}
// match
SP_TAG2(np) = -1;
// kill
RESET_BOOL1(np);
SpawnItemsMatch(-1);
break;
}
if (RANDOM_P2(1024) < 512)
return 0;
ASSERT(sp->sectnum >= 0);
actorNew = InsertActor(sp->sectnum, STAT_SPAWN_ITEMS);
np = &actorNew->s();
np->cstat = np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = SPRITEp_MID(sp);
np->ang = 0;
np->extra = 0;
// vel
SP_TAG7(np) = 1;
// zvel
SP_TAG8(np) = 40;
if (u->spal == PAL_XLAT_LT_TAN)
{
SP_TAG3(np) = 44;
}
else if (u->spal == PAL_XLAT_LT_GREY)
{
SP_TAG3(np) = 46;
}
else if (u->spal == PALETTE_PLAYER5) // Green Ninja
{
if (RANDOM_P2(1024) < 700)
SP_TAG3(np) = 61;
else
SP_TAG3(np) = 60;
}
else if (u->spal == PALETTE_PLAYER3) // Red Ninja
{
// type
if (RANDOM_P2(1024) < 800)
SP_TAG3(np) = 68;
else
SP_TAG3(np) = 44;
}
else
{
if (RANDOM_P2(1024) < 512)
SP_TAG3(np) = 41;
else
SP_TAG3(np) = 68;
}
// match
SP_TAG2(np) = -1;
// kill
RESET_BOOL1(np);
SpawnItemsMatch(-1);
break;
case PACHINKO1:
case PACHINKO2:
case PACHINKO3:
case PACHINKO4:
ASSERT(sp->sectnum >= 0);
actorNew = InsertActor(sp->sectnum, STAT_SPAWN_ITEMS);
np = &actorNew->s();
np->cstat = np->extra = 0;
np->x = sp->x;
np->y = sp->y;
np->z = SPRITEp_LOWER(sp)+Z(10);
np->ang = sp->ang;
// vel
SP_TAG7(np) = 10;
// zvel
SP_TAG8(np) = 10;
if (u->ID == PACHINKO1)
{
if (RANDOM_P2(1024) < 600)
SP_TAG3(np) = 64; // Small MedKit
else
SP_TAG3(np) = 59; // Fortune Cookie
}
else if (u->ID == PACHINKO2)
{
if (RANDOM_P2(1024) < 600)
SP_TAG3(np) = 52; // Lg Shot Shell
else
SP_TAG3(np) = 68; // Uzi clip
}
else if (u->ID == PACHINKO3)
{
if (RANDOM_P2(1024) < 600)
SP_TAG3(np) = 57;
else
SP_TAG3(np) = 63;
}
else if (u->ID == PACHINKO4)
{
if (RANDOM_P2(1024) < 600)
SP_TAG3(np) = 60;
else
SP_TAG3(np) = 61;
}
// match
SP_TAG2(np) = -1;
// kill
RESET_BOOL1(np);
SpawnItemsMatch(-1);
break;
}
return 0;
}
int SpawnItemsMatch(short match)
{
DSWActor* spawnedActor = nullptr;
SPRITEp sp,sip;
SWStatIterator it(STAT_SPAWN_ITEMS);
while (auto itActor = it.Next())
{
int si = itActor->GetSpriteIndex();
sip = &itActor->s();
if (SP_TAG2(sip) != match)
continue;
switch (SP_TAG3(sip))
{
case 90:
spawnedActor = BunnyHatch2(itActor);
sp = &spawnedActor->s();
spawnedActor->u()->spal = sp->pal = PALETTE_PLAYER8; // Boy
sp->ang = sip->ang;
break;
case 91:
spawnedActor = BunnyHatch2(itActor);
sp = &spawnedActor->s();
spawnedActor->u()->spal = sp->pal = PALETTE_PLAYER0; // Girl
sp->ang = sip->ang;
break;
case 92:
spawnedActor = BunnyHatch2(itActor);
sp = &spawnedActor->s();
sp->ang = sip->ang;
break;
case 40:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_REPAIR_KIT))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_REPAIR_KIT, s_RepairKit, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 41:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_STAR))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_STAR, s_IconStar, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 42:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_LG_MINE))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_LG_MINE, s_IconLgMine, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 43:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_MICRO_GUN))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_MICRO_GUN, s_IconMicroGun, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 44:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_MICRO_BATTERY))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_MICRO_BATTERY, s_IconMicroBattery, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 45:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_GRENADE_LAUNCHER))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_GRENADE_LAUNCHER, s_IconGrenadeLauncher, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 46:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_LG_GRENADE))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_LG_GRENADE, s_IconLgGrenade, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 47:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_RAIL_GUN))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_RAIL_GUN, s_IconRailGun, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 48:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_RAIL_AMMO))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_RAIL_AMMO, s_IconRailAmmo, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 49:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_ROCKET))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_ROCKET, s_IconRocket, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 51:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_SHOTGUN))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_SHOTGUN, s_IconShotgun, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 52:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_LG_SHOTSHELL))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_LG_SHOTSHELL, s_IconLgShotshell, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 53:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_GUARD_HEAD))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_GUARD_HEAD, s_IconGuardHead, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 54:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_FIREBALL_LG_AMMO))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_FIREBALL_LG_AMMO, s_IconFireballLgAmmo, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 55:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_HEART))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_HEART, s_IconHeart, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 56:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_HEART_LG_AMMO))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_HEART_LG_AMMO, s_IconHeartLgAmmo, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 57:
{
USERp u;
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_ARMOR))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_ARMOR, s_IconArmor, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
sp = &spawnedActor->s();
u = spawnedActor->u();
SET(u->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
if (sp->pal != PALETTE_PLAYER3)
sp->pal = u->spal = PALETTE_PLAYER1;
else
sp->pal = u->spal = PALETTE_PLAYER3;
break;
}
case 58:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_MEDKIT))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_MEDKIT, s_IconMedkit, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 59:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_SM_MEDKIT))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_SM_MEDKIT, s_IconSmMedkit, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 60:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_CHEMBOMB))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_CHEMBOMB, s_IconChemBomb, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 61:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_FLASHBOMB))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_FLASHBOMB, s_IconFlashBomb, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 62:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_NUKE))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_NUKE, s_IconNuke, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 63:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_CALTROPS))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_CALTROPS, s_IconCaltrops, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 64:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_BOOSTER))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_BOOSTER, s_IconBooster, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 65:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_HEAT_CARD))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_HEAT_CARD, s_IconHeatCard, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 66:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_CLOAK))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_CLOAK, s_IconCloak, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 67:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_NIGHT_VISION))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_NIGHT_VISION, s_IconNightVision, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 68:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_LG_UZI_AMMO))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_LG_UZI_AMMO, s_IconLgUziAmmo, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 69:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_GUARD_HEAD))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_GUARD_HEAD, s_IconGuardHead, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 70:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_HEART))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_HEART, s_IconHeart, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 20:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_UZIFLOOR))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_UZIFLOOR, s_IconUziFloor, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 32:
case 0:
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_UZI))
break;
spawnedActor = SpawnActor(STAT_ITEM, ICON_UZI, s_IconUzi, sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
SET(spawnedActor->u()->Flags2, SPR2_NEVER_RESPAWN);
IconDefault(spawnedActor);
SetupItemForJump(itActor, spawnedActor);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
{
short num;
USERp u;
uint8_t KeyPal[] =
{
PALETTE_PLAYER9,
PALETTE_PLAYER7,
PALETTE_PLAYER6,
PALETTE_PLAYER4,
PALETTE_PLAYER9,
PALETTE_PLAYER7,
PALETTE_PLAYER6,
PALETTE_PLAYER4,
PALETTE_PLAYER4,
PALETTE_PLAYER1,
PALETTE_PLAYER8,
PALETTE_PLAYER9
};
if (gNet.MultiGameType == MULTI_GAME_COMMBAT || gNet.MultiGameType == MULTI_GAME_AI_BOTS)
break;
num = SP_TAG3(sip) - 1;
if (!ItemSpotClear(itActor, STAT_ITEM, s_Key[num]->Pic))
break;
spawnedActor = SpawnActor(STAT_ITEM, s_Key[num]->Pic, s_Key[num], sip->sectnum, sip->x, sip->y, sip->z, sip->ang, 0);
u = spawnedActor->u();
sp = &spawnedActor->s();
ASSERT(u != nullptr);
sp->picnum = u->ID = s_Key[num]->Pic;
// need to set the palette here - suggest table lookup
u->spal = sp->pal = KeyPal[num];
//SET(sp->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
ChangeState(spawnedActor, s_Key[num]);
RESET(picanm[sp->picnum].sf, PICANM_ANIMTYPE_MASK);
RESET(picanm[sp->picnum + 1].sf, PICANM_ANIMTYPE_MASK);
SetupItemForJump(itActor, spawnedActor);
break;
}
}
if (!TEST_BOOL1(sip))
KillActor(itActor);
}
return 0;
}
int NewStateGroup(DSWActor* actor, STATEp StateGroup[])
{
USERp u = actor->u();
//if (Prediction)
// return;
if (!StateGroup)
return 0;
ASSERT(u);
// Kind of a goofy check, but it should catch alot of invalid states!
// BTW, 6144 is the max tile number allowed in editart.
if (u->State && (u->State->Pic < 0 || u->State->Pic > MAXTILES)) // JBF: verify this!
return 0;
u->Rot = StateGroup;
u->State = u->StateStart = StateGroup[0];
//sprite[SpriteNum].picnum = u->State->Pic;
u->Tics = 0;
// turn anims off because people keep setting them in the
// art file
RESET(picanm[actor->s().picnum].sf, PICANM_ANIMTYPE_MASK);
return 0;
}
bool SpriteOverlap(DSWActor* actor_a, DSWActor* actor_b)
{
SPRITEp spa = &actor_a->s(), spb = &actor_b->s();
USERp ua = actor_a->u();
USERp ub = actor_b->u();
int spa_tos, spa_bos, spb_tos, spb_bos, overlap_z;
if (!ua || !ub) return false;
if ((unsigned)Distance(spa->x, spa->y, spb->x, spb->y) > ua->Radius + ub->Radius)
{
return false;
}
spa_tos = SPRITEp_TOS(spa);
spa_bos = SPRITEp_BOS(spa);
spb_tos = SPRITEp_TOS(spb);
spb_bos = SPRITEp_BOS(spb);
overlap_z = ua->OverlapZ + ub->OverlapZ;
// if the top of sprite a is below the bottom of b
if (spa_tos - overlap_z > spb_bos)
{
return false;
}
// if the top of sprite b is is below the bottom of a
if (spb_tos - overlap_z > spa_bos)
{
return false;
}
return true;
}
bool
SpriteOverlapZ(int16_t spritenum_a, int16_t spritenum_b, int z_overlap)
{
SPRITEp spa = &sprite[spritenum_a], spb = &sprite[spritenum_b];
int spa_tos, spa_bos, spb_tos, spb_bos;
spa_tos = SPRITEp_TOS(spa);
spa_bos = SPRITEp_BOS(spa);
spb_tos = SPRITEp_TOS(spb);
spb_bos = SPRITEp_BOS(spb);
// if the top of sprite a is below the bottom of b
if (spa_tos + z_overlap > spb_bos)
{
return false;
}
// if the top of sprite b is is below the bottom of a
if (spb_tos + z_overlap > spa_bos)
{
return false;
}
return true;
}
void
getzrangepoint(int x, int y, int z, short sectnum,
int32_t* ceilz, int32_t* ceilhit, int32_t* florz, int32_t* florhit)
{
spritetype *spr;
int j, k, l, dax, day, daz, xspan, yspan, xoff, yoff;
int x1, y1, x2, y2, x3, y3, x4, y4, cosang, sinang, tilenum;
short cstat;
char clipyou;
if (sectnum < 0)
{
*ceilz = 0x80000000;
*ceilhit = -1;
*florz = 0x7fffffff;
*florhit = -1;
return;
}
// Initialize z's and hits to the current sector's top&bottom
getzsofslope(sectnum, x, y, ceilz, florz);
*ceilhit = sectnum + 16384;
*florhit = sectnum + 16384;
// Go through sprites of only the current sector
SWSectIterator it(sectnum);
while (auto itActor = it.Next())
{
spr = &itActor->s();
cstat = spr->cstat;
if ((cstat & 49) != 33)
continue; // Only check blocking floor sprites
daz = spr->z;
// Only check if sprite's 2-sided or your on the 1-sided side
if (((cstat & 64) != 0) && ((z > daz) == ((cstat & 8) == 0)))
continue;
// Calculate and store centering offset information into xoff&yoff
tilenum = spr->picnum;
xoff = (int)tileLeftOffset(tilenum) + (int)spr->xoffset;
yoff = (int)tileTopOffset(tilenum) + (int)spr->yoffset;
if (cstat & 4)
xoff = -xoff;
if (cstat & 8)
yoff = -yoff;
// Calculate all 4 points of the floor sprite.
// (x1,y1),(x2,y2),(x3,y3),(x4,y4)
// These points will already have (x,y) subtracted from them
cosang = bcos(spr->ang);
sinang = bsin(spr->ang);
xspan = tileWidth(tilenum);
dax = ((xspan >> 1) + xoff) * spr->xrepeat;
yspan = tileHeight(tilenum);
day = ((yspan >> 1) + yoff) * spr->yrepeat;
x1 = spr->x + DMulScale(sinang, dax, cosang, day, 16) - x;
y1 = spr->y + DMulScale(sinang, day, -cosang, dax, 16) - y;
l = xspan * spr->xrepeat;
x2 = x1 - MulScale(sinang, l, 16);
y2 = y1 + MulScale(cosang, l, 16);
l = yspan * spr->yrepeat;
k = -MulScale(cosang, l, 16);
x3 = x2 + k;
x4 = x1 + k;
k = -MulScale(sinang, l, 16);
y3 = y2 + k;
y4 = y1 + k;
// Check to see if point (0,0) is inside the 4 points by seeing if
// the number of lines crossed as a line is shot outward is odd
clipyou = 0;
if ((y1 ^ y2) < 0) // If y1 and y2 have different signs
// (- / +)
{
if ((x1 ^ x2) < 0)
clipyou ^= (x1 * y2 < x2 * y1) ^ (y1 < y2);
else if (x1 >= 0)
clipyou ^= 1;
}
if ((y2 ^ y3) < 0)
{
if ((x2 ^ x3) < 0)
clipyou ^= (x2 * y3 < x3 * y2) ^ (y2 < y3);
else if (x2 >= 0)
clipyou ^= 1;
}
if ((y3 ^ y4) < 0)
{
if ((x3 ^ x4) < 0)
clipyou ^= (x3 * y4 < x4 * y3) ^ (y3 < y4);
else if (x3 >= 0)
clipyou ^= 1;
}
if ((y4 ^ y1) < 0)
{
if ((x4 ^ x1) < 0)
clipyou ^= (x4 * y1 < x1 * y4) ^ (y4 < y1);
else if (x4 >= 0)
clipyou ^= 1;
}
if (clipyou == 0)
continue; // Point is not inside, don't clip
// Clipping time!
if (z > daz)
{
if (daz > *ceilz)
{
*ceilz = daz;
*ceilhit = itActor->GetSpriteIndex() + HIT_SPRITE;
}
}
else
{
if (daz < *florz)
{
*florz = daz;
*florhit = itActor->GetSpriteIndex() + HIT_SPRITE;
}
}
}
}
void DoActorZrange(DSWActor* actor)
{
USERp u = actor->u(), wu;
SPRITEp sp = &actor->s(), wp;
int ceilhit, florhit;
short save_cstat;
save_cstat = TEST(sp->cstat, CSTAT_SPRITE_BLOCK);
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
vec3_t pos = sp->pos;
pos.z -= DIV2(SPRITEp_SIZE_Z(sp));
FAFgetzrange(pos, sp->sectnum, &u->hiz, &ceilhit, &u->loz, &florhit, (((int) sp->clipdist) << 2) - GETZRANGE_CLIP_ADJ, CLIPMASK_ACTOR);
SET(sp->cstat, save_cstat);
u->lo_sectp = u->hi_sectp = nullptr;
u->highActor = nullptr;
u->lowActor = nullptr;
switch (TEST(ceilhit, HIT_MASK))
{
case HIT_SPRITE:
u->highActor = &swActors[NORM_SPRITE(ceilhit)];
break;
case HIT_SECTOR:
u->hi_sectp = &sector[NORM_SECTOR(ceilhit)];
break;
default:
ASSERT(true==false);
break;
}
switch (TEST(florhit, HIT_MASK))
{
case HIT_SPRITE:
u->lowActor = &swActors[NORM_SPRITE(florhit)];
break;
case HIT_SECTOR:
u->lo_sectp = &sector[NORM_SECTOR(florhit)];
break;
default:
ASSERT(true==false);
break;
}
}
// !AIC - puts getzrange results into USER varaible u->loz, u->hiz, u->lo_sectp, u->hi_sectp, etc.
// The loz and hiz are used a lot.
int DoActorGlobZ(DSWActor* actor)
{
USERp u = actor->u();
u->loz = globloz;
u->hiz = globhiz;
u->lo_sectp = u->hi_sectp = nullptr;
u->highActor = nullptr;
u->lowActor = nullptr;
switch (TEST(globhihit, HIT_MASK))
{
case HIT_SPRITE:
u->highActor = &swActors[globhihit & 4095];
break;
default:
u->hi_sectp = &sector[globhihit & 4095];
break;
}
switch (TEST(globlohit, HIT_MASK))
{
case HIT_SPRITE:
u->lowActor = &swActors[globlohit & 4095];
break;
default:
u->lo_sectp = &sector[globlohit & 4095];
break;
}
return 0;
}
bool
ActorDrop(short SpriteNum, int x, int y, int z, short new_sector, short min_height)
{
SPRITEp sp = &sprite[SpriteNum];
int ceilhit, florhit, hiz, loz;
short save_cstat;
// look only at the center point for a floor sprite
save_cstat = TEST(sp->cstat, CSTAT_SPRITE_BLOCK);
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
FAFgetzrangepoint(x, y, z - DIV2(SPRITEp_SIZE_Z(sp)), new_sector, &hiz, &ceilhit, &loz, &florhit);
SET(sp->cstat, save_cstat);
if (florhit < 0 || ceilhit < 0)
{
//SetSuicide(actor);
return true;
}
// ASSERT(florhit >= 0);
// ASSERT(ceilhit >= 0);
switch (TEST(florhit, HIT_MASK))
{
case HIT_SPRITE:
{
SPRITEp hsp = &sprite[florhit & 4095];
// if its a floor sprite and not too far down
if (TEST(hsp->cstat, CSTAT_SPRITE_ALIGNMENT_FLOOR) &&
(labs(loz - z) <= min_height))
{
return false;
}
break;
}
case HIT_SECTOR:
{
if (labs(loz - z) <= min_height)
{
return false;
}
break;
}
default:
ASSERT(true == false);
break;
}
return true;
}
// Primarily used in ai.c for now - need to get rid of
bool
DropAhead(DSWActor* actor, short min_height)
{
int SpriteNum = actor->GetSpriteIndex();
SPRITEp sp = &actor->s();
int dax, day;
int newsector;
// dax = sp->x + MOVEx(128, sp->ang);
// day = sp->y + MOVEy(128, sp->ang);
dax = sp->x + MOVEx(256, sp->ang);
day = sp->y + MOVEy(256, sp->ang);
newsector = sp->sectnum;
updatesector(dax, day, &newsector);
// look straight down for a drop
if (ActorDrop(SpriteNum, dax, day, sp->z, newsector, min_height))
return true;
return false;
}
/*
!AIC KEY - Called by ai.c routines. Calls move_sprite which calls clipmove.
This incapulates move_sprite and makes sure that actors don't walk off of
ledges. If it finds itself in mid air then it restores the last good
position. This is a hack because Ken had no good way of doing this from his
code. ActorDrop() is called from here.
*/
int move_actor(DSWActor* actor, int xchange, int ychange, int zchange)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
SPRITEp sp = &actor->s();
int x, y, z, loz, hiz;
DSWActor* highActor;
DSWActor* lowActor;
SECTORp lo_sectp, hi_sectp;
short sectnum;
short dist;
int cliptype = CLIPMASK_ACTOR;
if (TEST(u->Flags, SPR_NO_SCAREDZ))
{
// For COOLG & HORNETS
// set to actual z before you move
sp->z = u->sz;
}
// save off x,y values
x = sp->x;
y = sp->y;
z = sp->z;
loz = u->loz;
hiz = u->hiz;
lowActor = u->lowActor;
highActor = u->highActor;
lo_sectp = u->lo_sectp;
hi_sectp = u->hi_sectp;
sectnum = sp->sectnum;
u->coll = move_sprite(actor, xchange, ychange, zchange,
u->ceiling_dist, u->floor_dist, cliptype, ACTORMOVETICS);
ASSERT(sp->sectnum >= 0);
// try and determine whether you moved > lo_step in the z direction
if (!TEST(u->Flags, SPR_NO_SCAREDZ | SPR_JUMPING | SPR_CLIMBING | SPR_FALLING | SPR_DEAD | SPR_SWIMMING))
{
if (labs(sp->z - globloz) > u->lo_step)
{
// cancel move
sp->x = x;
sp->y = y;
sp->z = z;
//sp->z = u->loz; // place on ground in case you are in the air
u->loz = loz;
u->hiz = hiz;
u->lowActor = lowActor;
u->highActor = highActor;
u->lo_sectp = lo_sectp;
u->hi_sectp = hi_sectp;
u->coll.invalidate();
ChangeActorSect(actor, sectnum);
return false;
}
if (ActorDrop(SpriteNum, sp->x, sp->y, sp->z, sp->sectnum, u->lo_step))
{
//printf("cancel move 2\n", sp->z, u->loz);
// cancel move
sp->x = x;
sp->y = y;
sp->z = z;
//sp->z = u->loz; // place on ground in case you are in the air
u->loz = loz;
u->hiz = hiz;
u->lowActor = lowActor;
u->highActor = highActor;
u->lo_sectp = lo_sectp;
u->hi_sectp = hi_sectp;
u->coll.invalidate();
ChangeActorSect(actor, sectnum);
return false;
}
}
SET(u->Flags, SPR_MOVED);
if (u->coll.type == kHitNone)
{
// Keep track of how far sprite has moved
dist = Distance(x, y, sp->x, sp->y);
u->TargetDist -= dist;
u->Dist += dist;
u->DistCheck += dist;
return true;
}
else
{
return false;
}
}
int
DoStayOnFloor(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
sprite[SpriteNum].z = sector[sprite[SpriteNum].sectnum].floorz;
//sprite[SpriteNum].z = getflorzofslope(sprite[SpriteNum].sectnum, sprite[SpriteNum].x, sprite[SpriteNum].y);
return 0;
}
int
DoGrating(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
SPRITEp sp = User[SpriteNum]->SpriteP;
int dir;
#define GRATE_FACTOR 3
// reduce to 0 to 3 value
dir = sp->ang >> 9;
if ((dir & 1) == 0)
{
if (dir == 0)
sp->x += 2 * GRATE_FACTOR;
else
sp->x -= 2 * GRATE_FACTOR;
}
else
{
if (dir == 1)
sp->y += 2 * GRATE_FACTOR;
else
sp->y -= 2 * GRATE_FACTOR;
}
sp->hitag -= GRATE_FACTOR;
if (sp->hitag <= 0)
{
change_actor_stat(actor, STAT_DEFAULT);
User[SpriteNum].Clear();
}
setspritez(SpriteNum, &sp->pos);
return 0;
}
#if 0
int
DoSpriteFade(short SpriteNum)
{
USERp u = User[SpriteNum].Data();
SPRITEp sp = User[SpriteNum]->SpriteP;
short i;
// adjust Shade based on clock
for (i = 0; i < ACTORMOVETICS; i++)
{
if (TEST(u->Flags, SPR_SHADE_DIR))
{
sp->shade++;
if (sp->shade >= 10)
RESET(u->Flags, SPR_SHADE_DIR);
}
else
{
sp->shade--;
if (sp->shade <= -40)
SET(u->Flags, SPR_SHADE_DIR);
}
}
return 0;
}
#endif
int
DoKey(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
SPRITEp sp = User[SpriteNum]->SpriteP;
sp->ang = NORM_ANGLE(sp->ang + (14 * ACTORMOVETICS));
//DoSpriteFade(SpriteNum);
DoGet(actor);
return 0;
}
int
DoCoin(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
int offset;
u->WaitTics -= ACTORMOVETICS * 2;
if (u->WaitTics <= 0)
{
KillActor(actor);
return 0;
}
if (u->WaitTics < 10*120)
{
if (u->StateStart != s_GreenCoin)
{
offset = int(u->State - u->StateStart);
ChangeState(actor, s_GreenCoin);
u->State = u->StateStart + offset;
}
}
else if (u->WaitTics < 20*120)
{
if (u->StateStart != s_YellowCoin)
{
offset = int(u->State - u->StateStart);
ChangeState(actor, s_YellowCoin);
u->State = u->StateStart + offset;
}
}
return 0;
}
int
KillGet(short SpriteNum)
{
auto actor = &swActors[SpriteNum];
USERp u = actor->u();
SPRITEp sp = &actor->s();
USERp nu;
SPRITEp np;
short New;
switch (gNet.MultiGameType)
{
case MULTI_GAME_NONE:
case MULTI_GAME_COOPERATIVE:
KillActor(actor);
break;
case MULTI_GAME_COMMBAT:
case MULTI_GAME_AI_BOTS:
if (TEST(u->Flags2, SPR2_NEVER_RESPAWN))
{
KillActor(actor);
break;
}
u->WaitTics = 30*120;
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
// respawn markers
if (!gNet.SpawnMarkers || sp->hitag == TAG_NORESPAWN_FLAG) // No coin if it's a special flag
break;
New = SpawnSprite(STAT_ITEM, Red_COIN, s_RedCoin, sp->sectnum,
sp->x, sp->y, sp->z, 0, 0);
np = &sprite[New];
nu = User[New].Data();
np->shade = -20;
nu->WaitTics = u->WaitTics - 12;
break;
}
return 0;
}
int
KillGetAmmo(short SpriteNum)
{
auto actor = &swActors[SpriteNum];
USERp u = actor->u();
SPRITEp sp = &actor->s();
USERp nu;
SPRITEp np;
short New;
switch (gNet.MultiGameType)
{
case MULTI_GAME_NONE:
case MULTI_GAME_COOPERATIVE:
KillActor(actor);
break;
case MULTI_GAME_COMMBAT:
case MULTI_GAME_AI_BOTS:
if (TEST(u->Flags2, SPR2_NEVER_RESPAWN))
{
KillActor(actor);
break;
}
// No Respawn mode - all ammo goes away
if (gNet.NoRespawn)
{
KillActor(actor);
break;
}
u->WaitTics = 30*120;
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
// respawn markers
if (!gNet.SpawnMarkers)
break;
New = SpawnSprite(STAT_ITEM, Red_COIN, s_RedCoin, sp->sectnum,
sp->x, sp->y, sp->z, 0, 0);
np = &sprite[New];
nu = User[New].Data();
np->shade = -20;
nu->WaitTics = u->WaitTics - 12;
break;
}
return 0;
}
int
KillGetWeapon(short SpriteNum)
{
auto actor = &swActors[SpriteNum];
USERp u = actor->u();
SPRITEp sp = &actor->s();
USERp nu;
SPRITEp np;
short New;
switch (gNet.MultiGameType)
{
case MULTI_GAME_NONE:
KillActor(actor);
break;
case MULTI_GAME_COOPERATIVE:
// don't kill weapons in coop
// unless told too :)
if (TEST(u->Flags2, SPR2_NEVER_RESPAWN))
{
KillActor(actor);
break;
}
break;
case MULTI_GAME_COMMBAT:
case MULTI_GAME_AI_BOTS:
if (TEST(u->Flags2, SPR2_NEVER_RESPAWN))
{
KillActor(actor);
break;
}
// No Respawn mode - all weapons stay
// but can only get once
if (gNet.NoRespawn)
break;
u->WaitTics = 30*120;
SET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
// respawn markers
if (!gNet.SpawnMarkers)
break;
New = SpawnSprite(STAT_ITEM, Red_COIN, s_RedCoin, sp->sectnum,
sp->x, sp->y, sp->z, 0, 0);
np = &sprite[New];
nu = User[New].Data();
np->shade = -20;
nu->WaitTics = u->WaitTics - 12;
break;
}
return 0;
}
int
DoSpawnItemTeleporterEffect(SPRITEp sp)
{
extern STATE s_TeleportEffect[];
short effect;
SPRITEp ep;
effect = SpawnSprite(STAT_MISSILE, 0, s_TeleportEffect, sp->sectnum,
sp->x, sp->y, sp->z - Z(12),
sp->ang, 0);
ep = &sprite[effect];
ep->shade = -40;
ep->xrepeat = ep->yrepeat = 36;
SET(ep->cstat, CSTAT_SPRITE_YCENTER);
RESET(ep->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
return 0;
}
void ChoosePlayerGetSound(PLAYERp pp)
{
int choose_snd=0;
if (pp != Player+myconnectindex) return;
choose_snd = STD_RANDOM_RANGE((MAX_GETSOUNDS-1)<<8)>>8;
PlayerSound(PlayerGetItemVocs[choose_snd], v3df_follow|v3df_dontpan,pp);
}
bool CanGetWeapon(PLAYERp pp, short SpriteNum, int WPN)
{
USERp u = User[SpriteNum].Data();
switch (gNet.MultiGameType)
{
case MULTI_GAME_NONE:
return true;
case MULTI_GAME_COOPERATIVE:
if (TEST(u->Flags2, SPR2_NEVER_RESPAWN))
return true;
if (TEST(pp->WpnGotOnceFlags, BIT(WPN)))
return false;
return true;
case MULTI_GAME_COMMBAT:
case MULTI_GAME_AI_BOTS:
if (TEST(u->Flags2, SPR2_NEVER_RESPAWN))
return true;
// No Respawn - can't get a weapon again if you already got it
if (gNet.NoRespawn && TEST(pp->WpnGotOnceFlags, BIT(WPN)))
return false;
return true;
}
return true;
}
struct InventoryDecl_t InventoryDecls[InvDecl_TOTAL] =
{
{50 },
{100 },
{20 },
{50 },
{100 },
{1 },
{2 },
{3 },
{100 },
{100 },
{100 },
};
#define ITEMFLASHAMT -8
#define ITEMFLASHCLR 144
int
DoGet(DSWActor* actor)
{
USER* u = actor->u();
int SpriteNum = u->SpriteNum;
USERp pu;
SPRITEp sp = u->SpriteP;
PLAYERp pp;
short pnum, key_num;
int dist, a,b,c;
bool can_see;
int cstat_bak;
// For flag stuff
USERp nu;
SPRITEp np;
short New;
// Invisiblility is only used for DeathMatch type games
// Sprites stays invisible for a period of time and is un-gettable
// then "Re-Spawns" by becomming visible. Its never actually killed.
if (TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
{
u->WaitTics -= ACTORMOVETICS * 2;
if (u->WaitTics <= 0)
{
PlaySound(DIGI_ITEM_SPAWN, sp, v3df_none);
DoSpawnItemTeleporterEffect(sp);
RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE);
}
return 0;
}
if (sp->xvel)
{
if (!DoItemFly(actor))
{
sp->xvel = 0;
change_actor_stat(actor, STAT_ITEM);
}
}
TRAVERSE_CONNECT(pnum)
{
pp = &Player[pnum];
pu = pp->Actor()->u();
if (TEST(pp->Flags, PF_DEAD))
continue;
DISTANCE(pp->posx, pp->posy, sp->x, sp->y, dist, a,b,c);
if ((unsigned)dist > (pu->Radius + u->Radius))
{
continue;
}
if (!SpriteOverlap(actor, pp->Actor()))
{
continue;
}
cstat_bak = sp->cstat;
SET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
can_see = FAFcansee(sp->x, sp->y, sp->z, sp->sectnum,
pp->posx, pp->posy, pp->posz, pp->cursectnum);
sp->cstat = cstat_bak;
if (!can_see)
{
continue;
}
switch (u->ID)
{
//
// Keys
//
case RED_CARD:
case RED_KEY:
key_num = 0;
goto KeyMain;
case BLUE_CARD:
case BLUE_KEY:
key_num = 1;
goto KeyMain;
case GREEN_CARD:
case GREEN_KEY:
key_num = 2;
goto KeyMain;
case YELLOW_CARD:
case YELLOW_KEY:
key_num = 3;
goto KeyMain;
case GOLD_SKELKEY:
key_num = 4;
goto KeyMain;
case SILVER_SKELKEY:
key_num = 5;
goto KeyMain;
case BRONZE_SKELKEY:
key_num = 6;
goto KeyMain;
case RED_SKELKEY:
key_num = 7;
KeyMain:
if (pp->HasKey[key_num])
break;
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_KEYMSG + key_num));
pp->HasKey[key_num] = true;
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_KEY, sp, v3df_dontpan);
// don't kill keys in coop
if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
break;
KillActor(actor);
break;
case ICON_ARMOR:
if (pp->Armor < InventoryDecls[InvDecl_Kevlar].amount)
{
if (u->spal == PALETTE_PLAYER3)
{
PlayerUpdateArmor(pp, 1000+InventoryDecls[InvDecl_Kevlar].amount);
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_Kevlar));
}
else
{
if (pp->Armor < InventoryDecls[InvDecl_Armor].amount)
{
PlayerUpdateArmor(pp, 1000+InventoryDecls[InvDecl_Armor].amount);
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_Armor));
}
else
break;
}
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_BIGITEM, sp, v3df_dontpan);
// override for respawn mode
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
{
KillActor(actor);
break;
}
KillGet(SpriteNum);
}
break;
//
// Health - Instant Use
//
case ICON_SM_MEDKIT:
if (pu->Health < 100)
{
bool putbackmax=false;
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_SmMedkit));
if (pp->MaxHealth == 200)
{
pp->MaxHealth = 100;
putbackmax = true;
}
PlayerUpdateHealth(pp, InventoryDecls[InvDecl_SmMedkit].amount);
if (putbackmax) pp->MaxHealth = 200;
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
// override for respawn mode
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
{
KillActor(actor);
break;
}
KillGet(SpriteNum);
}
break;
case ICON_BOOSTER: // Fortune cookie
pp->MaxHealth = 200;
if (pu->Health < 200)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_Booster));
PlayerUpdateHealth(pp, InventoryDecls[InvDecl_Booster].amount); // This is for health
// over 100%
// Say something witty
if (pp == Player+myconnectindex)
{
int cookie = STD_RANDOM_RANGE(MAX_FORTUNES);
// print to the console, and the user quote display.
FStringf msg("%s %s", GStrings("TXTS_FORTUNE"), quoteMgr.GetQuote(QUOTE_COOKIE + cookie));
Printf(PRINT_NONOTIFY, TEXTCOLOR_SAPPHIRE "%s\n", msg.GetChars());
if (hud_messages)
{
strncpy(pp->cookieQuote, msg, 255);
pp->cookieQuote[255] = 0;
pp->cookieTime = 540;
}
}
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_BIGITEM, sp, v3df_dontpan);
// override for respawn mode
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
{
KillActor(actor);
break;
}
KillGet(SpriteNum);
}
break;
//
// Inventory
//
case ICON_MEDKIT:
if (!pp->InventoryAmount[INVENTORY_MEDKIT] || pp->InventoryPercent[INVENTORY_MEDKIT] < InventoryDecls[InvDecl_Medkit].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_Medkit));
pp->InventoryPercent[INVENTORY_MEDKIT] = InventoryDecls[InvDecl_Medkit].amount;
pp->InventoryAmount[INVENTORY_MEDKIT] = 1;
PlayerUpdateInventory(pp, INVENTORY_MEDKIT);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
// override for respawn mode
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
{
KillActor(actor);
break;
}
KillGet(SpriteNum);
}
break;
case ICON_CHEMBOMB:
if (pp->InventoryAmount[INVENTORY_CHEMBOMB] < InventoryDecls[InvDecl_ChemBomb].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_ChemBomb));
pp->InventoryPercent[INVENTORY_CHEMBOMB] = 0;
pp->InventoryAmount[INVENTORY_CHEMBOMB]++;
PlayerUpdateInventory(pp, INVENTORY_CHEMBOMB);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
}
break;
case ICON_FLASHBOMB:
if (pp->InventoryAmount[INVENTORY_FLASHBOMB] < InventoryDecls[InvDecl_FlashBomb].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_FlashBomb));
pp->InventoryPercent[INVENTORY_FLASHBOMB] = 0;
pp->InventoryAmount[INVENTORY_FLASHBOMB]++;
PlayerUpdateInventory(pp, INVENTORY_FLASHBOMB);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
}
break;
case ICON_CALTROPS:
if (pp->InventoryAmount[INVENTORY_CALTROPS] < InventoryDecls[InvDecl_Caltrops].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_Caltrops));
pp->InventoryPercent[INVENTORY_CALTROPS] = 0;
pp->InventoryAmount[INVENTORY_CALTROPS]+=3;
if (pp->InventoryAmount[INVENTORY_CALTROPS] > InventoryDecls[InvDecl_Caltrops].amount)
pp->InventoryAmount[INVENTORY_CALTROPS] = InventoryDecls[InvDecl_Caltrops].amount;
PlayerUpdateInventory(pp, INVENTORY_CALTROPS);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
}
break;
case ICON_NIGHT_VISION:
if (!pp->InventoryAmount[INVENTORY_NIGHT_VISION] || pp->InventoryPercent[INVENTORY_NIGHT_VISION] < InventoryDecls[InvDecl_NightVision].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_NightVision));
pp->InventoryPercent[INVENTORY_NIGHT_VISION] = InventoryDecls[InvDecl_NightVision].amount;
pp->InventoryAmount[INVENTORY_NIGHT_VISION] = 1;
PlayerUpdateInventory(pp, INVENTORY_NIGHT_VISION);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
}
break;
case ICON_REPAIR_KIT:
if (!pp->InventoryAmount[INVENTORY_REPAIR_KIT] || pp->InventoryPercent[INVENTORY_REPAIR_KIT] < InventoryDecls[InvDecl_RepairKit].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_RepairKit));
pp->InventoryPercent[INVENTORY_REPAIR_KIT] = InventoryDecls[InvDecl_RepairKit].amount;
pp->InventoryAmount[INVENTORY_REPAIR_KIT] = 1;
PlayerUpdateInventory(pp, INVENTORY_REPAIR_KIT);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
// don't kill repair kit in coop
if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
break;
KillGet(SpriteNum);
}
break;
#if 0
case ICON_ENVIRON_SUIT:
if (!pp->InventoryAmount[INVENTORY_ENVIRON_SUIT] || pp->InventoryPercent[INVENTORY_ENVIRON_SUIT] < 100)
{
pp->InventoryPercent[INVENTORY_ENVIRON_SUIT] = 100;
pp->InventoryAmount[INVENTORY_ENVIRON_SUIT] = 1;
PlayerUpdateInventory(pp, INVENTORY_ENVIRON_SUIT);
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
}
break;
#endif
case ICON_CLOAK:
if (!pp->InventoryAmount[INVENTORY_CLOAK] || pp->InventoryPercent[INVENTORY_CLOAK] < InventoryDecls[InvDecl_Cloak].amount)
{
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_INVENTORY + InvDecl_Cloak));
pp->InventoryPercent[INVENTORY_CLOAK] = InventoryDecls[InvDecl_Cloak].amount;
pp->InventoryAmount[INVENTORY_CLOAK] = 1;
PlayerUpdateInventory(pp, INVENTORY_CLOAK);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
}
break;
//
// Weapon
//
case ICON_STAR:
if (!CanGetWeapon(pp, SpriteNum, WPN_STAR))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_STAR));
if (pp->WpnAmmo[WPN_STAR] >= DamageData[WPN_STAR].max_ammo)
break;
PutStringInfo(Player+pnum, sw_darts? GStrings("TXTS_DARTS") : quoteMgr.GetQuote(QUOTE_WPNSHURIKEN));
PlayerUpdateAmmo(pp, WPN_STAR, DamageData[WPN_STAR].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_STAR)))
break;
SET(pp->WpnFlags, BIT(WPN_STAR));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum <= WPN_STAR && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponStar(pp);
break;
case ICON_LG_MINE:
if (!CanGetWeapon(pp, SpriteNum, WPN_MINE))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_MINE));
if (pp->WpnAmmo[WPN_MINE] >= DamageData[WPN_MINE].max_ammo)
break;
//sprintf(ds,"Sticky Bombs");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNSTICKY));
PlayerUpdateAmmo(pp, WPN_MINE, DamageData[WPN_MINE].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
ChoosePlayerGetSound(pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_MINE)))
break;
SET(pp->WpnFlags, BIT(WPN_MINE));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_MINE && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponMine(pp);
break;
case ICON_UZI:
case ICON_UZIFLOOR:
if (!CanGetWeapon(pp, SpriteNum, WPN_UZI))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_UZI));
if (TEST(pp->Flags, PF_TWO_UZI) && pp->WpnAmmo[WPN_UZI] >= DamageData[WPN_UZI].max_ammo)
break;
//sprintf(ds,"UZI Submachine Gun");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNUZI));
// pp->WpnAmmo[WPN_UZI] += 50;
PlayerUpdateAmmo(pp, WPN_UZI, DamageData[WPN_UZI].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_UZI)) && TEST(pp->Flags, PF_TWO_UZI))
break;
// flag to help with double uzi powerup - simpler but kludgy
SET(pp->Flags, PF_PICKED_UP_AN_UZI);
if (TEST(pp->WpnFlags, BIT(WPN_UZI)))
{
SET(pp->Flags, PF_TWO_UZI);
pp->WpnUziType = 0; // Let it come up
if (pp == Player+myconnectindex)
PlayerSound(DIGI_DOUBLEUZI, v3df_dontpan|v3df_follow, pp);
}
else
{
SET(pp->WpnFlags, BIT(WPN_UZI));
ChoosePlayerGetSound(pp);
}
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_UZI && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponUzi(pp);
break;
case ICON_LG_UZI_AMMO:
if (pp->WpnAmmo[WPN_UZI] >= DamageData[WPN_UZI].max_ammo)
break;
//sprintf(ds,"UZI Clip");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMOUZI));
PlayerUpdateAmmo(pp, WPN_UZI, DamageData[WPN_UZI].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
case ICON_MICRO_GUN:
if (!CanGetWeapon(pp, SpriteNum, WPN_MICRO))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_MICRO));
if (TEST(pp->WpnFlags, BIT(WPN_MICRO)) && pp->WpnAmmo[WPN_MICRO] >= DamageData[WPN_MICRO].max_ammo)
break;
//sprintf(ds,"Missile Launcher");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNLAUNCH));
// pp->WpnAmmo[WPN_MICRO] += 5;
PlayerUpdateAmmo(pp, WPN_MICRO, DamageData[WPN_MICRO].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
ChoosePlayerGetSound(pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_MICRO)))
break;
SET(pp->WpnFlags, BIT(WPN_MICRO));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_MICRO && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponMicro(pp);
break;
case ICON_MICRO_BATTERY:
if (pp->WpnAmmo[WPN_MICRO] >= DamageData[WPN_MICRO].max_ammo)
break;
//sprintf(ds,"Missiles");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMOLAUNCH));
PlayerUpdateAmmo(pp, WPN_MICRO, DamageData[WPN_MICRO].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
case ICON_NUKE:
if (pp->WpnRocketNuke != 1)
{
//sprintf(ds,"Nuclear Warhead");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNNUKE));
pp->WpnRocketNuke =uint8_t(DamageData[DMG_NUCLEAR_EXP].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
if (STD_RANDOM_RANGE(1000) > 800 && pp == Player+myconnectindex)
PlayerSound(DIGI_ILIKENUKES, v3df_dontpan|v3df_doppler|v3df_follow,pp);
if (pp->CurWpn == pp->Wpn[WPN_MICRO])
{
if (pp->WpnRocketType != 2)
{
extern PANEL_STATE ps_MicroNukeFlash[];
pp->CurWpn->over[MICRO_SHOT_NUM].tics = 0;
pp->CurWpn->over[MICRO_SHOT_NUM].State = ps_MicroNukeFlash;
// Play Nuke available sound here!
}
}
KillGetAmmo(SpriteNum);
}
break;
case ICON_GRENADE_LAUNCHER:
if (!CanGetWeapon(pp, SpriteNum, WPN_GRENADE))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_GRENADE));
if (TEST(pp->WpnFlags, BIT(WPN_GRENADE)) && pp->WpnAmmo[WPN_GRENADE] >= DamageData[WPN_GRENADE].max_ammo)
break;
//sprintf(ds,"Grenade Launcher");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNGRENADE));
// pp->WpnAmmo[WPN_GRENADE] += 6;
PlayerUpdateAmmo(pp, WPN_GRENADE, DamageData[WPN_GRENADE].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
//ChoosePlayerGetSound(pp);
if (STD_RANDOM_RANGE(1000) > 800 && pp == Player+myconnectindex)
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_GRENADE)))
break;
SET(pp->WpnFlags, BIT(WPN_GRENADE));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_GRENADE && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponGrenade(pp);
break;
case ICON_LG_GRENADE:
if (pp->WpnAmmo[WPN_GRENADE] >= DamageData[WPN_GRENADE].max_ammo)
break;
//sprintf(ds,"Grenade Shells");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMOGRENADE));
PlayerUpdateAmmo(pp, WPN_GRENADE, DamageData[WPN_GRENADE].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
#if 0
case ICON_ROCKET:
pp->WpnAmmo[WPN_ROCKET] += 15;
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_ROCKET)))
break;
SET(pp->WpnFlags, BIT(WPN_ROCKET));
if (!cl_weaponswitch)
break;
InitWeaponRocket(pp);
break;
case ICON_LG_ROCKET:
sprintf(ds,"20 Missiles");
PutStringInfo(Player+pnum, ds);
PlayerUpdateAmmo(pp, WPN_ROCKET, 20);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
break;
#endif
case ICON_RAIL_GUN:
if (SW_SHAREWARE) { KillActor(actor); break; }
if (!CanGetWeapon(pp, SpriteNum, WPN_RAIL))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_RAIL));
if (TEST(pp->WpnFlags, BIT(WPN_RAIL)) && pp->WpnAmmo[WPN_RAIL] >= DamageData[WPN_RAIL].max_ammo)
break;
//sprintf(ds,"Rail Gun");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNRAILGUN));
PlayerUpdateAmmo(pp, WPN_RAIL, DamageData[WPN_RAIL].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
if (pp == Player+myconnectindex)
{
if (STD_RANDOM_RANGE(1000) > 700)
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
else
PlayerSound(DIGI_GOTRAILGUN, v3df_dontpan|v3df_doppler|v3df_follow,pp);
}
//ChoosePlayerGetSound(pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_RAIL)))
break;
SET(pp->WpnFlags, BIT(WPN_RAIL));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_RAIL && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponRail(pp);
break;
case ICON_RAIL_AMMO:
if (SW_SHAREWARE) { KillActor(actor); break; }
if (pp->WpnAmmo[WPN_RAIL] >= DamageData[WPN_RAIL].max_ammo)
break;
//sprintf(ds,"Rail Gun Rods");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMORAILGUN));
PlayerUpdateAmmo(pp, WPN_RAIL, DamageData[WPN_RAIL].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
case ICON_SHOTGUN:
if (!CanGetWeapon(pp, SpriteNum, WPN_SHOTGUN))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_SHOTGUN));
if (TEST(pp->WpnFlags, BIT(WPN_SHOTGUN)) && pp->WpnAmmo[WPN_SHOTGUN] >= DamageData[WPN_SHOTGUN].max_ammo)
break;
//sprintf(ds,"Riot Gun");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNRIOT));
// pp->WpnAmmo[WPN_SHOTGUN] += 10;
PlayerUpdateAmmo(pp, WPN_SHOTGUN, DamageData[WPN_SHOTGUN].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
ChoosePlayerGetSound(pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_SHOTGUN)))
break;
SET(pp->WpnFlags, BIT(WPN_SHOTGUN));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_SHOTGUN && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponShotgun(pp);
break;
case ICON_LG_SHOTSHELL:
if (pp->WpnAmmo[WPN_SHOTGUN] >= DamageData[WPN_SHOTGUN].max_ammo)
break;
//sprintf(ds,"Shotshells");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMORIOT));
PlayerUpdateAmmo(pp, WPN_SHOTGUN, DamageData[WPN_SHOTGUN].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
#if 0
case ICON_AUTORIOT:
if (pp->WpnShotgunAuto != 50)
{
sprintf(ds,"Riot Gun TurboDrive, +50 12-Gauge Slugs");
PutStringInfo(Player+pnum, ds);
pp->WpnShotgunAuto = 50;
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
if (pp->CurWpn == pp->Wpn[WPN_SHOTGUN])
{
if (pp->WpnShotgunType != 1)
{
extern PANEL_STATE ps_ShotgunFlash[];
pp->CurWpn->over[SHOTGUN_AUTO_NUM].tics = 0;
pp->CurWpn->over[SHOTGUN_AUTO_NUM].State = ps_ShotgunFlash;
}
}
}
break;
#endif
case ICON_GUARD_HEAD:
if (SW_SHAREWARE) { KillActor(actor); break; }
if (!CanGetWeapon(pp, SpriteNum, WPN_HOTHEAD))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_HOTHEAD));
if (TEST(pp->WpnFlags, BIT(WPN_HOTHEAD)) && pp->WpnAmmo[WPN_HOTHEAD] >= DamageData[WPN_HOTHEAD].max_ammo)
break;
//sprintf(ds,"Guardian Head");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNHEAD));
PlayerUpdateAmmo(pp, WPN_HOTHEAD, DamageData[WPN_HOTHEAD].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
//ChoosePlayerGetSound(pp);
if (STD_RANDOM_RANGE(1000) > 800 && pp == Player+myconnectindex)
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_HOTHEAD)))
break;
SET(pp->WpnFlags, BIT(WPN_NAPALM) | BIT(WPN_RING) | BIT(WPN_HOTHEAD));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_HOTHEAD && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponHothead(pp);
break;
case ICON_FIREBALL_LG_AMMO:
if (SW_SHAREWARE) { KillActor(actor); break; }
if (pp->WpnAmmo[WPN_HOTHEAD] >= DamageData[WPN_HOTHEAD].max_ammo)
break;
//sprintf(ds,"Firebursts");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMOHEAD));
PlayerUpdateAmmo(pp, WPN_HOTHEAD, DamageData[WPN_HOTHEAD].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
case ICON_HEART:
if (SW_SHAREWARE) { KillActor(actor); break; }
if (!CanGetWeapon(pp, SpriteNum, WPN_HEART))
break;
SET(pp->WpnGotOnceFlags, BIT(WPN_HEART));
if (TEST(pp->WpnFlags, BIT(WPN_HEART)) && pp->WpnAmmo[WPN_HEART] >= DamageData[WPN_HEART].max_ammo)
break;
//sprintf(ds,"Ripper Heart");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_WPNRIPPER));
PlayerUpdateAmmo(pp, WPN_HEART, DamageData[WPN_HEART].weapon_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
//ChoosePlayerGetSound(pp);
if (STD_RANDOM_RANGE(1000) > 800 && pp == Player+myconnectindex)
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
KillGetWeapon(SpriteNum);
if (TEST(pp->WpnFlags, BIT(WPN_HEART)))
break;
SET(pp->WpnFlags, BIT(WPN_HEART));
if (!cl_weaponswitch)
break;
if (User[pp->PlayerSprite]->WeaponNum > WPN_HEART && User[pp->PlayerSprite]->WeaponNum != WPN_SWORD)
break;
InitWeaponHeart(pp);
break;
case ICON_HEART_LG_AMMO:
if (SW_SHAREWARE) { KillActor(actor); break; }
if (pp->WpnAmmo[WPN_HEART] >= DamageData[WPN_HEART].max_ammo)
break;
//sprintf(ds,"Deathcoils");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMORIPPER));
PlayerUpdateAmmo(pp, WPN_HEART, DamageData[WPN_HEART].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGetAmmo(SpriteNum);
break;
case ICON_HEAT_CARD:
if (pp->WpnRocketHeat != 5)
{
//sprintf(ds,"Heat Seeker Card");
PutStringInfo(Player+pnum, quoteMgr.GetQuote(QUOTE_AMMONUKE));
pp->WpnRocketHeat = uint8_t(DamageData[DMG_NUCLEAR_EXP].ammo_pickup);
SetFadeAmt(pp,ITEMFLASHAMT,ITEMFLASHCLR); // Flash blue on item pickup
if (pp == Player+myconnectindex)
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
KillGet(SpriteNum);
if (pp->CurWpn == pp->Wpn[WPN_MICRO])
{
if (pp->WpnRocketType == 0)
{
pp->WpnRocketType = 1;
}
else if (pp->WpnRocketType == 2)
{
extern PANEL_STATE ps_MicroHeatFlash[];
pp->CurWpn->over[MICRO_HEAT_NUM].tics = 0;
pp->CurWpn->over[MICRO_HEAT_NUM].State = ps_MicroHeatFlash;
}
}
}
break;
case ICON_FLAG:
{
if (sp->pal == sprite[pp->PlayerSprite].pal) break; // Can't pick up your own flag!
PlaySound(DIGI_ITEM, sp, v3df_dontpan);
if (sp->hitag == TAG_NORESPAWN_FLAG)
New = SpawnSprite(STAT_ITEM, ICON_FLAG, s_CarryFlagNoDet, sp->sectnum,
sp->x, sp->y, sp->z, 0, 0);
else
New = SpawnSprite(STAT_ITEM, ICON_FLAG, s_CarryFlag, sp->sectnum,
sp->x, sp->y, sp->z, 0, 0);
auto actorNew = &swActors[New];
np = &sprite[New];
nu = User[New].Data();
np->shade = -20;
// Attach flag to player
nu->Counter = 0;
RESET(np->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
SET(np->cstat, CSTAT_SPRITE_ALIGNMENT_WALL);
SetAttach(pp->Actor(), actorNew);
nu->sz = SPRITEp_MID(&sprite[pp->PlayerSprite]); // Set mid way up who it hit
nu->spal = np->pal = sp->pal; // Set the palette of the flag
SetOwner(pp->Actor(), actorNew); // Player now owns the flag
nu->flagOwnerActor = actor; // Tell carried flag who owns it
KillGet(SpriteNum); // Set up for flag respawning
break;
}
default:
KillActor(actor);
}
}
return 0;
}
// This function mostly only adjust the active_range field
void ProcessActiveVars(DSWActor* actor)
{
USERp u = actor->u();
const int TIME_TILL_INACTIVE = (4 * 120);
if (!TEST(u->Flags, SPR_ACTIVE))
{
// if actor has been unaware for more than a few seconds
u->inactive_time += ACTORMOVETICS;
if (u->inactive_time > TIME_TILL_INACTIVE)
{
// reset to min update range
u->active_range = MIN_ACTIVE_RANGE;
// keep time low so it doesn't roll over
u->inactive_time = TIME_TILL_INACTIVE;
}
}
u->wait_active_check += ACTORMOVETICS;
}
void
AdjustActiveRange(PLAYERp pp, DSWActor* actor, int dist)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
SPRITEp psp = &pp->Actor()->s();
int look_height;
// do no FAFcansee before it is time
if (u->wait_active_check < ACTIVE_CHECK_TIME)
return;
u->wait_active_check = 0;
// check aboslute max
if (dist > MAX_ACTIVE_RANGE)
return;
// do not do a FAFcansee if your already active
// Actor only becomes INACTIVE in DoActorDecision
if (TEST(u->Flags, SPR_ACTIVE))
return;
//
// From this point on Actor is INACTIVE
//
// if actor can still see the player
look_height = SPRITEp_TOS(sp);
if (FAFcansee(sp->x, sp->y, look_height, sp->sectnum, psp->x, psp->y, SPRITEp_UPPER(psp), psp->sectnum))
{
// Player is visible
// adjust update range of this sprite
// some huge distance
u->active_range = 75000;
// sprite is AWARE
SET(u->Flags, SPR_ACTIVE);
u->inactive_time = 0;
}
}
/*
!AIC KEY - Reads state tables for animation frame transitions and handles
calling animators, QUICK_CALLS, etc. This is handled for many types of
sprites not just actors.
*/
int StateControl(DSWActor* actor)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
short StateTics;
if (!u->State)
{
ASSERT(u->ActorActionFunc);
(u->ActorActionFunc)(actor);
return 0;
}
if (sp->statnum >= STAT_SKIP4_START && sp->statnum <= STAT_SKIP4_END)
u->Tics += ACTORMOVETICS * 2;
else
u->Tics += ACTORMOVETICS;
// Skip states if too much time has passed
while (u->Tics >= TEST(u->State->Tics, SF_TICS_MASK))
{
StateTics = TEST(u->State->Tics, SF_TICS_MASK);
if (TEST(u->State->Tics, SF_TIC_ADJUST))
{
ASSERT(u->Attrib);
ASSERT(u->speed < MAX_SPEED);
ASSERT(StateTics > -u->Attrib->TicAdjust[u->speed]);
StateTics += u->Attrib->TicAdjust[u->speed];
}
// Set Tics
u->Tics -= StateTics;
// Transition to the next state
u->State = u->State->NextState;
// Look for flags embedded into the Tics variable
while (TEST(u->State->Tics, SF_QUICK_CALL))
{
// Call it once and go to the next state
(*u->State->Animator)(actor);
ASSERT(u); //put this in to see if actor was getting killed with in his QUICK_CALL state
if (!u)
break;
// if still on the same QUICK_CALL should you
// go to the next state.
if (TEST(u->State->Tics, SF_QUICK_CALL))
u->State = u->State->NextState;
}
if (!u)
break;
if (!u->State->Pic)
{
NewStateGroup(actor, (STATEp *) u->State->NextState);
}
}
if (u)
{
ASSERT(u->State);
// Set picnum to the correct pic
if (TEST(u->State->Tics, SF_WALL_STATE))
{
ASSERT(u->WallP);
u->WallP->picnum = u->State->Pic;
}
else
{
//u->SpriteP->picnum = u->State->Pic;
if (u->RotNum > 1)
sp->picnum = u->Rot[0]->Pic;
else
sp->picnum = u->State->Pic;
}
// Call the correct animator
if (u->State->Animator && u->State->Animator != NullAnimator)
(*u->State->Animator)(actor);
}
return 0;
}
void SpriteControl(void)
{
int32_t stat;
SPRITEp sp;
USERp u;
short pnum, CloseToPlayer;
PLAYERp pp;
int tx, ty, tmin, dist;
short StateTics;
SWStatIterator it(STAT_MISC);
while (auto actor = it.Next())
{
StateControl(actor);
}
// Items and skip2 things
if (MoveSkip2 == 0)
{
for (stat = STAT_SKIP2_START + 1; stat <= STAT_SKIP2_END; stat++)
{
it.Reset(stat);
while (auto actor = it.Next())
{
StateControl(actor);
}
}
}
if (MoveSkip2 == 0) // limit to 20 times a second
{
// move bad guys around
it.Reset(STAT_ENEMY);
while (auto actor = it.Next())
{
if (!actor->hasU()) continue;
u = actor->u();
sp = &actor->s();
CloseToPlayer = false;
ProcessActiveVars(actor);
TRAVERSE_CONNECT(pnum)
{
pp = &Player[pnum];
// Only update the ones closest
DISTANCE(pp->posx, pp->posy, sp->x, sp->y, dist, tx, ty, tmin);
AdjustActiveRange(pp, actor, dist);
if (dist < u->active_range)
{
CloseToPlayer = true;
}
}
RESET(u->Flags, SPR_MOVED);
// Only update the ones close to ANY player
if (CloseToPlayer)
{
StateControl(actor);
}
else
{
// to far away to be attacked
RESET(u->Flags, SPR_ATTACKED);
}
}
}
// Skip4 things
if (MoveSkip4 == 0) // limit to 10 times a second
{
for (stat = STAT_SKIP4_START; stat <= STAT_SKIP4_END; stat++)
{
it.Reset(stat);
while (auto actor = it.Next())
{
StateControl(actor);
}
}
}
it.Reset(STAT_NO_STATE);
while (auto actor = it.Next())
{
auto u = actor->u();
if (u && u->ActorActionFunc)
u->ActorActionFunc(actor);
}
if (MoveSkip8 == 0)
{
it.Reset(STAT_STATIC_FIRE);
while (auto itActor = it.Next())
{
DoStaticFlamesDamage(itActor);
}
}
if (MoveSkip4 == 0) // limit to 10 times a second
{
it.Reset(STAT_WALLBLOOD_QUEUE);
while (auto actor = it.Next())
{
StateControl(actor);
}
}
// vator/rotator/spike/slidor all have some code to
// prevent calling of the action func()
it.Reset(STAT_VATOR);
while (auto actor = it.Next())
{
u = actor->u();
if (u == 0)
continue;
if (u->Tics)
{
if ((u->Tics -= synctics) <= 0)
SetVatorActive(actor);
else
continue;
}
if (!TEST(u->Flags, SPR_ACTIVE))
continue;
u->ActorActionFunc(actor);
}
it.Reset(STAT_SPIKE);
while (auto actor = it.Next())
{
u = actor->u();
if (u->Tics)
{
if ((u->Tics -= synctics) <= 0)
SetSpikeActive(actor);
else
continue;
}
if (!TEST(u->Flags, SPR_ACTIVE))
continue;
u->ActorActionFunc(actor);
}
it.Reset(STAT_ROTATOR);
while (auto actor = it.Next())
{
u = actor->u();
if (u->Tics)
{
if ((u->Tics -= synctics) <= 0)
SetRotatorActive(actor);
else
continue;
}
if (!TEST(u->Flags, SPR_ACTIVE))
continue;
u->ActorActionFunc(actor);
}
it.Reset(STAT_SLIDOR);
while (auto actor = it.Next())
{
u = actor->u();
if (u->Tics)
{
if ((u->Tics -= synctics) <= 0)
SetSlidorActive(actor);
else
continue;
}
if (!TEST(u->Flags, SPR_ACTIVE))
continue;
u->ActorActionFunc(actor);
}
it.Reset(STAT_SUICIDE);
while (auto actor = it.Next())
{
KillActor(actor);
}
}
//
// This moves an actor about with FAFgetzrange clip adjustment
//
/*
!AIC KEY - calls clipmove - Look through and try to understatnd Should
hopefully never have to change this. Its very delicate.
*/
Collision move_sprite(DSWActor* actor, int xchange, int ychange, int zchange, int ceildist, int flordist, uint32_t cliptype, int numtics)
{
int retval=0, zh;
int dasectnum;
short tempshort;
SPRITEp spr = &actor->s();
USERp u = actor->u();
short lastsectnum;
ASSERT(actor->hasU());
vec3_t clippos = spr->pos;
// Can't modify sprite sectors
// directly becuase of linked lists
dasectnum = lastsectnum = spr->sectnum;
if (TEST(spr->cstat, CSTAT_SPRITE_YCENTER))
{
zh = 0;
}
else
{
// move the center point up for moving
zh = u->zclip;
clippos.z -= zh;
}
// ASSERT(inside(spr->x,spr->y,dasectnum));
retval = clipmove(&clippos, &dasectnum,
((xchange * numtics) << 11), ((ychange * numtics) << 11),
(((int) spr->clipdist) << 2), ceildist, flordist, cliptype, 1);
spr->pos.vec2 = clippos.vec2;
if (dasectnum < 0)
{
retval = HIT_WALL;
//ASSERT(true == false);
return retval;
}
if ((dasectnum != spr->sectnum) && (dasectnum >= 0))
ChangeActorSect(actor, dasectnum);
// took this out - may not be to relevant anymore
//ASSERT(inside(spr->x,spr->y,dasectnum));
// Set the blocking bit to 0 temporarly so FAFgetzrange doesn't pick
// up its own sprite
tempshort = spr->cstat;
spr->cstat = 0;
//RESET(spr->cstat, CSTAT_SPRITE_BLOCK);
// I subtracted 8 from the clipdist because actors kept going up on
// ledges they were not supposed to go up on. Did the same for the
// player. Seems to work ok!
vec3_t pos = spr->pos;
pos.z -= zh + 1;
FAFgetzrange(pos, spr->sectnum,
&globhiz, &globhihit, &globloz, &globlohit,
(((int) spr->clipdist) << 2) - GETZRANGE_CLIP_ADJ, cliptype);
spr->cstat = tempshort;
// !AIC - puts getzrange results into USER varaible u->loz, u->hiz, u->lo_sectp, u->hi_sectp, etc.
// Takes info from global variables
DoActorGlobZ(actor);
clippos.z = spr->z + ((zchange * numtics) >> 3);
// test for hitting ceiling or floor
if ((clippos.z - zh <= globhiz) || (clippos.z - zh > globloz))
{
if (retval == 0)
{
if (TEST(u->Flags, SPR_CLIMBING))
{
spr->z = clippos.z;
return 0;
}
retval = HIT_SECTOR|dasectnum;
}
}
else
{
spr->z = clippos.z;
}
// extra processing for Stacks and warping
if (FAF_ConnectArea(spr->sectnum))
SetActorZ(actor, &spr->pos);
if (TEST(sector[spr->sectnum].extra, SECTFX_WARP_SECTOR))
{
DSWActor* sp_warp;
if ((sp_warp = WarpPlane(&spr->x, &spr->y, &spr->z, &dasectnum)))
{
ActorWarpUpdatePos(actor, dasectnum);
ActorWarpType(actor, sp_warp);
}
if (spr->sectnum != lastsectnum)
{
if ((sp_warp = Warp(&spr->x, &spr->y, &spr->z, &dasectnum)))
{
ActorWarpUpdatePos(actor, dasectnum);
ActorWarpType(actor, sp_warp);
}
}
}
return Collision(retval);
}
void MissileWarpUpdatePos(DSWActor* actor, short sectnum)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
sp->backuppos();
u->oz = sp->oz;
ChangeActorSect(actor, sectnum);
MissileZrange(actor);
}
void ActorWarpUpdatePos(DSWActor* actor, short sectnum)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
sp->backuppos();
u->oz = sp->oz;
ChangeActorSect(actor, sectnum);
DoActorZrange(actor);
}
void MissileWarpType(DSWActor* sp, DSWActor* act_warp)
{
auto sp_warp = &act_warp->s();
switch (SP_TAG1(sp_warp))
{
case WARP_CEILING_PLANE:
case WARP_FLOOR_PLANE:
return;
}
switch (SP_TAG3(sp_warp))
{
case 1:
break;
default:
PlaySound(DIGI_ITEM_SPAWN, sp, v3df_none);
DoSpawnItemTeleporterEffect(&sp->s());
break;
}
}
void ActorWarpType(DSWActor* sp, DSWActor* act_warp)
{
auto sp_warp = &act_warp->s();
switch (SP_TAG3(sp_warp))
{
case 1:
break;
default:
PlaySound(DIGI_ITEM_SPAWN, sp, v3df_none);
DoSpawnTeleporterEffectPlace(sp);
break;
}
}
//
// This moves a small projectile with FAFgetzrangepoint
//
int MissileWaterAdjust(DSWActor* actor)
{
USERp u = actor->u();
if (u->lo_sectp)
{
SECT_USERp sectu = SectUser[u->lo_sectp - sector].Data();
if (sectu && FixedToInt(sectu->depth_fixed))
u->loz -= Z(FixedToInt(sectu->depth_fixed));
}
return 0;
}
int
MissileZrange(DSWActor* actor)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
short tempshort;
// Set the blocking bit to 0 temporarly so FAFgetzrange doesn't pick
// up its own sprite
tempshort = sp->cstat;
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
FAFgetzrangepoint(sp->x, sp->y, sp->z - 1, sp->sectnum,
&globhiz, &globhihit, &globloz, &globlohit);
sp->cstat = tempshort;
DoActorGlobZ(actor);
return 0;
}
Collision move_missile(DSWActor* actor, int xchange, int ychange, int zchange, int ceildist, int flordist, uint32_t cliptype, int numtics)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
int retval, zh;
int dasectnum, tempshort;
short lastsectnum;
ASSERT(actor->hasU());
vec3_t clippos = sp->pos;
// Can't modify sprite sectors
// directly becuase of linked lists
dasectnum = lastsectnum = sp->sectnum;
if (TEST(sp->cstat, CSTAT_SPRITE_YCENTER))
{
zh = 0;
}
else
{
zh = u->zclip;
clippos.z -= zh;
}
// ASSERT(inside(sp->x,sp->y,dasectnum));
retval = clipmove(&clippos, &dasectnum,
((xchange * numtics) << 11), ((ychange * numtics) << 11),
(((int) sp->clipdist) << 2), ceildist, flordist, cliptype, 1);
sp->pos.vec2 = clippos.vec2;
if (dasectnum < 0)
{
// we've gone beyond a white wall - kill it
retval = 0;
SET(retval, HIT_PLAX_WALL);
return retval;
}
// took this out - may not be to relevant anymore
//ASSERT(inside(sp->x,sp->y,dasectnum));
if ((dasectnum != sp->sectnum) && (dasectnum >= 0))
ChangeActorSect(actor, dasectnum);
// Set the blocking bit to 0 temporarly so FAFgetzrange doesn't pick
// up its own sprite
tempshort = sp->cstat;
RESET(sp->cstat, CSTAT_SPRITE_BLOCK);
FAFgetzrangepoint(sp->x, sp->y, sp->z - 1, sp->sectnum,
&globhiz, &globhihit, &globloz, &globlohit);
sp->cstat = tempshort;
DoActorGlobZ(actor);
// getzrangepoint moves water down
// missiles don't need the water to be down
MissileWaterAdjust(actor);
clippos.z = sp->z + ((zchange * numtics) >> 3);
// NOTE: this does not tell you when you hit a floor sprite
// this case is currently treated like it hit a sector
// test for hitting ceiling or floor
if (clippos.z - zh <= u->hiz + ceildist)
{
// normal code
sp->z = u->hiz + zh + ceildist;
if (retval == 0)
retval = dasectnum|HIT_SECTOR;
}
else if (clippos.z - zh > u->loz - flordist)
{
sp->z = u->loz + zh - flordist;
if (retval == 0)
retval = dasectnum|HIT_SECTOR;
}
else
{
sp->z = clippos.z;
}
if (FAF_ConnectArea(sp->sectnum))
SetActorZ(actor, &sp->pos);
if (TEST(sector[sp->sectnum].extra, SECTFX_WARP_SECTOR))
{
DSWActor* sp_warp;
if ((sp_warp = WarpPlane(&sp->x, &sp->y, &sp->z, &dasectnum)))
{
MissileWarpUpdatePos(actor, dasectnum);
MissileWarpType(actor, sp_warp);
}
if (sp->sectnum != lastsectnum)
{
if ((sp_warp = Warp(&sp->x, &sp->y, &sp->z, &dasectnum)))
{
MissileWarpUpdatePos(actor, dasectnum);
MissileWarpType(actor, sp_warp);
}
}
}
if (retval && TEST(sector[sp->sectnum].ceilingstat, CEILING_STAT_PLAX))
{
if (sp->z < sector[sp->sectnum].ceilingz)
{
RESET(retval, HIT_WALL|HIT_SECTOR);
SET(retval, HIT_PLAX_WALL);
}
}
if (retval && TEST(sector[sp->sectnum].floorstat, FLOOR_STAT_PLAX))
{
if (sp->z > sector[sp->sectnum].floorz)
{
RESET(retval, HIT_WALL|HIT_SECTOR);
SET(retval, HIT_PLAX_WALL);
}
}
return Collision(retval);
}
Collision move_ground_missile(DSWActor* actor, int xchange, int ychange, int ceildist, int flordist, uint32_t cliptype, int numtics)
{
USERp u = actor->u();
SPRITEp sp = &actor->s();
int daz;
int retval=0;
int dasectnum;
short lastsectnum;
int ox,oy;
ASSERT(actor->hasU());
// Can't modify sprite sectors
// directly becuase of linked lists
dasectnum = lastsectnum = sp->sectnum;
vec3_t opos = sp->pos;
daz = sp->z;
// climbing a wall
if (u->z_tgt)
{
if (labs(u->z_tgt - sp->z) > Z(40))
{
if (u->z_tgt > sp->z)
{
sp->z += Z(30);
return retval;
}
else
{
sp->z -= Z(30);
return retval;
}
}
else
u->z_tgt = 0;
}
sp->x += xchange/2;
sp->y += ychange/2;
updatesector(sp->x, sp->y, &dasectnum);
if (dasectnum < 0)
{
// back up and try again
dasectnum = lastsectnum = sp->sectnum;
opos = sp->pos;
opos.z = daz;
retval = clipmove(&opos, &dasectnum,
((xchange * numtics) << 11), ((ychange * numtics) << 11),
(((int) sp->clipdist) << 2), ceildist, flordist, cliptype, 1);
sp->pos.vec2 = opos.vec2;
}
if (dasectnum < 0)
{
// we've gone beyond a white wall - kill it
retval = 0;
SET(retval, HIT_PLAX_WALL);
return retval;
}
if (retval) // ran into a white wall
{
//int new_loz,new_hiz;
// back up and try to clip UP
//dasectnum = lastsectnum = sp->sectnum;
//sp->x = ox;
//sp->y = oy;
#if 0
getzsofslope(dasectnum, sp->x, sp->y, &new_hiz, &new_loz);
if (labs(sp->z - new_hiz) > Z(40))
{
u->z_tgt = new_hiz;
retval = 0;
return retval;
}
else
#endif
return retval;
}
u->z_tgt = 0;
if ((dasectnum != sp->sectnum) && (dasectnum >= 0))
{
int new_loz,new_hiz;
getzsofslope(dasectnum, sp->x, sp->y, &new_hiz, &new_loz);
sp->z = new_loz;
#if 0
if (labs(sp->z - new_loz) > Z(40))
{
if (new_loz > sp->z)
{
// travelling DOWN
u->z_tgt = new_loz;
changespritesect(spritenum, dasectnum);
getzsofslope(sp->sectnum, sp->x, sp->y, &u->hiz, &u->loz);
u->hi_sectp = u->lo_sectp = &sector[sp->sectnum];
u->highActor = u->lowActor = nullptr;
return retval;
}
else
{
// travelling UP
u->z_tgt = new_loz;
// back up and climb wall
dasectnum = lastsectnum = sp->sectnum;
sp->x = ox;
sp->y = oy;
return retval;
}
}
else
{
u->z_tgt = 0;
}
#endif
ChangeActorSect(actor, dasectnum);
}
getzsofslope(sp->sectnum, sp->x, sp->y, &u->hiz, &u->loz);
u->hi_sectp = u->lo_sectp = &sector[sp->sectnum];
u->highActor = nullptr; u->lowActor = nullptr;
sp->z = u->loz - Z(8);
if (labs(u->hiz - u->loz) < Z(12))
{
// we've gone into a very small place - kill it
retval = 0;
SET(retval, HIT_PLAX_WALL);
return retval;
}
if (TEST(sector[sp->sectnum].extra, SECTFX_WARP_SECTOR))
{
DSWActor* sp_warp;
if ((sp_warp = WarpPlane(&sp->x, &sp->y, &sp->z, &dasectnum)))
{
MissileWarpUpdatePos(actor, dasectnum);
MissileWarpType(actor, sp_warp);
}
if (sp->sectnum != lastsectnum)
{
if ((sp_warp = Warp(&sp->x, &sp->y, &sp->z, &dasectnum)))
{
MissileWarpUpdatePos(actor, dasectnum);
MissileWarpType(actor, sp_warp);
}
}
}
return Collision(retval);
}
#include "saveable.h"
static saveable_code saveable_sprite_code[] =
{
SAVE_CODE(DoGrating),
SAVE_CODE(DoKey),
SAVE_CODE(DoCoin),
SAVE_CODE(DoGet),
};
static saveable_data saveable_sprite_data[] =
{
SAVE_DATA(Track),
SAVE_DATA(SectorObject),
SAVE_DATA(s_DebrisNinja),
SAVE_DATA(s_DebrisRat),
SAVE_DATA(s_DebrisCrab),
SAVE_DATA(s_DebrisStarFish),
SAVE_DATA(s_RepairKit),
SAVE_DATA(s_GoldSkelKey),
SAVE_DATA(s_BlueKey),
SAVE_DATA(s_BlueCard),
SAVE_DATA(s_SilverSkelKey),
SAVE_DATA(s_RedKey),
SAVE_DATA(s_RedCard),
SAVE_DATA(s_BronzeSkelKey),
SAVE_DATA(s_GreenKey),
SAVE_DATA(s_GreenCard),
SAVE_DATA(s_RedSkelKey),
SAVE_DATA(s_YellowKey),
SAVE_DATA(s_YellowCard),
SAVE_DATA(s_Key),
SAVE_DATA(s_BlueKey),
SAVE_DATA(s_RedKey),
SAVE_DATA(s_GreenKey),
SAVE_DATA(s_YellowKey),
SAVE_DATA(s_Key),
/*
SAVE_DATA(s_BlueKeyStatue),
SAVE_DATA(s_RedKeyStatue),
SAVE_DATA(s_GreenKeyStatue),
SAVE_DATA(s_YellowKeyStatue),
SAVE_DATA(s_KeyStatue),
*/
SAVE_DATA(s_RedCoin),
SAVE_DATA(s_YellowCoin),
SAVE_DATA(s_GreenCoin),
SAVE_DATA(s_FireFly),
SAVE_DATA(s_IconStar),
SAVE_DATA(s_IconUzi),
SAVE_DATA(s_IconLgUziAmmo),
SAVE_DATA(s_IconUziFloor),
SAVE_DATA(s_IconRocket),
SAVE_DATA(s_IconLgRocket),
SAVE_DATA(s_IconShotgun),
SAVE_DATA(s_IconLgShotshell),
SAVE_DATA(s_IconAutoRiot),
SAVE_DATA(s_IconGrenadeLauncher),
SAVE_DATA(s_IconLgGrenade),
SAVE_DATA(s_IconLgMine),
SAVE_DATA(s_IconGuardHead),
SAVE_DATA(s_IconFireballLgAmmo),
SAVE_DATA(s_IconHeart),
SAVE_DATA(s_IconHeartLgAmmo),
SAVE_DATA(s_IconMicroGun),
SAVE_DATA(s_IconMicroBattery),
SAVE_DATA(s_IconRailGun),
SAVE_DATA(s_IconRailAmmo),
SAVE_DATA(s_IconElectro),
SAVE_DATA(s_IconSpell),
SAVE_DATA(s_IconArmor),
SAVE_DATA(s_IconMedkit),
SAVE_DATA(s_IconChemBomb),
SAVE_DATA(s_IconFlashBomb),
SAVE_DATA(s_IconNuke),
SAVE_DATA(s_IconCaltrops),
SAVE_DATA(s_IconSmMedkit),
SAVE_DATA(s_IconBooster),
SAVE_DATA(s_IconHeatCard),
//SAVE_DATA(s_IconEnvironSuit),
SAVE_DATA(s_IconCloak),
SAVE_DATA(s_IconFly),
SAVE_DATA(s_IconNightVision),
SAVE_DATA(s_IconFlag),
};
saveable_module saveable_sprite =
{
// code
saveable_sprite_code,
SIZ(saveable_sprite_code),
// data
saveable_sprite_data,
SIZ(saveable_sprite_data)
};
END_SW_NS