//------------------------------------------------------------------------- /* 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 // ugly hack stuff. int parallaxyscale_override, pskybits_override; 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 double globhiz, globloz; static Collision globhihit, globlohit; short wait_active_check_offset; double 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 = 4; actor->user.bounce = 0; actor->user.motion_blur_num = 0; actor->user.motion_blur_dist = 16; 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 = 48 - 28; actor->user.ceiling_dist = 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, const DVector3& pos, DAngle init_ang, double vel) { if (sect == nullptr) return nullptr; ASSERT(!Prediction); auto spawnedActor = insertActor(sect, stat); spawnedActor->spr.pos = pos; 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.scale = DVector2(1, 1); spawnedActor->spr.Angles.Yaw = init_ang; spawnedActor->vel.X = 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; actorNew->clipdist = actor->clipdist; 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, int(actor->spr.pos.X), int(actor->spr.pos.Y), int(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->gameflags & 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; 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; } DVector2 dv = BoundList[base_offset].offset->spr.pos.XY() - BoundList[i].offset->spr.pos.XY(); BFSSectorSearch search(BoundList[i].offset->sector()); while (auto dasect = search.GetNext()) { SWSectIterator it2(dasect); while (auto jActor = it2.Next()) { jActor->spr.pos += dv; } for (auto& wal : dasect->walls) { wal.move(wal.pos + dv); 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->actor->spr.pos.XY() += dv; pp->actor->backupvec2(); break; } } } } // get rid of the sprites used it.Reset(STAT_FAF); while (auto actor = it.Next()) { KillActor(actor); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpriteSetupPost(void) { double 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 = it2.Next()) { if (jActor->spr.picnum == ST1) continue; if ((jActor->spr.cstat & (CSTAT_SPRITE_ALIGNMENT_WALL|CSTAT_SPRITE_ALIGNMENT_FLOOR))) continue; if (jActor->hasU()) continue; calcSlope(jActor->sector(), jActor->spr.pos, &cz, &fz); if (abs(jActor->spr.pos.Z - fz) > 4) continue; SpawnUser(jActor, 0, nullptr); change_actor_stat(jActor, STAT_NO_STATE); jActor->user.ceiling_dist = 4; jActor->user.floor_dist = -2; jActor->user.ActorActionFunc = DoActorDebris; jActor->spr.cstat |= CSTAT_SPRITE_BREAKABLE; jActor->spr.extra |= SPRX_BREAKABLE; } } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void SpriteSetup(void) { short num; double 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 calcSlope(actor->sector(), actor->spr.pos, &cz, &fz); if (actor->spr.pos.Z > ((cz + fz) * 0.5)) { // 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 SetActorSizeX(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->angle = actor->spr.Angles.Yaw; 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) // notreallyclipdist 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); defineSky(DEFAULTPSKY, pskybits_override, nullptr, 0, parallaxyscale_override / 8192.f); 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->vel.X = 0; else actor->vel.X = actor->spr.lotag * maptoworld; 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->vel.X = 0; else actor->vel.X = actor->spr.lotag * maptoworld; 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: { HitInfo hit{}; hitscan(actor->spr.pos.plusZ(-8), actor->sector(), DVector3(actor->spr.Angles.Yaw.ToVector() * 1024, 0), hit, CLIPMASK_MISSILE); if (hit.hitWall == nullptr) { KillActor(actor); break; } actor->tempwall = hit.hitWall; // if moves with SO if (TEST_BOOL1(actor)) actor->vel.X = 0; else actor->vel.X = actor->spr.lotag * maptoworld; actor->spr.Angles.Yaw = mapangle(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: { HitInfo hit{}; hitscan(actor->spr.pos.plusZ(-8), actor->sector(), DVector3(actor->spr.Angles.Yaw.ToVector() * 1024, 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)*10) * 120)); 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) { double amt = actor->spr.pos.Z - sectp->floorz; // start in the on position sectp->setfloorz(actor->spr.pos.Z); actor->user.z_tgt = actor->user.pos.Z; MoveSpritesWithSector(actor->sector(), amt, false); // floor } // set orig z actor->opos.Z = sectp->floorz; actor->user.oz = actor->opos.Z; } else { // start off actor->user.pos.Z = sectp->ceilingz; actor->user.z_tgt = actor->spr.pos.Z; if (start_on) { double amt = actor->spr.pos.Z - sectp->ceilingz; // starting in the on position sectp->setceilingz(actor->spr.pos.Z); actor->user.z_tgt = actor->user.pos.Z; MoveSpritesWithSector(actor->sector(), amt, true); // ceiling } // set orig z actor->opos.Z = sectp->ceilingz; actor->user.oz = actor->opos.Z; } 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()->walls.Size()); actor->user.rotator->orig_speed = actor->user.rotator->speed; wallcount = 0; for(auto& wal : actor->sector()->walls) { actor->user.rotator->orig[wallcount] = wal.pos; 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; double florz,ceilz; 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, actor->sector(), &ceilz, &trash, &florz, &trash); if (floor_vator) { actor->user.zclip = florz; // 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->user.zclip; actor->opos.Z = actor->user.oz; } else { actor->user.zclip = ceilz; // 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->user.zclip; actor->opos.Z = actor->user.oz; } 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 : actor->sector()->walls) { 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 : actor->sector()->walls) { 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 : actor->sector()->walls) { 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 : actor->sector()->walls) { 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->vel.Z = 100 / 256.; //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 += 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) = int(fabs(sectp->ceilingz - sectp->floorz)); 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.Angles.Yaw += DAngle180; actorNew->spr.picnum = actor->spr.picnum; actorNew->spr.pos += actor->spr.Angles.Yaw.ToVector() * 24; 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.scale.X == 1 && actor->spr.scale.Y == 1) // clear default scale. actor->spr.scale = DVector2(0, 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", int(actor->spr.pos.X), int(actor->spr.pos.Y), int(itActor->spr.pos.X), int(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) * zmaptoworld; PlaxFloorGlobZadjust = SP_TAG3(actor) * zmaptoworld; 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()->walls.Data(); 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()->walls.Data(); 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: 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); SetActorSizeX(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 = (6); actor->user.floor_dist = (0); actor->user.Counter = 0; actor->vel.X = SP_TAG7(spawner) * 0.25; actor->vel.Z = SP_TAG8(spawner) * -0.125; UpdateChange(actor); } } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- 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 = ActorVectOfMiddle(actor); actorNew->spr.Angles.Yaw = nullAngle; 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 = ActorVectOfMiddle(actor); actorNew->spr.Angles.Yaw = nullAngle; 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 = ActorVectOfMiddle(actor); actorNew->spr.Angles.Yaw = nullAngle; 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 = ActorVectOfMiddle(actor); actorNew->spr.Angles.Yaw = nullAngle; 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 = ActorVectOfMiddle(actor); actorNew->spr.Angles.Yaw = nullAngle; 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.XY() = actor->spr.pos.XY(); actorNew->spr.pos.Z = ActorLowerZ(actor) + 10; actorNew->spr.Angles.Yaw = actor->spr.Angles.Yaw; // 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.Angles.Yaw = itActor->spr.Angles.Yaw; break; case 91: spawnedActor = BunnyHatch2(itActor); spawnedActor->user.spal = spawnedActor->spr.pal = PALETTE_PLAYER0; // Girl spawnedActor->spr.Angles.Yaw = itActor->spr.Angles.Yaw; break; case 92: spawnedActor = BunnyHatch2(itActor); spawnedActor->spr.Angles.Yaw = itActor->spr.Angles.Yaw; 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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, itActor->spr.Angles.Yaw); 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) { if (!actor_a->hasU() || !actor_b->hasU()) return false; double dist = (actor_a->spr.pos.XY() - actor_b->spr.pos.XY()).Length(); if (dist > (actor_a->user.fRadius() + actor_b->user.fRadius())) { return false; } double spa_tos = ActorZOfTop(actor_a); double spa_bos = ActorZOfBottom(actor_a); double spb_tos = ActorZOfTop(actor_b); double spb_bos = ActorZOfBottom(actor_b); double 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, double z_overlap) { double spa_tos = ActorZOfTop(actor_a); double spa_bos = ActorZOfBottom(actor_a); double spb_tos = ActorZOfTop(actor_b); double 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(const DVector3& pos, sectortype* sect, double* ceil_z, Collision* ceilhit, double* flor_z, Collision* florhit) { short cstat; if (sect == nullptr) { *ceil_z = 0x800000; *flor_z = 0x7fffff; florhit->invalidate(); return; } // Initialize z's and hits to the current sector's top&bottom calcSlope(sect, pos, ceil_z, flor_z); 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 double fdaz = spriteGetZOfSlopef(&itActor->spr, pos.XY(), spriteGetSlope(itActor)); // Only check if sprite's 2-sided or your on the 1-sided side if (((cstat & CSTAT_SPRITE_ONE_SIDE) != 0) && ((pos.Z > fdaz) == ((cstat & CSTAT_SPRITE_YFLIP) == 0))) continue; DVector2 out[4]; GetFlatSpritePosition(itActor, itActor->spr.pos.XY(), out); if (!insidePoly(pos.X, pos.Y, out, 4)) continue; // Clipping time! if (pos.Z > fdaz) { if (fdaz > *ceil_z) { *ceil_z = fdaz; ceilhit->setSprite(itActor); } } else { if (fdaz < *flor_z) { *flor_z = fdaz; 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); DVector3 pos = actor->spr.pos.plusZ(-ActorSizeZ(actor) * 0.5); FAFgetzrange(pos, actor->sector(), &actor->user.hiz, &ceilhit, &actor->user.loz, &florhit, actor->clipdist - 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, const DVector3& pos, sectortype* new_sector, double min_height) { double 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(pos.plusZ(-ActorSizeZ(actor) * 0.5), 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) && (abs(loz - pos.Z) <= min_height)) { return false; } break; } case kHitSector: { if (abs(loz - pos.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, double min_height) { auto vect = actor->spr.pos + actor->spr.Angles.Yaw.ToVector() * 16; auto newsector = actor->sector(); updatesector(vect, &newsector); // look straight down for a drop if (ActorDrop(actor, vect, 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, const DVector3& change) { DSWActor* highActor; DSWActor* lowActor; sectortype* lo_sectp,* hi_sectp; 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 auto apos = actor->spr.pos; auto loz = actor->user.loz; auto 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, change, 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 (abs(actor->spr.pos.Z - globloz) > actor->user.lo_step) { // cancel move actor->spr.pos = apos; //actor->spr.pos.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, actor->sector(), actor->user.lo_step)) { // cancel move actor->spr.pos = apos; //actor->spr.pos.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 double dist = (apos.XY() - actor->spr.pos.XY()).Length(); 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_FACTORI = 3; const double GRATE_FACTOR = GRATE_FACTORI * maptoworld; // reduce to 0 to 3 value dir = int(actor->spr.Angles.Yaw.Normalized360().Degrees()) / 90; DVector2 v(0, 0); if ((dir & 1) == 0) { if (dir == 0) v.X = 2 * GRATE_FACTOR; else v.X = -2 * GRATE_FACTOR; } else { if (dir == 1) v.Y= 2 * GRATE_FACTOR; else v.Y= -2 * GRATE_FACTOR; } actor->spr.pos += v; actor->spr.hitag -= GRATE_FACTORI; 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.Angles.Yaw += mapangle(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, nullAngle); 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, nullAngle); 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, nullAngle); 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.plusZ(-12), actor->spr.Angles.Yaw); effect->spr.shade = -40; effect->spr.scale = DVector2(0.5625, 0.5625); 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; 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->vel.X != 0) { if (!DoItemFly(actor)) { actor->vel.X = 0; change_actor_stat(actor, STAT_ITEM); } } TRAVERSE_CONNECT(pnum) { pp = &Player[pnum]; DSWActor* plActor = pp->actor; int weaponswitch = WeaponSwitch(pnum); if (pp->Flags & (PF_DEAD)) continue; double dist = (pp->actor->spr.pos.XY() - actor->spr.pos).Length(); if ((unsigned)dist > (plActor->user.fRadius() + actor->user.fRadius())) { 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, actor->sector(), pp->actor->getPosWithOffsetZ(), 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 (!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 (!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 (!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 (!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 (!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 (!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 (!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 (!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 (!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 (!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, nullAngle); else actorNew = SpawnActor(STAT_ITEM, ICON_FLAG, s_CarryFlag, actor->sector(), actor->spr.pos, nullAngle); 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, double dist) { DSWActor* plActor = pp->actor; // 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 if (FAFcansee(ActorVectOfTop(actor), actor->sector(), ActorUpperVect(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->wallpicnum = 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; 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 double dist = (pp->actor->spr.pos.XY() - actor->spr.pos.XY()).Length(); 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, const DVector3& change, double ceildist, double flordist, uint32_t cliptype, int numtics) { Collision retval{}; double zH; short tempshort; ASSERT(actor->hasU()); auto clip_pos = 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; clip_pos.Z -= zH; } clipmove(clip_pos, &dasect, change * numtics * 0.125, actor->clipdist, ceildist, flordist, cliptype, retval, 1); actor->spr.pos.XY() = clip_pos.XY(); 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! auto pos = actor->spr.pos.plusZ(-zH - zmaptoworld); FAFgetzrange(pos, actor->sector(), &globhiz, &globhihit, &globloz, &globlohit, actor->clipdist - 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); clip_pos.Z = actor->spr.pos.Z + ((change.Z * numtics) * 0.125); // test for hitting ceiling or floor if ((clip_pos.Z - zH <= globhiz) || (clip_pos.Z - zH > globloz)) { if (retval.type == kHitNone) { if (actor->user.Flags & (SPR_CLIMBING)) { actor->spr.pos.Z = clip_pos.Z; return retval; } retval.setSector(dasect); } } else { actor->spr.pos.Z = clip_pos.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; auto posv = actor->spr.pos; if ((sp_warp = WarpPlane(posv, &dasect))) { actor->spr.pos = posv; ActorWarpUpdatePos(actor, dasect); ActorWarpType(actor, sp_warp); } if (actor->sector() != lastsect) { if ((sp_warp = Warp(posv, &dasect))) { actor->spr.pos = posv; 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 -= 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.plusZ(-zmaptoworld), actor->sector(), &globhiz, &globhihit, &globloz, &globlohit); actor->spr.cstat = tempshort; DoActorGlobZ(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- Collision move_missile(DSWActor* actor, const DVector3& change, double ceil_dist, double flor_dist, uint32_t cliptype, int numtics) { Collision retval{}; double zH; ASSERT(actor->hasU()); auto clip_pos = 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; clip_pos.Z -= zH; } clipmove(clip_pos, &dasect, change * numtics * 0.125, actor->clipdist, ceil_dist, flor_dist, cliptype, retval, 1); actor->spr.pos.XY() = clip_pos.XY(); 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.plusZ(-zmaptoworld), 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); clip_pos.Z = actor->spr.pos.Z + ((change.Z * numtics) * 0.125); // 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 (clip_pos.Z - zH <= actor->user.hiz + ceil_dist) { // normal code actor->spr.pos.Z = actor->user.hiz + zH + ceil_dist; if (retval.type == kHitNone) retval.setSector(dasect); } else if (clip_pos.Z - zH > actor->user.loz - flor_dist) { actor->spr.pos.Z = actor->user.loz + zH - flor_dist; if (retval.type == kHitNone) retval.setSector(dasect); } else { actor->spr.pos.Z = clip_pos.Z; } if (FAF_ConnectArea(actor->sector())) SetActorZ(actor, actor->spr.pos); if ((actor->sector()->extra & SECTFX_WARP_SECTOR)) { DSWActor* sp_warp; auto pos = actor->spr.pos; if ((sp_warp = WarpPlane(pos, &dasect))) { actor->spr.pos = pos; MissileWarpUpdatePos(actor, dasect); MissileWarpType(actor, sp_warp); } if (actor->sector() != lastsect) { if ((sp_warp = Warp(pos, &dasect))) { actor->spr.pos = pos; 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, const DVector2& change, double ceildist, double flordist, uint32_t cliptype, int numtics) { 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; auto opos = actor->spr.pos; double daz = actor->spr.pos.Z; // climbing a wall if (actor->user.z_tgt) { if (abs(actor->user.z_tgt - actor->spr.pos.Z) > 40) { if (actor->user.z_tgt > actor->spr.pos.Z) { actor->spr.pos.Z += 30; return retval; } else { actor->spr.pos.Z -= 30; return retval; } } else actor->user.z_tgt = 0; } actor->spr.pos += change * 0.5; updatesector(actor->spr.pos, &dasect); if (dasect == nullptr) { // back up and try again dasect = actor->sector(); lastsect = dasect; opos = actor->spr.pos; opos.Z = daz; clipmove(opos, &dasect, change * numtics * 0.125, actor->clipdist, ceildist, flordist, cliptype, retval, 1); actor->spr.pos.XY() = opos.XY(); } 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)) { actor->spr.pos.Z = getflorzofslopeptr(dasect, actor->spr.pos.X, actor->spr.pos.Y); ChangeActorSect(actor, dasect); } calcSlope(actor->sector(), actor->spr.pos, &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 - 8; if (abs(actor->user.hiz - actor->user.loz) < 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; auto pos = actor->spr.pos; if ((sp_warp = WarpPlane(pos, &dasect))) { actor->spr.pos = pos; MissileWarpUpdatePos(actor, dasect); MissileWarpType(actor, sp_warp); } if (actor->sector() != lastsect) { if ((sp_warp = Warp(pos, &dasect))) { actor->spr.pos = pos; 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