mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-14 06:31:34 +00:00
6752 lines
196 KiB
C++
6752 lines
196 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(PLAYER*);
|
|
void InitWeaponUzi(PLAYER*);
|
|
|
|
int MoveSkip4, MoveSkip2, MoveSkip8;
|
|
int MinEnemySkill;
|
|
|
|
extern STATE s_CarryFlag[];
|
|
extern STATE s_CarryFlagNoDet[];
|
|
|
|
// beware of mess... :(
|
|
static int globhiz, globloz;
|
|
static Collision globhihit, globlohit;
|
|
|
|
short wait_active_check_offset;
|
|
int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust;
|
|
void SetSectorWallBits(sectortype* sect, int bit_mask, bool set_sectwall, bool set_nextwall);
|
|
int DoActorDebris(DSWActor* actor);
|
|
void ActorWarpUpdatePos(DSWActor*,sectortype* sect);
|
|
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]}
|
|
};
|
|
|
|
STATE* 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]}
|
|
};
|
|
|
|
STATE* 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]}
|
|
};
|
|
|
|
STATE* 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)
|
|
{
|
|
if (flag && ownr != nullptr && ownr->hasU())
|
|
{
|
|
ownr->user.Flags2 |= (SPR2_CHILDREN);
|
|
}
|
|
child->ownerActor = ownr;
|
|
}
|
|
|
|
|
|
DSWActor* GetOwner(DSWActor* child)
|
|
{
|
|
return child ? child->ownerActor.Get() : nullptr;
|
|
}
|
|
|
|
void ClearOwner(DSWActor* child)
|
|
{
|
|
if (child) child->ownerActor = nullptr;
|
|
}
|
|
|
|
void SetAttach(DSWActor* ownr, DSWActor* child)
|
|
{
|
|
if (child && child->hasU() && ownr->hasU())
|
|
{
|
|
ownr->user.Flags2 |= (SPR2_CHILDREN);
|
|
child->user.attachActor = ownr;
|
|
}
|
|
}
|
|
|
|
void KillActor(DSWActor* actor)
|
|
{
|
|
int i;
|
|
unsigned stat;
|
|
//extern short Zombies;
|
|
|
|
ASSERT(!Prediction);
|
|
|
|
ASSERT(actor->spr.statnum < MAXSTATUS);
|
|
|
|
//////////////////////////////////////////////
|
|
// Check sounds list to kill attached sounds
|
|
DeleteNoSoundOwner(actor);
|
|
DeleteNoFollowSoundOwner(actor);
|
|
//////////////////////////////////////////////
|
|
|
|
if (actor->hasU())
|
|
{
|
|
PLAYER* pp;
|
|
int pnum;
|
|
|
|
// doing a MissileSetPos - don't allow killing
|
|
if (actor->user.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(actor, Interp_Sprite_Z);
|
|
|
|
// adjust sprites attached to sector objects
|
|
if (actor->user.Flags & (SPR_SO_ATTACHED))
|
|
{
|
|
SECTOR_OBJECT* sop;
|
|
int 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 (actor->spr.statnum == STAT_ENEMY)
|
|
{
|
|
if (actor->user.track != -1)
|
|
{
|
|
if (Track[actor->user.track].flags)
|
|
Track[actor->user.track].flags &= ~(TF_TRACK_OCCUPIED);
|
|
}
|
|
}
|
|
|
|
// if missile is heading for the sprite, the missile need to know
|
|
// that it is already dead
|
|
if ((actor->spr.extra & SPRX_PLAYER_OR_ENEMY))
|
|
{
|
|
static int8_t MissileStats[] = {STAT_MISSILE, STAT_MISSILE_SKIP4};
|
|
|
|
for (stat = 0; stat < SIZ(MissileStats); stat++)
|
|
{
|
|
SWStatIterator it(MissileStats[stat]);
|
|
while (auto itActor = it.Next())
|
|
{
|
|
if (!itActor->hasU()) continue;
|
|
if (itActor->user.WpnGoalActor == itActor)
|
|
{
|
|
itActor->user.WpnGoalActor = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// much faster
|
|
if (actor->user.Flags2 & (SPR2_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->user.attachActor == actor)
|
|
{
|
|
itActor->user.attachActor = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actor->spr.statnum == STAT_ENEMY)
|
|
{
|
|
SWStatIterator it(STAT_ENEMY);
|
|
while (auto itActor = it.Next())
|
|
{
|
|
if (itActor->hasU() && itActor->user.targetActor == actor)
|
|
{
|
|
DoActorPickClosePlayer(itActor);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actor->user.flameActor != nullptr)
|
|
{
|
|
SetSuicide(actor->user.flameActor);
|
|
}
|
|
}
|
|
actor->Destroy();
|
|
}
|
|
|
|
void ChangeState(DSWActor* actor, STATE* statep)
|
|
{
|
|
if (!actor->hasU())
|
|
return;
|
|
|
|
actor->user.Tics = 0;
|
|
actor->user.State = actor->user.StateStart = statep;
|
|
// Just in case
|
|
PicAnimOff(actor->user.State->Pic);
|
|
}
|
|
|
|
void change_actor_stat(DSWActor* actor, int stat, bool quick)
|
|
{
|
|
|
|
ChangeActorStat(actor, stat);
|
|
|
|
if (actor->hasU() && !quick)
|
|
{
|
|
actor->user.Flags &= ~(SPR_SKIP2|SPR_SKIP4);
|
|
|
|
if (stat >= STAT_SKIP4_START && stat <= STAT_SKIP4_END)
|
|
actor->user.Flags |= (SPR_SKIP4);
|
|
|
|
if (stat >= STAT_SKIP2_START && stat <= STAT_SKIP2_END)
|
|
actor->user.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;
|
|
actor->user.wait_active_check = wait_active_check_offset;
|
|
// don't do a break here
|
|
actor->user.Flags |= (SPR_SHADOW);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void SpawnUser(DSWActor* actor, short id, STATE* state)
|
|
{
|
|
ASSERT(!Prediction);
|
|
|
|
actor->clearUser(); // make sure to delete old, stale content first!
|
|
actor->allocUser();
|
|
|
|
PRODUCTION_ASSERT(actor->hasU());
|
|
|
|
// be careful State can be nullptr
|
|
actor->user.State = actor->user.StateStart = state;
|
|
|
|
change_actor_stat(actor, actor->spr.statnum);
|
|
|
|
actor->user.ID = id;
|
|
actor->user.Health = 100;
|
|
actor->user.WpnGoalActor = nullptr;
|
|
actor->user.attachActor = nullptr;
|
|
actor->user.track = -1;
|
|
actor->user.targetActor = Player[0].actor;
|
|
actor->user.Radius = 220;
|
|
actor->user.Sibling = -1;
|
|
actor->user.WaitTics = 0;
|
|
actor->user.OverlapZ = Z(4);
|
|
actor->user.bounce = 0;
|
|
|
|
actor->user.motion_blur_num = 0;
|
|
actor->user.motion_blur_dist = 256;
|
|
|
|
actor->backuppos();
|
|
actor->user.oz = actor->opos.Z;
|
|
|
|
actor->user.active_range = MIN_ACTIVE_RANGE;
|
|
|
|
// default
|
|
|
|
// based on clipmove z of 48 pixels off the floor
|
|
actor->user.floor_dist = Z(48) - Z(28);
|
|
actor->user.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
|
|
actor->user.loz = actor->sector()->floorz;
|
|
actor->user.hiz = actor->sector()->ceilingz;
|
|
actor->user.lowActor = nullptr;
|
|
actor->user.highActor = nullptr;
|
|
actor->user.lo_sectp = actor->sector();
|
|
actor->user.hi_sectp = actor->sector();
|
|
}
|
|
|
|
DSWActor* SpawnActor(int stat, int id, STATE* state, sectortype* sect, int x, int y, int z, int init_ang, int vel)
|
|
{
|
|
if (sect == nullptr)
|
|
return nullptr;
|
|
|
|
ASSERT(!Prediction);
|
|
|
|
auto spawnedActor = insertActor(sect, stat);
|
|
|
|
spawnedActor->spr.pos = { x, y, z };
|
|
|
|
SpawnUser(spawnedActor, id, state);
|
|
|
|
// be careful State can be nullptr
|
|
if (spawnedActor->user.State)
|
|
{
|
|
spawnedActor->spr.picnum = spawnedActor->user.State->Pic;
|
|
PicAnimOff(spawnedActor->spr.picnum);
|
|
}
|
|
|
|
spawnedActor->spr.xrepeat = 64;
|
|
spawnedActor->spr.yrepeat = 64;
|
|
spawnedActor->spr.ang = NORM_ANGLE(init_ang);
|
|
spawnedActor->spr.xvel = vel;
|
|
|
|
return spawnedActor;
|
|
}
|
|
|
|
void PicAnimOff(short picnum)
|
|
{
|
|
short anim_type = (picanm[picnum].sf & PICANM_ANIMTYPE_MASK) >> PICANM_ANIMTYPE_SHIFT;
|
|
|
|
ASSERT(picnum >= 0 && picnum < MAXTILES);
|
|
|
|
if (!anim_type)
|
|
return;
|
|
|
|
picanm[picnum].sf &= ~(PICANM_ANIMTYPE_MASK);
|
|
}
|
|
|
|
bool IconSpawn(DSWActor* actor)
|
|
{
|
|
// if multi item and not a modem game
|
|
if ((actor->spr.extra & SPRX_MULTI_ITEM))
|
|
{
|
|
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
|
|
return false;
|
|
}
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN | CSTAT_SPRITE_ONE_SIDE | CSTAT_SPRITE_ALIGNMENT_FLOOR);
|
|
return true;
|
|
}
|
|
|
|
bool ActorTestSpawn(DSWActor* actor)
|
|
{
|
|
if (actor->spr.statnum == STAT_DEFAULT && actor->spr.lotag == TAG_SPAWN_ACTOR)
|
|
{
|
|
auto actorNew = insertActor(actor->sector(), STAT_DEFAULT);
|
|
actorNew->spr = actor->spr;
|
|
change_actor_stat(actorNew, STAT_SPAWN_TRIGGER);
|
|
actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
return false;
|
|
}
|
|
|
|
// Countermeasure for mods that leave the lower skills unpopulated.
|
|
int spawnskill = (actor->spr.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 (((actor->spr.picnum == SERP_RUN_R0 || actor->spr.picnum == SUMO_RUN_R0) && actor->spr.lotag > 0 &&
|
|
actor->spr.lotag != TAG_SPAWN_ACTOR && actor->spr.extra > 0) || actor->spr.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 (actor->spr.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, actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z);
|
|
return true;
|
|
}
|
|
//always spawn girls in addons
|
|
if ((actor->spr.picnum == TOILETGIRL_R0 ||
|
|
actor->spr.picnum == WASHGIRL_R0 ||
|
|
actor->spr.picnum == MECHANICGIRL_R0 ||
|
|
actor->spr.picnum == CARGIRL_R0 ||
|
|
actor->spr.picnum == PRUNEGIRL_R0 ||
|
|
actor->spr.picnum == SAILORGIRL_R0) && (g_gameType & GAMEFLAG_ADDON)) return true;
|
|
|
|
// spawn Bouncing Betty (mine) in TD map 09 Warehouse
|
|
if (actor->spr.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())
|
|
{
|
|
switch (actor->spr.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 = actor->spr.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->spr.picnum;
|
|
switch (picnum)
|
|
{
|
|
case COOLIE_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupCoolie(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case NINJA_RUN_R0:
|
|
case NINJA_CRAWL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupNinja(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case GORO_RUN_R0:
|
|
{
|
|
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:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupEel(actor);
|
|
break;
|
|
}
|
|
|
|
case SUMO_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupSumo(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case ZILLA_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupZilla(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case TOILETGIRL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupToiletGirl(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case WASHGIRL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupWashGirl(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case CARGIRL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupCarGirl(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case MECHANICGIRL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupMechanicGirl(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case SAILORGIRL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupSailorGirl(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case PRUNEGIRL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupPruneGirl(actor);
|
|
|
|
break;
|
|
}
|
|
|
|
case TRASHCAN:
|
|
{
|
|
PicAnimOff(picnum);
|
|
SetupTrashCan(actor);
|
|
break;
|
|
}
|
|
|
|
case BUNNY_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupBunny(actor);
|
|
break;
|
|
}
|
|
|
|
case RIPPER_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupRipper(actor);
|
|
break;
|
|
}
|
|
|
|
case RIPPER2_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupRipper2(actor);
|
|
break;
|
|
}
|
|
|
|
case SERP_RUN_R0:
|
|
{
|
|
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:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupSkel(actor);
|
|
break;
|
|
}
|
|
|
|
case HORNET_RUN_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupHornet(actor);
|
|
break;
|
|
}
|
|
|
|
case SKULL_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupSkull(actor);
|
|
break;
|
|
}
|
|
|
|
case BETTY_R0:
|
|
{
|
|
if (!ActorTestSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
return false;
|
|
}
|
|
|
|
PicAnimOff(picnum);
|
|
SetupBetty(actor);
|
|
break;
|
|
}
|
|
|
|
case 623: // Pachinko win light
|
|
{
|
|
PicAnimOff(picnum);
|
|
SetupPachinkoLight(actor);
|
|
break;
|
|
}
|
|
|
|
case PACHINKO1:
|
|
{
|
|
PicAnimOff(picnum);
|
|
SetupPachinko1(actor);
|
|
break;
|
|
}
|
|
|
|
case PACHINKO2:
|
|
{
|
|
PicAnimOff(picnum);
|
|
SetupPachinko2(actor);
|
|
break;
|
|
}
|
|
|
|
case PACHINKO3:
|
|
{
|
|
PicAnimOff(picnum);
|
|
SetupPachinko3(actor);
|
|
break;
|
|
}
|
|
|
|
case PACHINKO4:
|
|
{
|
|
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)
|
|
{
|
|
change_actor_stat(actor, STAT_ITEM);
|
|
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
actor->user.Radius = 650;
|
|
|
|
DoActorZrange(actor);
|
|
}
|
|
|
|
void PreMapCombineFloors(void)
|
|
{
|
|
const int MAX_FLOORS = 32;
|
|
int i, j, k;
|
|
int base_offset;
|
|
int dx, dy;
|
|
short pnum;
|
|
|
|
struct BOUND_LIST
|
|
{
|
|
DSWActor* offset;
|
|
};
|
|
|
|
BOUND_LIST BoundList[MAX_FLOORS];
|
|
|
|
memset(BoundList, 0, MAX_FLOORS * sizeof(BOUND_LIST));
|
|
|
|
SWStatIterator it(0);
|
|
while (auto actor = it.Next())
|
|
{
|
|
if (actor->spr.picnum != ST1)
|
|
continue;
|
|
|
|
if (actor->spr.hitag == BOUND_FLOOR_OFFSET || actor->spr.hitag == BOUND_FLOOR_BASE_OFFSET)
|
|
{
|
|
ASSERT(actor->spr.lotag < MAX_FLOORS);
|
|
BoundList[actor->spr.lotag].offset = actor;
|
|
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->spr.hitag == BOUND_FLOOR_BASE_OFFSET)
|
|
{
|
|
base_offset = i;
|
|
continue;
|
|
}
|
|
|
|
dx = BoundList[base_offset].offset->spr.pos.X - BoundList[i].offset->spr.pos.X;
|
|
dy = BoundList[base_offset].offset->spr.pos.Y - BoundList[i].offset->spr.pos.Y;
|
|
|
|
BFSSectorSearch search(BoundList[i].offset->sector());
|
|
while (auto dasect = search.GetNext())
|
|
{
|
|
SWSectIterator it2(dasect);
|
|
while (auto jActor = it2.Next())
|
|
{
|
|
jActor->spr.pos.X += dx;
|
|
jActor->spr.pos.Y += dy;
|
|
}
|
|
|
|
for (auto& wal : wallsofsector(dasect))
|
|
{
|
|
wal.move(wal.pos.X + dx, wal.pos.Y + dy);
|
|
|
|
if (wal.twoSided())
|
|
search.Add(wal.nextSector());
|
|
}
|
|
}
|
|
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
PLAYER* pp = &Player[pnum];
|
|
auto dasect = pp->cursector;
|
|
search.Rewind();
|
|
while (auto itsect = search.GetNext())
|
|
{
|
|
if (itsect == dasect)
|
|
{
|
|
pp->pos.X += dx;
|
|
pp->pos.Y += dy;
|
|
pp->opos.X = pp->oldpos.X = pp->pos.X;
|
|
pp->opos.Y = pp->oldpos.Y = pp->pos.Y;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// get rid of the sprites used
|
|
it.Reset(STAT_FAF);
|
|
while (auto actor = it.Next())
|
|
{
|
|
KillActor(actor);
|
|
}
|
|
}
|
|
|
|
|
|
void SpriteSetupPost(void)
|
|
{
|
|
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->sector());
|
|
while (auto jActor = it.Next())
|
|
{
|
|
if (jActor->spr.picnum == ST1)
|
|
continue;
|
|
|
|
if ((jActor->spr.cstat & (CSTAT_SPRITE_ALIGNMENT_WALL|CSTAT_SPRITE_ALIGNMENT_FLOOR)))
|
|
continue;
|
|
|
|
if (jActor->hasU())
|
|
continue;
|
|
|
|
getzsofslopeptr(jActor->sector(), jActor->spr.pos.X, jActor->spr.pos.Y, &cz, &fz);
|
|
if (labs(jActor->spr.pos.Z - fz) > Z(4))
|
|
continue;
|
|
|
|
SpawnUser(jActor, 0, nullptr);
|
|
change_actor_stat(jActor, STAT_NO_STATE);
|
|
jActor->user.ceiling_dist = Z(4);
|
|
jActor->user.floor_dist = -Z(2);
|
|
|
|
jActor->user.ActorActionFunc = DoActorDebris;
|
|
|
|
jActor->spr.cstat |= CSTAT_SPRITE_BREAKABLE;
|
|
jActor->spr.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(auto& sect: sector)
|
|
{
|
|
sect.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())
|
|
{
|
|
// not used yetv
|
|
getzsofslopeptr(actor->sector(), actor->spr.pos.X, actor->spr.pos.Y, &cz, &fz);
|
|
if (actor->spr.pos.Z > ((cz + fz) >> 1))
|
|
{
|
|
// closer to a floor
|
|
actor->spr.cstat |= (CSTAT_SPRITE_CLOSE_FLOOR);
|
|
}
|
|
|
|
// CSTAT_SPIN is insupported - get rid of it
|
|
if ((actor->spr.cstat & (CSTAT_SPRITE_ALIGNMENT_MASK)) == CSTAT_SPRITE_ALIGNMENT_SLAB)
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_ALIGNMENT_SLAB);
|
|
|
|
// if BLOCK is set set BLOCK_HITSCAN
|
|
// Hope this doesn't screw up anything
|
|
if (actor->spr.cstat & (CSTAT_SPRITE_BLOCK))
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
////////////////////////////////////////////
|
|
//
|
|
// BREAKABLE CHECK
|
|
//
|
|
////////////////////////////////////////////
|
|
|
|
// USER SETUP - TAGGED BY USER
|
|
// Non ST1 sprites that are tagged like them
|
|
if (TEST_BOOL1(actor) && actor->spr.picnum != ST1)
|
|
{
|
|
actor->spr.extra &= ~(
|
|
SPRX_BOOL4|
|
|
SPRX_BOOL5|
|
|
SPRX_BOOL6|
|
|
SPRX_BOOL7|
|
|
SPRX_BOOL8|
|
|
SPRX_BOOL9|
|
|
SPRX_BOOL10);
|
|
|
|
switch (actor->spr.hitag)
|
|
{
|
|
case BREAKABLE:
|
|
// need something that tells missiles to hit them
|
|
// but allows actors to move through them
|
|
actor->spr.clipdist = ActorSizeX(actor);
|
|
actor->spr.extra |= (SPRX_BREAKABLE);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BREAKABLE);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// BREAK SETUP TABLE AUTOMATED
|
|
SetupSpriteForBreak(actor);
|
|
}
|
|
|
|
if (actor->spr.lotag == TAG_SPRITE_HIT_MATCH)
|
|
{
|
|
// if multi item and not a modem game
|
|
if ((actor->spr.extra & SPRX_MULTI_ITEM))
|
|
{
|
|
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
|
|
{
|
|
KillActor(actor);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
// crack sprite
|
|
if (actor->spr.picnum == 80)
|
|
{
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE);;
|
|
}
|
|
else
|
|
{
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK_HITSCAN|CSTAT_SPRITE_BLOCK_MISSILE);;
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);;
|
|
}
|
|
|
|
if (SP_TAG8(actor) & BIT(0))
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
|
|
if (SP_TAG8(actor) & BIT(1))
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_INVISIBLE);
|
|
|
|
change_actor_stat(actor, STAT_SPRITE_HIT_MATCH);
|
|
continue;
|
|
}
|
|
|
|
if (actor->spr.picnum >= TRACK_SPRITE &&
|
|
actor->spr.picnum <= TRACK_SPRITE + MAX_TRACKS)
|
|
{
|
|
short track_num;
|
|
|
|
// skip this sprite, just for numbering walls/sectors
|
|
if (actor->spr.cstat & (CSTAT_SPRITE_ALIGNMENT_WALL))
|
|
continue;
|
|
|
|
track_num = actor->spr.picnum - TRACK_SPRITE + 0;
|
|
|
|
change_actor_stat(actor, STAT_TRACK + track_num);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (ActorSpawn(actor))
|
|
continue;
|
|
|
|
switch (actor->spr.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:
|
|
{
|
|
sectortype* sectp = actor->sector();
|
|
short tag;
|
|
short bit;
|
|
|
|
// get rid of defaults
|
|
if (SP_TAG3(actor) == 32)
|
|
SP_TAG3(actor) = 0;
|
|
|
|
tag = actor->spr.hitag;
|
|
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
actor->spr.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);
|
|
|
|
actor->sector()->extra |= (bit);
|
|
|
|
if (bit & (SECTFX_SINK))
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->depth_fixed = IntToFixed(actor->spr.lotag);
|
|
KillActor(actor);
|
|
}
|
|
else if (bit & (SECTFX_OPERATIONAL))
|
|
{
|
|
KillActor(actor);
|
|
}
|
|
else if (bit & (SECTFX_CURRENT))
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->speed = actor->spr.lotag;
|
|
sectp->ang = actor->spr.ang;
|
|
KillActor(actor);
|
|
}
|
|
else if (bit & (SECTFX_NO_RIDE))
|
|
{
|
|
change_actor_stat(actor, STAT_NO_RIDE);
|
|
}
|
|
else if (bit & (SECTFX_DIVE_AREA))
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->number = actor->spr.lotag;
|
|
change_actor_stat(actor, STAT_DIVE_AREA);
|
|
}
|
|
else if (bit & (SECTFX_UNDERWATER))
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->number = actor->spr.lotag;
|
|
change_actor_stat(actor, STAT_UNDERWATER);
|
|
}
|
|
else if (bit & (SECTFX_UNDERWATER2))
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->number = actor->spr.lotag;
|
|
if (actor->spr.clipdist == 1)
|
|
sectp->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 + actor->spr.lotag);
|
|
break;
|
|
case MULTI_COOPERATIVE_START:
|
|
change_actor_stat(actor, STAT_CO_OP_START + actor->spr.lotag);
|
|
break;
|
|
#endif
|
|
|
|
case SECT_MATCH:
|
|
sectp->u_defined = true;
|
|
sectp->number = actor->spr.lotag;
|
|
|
|
KillActor(actor);
|
|
break;
|
|
|
|
case SLIDE_SECTOR:
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_SLIDE_SECTOR);
|
|
sectp->speed = SP_TAG2(actor);
|
|
KillActor(actor);
|
|
break;
|
|
|
|
case SECT_DAMAGE:
|
|
{
|
|
sectp->u_defined = true;
|
|
if (TEST_BOOL1(actor))
|
|
sectp->flags |= (SECTFU_DAMAGE_ABOVE_SECTOR);
|
|
sectp->damage = actor->spr.lotag;
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case PARALLAX_LEVEL:
|
|
{
|
|
parallaxyscale_override = 8192;
|
|
pskybits_override = actor->spr.lotag;
|
|
if (SP_TAG4(actor) > 2048)
|
|
parallaxyscale_override = SP_TAG4(actor);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case BREAKABLE:
|
|
// used for wall info
|
|
change_actor_stat(actor, STAT_BREAKABLE);
|
|
break;
|
|
|
|
case SECT_DONT_COPY_PALETTE:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_DONT_COPY_PALETTE);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SECT_FLOOR_PAN:
|
|
{
|
|
// if moves with SO
|
|
if (TEST_BOOL1(actor))
|
|
actor->spr.xvel = 0;
|
|
else
|
|
actor->spr.xvel = actor->spr.lotag;
|
|
|
|
StartInterpolation(actor->sector(), Interp_Sect_FloorPanX);
|
|
StartInterpolation(actor->sector(), Interp_Sect_FloorPanY);
|
|
change_actor_stat(actor, STAT_FLOOR_PAN);
|
|
break;
|
|
}
|
|
|
|
case SECT_CEILING_PAN:
|
|
{
|
|
// if moves with SO
|
|
if (TEST_BOOL1(actor))
|
|
actor->spr.xvel = 0;
|
|
else
|
|
actor->spr.xvel = actor->spr.lotag;
|
|
StartInterpolation(actor->sector(), Interp_Sect_CeilingPanX);
|
|
StartInterpolation(actor->sector(), Interp_Sect_CeilingPanY);
|
|
change_actor_stat(actor, STAT_CEILING_PAN);
|
|
break;
|
|
}
|
|
|
|
case SECT_WALL_PAN_SPEED:
|
|
{
|
|
vec3_t hit_pos = { actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - Z(8) };
|
|
HitInfo hit{};
|
|
|
|
hitscan(hit_pos, actor->sector(), // Start position
|
|
{ bcos(actor->spr.ang), bsin(actor->spr.ang), 0 }, hit, CLIPMASK_MISSILE);
|
|
|
|
if (hit.hitWall == nullptr)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
actor->tempwall = hit.hitWall;
|
|
// if moves with SO
|
|
if (TEST_BOOL1(actor))
|
|
actor->spr.xvel = 0;
|
|
else
|
|
actor->spr.xvel = actor->spr.lotag;
|
|
actor->spr.ang = SP_TAG6(actor);
|
|
// attach to the sector that contains the wall
|
|
ChangeActorSect(actor, hit.hitSector);
|
|
StartInterpolation(hit.hitWall, Interp_Wall_PanX);
|
|
StartInterpolation(hit.hitWall, Interp_Wall_PanY);
|
|
change_actor_stat(actor, STAT_WALL_PAN);
|
|
break;
|
|
}
|
|
|
|
case WALL_DONT_STICK:
|
|
{
|
|
vec3_t hit_pos = { actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - Z(8) };
|
|
HitInfo hit{};
|
|
|
|
hitscan(hit_pos, actor->sector(), // Start position
|
|
{ bcos(actor->spr.ang), bsin(actor->spr.ang), 0 }, hit, CLIPMASK_MISSILE);
|
|
|
|
if (hit.hitWall == nullptr)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
hit.hitWall->extra |= WALLFX_DONT_STICK;
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case TRIGGER_SECTOR:
|
|
{
|
|
actor->sector()->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 ((actor->spr.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(actor) == 0 || SP_TAG7(actor) == 1)
|
|
{
|
|
if (SP_TAG3(actor) == 0)
|
|
actor->sector()->ceilingstat |= (CSTAT_SECTOR_FAF_BLOCK_HITSCAN);
|
|
else
|
|
actor->sector()->floorstat |= (CSTAT_SECTOR_FAF_BLOCK_HITSCAN);
|
|
}
|
|
else if (TEST_BOOL1(actor))
|
|
{
|
|
if (SP_TAG3(actor) == 0)
|
|
actor->sector()->ceilingstat |= (CSTAT_SECTOR_FAF_BLOCK_HITSCAN);
|
|
else
|
|
actor->sector()->floorstat |= (CSTAT_SECTOR_FAF_BLOCK_HITSCAN);
|
|
}
|
|
|
|
// copy tag 7 to tag 6 and pre-shift it
|
|
SP_TAG6(actor) = SP_TAG7(actor);
|
|
SP_TAG6(actor) <<= 7;
|
|
change_actor_stat(actor, STAT_CEILING_FLOOR_PIC_OVERRIDE);
|
|
break;
|
|
}
|
|
|
|
case QUAKE_SPOT:
|
|
{
|
|
change_actor_stat(actor, STAT_QUAKE_SPOT);
|
|
SET_SP_TAG13(actor, ((SP_TAG6(actor)*10L) * 120L));
|
|
break;
|
|
}
|
|
|
|
case SECT_CHANGOR:
|
|
{
|
|
change_actor_stat(actor, STAT_CHANGOR);
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
case SECT_DEBRIS_SEWER:
|
|
{
|
|
ANIMATOR DoGenerateSewerDebris;
|
|
|
|
SpawnUser(actor, 0, nullptr);
|
|
|
|
ASSERT(actor->hasU());
|
|
actor->user.RotNum = 0;
|
|
actor->user.WaitTics = actor->spr.lotag * 120;
|
|
|
|
actor->user.ActorActionFunc = DoGenerateSewerDebris;
|
|
|
|
change_actor_stat(actor, STAT_NO_STATE);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case SECT_VATOR:
|
|
{
|
|
short speed,vel,time,type,start_on,floor_vator;
|
|
SpawnUser(actor, 0, nullptr);
|
|
|
|
// vator already set - ceiling AND floor vator
|
|
if ((sectp->extra & SECTFX_VATOR))
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_VATOR_BOTH);
|
|
}
|
|
sectp->extra |= (SECTFX_VATOR);
|
|
SetSectorWallBits(actor->sector(), WALLFX_DONT_STICK, true, true);
|
|
actor->sector()->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(actor);
|
|
speed = SP_TAG4(actor);
|
|
vel = SP_TAG5(actor);
|
|
time = SP_TAG9(actor);
|
|
start_on = !!TEST_BOOL1(actor);
|
|
floor_vator = true;
|
|
if (actor->spr.cstat & (CSTAT_SPRITE_YFLIP))
|
|
floor_vator = false;
|
|
|
|
actor->user.jump_speed = actor->user.vel_tgt = speed;
|
|
actor->user.vel_rate = vel;
|
|
actor->user.WaitTics = time*15; // 1/8 of a sec
|
|
actor->user.Tics = 0;
|
|
|
|
actor->user.Flags |= (SPR_ACTIVE);
|
|
|
|
switch (type)
|
|
{
|
|
case 0:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoVator;
|
|
break;
|
|
case 1:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoVator;
|
|
break;
|
|
case 2:
|
|
actor->user.ActorActionFunc = DoVatorAuto;
|
|
break;
|
|
case 3:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoVatorAuto;
|
|
break;
|
|
}
|
|
|
|
if (floor_vator)
|
|
{
|
|
// start off
|
|
actor->user.pos.Z = sectp->floorz;
|
|
actor->user.z_tgt = actor->spr.pos.Z;
|
|
if (start_on)
|
|
{
|
|
int amt;
|
|
amt = actor->spr.pos.Z - sectp->floorz;
|
|
|
|
// start in the on position
|
|
sectp->addfloorz(amt);
|
|
actor->user.z_tgt = actor->user.pos.Z;
|
|
|
|
MoveSpritesWithSector(actor->sector(), amt, false); // floor
|
|
}
|
|
|
|
// set orig z
|
|
actor->user.oz = actor->opos.Z = sectp->floorz;
|
|
}
|
|
else
|
|
{
|
|
// start off
|
|
actor->user.pos.Z = sectp->ceilingz;
|
|
actor->user.z_tgt = actor->spr.pos.Z;
|
|
if (start_on)
|
|
{
|
|
int amt;
|
|
amt = actor->spr.pos.Z - sectp->ceilingz;
|
|
|
|
// starting in the on position
|
|
sectp->addceilingz(amt);
|
|
actor->user.z_tgt = actor->user.pos.Z;
|
|
|
|
MoveSpritesWithSector(actor->sector(), amt, true); // ceiling
|
|
}
|
|
|
|
// set orig z
|
|
actor->user.oz = actor->opos.Z = sectp->ceilingz;
|
|
}
|
|
|
|
|
|
change_actor_stat(actor, STAT_VATOR);
|
|
break;
|
|
}
|
|
|
|
case SECT_ROTATOR_PIVOT:
|
|
{
|
|
change_actor_stat(actor, STAT_ROTATOR_PIVOT);
|
|
break;
|
|
}
|
|
|
|
case SECT_ROTATOR:
|
|
{
|
|
short time,type;
|
|
short wallcount,startwall,endwall,w;
|
|
SpawnUser(actor, 0, nullptr);
|
|
|
|
SetSectorWallBits(actor->sector(), WALLFX_DONT_STICK, true, true);
|
|
|
|
// need something for this
|
|
sectp->lotag = TAG_ROTATOR;
|
|
sectp->hitag = actor->spr.lotag;
|
|
|
|
type = SP_TAG3(actor);
|
|
time = SP_TAG9(actor);
|
|
|
|
actor->user.WaitTics = time*15; // 1/8 of a sec
|
|
actor->user.Tics = 0;
|
|
|
|
actor->user.rotator.Alloc();
|
|
actor->user.rotator->open_dest = SP_TAG5(actor);
|
|
actor->user.rotator->speed = SP_TAG7(actor);
|
|
actor->user.rotator->vel = SP_TAG8(actor);
|
|
actor->user.rotator->pos = 0; // closed
|
|
actor->user.rotator->tgt = actor->user.rotator->open_dest; // closed
|
|
actor->user.rotator->SetNumWalls(actor->sector()->wallnum);
|
|
|
|
actor->user.rotator->orig_speed = actor->user.rotator->speed;
|
|
|
|
wallcount = 0;
|
|
for(auto& wal : wallsofsector(actor->sector()))
|
|
{
|
|
actor->user.rotator->origX[wallcount] = wal.pos.X;
|
|
actor->user.rotator->origY[wallcount] = wal.pos.Y;
|
|
wallcount++;
|
|
}
|
|
|
|
actor->user.Flags |= (SPR_ACTIVE);
|
|
|
|
switch (type)
|
|
{
|
|
case 0:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoRotator;
|
|
break;
|
|
case 1:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoRotator;
|
|
break;
|
|
}
|
|
|
|
change_actor_stat(actor, STAT_ROTATOR);
|
|
break;
|
|
}
|
|
|
|
case SECT_SLIDOR:
|
|
{
|
|
short time,type;
|
|
|
|
SpawnUser(actor, 0, nullptr);
|
|
|
|
SetSectorWallBits(actor->sector(), WALLFX_DONT_STICK, true, true);
|
|
|
|
// need something for this
|
|
sectp->lotag = TAG_SLIDOR;
|
|
sectp->hitag = actor->spr.lotag;
|
|
|
|
type = SP_TAG3(actor);
|
|
time = SP_TAG9(actor);
|
|
|
|
actor->user.WaitTics = time*15; // 1/8 of a sec
|
|
actor->user.Tics = 0;
|
|
|
|
actor->user.rotator.Alloc();
|
|
actor->user.rotator->open_dest = SP_TAG5(actor);
|
|
actor->user.rotator->speed = SP_TAG7(actor);
|
|
actor->user.rotator->vel = SP_TAG8(actor);
|
|
actor->user.rotator->pos = 0; // closed
|
|
actor->user.rotator->tgt = actor->user.rotator->open_dest; // closed
|
|
actor->user.rotator->ClearWalls();
|
|
actor->user.rotator->orig_speed = actor->user.rotator->speed;
|
|
|
|
actor->user.Flags |= (SPR_ACTIVE);
|
|
|
|
switch (type)
|
|
{
|
|
case 0:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoSlidor;
|
|
break;
|
|
case 1:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoSlidor;
|
|
break;
|
|
}
|
|
|
|
|
|
if (TEST_BOOL5(actor))
|
|
{
|
|
DoSlidorInstantClose(actor);
|
|
}
|
|
|
|
change_actor_stat(actor, STAT_SLIDOR);
|
|
break;
|
|
}
|
|
|
|
case SECT_SPIKE:
|
|
{
|
|
short speed,vel,time,type,start_on,floor_vator;
|
|
int floorz,ceilingz;
|
|
Collision trash;
|
|
SpawnUser(actor, 0, nullptr);
|
|
|
|
SetSectorWallBits(actor->sector(), WALLFX_DONT_STICK, false, true);
|
|
actor->sector()->extra |= (SECTFX_DYNAMIC_AREA);
|
|
|
|
type = SP_TAG3(actor);
|
|
speed = SP_TAG4(actor);
|
|
vel = SP_TAG5(actor);
|
|
time = SP_TAG9(actor);
|
|
start_on = !!TEST_BOOL1(actor);
|
|
floor_vator = true;
|
|
if (actor->spr.cstat & (CSTAT_SPRITE_YFLIP))
|
|
floor_vator = false;
|
|
|
|
actor->user.jump_speed = actor->user.vel_tgt = speed;
|
|
actor->user.vel_rate = vel;
|
|
actor->user.WaitTics = time*15; // 1/8 of a sec
|
|
actor->user.Tics = 0;
|
|
|
|
actor->user.Flags |= (SPR_ACTIVE);
|
|
|
|
switch (type)
|
|
{
|
|
case 0:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoSpike;
|
|
break;
|
|
case 1:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoSpike;
|
|
break;
|
|
case 2:
|
|
actor->user.ActorActionFunc = DoSpikeAuto;
|
|
break;
|
|
case 3:
|
|
actor->user.Flags &= ~(SPR_ACTIVE);
|
|
actor->user.ActorActionFunc = DoSpikeAuto;
|
|
break;
|
|
}
|
|
|
|
getzrangepoint(actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, actor->sector(), &ceilingz, &trash, &floorz, &trash);
|
|
|
|
if (floor_vator)
|
|
{
|
|
actor->user.zclip = floorz;
|
|
|
|
// start off
|
|
actor->user.pos.Z = actor->user.zclip;
|
|
actor->user.z_tgt = actor->spr.pos.Z;
|
|
if (start_on)
|
|
{
|
|
// start in the on position
|
|
actor->user.zclip = actor->spr.pos.Z;
|
|
actor->user.z_tgt = actor->user.pos.Z;
|
|
SpikeAlign(actor);
|
|
}
|
|
|
|
// set orig z
|
|
actor->user.oz = actor->opos.Z = actor->user.zclip;
|
|
}
|
|
else
|
|
{
|
|
actor->user.zclip = ceilingz;
|
|
|
|
// start off
|
|
actor->user.pos.Z = actor->user.zclip;
|
|
actor->user.z_tgt = actor->spr.pos.Z;
|
|
if (start_on)
|
|
{
|
|
// starting in the on position
|
|
actor->user.zclip = actor->spr.pos.Z;
|
|
actor->user.z_tgt = actor->user.pos.Z;
|
|
SpikeAlign(actor);
|
|
}
|
|
|
|
// set orig z
|
|
actor->user.oz = actor->opos.Z = actor->user.zclip;
|
|
}
|
|
|
|
change_actor_stat(actor, STAT_SPIKE);
|
|
break;
|
|
}
|
|
|
|
case LIGHTING:
|
|
{
|
|
int wallcount = 0;
|
|
int8_t* wall_shade;
|
|
|
|
LIGHT_Tics(actor) = 0;
|
|
|
|
if (LIGHT_ShadeInc(actor) == 0)
|
|
LIGHT_ShadeInc(actor) = 1;
|
|
|
|
// save off original floor and ceil shades
|
|
LIGHT_FloorShade(actor) = actor->sector()->floorshade;
|
|
LIGHT_CeilingShade(actor) = actor->sector()->ceilingshade;
|
|
|
|
// count walls of sector
|
|
for(auto& wal : wallsofsector(actor->sector()))
|
|
{
|
|
wallcount++;
|
|
if (TEST_BOOL5(actor))
|
|
{
|
|
if (wal.twoSided())
|
|
wallcount++;
|
|
}
|
|
}
|
|
|
|
SpawnUser(actor, 0, nullptr);
|
|
actor->user.WallShade.Resize(wallcount);
|
|
wallcount = 0;
|
|
wall_shade = actor->user.WallShade.Data();
|
|
|
|
// save off original wall shades
|
|
for(auto& wal : wallsofsector(actor->sector()))
|
|
{
|
|
wall_shade[wallcount] = wal.shade;
|
|
wallcount++;
|
|
if (TEST_BOOL5(actor))
|
|
{
|
|
if (wal.twoSided())
|
|
{
|
|
wall_shade[wallcount] = wal.nextWall()->shade;
|
|
wallcount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
actor->user.spal = actor->spr.pal;
|
|
|
|
// DON'T USE COVER function
|
|
change_actor_stat(actor, STAT_LIGHTING, true);
|
|
break;
|
|
}
|
|
|
|
case LIGHTING_DIFFUSE:
|
|
{
|
|
int wallcount = 0;
|
|
int8_t* wall_shade;
|
|
|
|
LIGHT_Tics(actor) = 0;
|
|
|
|
// save off original floor and ceil shades
|
|
LIGHT_FloorShade(actor) = actor->sector()->floorshade;
|
|
LIGHT_CeilingShade(actor) = actor->sector()->ceilingshade;
|
|
|
|
// count walls of sector
|
|
for (auto& wal : wallsofsector(actor->sector()))
|
|
{
|
|
wallcount++;
|
|
if (TEST_BOOL5(actor))
|
|
{
|
|
if (wal.twoSided())
|
|
wallcount++;
|
|
}
|
|
}
|
|
|
|
// !LIGHT
|
|
// make an wall_shade array and put it in User
|
|
SpawnUser(actor, 0, nullptr);
|
|
actor->user.WallShade.Resize(wallcount);
|
|
wallcount = 0;
|
|
wall_shade = actor->user.WallShade.Data();
|
|
|
|
// save off original wall shades
|
|
for (auto& wal : wallsofsector(actor->sector()))
|
|
{
|
|
wall_shade[wallcount] = wal.shade;
|
|
wallcount++;
|
|
if (TEST_BOOL5(actor))
|
|
{
|
|
if (wal.twoSided())
|
|
{
|
|
wall_shade[wallcount] = wal.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:
|
|
actor->spr.yvel = actor->spr.zvel = 100; //attempt horiz control
|
|
change_actor_stat(actor, STAT_DEMO_CAMERA);
|
|
break;
|
|
|
|
case LAVA_ERUPT:
|
|
{
|
|
|
|
SpawnUser(actor, ST1, nullptr);
|
|
|
|
change_actor_stat(actor, STAT_NO_STATE);
|
|
actor->user.ActorActionFunc = DoLavaErupt;
|
|
|
|
// interval between erupts
|
|
if (SP_TAG10(actor) == 0)
|
|
SP_TAG10(actor) = 20;
|
|
|
|
// interval in seconds
|
|
actor->user.WaitTics = RandomRange(SP_TAG10(actor)) * 120;
|
|
|
|
// time to erupt
|
|
if (SP_TAG9(actor) == 0)
|
|
SP_TAG9(actor) = 10;
|
|
|
|
actor->spr.pos.Z += Z(30);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case SECT_EXPLODING_CEIL_FLOOR:
|
|
{
|
|
SetSectorWallBits(actor->sector(), WALLFX_DONT_STICK, false, true);
|
|
|
|
if ((sectp->floorstat & CSTAT_SECTOR_SLOPE))
|
|
{
|
|
SP_TAG5(actor) = sectp->floorheinum;
|
|
sectp->setfloorslope(0);
|
|
}
|
|
|
|
if ((sectp->ceilingstat & CSTAT_SECTOR_SLOPE))
|
|
{
|
|
SP_TAG6(actor) = sectp->ceilingheinum;
|
|
sectp->setceilingslope(0);
|
|
}
|
|
|
|
SP_TAG4(actor) = abs(sectp->ceilingz - sectp->floorz)>>8;
|
|
|
|
sectp->setceilingz(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(actor->sector(), 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 actorNew = insertActor(actor->sector(), STAT_QUICK_LADDER);
|
|
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos = actor->spr.pos;
|
|
actorNew->spr.ang = NORM_ANGLE(actor->spr.ang + 1024);
|
|
actorNew->spr.picnum = actor->spr.picnum;
|
|
|
|
actorNew->spr.pos.X += MOVEx(256+128, actor->spr.ang);
|
|
actorNew->spr.pos.Y += MOVEy(256+128, actor->spr.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:
|
|
SET_SP_TAG13(actor, SP_TAG4(actor));
|
|
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())
|
|
SpawnUser(actor, ST1, nullptr);
|
|
|
|
if (actor->spr.xrepeat == 64 && actor->spr.yrepeat == 64) // clear default xrepeat.
|
|
actor->spr.xrepeat = actor->spr.yrepeat = 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 it2(STAT_FAF);
|
|
while (auto itActor = it2.Next())
|
|
{
|
|
if (itActor->spr.hitag == actor->spr.hitag && itActor->spr.lotag == actor->spr.lotag)
|
|
{
|
|
I_Error("Two VIEW_THRU_ tags with same match found on level\n1: x %d, y %d \n2: x %d, y %d", actor->spr.pos.X, actor->spr.pos.Y, itActor->spr.pos.X, itActor->spr.pos.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:
|
|
{
|
|
actor->sector()->extra |= (SECTFX_Z_ADJUST);
|
|
PlaxCeilGlobZadjust = SP_TAG2(actor);
|
|
PlaxFloorGlobZadjust = SP_TAG3(actor);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case CEILING_Z_ADJUST:
|
|
{
|
|
actor->sector()->extra |= (SECTFX_Z_ADJUST);
|
|
change_actor_stat(actor, STAT_ST1);
|
|
break;
|
|
}
|
|
|
|
case FLOOR_Z_ADJUST:
|
|
{
|
|
actor->sector()->extra |= (SECTFX_Z_ADJUST);
|
|
change_actor_stat(actor, STAT_ST1);
|
|
break;
|
|
}
|
|
|
|
case WARP_TELEPORTER:
|
|
{
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
actor->sector()->extra |= (SECTFX_WARP_SECTOR);
|
|
change_actor_stat(actor, STAT_WARP);
|
|
|
|
// if just a destination teleporter
|
|
// don't set up flags
|
|
if (SP_TAG10(actor) == 1)
|
|
break;
|
|
|
|
// move the the next wall
|
|
auto start_wall = actor->sector()->firstWall();
|
|
auto wall_num = start_wall;
|
|
|
|
// Travel all the way around loop setting wall bits
|
|
do
|
|
{
|
|
// DO NOT TAG WHITE WALLS!
|
|
if (wall_num->twoSided())
|
|
{
|
|
wall_num->cstat |= (CSTAT_WALL_WARP_HITSCAN);
|
|
}
|
|
|
|
wall_num = wall_num->point2Wall();
|
|
}
|
|
while (wall_num != start_wall);
|
|
|
|
break;
|
|
}
|
|
|
|
case WARP_CEILING_PLANE:
|
|
case WARP_FLOOR_PLANE:
|
|
{
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
actor->sector()->extra |= (SECTFX_WARP_SECTOR);
|
|
change_actor_stat(actor, STAT_WARP);
|
|
break;
|
|
}
|
|
|
|
case WARP_COPY_SPRITE1:
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
actor->sector()->extra |= (SECTFX_WARP_SECTOR);
|
|
change_actor_stat(actor, STAT_WARP_COPY_SPRITE1);
|
|
break;
|
|
case WARP_COPY_SPRITE2:
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
actor->sector()->extra |= (SECTFX_WARP_SECTOR);
|
|
change_actor_stat(actor, STAT_WARP_COPY_SPRITE2);
|
|
break;
|
|
|
|
case FIREBALL_TRAP:
|
|
case BOLT_TRAP:
|
|
case SPEAR_TRAP:
|
|
{
|
|
SpawnUser(actor, 0, nullptr);
|
|
ClearOwner(actor);
|
|
change_actor_stat(actor, STAT_TRAP);
|
|
break;
|
|
}
|
|
|
|
case SECT_SO_DONT_BOB:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_SO_DONT_BOB);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SECT_LOCK_DOOR:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->number = actor->spr.lotag;
|
|
sectp->stag = SECT_LOCK_DOOR;
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SECT_SO_SINK_DEST:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_SO_SINK_DEST);
|
|
sectp->number = actor->spr.lotag; // acually the offset Z
|
|
// value
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SECT_SO_DONT_SINK:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_SO_DONT_SINK);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SO_SLOPE_FLOOR_TO_POINT:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_SO_SLOPE_FLOOR_TO_POINT);
|
|
sectp->extra |= (SECTFX_DYNAMIC_AREA);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SO_SLOPE_CEILING_TO_POINT:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->flags |= (SECTFU_SO_SLOPE_CEILING_TO_POINT);
|
|
sectp->extra |= (SECTFX_DYNAMIC_AREA);
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
case SECT_SO_FORM_WHIRLPOOL:
|
|
{
|
|
sectp->u_defined = true;
|
|
sectp->stag = SECT_SO_FORM_WHIRLPOOL;
|
|
sectp->height = actor->spr.lotag;
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
case SECT_ACTOR_BLOCK:
|
|
{
|
|
// move the the next wall
|
|
auto start_wall = actor->sector()->firstWall();
|
|
auto wall_num = start_wall;
|
|
|
|
// Travel all the way around loop setting wall bits
|
|
do
|
|
{
|
|
wall_num->cstat |= (CSTAT_WALL_BLOCK_ACTOR);
|
|
if (wall_num->twoSided())
|
|
wall_num->nextWall()->cstat |= CSTAT_WALL_BLOCK_ACTOR;
|
|
wall_num = wall_num->point2Wall();
|
|
}
|
|
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;
|
|
}
|
|
|
|
SpawnUser(actor, 0, nullptr);
|
|
|
|
actor->spr.picnum = actor->user.ID = actor->spr.picnum;
|
|
|
|
actor->user.spal = actor->spr.pal; // Set the palette from build
|
|
|
|
//actor->spr.cstat |= (CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
|
|
ChangeState(actor, s_Key[num]);
|
|
|
|
picanm[actor->spr.picnum].sf &= ~(PICANM_ANIMTYPE_MASK);
|
|
picanm[actor->spr.picnum + 1].sf &= ~(PICANM_ANIMTYPE_MASK);
|
|
change_actor_stat(actor, STAT_ITEM);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN | CSTAT_SPRITE_ONE_SIDE);
|
|
actor->user.Radius = 500;
|
|
actor->spr.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 ((actor->spr.extra & SPRX_MULTI_ITEM))
|
|
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
|
|
{
|
|
KillActor(actor);
|
|
}
|
|
|
|
break;
|
|
|
|
case FIRE_FLY0:
|
|
|
|
/*
|
|
* SpawnUser(actor, FIRE_FLY0, nullptr);
|
|
*
|
|
* actor->user.State = actor->user.StateStart = &s_FireFly[0]; actor->user.RotNum = 0;
|
|
*
|
|
* actor->spr.ang = 0; actor->spr.xvel = 4;
|
|
*
|
|
* if (labs(actor->spr.z - actor->sector()->floorz) < Z(32)) actor->spr.z =
|
|
* actor->sector()->floorz - Z(32);
|
|
*
|
|
* actor->user.sz = actor->spr.z;
|
|
*
|
|
* change_actor_stat(actor, STAT_MISC);
|
|
*/
|
|
|
|
break;
|
|
|
|
case ICON_REPAIR_KIT:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_REPAIR_KIT, s_RepairKit);
|
|
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_STAR:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_STAR, s_IconStar);
|
|
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_LG_MINE:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_LG_MINE, s_IconLgMine);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
|
|
case ICON_MICRO_GUN:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_MICRO_GUN, s_IconMicroGun);
|
|
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_MICRO_BATTERY:
|
|
|
|
NUKE_REPLACEMENT:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_MICRO_BATTERY, s_IconMicroBattery);
|
|
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_UZI:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_UZI, s_IconUzi);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_UZIFLOOR:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_UZIFLOOR, s_IconUziFloor);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_LG_UZI_AMMO:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_LG_UZI_AMMO, s_IconLgUziAmmo);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_GRENADE_LAUNCHER:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_GRENADE_LAUNCHER, s_IconGrenadeLauncher);
|
|
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_LG_GRENADE:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_LG_GRENADE, s_IconLgGrenade);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_RAIL_GUN:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_RAIL_GUN, s_IconRailGun);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_RAIL_AMMO:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_RAIL_AMMO, s_IconRailAmmo);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
|
|
case ICON_ROCKET:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_ROCKET, s_IconRocket);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_LG_ROCKET:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_LG_ROCKET, s_IconLgRocket);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_SHOTGUN:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_SHOTGUN, s_IconShotgun);
|
|
|
|
actor->user.Radius = 350; // Shotgun is hard to pick up for some reason.
|
|
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_LG_SHOTSHELL:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_LG_SHOTSHELL, s_IconLgShotshell);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_AUTORIOT:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_AUTORIOT, s_IconAutoRiot);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
|
|
case ICON_GUARD_HEAD:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_GUARD_HEAD, s_IconGuardHead);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_FIREBALL_LG_AMMO:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_FIREBALL_LG_AMMO, s_IconFireballLgAmmo);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_HEART:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_HEART, s_IconHeart);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_HEART_LG_AMMO:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_HEART_LG_AMMO, s_IconHeartLgAmmo);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
#if 0
|
|
case ICON_ELECTRO:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_ELECTRO, s_IconElectro);
|
|
IconDefault(actor);
|
|
break;
|
|
#endif
|
|
|
|
case ICON_SPELL:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_SPELL, s_IconSpell);
|
|
IconDefault(actor);
|
|
|
|
PicAnimOff(actor->spr.picnum);
|
|
break;
|
|
|
|
case ICON_ARMOR:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_ARMOR, s_IconArmor);
|
|
if (actor->spr.pal != PALETTE_PLAYER3)
|
|
actor->spr.pal = actor->user.spal = PALETTE_PLAYER1;
|
|
else
|
|
actor->spr.pal = actor->user.spal = PALETTE_PLAYER3;
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_MEDKIT:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_MEDKIT, s_IconMedkit);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_SM_MEDKIT:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_SM_MEDKIT, s_IconSmMedkit);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_CHEMBOMB:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_CHEMBOMB, s_IconChemBomb);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_FLASHBOMB:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_FLASHBOMB, s_IconFlashBomb);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_NUKE:
|
|
|
|
if (gNet.MultiGameType)
|
|
{
|
|
if (!gNet.Nuke)
|
|
{
|
|
goto NUKE_REPLACEMENT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_NUKE, s_IconNuke);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
|
|
case ICON_CALTROPS:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_CALTROPS, s_IconCaltrops);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_BOOSTER:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_BOOSTER, s_IconBooster);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
case ICON_HEAT_CARD:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_HEAT_CARD, s_IconHeatCard);
|
|
IconDefault(actor);
|
|
break;
|
|
|
|
#if 0
|
|
case ICON_ENVIRON_SUIT:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_ENVIRON_SUIT, s_IconEnvironSuit);
|
|
IconDefault(actor);
|
|
PicAnimOff(actor->spr.picnum);
|
|
break;
|
|
#endif
|
|
|
|
case ICON_CLOAK:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_CLOAK, s_IconCloak);
|
|
IconDefault(actor);
|
|
PicAnimOff(actor->spr.picnum);
|
|
break;
|
|
|
|
case ICON_FLY:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_FLY, s_IconFly);
|
|
IconDefault(actor);
|
|
PicAnimOff(actor->spr.picnum);
|
|
break;
|
|
|
|
case ICON_NIGHT_VISION:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_NIGHT_VISION, s_IconNightVision);
|
|
IconDefault(actor);
|
|
PicAnimOff(actor->spr.picnum);
|
|
break;
|
|
|
|
case ICON_FLAG:
|
|
|
|
if (!IconSpawn(actor))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
SpawnUser(actor, ICON_FLAG, s_IconFlag);
|
|
actor->user.spal = actor->spr.pal;
|
|
actor->sector()->hitag = 9000; // Put flag's color in sect containing it
|
|
actor->sector()->lotag = actor->user.spal;
|
|
IconDefault(actor);
|
|
PicAnimOff(actor->spr.picnum);
|
|
break;
|
|
|
|
#if 0
|
|
case 380:
|
|
case 396:
|
|
case 430:
|
|
case 512:
|
|
case 521:
|
|
case 541:
|
|
case 2720:
|
|
#endif
|
|
case 3143:
|
|
case 3157:
|
|
{
|
|
SpawnUser(actor, actor->spr.picnum, nullptr);
|
|
|
|
change_actor_stat(actor, STAT_STATIC_FIRE);
|
|
|
|
actor->user.ID = FIREBALL_FLAMES;
|
|
actor->user.Radius = 200;
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
|
|
actor->spr.hitag = LUMINOUS; //Always full brightness
|
|
actor->spr.shade = -40;
|
|
|
|
break;
|
|
}
|
|
|
|
// blades
|
|
case BLADE1:
|
|
case BLADE2:
|
|
case BLADE3:
|
|
case 5011:
|
|
{
|
|
SpawnUser(actor, actor->spr.picnum, nullptr);
|
|
|
|
change_actor_stat(actor, STAT_DEFAULT);
|
|
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
actor->spr.extra |= (SPRX_BLADE);
|
|
|
|
break;
|
|
}
|
|
|
|
case BREAK_LIGHT:
|
|
case BREAK_BARREL:
|
|
case BREAK_PEDISTAL:
|
|
case BREAK_BOTTLE1:
|
|
case BREAK_BOTTLE2:
|
|
case BREAK_MUSHROOM:
|
|
|
|
//if ((actor->spr.extra & SPRX_BREAKABLE))
|
|
// break;
|
|
|
|
SpawnUser(actor, actor->spr.picnum, nullptr);
|
|
|
|
actor->spr.clipdist = ActorSizeX(actor);
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BREAKABLE);
|
|
actor->spr.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 ((actor->spr.extra & SPRX_MULTI_ITEM))
|
|
{
|
|
if (numplayers <= 1 || gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ItemSpotClear(DSWActor* actor, short statnum, short id)
|
|
{
|
|
bool found = false;
|
|
int i;
|
|
|
|
if (TEST_BOOL2(actor))
|
|
{
|
|
SWSectIterator it(actor->sector());
|
|
while (auto itActor = it.Next())
|
|
{
|
|
if (itActor->spr.statnum == statnum && itActor->user.ID == id)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return !found;
|
|
}
|
|
|
|
void SetupItemForJump(DSWActor* spawner, DSWActor* actor)
|
|
{
|
|
// setup item for jumping
|
|
if (SP_TAG7(spawner))
|
|
{
|
|
change_actor_stat(actor, STAT_SKIP4);
|
|
actor->user.ceiling_dist = Z(6);
|
|
actor->user.floor_dist = Z(0);
|
|
actor->user.Counter = 0;
|
|
|
|
actor->spr.xvel = (int)SP_TAG7(spawner)<<2;
|
|
actor->spr.zvel = -(((int)SP_TAG8(spawner))<<5);
|
|
|
|
actor->user.change.X = MOVEx(actor->spr.xvel, actor->spr.ang);
|
|
actor->user.change.Y = MOVEy(actor->spr.xvel, actor->spr.ang);
|
|
actor->user.change.Z = actor->spr.zvel;
|
|
}
|
|
}
|
|
|
|
int ActorCoughItem(DSWActor* actor)
|
|
{
|
|
short choose;
|
|
DSWActor* actorNew = nullptr;
|
|
|
|
switch (actor->user.ID)
|
|
{
|
|
case SAILORGIRL_R0:
|
|
ASSERT(actor->insector());
|
|
actorNew = insertActor(actor->sector(), STAT_SPAWN_ITEMS);
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos.X = actor->spr.pos.X;
|
|
actorNew->spr.pos.Y = actor->spr.pos.Y;
|
|
actorNew->spr.pos.Z = ActorZOfMiddle(actor);
|
|
actorNew->spr.ang = 0;
|
|
actorNew->spr.extra = 0;
|
|
|
|
// vel
|
|
SP_TAG7(actorNew) = 1;
|
|
// zvel
|
|
SP_TAG8(actorNew) = 40;
|
|
|
|
choose = RANDOM_P2(1024);
|
|
|
|
if (choose > 854)
|
|
SP_TAG3(actorNew) = 91; // Match number
|
|
else if (choose > 684)
|
|
SP_TAG3(actorNew) = 48; // Match number
|
|
else if (choose > 514)
|
|
SP_TAG3(actorNew) = 58; // Match number
|
|
else if (choose > 344)
|
|
SP_TAG3(actorNew) = 60; // Match number
|
|
else if (choose > 174)
|
|
SP_TAG3(actorNew) = 62; // Match number
|
|
else
|
|
SP_TAG3(actorNew) = 68; // Match number
|
|
|
|
// match
|
|
SP_TAG2(actorNew) = -1;
|
|
// kill
|
|
RESET_BOOL1(actorNew);
|
|
SpawnItemsMatch(-1);
|
|
break;
|
|
|
|
case GORO_RUN_R0:
|
|
if (RANDOM_P2(1024) < 700)
|
|
return 0;
|
|
|
|
ASSERT(actor->insector());
|
|
actorNew = insertActor(actor->sector(), STAT_SPAWN_ITEMS);
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos.X = actor->spr.pos.X;
|
|
actorNew->spr.pos.Y = actor->spr.pos.Y;
|
|
actorNew->spr.pos.Z = ActorZOfMiddle(actor);
|
|
actorNew->spr.ang = 0;
|
|
actorNew->spr.extra = 0;
|
|
|
|
// vel
|
|
SP_TAG7(actorNew) = 1;
|
|
// zvel
|
|
SP_TAG8(actorNew) = 40;
|
|
|
|
SP_TAG3(actorNew) = 69; // Match number
|
|
|
|
// match
|
|
SP_TAG2(actorNew) = -1;
|
|
// kill
|
|
RESET_BOOL1(actorNew);
|
|
SpawnItemsMatch(-1);
|
|
break;
|
|
|
|
case RIPPER2_RUN_R0:
|
|
if (RANDOM_P2(1024) < 700)
|
|
return 0;
|
|
|
|
ASSERT(actor->insector());
|
|
actorNew = insertActor(actor->sector(), STAT_SPAWN_ITEMS);
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos.X = actor->spr.pos.X;
|
|
actorNew->spr.pos.Y = actor->spr.pos.Y;
|
|
actorNew->spr.pos.Z = ActorZOfMiddle(actor);
|
|
actorNew->spr.ang = 0;
|
|
actorNew->spr.extra = 0;
|
|
|
|
// vel
|
|
SP_TAG7(actorNew) = 1;
|
|
// zvel
|
|
SP_TAG8(actorNew) = 40;
|
|
|
|
SP_TAG3(actorNew) = 70; // Match number
|
|
|
|
// match
|
|
SP_TAG2(actorNew) = -1;
|
|
// kill
|
|
RESET_BOOL1(actorNew);
|
|
SpawnItemsMatch(-1);
|
|
break;
|
|
|
|
case NINJA_RUN_R0:
|
|
|
|
if (actor->user.PlayerP)
|
|
{
|
|
if (RANDOM_P2(1024) > 200)
|
|
return 0;
|
|
|
|
ASSERT(actor->insector());
|
|
actorNew = insertActor(actor->sector(), STAT_SPAWN_ITEMS);
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos.X = actor->spr.pos.X;
|
|
actorNew->spr.pos.Y = actor->spr.pos.Y;
|
|
actorNew->spr.pos.Z = ActorZOfMiddle(actor);
|
|
actorNew->spr.ang = 0;
|
|
actorNew->spr.extra = 0;
|
|
|
|
// vel
|
|
SP_TAG7(actorNew) = 1;
|
|
// zvel
|
|
SP_TAG8(actorNew) = 40;
|
|
|
|
switch (actor->user.WeaponNum)
|
|
{
|
|
case WPN_UZI:
|
|
SP_TAG3(actorNew) = 0;
|
|
break;
|
|
case WPN_SHOTGUN:
|
|
SP_TAG3(actorNew) = 51;
|
|
break;
|
|
case WPN_STAR:
|
|
if (actor->user.PlayerP->WpnAmmo[WPN_STAR] < 9)
|
|
break;
|
|
SP_TAG3(actorNew) = 41;
|
|
break;
|
|
case WPN_MINE:
|
|
if (actor->user.PlayerP->WpnAmmo[WPN_MINE] < 5)
|
|
break;
|
|
SP_TAG3(actorNew) = 42;
|
|
break;
|
|
case WPN_MICRO:
|
|
case WPN_ROCKET:
|
|
SP_TAG3(actorNew) = 43;
|
|
break;
|
|
case WPN_GRENADE:
|
|
SP_TAG3(actorNew) = 45;
|
|
break;
|
|
case WPN_RAIL:
|
|
SP_TAG3(actorNew) = 47;
|
|
break;
|
|
case WPN_HEART:
|
|
SP_TAG3(actorNew) = 55;
|
|
break;
|
|
case WPN_HOTHEAD:
|
|
SP_TAG3(actorNew) = 53;
|
|
break;
|
|
}
|
|
|
|
// match
|
|
SP_TAG2(actorNew) = -1;
|
|
// kill
|
|
RESET_BOOL1(actorNew);
|
|
SpawnItemsMatch(-1);
|
|
break;
|
|
}
|
|
|
|
if (RANDOM_P2(1024) < 512)
|
|
return 0;
|
|
|
|
ASSERT(actor->insector());
|
|
actorNew = insertActor(actor->sector(), STAT_SPAWN_ITEMS);
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos.X = actor->spr.pos.X;
|
|
actorNew->spr.pos.Y = actor->spr.pos.Y;
|
|
actorNew->spr.pos.Z = ActorZOfMiddle(actor);
|
|
actorNew->spr.ang = 0;
|
|
actorNew->spr.extra = 0;
|
|
|
|
// vel
|
|
SP_TAG7(actorNew) = 1;
|
|
// zvel
|
|
SP_TAG8(actorNew) = 40;
|
|
|
|
if (actor->user.spal == PAL_XLAT_LT_TAN)
|
|
{
|
|
SP_TAG3(actorNew) = 44;
|
|
}
|
|
else if (actor->user.spal == PAL_XLAT_LT_GREY)
|
|
{
|
|
SP_TAG3(actorNew) = 46;
|
|
}
|
|
else if (actor->user.spal == PALETTE_PLAYER5) // Green Ninja
|
|
{
|
|
if (RANDOM_P2(1024) < 700)
|
|
SP_TAG3(actorNew) = 61;
|
|
else
|
|
SP_TAG3(actorNew) = 60;
|
|
}
|
|
else if (actor->user.spal == PALETTE_PLAYER3) // Red Ninja
|
|
{
|
|
// type
|
|
if (RANDOM_P2(1024) < 800)
|
|
SP_TAG3(actorNew) = 68;
|
|
else
|
|
SP_TAG3(actorNew) = 44;
|
|
}
|
|
else
|
|
{
|
|
if (RANDOM_P2(1024) < 512)
|
|
SP_TAG3(actorNew) = 41;
|
|
else
|
|
SP_TAG3(actorNew) = 68;
|
|
}
|
|
|
|
// match
|
|
SP_TAG2(actorNew) = -1;
|
|
// kill
|
|
RESET_BOOL1(actorNew);
|
|
SpawnItemsMatch(-1);
|
|
break;
|
|
|
|
case PACHINKO1:
|
|
case PACHINKO2:
|
|
case PACHINKO3:
|
|
case PACHINKO4:
|
|
|
|
ASSERT(actor->insector());
|
|
actorNew = insertActor(actor->sector(), STAT_SPAWN_ITEMS);
|
|
actorNew->spr.cstat = 0;
|
|
actorNew->spr.extra = 0;
|
|
actorNew->spr.pos.X = actor->spr.pos.X;
|
|
actorNew->spr.pos.Y = actor->spr.pos.Y;
|
|
actorNew->spr.pos.Z = ActorLowerZ(actor) + Z(10);
|
|
actorNew->spr.ang = actor->spr.ang;
|
|
|
|
// vel
|
|
SP_TAG7(actorNew) = 10;
|
|
// zvel
|
|
SP_TAG8(actorNew) = 10;
|
|
|
|
if (actor->user.ID == PACHINKO1)
|
|
{
|
|
if (RANDOM_P2(1024) < 600)
|
|
SP_TAG3(actorNew) = 64; // Small MedKit
|
|
else
|
|
SP_TAG3(actorNew) = 59; // Fortune Cookie
|
|
}
|
|
else if (actor->user.ID == PACHINKO2)
|
|
{
|
|
if (RANDOM_P2(1024) < 600)
|
|
SP_TAG3(actorNew) = 52; // Lg Shot Shell
|
|
else
|
|
SP_TAG3(actorNew) = 68; // Uzi clip
|
|
}
|
|
else if (actor->user.ID == PACHINKO3)
|
|
{
|
|
if (RANDOM_P2(1024) < 600)
|
|
SP_TAG3(actorNew) = 57;
|
|
else
|
|
SP_TAG3(actorNew) = 63;
|
|
}
|
|
else if (actor->user.ID == PACHINKO4)
|
|
{
|
|
if (RANDOM_P2(1024) < 600)
|
|
SP_TAG3(actorNew) = 60;
|
|
else
|
|
SP_TAG3(actorNew) = 61;
|
|
}
|
|
|
|
// match
|
|
SP_TAG2(actorNew) = -1;
|
|
// kill
|
|
RESET_BOOL1(actorNew);
|
|
SpawnItemsMatch(-1);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int SpawnItemsMatch(short match)
|
|
{
|
|
DSWActor* spawnedActor = nullptr;
|
|
|
|
SWStatIterator it(STAT_SPAWN_ITEMS);
|
|
while (auto itActor = it.Next())
|
|
{
|
|
|
|
if (SP_TAG2(itActor) != match)
|
|
continue;
|
|
|
|
switch (SP_TAG3(itActor))
|
|
{
|
|
case 90:
|
|
spawnedActor = BunnyHatch2(itActor);
|
|
spawnedActor->user.spal = spawnedActor->spr.pal = PALETTE_PLAYER8; // Boy
|
|
spawnedActor->spr.ang = itActor->spr.ang;
|
|
break;
|
|
case 91:
|
|
spawnedActor = BunnyHatch2(itActor);
|
|
spawnedActor->user.spal = spawnedActor->spr.pal = PALETTE_PLAYER0; // Girl
|
|
spawnedActor->spr.ang = itActor->spr.ang;
|
|
break;
|
|
case 92:
|
|
spawnedActor = BunnyHatch2(itActor);
|
|
spawnedActor->spr.ang = itActor->spr.ang;
|
|
break;
|
|
|
|
case 40:
|
|
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_REPAIR_KIT))
|
|
break;
|
|
|
|
spawnedActor = SpawnActor(STAT_ITEM, ICON_REPAIR_KIT, s_RepairKit, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.Flags2 |= SPR2_NEVER_RESPAWN;
|
|
IconDefault(spawnedActor);
|
|
|
|
SetupItemForJump(itActor, spawnedActor);
|
|
break;
|
|
|
|
case 57:
|
|
{
|
|
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_ARMOR))
|
|
break;
|
|
|
|
spawnedActor = SpawnActor(STAT_ITEM, ICON_ARMOR, s_IconArmor, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.Flags2 |= SPR2_NEVER_RESPAWN;
|
|
IconDefault(spawnedActor);
|
|
|
|
SetupItemForJump(itActor, spawnedActor);
|
|
|
|
if (spawnedActor->spr.pal != PALETTE_PLAYER3)
|
|
spawnedActor->spr.pal = spawnedActor->user.spal = PALETTE_PLAYER1;
|
|
else
|
|
spawnedActor->spr.pal = spawnedActor->user.spal = PALETTE_PLAYER3;
|
|
break;
|
|
}
|
|
|
|
case 58:
|
|
if (!ItemSpotClear(itActor, STAT_ITEM, ICON_MEDKIT))
|
|
break;
|
|
|
|
spawnedActor = SpawnActor(STAT_ITEM, ICON_MEDKIT, s_IconMedkit, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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, itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
spawnedActor->user.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;
|
|
|
|
static const 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(itActor) - 1;
|
|
|
|
if (!ItemSpotClear(itActor, STAT_ITEM, s_Key[num]->Pic))
|
|
break;
|
|
|
|
spawnedActor = SpawnActor(STAT_ITEM, s_Key[num]->Pic, s_Key[num], itActor->sector(), itActor->spr.pos.X, itActor->spr.pos.Y, itActor->spr.pos.Z, itActor->spr.ang, 0);
|
|
|
|
spawnedActor->spr.picnum = spawnedActor->user.ID = s_Key[num]->Pic;
|
|
|
|
|
|
// need to set the palette here - suggest table lookup
|
|
spawnedActor->user.spal = spawnedActor->spr.pal = KeyPal[num];
|
|
|
|
|
|
ChangeState(spawnedActor, s_Key[num]);
|
|
|
|
picanm[spawnedActor->spr.picnum].sf &= ~(PICANM_ANIMTYPE_MASK);
|
|
picanm[spawnedActor->spr.picnum + 1].sf &= ~(PICANM_ANIMTYPE_MASK);
|
|
|
|
SetupItemForJump(itActor, spawnedActor);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!TEST_BOOL1(itActor))
|
|
KillActor(itActor);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int NewStateGroup(DSWActor* actor, STATE* StateGroup[])
|
|
{
|
|
if (!StateGroup)
|
|
return 0;
|
|
|
|
ASSERT(actor->hasU());
|
|
|
|
// Kind of a goofy check, but it should catch alot of invalid states!
|
|
// BTW, 6144 is the max tile number allowed in editart.
|
|
if (actor->user.State && (actor->user.State->Pic < 0 || actor->user.State->Pic > MAXTILES)) // JBF: verify this!
|
|
return 0;
|
|
|
|
actor->user.Rot = StateGroup;
|
|
actor->user.State = actor->user.StateStart = StateGroup[0];
|
|
|
|
actor->user.Tics = 0;
|
|
|
|
// turn anims off because people keep setting them in the
|
|
// art file
|
|
picanm[actor->spr.picnum].sf &= ~(PICANM_ANIMTYPE_MASK);
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool SpriteOverlap(DSWActor* actor_a, DSWActor* actor_b)
|
|
{
|
|
int spa_tos, spa_bos, spb_tos, spb_bos, overlap_z;
|
|
|
|
if (!actor_a->hasU() || !actor_b->hasU()) return false;
|
|
if ((unsigned)Distance(actor_a->spr.pos.X, actor_a->spr.pos.Y, actor_b->spr.pos.X, actor_b->spr.pos.Y) > actor_a->user.Radius + actor_b->user.Radius)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
spa_tos = ActorZOfTop(actor_a);
|
|
spa_bos = ActorZOfBottom(actor_a);
|
|
|
|
spb_tos = ActorZOfTop(actor_b);
|
|
spb_bos = ActorZOfBottom(actor_b);
|
|
|
|
|
|
overlap_z = actor_a->user.OverlapZ + actor_b->user.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(DSWActor* actor_a, DSWActor* actor_b, int z_overlap)
|
|
{
|
|
int spa_tos, spa_bos, spb_tos, spb_bos;
|
|
|
|
spa_tos = ActorZOfTop(actor_a);
|
|
spa_bos = ActorZOfBottom(actor_a);
|
|
|
|
spb_tos = ActorZOfTop(actor_b);
|
|
spb_bos = ActorZOfBottom(actor_b);
|
|
|
|
|
|
// 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, sectortype* sect,
|
|
int32_t* ceilz, Collision* ceilhit, int32_t* florz, Collision* florhit)
|
|
{
|
|
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 (sect == nullptr)
|
|
{
|
|
*ceilz = 0x80000000;
|
|
|
|
*florz = 0x7fffffff;
|
|
florhit->invalidate();
|
|
return;
|
|
}
|
|
|
|
// Initialize z's and hits to the current sector's top&bottom
|
|
getzsofslopeptr(sect, x, y, ceilz, florz);
|
|
ceilhit->setSector(sect);
|
|
florhit->setSector(sect);
|
|
|
|
// Go through sprites of only the current sector
|
|
SWSectIterator it(sect);
|
|
while (auto itActor = it.Next())
|
|
{
|
|
cstat = itActor->spr.cstat;
|
|
if ((cstat & (CSTAT_SPRITE_ALIGNMENT_MASK | CSTAT_SPRITE_BLOCK)) != (CSTAT_SPRITE_ALIGNMENT_FLOOR|CSTAT_SPRITE_BLOCK))
|
|
continue; // Only check blocking floor sprites
|
|
|
|
daz = itActor->spr.pos.Z;
|
|
|
|
// Only check if sprite's 2-sided or your on the 1-sided side
|
|
if (((cstat & CSTAT_SPRITE_ONE_SIDE) != 0) && ((z > daz) == ((cstat & CSTAT_SPRITE_YFLIP) == 0)))
|
|
continue;
|
|
|
|
// Calculate and store centering offset information into xoff&yoff
|
|
tilenum = itActor->spr.picnum;
|
|
xoff = (int)tileLeftOffset(tilenum) + (int)itActor->spr.xoffset;
|
|
yoff = (int)tileTopOffset(tilenum) + (int)itActor->spr.yoffset;
|
|
if (cstat & CSTAT_SPRITE_XFLIP)
|
|
xoff = -xoff;
|
|
if (cstat & CSTAT_SPRITE_YFLIP)
|
|
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(itActor->spr.ang);
|
|
sinang = bsin(itActor->spr.ang);
|
|
xspan = tileWidth(tilenum);
|
|
dax = ((xspan >> 1) + xoff) * itActor->spr.xrepeat;
|
|
yspan = tileHeight(tilenum);
|
|
day = ((yspan >> 1) + yoff) * itActor->spr.yrepeat;
|
|
x1 = itActor->spr.pos.X + DMulScale(sinang, dax, cosang, day, 16) - x;
|
|
y1 = itActor->spr.pos.Y + DMulScale(sinang, day, -cosang, dax, 16) - y;
|
|
l = xspan * itActor->spr.xrepeat;
|
|
x2 = x1 - MulScale(sinang, l, 16);
|
|
y2 = y1 + MulScale(cosang, l, 16);
|
|
l = yspan * itActor->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->setSprite(itActor);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (daz < *florz)
|
|
{
|
|
*florz = daz;
|
|
florhit->setSprite(itActor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DoActorZrange(DSWActor* actor)
|
|
{
|
|
Collision ceilhit, florhit;
|
|
|
|
auto save_cstat = actor->spr.cstat & CSTAT_SPRITE_BLOCK;
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
vec3_t pos = actor->spr.pos;
|
|
pos.Z -= (ActorSizeZ(actor) >> 1);
|
|
FAFgetzrange(pos, actor->sector(), &actor->user.hiz, &ceilhit, &actor->user.loz, &florhit, (((int) actor->spr.clipdist) << 2) - GETZRANGE_CLIP_ADJ, CLIPMASK_ACTOR);
|
|
actor->spr.cstat |= save_cstat;
|
|
|
|
actor->user.lo_sectp = actor->user.hi_sectp = nullptr;
|
|
actor->user.highActor = nullptr;
|
|
actor->user.lowActor = nullptr;
|
|
|
|
switch (ceilhit.type)
|
|
{
|
|
case kHitSprite:
|
|
actor->user.highActor = ceilhit.actor();
|
|
break;
|
|
case kHitSector:
|
|
actor->user.hi_sectp = ceilhit.hitSector;
|
|
break;
|
|
default:
|
|
ASSERT(true==false);
|
|
break;
|
|
}
|
|
|
|
switch (florhit.type)
|
|
{
|
|
case kHitSprite:
|
|
actor->user.lowActor = florhit.actor();
|
|
break;
|
|
case kHitSector:
|
|
actor->user.lo_sectp = florhit.hitSector;
|
|
break;
|
|
default:
|
|
ASSERT(true==false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// !AIC - puts getzrange results into USER varaible actor->user.loz, actor->user.hiz, actor->user.lo_sectp, actor->user.hi_sectp, etc.
|
|
// The loz and hiz are used a lot.
|
|
|
|
int DoActorGlobZ(DSWActor* actor)
|
|
{
|
|
actor->user.loz = globloz;
|
|
actor->user.hiz = globhiz;
|
|
|
|
actor->user.lo_sectp = actor->user.hi_sectp = nullptr;
|
|
actor->user.highActor = nullptr;
|
|
actor->user.lowActor = nullptr;
|
|
|
|
switch (globhihit.type)
|
|
{
|
|
case kHitSprite:
|
|
actor->user.highActor = globhihit.actor();
|
|
break;
|
|
default:
|
|
actor->user.hi_sectp = globhihit.hitSector;
|
|
break;
|
|
}
|
|
|
|
switch (globlohit.type)
|
|
{
|
|
case kHitSprite:
|
|
actor->user.lowActor = globlohit.actor();
|
|
break;
|
|
default:
|
|
actor->user.lo_sectp = globlohit.hitSector;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool ActorDrop(DSWActor* actor, int x, int y, int z, sectortype* new_sector, short min_height)
|
|
{
|
|
int hiz, loz;
|
|
Collision ceilhit, florhit;
|
|
|
|
// look only at the center point for a floor sprite
|
|
auto save_cstat = (actor->spr.cstat & CSTAT_SPRITE_BLOCK);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
FAFgetzrangepoint(x, y, z - (ActorSizeZ(actor) >> 1), new_sector, &hiz, &ceilhit, &loz, &florhit);
|
|
actor->spr.cstat |= (save_cstat);
|
|
|
|
if (florhit.type < 0 || ceilhit.type < 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
switch (florhit.type)
|
|
{
|
|
case kHitSprite:
|
|
{
|
|
auto hsp = florhit.actor();
|
|
|
|
// if its a floor sprite and not too far down
|
|
if ((hsp->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR) &&
|
|
(labs(loz - z) <= min_height))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case kHitSector:
|
|
{
|
|
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, int min_height)
|
|
{
|
|
int dax, day;
|
|
|
|
// dax = actor->spr.x + MOVEx(128, actor->spr.ang);
|
|
// day = actor->spr.y + MOVEy(128, actor->spr.ang);
|
|
|
|
dax = actor->spr.pos.X + MOVEx(256, actor->spr.ang);
|
|
day = actor->spr.pos.Y + MOVEy(256, actor->spr.ang);
|
|
|
|
auto newsector = actor->sector();
|
|
updatesector(dax, day, &newsector);
|
|
|
|
// look straight down for a drop
|
|
if (ActorDrop(actor, dax, day, actor->spr.pos.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)
|
|
{
|
|
int x, y, z, loz, hiz;
|
|
DSWActor* highActor;
|
|
DSWActor* lowActor;
|
|
sectortype* lo_sectp,* hi_sectp;
|
|
short dist;
|
|
int cliptype = CLIPMASK_ACTOR;
|
|
|
|
|
|
if (actor->user.Flags & (SPR_NO_SCAREDZ))
|
|
{
|
|
// For COOLG & HORNETS
|
|
// set to actual z before you move
|
|
actor->spr.pos.Z = actor->user.pos.Z;
|
|
}
|
|
|
|
// save off x,y values
|
|
x = actor->spr.pos.X;
|
|
y = actor->spr.pos.Y;
|
|
z = actor->spr.pos.Z;
|
|
loz = actor->user.loz;
|
|
hiz = actor->user.hiz;
|
|
lowActor = actor->user.lowActor;
|
|
highActor = actor->user.highActor;
|
|
lo_sectp = actor->user.lo_sectp;
|
|
hi_sectp = actor->user.hi_sectp;
|
|
auto sect = actor->sector();
|
|
|
|
actor->user.coll = move_sprite(actor, xchange, ychange, zchange,
|
|
actor->user.ceiling_dist, actor->user.floor_dist, cliptype, ACTORMOVETICS);
|
|
|
|
ASSERT(actor->insector());
|
|
|
|
// try and determine whether you moved > lo_step in the z direction
|
|
if (!(actor->user.Flags & (SPR_NO_SCAREDZ | SPR_JUMPING | SPR_CLIMBING | SPR_FALLING | SPR_DEAD | SPR_SWIMMING)))
|
|
{
|
|
if (labs(actor->spr.pos.Z - globloz) > actor->user.lo_step)
|
|
{
|
|
// cancel move
|
|
actor->spr.pos.X = x;
|
|
actor->spr.pos.Y = y;
|
|
actor->spr.pos.Z = z;
|
|
//actor->spr.z = actor->user.loz; // place on ground in case you are in the air
|
|
actor->user.loz = loz;
|
|
actor->user.hiz = hiz;
|
|
actor->user.lowActor = lowActor;
|
|
actor->user.highActor = highActor;
|
|
actor->user.lo_sectp = lo_sectp;
|
|
actor->user.hi_sectp = hi_sectp;
|
|
actor->user.coll.invalidate();
|
|
ChangeActorSect(actor, sect);
|
|
return false;
|
|
}
|
|
|
|
if (ActorDrop(actor, actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, actor->sector(), actor->user.lo_step))
|
|
{
|
|
// cancel move
|
|
actor->spr.pos.X = x;
|
|
actor->spr.pos.Y = y;
|
|
actor->spr.pos.Z = z;
|
|
//actor->spr.z = actor->user.loz; // place on ground in case you are in the air
|
|
actor->user.loz = loz;
|
|
actor->user.hiz = hiz;
|
|
actor->user.lowActor = lowActor;
|
|
actor->user.highActor = highActor;
|
|
actor->user.lo_sectp = lo_sectp;
|
|
actor->user.hi_sectp = hi_sectp;
|
|
actor->user.coll.invalidate();
|
|
ChangeActorSect(actor, sect);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
actor->user.Flags |= (SPR_MOVED);
|
|
|
|
if (actor->user.coll.type == kHitNone)
|
|
{
|
|
// Keep track of how far sprite has moved
|
|
dist = Distance(x, y, actor->spr.pos.X, actor->spr.pos.Y);
|
|
actor->user.TargetDist -= dist;
|
|
actor->user.Dist += dist;
|
|
actor->user.DistCheck += dist;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
int DoStayOnFloor(DSWActor* actor)
|
|
{
|
|
actor->spr.pos.Z = actor->sector()->floorz;
|
|
return 0;
|
|
}
|
|
|
|
int DoGrating(DSWActor* actor)
|
|
{
|
|
int dir;
|
|
const int GRATE_FACTOR = 3;
|
|
|
|
// reduce to 0 to 3 value
|
|
dir = actor->spr.ang >> 9;
|
|
|
|
if ((dir & 1) == 0)
|
|
{
|
|
if (dir == 0)
|
|
actor->spr.pos.X += 2 * GRATE_FACTOR;
|
|
else
|
|
actor->spr.pos.X -= 2 * GRATE_FACTOR;
|
|
}
|
|
else
|
|
{
|
|
if (dir == 1)
|
|
actor->spr.pos.Y += 2 * GRATE_FACTOR;
|
|
else
|
|
actor->spr.pos.Y -= 2 * GRATE_FACTOR;
|
|
}
|
|
|
|
actor->spr.hitag -= GRATE_FACTOR;
|
|
|
|
if (actor->spr.hitag <= 0)
|
|
{
|
|
change_actor_stat(actor, STAT_DEFAULT);
|
|
actor->clearUser();
|
|
}
|
|
|
|
SetActorZ(actor, &actor->spr.pos);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int DoKey(DSWActor* actor)
|
|
{
|
|
actor->spr.ang = NORM_ANGLE(actor->spr.ang + (14 * ACTORMOVETICS));
|
|
|
|
DoGet(actor);
|
|
return 0;
|
|
}
|
|
|
|
int DoCoin(DSWActor* actor)
|
|
{
|
|
int offset;
|
|
|
|
actor->user.WaitTics -= ACTORMOVETICS * 2;
|
|
|
|
if (actor->user.WaitTics <= 0)
|
|
{
|
|
KillActor(actor);
|
|
return 0;
|
|
}
|
|
|
|
if (actor->user.WaitTics < 10*120)
|
|
{
|
|
if (actor->user.StateStart != s_GreenCoin)
|
|
{
|
|
offset = int(actor->user.State - actor->user.StateStart);
|
|
ChangeState(actor, s_GreenCoin);
|
|
actor->user.State = actor->user.StateStart + offset;
|
|
}
|
|
}
|
|
else if (actor->user.WaitTics < 20*120)
|
|
{
|
|
if (actor->user.StateStart != s_YellowCoin)
|
|
{
|
|
offset = int(actor->user.State - actor->user.StateStart);
|
|
ChangeState(actor, s_YellowCoin);
|
|
actor->user.State = actor->user.StateStart + offset;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KillGet(DSWActor* actor)
|
|
{
|
|
switch (gNet.MultiGameType)
|
|
{
|
|
case MULTI_GAME_NONE:
|
|
case MULTI_GAME_COOPERATIVE:
|
|
KillActor(actor);
|
|
break;
|
|
case MULTI_GAME_COMMBAT:
|
|
case MULTI_GAME_AI_BOTS:
|
|
|
|
if (actor->user.Flags2 & (SPR2_NEVER_RESPAWN))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
actor->user.WaitTics = 30*120;
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
|
|
// respawn markers
|
|
if (!gNet.SpawnMarkers || actor->spr.hitag == TAG_NORESPAWN_FLAG) // No coin if it's a special flag
|
|
break;
|
|
|
|
auto actorNew = SpawnActor(STAT_ITEM, Red_COIN, s_RedCoin, actor->sector(),
|
|
actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, 0, 0);
|
|
|
|
actorNew->spr.shade = -20;
|
|
actorNew->user.WaitTics = actor->user.WaitTics - 12;
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int KillGetAmmo(DSWActor* actor)
|
|
{
|
|
switch (gNet.MultiGameType)
|
|
{
|
|
case MULTI_GAME_NONE:
|
|
case MULTI_GAME_COOPERATIVE:
|
|
KillActor(actor);
|
|
break;
|
|
|
|
case MULTI_GAME_COMMBAT:
|
|
case MULTI_GAME_AI_BOTS:
|
|
|
|
if (actor->user.Flags2 & (SPR2_NEVER_RESPAWN))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
// No Respawn mode - all ammo goes away
|
|
if (gNet.NoRespawn)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
actor->user.WaitTics = 30*120;
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
|
|
// respawn markers
|
|
if (!gNet.SpawnMarkers)
|
|
break;
|
|
|
|
auto actorNew = SpawnActor(STAT_ITEM, Red_COIN, s_RedCoin, actor->sector(),
|
|
actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, 0, 0);
|
|
|
|
actorNew->spr.shade = -20;
|
|
actorNew->user.WaitTics = actor->user.WaitTics - 12;
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int KillGetWeapon(DSWActor* actor)
|
|
{
|
|
switch (gNet.MultiGameType)
|
|
{
|
|
case MULTI_GAME_NONE:
|
|
KillActor(actor);
|
|
break;
|
|
|
|
case MULTI_GAME_COOPERATIVE:
|
|
// don't kill weapons in coop
|
|
|
|
// unless told too :)
|
|
if (actor->user.Flags2 & (SPR2_NEVER_RESPAWN))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MULTI_GAME_COMMBAT:
|
|
case MULTI_GAME_AI_BOTS:
|
|
|
|
if (actor->user.Flags2 & (SPR2_NEVER_RESPAWN))
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
// No Respawn mode - all weapons stay
|
|
// but can only get once
|
|
if (gNet.NoRespawn)
|
|
break;
|
|
|
|
actor->user.WaitTics = 30*120;
|
|
actor->spr.cstat |= (CSTAT_SPRITE_INVISIBLE);
|
|
|
|
// respawn markers
|
|
if (!gNet.SpawnMarkers)
|
|
break;
|
|
|
|
auto actorNew = SpawnActor(STAT_ITEM, Red_COIN, s_RedCoin, actor->sector(),
|
|
actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, 0, 0);
|
|
|
|
actorNew->spr.shade = -20;
|
|
actorNew->user.WaitTics = actor->user.WaitTics - 12;
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DoSpawnItemTeleporterEffect(DSWActor* actor)
|
|
{
|
|
extern STATE s_TeleportEffect[];
|
|
|
|
auto effect = SpawnActor(STAT_MISSILE, 0, s_TeleportEffect, actor->sector(),
|
|
actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - Z(12),
|
|
actor->spr.ang, 0);
|
|
|
|
effect->spr.shade = -40;
|
|
effect->spr.xrepeat = effect->spr.yrepeat = 36;
|
|
effect->spr.cstat |= CSTAT_SPRITE_YCENTER;
|
|
effect->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
return 0;
|
|
}
|
|
|
|
void ChoosePlayerGetSound(PLAYER* pp)
|
|
{
|
|
int choose_snd=0;
|
|
|
|
if (pp != Player+myconnectindex) return;
|
|
|
|
choose_snd = StdRandomRange((MAX_GETSOUNDS-1)<<8)>>8;
|
|
|
|
PlayerSound(PlayerGetItemVocs[choose_snd], v3df_follow|v3df_dontpan,pp);
|
|
}
|
|
|
|
bool CanGetWeapon(PLAYER* pp, DSWActor* actor, int WPN)
|
|
{
|
|
switch (gNet.MultiGameType)
|
|
{
|
|
case MULTI_GAME_NONE:
|
|
return true;
|
|
|
|
case MULTI_GAME_COOPERATIVE:
|
|
if (actor->user.Flags2 & (SPR2_NEVER_RESPAWN))
|
|
return true;
|
|
|
|
if (pp->WpnGotOnceFlags & BIT(WPN))
|
|
return false;
|
|
|
|
return true;
|
|
|
|
case MULTI_GAME_COMMBAT:
|
|
case MULTI_GAME_AI_BOTS:
|
|
|
|
if (actor->user.Flags2 & (SPR2_NEVER_RESPAWN))
|
|
return true;
|
|
|
|
// No Respawn - can't get a weapon again if you already got it
|
|
if (gNet.NoRespawn && (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 },
|
|
};
|
|
|
|
enum
|
|
{
|
|
ITEMFLASHAMT = -8,
|
|
ITEMFLASHCLR = 144
|
|
};
|
|
int DoGet(DSWActor* actor)
|
|
{
|
|
PLAYER* pp;
|
|
short pnum, key_num;
|
|
int dist, a,b,c;
|
|
bool can_see;
|
|
|
|
// For flag stuff
|
|
|
|
// 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 (actor->spr.cstat & (CSTAT_SPRITE_INVISIBLE))
|
|
{
|
|
actor->user.WaitTics -= ACTORMOVETICS * 2;
|
|
if (actor->user.WaitTics <= 0)
|
|
{
|
|
PlaySound(DIGI_ITEM_SPAWN, actor, v3df_none);
|
|
DoSpawnItemTeleporterEffect(actor);
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_INVISIBLE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (actor->spr.xvel)
|
|
{
|
|
if (!DoItemFly(actor))
|
|
{
|
|
actor->spr.xvel = 0;
|
|
change_actor_stat(actor, STAT_ITEM);
|
|
}
|
|
}
|
|
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
pp = &Player[pnum];
|
|
DSWActor* plActor = pp->actor;
|
|
|
|
if (pp->Flags & (PF_DEAD))
|
|
continue;
|
|
|
|
DISTANCE(pp->pos.X, pp->pos.Y, actor->spr.pos.X, actor->spr.pos.Y, dist, a,b,c);
|
|
if ((unsigned)dist > (plActor->user.Radius + actor->user.Radius))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!SpriteOverlap(actor, pp->actor))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto cstat_bak = actor->spr.cstat;
|
|
actor->spr.cstat |= (CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
can_see = FAFcansee(actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, actor->sector(),
|
|
pp->pos.X, pp->pos.Y, pp->pos.Z, pp->cursector);
|
|
actor->spr.cstat = cstat_bak;
|
|
|
|
if (!can_see)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (actor->user.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, actor, 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 (actor->user.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, actor, v3df_dontpan);
|
|
|
|
// override for respawn mode
|
|
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
KillGet(actor);
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Health - Instant Use
|
|
//
|
|
|
|
case ICON_SM_MEDKIT:
|
|
if (plActor->user.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, actor, v3df_dontpan);
|
|
|
|
// override for respawn mode
|
|
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
KillGet(actor);
|
|
}
|
|
break;
|
|
|
|
case ICON_BOOSTER: // Fortune cookie
|
|
pp->MaxHealth = 200;
|
|
if (plActor->user.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 = StdRandomRange(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, actor, v3df_dontpan);
|
|
|
|
// override for respawn mode
|
|
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
|
|
// override for respawn mode
|
|
if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.NoRespawn)
|
|
{
|
|
KillActor(actor);
|
|
break;
|
|
}
|
|
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
|
|
// don't kill repair kit in coop
|
|
if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
|
|
break;
|
|
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
}
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
}
|
|
break;
|
|
//
|
|
// Weapon
|
|
//
|
|
case ICON_STAR:
|
|
|
|
if (!CanGetWeapon(pp, actor, WPN_STAR))
|
|
break;
|
|
|
|
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, actor, v3df_dontpan);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_STAR)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_STAR));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum <= WPN_STAR && plActor->user.WeaponNum != WPN_SWORD)
|
|
break;
|
|
InitWeaponStar(pp);
|
|
break;
|
|
|
|
case ICON_LG_MINE:
|
|
|
|
if (!CanGetWeapon(pp, actor, WPN_MINE))
|
|
break;
|
|
|
|
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, actor, v3df_dontpan);
|
|
ChoosePlayerGetSound(pp);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_MINE)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_MINE));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum > WPN_MINE && plActor->user.WeaponNum != WPN_SWORD)
|
|
break;
|
|
InitWeaponMine(pp);
|
|
break;
|
|
|
|
case ICON_UZI:
|
|
case ICON_UZIFLOOR:
|
|
|
|
if (!CanGetWeapon(pp, actor, WPN_UZI))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_UZI));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
KillGetWeapon(actor);
|
|
|
|
if (pp->WpnFlags & (BIT(WPN_UZI)) && (pp->Flags & PF_TWO_UZI))
|
|
break;
|
|
// flag to help with double uzi powerup - simpler but kludgy
|
|
pp->Flags |= (PF_PICKED_UP_AN_UZI);
|
|
if (pp->WpnFlags & (BIT(WPN_UZI)))
|
|
{
|
|
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
|
|
{
|
|
pp->WpnFlags |= (BIT(WPN_UZI));
|
|
ChoosePlayerGetSound(pp);
|
|
}
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
|
|
if (plActor->user.WeaponNum > WPN_UZI && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
break;
|
|
|
|
case ICON_MICRO_GUN:
|
|
|
|
if (!CanGetWeapon(pp, actor, WPN_MICRO))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_MICRO));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
ChoosePlayerGetSound(pp);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_MICRO)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_MICRO));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum > WPN_MICRO && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
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, actor, v3df_dontpan);
|
|
if (StdRandomRange(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(actor);
|
|
}
|
|
break;
|
|
|
|
case ICON_GRENADE_LAUNCHER:
|
|
if (!CanGetWeapon(pp, actor, WPN_GRENADE))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_GRENADE));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
//ChoosePlayerGetSound(pp);
|
|
if (StdRandomRange(1000) > 800 && pp == Player+myconnectindex)
|
|
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_GRENADE)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_GRENADE));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum > WPN_GRENADE && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
break;
|
|
|
|
#if 0
|
|
case ICON_ROCKET:
|
|
pp->WpnAmmo[WPN_ROCKET] += 15;
|
|
if (pp == Player+myconnectindex)
|
|
PlaySound(DIGI_ITEM, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_ROCKET)))
|
|
break;
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
break;
|
|
#endif
|
|
|
|
case ICON_RAIL_GUN:
|
|
if (SW_SHAREWARE) { KillActor(actor); break; }
|
|
|
|
if (!CanGetWeapon(pp, actor, WPN_RAIL))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_RAIL));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
if (pp == Player+myconnectindex)
|
|
{
|
|
if (StdRandomRange(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(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_RAIL)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_RAIL));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum > WPN_RAIL && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
break;
|
|
|
|
case ICON_SHOTGUN:
|
|
if (!CanGetWeapon(pp, actor, WPN_SHOTGUN))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_SHOTGUN));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
ChoosePlayerGetSound(pp);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_SHOTGUN)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_SHOTGUN));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum > WPN_SHOTGUN && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
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, actor, WPN_HOTHEAD))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_HOTHEAD));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
//ChoosePlayerGetSound(pp);
|
|
if (StdRandomRange(1000) > 800 && pp == Player+myconnectindex)
|
|
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_HOTHEAD)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_NAPALM) | BIT(WPN_RING) | BIT(WPN_HOTHEAD));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
if (plActor->user.WeaponNum > WPN_HOTHEAD && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
break;
|
|
|
|
case ICON_HEART:
|
|
if (SW_SHAREWARE) { KillActor(actor); break; }
|
|
|
|
if (!CanGetWeapon(pp, actor, WPN_HEART))
|
|
break;
|
|
|
|
pp->WpnGotOnceFlags |= (BIT(WPN_HEART));
|
|
|
|
if (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, actor, v3df_dontpan);
|
|
//ChoosePlayerGetSound(pp);
|
|
if (StdRandomRange(1000) > 800 && pp == Player+myconnectindex)
|
|
PlayerSound(DIGI_LIKEBIGWEAPONS, v3df_dontpan|v3df_doppler|v3df_follow,pp);
|
|
KillGetWeapon(actor);
|
|
if (pp->WpnFlags & (BIT(WPN_HEART)))
|
|
break;
|
|
pp->WpnFlags |= (BIT(WPN_HEART));
|
|
|
|
if (!cl_weaponswitch)
|
|
break;
|
|
|
|
if (plActor->user.WeaponNum > WPN_HEART && plActor->user.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, actor, v3df_dontpan);
|
|
KillGetAmmo(actor);
|
|
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, actor, v3df_dontpan);
|
|
KillGet(actor);
|
|
|
|
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 (actor->spr.pal == pp->actor->spr.pal) break; // Can't pick up your own flag!
|
|
|
|
PlaySound(DIGI_ITEM, actor, v3df_dontpan);
|
|
|
|
DSWActor* actorNew;
|
|
if (actor->spr.hitag == TAG_NORESPAWN_FLAG)
|
|
actorNew = SpawnActor(STAT_ITEM, ICON_FLAG, s_CarryFlagNoDet, actor->sector(),
|
|
actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, 0, 0);
|
|
else
|
|
actorNew = SpawnActor(STAT_ITEM, ICON_FLAG, s_CarryFlag, actor->sector(),
|
|
actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z, 0, 0);
|
|
|
|
actorNew->spr.shade = -20;
|
|
|
|
// Attach flag to player
|
|
actorNew->user.Counter = 0;
|
|
actorNew->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
|
|
actorNew->spr.cstat |= (CSTAT_SPRITE_ALIGNMENT_WALL);
|
|
SetAttach(pp->actor, actorNew);
|
|
actorNew->user.pos.Z = ActorZOfMiddle(pp->actor); // Set mid way up who it hit
|
|
actorNew->user.spal = actorNew->spr.pal = actor->spr.pal; // Set the palette of the flag
|
|
|
|
SetOwner(pp->actor, actorNew); // Player now owns the flag
|
|
actorNew->user.flagOwnerActor = actor; // Tell carried flag who owns it
|
|
KillGet(actor); // Set up for flag respawning
|
|
break;
|
|
}
|
|
default:
|
|
KillActor(actor);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This function mostly only adjust the active_range field
|
|
|
|
void ProcessActiveVars(DSWActor* actor)
|
|
{
|
|
const int TIME_TILL_INACTIVE = (4 * 120);
|
|
|
|
if (!(actor->user.Flags & SPR_ACTIVE))
|
|
{
|
|
// if actor has been unaware for more than a few seconds
|
|
actor->user.inactive_time += ACTORMOVETICS;
|
|
if (actor->user.inactive_time > TIME_TILL_INACTIVE)
|
|
{
|
|
// reset to min update range
|
|
actor->user.active_range = MIN_ACTIVE_RANGE;
|
|
// keep time low so it doesn't roll over
|
|
actor->user.inactive_time = TIME_TILL_INACTIVE;
|
|
}
|
|
}
|
|
|
|
actor->user.wait_active_check += ACTORMOVETICS;
|
|
}
|
|
|
|
void AdjustActiveRange(PLAYER* pp, DSWActor* actor, int dist)
|
|
{
|
|
DSWActor* plActor = pp->actor;
|
|
int look_height;
|
|
|
|
|
|
// do no FAFcansee before it is time
|
|
if (actor->user.wait_active_check < ACTIVE_CHECK_TIME)
|
|
return;
|
|
|
|
actor->user.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 (actor->user.Flags & (SPR_ACTIVE))
|
|
return;
|
|
|
|
//
|
|
// From this point on Actor is INACTIVE
|
|
//
|
|
|
|
// if actor can still see the player
|
|
look_height = ActorZOfTop(actor);
|
|
if (FAFcansee(actor->spr.pos.X, actor->spr.pos.Y, look_height, actor->sector(), plActor->spr.pos.X, plActor->spr.pos.Y, ActorUpperZ(plActor), plActor->sector()))
|
|
{
|
|
// Player is visible
|
|
// adjust update range of this sprite
|
|
|
|
// some huge distance
|
|
actor->user.active_range = 75000;
|
|
// sprite is AWARE
|
|
actor->user.Flags |= (SPR_ACTIVE);
|
|
actor->user.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)
|
|
{
|
|
short StateTics;
|
|
|
|
if (!actor->user.State)
|
|
{
|
|
ASSERT(actor->user.ActorActionFunc);
|
|
(actor->user.ActorActionFunc)(actor);
|
|
return 0;
|
|
}
|
|
|
|
if (actor->spr.statnum >= STAT_SKIP4_START && actor->spr.statnum <= STAT_SKIP4_END)
|
|
actor->user.Tics += ACTORMOVETICS * 2;
|
|
else
|
|
actor->user.Tics += ACTORMOVETICS;
|
|
|
|
// Skip states if too much time has passed
|
|
while (actor->user.Tics >= (actor->user.State->Tics & SF_TICS_MASK))
|
|
{
|
|
StateTics = (actor->user.State->Tics & SF_TICS_MASK);
|
|
|
|
if ((actor->user.State->Tics & SF_TIC_ADJUST))
|
|
{
|
|
ASSERT(actor->user.Attrib);
|
|
ASSERT(actor->user.speed < MAX_SPEED);
|
|
ASSERT(StateTics > -actor->user.Attrib->TicAdjust[actor->user.speed]);
|
|
|
|
StateTics += actor->user.Attrib->TicAdjust[actor->user.speed];
|
|
}
|
|
|
|
// Set Tics
|
|
actor->user.Tics -= StateTics;
|
|
|
|
// Transition to the next state
|
|
actor->user.State = actor->user.State->NextState;
|
|
|
|
// Look for flags embedded into the Tics variable
|
|
while ((actor->user.State->Tics & SF_QUICK_CALL))
|
|
{
|
|
// Call it once and go to the next state
|
|
(*actor->user.State->Animator)(actor);
|
|
|
|
ASSERT(actor->hasU()); //put this in to see if actor was getting killed with in his QUICK_CALL state
|
|
|
|
if (!actor->hasU())
|
|
break;
|
|
|
|
// if still on the same QUICK_CALL should you
|
|
// go to the next state.
|
|
if ((actor->user.State->Tics & SF_QUICK_CALL))
|
|
actor->user.State = actor->user.State->NextState;
|
|
}
|
|
|
|
if (!actor->hasU())
|
|
break;
|
|
|
|
if (!actor->user.State->Pic)
|
|
{
|
|
NewStateGroup(actor, (STATE* *) actor->user.State->NextState);
|
|
}
|
|
}
|
|
|
|
if (actor->hasU())
|
|
{
|
|
ASSERT(actor->user.State);
|
|
// Set picnum to the correct pic
|
|
if ((actor->user.State->Tics & SF_WALL_STATE))
|
|
{
|
|
ASSERT(actor->user.WallP);
|
|
actor->user.WallP->picnum = actor->user.State->Pic;
|
|
}
|
|
else
|
|
{
|
|
if (actor->user.RotNum > 1)
|
|
actor->spr.picnum = actor->user.Rot[0]->Pic;
|
|
else
|
|
actor->spr.picnum = actor->user.State->Pic;
|
|
}
|
|
|
|
// Call the correct animator
|
|
if (actor->user.State->Animator && actor->user.State->Animator != NullAnimator)
|
|
(*actor->user.State->Animator)(actor);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void SpriteControl(void)
|
|
{
|
|
int32_t stat;
|
|
short pnum, CloseToPlayer;
|
|
PLAYER* 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;
|
|
|
|
CloseToPlayer = false;
|
|
|
|
ProcessActiveVars(actor);
|
|
|
|
TRAVERSE_CONNECT(pnum)
|
|
{
|
|
pp = &Player[pnum];
|
|
|
|
// Only update the ones closest
|
|
DISTANCE(pp->pos.X, pp->pos.Y, actor->spr.pos.X, actor->spr.pos.Y, dist, tx, ty, tmin);
|
|
|
|
AdjustActiveRange(pp, actor, dist);
|
|
|
|
if (dist < actor->user.active_range)
|
|
{
|
|
CloseToPlayer = true;
|
|
}
|
|
}
|
|
|
|
actor->user.Flags &= ~(SPR_MOVED);
|
|
|
|
// Only update the ones close to ANY player
|
|
if (CloseToPlayer)
|
|
{
|
|
StateControl(actor);
|
|
}
|
|
else
|
|
{
|
|
// to far away to be attacked
|
|
actor->user.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())
|
|
{
|
|
if (actor->hasU() && actor->user.ActorActionFunc)
|
|
actor->user.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())
|
|
{
|
|
if (!actor->hasU())
|
|
continue;
|
|
if (actor->user.Tics)
|
|
{
|
|
if ((actor->user.Tics -= synctics) <= 0)
|
|
SetVatorActive(actor);
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (!(actor->user.Flags & SPR_ACTIVE))
|
|
continue;
|
|
|
|
actor->user.ActorActionFunc(actor);
|
|
}
|
|
|
|
it.Reset(STAT_SPIKE);
|
|
while (auto actor = it.Next())
|
|
{
|
|
if (actor->user.Tics)
|
|
{
|
|
if ((actor->user.Tics -= synctics) <= 0)
|
|
SetSpikeActive(actor);
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (!(actor->user.Flags & SPR_ACTIVE))
|
|
continue;
|
|
|
|
actor->user.ActorActionFunc(actor);
|
|
}
|
|
|
|
it.Reset(STAT_ROTATOR);
|
|
while (auto actor = it.Next())
|
|
{
|
|
if (actor->user.Tics)
|
|
{
|
|
if ((actor->user.Tics -= synctics) <= 0)
|
|
SetRotatorActive(actor);
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (!(actor->user.Flags & SPR_ACTIVE))
|
|
continue;
|
|
|
|
actor->user.ActorActionFunc(actor);
|
|
}
|
|
|
|
it.Reset(STAT_SLIDOR);
|
|
while (auto actor = it.Next())
|
|
{
|
|
if (actor->user.Tics)
|
|
{
|
|
if ((actor->user.Tics -= synctics) <= 0)
|
|
SetSlidorActive(actor);
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (!(actor->user.Flags & SPR_ACTIVE))
|
|
continue;
|
|
|
|
actor->user.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)
|
|
{
|
|
Collision retval{};
|
|
int zh;
|
|
short tempshort;
|
|
|
|
ASSERT(actor->hasU());
|
|
|
|
vec3_t clippos = actor->spr.pos;
|
|
|
|
// Can't modify sprite sectors
|
|
// directly becuase of linked lists
|
|
auto dasect = actor->sector();
|
|
auto lastsect = dasect;
|
|
|
|
if (actor->spr.cstat & (CSTAT_SPRITE_YCENTER))
|
|
{
|
|
zh = 0;
|
|
}
|
|
else
|
|
{
|
|
// move the center point up for moving
|
|
zh = actor->user.zclip;
|
|
clippos.Z -= zh;
|
|
}
|
|
|
|
|
|
// ASSERT(inside(actor->spr.x,actor->spr.y,dasectnum));
|
|
|
|
clipmove(clippos, &dasect,
|
|
((xchange * numtics) << 11), ((ychange * numtics) << 11),
|
|
(((int) actor->spr.clipdist) << 2), ceildist, flordist, cliptype, retval, 1);
|
|
|
|
actor->spr.pos.vec2 = clippos.vec2;
|
|
|
|
if (dasect == nullptr)
|
|
{
|
|
retval.setWall(0); // this is wrong but what the original code did.
|
|
return retval;
|
|
}
|
|
|
|
if ((dasect != actor->sector()) && (dasect != nullptr))
|
|
ChangeActorSect(actor, dasect);
|
|
|
|
// Set the blocking bit to 0 temporarly so FAFgetzrange doesn't pick
|
|
// up its own sprite
|
|
auto tempstat = actor->spr.cstat;
|
|
actor->spr.cstat = 0;
|
|
|
|
// 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 = actor->spr.pos;
|
|
pos.Z -= zh + 1;
|
|
FAFgetzrange(pos, actor->sector(),
|
|
&globhiz, &globhihit, &globloz, &globlohit,
|
|
(((int) actor->spr.clipdist) << 2) - GETZRANGE_CLIP_ADJ, cliptype);
|
|
|
|
actor->spr.cstat = tempstat;
|
|
|
|
// !AIC - puts getzrange results into USER varaible actor->user.loz, actor->user.hiz, actor->user.lo_sectp, actor->user.hi_sectp, etc.
|
|
// Takes info from global variables
|
|
DoActorGlobZ(actor);
|
|
|
|
clippos.Z = actor->spr.pos.Z + ((zchange * numtics) >> 3);
|
|
|
|
// test for hitting ceiling or floor
|
|
if ((clippos.Z - zh <= globhiz) || (clippos.Z - zh > globloz))
|
|
{
|
|
if (retval.type == kHitNone)
|
|
{
|
|
if (actor->user.Flags & (SPR_CLIMBING))
|
|
{
|
|
actor->spr.pos.Z = clippos.Z;
|
|
return retval;
|
|
}
|
|
|
|
retval.setSector(dasect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
actor->spr.pos.Z = clippos.Z;
|
|
}
|
|
|
|
// extra processing for Stacks and warping
|
|
if (FAF_ConnectArea(actor->sector()))
|
|
SetActorZ(actor, &actor->spr.pos);
|
|
|
|
if ((actor->sector()->extra & SECTFX_WARP_SECTOR))
|
|
{
|
|
DSWActor* sp_warp;
|
|
if ((sp_warp = WarpPlane(&actor->spr.pos.X, &actor->spr.pos.Y, &actor->spr.pos.Z, &dasect)))
|
|
{
|
|
ActorWarpUpdatePos(actor, dasect);
|
|
ActorWarpType(actor, sp_warp);
|
|
}
|
|
|
|
if (actor->sector() != lastsect)
|
|
{
|
|
if ((sp_warp = Warp(&actor->spr.pos.X, &actor->spr.pos.Y, &actor->spr.pos.Z, &dasect)))
|
|
{
|
|
ActorWarpUpdatePos(actor, dasect);
|
|
ActorWarpType(actor, sp_warp);
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
void MissileWarpUpdatePos(DSWActor* actor, sectortype* sect)
|
|
{
|
|
actor->backuppos();
|
|
actor->user.oz = actor->opos.Z;
|
|
ChangeActorSect(actor, sect);
|
|
MissileZrange(actor);
|
|
}
|
|
|
|
void ActorWarpUpdatePos(DSWActor* actor, sectortype* sect)
|
|
{
|
|
actor->backuppos();
|
|
actor->user.oz = actor->opos.Z;
|
|
ChangeActorSect(actor, sect);
|
|
DoActorZrange(actor);
|
|
}
|
|
|
|
void MissileWarpType(DSWActor* actor, DSWActor* act_warp)
|
|
{
|
|
switch (SP_TAG1(act_warp))
|
|
{
|
|
case WARP_CEILING_PLANE:
|
|
case WARP_FLOOR_PLANE:
|
|
return;
|
|
}
|
|
|
|
switch (SP_TAG3(act_warp))
|
|
{
|
|
case 1:
|
|
break;
|
|
default:
|
|
PlaySound(DIGI_ITEM_SPAWN, actor, v3df_none);
|
|
DoSpawnItemTeleporterEffect(actor);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ActorWarpType(DSWActor* actor, DSWActor* act_warp)
|
|
{
|
|
switch (SP_TAG3(act_warp))
|
|
{
|
|
case 1:
|
|
break;
|
|
default:
|
|
PlaySound(DIGI_ITEM_SPAWN, actor, v3df_none);
|
|
DoSpawnTeleporterEffectPlace(actor);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This moves a small projectile with FAFgetzrangepoint
|
|
//
|
|
|
|
int MissileWaterAdjust(DSWActor* actor)
|
|
{
|
|
auto sectp = actor->user.lo_sectp;
|
|
if (sectp && sectp->hasU())
|
|
{
|
|
if (FixedToInt(sectp->depth_fixed))
|
|
actor->user.loz -= Z(FixedToInt(sectp->depth_fixed));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int MissileZrange(DSWActor* actor)
|
|
{
|
|
// Set the blocking bit to 0 temporarly so FAFgetzrange doesn't pick
|
|
// up its own sprite
|
|
auto tempshort = actor->spr.cstat;
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
|
|
FAFgetzrangepoint(actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - 1, actor->sector(),
|
|
&globhiz, &globhihit, &globloz, &globlohit);
|
|
|
|
actor->spr.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)
|
|
{
|
|
Collision retval{};
|
|
int zh;
|
|
|
|
ASSERT(actor->hasU());
|
|
|
|
vec3_t clippos = actor->spr.pos;
|
|
|
|
// Can't modify sprite sectors
|
|
// directly becuase of linked lists
|
|
auto dasect = actor->sector();
|
|
auto lastsect = dasect;
|
|
|
|
if (actor->spr.cstat & (CSTAT_SPRITE_YCENTER))
|
|
{
|
|
zh = 0;
|
|
}
|
|
else
|
|
{
|
|
zh = actor->user.zclip;
|
|
clippos.Z -= zh;
|
|
}
|
|
|
|
|
|
clipmove(clippos, &dasect,
|
|
((xchange * numtics) << 11), ((ychange * numtics) << 11),
|
|
(((int) actor->spr.clipdist) << 2), ceildist, flordist, cliptype, retval, 1);
|
|
actor->spr.pos.vec2 = clippos.vec2;
|
|
|
|
if (dasect == nullptr)
|
|
{
|
|
// we've gone beyond a white wall - kill it
|
|
retval.setVoid();
|
|
return retval;
|
|
}
|
|
|
|
if ((dasect != actor->sector()) && (dasect != nullptr))
|
|
ChangeActorSect(actor, dasect);
|
|
|
|
// Set the blocking bit to 0 temporarly so FAFgetzrange doesn't pick
|
|
// up its own sprite
|
|
auto tempshort = actor->spr.cstat;
|
|
actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK);
|
|
|
|
FAFgetzrangepoint(actor->spr.pos.X, actor->spr.pos.Y, actor->spr.pos.Z - 1, actor->sector(),
|
|
&globhiz, &globhihit, &globloz, &globlohit);
|
|
|
|
actor->spr.cstat = tempshort;
|
|
|
|
DoActorGlobZ(actor);
|
|
|
|
// getzrangepoint moves water down
|
|
// missiles don't need the water to be down
|
|
MissileWaterAdjust(actor);
|
|
|
|
clippos.Z = actor->spr.pos.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 <= actor->user.hiz + ceildist)
|
|
{
|
|
// normal code
|
|
actor->spr.pos.Z = actor->user.hiz + zh + ceildist;
|
|
if (retval.type == kHitNone)
|
|
retval.setSector(dasect);
|
|
}
|
|
else if (clippos.Z - zh > actor->user.loz - flordist)
|
|
{
|
|
actor->spr.pos.Z = actor->user.loz + zh - flordist;
|
|
if (retval.type == kHitNone)
|
|
retval.setSector(dasect);
|
|
}
|
|
else
|
|
{
|
|
actor->spr.pos.Z = clippos.Z;
|
|
}
|
|
|
|
if (FAF_ConnectArea(actor->sector()))
|
|
SetActorZ(actor, &actor->spr.pos);
|
|
|
|
if ((actor->sector()->extra & SECTFX_WARP_SECTOR))
|
|
{
|
|
DSWActor* sp_warp;
|
|
|
|
if ((sp_warp = WarpPlane(&actor->spr.pos.X, &actor->spr.pos.Y, &actor->spr.pos.Z, &dasect)))
|
|
{
|
|
MissileWarpUpdatePos(actor, dasect);
|
|
MissileWarpType(actor, sp_warp);
|
|
}
|
|
|
|
if (actor->sector() != lastsect)
|
|
{
|
|
if ((sp_warp = Warp(&actor->spr.pos.X, &actor->spr.pos.Y, &actor->spr.pos.Z, &dasect)))
|
|
{
|
|
MissileWarpUpdatePos(actor, dasect);
|
|
MissileWarpType(actor, sp_warp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (retval.type != kHitNone && (actor->sector()->ceilingstat & CSTAT_SECTOR_SKY))
|
|
{
|
|
if (actor->spr.pos.Z < actor->sector()->ceilingz)
|
|
{
|
|
retval.setVoid();
|
|
}
|
|
}
|
|
|
|
if (retval.type != kHitNone && (actor->sector()->floorstat & CSTAT_SECTOR_SKY))
|
|
{
|
|
if (actor->spr.pos.Z > actor->sector()->floorz)
|
|
{
|
|
retval.setVoid();
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
Collision move_ground_missile(DSWActor* actor, int xchange, int ychange, int ceildist, int flordist, uint32_t cliptype, int numtics)
|
|
{
|
|
int daz;
|
|
Collision retval{};
|
|
int ox,oy;
|
|
|
|
ASSERT(actor->hasU());
|
|
|
|
// Can't modify sprite sectors
|
|
// directly becuase of linked lists
|
|
auto dasect = actor->sector();
|
|
auto lastsect = dasect;
|
|
|
|
vec3_t opos = actor->spr.pos;
|
|
daz = actor->spr.pos.Z;
|
|
|
|
// climbing a wall
|
|
if (actor->user.z_tgt)
|
|
{
|
|
if (labs(actor->user.z_tgt - actor->spr.pos.Z) > Z(40))
|
|
{
|
|
if (actor->user.z_tgt > actor->spr.pos.Z)
|
|
{
|
|
actor->spr.pos.Z += Z(30);
|
|
return retval;
|
|
}
|
|
else
|
|
{
|
|
actor->spr.pos.Z -= Z(30);
|
|
return retval;
|
|
}
|
|
}
|
|
else
|
|
actor->user.z_tgt = 0;
|
|
}
|
|
|
|
actor->spr.pos.X += xchange/2;
|
|
actor->spr.pos.Y += ychange/2;
|
|
|
|
updatesector(actor->spr.pos.X, actor->spr.pos.Y, &dasect);
|
|
|
|
if (dasect == nullptr)
|
|
{
|
|
// back up and try again
|
|
dasect = actor->sector();
|
|
lastsect = dasect;
|
|
opos = actor->spr.pos;
|
|
opos.Z = daz;
|
|
clipmove(opos, &dasect,
|
|
((xchange * numtics) << 11), ((ychange * numtics) << 11),
|
|
(((int) actor->spr.clipdist) << 2), ceildist, flordist, cliptype, retval, 1);
|
|
actor->spr.pos.vec2 = opos.vec2;
|
|
}
|
|
|
|
if (dasect == nullptr)
|
|
{
|
|
// we've gone beyond a white wall - kill it
|
|
retval.setVoid();
|
|
return retval;
|
|
}
|
|
|
|
|
|
if (retval.type != kHitNone) // ran into a white wall
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
|
|
actor->user.z_tgt = 0;
|
|
if ((dasect != actor->sector()) && (dasect != nullptr))
|
|
{
|
|
int new_loz,new_hiz;
|
|
getzsofslopeptr(dasect, actor->spr.pos.X, actor->spr.pos.Y, &new_hiz, &new_loz);
|
|
|
|
actor->spr.pos.Z = new_loz;
|
|
ChangeActorSect(actor, dasect);
|
|
}
|
|
|
|
getzsofslopeptr(actor->sector(), actor->spr.pos.X, actor->spr.pos.Y, &actor->user.hiz, &actor->user.loz);
|
|
|
|
actor->user.hi_sectp = actor->user.lo_sectp = actor->sector();
|
|
actor->user.highActor = nullptr; actor->user.lowActor = nullptr;
|
|
actor->spr.pos.Z = actor->user.loz - Z(8);
|
|
|
|
if (labs(actor->user.hiz - actor->user.loz) < Z(12))
|
|
{
|
|
// we've gone into a very small place - kill it
|
|
retval.setVoid();
|
|
return retval;
|
|
}
|
|
|
|
if ((actor->sector()->extra & SECTFX_WARP_SECTOR))
|
|
{
|
|
DSWActor* sp_warp;
|
|
|
|
if ((sp_warp = WarpPlane(&actor->spr.pos.X, &actor->spr.pos.Y, &actor->spr.pos.Z, &dasect)))
|
|
{
|
|
MissileWarpUpdatePos(actor, dasect);
|
|
MissileWarpType(actor, sp_warp);
|
|
}
|
|
|
|
if (actor->sector() != lastsect)
|
|
{
|
|
if ((sp_warp = Warp(&actor->spr.pos.X, &actor->spr.pos.Y, &actor->spr.pos.Z, &dasect)))
|
|
{
|
|
MissileWarpUpdatePos(actor, dasect);
|
|
MissileWarpType(actor, sp_warp);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 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
|