diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index a5d8495a0..8a8197a26 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -24,13 +24,13 @@ jobs: } - { name: "macOS", - os: macos-10.15, + os: macos-11, deps_cmdline: "brew install libvpx", build_type: "Release" } - { name: "macOS", - os: macos-10.15, + os: macos-11, extra_options: "-DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF", deps_cmdline: "brew install libvpx fluidsynth mpg123 libsndfile", build_type: "Debug" @@ -54,14 +54,14 @@ jobs: os: ubuntu-20.04, extra_options: "-DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 \ -DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF", - deps_cmdline: "sudo apt update && sudo apt remove gcc-11 libgcc-11-dev g++-11 libstdc++-11-dev && sudo apt install clang-6.0 libstdc++-9-dev libsdl2-dev libvpx-dev libopenal-dev libfluidsynth-dev libmpg123-dev libsndfile1-dev", + deps_cmdline: "sudo apt update && sudo apt install clang-6.0 libsdl2-dev libvpx-dev libopenal-dev libfluidsynth-dev libmpg123-dev libsndfile1-dev", build_type: "Debug" } - { - name: "Linux Clang 11", + name: "Linux Clang 12", os: ubuntu-20.04, - extra_options: "-DCMAKE_C_COMPILER=clang-11 -DCMAKE_CXX_COMPILER=clang++-11", - deps_cmdline: "sudo apt update && sudo apt install clang-11 libsdl2-dev libvpx-dev", + extra_options: "-DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12", + deps_cmdline: "sudo apt update && sudo apt install clang-12 libsdl2-dev libvpx-dev", build_type: "Release" } diff --git a/source/common/utility/tarray.h b/source/common/utility/tarray.h index 2aaf5c0cf..4a9e7388b 100644 --- a/source/common/utility/tarray.h +++ b/source/common/utility/tarray.h @@ -837,6 +837,18 @@ public: CopyNodes(o.Nodes, o.Size); } + TMap(TMap &&o) + { + Nodes = o.Nodes; + LastFree = o.LastFree; /* any free position is before this position */ + Size = o.Size; /* must be a power of 2 */ + NumUsed = o.NumUsed; + + o.Size = 0; + o.NumUsed = 0; + o.SetNodeVector(1); + } + TMap &operator= (const TMap &o) { NumUsed = 0; @@ -846,6 +858,12 @@ public: return *this; } + TMap &operator= (TMap &&o) + { + TransferFrom(o); + return *this; + } + //======================================================================= // // TransferFrom diff --git a/source/core/defparser.cpp b/source/core/defparser.cpp index da0cb5840..64a1080cc 100644 --- a/source/core/defparser.cpp +++ b/source/core/defparser.cpp @@ -2026,7 +2026,7 @@ static bool parseDefineQAVInterpolateIgnoreBlock(FScanner& sc, const int& res_id if (sc.StartBraces(&blockend)) { - pos.Message(MSG_ERROR, "defineqav (%d): interpolate: malformed syntax, unable to continue", res_id); + pos.Message(MSG_ERROR, "defineqav (%d): interpolate: ignore: malformed syntax, unable to continue", res_id); return false; } while (!sc.FoundEndBrace(blockend)) @@ -2039,7 +2039,7 @@ static bool parseDefineQAVInterpolateIgnoreBlock(FScanner& sc, const int& res_id // Confirm we received something for 'frames' and 'tiles'. if (scframes.IsEmpty() || sctiles.IsEmpty()) { - pos.Message(MSG_ERROR, "defineqav (%d): interpolate: unable to get any values for 'frames' or 'tiles', unable to continue", res_id); + pos.Message(MSG_ERROR, "defineqav (%d): interpolate: ignore: unable to get any values for 'frames' or 'tiles', unable to continue", res_id); return false; } @@ -2102,7 +2102,7 @@ static bool parseDefineQAVInterpolateBlock(FScanner& sc, const int& res_id, cons if (sc.StartBraces(&blockend)) { - pos.Message(MSG_ERROR, "defineqav (%d): interpolate (%s): malformed syntax, unable to continue", res_id, interptype.GetChars()); + pos.Message(MSG_ERROR, "defineqav (%d): interpolate: malformed syntax, unable to continue", res_id); return false; } while (!sc.FoundEndBrace(blockend)) @@ -2112,13 +2112,13 @@ static bool parseDefineQAVInterpolateBlock(FScanner& sc, const int& res_id, cons { if (interptype.IsNotEmpty()) { - pos.Message(MSG_ERROR, "defineqav (%d): interpolate (%s): more than one interpolation type defined, unable to continue", res_id, interptype.GetChars()); + pos.Message(MSG_ERROR, "defineqav (%d): interpolate: more than one interpolation type defined, unable to continue", res_id); return false; } sc.GetString(interptype); if (!gi->IsQAVInterpTypeValid(interptype)) { - pos.Message(MSG_ERROR, "defineqav (%d): interpolate (%s): interpolation type not found", res_id, interptype.GetChars()); + pos.Message(MSG_ERROR, "defineqav (%d): interpolate: interpolation type not found", res_id); return false; } } diff --git a/source/core/gamecvars.cpp b/source/core/gamecvars.cpp index 60e1a626b..87df2e201 100644 --- a/source/core/gamecvars.cpp +++ b/source/core/gamecvars.cpp @@ -87,8 +87,10 @@ CVARD(Bool, cl_bloodvanillabobbing, true, CVAR_ARCHIVE, "enable/disable Blood's CVARD(Bool, cl_bloodvanillaexplosions, false, CVAR_ARCHIVE, "enable/disable Blood's vanilla explosion behavior") CVARD(Bool, cl_bloodvanillaenemies, false, CVAR_ARCHIVE, "enable/disable Blood's vanilla enemy behavior") CVARD(Bool, cl_bloodqavinterp, true, CVAR_ARCHIVE, "enable/disable Blood's QAV interpolation") +CVARD(Bool, cl_bloodqavforcedinterp, false, CVAR_ARCHIVE, "enable/disable Blood's QAV interpolation forcefully for QAVs that aren't defined as interpolatable") CVARD(Bool, cl_bloodweapinterp, false, CVAR_ARCHIVE, "enable/disable Blood's weapon interpolation. Depends on 'cl_bloodqavinterp'") CVARD(Bool, cl_bloodoldweapbalance, false, CVAR_ARCHIVE, "enable/disable legacy 1.0 weapon handling for Blood") +CVARD(Bool, cl_loadingscreens, true, CVAR_ARCHIVE, "enable/disable loading screens for games") CUSTOM_CVARD(Int, cl_autoaim, 1, CVAR_ARCHIVE|CVAR_USERINFO, "enable/disable weapon autoaim") diff --git a/source/core/gamecvars.h b/source/core/gamecvars.h index 883af97d6..acbe197eb 100644 --- a/source/core/gamecvars.h +++ b/source/core/gamecvars.h @@ -32,8 +32,10 @@ EXTERN_CVAR(Bool, cl_bloodvanillabobbing) EXTERN_CVAR(Bool, cl_bloodvanillaexplosions) EXTERN_CVAR(Bool, cl_bloodvanillaenemies) EXTERN_CVAR(Bool, cl_bloodqavinterp) +EXTERN_CVAR(Bool, cl_bloodqavforcedinterp) EXTERN_CVAR(Bool, cl_bloodweapinterp) EXTERN_CVAR(Bool, cl_bloodoldweapbalance) +EXTERN_CVAR(Bool, cl_loadingscreens) EXTERN_CVAR(Bool, demorec_seeds_cvar) EXTERN_CVAR(Bool, demoplay_diffs) diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index c0ac0722f..871fddca5 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -222,10 +222,10 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C if (tocluster == nullptr || !CreateCutscene(&tocluster->intro, runner, toMap, !!fromMap)) CreateCutscene(&globalCutscenes.DefaultMapIntro, runner, toMap, !!fromMap); } - // Skip the load screen if the level is started from the console. + // Skip the load screen if the level is started from the console or loading screens are disabled. // In this case the load screen is not helpful as it blocks the actual level start, // requiring closing and reopening the console first before entering any commands that need the level. - if (ConsoleState == c_up || ConsoleState == c_rising) + if ((ConsoleState == c_up || ConsoleState == c_rising) && cl_loadingscreens) CreateCutscene(&globalCutscenes.LoadingScreen, runner, toMap, true); } else if (isShareware()) diff --git a/source/core/searchpaths.cpp b/source/core/searchpaths.cpp index df4c6e51c..61d3f8565 100644 --- a/source/core/searchpaths.cpp +++ b/source/core/searchpaths.cpp @@ -840,9 +840,6 @@ TArray GrpScan() sortedFileList.Delete(0, findex + 1); sortedGroupList.Delete(0, gindex + 1); - if (sortedGroupList.Size() == 0 || sortedFileList.Size() == 0) - return foundGames; - for (auto entry : sortedFileList) { GetCRC(entry, cachedCRCs); diff --git a/source/games/blood/src/actor.cpp b/source/games/blood/src/actor.cpp index 7035739d8..f6c6aa621 100644 --- a/source/games/blood/src/actor.cpp +++ b/source/games/blood/src/actor.cpp @@ -2408,7 +2408,7 @@ static void actInitThings() #endif pXSprite->state = 0; break; - case kThingBloodChunks: + case kThingBloodChunks: { SEQINST* pInst = GetInstance(3, pSprite->extra); if (pInst) @@ -2529,12 +2529,12 @@ static void actInitDudes() void actInit(bool bSaveLoad) { - - #ifdef NOONE_EXTENSIONS + +#ifdef NOONE_EXTENSIONS if (!gModernMap) nnExtResetGlobals(); else nnExtInitModernStuff(bSaveLoad); - #endif - +#endif + BloodStatIterator it(kStatItem); while (auto act = it.Next()) { @@ -2779,7 +2779,7 @@ static DBloodActor* actSpawnFloor(DBloodActor* actor) int y = pSprite->y; updatesector(x, y, §or); int zFloor = getflorzofslope(sector, x, y); - auto* spawned = actSpawnSprite(sector, x, y, zFloor, 3, 0); + auto spawned = actSpawnSprite(sector, x, y, zFloor, 3, 0); if (spawned) spawned->s().cstat &= ~257; return spawned; } @@ -2951,7 +2951,7 @@ static bool actKillModernDude(DBloodActor* actor, DAMAGE_TYPE damageType) auto pXSprite = &actor->x(); GENDUDEEXTRA* pExtra = genDudeExtra(pSprite); removeDudeStuff(pSprite); - if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == nullptr) + if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == nullptr) { if (pExtra->weaponType == kGenDudeWeaponKamikaze && Chance(0x4000) && damageType != kDamageSpirit && damageType != kDamageDrown) { @@ -2972,7 +2972,7 @@ static bool actKillModernDude(DBloodActor* actor, DAMAGE_TYPE damageType) aiGenDudeNewState(pSprite, &genDudeBurnGoto); actHealDude(pXSprite, dudeInfo[55].startHealth, dudeInfo[55].startHealth); if (pXSprite->burnTime <= 0) pXSprite->burnTime = 1200; - actor->dudeExtra().time = PlayClock + 360; + actor->dudeExtra.time = PlayClock + 360; return true; } @@ -3484,6 +3484,7 @@ void actKillDude(DBloodActor* killerActor, DBloodActor* actor, DAMAGE_TYPE damag return; } + auto Owner = actor->GetOwner(); switch (pSprite->type) { case kDudeZombieAxeNormal: @@ -3554,29 +3555,17 @@ void actKillDude(DBloodActor* killerActor, DBloodActor* actor, DAMAGE_TYPE damag break; case kDudeSpiderBrown: - if (pSprite->owner != -1) - { - spritetype* pOwner = &sprite[pSprite->owner]; - gDudeExtra[pOwner->extra].at6.u1.xval2--; - } + if (Owner) Owner->dudeExtra.at6.u1.xval2--; genericDeath(actor, nSeq, 1803, dudeInfo[nType].seqStartID + nSeq); break; case kDudeSpiderRed: - if (pSprite->owner != -1) - { - spritetype* pOwner = &sprite[pSprite->owner]; - gDudeExtra[pOwner->extra].at6.u1.xval2--; - } + if (Owner) Owner->dudeExtra.at6.u1.xval2--; genericDeath(actor, nSeq, 1803, dudeInfo[nType].seqStartID + nSeq); break; case kDudeSpiderBlack: - if (pSprite->owner != -1) - { - spritetype* pOwner = &sprite[pSprite->owner]; - gDudeExtra[pOwner->extra].at6.u1.xval2--; - } + if (Owner) Owner->dudeExtra.at6.u1.xval2--; genericDeath(actor, nSeq, 1803, dudeInfo[nType].seqStartID + nSeq); break; @@ -4021,12 +4010,12 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode) int nDamage = (50 + Random(50)) << 4; actDamageSprite(missileOwner, actorHit, kDamageBullet, nDamage); } - actExplodeSprite(pMissile); + actExplodeSprite(missileActor); break; case kMissileFlareAlt: sfxKill3DSound(pMissile, -1, -1); - actExplodeSprite(pMissile); + actExplodeSprite(missileActor); break; case kMissileFlareRegular: @@ -4083,7 +4072,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode) break; case kMissileFireballCerberus: - actExplodeSprite(pMissile); + actExplodeSprite(missileActor); if (hitCode == 3 && actorHit && actorHit->hasX()) { if ((pSpriteHit->statnum == kStatThing || pSpriteHit->statnum == kStatDude) && pXSpriteHit->burnTime == 0) @@ -4094,11 +4083,11 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode) int nDamage = (25 + Random(10)) << 4; actDamageSprite(missileOwner, actorHit, kDamageBullet, nDamage); } - actExplodeSprite(pMissile); + actExplodeSprite(missileActor); break; case kMissileFireballTchernobog: - actExplodeSprite(pMissile); + actExplodeSprite(missileActor); if (hitCode == 3 && actorHit && actorHit->hasX()) { if ((pSpriteHit->statnum == kStatThing || pSpriteHit->statnum == kStatDude) && pXSpriteHit->burnTime == 0) @@ -4109,7 +4098,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode) int nDamage = (25 + Random(10)) << 4; actDamageSprite(missileOwner, actorHit, kDamageBullet, nDamage); } - actExplodeSprite(pMissile); + actExplodeSprite(missileActor); break; case kMissileEctoSkull: @@ -4208,10 +4197,10 @@ static void actTouchFloor(DBloodActor* actor, int nSector) bool doDamage = (pXSector && (pSector->type == kSectorDamage || pXSector->damageType > 0)); // don't allow damage for damage sectors if they are not enabled - #ifdef NOONE_EXTENSIONS +#ifdef NOONE_EXTENSIONS if (gModernMap && doDamage && pSector->type == kSectorDamage && !pXSector->state) doDamage = false; - #endif +#endif if (doDamage) { DAMAGE_TYPE nDamageType; @@ -4360,7 +4349,6 @@ static void checkHit(DBloodActor* actor) { auto actor2 = coll.actor; spritetype* pSprite2 = &actor2->s(); - //XSPRITE *pXSprite2 = &Xsprite[pSprite2->extra]; #ifdef NOONE_EXTENSIONS // add size shroom abilities @@ -4607,1827 +4595,1833 @@ static void ProcessTouchObjects(DBloodActor* actor) // //--------------------------------------------------------------------------- -void actAirDrag(spritetype *pSprite, int a2) +void actAirDrag(DBloodActor* actor, int a2) { - int vbp = 0; - int v4 = 0; - int nSector = pSprite->sectnum; - assert(nSector >= 0 && nSector < kMaxSectors); - sectortype *pSector = §or[nSector]; - int nXSector = pSector->extra; - if (nXSector > 0) - { - assert(nXSector < kMaxXSectors); - XSECTOR *pXSector = &xsector[nXSector]; - if (pXSector->windVel && (pXSector->windAlways || pXSector->busy)) - { - int vcx = pXSector->windVel<<12; - if (!pXSector->windAlways && pXSector->busy) - vcx = MulScale(vcx, pXSector->busy, 16); - vbp = MulScale(vcx, Cos(pXSector->windAng), 30); - v4 = MulScale(vcx, Sin(pXSector->windAng), 30); - } - } - xvel[pSprite->index] += MulScale(vbp-xvel[pSprite->index], a2, 16); - yvel[pSprite->index] += MulScale(v4-yvel[pSprite->index], a2, 16); - zvel[pSprite->index] -= MulScale(zvel[pSprite->index], a2, 16); + auto pSprite = &actor->s(); + + int wind_x = 0; + int wind_y = 0; + int nSector = pSprite->sectnum; + assert(nSector >= 0 && nSector < kMaxSectors); + sectortype* pSector = §or[nSector]; + int nXSector = pSector->extra; + if (nXSector > 0) + { + assert(nXSector < kMaxXSectors); + XSECTOR* pXSector = &xsector[nXSector]; + if (pXSector->windVel && (pXSector->windAlways || pXSector->busy)) + { + int wind = pXSector->windVel << 12; + if (!pXSector->windAlways && pXSector->busy) wind = MulScale(wind, pXSector->busy, 16); + wind_x = MulScale(wind, Cos(pXSector->windAng), 30); + wind_y = MulScale(wind, Sin(pXSector->windAng), 30); + } + } + actor->xvel() += MulScale(wind_x - actor->xvel(), a2, 16); + actor->yvel() += MulScale(wind_y - actor->yvel(), a2, 16); + actor->zvel() -= MulScale(actor->zvel(), a2, 16); } -int MoveThing(spritetype *pSprite) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +int MoveThing(DBloodActor* actor) { - int nXSprite = pSprite->extra; - assert(nXSprite > 0 && nXSprite < kMaxXSprites); - XSPRITE *pXSprite = &xsprite[nXSprite]; - int nSprite = pSprite->index; - int v8 = 0; - assert(pSprite->type >= kThingBase && pSprite->type < kThingMax); - const THINGINFO *pThingInfo = &thingInfo[pSprite->type-kThingBase]; - int nSector = pSprite->sectnum; - assert(nSector >= 0 && nSector < kMaxSectors); - int top, bottom; - GetSpriteExtents(pSprite, &top, &bottom); - const int bakCompat = enginecompatibility_mode; - if (xvel[nSprite] || yvel[nSprite]) - { - short bakCstat = pSprite->cstat; - pSprite->cstat &= ~257; - if ((pSprite->owner >= 0) && !cl_bloodvanillaexplosions && !VanillaMode()) - enginecompatibility_mode = ENGINECOMPATIBILITY_NONE; // improved clipmove accuracy - v8 = gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, pSprite->clipdist<<2, (pSprite->z-top)/4, (bottom-pSprite->z)/4, CLIPMASK0); - enginecompatibility_mode = bakCompat; // restore - pSprite->cstat = bakCstat; - assert(nSector >= 0); - if (pSprite->sectnum != nSector) - { - assert(nSector >= 0 && nSector < kMaxSectors); - ChangeSpriteSect(nSprite, nSector); - } - if ((gSpriteHit[nXSprite].hit&0xc000) == 0x8000) { - int nHitWall = gSpriteHit[nXSprite].hit&0x3fff; - actWallBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], nHitWall, pThingInfo->elastic); - switch (pSprite->type) { - case kThingZombieHead: - sfxPlay3DSound(pSprite, 607, 0, 0); - actDamageSprite(-1, pSprite, kDamageFall, 80); - break; - case kThingKickablePail: - sfxPlay3DSound(pSprite, 374, 0, 0); - break; - } - } - } - else - { - assert(nSector >= 0 && nSector < kMaxSectors); - FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); - } - if (zvel[nSprite]) - pSprite->z += zvel[nSprite]>>8; - int ceilZ, ceilHit, floorZ, floorHit; - GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0); - GetSpriteExtents(pSprite, &top, &bottom); - if ((pSprite->flags & 2) && bottom < floorZ) - { - pSprite->z += 455; - zvel[nSprite] += 58254; - if (pSprite->type == kThingZombieHead) - { - spritetype *pFX = gFX.fxSpawn(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); - if (pFX) - { - int v34 = (PlayClock*3)&2047; - int v30 = (PlayClock*5)&2047; - int vbx = (PlayClock*11)&2047; - int v2c = 0x44444; - int v28 = 0; - int v24 = 0; - RotateVector(&v2c,&v28,vbx); - RotateVector(&v2c,&v24,v30); - RotateVector(&v28,&v24,v34); - xvel[pFX->index] = xvel[pSprite->index]+v2c; - yvel[pFX->index] = yvel[pSprite->index]+v28; - zvel[pFX->index] = zvel[pSprite->index]+v24; - } - } - } - if (CheckLink(pSprite)) - GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0); - GetSpriteExtents(pSprite, &top, &bottom); - if (bottom >= floorZ) - { - actTouchFloor(&bloodActors[pSprite->index], pSprite->sectnum); - gSpriteHit[nXSprite].florhit = floorHit; - pSprite->z += floorZ-bottom; - int v20 = zvel[nSprite]-velFloor[pSprite->sectnum]; - if (v20 > 0) - { + auto pSprite = &actor->s(); + assert(actor->hasX()); + XSPRITE* pXSprite = &actor->x(); + int lhit = 0; + assert(pSprite->type >= kThingBase && pSprite->type < kThingMax); + const THINGINFO* pThingInfo = &thingInfo[pSprite->type - kThingBase]; + int nSector = pSprite->sectnum; + assert(nSector >= 0 && nSector < kMaxSectors); + int top, bottom; - pSprite->flags |= 4; - int vax = actFloorBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], (int*)&v20, pSprite->sectnum, pThingInfo->elastic); - int nDamage = MulScale(vax, vax, 30)-pThingInfo->dmgResist; - if (nDamage > 0) - actDamageSprite(nSprite, pSprite, kDamageFall, nDamage); - zvel[nSprite] = v20; - if (velFloor[pSprite->sectnum] == 0 && abs(zvel[nSprite]) < 0x10000) - { - zvel[nSprite] = 0; + GetActorExtents(actor, &top, &bottom); + const int bakCompat = enginecompatibility_mode; + if (actor->xvel() || actor->yvel()) + { + short bakCstat = pSprite->cstat; + pSprite->cstat &= ~257; + if ((pSprite->owner >= 0) && !cl_bloodvanillaexplosions && !VanillaMode()) + enginecompatibility_mode = ENGINECOMPATIBILITY_NONE; // improved clipmove accuracy + lhit = actor->hit().hit = ClipMove(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, actor->xvel() >> 12, actor->yvel() >> 12, pSprite->clipdist << 2, (pSprite->z - top) / 4, (bottom - pSprite->z) / 4, CLIPMASK0); + enginecompatibility_mode = bakCompat; // restore + pSprite->cstat = bakCstat; + assert(nSector >= 0); + if (pSprite->sectnum != nSector) + { + assert(nSector >= 0 && nSector < kMaxSectors); + ChangeSpriteSect(pSprite->index, nSector); + } - pSprite->flags &= ~4; - } - - switch (pSprite->type) { - case kThingNapalmBall: - if (zvel[nSprite] == 0 || Chance(0xA000)) actNapalmMove(&bloodActors[pXSprite->reference]); - break; - case kThingZombieHead: - if (abs(zvel[nSprite]) > 0x80000) { - sfxPlay3DSound(pSprite, 607, 0, 0); - actDamageSprite(-1, pSprite, kDamageFall, 80); - } - break; - case kThingKickablePail: - if (abs(zvel[nSprite]) > 0x80000) - sfxPlay3DSound(pSprite, 374, 0, 0); - break; - } + Collision coll(actor->hit().hit); + if (coll.type == kHitWall) + { + int nHitWall = coll.index; + actWallBounceVector(&actor->xvel(), &actor->yvel(), nHitWall, pThingInfo->elastic); + switch (pSprite->type) + { + case kThingZombieHead: + sfxPlay3DSound(pSprite, 607, 0, 0); + actDamageSprite(nullptr, actor, kDamageFall, 80); + break; - v8 = 0x4000|nSector; - } - else if (zvel[nSprite] == 0) + case kThingKickablePail: + sfxPlay3DSound(pSprite, 374, 0, 0); + break; + } + } + } + else + { + assert(nSector >= 0 && nSector < kMaxSectors); + FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); + } - pSprite->flags &= ~4; - } - else - { - gSpriteHit[nXSprite].florhit = 0; + pSprite->z += actor->zvel() >> 8; - if (pSprite->flags&2) - pSprite->flags |= 4; - } - if (top <= ceilZ) - { - gSpriteHit[nXSprite].ceilhit = ceilHit; - pSprite->z += ClipLow(ceilZ-top, 0); - if (zvel[nSprite] < 0) - { - xvel[nSprite] = MulScale(xvel[nSprite], 0xc000, 16); - yvel[nSprite] = MulScale(yvel[nSprite], 0xc000, 16); - zvel[nSprite] = MulScale(-zvel[nSprite], 0x4000, 16); - switch (pSprite->type) { - case kThingZombieHead: - if (abs(zvel[nSprite]) > 0x80000) { - sfxPlay3DSound(pSprite, 607, 0, 0); - actDamageSprite(-1, pSprite, kDamageFall, 80); - } - break; - case kThingKickablePail: - if (abs(zvel[nSprite]) > 0x80000) - sfxPlay3DSound(pSprite, 374, 0, 0); - break; - } - } - } - else - gSpriteHit[nXSprite].ceilhit = 0; - if (bottom >= floorZ) - { - int nVel = approxDist(xvel[nSprite], yvel[nSprite]); - int nVelClipped = ClipHigh(nVel, 0x11111); - if ((floorHit & 0xc000) == 0xc000) - { - int nHitSprite = floorHit & 0x3fff; - if ((sprite[nHitSprite].cstat & 0x30) == 0) - { - xvel[nSprite] += MulScale(4, pSprite->x - sprite[nHitSprite].x, 2); - yvel[nSprite] += MulScale(4, pSprite->y - sprite[nHitSprite].y, 2); - v8 = gSpriteHit[nXSprite].hit; - } - } - if (nVel > 0) - { - int t = DivScale(nVelClipped, nVel, 16); - xvel[nSprite] -= MulScale(t, xvel[nSprite], 16); - yvel[nSprite] -= MulScale(t, yvel[nSprite], 16); - } - } - if (xvel[nSprite] || yvel[nSprite]) - pSprite->ang = getangle(xvel[nSprite], yvel[nSprite]); - return v8; + int ceilZ, ceilHit, floorZ, floorHit; + GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist << 2, CLIPMASK0); + GetActorExtents(actor, &top, &bottom); + + if ((pSprite->flags & 2) && bottom < floorZ) + { + pSprite->z += 455; + actor->zvel() += 58254; + if (pSprite->type == kThingZombieHead) + { + auto* fxActor = gFX.fxSpawnActor(FX_27, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); + if (fxActor) + { + int v34 = (PlayClock * 3) & 2047; + int v30 = (PlayClock * 5) & 2047; + int vbx = (PlayClock * 11) & 2047; + int v2c = 0x44444; + int v28 = 0; + int v24 = 0; + RotateVector(&v2c, &v28, vbx); + RotateVector(&v2c, &v24, v30); + RotateVector(&v28, &v24, v34); + fxActor->xvel() = actor->xvel() + v2c; + fxActor->yvel() = actor->yvel() + v28; + fxActor->zvel() = actor->zvel() + v24; + } + } + } + if (CheckLink(pSprite)) GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist << 2, CLIPMASK0); + + GetActorExtents(actor, &top, &bottom); + if (bottom >= floorZ) + { + actTouchFloor(actor, pSprite->sectnum); + actor->hit().florhit = floorHit; + pSprite->z += floorZ - bottom; + + int v20 = actor->zvel() - velFloor[pSprite->sectnum]; + if (v20 > 0) + { + + pSprite->flags |= 4; + int vax = actFloorBounceVector(&actor->xvel(), &actor->yvel(), (int*)&v20, pSprite->sectnum, pThingInfo->elastic); + int nDamage = MulScale(vax, vax, 30) - pThingInfo->dmgResist; + if (nDamage > 0) actDamageSprite(actor, actor, kDamageFall, nDamage); + + actor->zvel() = v20; + if (velFloor[pSprite->sectnum] == 0 && abs(actor->zvel()) < 0x10000) + { + actor->zvel() = 0; + pSprite->flags &= ~4; + } + + switch (pSprite->type) + { + case kThingNapalmBall: + if (actor->zvel() == 0 || Chance(0xA000)) actNapalmMove(actor); + break; + + case kThingZombieHead: + if (abs(actor->zvel()) > 0x80000) + { + sfxPlay3DSound(pSprite, 607, 0, 0); + actDamageSprite(nullptr, actor, kDamageFall, 80); + } + break; + + case kThingKickablePail: + if (abs(actor->zvel()) > 0x80000) + sfxPlay3DSound(pSprite, 374, 0, 0); + break; + } + + lhit = kHitSector | nSector; + } + else if (actor->zvel() == 0) + + pSprite->flags &= ~4; + } + else + { + actor->hit().florhit = 0; + + if (pSprite->flags & 2) + pSprite->flags |= 4; + } + + if (top <= ceilZ) + { + actor->hit().ceilhit = ceilHit; + pSprite->z += ClipLow(ceilZ - top, 0); + if (actor->zvel() < 0) + { + actor->xvel() = MulScale(actor->xvel(), 0xc000, 16); + actor->yvel() = MulScale(actor->yvel(), 0xc000, 16); + actor->zvel() = MulScale(-actor->zvel(), 0x4000, 16); + + switch (pSprite->type) + { + case kThingZombieHead: + if (abs(actor->zvel()) > 0x80000) + { + sfxPlay3DSound(pSprite, 607, 0, 0); + actDamageSprite(nullptr, actor, kDamageFall, 80); + } + break; + + case kThingKickablePail: + if (abs(actor->zvel()) > 0x80000) + sfxPlay3DSound(pSprite, 374, 0, 0); + break; + } + } + } + else actor->hit().ceilhit = 0; + + if (bottom >= floorZ) + { + int nVel = approxDist(actor->xvel(), actor->yvel()); + int nVelClipped = ClipHigh(nVel, 0x11111); + Collision coll(floorHit); + + if (coll.type == kHitSprite) + { + auto hitActor = coll.actor; + auto hitSpr = &hitActor->s(); + if ((hitSpr->cstat & 0x30) == 0) + { + actor->xvel() += MulScale(4, pSprite->x - hitSpr->x, 2); + actor->yvel() += MulScale(4, pSprite->y - hitSpr->y, 2); + lhit = actor->hit().hit; + } + } + if (nVel > 0) + { + int t = DivScale(nVelClipped, nVel, 16); + actor->xvel() -= MulScale(t, actor->xvel(), 16); + actor->yvel() -= MulScale(t, actor->yvel(), 16); + } + } + if (actor->xvel() || actor->yvel()) + pSprite->ang = getangle(actor->xvel(), actor->yvel()); + return lhit; } -void MoveDude(spritetype *pSprite) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void MoveDude(DBloodActor* actor) { - int nXSprite = pSprite->extra; - XSPRITE *pXSprite = &xsprite[nXSprite]; - int nSprite = pSprite->index; - PLAYER *pPlayer = NULL; - if (IsPlayerSprite(pSprite)) - pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; - if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { - Printf(PRINT_HIGH, "pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); - return; - } - DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - int top, bottom; - GetSpriteExtents(pSprite, &top, &bottom); - int bz = (bottom-pSprite->z)/4; - int tz = (pSprite->z-top)/4; - int wd = pSprite->clipdist<<2; - int nSector = pSprite->sectnum; - int nAiStateType = (pXSprite->aiState) ? pXSprite->aiState->stateType : -1; + auto const pSprite = &actor->s(); + auto const pXSprite = &actor->x(); + PLAYER* pPlayer = nullptr; + if (actor->IsPlayerActor()) pPlayer = &gPlayer[pSprite->type - kDudePlayer1]; + if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) + { + Printf(PRINT_HIGH, "%d: pSprite->type >= kDudeBase && pSprite->type < kDudeMax", pSprite->type); + return; + } - assert(nSector >= 0 && nSector < kMaxSectors); - if (xvel[nSprite] || yvel[nSprite]) - { - if (pPlayer && gNoClip) - { - pSprite->x += xvel[nSprite]>>12; - pSprite->y += yvel[nSprite]>>12; - if (!FindSector(pSprite->x, pSprite->y, &nSector)) - nSector = pSprite->sectnum; - } - else - { - short bakCstat = pSprite->cstat; - pSprite->cstat &= ~257; - gSpriteHit[nXSprite].hit = ClipMove((int*)&pSprite->x, (int*)&pSprite->y, (int*)&pSprite->z, &nSector, xvel[nSprite]>>12, yvel[nSprite]>>12, wd, tz, bz, CLIPMASK0); - if (nSector == -1) - { - nSector = pSprite->sectnum; - if (pSprite->statnum == kStatDude || pSprite->statnum == kStatThing) - actDamageSprite(pSprite->index, pSprite, kDamageFall, 1000<<4); - } + DUDEINFO* pDudeInfo = getDudeInfo(pSprite->type); + int top, bottom; + GetActorExtents(actor, &top, &bottom); + int bz = (bottom - pSprite->z) / 4; + int tz = (pSprite->z - top) / 4; + int wd = pSprite->clipdist << 2; + int nSector = pSprite->sectnum; + int nAiStateType = (pXSprite->aiState) ? pXSprite->aiState->stateType : -1; - if (sector[nSector].type >= kSectorPath && sector[nSector].type <= kSectorRotate) - { - short nSector2 = nSector; - if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector2, wd, tz, bz, CLIPMASK0) == -1) - actDamageSprite(nSprite, pSprite, kDamageFall, 1000 << 4); - if (nSector2 != -1) - nSector = nSector2; - } - assert(nSector >= 0); - pSprite->cstat = bakCstat; - } - switch (gSpriteHit[nXSprite].hit&0xc000) - { - case 0xc000: - { - int nHitSprite = gSpriteHit[nXSprite].hit&0x3fff; - spritetype *pHitSprite = &sprite[nHitSprite]; - XSPRITE *pHitXSprite = NULL; - // Should be pHitSprite here - if (pSprite->extra > 0) - pHitXSprite = &xsprite[pHitSprite->extra]; - int nOwner = pHitSprite->owner; + assert(nSector >= 0 && nSector < kMaxSectors); - if (pHitSprite->statnum == kStatProjectile && !(pHitSprite->flags&32) && pSprite->index != nOwner) - { - HITINFO hitInfo = gHitInfo; - gHitInfo.hitsprite = nSprite; - actImpactMissile(&bloodActors[nHitSprite], 3); - gHitInfo = hitInfo; - } - #ifdef NOONE_EXTENSIONS - if (!gModernMap && pHitXSprite && pHitXSprite->Touch && !pHitXSprite->state && !pHitXSprite->isTriggered) - trTriggerSprite(nHitSprite, pHitXSprite, kCmdSpriteTouch); - #else - if (pHitXSprite && pHitXSprite->Touch && !pHitXSprite->state && !pHitXSprite->isTriggered) - trTriggerSprite(nHitSprite, pHitXSprite, kCmdSpriteTouch); - #endif + if (actor->xvel() || actor->yvel()) + { + if (pPlayer && gNoClip) + { + pSprite->x += actor->xvel() >> 12; + pSprite->y += actor->yvel() >> 12; + if (!FindSector(pSprite->x, pSprite->y, &nSector)) + nSector = pSprite->sectnum; + } + else + { + short bakCstat = pSprite->cstat; + pSprite->cstat &= ~257; + actor->hit().hit = ClipMove(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, actor->xvel() >> 12, actor->yvel() >> 12, wd, tz, bz, CLIPMASK0); + if (nSector == -1) + { + nSector = pSprite->sectnum; + if (pSprite->statnum == kStatDude || pSprite->statnum == kStatThing) + actDamageSprite(pSprite->index, pSprite, kDamageFall, 1000 << 4); + } - if (pDudeInfo->lockOut && pHitXSprite && pHitXSprite->Push && !pHitXSprite->key && !pHitXSprite->DudeLockout && !pHitXSprite->state && !pHitXSprite->busy && !pPlayer) - trTriggerSprite(nHitSprite, pHitXSprite, kCmdSpritePush); + if (sector[nSector].type >= kSectorPath && sector[nSector].type <= kSectorRotate) + { + short nSector2 = nSector; + if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector2, wd, tz, bz, CLIPMASK0) == -1) + actDamageSprite(actor, actor, kDamageFall, 1000 << 4); + if (nSector2 != -1) + nSector = nSector2; + } + assert(nSector >= 0); + pSprite->cstat = bakCstat; + } + Collision coll = actor->hit().hit; + switch (actor->hit().hit & 0xc000) + { + case kHitSprite: + { + spritetype* pHitSprite = &coll.actor->s(); + XSPRITE* pHitXSprite = coll.actor->hasX() ? &coll.actor->x() : nullptr;; - break; - } - case 0x8000: - { - int nHitWall = gSpriteHit[nXSprite].hit&0x3fff; - walltype *pHitWall = &wall[nHitWall]; - XWALL *pHitXWall = NULL; - if (pHitWall->extra > 0) - pHitXWall = &xwall[pHitWall->extra]; - if (pDudeInfo->lockOut && pHitXWall && pHitXWall->triggerPush && !pHitXWall->key && !pHitXWall->dudeLockout && !pHitXWall->state && !pHitXWall->busy && !pPlayer) - trTriggerWall(nHitWall, pHitXWall, kCmdWallPush); - if (pHitWall->nextsector != -1) - { - sectortype *pHitSector = §or[pHitWall->nextsector]; - XSECTOR *pHitXSector = NULL; - if (pHitSector->extra > 0) - pHitXSector = &xsector[pHitSector->extra]; - if (pDudeInfo->lockOut && pHitXSector && pHitXSector->Wallpush && !pHitXSector->Key && !pHitXSector->dudeLockout && !pHitXSector->state && !pHitXSector->busy && !pPlayer) - trTriggerSector(pHitWall->nextsector, pHitXSector, kCmdSectorPush); - if (top < pHitSector->ceilingz || bottom > pHitSector->floorz) - { - // ??? - } - } - actWallBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], nHitWall, 0); - break; - } - } - } - else - { - assert(nSector >= 0 && nSector < kMaxSectors); - FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); - } - if (pSprite->sectnum != nSector) - { - assert(nSector >= 0 && nSector < kMaxSectors); - XSECTOR *pXSector; - int nXSector = sector[pSprite->sectnum].extra; - if (nXSector > 0) - pXSector = &xsector[nXSector]; - else - pXSector = NULL; - if (pXSector && pXSector->Exit && (pPlayer || !pXSector->dudeLockout)) - trTriggerSector(pSprite->sectnum, pXSector, kCmdSectorExit); - ChangeSpriteSect(nSprite, nSector); - - nXSector = sector[nSector].extra; - pXSector = (nXSector > 0) ? &xsector[nXSector] : NULL; - if (pXSector && pXSector->Enter && (pPlayer || !pXSector->dudeLockout)) { + auto Owner = coll.actor->GetOwner(); - if (sector[nSector].type == kSectorTeleport) - pXSector->data = pPlayer ? nSprite : -1; - trTriggerSector(nSector, pXSector, kCmdSectorEnter); - } + if (pHitSprite->statnum == kStatProjectile && !(pHitSprite->flags & 32) && actor != Owner) + { + HITINFO hitInfo = gHitInfo; + gHitInfo.hitsprite = pSprite->index; + actImpactMissile(coll.actor, 3); + gHitInfo = hitInfo; + } +#ifdef NOONE_EXTENSIONS + if (!gModernMap && pHitXSprite && pHitXSprite->Touch && !pHitXSprite->state && !pHitXSprite->isTriggered) + trTriggerSprite(coll.actor, kCmdSpriteTouch); +#else + if (pHitXSprite && pHitXSprite->Touch && !pHitXSprite->state && !pHitXSprite->isTriggered) + trTriggerSprite(coll.actor, kCmdSpriteTouch); +#endif - nSector = pSprite->sectnum; - } - char bUnderwater = 0; - char bDepth = 0; - if (sector[nSector].extra > 0) - { - XSECTOR *pXSector = &xsector[sector[nSector].extra]; - if (pXSector->Underwater) - bUnderwater = 1; - if (pXSector->Depth) - bDepth = 1; - } - int nUpperLink = gUpperLink[nSector]; - int nLowerLink = gLowerLink[nSector]; - if (nUpperLink >= 0 && (sprite[nUpperLink].type == kMarkerUpWater || sprite[nUpperLink].type == kMarkerUpGoo)) - bDepth = 1; - if (nLowerLink >= 0 && (sprite[nLowerLink].type == kMarkerLowWater || sprite[nLowerLink].type == kMarkerLowGoo)) - bDepth = 1; - if (pPlayer) - wd += 16; - if (zvel[nSprite]) - pSprite->z += zvel[nSprite]>>8; - int ceilZ, ceilHit, floorZ, floorHit; - GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); - GetSpriteExtents(pSprite, &top, &bottom); + if (pDudeInfo->lockOut && pHitXSprite && pHitXSprite->Push && !pHitXSprite->key && !pHitXSprite->DudeLockout && !pHitXSprite->state && !pHitXSprite->busy && !pPlayer) + trTriggerSprite(coll.actor, kCmdSpritePush); - if (pSprite->flags & 2) - { - int vc = 58254; - if (bDepth) - { - if (bUnderwater) - { - int cz = getceilzofslope(nSector, pSprite->x, pSprite->y); - if (cz > top) - vc += ((bottom-cz)*-80099) / (bottom-top); - else - vc = 0; - } - else - { - int fz = getflorzofslope(nSector, pSprite->x, pSprite->y); - if (fz < bottom) - vc += ((bottom-fz)*-80099) / (bottom-top); - } - } - else - { - if (bUnderwater) - vc = 0; - else if (bottom >= floorZ) - vc = 0; - } - if (vc) - { - pSprite->z += ((vc*4)/2)>>8; - zvel[nSprite] += vc; - } - } - if (pPlayer && zvel[nSprite] > 0x155555 && !pPlayer->fallScream && pXSprite->height > 0) - { - const bool playerAlive = (pXSprite->health > 0) || VanillaMode(); // only trigger falling scream if player is alive or vanilla mode - if (playerAlive) - { - pPlayer->fallScream = 1; - sfxPlay3DSound(pSprite, 719, 0, 0); - } - } - vec3_t const oldpos = pSprite->pos; - int nLink = CheckLink(pSprite); - if (nLink) - { - GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); - if (pPlayer) - playerCorrectInertia(pPlayer, &oldpos); - switch (nLink) { - case kMarkerLowStack: - if (pPlayer == gView) - setgotpic(sector[pSprite->sectnum].floorpicnum); - break; - case kMarkerUpStack: - if (pPlayer == gView) - setgotpic(sector[pSprite->sectnum].ceilingpicnum); - break; - case kMarkerLowWater: - case kMarkerLowGoo: - pXSprite->medium = kMediumNormal; - if (pPlayer) { - pPlayer->posture = 0; - pPlayer->bubbleTime = 0; - if (!pPlayer->cantJump && (pPlayer->input.actions & SB_JUMP)) { - zvel[nSprite] = -0x6aaaa; - pPlayer->cantJump = 1; - } - sfxPlay3DSound(pSprite, 721, -1, 0); - } else { + break; + } + case kHitWall: + { + int nHitWall = coll.index; + walltype* pHitWall = &wall[nHitWall]; + XWALL* pHitXWall = nullptr; + if (pHitWall->extra > 0) pHitXWall = &xwall[pHitWall->extra]; - switch (pSprite->type) { - case kDudeCultistTommy: - case kDudeCultistShotgun: - aiNewState(&bloodActors[pXSprite->reference], &cultistGoto); - break; - case kDudeGillBeast: - aiNewState(&bloodActors[pXSprite->reference], &gillBeastGoto); - pSprite->flags |= 6; - break; - case kDudeBoneEel: - actKillDude(pSprite->index, pSprite, kDamageFall, 1000<<4); - break; - } + if (pDudeInfo->lockOut && pHitXWall && pHitXWall->triggerPush && !pHitXWall->key && !pHitXWall->dudeLockout && !pHitXWall->state && !pHitXWall->busy && !pPlayer) + trTriggerWall(nHitWall, pHitXWall, kCmdWallPush); - #ifdef NOONE_EXTENSIONS - if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType)) - aiPatrolState(pSprite, kAiStatePatrolMoveL); // continue patrol when going from water - #endif - } - break; - case kMarkerUpWater: - case kMarkerUpGoo: - { - int chance = 0xa00; int medium = kMediumWater; - if (nLink == kMarkerUpGoo){ - medium = kMediumGoo; - chance = 0x400; - } + if (pHitWall->nextsector != -1) + { + sectortype* pHitSector = §or[pHitWall->nextsector]; + XSECTOR* pHitXSector = nullptr; + if (pHitSector->extra > 0) + pHitXSector = &xsector[pHitSector->extra]; - pXSprite->medium = medium; + if (pDudeInfo->lockOut && pHitXSector && pHitXSector->Wallpush && !pHitXSector->Key && !pHitXSector->dudeLockout && !pHitXSector->state && !pHitXSector->busy && !pPlayer) + trTriggerSector(pHitWall->nextsector, pHitXSector, kCmdSectorPush); - if (pPlayer) - { - #ifdef NOONE_EXTENSIONS - // look for palette in data2 of marker. If value <= 0, use default ones. - if (gModernMap) { - pPlayer->nWaterPal = 0; - int nXUpper = sprite[gUpperLink[nSector]].extra; - if (nXUpper >= 0) - pPlayer->nWaterPal = xsprite[nXUpper].data2; - } - #endif + if (top < pHitSector->ceilingz || bottom > pHitSector->floorz) + { + // ??? + } + } + actWallBounceVector((int*)&actor->xvel(), (int*)&actor->yvel(), nHitWall, 0); + break; + } + } + } + else + { + assert(nSector >= 0 && nSector < kMaxSectors); + FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector); + } + if (pSprite->sectnum != nSector) + { + assert(nSector >= 0 && nSector < kMaxSectors); + XSECTOR* pXSector; + int nXSector = sector[pSprite->sectnum].extra; + if (nXSector > 0) + pXSector = &xsector[nXSector]; + else + pXSector = nullptr; + if (pXSector && pXSector->Exit && (pPlayer || !pXSector->dudeLockout)) + trTriggerSector(pSprite->sectnum, pXSector, kCmdSectorExit); + ChangeSpriteSect(pSprite->index, nSector); - pPlayer->posture = 1; - pXSprite->burnTime = 0; - pPlayer->bubbleTime = abs(zvel[nSprite]) >> 12; - evPost(nSprite, 3, 0, kCallbackPlayerBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - } - else - { + nXSector = sector[nSector].extra; + pXSector = (nXSector > 0) ? &xsector[nXSector] : nullptr; + if (pXSector && pXSector->Enter && (pPlayer || !pXSector->dudeLockout)) + { + if (sector[nSector].type == kSectorTeleport) + pXSector->data = pPlayer ? pSprite->index : -1; + trTriggerSector(nSector, pXSector, kCmdSectorEnter); + } - switch (pSprite->type) { - case kDudeCultistTommy: - case kDudeCultistShotgun: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(&bloodActors[pXSprite->reference], &cultistSwimGoto); - break; - case kDudeBurningCultist: - { - const bool fixRandomCultist = !cl_bloodvanillaenemies && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode(); // fix burning cultists randomly switching types underwater - if (Chance(chance)) - pSprite->type = kDudeCultistTommy; - else - pSprite->type = kDudeCultistShotgun; - if (fixRandomCultist) // fix burning cultists randomly switching types underwater - pSprite->type = pSprite->inittype; // restore back to spawned cultist type - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(&bloodActors[pXSprite->reference], &cultistSwimGoto); - break; - } - case kDudeZombieAxeNormal: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(&bloodActors[pXSprite->reference], &zombieAGoto); - break; - case kDudeZombieButcher: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(&bloodActors[pXSprite->reference], &zombieFGoto); - break; - case kDudeGillBeast: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(&bloodActors[pXSprite->reference], &gillBeastSwimGoto); + nSector = pSprite->sectnum; + } + int bUnderwater = 0; + int bDepth = 0; + if (sector[nSector].extra > 0) + { + XSECTOR* pXSector = &xsector[sector[nSector].extra]; + if (pXSector->Underwater) bUnderwater = 1; + if (pXSector->Depth) bDepth = 1; + } + auto pUpperLink = getUpperLink(nSector); + auto pLowerLink = getLowerLink(nSector); + if (pUpperLink && (pUpperLink->s().type == kMarkerUpWater || pUpperLink->s().type == kMarkerUpGoo)) bDepth = 1; + if (pLowerLink && (pLowerLink->s().type == kMarkerLowWater || pLowerLink->s().type == kMarkerLowGoo)) bDepth = 1; + if (pPlayer) wd += 16; + if (actor->zvel()) pSprite->z += actor->zvel() >> 8; - pSprite->flags &= ~6; - break; - case kDudeGargoyleFlesh: - case kDudeHellHound: - case kDudeSpiderBrown: - case kDudeSpiderRed: - case kDudeSpiderBlack: - case kDudeBat: - case kDudeRat: - case kDudeBurningInnocent: - actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4); - break; - } + int ceilZ, ceilHit, floorZ, floorHit; + GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR); + GetActorExtents(actor, &top, &bottom); - #ifdef NOONE_EXTENSIONS - if (gModernMap) { + if (pSprite->flags & 2) + { + int vc = 58254; + if (bDepth) + { + if (bUnderwater) + { + int cz = getceilzofslope(nSector, pSprite->x, pSprite->y); + if (cz > top) + vc += ((bottom - cz) * -80099) / (bottom - top); + else + vc = 0; + } + else + { + int fz = getflorzofslope(nSector, pSprite->x, pSprite->y); + if (fz < bottom) + vc += ((bottom - fz) * -80099) / (bottom - top); + } + } + else + { + if (bUnderwater) + vc = 0; + else if (bottom >= floorZ) + vc = 0; + } + if (vc) + { + pSprite->z += ((vc * 4) / 2) >> 8; + actor->zvel() += vc; + } + } + if (pPlayer && actor->zvel() > 0x155555 && !pPlayer->fallScream && pXSprite->height > 0) + { + const bool playerAlive = (pXSprite->health > 0) || VanillaMode(); // only trigger falling scream if player is alive or vanilla mode + if (playerAlive) + { + pPlayer->fallScream = 1; + sfxPlay3DSound(pSprite, 719, 0, 0); + } + } + vec3_t const oldpos = pSprite->pos; + int nLink = CheckLink(pSprite); + if (nLink) + { + GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR); + if (pPlayer) + playerCorrectInertia(pPlayer, &oldpos); + switch (nLink) + { + case kMarkerLowStack: + if (pPlayer == gView) + setgotpic(sector[pSprite->sectnum].floorpicnum); + break; + case kMarkerUpStack: + if (pPlayer == gView) + setgotpic(sector[pSprite->sectnum].ceilingpicnum); + break; + case kMarkerLowWater: + case kMarkerLowGoo: + pXSprite->medium = kMediumNormal; + if (pPlayer) + { + pPlayer->posture = 0; + pPlayer->bubbleTime = 0; + if (!pPlayer->cantJump && (pPlayer->input.actions & SB_JUMP)) + { + actor->zvel() = -0x6aaaa; + pPlayer->cantJump = 1; + } + sfxPlay3DSound(pSprite, 721, -1, 0); + } + else + { + switch (pSprite->type) + { + case kDudeCultistTommy: + case kDudeCultistShotgun: + aiNewState(&bloodActors[pXSprite->reference], &cultistGoto); + break; + case kDudeGillBeast: + aiNewState(&bloodActors[pXSprite->reference], &gillBeastGoto); + pSprite->flags |= 6; + break; + case kDudeBoneEel: + actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4); + break; + } - if (pSprite->type == kDudeModernCustom) { - - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - if (!canSwim(pSprite)) - actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4); +#ifdef NOONE_EXTENSIONS + if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType)) + aiPatrolState(pSprite, kAiStatePatrolMoveL); // continue patrol when going from water +#endif + } + break; + case kMarkerUpWater: + case kMarkerUpGoo: + { + int chance = 0xa00; int medium = kMediumWater; + if (nLink == kMarkerUpGoo) { + medium = kMediumGoo; + chance = 0x400; + } - } + pXSprite->medium = medium; - // continue patrol when fall into water - if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType)) - aiPatrolState(pSprite, kAiStatePatrolMoveW); + if (pPlayer) + { +#ifdef NOONE_EXTENSIONS + // look for palette in data2 of marker. If value <= 0, use default ones. + if (gModernMap) + { + pPlayer->nWaterPal = 0; + auto pUpper = getUpperLink(nSector); + if (pUpper && pUpper->hasX()) pPlayer->nWaterPal = pUpper->x().data2; + } +#endif - } - #endif + pPlayer->posture = 1; + pXSprite->burnTime = 0; + pPlayer->bubbleTime = abs(actor->zvel()) >> 12; + evPost(actor, 0, kCallbackPlayerBubble); + sfxPlay3DSound(pSprite, 720, -1, 0); + } + else + { + switch (pSprite->type) + { + case kDudeCultistTommy: + case kDudeCultistShotgun: + pXSprite->burnTime = 0; + evPost(actor, 0, kCallbackEnemeyBubble); + sfxPlay3DSound(pSprite, 720, -1, 0); + aiNewState(actor, &cultistSwimGoto); + break; + case kDudeBurningCultist: + { + const bool fixRandomCultist = !cl_bloodvanillaenemies && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode(); // fix burning cultists randomly switching types underwater + if (Chance(chance)) + pSprite->type = kDudeCultistTommy; + else + pSprite->type = kDudeCultistShotgun; + if (fixRandomCultist) // fix burning cultists randomly switching types underwater + pSprite->type = pSprite->inittype; // restore back to spawned cultist type + pXSprite->burnTime = 0; + evPost(actor, 0, kCallbackEnemeyBubble); + sfxPlay3DSound(pSprite, 720, -1, 0); + aiNewState(actor, &cultistSwimGoto); + break; + } + case kDudeZombieAxeNormal: + pXSprite->burnTime = 0; + evPost(actor, 0, kCallbackEnemeyBubble); + sfxPlay3DSound(pSprite, 720, -1, 0); + aiNewState(actor, &zombieAGoto); + break; + case kDudeZombieButcher: + pXSprite->burnTime = 0; + evPost(actor, 0, kCallbackEnemeyBubble); + sfxPlay3DSound(pSprite, 720, -1, 0); + aiNewState(actor, &zombieFGoto); + break; + case kDudeGillBeast: + pXSprite->burnTime = 0; + evPost(actor, 0, kCallbackEnemeyBubble); + sfxPlay3DSound(pSprite, 720, -1, 0); + aiNewState(actor, &gillBeastSwimGoto); - } - break; - } - /*case 13: - pXSprite->medium = kMediumGoo; - if (pPlayer) - { - pPlayer->changeTargetKin = 1; - pXSprite->burnTime = 0; - pPlayer->bubbleTime = abs(zvel[nSprite])>>12; - evPost(nSprite, 3, 0, kCallbackPlayerBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - } - else - { - switch (pSprite->type) - { - case kDudeCultistTommy: - case kDudeCultistShotgun: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(actor, &cultistSwimGoto); - break; - case kDudeBurningCultist: - if (Chance(0x400)) - { - pSprite->type = kDudeCultistTommy; - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(actor, &cultistSwimGoto); - } - else - { - pSprite->type = kDudeCultistShotgun; - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(actor, &cultistSwimGoto); - } - break; - case kDudeZombieAxeNormal: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(actor, &zombieAGoto); - break; - case kDudeZombieButcher: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(actor, &zombieFGoto); - break; - case kDudeGillBeast: - pXSprite->burnTime = 0; - evPost(nSprite, 3, 0, kCallbackEnemeyBubble); - sfxPlay3DSound(pSprite, 720, -1, 0); - aiNewState(actor, &gillBeastSwimGoto); - pSprite->flags &= ~6; - break; - case kDudeGargoyleFlesh: - case kDudeHellHound: - case kDudeSpiderBrown: - case kDudeSpiderRed: - case kDudeSpiderBlack: - case kDudeBat: - case kDudeRat: - case kDudeBurningInnocent: - actKillDude(pSprite->index, pSprite, kDamageFall, 1000<<4); - break; - } - } - break;*/ - } - } - GetSpriteExtents(pSprite, &top, &bottom); - if (pPlayer && bottom >= floorZ) - { - int floorZ2 = floorZ; - int floorHit2 = floorHit; - GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR); - if (bottom <= floorZ && pSprite->z - floorZ2 < bz) - { - floorZ = floorZ2; - floorHit = floorHit2; - } - } - if (floorZ <= bottom) - { - gSpriteHit[nXSprite].florhit = floorHit; - pSprite->z += floorZ-bottom; - int v30 = zvel[nSprite]-velFloor[pSprite->sectnum]; - if (v30 > 0) - { - int vax = actFloorBounceVector((int*)&xvel[nSprite], (int*)&yvel[nSprite], (int*)&v30, pSprite->sectnum, 0); - int nDamage = MulScale(vax, vax, 30); - if (pPlayer) - { - pPlayer->fallScream = 0; + pSprite->flags &= ~6; + break; + case kDudeGargoyleFlesh: + case kDudeHellHound: + case kDudeSpiderBrown: + case kDudeSpiderRed: + case kDudeSpiderBlack: + case kDudeBat: + case kDudeRat: + case kDudeBurningInnocent: + actKillDude(pSprite->index, pSprite, kDamageFall, 1000 << 4); + break; + } - if (nDamage > (15<<4) && (pSprite->flags&4)) - playerLandingSound(pPlayer); - if (nDamage > (30<<4)) - sfxPlay3DSound(pSprite, 701, 0, 0); - } - nDamage -= 100<<4; - if (nDamage > 0) - actDamageSprite(nSprite, pSprite, kDamageFall, nDamage); - zvel[nSprite] = v30; - if (abs(zvel[nSprite]) < 0x10000) - { - zvel[nSprite] = velFloor[pSprite->sectnum]; +#ifdef NOONE_EXTENSIONS + if (gModernMap) { - pSprite->flags &= ~4; - } - else + if (pSprite->type == kDudeModernCustom) { - pSprite->flags |= 4; - switch (tileGetSurfType(floorHit)) - { - case kSurfWater: - gFX.fxSpawn(FX_9, pSprite->sectnum, pSprite->x, pSprite->y, floorZ, 0); - break; - case kSurfLava: - { - spritetype *pFX = gFX.fxSpawn(FX_10, pSprite->sectnum, pSprite->x, pSprite->y, floorZ, 0); - if (pFX) - { - for (int i = 0; i < 7; i++) - { - spritetype *pFX2 = gFX.fxSpawn(FX_14, pFX->sectnum, pFX->x, pFX->y, pFX->z, 0); - if (pFX2) - { - xvel[pFX2->index] = Random2(0x6aaaa); - yvel[pFX2->index] = Random2(0x6aaaa); - zvel[pFX2->index] = -(int)Random(0xd5555); - } - } - } - break; - } - } - } - else if (zvel[nSprite] == 0) + evPost(actor, 0, kCallbackEnemeyBubble); + if (!canSwim(pSprite)) actKillDude(actor, actor, kDamageFall, 1000 << 4); + break; + } - pSprite->flags &= ~4; - } - else - { - gSpriteHit[nXSprite].florhit = 0; + // continue patrol when fall into water + if (IsDudeSprite(pSprite) && pXSprite->health > 0 && aiInPatrolState(nAiStateType)) + aiPatrolState(pSprite, kAiStatePatrolMoveW); - if (pSprite->flags&2) - pSprite->flags |= 4; - } - if (top <= ceilZ) - { - gSpriteHit[nXSprite].ceilhit = ceilHit; - pSprite->z += ClipLow(ceilZ-top, 0); + } +#endif - if (zvel[nSprite] <= 0 && (pSprite->flags&4)) - zvel[nSprite] = MulScale(-zvel[nSprite], 0x2000, 16); - } - else - gSpriteHit[nXSprite].ceilhit = 0; - GetSpriteExtents(pSprite,&top,&bottom); + } + break; + } + } + } + GetActorExtents(actor, &top, &bottom); + if (pPlayer && bottom >= floorZ) + { + int floorZ2 = floorZ; + int floorHit2 = floorHit; + GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist << 2, CLIPMASK0, PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR); + if (bottom <= floorZ && pSprite->z - floorZ2 < bz) + { + floorZ = floorZ2; + floorHit = floorHit2; + } + } + if (floorZ <= bottom) + { + actor->hit().florhit = floorHit; + pSprite->z += floorZ - bottom; + int v30 = actor->zvel() - velFloor[pSprite->sectnum]; + if (v30 > 0) + { + int vax = actFloorBounceVector((int*)&actor->xvel(), (int*)&actor->yvel(), (int*)&v30, pSprite->sectnum, 0); + int nDamage = MulScale(vax, vax, 30); + if (pPlayer) + { + pPlayer->fallScream = 0; - pXSprite->height = ClipLow(floorZ-bottom, 0)>>8; - if (xvel[nSprite] || yvel[nSprite]) - { - if ((floorHit & 0xc000) == 0xc000) - { - int nHitSprite = floorHit & 0x3fff; - if ((sprite[nHitSprite].cstat & 0x30) == 0) - { - xvel[nSprite] += MulScale(4, pSprite->x - sprite[nHitSprite].x, 2); - yvel[nSprite] += MulScale(4, pSprite->y - sprite[nHitSprite].y, 2); - return; - } - } - int nXSector = sector[pSprite->sectnum].extra; - if (nXSector > 0 && xsector[nXSector].Underwater) - return; - if (pXSprite->height >= 0x100) - return; - int nDrag = gDudeDrag; - if (pXSprite->height > 0) - nDrag -= scale(gDudeDrag, pXSprite->height, 0x100); - xvel[nSprite] -= mulscale16r(xvel[nSprite], nDrag); - yvel[nSprite] -= mulscale16r(yvel[nSprite], nDrag); + if (nDamage > (15 << 4) && (pSprite->flags & 4)) + playerLandingSound(pPlayer); + if (nDamage > (30 << 4)) + sfxPlay3DSound(pSprite, 701, 0, 0); + } + nDamage -= 100 << 4; + if (nDamage > 0) + actDamageSprite(actor, actor, kDamageFall, nDamage); + actor->zvel() = v30; + if (abs(actor->zvel()) < 0x10000) + { + actor->zvel() = velFloor[pSprite->sectnum]; - if (approxDist(xvel[nSprite], yvel[nSprite]) < 0x1000) - xvel[nSprite] = yvel[nSprite] = 0; - } + pSprite->flags &= ~4; + } + else + + pSprite->flags |= 4; + switch (tileGetSurfType(floorHit)) + { + case kSurfWater: + gFX.fxSpawn(FX_9, pSprite->sectnum, pSprite->x, pSprite->y, floorZ, 0); + break; + case kSurfLava: + { + spritetype* pFX = gFX.fxSpawn(FX_10, pSprite->sectnum, pSprite->x, pSprite->y, floorZ, 0); + if (pFX) + { + for (int i = 0; i < 7; i++) + { + auto pFX2 = gFX.fxSpawnActor(FX_14, pFX->sectnum, pFX->x, pFX->y, pFX->z, 0); + if (pFX2) + { + pFX2->xvel() = Random2(0x6aaaa); + pFX2->yvel() = Random2(0x6aaaa); + pFX2->zvel() = -(int)Random(0xd5555); + } + } + } + break; + } + } + } + else if (actor->zvel() == 0) + + pSprite->flags &= ~4; + } + else + { + actor->hit().florhit = 0; + + if (pSprite->flags & 2) + pSprite->flags |= 4; + } + if (top <= ceilZ) + { + actor->hit().ceilhit = ceilHit; + pSprite->z += ClipLow(ceilZ - top, 0); + + if (actor->zvel() <= 0 && (pSprite->flags & 4)) + actor->zvel() = MulScale(-actor->zvel(), 0x2000, 16); + } + else + actor->hit().ceilhit = 0; + + GetActorExtents(actor, &top, &bottom); + + pXSprite->height = ClipLow(floorZ - bottom, 0) >> 8; + if (actor->xvel() || actor->yvel()) + { + Collision coll = floorHit; + if (coll.type == kHitSprite) + { + auto hitAct = coll.actor; + if ((hitAct->s().cstat & 0x30) == 0) + { + actor->xvel() += MulScale(4, pSprite->x - hitAct->s().x, 2); + actor->yvel() += MulScale(4, pSprite->y - hitAct->s().y, 2); + return; + } + } + int nXSector = sector[pSprite->sectnum].extra; + if (nXSector > 0 && xsector[nXSector].Underwater) + return; + if (pXSprite->height >= 0x100) + return; + int nDrag = gDudeDrag; + if (pXSprite->height > 0) + nDrag -= scale(gDudeDrag, pXSprite->height, 0x100); + actor->xvel() -= mulscale16r(actor->xvel(), nDrag); + actor->yvel() -= mulscale16r(actor->yvel(), nDrag); + + if (approxDist(actor->xvel(), actor->yvel()) < 0x1000) + actor->xvel() = actor->yvel() = 0; + } } -int MoveMissile(spritetype *pSprite) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +int MoveMissile(DBloodActor* actor) { - int nXSprite = pSprite->extra; - XSPRITE *pXSprite = &xsprite[nXSprite]; - int vdi = -1; - spritetype *pOwner = NULL; - int bakCstat = 0; - if (pSprite->owner >= 0) - { - int nOwner = pSprite->owner; - pOwner = &sprite[nOwner]; - if (IsDudeSprite(pOwner)) - { - bakCstat = pOwner->cstat; - pOwner->cstat &= ~257; - } - else - pOwner = NULL; - } - gHitInfo.hitsect = -1; - gHitInfo.hitwall = -1; - gHitInfo.hitsprite = -1; - if (pSprite->type == kMissileFlameSpray) - actAirDrag(pSprite, 0x1000); - int nSprite = pSprite->index; - if (pXSprite->target != -1 && (xvel[nSprite] || yvel[nSprite] || zvel[nSprite])) - { - spritetype *pTarget = &sprite[pXSprite->target]; - XSPRITE *pXTarget; - if (pTarget->extra > 0) - pXTarget = &xsprite[pTarget->extra]; - else - pXTarget = NULL; - if (pTarget->statnum == kStatDude && pXTarget && pXTarget->health > 0) - { - int nTargetAngle = getangle(-(pTarget->y-pSprite->y), pTarget->x-pSprite->x); - int vx = missileInfo[pSprite->type - kMissileBase].velocity; - int vy = 0; - RotatePoint(&vx, &vy, (nTargetAngle+1536)&2047, 0, 0); - xvel[nSprite] = vx; - yvel[nSprite] = vy; - int dx = pTarget->x-pSprite->x; - int dy = pTarget->y-pSprite->y; - int dz = pTarget->z-pSprite->z; - // Inlined - int vax = dz/10; - if (pTarget->z < pSprite->z) - vax = -vax; - zvel[nSprite] += vax; - ksqrt(dx*dx+dy*dy+dz*dz); - } - } - int vx = xvel[nSprite]>>12; - int vy = yvel[nSprite]>>12; - int vz = zvel[nSprite]>>8; - int top, bottom; - GetSpriteExtents(pSprite, &top, &bottom); - int i = 1; - const int bakCompat = enginecompatibility_mode; - const bool isFlameSprite = (pSprite->type == kMissileFlameSpray || pSprite->type == kMissileFlameHound); // do not use accurate clipmove for flame based sprites (changes damage too much) - while (1) - { - int x = pSprite->x; - int y = pSprite->y; - int z = pSprite->z; - int nSector2 = pSprite->sectnum; - clipmoveboxtracenum = 1; - if ((pSprite->owner >= 0) && !isFlameSprite && !cl_bloodvanillaexplosions && !VanillaMode()) - enginecompatibility_mode = ENGINECOMPATIBILITY_NONE; // improved clipmove accuracy - int vdx = ClipMove(&x, &y, &z, &nSector2, vx, vy, pSprite->clipdist<<2, (z-top)/4, (bottom-z)/4, CLIPMASK0); - enginecompatibility_mode = bakCompat; // restore - clipmoveboxtracenum = 3; - short nSector = nSector2; - if (nSector2 < 0) - { - vdi = -1; - break; - } - if (vdx) - { - int nHitSprite = vdx & 0x3fff; - if ((vdx&0xc000) == 0xc000) - { - gHitInfo.hitsprite = nHitSprite; - vdi = 3; - } - else if ((vdx & 0xc000) == 0x8000) - { - gHitInfo.hitwall = nHitSprite; - if (wall[nHitSprite].nextsector == -1) - vdi = 0; - else - { - int32_t fz, cz; - getzsofslope(wall[nHitSprite].nextsector, x, y, &cz, &fz); - if (z <= cz || z >= fz) - vdi = 0; - else - vdi = 4; - } - } - } - if (vdi == 4) - { - walltype *pWall = &wall[gHitInfo.hitwall]; - if (pWall->extra > 0) - { - XWALL *pXWall = &xwall[pWall->extra]; - if (pXWall->triggerVector) - { - trTriggerWall(gHitInfo.hitwall, pXWall, kCmdWallImpact); - if (!(pWall->cstat&64)) - { - vdi = -1; - if (i-- > 0) - continue; - vdi = 0; - break; - } - } - } - } - if (vdi >= 0 && vdi != 3) - { - int nAngle = getangle(xvel[nSprite], yvel[nSprite]); - x -= MulScale(Cos(nAngle), 16, 30); - y -= MulScale(Sin(nAngle), 16, 30); - int nVel = approxDist(xvel[nSprite], yvel[nSprite]); - vz -= scale(0x100, zvel[nSprite], nVel); - updatesector(x, y, &nSector); - nSector2 = nSector; - } - int ceilZ, ceilHit, floorZ, floorHit; - GetZRangeAtXYZ(x, y, z, nSector2, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0); - GetSpriteExtents(pSprite, &top, &bottom); - top += vz; - bottom += vz; - if (bottom >= floorZ) - { - gSpriteHit[nXSprite].florhit = floorHit; - vz += floorZ-bottom; - vdi = 2; - } - if (top <= ceilZ) - { - gSpriteHit[nXSprite].ceilhit = ceilHit; - vz += ClipLow(ceilZ-top, 0); - vdi = 1; - } - pSprite->x = x; - pSprite->y = y; - pSprite->z = z+vz; - updatesector(x, y, &nSector); - if (nSector >= 0 && nSector != pSprite->sectnum) - { - assert(nSector >= 0 && nSector < kMaxSectors); - ChangeSpriteSect(nSprite, nSector); - } - CheckLink(pSprite); - gHitInfo.hitsect = pSprite->sectnum; - gHitInfo.hitx = pSprite->x; - gHitInfo.hity = pSprite->y; - gHitInfo.hitz = pSprite->z; - break; - } - if (pOwner) - pOwner->cstat = bakCstat; - return vdi; + auto pSprite = &actor->s(); + auto pXSprite = &actor->x(); + auto Owner = actor->GetOwner(); + int cliptype = -1; + int bakCstat = 0; + spritetype* pOwner = nullptr; + if (Owner && Owner->IsDudeActor()) + { + pOwner = &Owner->s(); + bakCstat = pOwner->cstat; + pOwner->cstat &= ~257; + } + gHitInfo.hitsect = -1; + gHitInfo.hitwall = -1; + gHitInfo.hitsprite = -1; + if (pSprite->type == kMissileFlameSpray) actAirDrag(actor, 0x1000); + + if (pXSprite->target != -1 && (actor->xvel() || actor->yvel() || actor->zvel())) + { + auto target = actor->GetTarget(); + spritetype* pTarget = &target->s(); + XSPRITE* pXTarget = target->hasX() ? &target->x() : nullptr; + + if (pTarget->statnum == kStatDude && pXTarget && pXTarget->health > 0) + { + int nTargetAngle = getangle(-(pTarget->y - pSprite->y), pTarget->x - pSprite->x); + int vx = missileInfo[pSprite->type - kMissileBase].velocity; + int vy = 0; + RotatePoint(&vx, &vy, (nTargetAngle + 1536) & 2047, 0, 0); + actor->xvel() = vx; + actor->yvel() = vy; + int dx = pTarget->x - pSprite->x; + int dy = pTarget->y - pSprite->y; + int dz = pTarget->z - pSprite->z; + + int deltaz = dz / 10; + if (pTarget->z < pSprite->z) deltaz = -deltaz; + actor->zvel() += deltaz; + } + } + int vx = actor->xvel() >> 12; + int vy = actor->yvel() >> 12; + int vz = actor->zvel() >> 8; + int top, bottom; + GetActorExtents(actor, &top, &bottom); + int i = 1; + const int bakCompat = enginecompatibility_mode; + const bool isFlameSprite = (pSprite->type == kMissileFlameSpray || pSprite->type == kMissileFlameHound); // do not use accurate clipmove for flame based sprites (changes damage too much) + while (1) + { + int x = pSprite->x; + int y = pSprite->y; + int z = pSprite->z; + int nSector2 = pSprite->sectnum; + clipmoveboxtracenum = 1; + const short bakSpriteCstat = pSprite->cstat; + if (pOwner && !isFlameSprite && !cl_bloodvanillaexplosions && !VanillaMode()) + { + enginecompatibility_mode = ENGINECOMPATIBILITY_NONE; // improved clipmove accuracy + pSprite->cstat &= ~257; // remove self collisions for accurate clipmove + } + Collision clipmoveresult = ClipMove(&x, &y, &z, &nSector2, vx, vy, pSprite->clipdist << 2, (z - top) / 4, (bottom - z) / 4, CLIPMASK0); + enginecompatibility_mode = bakCompat; // restore + pSprite->cstat = bakSpriteCstat; + clipmoveboxtracenum = 3; + short nSector = nSector2; + if (nSector2 < 0) + { + cliptype = -1; + break; + } + if (clipmoveresult.type == kHitSprite) + { + gHitInfo.hitsprite = clipmoveresult.legacyVal & kHitIndexMask; + cliptype = 3; + } + else if (clipmoveresult.type == kHitWall) + { + gHitInfo.hitwall = clipmoveresult.index; + if (wall[clipmoveresult.index].nextsector == -1) cliptype = 0; + else + { + int32_t fz, cz; + getzsofslope(wall[clipmoveresult.index].nextsector, x, y, &cz, &fz); + if (z <= cz || z >= fz) cliptype = 0; + else cliptype = 4; + } + } + if (cliptype == 4) + { + walltype* pWall = &wall[gHitInfo.hitwall]; + if (pWall->extra > 0) + { + XWALL* pXWall = &xwall[pWall->extra]; + if (pXWall->triggerVector) + { + trTriggerWall(gHitInfo.hitwall, pXWall, kCmdWallImpact); + if (!(pWall->cstat & 64)) + { + cliptype = -1; + if (i-- > 0) + continue; + cliptype = 0; + break; + } + } + } + } + if (cliptype >= 0 && cliptype != 3) + { + int nAngle = getangle(actor->xvel(), actor->yvel()); + x -= MulScale(Cos(nAngle), 16, 30); + y -= MulScale(Sin(nAngle), 16, 30); + int nVel = approxDist(actor->xvel(), actor->yvel()); + vz -= scale(0x100, actor->zvel(), nVel); + updatesector(x, y, &nSector); + nSector2 = nSector; + } + int ceilZ, ceilHit, floorZ, floorHit; + GetZRangeAtXYZ(x, y, z, nSector2, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist << 2, CLIPMASK0); + GetActorExtents(actor, &top, &bottom); + top += vz; + bottom += vz; + if (bottom >= floorZ) + { + actor->hit().florhit = floorHit; + vz += floorZ - bottom; + cliptype = 2; + } + if (top <= ceilZ) + { + actor->hit().ceilhit = ceilHit; + vz += ClipLow(ceilZ - top, 0); + cliptype = 1; + } + pSprite->x = x; + pSprite->y = y; + pSprite->z = z + vz; + updatesector(x, y, &nSector); + if (nSector >= 0 && nSector != pSprite->sectnum) + { + assert(nSector >= 0 && nSector < kMaxSectors); + ChangeSpriteSect(pSprite->index, nSector); + } + CheckLink(pSprite); + gHitInfo.hitsect = pSprite->sectnum; + gHitInfo.hitx = pSprite->x; + gHitInfo.hity = pSprite->y; + gHitInfo.hitz = pSprite->z; + break; + } + if (pOwner) pOwner->cstat = bakCstat; + + return cliptype; } -void actExplodeSprite(spritetype *pSprite) -{ - int nXSprite = pSprite->extra; - if (nXSprite <= 0 || nXSprite >= kMaxXSprites) - return; - if (pSprite->statnum == kStatExplosion) - return; - sfxKill3DSound(pSprite, -1, -1); - evKill(pSprite->index, 3); - int nType = kExplosionStandard; +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- - switch (pSprite->type) - { - case kMissileFireballNapalm: - nType = kExplosionNapalm; - seqSpawn(4, 3, nXSprite, -1); - if (Chance(0x8000)) - pSprite->cstat |= 4; - sfxPlay3DSound(pSprite, 303, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - case kMissileFlareAlt: - nType = kExplosionFireball; - seqSpawn(9, 3, nXSprite, -1); - if (Chance(0x8000)) - pSprite->cstat |= 4; - sfxPlay3DSound(pSprite, 306, 24+(pSprite->index&3), 1); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - case kMissileFireballCerberus: - case kMissileFireballTchernobog: - nType = kExplosionFireball; - seqSpawn(5, 3, nXSprite, -1); - sfxPlay3DSound(pSprite, 304, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - case kThingArmedTNTStick: - nType = kExplosionSmall; - if (gSpriteHit[nXSprite].florhit == 0) seqSpawn(4,3,nXSprite,-1); - else seqSpawn(3,3,nXSprite,-1); - sfxPlay3DSound(pSprite, 303, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - case kThingArmedProxBomb: - case kThingArmedRemoteBomb: - case kThingArmedTNTBundle: - #ifdef NOONE_EXTENSIONS - case kModernThingTNTProx: - #endif - nType = kExplosionStandard; - if (gSpriteHit[nXSprite].florhit == 0) - seqSpawn(4,3,nXSprite,-1); - else - seqSpawn(3,3,nXSprite,-1); - sfxPlay3DSound(pSprite, 304, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - case kThingArmedSpray: - nType = kExplosionSpray; - seqSpawn(5, 3, nXSprite, -1); - sfxPlay3DSound(pSprite, 307, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - case kThingTNTBarrel: - { - spritetype *pSprite2 = actSpawnSprite_(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0, 1); - pSprite2->owner = pSprite->owner; - if (actCheckRespawn(pSprite)) - { - XSPRITE *pXSprite = &xsprite[nXSprite]; - pXSprite->state = 1; - pXSprite->health = thingInfo[0].startHealth<<4; - } - else - actPostSprite(pSprite->index, kStatFree); - nType = kExplosionLarge; - nXSprite = pSprite2->extra; - seqSpawn(4, 3, nXSprite, -1); - sfxPlay3DSound(pSprite2, 305, -1, 0); - GibSprite(pSprite2, GIBTYPE_14, NULL, NULL); - pSprite = pSprite2; - break; - } - case kTrapExploder: +void actExplodeSprite(DBloodActor* actor) +{ + if (!actor->hasX()) return; + auto pSprite = &actor->s(); + auto pXSprite = &actor->x(); + //auto Owner = actor->GetOwner(); + + if (pSprite->statnum == kStatExplosion) return; + sfxKill3DSound(pSprite, -1, -1); + evKill(pSprite->index, 3); + + int nType = kExplosionStandard; + + switch (pSprite->type) + { + case kMissileFireballNapalm: + nType = kExplosionNapalm; + seqSpawn(4, actor, -1); + if (Chance(0x8000)) pSprite->cstat |= 4; + sfxPlay3DSound(pSprite, 303, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + + case kMissileFlareAlt: + nType = kExplosionFireball; + seqSpawn(9, actor, -1); + if (Chance(0x8000)) pSprite->cstat |= 4; + sfxPlay3DSound(pSprite, 306, 24 + (pSprite->index & 3), FX_GlobalChannel); // ouch... + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + + case kMissileFireballCerberus: + case kMissileFireballTchernobog: + nType = kExplosionFireball; + seqSpawn(5, actor, -1); + sfxPlay3DSound(pSprite, 304, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + + case kThingArmedTNTStick: + nType = kExplosionSmall; + if (actor->hit().florhit == 0) seqSpawn(4, actor, -1); + else seqSpawn(3, actor, -1); + sfxPlay3DSound(pSprite, 303, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + + case kThingArmedProxBomb: + case kThingArmedRemoteBomb: + case kThingArmedTNTBundle: +#ifdef NOONE_EXTENSIONS + case kModernThingTNTProx: +#endif + nType = kExplosionStandard; + if (actor->hit().florhit == 0) seqSpawn(4, actor, -1); + else + seqSpawn(3, actor, -1); + sfxPlay3DSound(pSprite, 304, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + + case kThingArmedSpray: + nType = kExplosionSpray; + seqSpawn(5, actor, -1); + sfxPlay3DSound(pSprite, 307, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + + case kThingTNTBarrel: + { + auto spawned = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0, 1); + spawned->SetOwner(actor->GetOwner()); + if (actCheckRespawn(pSprite)) + { + pXSprite->state = 1; + pXSprite->health = thingInfo[0].startHealth << 4; + } + else actPostSprite(actor, kStatFree); + + nType = kExplosionLarge; + seqSpawn(4, spawned, -1); + actor = spawned; + pSprite = &spawned->s(); + pXSprite = &spawned->x(); + + sfxPlay3DSound(pSprite, 305, -1, 0); + GibSprite(pSprite, GIBTYPE_14, nullptr, nullptr); + break; + } + case kTrapExploder: { // Defaults for exploder - nType = 1; int nSnd = 304; int nSeq = 4; + nType = 1; + int nSnd = 304; + int nSeq = 4; - #ifdef NOONE_EXTENSIONS - // allow to customize hidden exploder trap - if (gModernMap) { - // Temp variables for override via data fields - int tSnd = 0; int tSeq = 0; +#ifdef NOONE_EXTENSIONS + // allow to customize hidden exploder trap + if (gModernMap) + { + nType = pXSprite->data1; // Explosion type + int tSeq = pXSprite->data2; // SEQ id + int tSnd = pXSprite->data3; // Sound Id + if (nType <= 1 || nType > kExplodeMax) { nType = 1; nSeq = 4; nSnd = 304; } + else if (nType == 2) { nSeq = 4; nSnd = 305; } + else if (nType == 3) { nSeq = 9; nSnd = 307; } + else if (nType == 4) { nSeq = 5; nSnd = 307; } + else if (nType <= 6) { nSeq = 4; nSnd = 303; } + else if (nType == 7) { nSeq = 4; nSnd = 303; } + else if (nType == 8) { nType = 0; nSeq = 3; nSnd = 303; } - XSPRITE* pXSPrite = &xsprite[nXSprite]; - nType = pXSPrite->data1; // Explosion type - tSeq = pXSPrite->data2; // SEQ id - tSnd = pXSPrite->data3; // Sound Id - - if (nType <= 1 || nType > kExplodeMax) { nType = 1; nSeq = 4; nSnd = 304; } - else if (nType == 2) { nSeq = 4; nSnd = 305; } - else if (nType == 3) { nSeq = 9; nSnd = 307; } - else if (nType == 4) { nSeq = 5; nSnd = 307; } - else if (nType <= 6) { nSeq = 4; nSnd = 303; } - else if (nType == 7) { nSeq = 4; nSnd = 303; } - else if (nType == 8) { nType = 0; nSeq = 3; nSnd = 303; } - - // Override previous sound and seq assigns - if (tSeq > 0) nSeq = tSeq; - if (tSnd > 0) nSnd = tSnd; - } - #endif - - if (getSequence(nSeq)) - seqSpawn(nSeq, 3, nXSprite, -1); + // Override previous sound and seq assigns + if (tSeq > 0) nSeq = tSeq; + if (tSnd > 0) nSnd = tSnd; + } +#endif + if (getSequence(nSeq)) seqSpawn(nSeq, actor, -1); sfxPlay3DSound(pSprite, nSnd, -1, 0); + break; } - break; - case kThingPodFireBall: - nType = kExplosionFireball; - seqSpawn(9, 3, nXSprite, -1); - sfxPlay3DSound(pSprite, 307, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - sub_746D4(pSprite, 240); - break; - default: - nType = kExplosionStandard; - seqSpawn(4, 3, nXSprite, -1); - if (Chance(0x8000)) - pSprite->cstat |= 4; - sfxPlay3DSound(pSprite, 303, -1, 0); - GibSprite(pSprite, GIBTYPE_5, NULL, NULL); - break; - } - int nSprite = pSprite->index; - xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0; - actPostSprite(nSprite, kStatExplosion); - pSprite->xrepeat = pSprite->yrepeat = explodeInfo[nType].repeat; + case kThingPodFireBall: + nType = kExplosionFireball; + seqSpawn(9, actor, -1); + sfxPlay3DSound(pSprite, 307, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + sub_746D4(pSprite, 240); + break; - pSprite->flags &= ~3; - pSprite->type = nType; - const EXPLOSION *pExplodeInfo = &explodeInfo[nType]; - xsprite[nXSprite].target = 0; - xsprite[nXSprite].data1 = pExplodeInfo->ticks; - xsprite[nXSprite].data2 = pExplodeInfo->quakeEffect; - xsprite[nXSprite].data3 = pExplodeInfo->flashEffect; + default: + nType = kExplosionStandard; + seqSpawn(4, actor, -1); + if (Chance(0x8000)) pSprite->cstat |= 4; + sfxPlay3DSound(pSprite, 303, -1, 0); + GibSprite(pSprite, GIBTYPE_5, nullptr, nullptr); + break; + } + actor->xvel() = actor->yvel() = actor->zvel() = 0; + actPostSprite(actor, kStatExplosion); + pSprite->xrepeat = pSprite->yrepeat = explodeInfo[nType].repeat; + + pSprite->flags &= ~3; + pSprite->type = nType; + const EXPLOSION* pExplodeInfo = &explodeInfo[nType]; + pXSprite->target = 0; + pXSprite->data1 = pExplodeInfo->ticks; + pXSprite->data2 = pExplodeInfo->quakeEffect; + pXSprite->data3 = pExplodeInfo->flashEffect; } +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + void actActivateGibObject(DBloodActor* actor) { - auto pXSprite = &actor->x(); - auto pSprite = &actor->s(); - int vdx = ClipRange(pXSprite->data1, 0, 31); - int vc = ClipRange(pXSprite->data2, 0, 31); - int v4 = ClipRange(pXSprite->data3, 0, 31); - int vbp = pXSprite->data4; - int v8 = pXSprite->dropMsg; - if (vdx > 0) - GibSprite(pSprite, (GIBTYPE)(vdx-1), NULL, NULL); - if (vc > 0) - GibSprite(pSprite, (GIBTYPE)(vc-1), NULL, NULL); - if (v4 > 0 && pXSprite->burnTime > 0) - GibSprite(pSprite, (GIBTYPE)(v4-1), NULL, NULL); - if (vbp > 0) - sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, vbp, pSprite->sectnum); - if (v8 > 0) - actDropObject(pSprite, v8); + auto pXSprite = &actor->x(); + auto pSprite = &actor->s(); - if (!(pSprite->cstat&32768) && !(pSprite->flags&kHitagRespawn)) - actPostSprite(pSprite->index, kStatFree); + int gib1 = ClipRange(pXSprite->data1, 0, 31); + int gib2 = ClipRange(pXSprite->data2, 0, 31); + int gib3 = ClipRange(pXSprite->data3, 0, 31); + int sound = pXSprite->data4; + int dropmsg = pXSprite->dropMsg; + + if (gib1 > 0) GibSprite(pSprite, (GIBTYPE)(gib1 - 1), nullptr, nullptr); + if (gib2 > 0) GibSprite(pSprite, (GIBTYPE)(gib2 - 1), nullptr, nullptr); + if (gib3 > 0 && pXSprite->burnTime > 0) GibSprite(pSprite, (GIBTYPE)(gib3 - 1), nullptr, nullptr); + if (sound > 0) sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, sound, pSprite->sectnum); + if (dropmsg > 0) actDropObject(pSprite, dropmsg); + + if (!(pSprite->cstat & 32768) && !(pSprite->flags & kHitagRespawn)) + actPostSprite(actor, kStatFree); } -bool IsUnderWater(spritetype *pSprite) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void actCheckProximity() { - int nSector = pSprite->sectnum; - int nXSector = sector[nSector].extra; - if (nXSector > 0 && nXSector < kMaxXSectors) - if (xsector[nXSector].Underwater) - return 1; - return 0; + BloodStatIterator it(kStatThing); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + if (pSprite->flags & 32) continue; + + if (actor->hasX()) + { + XSPRITE* pXSprite = &actor->x(); + switch (pSprite->type) + { + case kThingBloodBits: + case kThingBloodChunks: + case kThingZombieHead: + if (pXSprite->locked && PlayClock >= pXSprite->targetX) pXSprite->locked = 0; + break; + } + + if (pXSprite->burnTime > 0) + { + pXSprite->burnTime = ClipLow(pXSprite->burnTime - 4, 0); + actDamageSprite(actor->GetBurnSource(), actor, kDamageFall, 8); + } + + if (pXSprite->Proximity) + { +#ifdef NOONE_EXTENSIONS + // don't process locked or 1-shot things for proximity + if (gModernMap && (pXSprite->locked || pXSprite->isTriggered)) + continue; +#endif + + if (pSprite->type == kThingDroppedLifeLeech) pXSprite->target = -1; + BloodStatIterator it1(kStatDude); + while (auto dudeactor = it1.Next()) + { + auto nextdude = it1.Peek(); + spritetype* pSprite2 = &dudeactor->s(); + + if (pSprite2->flags & 32 || !dudeactor->hasX()) continue; + + XSPRITE* pXSprite2 = &dudeactor->x(); + if ((unsigned int)pXSprite2->health > 0) + { + int proxyDist = 96; +#ifdef NOONE_EXTENSIONS + // allow dudeLockout for proximity flag + if (gModernMap && pSprite->type != kThingDroppedLifeLeech && pXSprite->DudeLockout && !dudeactor->IsPlayerActor()) + continue; + + if (pSprite->type == kModernThingEnemyLifeLeech) proxyDist = 512; +#endif + if (pSprite->type == kThingDroppedLifeLeech && pXSprite->target == -1) + { + auto Owner = actor->GetOwner(); + if (!Owner->IsPlayerActor()) continue; + + spritetype* pOwner = &Owner->s(); + PLAYER* pPlayer = &gPlayer[pOwner->type - kDudePlayer1]; + PLAYER* pPlayer2 = dudeactor->IsPlayerActor() ? &gPlayer[pSprite2->type - kDudePlayer1] : nullptr; + + if (dudeactor == Owner || pSprite2->type == kDudeZombieAxeBuried || pSprite2->type == kDudeRat || pSprite2->type == kDudeBat) continue; + if (gGameOptions.nGameType == 3 && pPlayer2 && pPlayer->teamId == pPlayer2->teamId) continue; + if (gGameOptions.nGameType == 1 && pPlayer2) continue; + proxyDist = 512; + } + + if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, proxyDist)) + { + switch (pSprite->type) + { + case kThingDroppedLifeLeech: + if (!Chance(0x4000) && nextdude) continue; + if (pSprite2->cstat & CLIPMASK0) pXSprite->target = pSprite2->index; + else continue; + break; + +#ifdef NOONE_EXTENSIONS + case kModernThingTNTProx: + if (!dudeactor->IsPlayerActor()) continue; + pSprite->pal = 0; + break; + + case kModernThingEnemyLifeLeech: + if (pXSprite->target != pSprite2->index) continue; + break; +#endif + + default: + break; + } + if (pSprite->owner == -1) pSprite->owner = pSprite2->index; + trTriggerSprite(actor, kCmdSpriteProximity); + } + } + } + } + } + } } -void MakeSplash(DBloodActor *actor); +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void actCheckThings() +{ + BloodStatIterator it(kStatThing); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + + if (pSprite->flags & 32) continue; + if (!actor->hasX()) continue; + + auto pXSprite = &actor->x(); + int nSector = pSprite->sectnum; + + int nXSector = sector[nSector].extra; + XSECTOR* pXSector = NULL; + if (nXSector > 0) + { + assert(nXSector > 0 && nXSector < kMaxXSectors); + assert(xsector[nXSector].reference == nSector); + pXSector = &xsector[nXSector]; + } + if (pXSector && pXSector->panVel && (pXSector->panAlways || pXSector->state || pXSector->busy)) + { + int nType = pSprite->type - kThingBase; + const THINGINFO* pThingInfo = &thingInfo[nType]; + if (pThingInfo->flags & 1) pSprite->flags |= 1; + if (pThingInfo->flags & 2) pSprite->flags |= 4; + } + + if (pSprite->flags & 3) + { + viewBackupSpriteLoc(actor); + if (pXSector && pXSector->panVel) + { + int top, bottom; + GetActorExtents(actor, &top, &bottom); + if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom) + { + int angle = pXSector->panAngle; + int speed = 0; + if (pXSector->panAlways || pXSector->state || pXSector->busy) + { + speed = pXSector->panVel << 9; + if (!pXSector->panAlways && pXSector->busy) speed = MulScale(speed, pXSector->busy, 16); + } + if (sector[nSector].floorstat & 64) angle = (angle + GetWallAngle(sector[nSector].wallptr) + 512) & 2047; + + actor->xvel() += MulScale(speed, Cos(angle), 30); + actor->yvel() += MulScale(speed, Sin(angle), 30); + } + } + actAirDrag(actor, 128); + + if (((pSprite->index >> 8) & 15) == (gFrameCount & 15) && (pSprite->flags & 2)) pSprite->flags |= 4; + if ((pSprite->flags & 4) || actor->xvel() || actor->yvel() || actor->zvel() || velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum]) + { + Collision hit = MoveThing(actor); + if (hit.type) + { + if (pXSprite->Impact) trTriggerSprite(actor, kCmdOff); + + switch (pSprite->type) + { + case kThingDripWater: + case kThingDripBlood: + MakeSplash(&bloodActors[pXSprite->reference]); + break; +#ifdef NOONE_EXTENSIONS + case kModernThingThrowableRock: + seqSpawn(24, actor, -1); + if (hit.type == kHitSprite) + { + pSprite->xrepeat = 32; + pSprite->yrepeat = 32; + actDamageSprite(actor->GetOwner(), hit.actor, kDamageFall, pXSprite->data1); + } + break; +#endif + case kThingBone: + seqSpawn(24, actor, -1); + if (hit.type == kHitSprite) + { + actDamageSprite(actor->GetOwner(), hit.actor, kDamageFall, 12); + } + break; + + case kThingPodGreenBall: + if (hit.type == kHitSector) + { + actRadiusDamage(actor->GetOwner(), pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 200, 1, 20, kDamageExplode, 6, 0); + evPost(actor, 0, kCallbackFXPodBloodSplat); + } + else if (hit.type == kHitSprite) + { + actDamageSprite(actor->GetOwner(), hit.actor, kDamageFall, 12); + evPost(actor, 0, kCallbackFXPodBloodSplat); + } + break; + + case kThingPodFireBall: + actExplodeSprite(actor); + break; + } + } + } + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void actCheckProjectiles() +{ + BloodStatIterator it(kStatProjectile); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + + if (pSprite->flags & 32) + continue; + viewBackupSpriteLoc(actor); + int hit = MoveMissile(actor); + if (hit >= 0) actImpactMissile(&bloodActors[pSprite->index], hit); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void actCheckExplosion() +{ + BloodStatIterator it(kStatExplosion); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + + if (pSprite->flags & 32) + continue; + + if (!actor->hasX()) continue; + XSPRITE* pXSprite = &actor->x(); + + auto Owner = actor->GetOwner(); + auto pOwner = Owner ? &Owner->s() : nullptr; + int nType = pSprite->type; + assert(nType >= 0 && nType < kExplodeMax); + const EXPLOSION* pExplodeInfo = &explodeInfo[nType]; + int x = pSprite->x; + int y = pSprite->y; + int z = pSprite->z; + int nSector = pSprite->sectnum; + int radius = pExplodeInfo->radius; + +#ifdef NOONE_EXTENSIONS + // Allow to override explosion radius by data4 field of any sprite which have statnum 2 set in editor + // or of Hidden Exploder. + if (gModernMap && pXSprite->data4 > 0) + radius = pXSprite->data4; +#endif + + uint8_t sectormap[(kMaxSectors + 7) >> 3]; + + // GetClosestSpriteSectors() has issues checking some sectors due to optimizations + // the new flag newSectCheckMethod for GetClosestSpriteSectors() does rectify these issues, but this may cause unintended side effects for level scripted explosions + // so only allow this new checking method for dude spawned explosions + short gAffectedXWalls[kMaxXWalls]; + const bool newSectCheckMethod = !cl_bloodvanillaexplosions && Owner && Owner->IsDudeActor() && !VanillaMode(); // use new sector checking logic + GetClosestSpriteSectors(nSector, x, y, radius, sectormap, gAffectedXWalls, newSectCheckMethod); + + for (int i = 0; i < kMaxXWalls; i++) + { + int nWall = gAffectedXWalls[i]; + if (nWall == -1) + break; + XWALL* pXWall = &xwall[wall[nWall].extra]; + trTriggerWall(nWall, pXWall, kCmdWallImpact); + } + + BloodStatIterator it1(kStatDude); + while (auto dudeactor = it1.Next()) + { + spritetype* pDude = &dudeactor->s(); + + if (pDude->flags & 32) continue; + + if (TestBitString(sectormap, pDude->sectnum)) + { + if (pXSprite->data1 && CheckProximity(pDude, x, y, z, nSector, radius)) + { + if (pExplodeInfo->dmg && pXSprite->target == 0) + { + pXSprite->target = 1; + actDamageSprite(Owner, dudeactor, kDamageFall, (pExplodeInfo->dmg + Random(pExplodeInfo->dmgRng)) << 4); + } + if (pExplodeInfo->dmgType) ConcussSprite(actor, dudeactor, x, y, z, pExplodeInfo->dmgType); + + if (pExplodeInfo->burnTime) + { + assert(dudeactor->hasX()); + XSPRITE* pXDude = &dudeactor->x(); + if (!pXDude->burnTime) evPost(dudeactor, 0, kCallbackFXFlameLick); + actBurnSprite(Owner, dudeactor, pExplodeInfo->burnTime << 2); + } + } + } + } + + it1.Reset(kStatThing); + while (auto thingactor = it1.Next()) + { + spritetype* pThing = &thingactor->s(); + + if (pThing->flags & 32) continue; + + if (TestBitString(sectormap, pThing->sectnum)) + { + if (pXSprite->data1 && CheckProximity(pThing, x, y, z, nSector, radius) && thingactor->hasX()) + { + XSPRITE* pXThing = &thingactor->x(); + if (!pXThing->locked) + { + if (pExplodeInfo->dmgType) ConcussSprite(Owner, thingactor, x, y, z, pExplodeInfo->dmgType); + + if (pExplodeInfo->burnTime) + { + if (pThing->type == kThingTNTBarrel && !pXThing->burnTime) + evPost(thingactor, 0, kCallbackFXFlameLick); + actBurnSprite(Owner, thingactor, pExplodeInfo->burnTime << 2); + } + } + } + } + } + + for (int p = connecthead; p >= 0; p = connectpoint2[p]) + { + spritetype* pSprite2 = gPlayer[p].pSprite; + int dx = (x - pSprite2->x) >> 4; + int dy = (y - pSprite2->y) >> 4; + int dz = (z - pSprite2->z) >> 8; + int nDist = dx * dx + dy * dy + dz * dz + 0x40000; + int t = DivScale(pXSprite->data2, nDist, 16); + gPlayer[p].flickerEffect += t; + } + +#ifdef NOONE_EXTENSIONS + if (pXSprite->data1 != 0) + { + // add impulse for sprites from physics list + if (gPhysSpritesCount > 0 && pExplodeInfo->dmgType != 0) + { + for (int i = 0; i < gPhysSpritesCount; i++) + { + if (gPhysSpritesList[i] == -1) continue; + auto physactor = &bloodActors[gPhysSpritesList[i]]; + spritetype* pDebris = &physactor->s(); + if (pDebris->sectnum < 0 || (pDebris->flags & kHitagFree) != 0) continue; + + if (!TestBitString(sectormap, pDebris->sectnum) || !CheckProximity(pDebris, x, y, z, nSector, radius)) continue; + else debrisConcuss(Owner ? Owner->s().index : -1, i, x, y, z, pExplodeInfo->dmgType); + } + } + + // trigger sprites from impact list + if (gImpactSpritesCount > 0) { + for (int i = 0; i < gImpactSpritesCount; i++) + { + if (gImpactSpritesList[i] == -1) continue; + auto impactactor = &bloodActors[gImpactSpritesList[i]]; + auto impactsprite = &impactactor->s(); + if (impactsprite->sectnum < 0 || (impactsprite->flags & kHitagFree) != 0) + continue; + + if (impactsprite->extra <= 0) + continue; + XSPRITE* pXImpact = &impactactor->x(); + if (/*pXImpact->state == pXImpact->restState ||*/ !TestBitString(sectormap, impactsprite->sectnum) || !CheckProximity(impactsprite, x, y, z, nSector, radius)) + continue; + + trTriggerSprite(impactsprite->index, pXImpact, kCmdSpriteImpact); + } + } + + } + + if (!gModernMap || !(pSprite->flags & kModernTypeFlag1)) + { + // if data4 > 0, do not remove explosion. This can be useful when designer wants put explosion generator in map manually via sprite statnum 2. + pXSprite->data1 = ClipLow(pXSprite->data1 - 4, 0); + pXSprite->data2 = ClipLow(pXSprite->data2 - 4, 0); + pXSprite->data3 = ClipLow(pXSprite->data3 - 4, 0); + } +#else + pXSprite->data1 = ClipLow(pXSprite->data1 - 4, 0); + pXSprite->data2 = ClipLow(pXSprite->data2 - 4, 0); + pXSprite->data3 = ClipLow(pXSprite->data3 - 4, 0); +#endif + + if (pXSprite->data1 == 0 && pXSprite->data2 == 0 && pXSprite->data3 == 0 && seqGetStatus(actor) < 0) + actPostSprite(actor, kStatFree); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void actCheckTraps() +{ + BloodStatIterator it(kStatTraps); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + + if ((pSprite->flags & 32) || !actor->hasX()) + continue; + + XSPRITE* pXSprite = &actor->x(); + switch (pSprite->type) { + case kTrapSawCircular: + pXSprite->data2 = ClipLow(pXSprite->data2 - 4, 0); + break; + + case kTrapFlame: + if (pXSprite->state && seqGetStatus(actor) < 0) + { + int x = pSprite->x; + int y = pSprite->y; + int z = pSprite->z; + int t = (pXSprite->data1 << 23) / 120; + int dx = MulScale(t, Cos(pSprite->ang), 30); + int dy = MulScale(t, Sin(pSprite->ang), 30); + for (int i = 0; i < 2; i++) + { + auto pFX = gFX.fxSpawnActor(FX_32, pSprite->sectnum, x, y, z, 0); + if (pFX) + { + pFX->xvel() = dx + Random2(0x8888); + pFX->yvel() = dy + Random2(0x8888); + pFX->zvel() = Random2(0x8888); + } + x += (dx / 2) >> 12; + y += (dy / 2) >> 12; + } + dy = SinScale16(pSprite->ang); + dx = CosScale16(pSprite->ang); + gVectorData[kVectorTchernobogBurn].maxDist = pXSprite->data1 << 9; + actFireVector(pSprite, 0, 0, dx, dy, Random2(0x8888), kVectorTchernobogBurn); + } + break; + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static void actCheckDudes() +{ + BloodStatIterator it(kStatDude); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + + if (pSprite->flags & 32) + continue; + + if (actor->hasX()) + { + XSPRITE* pXSprite = &actor->x(); + const bool fixBurnGlitch = !cl_bloodvanillaenemies && IsBurningDude(pSprite) && !VanillaMode(); // if enemies are burning, always apply burning damage per tick + if ((pXSprite->burnTime > 0) || fixBurnGlitch) + { + switch (pSprite->type) + { + case kDudeBurningInnocent: + case kDudeBurningCultist: + case kDudeBurningZombieAxe: + case kDudeBurningZombieButcher: + actDamageSprite(actor->GetBurnSource(), actor, kDamageBurn, 8); + break; + + default: + pXSprite->burnTime = ClipLow(pXSprite->burnTime - 4, 0); + actDamageSprite(actor->GetBurnSource(), actor, kDamageBurn, 8); + break; + } + } + +#ifdef NOONE_EXTENSIONS + // handle incarnations of custom dude + if (pSprite->type == kDudeModernCustom && pXSprite->txID > 0 && pXSprite->sysData1 == kGenDudeTransformStatus) + { + actor->xvel() = actor->yvel() = 0; + if (seqGetStatus(actor) < 0) genDudeTransform(pSprite); + } +#endif + if (pSprite->type == kDudeCerberusTwoHead) + { + if (pXSprite->health <= 0 && seqGetStatus(actor) < 0) + { + pXSprite->health = dudeInfo[28].startHealth << 4; + pSprite->type = kDudeCerberusOneHead; + if (pXSprite->target != -1) aiSetTarget(pXSprite, pXSprite->target); + aiActivateDude(actor); + } + } + if (pXSprite->Proximity && !pXSprite->isTriggered) + { + BloodStatIterator it1(kStatDude); + while (auto actor2 = it1.Next()) + { + spritetype* pSprite2 = &actor->s(); + if (pSprite2->flags & 32) continue; + + XSPRITE* pXSprite2 = &actor->x(); + + if ((unsigned int)pXSprite2->health > 0 && IsPlayerSprite(pSprite2)) + { + if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 128)) + trTriggerSprite(actor, kCmdSpriteProximity); + } + } + } + if (actor->IsPlayerActor()) + { + PLAYER* pPlayer = &gPlayer[pSprite->type - kDudePlayer1]; + if (pPlayer->voodooTargets) voodooTarget(pPlayer); + if (pPlayer->hand && Chance(0x8000)) actDamageSprite(actor, actor, kDamageDrown, 12); + + if (pPlayer->isUnderwater) + { + char bActive = packItemActive(pPlayer, 1); + + if (bActive || pPlayer->godMode) pPlayer->underwaterTime = 1200; + else pPlayer->underwaterTime = ClipLow(pPlayer->underwaterTime - 4, 0); + + if (pPlayer->underwaterTime < 1080 && packCheckItem(pPlayer, 1) && !bActive) + packUseItem(pPlayer, 1); + + if (!pPlayer->underwaterTime) + { + pPlayer->chokeEffect += 4; + if (Chance(pPlayer->chokeEffect)) + actDamageSprite(actor, actor, kDamageDrown, 3 << 4); + } + else + pPlayer->chokeEffect = 0; + + if (actor->xvel() || actor->yvel()) + sfxPlay3DSound(pSprite, 709, 100, 2); + + pPlayer->bubbleTime = ClipLow(pPlayer->bubbleTime - 4, 0); + } + else if (gGameOptions.nGameType == 0) + { + if (pPlayer->pXSprite->health > 0 && pPlayer->restTime >= 1200 && Chance(0x200)) + { + pPlayer->restTime = -1; + sfxPlay3DSound(pSprite, 3100 + Random(11), 0, 2); + } + } + } + ProcessTouchObjects(actor); + } + } + + it.Reset(kStatDude); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + if (pSprite->flags & 32 || !actor->hasX()) continue; + + int nSector = pSprite->sectnum; + viewBackupSpriteLoc(actor); + int nXSector = sector[nSector].extra; + XSECTOR* pXSector = NULL; + + if (nXSector > 0) + { + assert(nXSector > 0 && nXSector < kMaxXSectors); + assert(xsector[nXSector].reference == nSector); + pXSector = &xsector[nXSector]; + } + if (pXSector) + { + int top, bottom; + GetActorExtents(actor, &top, &bottom); + if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom) + { + int angle = pXSector->panAngle; + int speed = 0; + if (pXSector->panAlways || pXSector->state || pXSector->busy) + { + speed = pXSector->panVel << 9; + if (!pXSector->panAlways && pXSector->busy) + speed = MulScale(speed, pXSector->busy, 16); + } + if (sector[nSector].floorstat & 64) + angle = (angle + GetWallAngle(sector[nSector].wallptr) + 512) & 2047; + int dx = MulScale(speed, Cos(angle), 30); + int dy = MulScale(speed, Sin(angle), 30); + actor->xvel() += dx; + actor->yvel() += dy; + } + } + if (pXSector && pXSector->Underwater) actAirDrag(actor, 5376); + else actAirDrag(actor, 128); + + if ((pSprite->flags & 4) || actor->xvel() || actor->yvel() || actor->zvel() || velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum]) + MoveDude(actor); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void actCheckFlares() +{ + BloodStatIterator it(kStatFlare); + while (auto actor = it.Next()) + { + spritetype* pSprite = &actor->s(); + if ((pSprite->flags & 32) || !actor->hasX()) continue; + + XSPRITE* pXSprite = &actor->x(); + auto target = actor->GetTarget(); + if (!target) continue; + + viewBackupSpriteLoc(actor); + spritetype* pTarget = &target->s(); + auto pXTarget = target->hasX() ? &target->x() : nullptr; + if (pTarget->statnum == kMaxStatus) + { + GibSprite(pSprite, GIBTYPE_17, NULL, NULL); + actPostSprite(actor, kStatFree); + } + if (pXTarget && pXTarget->health > 0) + { + int x = pTarget->x + mulscale30r(Cos(pXSprite->goalAng + pTarget->ang), pTarget->clipdist * 2); + int y = pTarget->y + mulscale30r(Sin(pXSprite->goalAng + pTarget->ang), pTarget->clipdist * 2); + int z = pTarget->z + pXSprite->targetZ; + vec3_t pos = { x, y, z }; + setsprite(pSprite->index, &pos); + actor->xvel() = target->xvel(); + actor->yvel() = target->yvel(); + actor->zvel() = target->zvel(); + } + else + { + GibSprite(pSprite, GIBTYPE_17, NULL, NULL); + actPostSprite(actor, kStatFree); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- void actProcessSprites(void) { - int nSprite; - int nNextSprite; - - #ifdef NOONE_EXTENSIONS - if (gModernMap) nnExtProcessSuperSprites(); - #endif +#ifdef NOONE_EXTENSIONS + if (gModernMap) nnExtProcessSuperSprites(); +#endif - StatIterator it(kStatThing); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags&32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) { - XSPRITE *pXSprite = &xsprite[nXSprite]; - switch (pSprite->type) { - case kThingBloodBits: - case kThingBloodChunks: - case kThingZombieHead: - if (pXSprite->locked && PlayClock >= pXSprite->targetX) pXSprite->locked = 0; - break; - } - - if (pXSprite->burnTime > 0) - { - pXSprite->burnTime = ClipLow(pXSprite->burnTime-4,0); - actDamageSprite(pXSprite->burnSource, pSprite, kDamageBurn, 8); - } - - if (pXSprite->Proximity) { - #ifdef NOONE_EXTENSIONS - // don't process locked or 1-shot things for proximity - if (gModernMap && (pXSprite->locked || pXSprite->isTriggered)) - continue; - #endif - - if (pSprite->type == kThingDroppedLifeLeech) pXSprite->target = -1; - int nSprite2; - StatIterator it1(kStatDude); - while ((nSprite2 = it1.NextIndex()) >= 0) - { - nNextSprite = it1.PeekIndex(); - spritetype *pSprite2 = &sprite[nSprite2]; - - if (pSprite2->flags&32) continue; - XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; - if ((unsigned int)pXSprite2->health > 0) { - - #ifdef NOONE_EXTENSIONS - // allow dudeLockout for proximity flag - if (gModernMap && pSprite->type != kThingDroppedLifeLeech && pXSprite->DudeLockout && !IsPlayerSprite(pSprite2)) - continue; - #endif - - int proxyDist = 96; - #ifdef NOONE_EXTENSIONS - if (pSprite->type == kModernThingEnemyLifeLeech) proxyDist = 512; - #endif - if (pSprite->type == kThingDroppedLifeLeech && pXSprite->target == -1) { - int nOwner = pSprite->owner; - spritetype *pOwner = &sprite[nOwner]; - if (!IsPlayerSprite(pOwner)) - continue; - PLAYER *pPlayer = &gPlayer[pOwner->type - kDudePlayer1]; - PLAYER *pPlayer2 = NULL; - if (IsPlayerSprite(pSprite2)) - pPlayer2 = &gPlayer[pSprite2->type - kDudePlayer1]; - if (nSprite2 == nOwner || pSprite2->type == kDudeZombieAxeBuried || pSprite2->type == kDudeRat || pSprite2->type == kDudeBat) - continue; - if (gGameOptions.nGameType == 3 && pPlayer2 && pPlayer->teamId == pPlayer2->teamId) - continue; - if (gGameOptions.nGameType == 1 && pPlayer2) - continue; - proxyDist = 512; - } - - if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, proxyDist)) { - - switch (pSprite->type) { - case kThingDroppedLifeLeech: - if (!Chance(0x4000) && nNextSprite >= 0) continue; - if (pSprite2->cstat & CLIPMASK0) pXSprite->target = pSprite2->index; - else continue; - break; - #ifdef NOONE_EXTENSIONS - case kModernThingTNTProx: - if (!IsPlayerSprite(pSprite2)) continue; - pSprite->pal = 0; - break; - case kModernThingEnemyLifeLeech: - if (pXSprite->target != pSprite2->index) continue; - break; - #endif - } - if (pSprite->owner == -1) pSprite->owner = pSprite2->index; - trTriggerSprite(nSprite, pXSprite, kCmdSpriteProximity); - } - } - } - } - } - } - - it.Reset(kStatThing); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - int nSector = pSprite->sectnum; - int nXSprite = pSprite->extra; - assert(nXSprite > 0 && nXSprite < kMaxXSprites); - int nXSector = sector[nSector].extra; - XSECTOR *pXSector = NULL; - if (nXSector > 0) - { - assert(nXSector > 0 && nXSector < kMaxXSectors); - assert(xsector[nXSector].reference == nSector); - pXSector = &xsector[nXSector]; - } - if (pXSector && pXSector->panVel && (pXSector->panAlways || pXSector->state || pXSector->busy)) - { - int nType = pSprite->type - kThingBase; - const THINGINFO *pThingInfo = &thingInfo[nType]; - if (pThingInfo->flags & 1) - - pSprite->flags |= 1; - if (pThingInfo->flags & 2) - - pSprite->flags |= 4; - } - - if (pSprite->flags&3) - { - viewBackupSpriteLoc(nSprite, pSprite); - if (pXSector && pXSector->panVel) - { - int top, bottom; - GetSpriteExtents(pSprite, &top, &bottom); - if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom) - { - int angle = pXSector->panAngle; - int speed = 0; - if (pXSector->panAlways || pXSector->state || pXSector->busy) - { - speed = pXSector->panVel << 9; - if (!pXSector->panAlways && pXSector->busy) - speed = MulScale(speed, pXSector->busy, 16); - } - if (sector[nSector].floorstat&64) - angle = (angle+GetWallAngle(sector[nSector].wallptr)+512)&2047; - int dx = MulScale(speed, Cos(angle), 30); - int dy = MulScale(speed, Sin(angle), 30); - xvel[nSprite] += dx; - yvel[nSprite] += dy; - } - } - actAirDrag(pSprite, 128); - - if (((pSprite->index>>8)&15) == (gFrameCount&15) && (pSprite->flags&2)) - pSprite->flags |= 4; - if ((pSprite->flags&4) || xvel[nSprite] || yvel[nSprite] || zvel[nSprite] || - velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum]) - { - int hit = MoveThing(pSprite); - if (hit) - { - int nXSprite = pSprite->extra; - if (nXSprite) - { - XSPRITE *pXSprite = &xsprite[nXSprite]; - if (pXSprite->Impact) - trTriggerSprite(nSprite, pXSprite, kCmdOff); - switch (pSprite->type) { - case kThingDripWater: - case kThingDripBlood: - MakeSplash(&bloodActors[pXSprite->reference]); - break; - #ifdef NOONE_EXTENSIONS - case kModernThingThrowableRock: - seqSpawn(24, 3, nXSprite, -1); - if ((hit & 0xc000) == 0xc000) - { - pSprite->xrepeat = 32; - pSprite->yrepeat = 32; - int nObject = hit & 0x3fff; - assert(nObject >= 0 && nObject < kMaxSprites); - spritetype * pObject = &sprite[nObject]; - actDamageSprite(pSprite->owner, pObject, kDamageFall, pXSprite->data1); - } - break; - #endif - case kThingBone: - seqSpawn(24, 3, nXSprite, -1); - if ((hit&0xc000) == 0xc000) - { - int nObject = hit & 0x3fff; - assert(nObject >= 0 && nObject < kMaxSprites); - spritetype *pObject = &sprite[nObject]; - actDamageSprite(pSprite->owner, pObject, kDamageFall, 12); - } - break; - case kThingPodGreenBall: - if ((hit&0xc000) == 0x4000) - { - actRadiusDamage(&bloodActors[pSprite->owner], pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 200, 1, 20, kDamageExplode, 6, 0); - evPost(pSprite->index, 3, 0, kCallbackFXPodBloodSplat); - } - else - { - int nObject = hit & 0x3fff; - if ((hit&0xc000) != 0xc000 && (nObject < 0 || nObject >= 4096)) - break; - assert(nObject >= 0 && nObject < kMaxSprites); - spritetype *pObject = &sprite[nObject]; - actDamageSprite(pSprite->owner, pObject, kDamageFall, 12); - evPost(pSprite->index, 3, 0, kCallbackFXPodBloodSplat); - } - break; - case kThingPodFireBall: - { - int nObject = hit & 0x3fff; - if ((hit&0xc000) != 0xc000 && (nObject < 0 || nObject >= 4096)) - break; - assert(nObject >= 0 && nObject < kMaxSprites); - actExplodeSprite(pSprite); - break; - } - } - } - } - } - } - } - it.Reset(kStatProjectile); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - viewBackupSpriteLoc(nSprite, pSprite); - int hit = MoveMissile(pSprite); - if (hit >= 0) - actImpactMissile(&bloodActors[pSprite->index], hit); - } - it.Reset(kStatExplosion); - while ((nSprite = it.NextIndex()) >= 0) - { - uint8_t sectmap[(kMaxSectors+7)>>3]; - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - int nOwner = pSprite->owner; - auto pOwner = nOwner == -1? nullptr : &bloodActors[pSprite->owner]; - int nType = pSprite->type; - assert(nType >= 0 && nType < kExplodeMax); - const EXPLOSION *pExplodeInfo = &explodeInfo[nType]; - int nXSprite = pSprite->extra; - assert(nXSprite > 0 && nXSprite < kMaxXSprites); - XSPRITE *pXSprite = &xsprite[nXSprite]; - int x = pSprite->x; - int y = pSprite->y; - int z = pSprite->z; - int nSector = pSprite->sectnum; - int radius = pExplodeInfo->radius; - - #ifdef NOONE_EXTENSIONS - // Allow to override explosion radius by data4 field of any sprite which have statnum 2 set in editor - // or of Hidden Exploder. - if (gModernMap && pXSprite->data4 > 0) - radius = pXSprite->data4; - #endif - - // GetClosestSpriteSectors() has issues checking some sectors due to optimizations - // the new flag newSectCheckMethod for GetClosestSpriteSectors() does rectify these issues, but this may cause unintended side effects for level scripted explosions - // so only allow this new checking method for dude spawned explosions - short gAffectedXWalls[kMaxXWalls]; - const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode(); // use new sector checking logic - GetClosestSpriteSectors(nSector, x, y, radius, sectmap, gAffectedXWalls, newSectCheckMethod); - - for (int i = 0; i < kMaxXWalls; i++) - { - int nWall = gAffectedXWalls[i]; - if (nWall == -1) - break; - XWALL *pXWall = &xwall[wall[nWall].extra]; - trTriggerWall(nWall, pXWall, kCmdWallImpact); - } - - int nSprite2; - StatIterator it1(kStatDude); - while ((nSprite2 = it1.NextIndex()) >= 0) - { - DBloodActor* act2 = &bloodActors[nSprite2]; - spritetype *pDude = &act2->s(); - - if (pDude->flags & 32) - continue; - if (TestBitString(sectmap, pDude->sectnum)) - { - if (pXSprite->data1 && CheckProximity(pDude, x, y, z, nSector, radius)) - { - if (pExplodeInfo->dmg && pXSprite->target == 0) - { - pXSprite->target = 1; - actDamageSprite(nOwner, pDude, kDamageFall, (pExplodeInfo->dmg+Random(pExplodeInfo->dmgRng))<<4); - } - if (pExplodeInfo->dmgType) - ConcussSprite(pOwner, act2, x, y, z, pExplodeInfo->dmgType); - if (pExplodeInfo->burnTime) - { - assert(pDude->extra > 0 && pDude->extra < kMaxXSprites); - XSPRITE *pXDude = &xsprite[pDude->extra]; - if (!pXDude->burnTime) - evPost(nSprite2, 3, 0, kCallbackFXFlameLick); - actBurnSprite(pSprite->owner, pXDude, pExplodeInfo->burnTime<<2); - } - } - } - } - - it1.Reset(kStatThing); - while ((nSprite2 = it1.NextIndex()) >= 0) - { - auto act2 = &bloodActors[nSprite2]; - spritetype *pThing = &sprite[nSprite2]; - - if (pThing->flags & 32) - continue; - if (TestBitString(sectmap, pThing->sectnum)) - { - if (pXSprite->data1 && CheckProximity(pThing, x, y, z, nSector, radius)) - { - XSPRITE *pXSprite2 = &xsprite[pThing->extra]; - if (!pXSprite2->locked) - { - if (pExplodeInfo->dmgType) - ConcussSprite(pOwner, act2, x, y, z, pExplodeInfo->dmgType); - if (pExplodeInfo->burnTime) - { - assert(pThing->extra > 0 && pThing->extra < kMaxXSprites); - XSPRITE *pXThing = &xsprite[pThing->extra]; - if (pThing->type == kThingTNTBarrel && !pXThing->burnTime) - evPost(nSprite2, 3, 0, kCallbackFXFlameLick); - actBurnSprite(pSprite->owner, pXThing, pExplodeInfo->burnTime<<2); - } - } - } - } - } - - for (int p = connecthead; p >= 0; p = connectpoint2[p]) - { - spritetype *pSprite2 = gPlayer[p].pSprite; - int dx = (x - pSprite2->x)>>4; - int dy = (y - pSprite2->y)>>4; - int dz = (z - pSprite2->z)>>8; - int nDist = dx*dx+dy*dy+dz*dz+0x40000; - int t = DivScale(pXSprite->data2, nDist, 16); - gPlayer[p].flickerEffect += t; - } - - #ifdef NOONE_EXTENSIONS - if (pXSprite->data1 != 0) { - - // add impulse for sprites from physics list - if (gPhysSpritesCount > 0 && pExplodeInfo->dmgType != 0) { - for (int i = 0; i < gPhysSpritesCount; i++) { - if (gPhysSpritesList[i] == -1) continue; - else if (sprite[gPhysSpritesList[i]].sectnum < 0 || (sprite[gPhysSpritesList[i]].flags & kHitagFree) != 0) - continue; - - spritetype* pDebris = &sprite[gPhysSpritesList[i]]; - if (!TestBitString(sectmap, pDebris->sectnum) || !CheckProximity(pDebris, x, y, z, nSector, radius)) continue; - else debrisConcuss(nOwner, i, x, y, z, pExplodeInfo->dmgType); - } - } - - // trigger sprites from impact list - if (gImpactSpritesCount > 0) { - for (int i = 0; i < gImpactSpritesCount; i++) { - if (gImpactSpritesList[i] == -1) continue; - else if (sprite[gImpactSpritesList[i]].sectnum < 0 || (sprite[gImpactSpritesList[i]].flags & kHitagFree) != 0) - continue; - - spritetype* pImpact = &sprite[gImpactSpritesList[i]]; - if (pImpact->extra <= 0) - continue; - XSPRITE* pXImpact = &xsprite[pImpact->extra]; - if (/*pXImpact->state == pXImpact->restState ||*/ !TestBitString(sectmap, pImpact->sectnum) || !CheckProximity(pImpact, x, y, z, nSector, radius)) - continue; - - trTriggerSprite(pImpact->index, pXImpact, kCmdSpriteImpact); - } - } - - } - - if (!gModernMap || !(pSprite->flags & kModernTypeFlag1)) { - // if data4 > 0, do not remove explosion. This can be useful when designer wants put explosion generator in map manually - // via sprite statnum 2. - pXSprite->data1 = ClipLow(pXSprite->data1 - 4, 0); - pXSprite->data2 = ClipLow(pXSprite->data2 - 4, 0); - pXSprite->data3 = ClipLow(pXSprite->data3 - 4, 0); - } - #else - pXSprite->data1 = ClipLow(pXSprite->data1 - 4, 0); - pXSprite->data2 = ClipLow(pXSprite->data2 - 4, 0); - pXSprite->data3 = ClipLow(pXSprite->data3 - 4, 0); - #endif - - if (pXSprite->data1 == 0 && pXSprite->data2 == 0 && pXSprite->data3 == 0 && seqGetStatus(3, nXSprite) < 0) - actPostSprite(nSprite, kStatFree); - } - - it.Reset(kStatTraps); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - int nXSprite = pSprite->extra; - //assert(nXSprite > 0 && nXSprite < kMaxXSprites); - if (nXSprite <= 0 || nXSprite >= kMaxXSprites) - continue; - XSPRITE *pXSprite = &xsprite[nXSprite]; - switch (pSprite->type) { - case kTrapSawCircular: - pXSprite->data2 = ClipLow(pXSprite->data2-4, 0); - break; - case kTrapFlame: - if (pXSprite->state && seqGetStatus(3, nXSprite) < 0) { - int x = pSprite->x; - int y = pSprite->y; - int z = pSprite->z; - int t = (pXSprite->data1<<23)/120; - int dx = MulScale(t, Cos(pSprite->ang), 30); - int dy = MulScale(t, Sin(pSprite->ang), 30); - for (int i = 0; i < 2; i++) - { - spritetype *pFX = gFX.fxSpawn(FX_32, pSprite->sectnum, x, y, z, 0); - if (pFX) - { - xvel[pFX->index] = dx + Random2(0x8888); - yvel[pFX->index] = dy + Random2(0x8888); - zvel[pFX->index] = Random2(0x8888); - } - x += (dx/2)>>12; - y += (dy/2)>>12; - } - dy = SinScale16(pSprite->ang); - dx = CosScale16(pSprite->ang); - gVectorData[kVectorTchernobogBurn].maxDist = pXSprite->data1<<9; - actFireVector(pSprite, 0, 0, dx, dy, Random2(0x8888), kVectorTchernobogBurn); - } - break; - } - } - it.Reset(kStatDude); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) - { - XSPRITE *pXSprite = &xsprite[nXSprite]; - const bool fixBurnGlitch = !cl_bloodvanillaenemies && IsBurningDude(pSprite) && !VanillaMode(); // if enemies are burning, always apply burning damage per tick - if ((pXSprite->burnTime > 0) || fixBurnGlitch) - { - switch (pSprite->type) - { - case kDudeBurningInnocent: - case kDudeBurningCultist: - case kDudeBurningZombieAxe: - case kDudeBurningZombieButcher: - actDamageSprite(pXSprite->burnSource, pSprite, kDamageBurn, 8); - break; - default: - pXSprite->burnTime = ClipLow(pXSprite->burnTime-4, 0); - actDamageSprite(pXSprite->burnSource, pSprite, kDamageBurn, 8); - break; - } - } - - #ifdef NOONE_EXTENSIONS - // handle incarnations of custom dude - if (pSprite->type == kDudeModernCustom && pXSprite->txID > 0 && pXSprite->sysData1 == kGenDudeTransformStatus) { - xvel[pSprite->index] = yvel[pSprite->index] = 0; - if (seqGetStatus(3, nXSprite) < 0) - genDudeTransform(pSprite); - } - #endif - if (pSprite->type == kDudeCerberusTwoHead) - { - if (pXSprite->health <= 0 && seqGetStatus(3, nXSprite) < 0) - { - pXSprite->health = dudeInfo[28].startHealth<<4; - pSprite->type = kDudeCerberusOneHead; - if (pXSprite->target != -1) - aiSetTarget(pXSprite, pXSprite->target); - aiActivateDude(&bloodActors[pXSprite->reference]); - } - } - if (pXSprite->Proximity && !pXSprite->isTriggered) - { - int nSprite2; - StatIterator it1(kStatDude); - while ((nSprite2 = it1.NextIndex()) >= 0) - { - nNextSprite = it1.PeekIndex(); - spritetype *pSprite2 = &sprite[nSprite2]; - - if (pSprite2->flags&32) - continue; - XSPRITE *pXSprite2 = &xsprite[pSprite2->extra]; - if ((unsigned int)pXSprite2->health > 0 && IsPlayerSprite(pSprite2)) { - if (CheckProximity(pSprite2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 128)) - trTriggerSprite(nSprite, pXSprite, kCmdSpriteProximity); - } - } - } - if (IsPlayerSprite(pSprite)) - { - PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; - if (pPlayer->voodooTargets) - voodooTarget(pPlayer); - if (pPlayer->hand && Chance(0x8000)) - actDamageSprite(nSprite, pSprite, kDamageDrown, 12); - if (pPlayer->isUnderwater) - { - char bActive = packItemActive(pPlayer, 1); - if (bActive || pPlayer->godMode) - pPlayer->underwaterTime = 1200; - else - pPlayer->underwaterTime = ClipLow(pPlayer->underwaterTime-4, 0); - if (pPlayer->underwaterTime < 1080 && packCheckItem(pPlayer, 1) && !bActive) - packUseItem(pPlayer, 1); - if (!pPlayer->underwaterTime) - { - pPlayer->chokeEffect += 4; - if (Chance(pPlayer->chokeEffect)) - actDamageSprite(nSprite, pSprite, kDamageDrown, 3<<4); - } - else - pPlayer->chokeEffect = 0; - if (xvel[nSprite] || yvel[nSprite]) - sfxPlay3DSound(pSprite, 709, 100, 2); - pPlayer->bubbleTime = ClipLow(pPlayer->bubbleTime-4, 0); - } - else if (gGameOptions.nGameType == 0) - { - if (pPlayer->pXSprite->health > 0 && pPlayer->restTime >= 1200 && Chance(0x200)) - { - pPlayer->restTime = -1; - sfxPlay3DSound(pSprite, 3100+Random(11), 0, 2); - } - } - } - ProcessTouchObjects(&bloodActors[pSprite->index]); - } - } - it.Reset(kStatDude); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - int nXSprite = pSprite->extra; - assert(nXSprite > 0 && nXSprite < kMaxXSprites); - int nSector = pSprite->sectnum; - viewBackupSpriteLoc(nSprite, pSprite); - int nXSector = sector[nSector].extra; - XSECTOR *pXSector = NULL; - if (nXSector > 0) - { - assert(nXSector > 0 && nXSector < kMaxXSectors); - assert(xsector[nXSector].reference == nSector); - pXSector = &xsector[nXSector]; - } - if (pXSector) - { - int top, bottom; - GetSpriteExtents(pSprite, &top, &bottom); - if (getflorzofslope(nSector, pSprite->x, pSprite->y) <= bottom) - { - int angle = pXSector->panAngle; - int speed = 0; - if (pXSector->panAlways || pXSector->state || pXSector->busy) - { - speed = pXSector->panVel << 9; - if (!pXSector->panAlways && pXSector->busy) - speed = MulScale(speed, pXSector->busy, 16); - } - if (sector[nSector].floorstat&64) - angle = (angle+GetWallAngle(sector[nSector].wallptr)+512)&2047; - int dx = MulScale(speed, Cos(angle), 30); - int dy = MulScale(speed, Sin(angle), 30); - xvel[nSprite] += dx; - yvel[nSprite] += dy; - } - } - if (pXSector && pXSector->Underwater) - actAirDrag(pSprite, 5376); - else - actAirDrag(pSprite, 128); - - if ((pSprite->flags&4) || xvel[nSprite] || yvel[nSprite] || zvel[nSprite] || - velFloor[pSprite->sectnum] || velCeil[pSprite->sectnum]) - MoveDude(pSprite); - } - it.Reset(kStatFlare); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype *pSprite = &sprite[nSprite]; - - if (pSprite->flags & 32) - continue; - int nXSprite = pSprite->extra; - assert(nXSprite > 0 && nXSprite < kMaxXSprites); - XSPRITE *pXSprite = &xsprite[nXSprite]; - int nTarget = pXSprite->target; - assert(nTarget >= 0); - viewBackupSpriteLoc(nSprite, pSprite); - assert(nTarget < kMaxSprites); - spritetype *pTarget = &sprite[nTarget]; - if (pTarget->statnum == kMaxStatus) - { - GibSprite(pSprite, GIBTYPE_17, NULL, NULL); - actPostSprite(pSprite->index, kStatFree); - } - if (pTarget->extra > 0 && xsprite[pTarget->extra].health > 0) - { - int x = pTarget->x+mulscale30r(Cos(pXSprite->goalAng+pTarget->ang), pTarget->clipdist*2); - int y = pTarget->y+mulscale30r(Sin(pXSprite->goalAng+pTarget->ang), pTarget->clipdist*2); - int z = pTarget->z+pXSprite->targetZ; - vec3_t pos = { x, y, z }; - setsprite(nSprite,&pos); - xvel[nSprite] = xvel[nTarget]; - yvel[nSprite] = yvel[nTarget]; - zvel[nSprite] = zvel[nTarget]; - } - else - { - GibSprite(pSprite, GIBTYPE_17, NULL, NULL); - actPostSprite(pSprite->index, kStatFree); - } - } - aiProcessDudes(); - gFX.fxProcess(); + actCheckProximity(); + actCheckThings(); + actCheckProjectiles(); + actCheckExplosion(); + actCheckTraps(); + actCheckDudes(); + actCheckFlares(); + aiProcessDudes(); + gFX.fxProcess(); } -spritetype * actSpawnSprite_(int nSector, int x, int y, int z, int nStat, char a6) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +DBloodActor* actSpawnSprite(int nSector, int x, int y, int z, int nStat, bool setextra) { - int nSprite = InsertSprite(nSector, nStat); - if (nSprite >= 0) - sprite[nSprite].extra = -1; - else - { - StatIterator it(kStatPurge); - nSprite = it.NextIndex(); - assert(nSprite >= 0); - assert(nSector >= 0 && nSector < kMaxSectors); - ChangeSpriteSect(nSprite, nSector); - actPostSprite(nSprite, nStat); - } - vec3_t pos = { x, y, z }; - setsprite(nSprite, &pos); - spritetype *pSprite = &sprite[nSprite]; - pSprite->type = kSpriteDecoration; - if (a6 && pSprite->extra == -1) - { - int nXSprite = dbInsertXSprite(nSprite); - gSpriteHit[nXSprite].florhit = 0; - gSpriteHit[nXSprite].ceilhit = 0; - if (!VanillaMode()) - xsprite[nXSprite].target = -1; - } - return pSprite; + int nSprite = InsertSprite(nSector, nStat); + if (nSprite >= 0) sprite[nSprite].extra = -1; + else + { + BloodStatIterator it(kStatPurge); + nSprite = it.NextIndex(); + assert(nSprite >= 0); + assert(nSector >= 0 && nSector < kMaxSectors); + ChangeSpriteSect(nSprite, nSector); + actPostSprite(nSprite, nStat); + } + DBloodActor* actor = &bloodActors[nSprite]; + + vec3_t pos = { x, y, z }; + setsprite(nSprite, &pos); + spritetype* pSprite = &actor->s(); + pSprite->type = kSpriteDecoration; + if (setextra && !actor->hasX()) + { + actor->addExtra(); + actor->hit().florhit = 0; + actor->hit().ceilhit = 0; + if (!VanillaMode()) actor->SetTarget(nullptr); + } + return actor; } -DBloodActor* actSpawnSprite(int nSector, int x, int y, int z, int nStat, bool a6) -{ - auto spr = actSpawnSprite_(nSector, x, y, z, nStat, a6); - return &bloodActors[spr->index]; -} - -spritetype * actSpawnSprite(spritetype *pSource, int nStat); +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- spritetype *actSpawnDude(spritetype *pSource, short nType, int a3, int a4) { @@ -6524,7 +6518,8 @@ spritetype * actSpawnSprite(spritetype *pSource, int nStat) spritetype * actSpawnThing(int nSector, int x, int y, int z, int nThingType) { assert(nThingType >= kThingBase && nThingType < kThingMax); - spritetype *pSprite = actSpawnSprite_(nSector, x, y, z, 4, 1); + auto actor = actSpawnSprite(nSector, x, y, z, 4, 1); + spritetype* pSprite = &actor->s(); int nType = nThingType-kThingBase; int nThing = pSprite->index; int nXThing = pSprite->extra; @@ -6665,7 +6660,8 @@ spritetype* actFireMissile(spritetype *pSprite, int a2, int a3, int a4, int a5, y = gHitInfo.hity-MulScale(pMissileInfo->clipDist<<1, Sin(pSprite->ang), 28); } } - spritetype *pMissile = actSpawnSprite_(pSprite->sectnum, x, y, z, 5, 1); + auto actor = actSpawnSprite(pSprite->sectnum, x, y, z, 5, 1); + spritetype* pMissile = &actor->s(); int nMissile = pMissile->index; show2dsprite.Set(nMissile); pMissile->type = nType; @@ -7276,10 +7272,13 @@ void actPostProcess(void) int nStatus = pPost->status; if (nStatus == kStatFree) { - evKill(nSprite, 3); - if (sprite[nSprite].extra > 0) - seqKill(3, sprite[nSprite].extra); - DeleteSprite(nSprite); + if (pSprite->statnum != kStatFree) + { + evKill(nSprite, 3); + if (sprite[nSprite].extra > 0) + seqKill(3, pSprite->extra); + DeleteSprite(nSprite); + } } else ChangeSpriteStat(nSprite, nStatus); diff --git a/source/games/blood/src/actor.h b/source/games/blood/src/actor.h index 8eccca604..6bd3b4206 100644 --- a/source/games/blood/src/actor.h +++ b/source/games/blood/src/actor.h @@ -228,15 +228,10 @@ void actKillDude(int a1, spritetype *pSprite, DAMAGE_TYPE a3, int a4); int actDamageSprite(int nSource, spritetype *pSprite, DAMAGE_TYPE a3, int a4); int actDamageSprite(DBloodActor* pSource, DBloodActor* pTarget, DAMAGE_TYPE damageType, int damage); void actHitcodeToData(int a1, HITINFO *pHitInfo, DBloodActor **actor, walltype **a7 = nullptr); -void actAirDrag(spritetype *pSprite, int a2); -int MoveThing(spritetype *pSprite); -void MoveDude(spritetype *pSprite); -int MoveMissile(spritetype *pSprite); -void actExplodeSprite(spritetype *pSprite); +void actAirDrag(DBloodActor *pSprite, int a2); +void actExplodeSprite(DBloodActor *pSprite); void actActivateGibObject(DBloodActor *actor); -bool IsUnderWater(spritetype *pSprite); void actProcessSprites(void); -spritetype * actSpawnSprite_(int nSector, int x, int y, int z, int nStat, char a6); DBloodActor* actSpawnSprite(int nSector, int x, int y, int z, int nStat, bool a6); spritetype *actSpawnDude(spritetype *pSource, short nType, int a3, int a4); spritetype * actSpawnSprite(spritetype *pSource, int nStat); diff --git a/source/games/blood/src/ai.cpp b/source/games/blood/src/ai.cpp index 3d88c236b..4f605171d 100644 --- a/source/games/blood/src/ai.cpp +++ b/source/games/blood/src/ai.cpp @@ -33,7 +33,6 @@ BEGIN_BLD_NS void RecoilDude(DBloodActor* actor); int cumulDamage[kMaxXSprites]; -DUDEEXTRA gDudeExtra[kMaxXSprites]; AISTATE genIdle = {kAiStateGenIdle, 0, -1, 0, NULL, NULL, NULL, NULL }; AISTATE genRecoil = {kAiStateRecoil, 5, -1, 20, NULL, NULL, NULL, &genIdle }; @@ -53,7 +52,8 @@ bool dudeIsPlayingSeq(spritetype *pSprite, int nSeq) void aiPlay3DSound(spritetype *pSprite, int a2, AI_SFX_PRIORITY a3, int a4) { - DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; + auto actor = &bloodActors[pSprite->index]; + DUDEEXTRA *pDudeExtra = &actor->dudeExtra; if (a3 == AI_SFX_PRIORITY_0) sfxPlay3DSound(pSprite, a2, a4, 2); else if (a3 > pDudeExtra->prio || pDudeExtra->time <= PlayClock) @@ -321,7 +321,7 @@ void aiActivateDude(DBloodActor* actor) switch (pSprite->type) { case kDudePhantasm: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; @@ -340,7 +340,7 @@ void aiActivateDude(DBloodActor* actor) case kDudeCultistTNT: case kDudeCultistBeast: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->target == -1) { @@ -378,7 +378,7 @@ void aiActivateDude(DBloodActor* actor) #ifdef NOONE_EXTENSIONS case kDudeModernCustom: { - DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1* pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->target == -1) { @@ -397,7 +397,7 @@ void aiActivateDude(DBloodActor* actor) break; #endif case kDudeCultistTommyProne: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; pSprite->type = kDudeCultistTommy; if (pXSprite->target == -1) { @@ -430,7 +430,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeCultistShotgunProne: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; pSprite->type = kDudeCultistShotgun; @@ -474,7 +474,7 @@ void aiActivateDude(DBloodActor* actor) break; case kDudeBat: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; @@ -492,7 +492,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeBoneEel: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; @@ -509,7 +509,7 @@ void aiActivateDude(DBloodActor* actor) break; } case kDudeGillBeast: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; XSECTOR *pXSector = NULL; if (sector[pSprite->sectnum].extra > 0) pXSector = &xsector[sector[pSprite->sectnum].extra]; @@ -537,7 +537,7 @@ void aiActivateDude(DBloodActor* actor) break; } case kDudeZombieAxeNormal: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->target == -1) @@ -567,7 +567,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeZombieAxeBuried: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->aiState == &zombieEIdle) @@ -576,7 +576,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeZombieAxeLaying: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->aiState == &zombieSIdle) @@ -584,7 +584,7 @@ void aiActivateDude(DBloodActor* actor) break; } case kDudeZombieButcher: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->target == -1) @@ -612,7 +612,7 @@ void aiActivateDude(DBloodActor* actor) aiNewState(actor, &zombieFBurnChase); break; case kDudeGargoyleFlesh: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; @@ -630,7 +630,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeGargoyleStone: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; @@ -747,7 +747,7 @@ void aiActivateDude(DBloodActor* actor) } break; case kDudeSpiderMother: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0; pSprite->flags |= 2; @@ -763,7 +763,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeTinyCaleb: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->target == -1) @@ -796,7 +796,7 @@ void aiActivateDude(DBloodActor* actor) } case kDudeBeast: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 1; pDudeExtraE->xval1 = 0; if (pXSprite->target == -1) @@ -944,9 +944,9 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType if (pSprite->type == kDudeModernCustomBurning) { - if (Chance(0x2000) && gDudeExtra[pSprite->extra].time < PlayClock) { + if (Chance(0x2000) && actor->dudeExtra.time < PlayClock) { playGenDudeSound(pSprite, kGenDudeSndBurning); - gDudeExtra[pSprite->extra].time = PlayClock + 360; + actor->dudeExtra.time = PlayClock + 360; } if (pXSprite->burnTime == 0) pXSprite->burnTime = 2400; @@ -992,7 +992,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType aiGenDudeNewState(pSprite, &genDudeBurnGoto); actHealDude(pXSprite, dudeInfo[55].startHealth, dudeInfo[55].startHealth); - gDudeExtra[pSprite->extra].time = PlayClock + 360; + actor->dudeExtra.time = PlayClock + 360; evKill(nSprite, 3, kCallbackFXFlameLick); } @@ -1030,7 +1030,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType if (nDmgType == kDamageTesla) { - DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; + DUDEEXTRA *pDudeExtra = &actor->dudeExtra; pDudeExtra->recoil = 1; } const bool fixRandomCultist = !cl_bloodvanillaenemies && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode(); // fix burning cultists randomly switching types underwater @@ -1055,7 +1055,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType aiNewState(actor, &cultistBurnGoto); aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); aiPlay3DSound(pSprite, 1031+Random(2), AI_SFX_PRIORITY_2, -1); - gDudeExtra[pSprite->extra].time = PlayClock+360; + actor->dudeExtra.time = PlayClock+360; actHealDude(pXSprite, dudeInfo[40].startHealth, dudeInfo[40].startHealth); evKill(nSprite, 3, kCallbackFXFlameLick); } @@ -1066,16 +1066,16 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType pSprite->type = kDudeBurningInnocent; aiNewState(actor, &cultistBurnGoto); aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); - gDudeExtra[pSprite->extra].time = PlayClock+360; + actor->dudeExtra.time = PlayClock+360; actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth); evKill(nSprite, 3, kCallbackFXFlameLick); } break; case kDudeBurningCultist: - if (Chance(0x4000) && gDudeExtra[pSprite->extra].time < PlayClock) + if (Chance(0x4000) && actor->dudeExtra.time < PlayClock) { aiPlay3DSound(pSprite, 1031+Random(2), AI_SFX_PRIORITY_2, -1); - gDudeExtra[pSprite->extra].time = PlayClock+360; + actor->dudeExtra.time = PlayClock+360; } if (Chance(0x600) && (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo)) { @@ -1121,7 +1121,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType aiNewState(actor, &cultistBurnGoto); } aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); - gDudeExtra[pSprite->extra].time = PlayClock+360; + actor->dudeExtra.time = PlayClock+360; actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth); evKill(nSprite, 3, kCallbackFXFlameLick); } @@ -1157,7 +1157,7 @@ void RecoilDude(DBloodActor* actor) auto pXSprite = &actor->x(); auto pSprite = &actor->s(); char v4 = Chance(0x8000); - DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; + DUDEEXTRA *pDudeExtra = &actor->dudeExtra; if (pSprite->statnum == kStatDude && (pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); switch (pSprite->type) { @@ -1573,15 +1573,15 @@ void aiInit(void) void aiInitSprite(spritetype *pSprite) { + auto actor = &bloodActors[pSprite->index]; int nXSprite = pSprite->extra; XSPRITE *pXSprite = &xsprite[nXSprite]; - auto actor = &bloodActors[pXSprite->reference]; int nSector = pSprite->sectnum; int nXSector = sector[nSector].extra; XSECTOR *pXSector = NULL; if (nXSector > 0) pXSector = &xsector[nXSector]; - DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; + DUDEEXTRA *pDudeExtra = &actor->dudeExtra; pDudeExtra->recoil = 0; pDudeExtra->time = 0; @@ -1609,7 +1609,7 @@ void aiInitSprite(spritetype *pSprite) case kDudeCultistTNT: case kDudeCultistBeast: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &cultistIdle); @@ -1617,7 +1617,7 @@ void aiInitSprite(spritetype *pSprite) } case kDudeCultistTommyProne: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &fanaticProneIdle); @@ -1625,21 +1625,21 @@ void aiInitSprite(spritetype *pSprite) } case kDudeCultistShotgunProne: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &cultistProneIdle); break; } case kDudeZombieButcher: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &zombieFIdle); break; } case kDudeZombieAxeNormal: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &zombieAIdle); @@ -1647,7 +1647,7 @@ void aiInitSprite(spritetype *pSprite) } case kDudeZombieAxeLaying: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &zombieSIdle); @@ -1655,7 +1655,7 @@ void aiInitSprite(spritetype *pSprite) break; } case kDudeZombieAxeBuried: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &zombieEIdle); @@ -1663,7 +1663,7 @@ void aiInitSprite(spritetype *pSprite) } case kDudeGargoyleFlesh: case kDudeGargoyleStone: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; @@ -1675,7 +1675,7 @@ void aiInitSprite(spritetype *pSprite) aiNewState(actor, &gargoyleStatueIdle); break; case kDudeCerberusTwoHead: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &cerberusIdle); @@ -1689,7 +1689,7 @@ void aiInitSprite(spritetype *pSprite) break; case kDudePhantasm: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; @@ -1704,7 +1704,7 @@ void aiInitSprite(spritetype *pSprite) break; case kDudeBoneEel: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; @@ -1716,7 +1716,7 @@ void aiInitSprite(spritetype *pSprite) break; case kDudeBat: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval2 = 0; pDudeExtraE->xval3 = 0; pDudeExtraE->xval1 = 0; @@ -1727,7 +1727,7 @@ void aiInitSprite(spritetype *pSprite) case kDudeSpiderRed: case kDudeSpiderBlack: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; @@ -1736,7 +1736,7 @@ void aiInitSprite(spritetype *pSprite) } case kDudeSpiderMother: { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; @@ -1745,7 +1745,7 @@ void aiInitSprite(spritetype *pSprite) } case kDudeTchernobog: { - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; pDudeExtraE->xval2 = 0; pDudeExtraE->xval1 = 0; aiNewState(actor, &tchernobogIdle); @@ -1861,45 +1861,4 @@ void aiInitSprite(spritetype *pSprite) } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -FSerializer& Serialize(FSerializer& arc, const char* keyname, DUDEEXTRA& w, DUDEEXTRA* def) -{ - int empty = 0; - char empty2 = 0; - if (arc.isReading()) w = {}; - - if (arc.BeginObject(keyname)) - { - arc("time", w.time, &empty) - ("recoil", w.recoil, &empty) - ("prio", w.prio, &empty) - ("x1", w.at6.u1.xval1, &empty) - ("x2", w.at6.u1.xval2, &empty) - ("x3", w.at6.u1.xval3, &empty2) - .EndObject(); - } - return arc; -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void SerializeAI(FSerializer& arc) -{ - if (arc.BeginObject("ai")) - { - arc.SparseArray("dudeextra", gDudeExtra, kMaxSprites, activeXSprites) - .EndObject(); - } -} - - END_BLD_NS diff --git a/source/games/blood/src/ai.h b/source/games/blood/src/ai.h index 8e68301ca..3b6e5b92f 100644 --- a/source/games/blood/src/ai.h +++ b/source/games/blood/src/ai.h @@ -87,7 +87,6 @@ struct TARGETTRACK { }; extern const int dword_138BB0[5]; -extern DUDEEXTRA gDudeExtra[]; bool dudeIsPlayingSeq(spritetype *pSprite, int nSeq); void aiPlay3DSound(spritetype *pSprite, int a2, AI_SFX_PRIORITY a3, int a4); diff --git a/source/games/blood/src/aibat.cpp b/source/games/blood/src/aibat.cpp index 2eb89ed23..3525eb220 100644 --- a/source/games/blood/src/aibat.cpp +++ b/source/games/blood/src/aibat.cpp @@ -84,7 +84,7 @@ static void batThinkTarget(DBloodActor* actor) auto pSprite = &actor->s(); assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10) pDudeExtraE->xval2++; else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) @@ -433,7 +433,7 @@ void batMoveToCeil(DBloodActor* actor) int nSector = pSprite->sectnum; if (z - pXSprite->targetZ < 0x1000) { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pSprite->flags = 0; aiNewState(actor, &batIdle); diff --git a/source/games/blood/src/aiboneel.cpp b/source/games/blood/src/aiboneel.cpp index 6c09d26de..af24c4699 100644 --- a/source/games/blood/src/aiboneel.cpp +++ b/source/games/blood/src/aiboneel.cpp @@ -95,7 +95,7 @@ static void eelThinkTarget(DBloodActor* actor) auto pSprite = &actor->s(); assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10) pDudeExtraE->xval2++; else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) @@ -436,7 +436,7 @@ void eelMoveToCeil(DBloodActor* actor) int nSector = pSprite->sectnum; if (z - pXSprite->targetZ < 0x1000) { - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = 0; pSprite->flags = 0; aiNewState(actor, &eelIdle); diff --git a/source/games/blood/src/aicerber.cpp b/source/games/blood/src/aicerber.cpp index 3455c89dc..391031a36 100644 --- a/source/games/blood/src/aicerber.cpp +++ b/source/games/blood/src/aicerber.cpp @@ -266,7 +266,7 @@ static void cerberusThinkTarget(DBloodActor* actor) return; } DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10) pDudeExtraE->xval2++; else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) diff --git a/source/games/blood/src/aigarg.cpp b/source/games/blood/src/aigarg.cpp index d0ce37bda..23dd564c5 100644 --- a/source/games/blood/src/aigarg.cpp +++ b/source/games/blood/src/aigarg.cpp @@ -215,7 +215,7 @@ static void gargThinkTarget(DBloodActor* actor) return; } DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10) pDudeExtraE->xval2++; else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) diff --git a/source/games/blood/src/aighost.cpp b/source/games/blood/src/aighost.cpp index 2295cbffb..29c457ae6 100644 --- a/source/games/blood/src/aighost.cpp +++ b/source/games/blood/src/aighost.cpp @@ -191,7 +191,7 @@ static void ghostThinkTarget(DBloodActor* actor) return; } DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10) pDudeExtraE->xval2++; else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) diff --git a/source/games/blood/src/aispid.cpp b/source/games/blood/src/aispid.cpp index ced581fcb..bdf888a7b 100644 --- a/source/games/blood/src/aispid.cpp +++ b/source/games/blood/src/aispid.cpp @@ -146,7 +146,7 @@ void SpidBirthSeqCallback(int, DBloodActor* actor) DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); assert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); spritetype *pTarget = &sprite[pXSprite->target]; - DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1; int dx = pXSprite->targetX-pSprite->x; int dy = pXSprite->targetY-pSprite->y; int nAngle = getangle(dx, dy); diff --git a/source/games/blood/src/aitchern.cpp b/source/games/blood/src/aitchern.cpp index 17be3e2e5..1be20c9d2 100644 --- a/source/games/blood/src/aitchern.cpp +++ b/source/games/blood/src/aitchern.cpp @@ -233,7 +233,7 @@ static void sub_725A4(DBloodActor* actor) return; } DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); - DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; + DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2; if (pDudeExtraE->xval2 && pDudeExtraE->xval1 < 10) pDudeExtraE->xval1++; else if (pDudeExtraE->xval1 >= 10 && pDudeExtraE->xval2) diff --git a/source/games/blood/src/aiunicult.cpp b/source/games/blood/src/aiunicult.cpp index 4e9ad295f..ee73a3014 100644 --- a/source/games/blood/src/aiunicult.cpp +++ b/source/games/blood/src/aiunicult.cpp @@ -1593,8 +1593,10 @@ void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event) } } -bool doExplosion(spritetype* pSprite, int nType) { - spritetype* pExplosion = actSpawnSprite_(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatExplosion, true); +bool doExplosion(spritetype* pSprite, int nType) +{ + auto actor = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatExplosion, true); + spritetype* pExplosion = &actor->s(); if (pExplosion->extra < 0 || pExplosion->extra >= kMaxXSprites) return false; @@ -2215,10 +2217,12 @@ void genDudePostDeath(spritetype* pSprite, DAMAGE_TYPE damageType, int damage) { actPostSprite(pSprite->index, kStatThing); } -void aiGenDudeInitSprite(spritetype* pSprite, XSPRITE* pXSprite) { +void aiGenDudeInitSprite(spritetype* pSprite, XSPRITE* pXSprite) +{ + auto actor = &bloodActors[pSprite->index]; switch (pSprite->type) { case kDudeModernCustom: { - DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + DUDEEXTRA_at6_u1* pDudeExtraE = &actor->dudeExtra.at6.u1; pDudeExtraE->xval3 = pDudeExtraE->xval1 = 0; aiGenDudeNewState(pSprite, &genDudeIdleL); break; diff --git a/source/games/blood/src/bloodactor.h b/source/games/blood/src/bloodactor.h index 79d5d051b..338fc8c24 100644 --- a/source/games/blood/src/bloodactor.h +++ b/source/games/blood/src/bloodactor.h @@ -4,6 +4,8 @@ BEGIN_BLD_NS extern int cumulDamage[kMaxXSprites]; + + // Due to the messed up array storage of all the game data we cannot do any direct references here yet. We have to access everything via wrapper functions for now. // Note that the indexing is very inconsistent - partially by sprite index, partially by xsprite index. class DBloodActor @@ -13,6 +15,7 @@ class DBloodActor public: int dudeSlope; + DUDEEXTRA dudeExtra; DBloodActor() :index(int(this - base())) { /*assert(index >= 0 && index < kMaxSprites);*/ } DBloodActor& operator=(const DBloodActor& other) = default; @@ -20,6 +23,7 @@ public: void Clear() { dudeSlope = 0; + dudeExtra = {}; } bool hasX() { return sprite[index].extra > 0; } void addX() @@ -34,7 +38,6 @@ public: int& zvel() { return Blood::zvel[index]; } int& cumulDamage() { return Blood::cumulDamage[sprite[index].extra]; } - DUDEEXTRA& dudeExtra() { return gDudeExtra[sprite[index].extra]; } SPRITEMASS& spriteMass() { return gSpriteMass[sprite[index].extra]; } GENDUDEEXTRA& genDudeExtra() { return Blood::gGenDudeExtra[index]; } POINT3D& basePoint() { return Blood::baseSprite[index]; } @@ -125,6 +128,11 @@ public: } } + void addExtra() + { + if (s().extra <= 0) s().extra = dbInsertXSprite(index); + } + }; extern DBloodActor bloodActors[kMaxSprites]; @@ -258,4 +266,21 @@ struct Collision }; +inline DBloodActor* getUpperLink(int sect) +{ + auto l = gUpperLink[sect]; + return l == -1 ? nullptr : &bloodActors[l]; +} + +inline DBloodActor* getLowerLink(int sect) +{ + auto l = gLowerLink[sect]; + return l == -1 ? nullptr : &bloodActors[l]; +} + +inline void viewBackupSpriteLoc(DBloodActor* actor) +{ + viewBackupSpriteLoc(actor->s().index, &actor->s()); +} + END_BLD_NS diff --git a/source/games/blood/src/common_game.h b/source/games/blood/src/common_game.h index 30768ba05..4e5684c34 100644 --- a/source/games/blood/src/common_game.h +++ b/source/games/blood/src/common_game.h @@ -561,6 +561,11 @@ inline int QRandom2(int a1) return MulScale(qrand(), a1, 14)-a1; } +inline double QRandom2F(double a1) +{ + return MulScaleF(qrand(), a1, 14)-a1; +} + template inline void SetBitString(T *pArray, int nIndex) { diff --git a/source/games/blood/src/fx.cpp b/source/games/blood/src/fx.cpp index c156c9415..34bf2a367 100644 --- a/source/games/blood/src/fx.cpp +++ b/source/games/blood/src/fx.cpp @@ -165,7 +165,8 @@ spritetype * CFX::fxSpawn(FX_ID nFx, int nSector, int x, int y, int z, unsigned return NULL; destroy(nSprite); } - spritetype *pSprite = actSpawnSprite_(nSector, x, y, z, 1, 0); + auto actor = actSpawnSprite(nSector, x, y, z, 1, 0); + spritetype* pSprite = &actor->s(); pSprite->type = nFx; pSprite->picnum = pFX->picnum; pSprite->cstat |= pFX->cstat; @@ -204,7 +205,7 @@ void CFX::fxProcess(void) assert(nSector >= 0 && nSector < kMaxSectors); assert(pSprite->type < kFXMax); FXDATA *pFXData = &gFXData[pSprite->type]; - actAirDrag(pSprite, pFXData->drag); + actAirDrag(&bloodActors[pSprite->index], pFXData->drag); if (xvel[nSprite]) pSprite->x += xvel[nSprite]>>12; if (yvel[nSprite]) diff --git a/source/games/blood/src/gameutil.cpp b/source/games/blood/src/gameutil.cpp index 2840c85f8..f9318e3ca 100644 --- a/source/games/blood/src/gameutil.cpp +++ b/source/games/blood/src/gameutil.cpp @@ -869,8 +869,12 @@ int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSect if (TestBitString(sectbits, nNextSector)) // if we've already checked this sector, skip continue; bool setSectBit = true; - bool withinRange = CheckProximityWall(pWall->point2, x, y, nDist); - if (newSectCheckMethod && !withinRange) // if range check failed, try comparing midpoints/subdivides of wall span + bool withinRange = false; + if (!newSectCheckMethod) // original method + { + withinRange = CheckProximityWall(pWall->point2, x, y, nDist); + } + else // new method - first test edges and then wall span midpoints { for (int k = (j+1); k < nEndWall; k++) // scan through the rest of the sector's walls { @@ -884,27 +888,34 @@ int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSect const int nWallB = wall[nWallA].point2; int x1 = wall[nWallA].x, y1 = wall[nWallA].y; int x2 = wall[nWallB].x, y2 = wall[nWallB].y; + int point1Dist = approxDist(x-x1, y-y1); // setup edge distance needed for below loop (determines which point to shift closer to center) + int point2Dist = approxDist(x-x2, y-y2); int nLength = approxDist(x1-x2, y1-y2); - const int nDist2 = (nDist+(nDist>>1))<<4; - nLength = ClipRange(nLength / nDist2, 1, 4); // never split more than 4 times - for (int k = 0; true; k++) // subdivide span into smaller chunks towards direction + const int nDist4 = nDist<<4; + nLength = ClipRange(nLength / (nDist4+(nDist4>>1)), 1, 4); // always test midpoint at least once, and never split more than 4 times + for (int k = 0; true; k++) // check both points of wall and subdivide span into smaller chunks towards target { - const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1; - withinRange = CheckProximityPoint(xcenter, ycenter, 0, x, y, 0, nDist); + withinRange = (point1Dist < nDist4) || (point2Dist < nDist4); // check if both points of span is within radius if (withinRange) break; - if (k == (nLength-1)) // reached end + if (k == nLength) // reached end break; - const bool bDir = approxDist(x-x1, y-y1) < approxDist(x-x2, y-y2); - if (bDir) // step closer and check again + const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1; + if (point1Dist < point2Dist) // shift closest side of wall towards target point, and refresh point distance values + { x2 = xcenter, y2 = ycenter; + point2Dist = approxDist(x-x2, y-y2); + } else + { x1 = xcenter, y1 = ycenter; + point1Dist = approxDist(x-x1, y-y1); + } } } if (withinRange) // if new sector is within range, set to current sector and test walls { - setSectBit = true; // sector is within range, set as checked + setSectBit = true; // sector is within range, set the sector as checked if (pSectBit) SetBitString(pSectBit, nNextSector); pSectors[n++] = nNextSector; diff --git a/source/games/blood/src/levels.cpp b/source/games/blood/src/levels.cpp index a4692c6fe..230008145 100644 --- a/source/games/blood/src/levels.cpp +++ b/source/games/blood/src/levels.cpp @@ -120,7 +120,7 @@ static const char* DefFile(void) for (int i = numlumps - 1; i >= 0; i--) { int fileno = fileSystem.GetFileContainer(i); - if (fileno != -1 && fileno <= fileSystem.GetMaxIwadNum()) break; + if (fileno != -1 && fileno <= fileSystem.GetMaxIwadNum()) continue; FString fn = fileSystem.GetFileFullName(i, false); FString ext = fn.Right(4); if (ext.CompareNoCase(".ini") == 0) diff --git a/source/games/blood/src/loadsave.cpp b/source/games/blood/src/loadsave.cpp index 7589b0e0e..f5d17b526 100644 --- a/source/games/blood/src/loadsave.cpp +++ b/source/games/blood/src/loadsave.cpp @@ -423,6 +423,32 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, AISTATE*& w, AISTA return arc; } +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +FSerializer& Serialize(FSerializer& arc, const char* keyname, DUDEEXTRA& w, DUDEEXTRA* def) +{ + int empty = 0; + char empty2 = 0; + if (arc.isReading()) w = {}; + + if (arc.BeginObject(keyname)) + { + arc("time", w.time, &empty) + ("recoil", w.recoil, &empty) + ("prio", w.prio, &empty) + ("x1", w.at6.u1.xval1, &empty) + ("x2", w.at6.u1.xval2, &empty) + ("x3", w.at6.u1.xval3, &empty2) + .EndObject(); + } + return arc; +} + + FSerializer& Serialize(FSerializer& arc, const char* keyname, DBloodActor& w, DBloodActor* def) { static DBloodActor nul; @@ -437,7 +463,8 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, DBloodActor& w, DB // The rest is only relevant if the actor has an xsprite. if (w.s().extra > 0) { - arc("dudeslope", w.dudeSlope, def->dudeSlope); + arc("dudeslope", w.dudeSlope, def->dudeSlope) + ("dudeextra", w.dudeExtra, def->dudeExtra); } arc.EndObject(); } @@ -668,7 +695,6 @@ void SerializeSequences(FSerializer& arc); void SerializeWarp(FSerializer& arc); void SerializeTriggers(FSerializer& arc); void SerializeActor(FSerializer& arc); -void SerializeAI(FSerializer& arc); void SerializeGameStats(FSerializer& arc); void SerializePlayers(FSerializer& arc); void SerializeView(FSerializer& arc); @@ -700,7 +726,6 @@ void GameInterface::SerializeGameState(FSerializer& arc) SerializeState(arc); InitFreeList(nextXSprite, kMaxXSprites, activeXSprites); SerializeActor(arc); - SerializeAI(arc); SerializePlayers(arc); SerializeEvents(arc); SerializeGameStats(arc); diff --git a/source/games/blood/src/misc.h b/source/games/blood/src/misc.h index 96914d50f..f889ce817 100644 --- a/source/games/blood/src/misc.h +++ b/source/games/blood/src/misc.h @@ -43,6 +43,7 @@ void HookReplaceFunctions(); struct PLAYER; +bool checkFired6or7(PLAYER *pPlayer); void WeaponInit(void); void WeaponDraw(PLAYER *pPlayer, int a2, double a3, double a4, int a5); void WeaponRaise(PLAYER *pPlayer); diff --git a/source/games/blood/src/nnexts.cpp b/source/games/blood/src/nnexts.cpp index b97ffbfb6..e419aadb5 100644 --- a/source/games/blood/src/nnexts.cpp +++ b/source/games/blood/src/nnexts.cpp @@ -1286,7 +1286,7 @@ void nnExtProcessSuperSprites() { } - actAirDrag(pDebris, airVel); + actAirDrag(&bloodActors[pDebris->index], airVel); if (pXDebris->physAttr & kPhysDebrisTouch) { PLAYER* pPlayer = NULL; @@ -1919,7 +1919,7 @@ void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer, bool checkCondition) { for (unsigned k = 0; k < pCond->length; k++) { if (pCond->obj[k].type != OBJ_SPRITE || pCond->obj[k].index != pXSource->reference) continue; pCond->obj[k].index = pPlayer->nSprite; - pCond->obj[k].cmd = pPlayer->pXSprite->command; + pCond->obj[k].cmd = (uint8_t)pPlayer->pXSprite->command; break; } @@ -2985,6 +2985,7 @@ void useSpriteDamager(XSPRITE* pXSource, int objType, int objIndex) { } void damageSprites(XSPRITE* pXSource, spritetype* pSprite) { + auto actor = &bloodActors[pSprite->index]; spritetype* pSource = &sprite[pXSource->reference]; if (!IsDudeSprite(pSprite) || !xspriRangeIsFine(pSprite->extra) || xsprite[pSprite->extra].health <= 0 || pXSource->data3 < 0) return; @@ -3078,7 +3079,7 @@ void damageSprites(XSPRITE* pXSource, spritetype* pSprite) { if (forceRecoil && !pPlayer) { pXSprite->data3 = 32767; - gDudeExtra[pSprite->extra].recoil = (dmgType == kDmgElectric) ? 1 : 0; + actor->dudeExtra.recoil = (dmgType == kDmgElectric) ? 1 : 0; if (pXSprite->aiState->stateType != kAiStateRecoil) RecoilDude(&bloodActors[pXSprite->reference]); } @@ -3569,8 +3570,8 @@ bool condCheckSector(XSPRITE* pXCond, int cmpOp, bool PUSH) { SectIterator it(objIndex); while ((nSprite = it.NextIndex()) >= 0) { - if (!condCmp(sprite[var].type, arg1, arg2, cmpOp)) continue; - else if (PUSH) condPush(pXCond, OBJ_SPRITE, var); + if (!condCmp(sprite[nSprite].type, arg1, arg2, cmpOp)) continue; + else if (PUSH) condPush(pXCond, OBJ_SPRITE, nSprite); return true; } return false; @@ -5041,7 +5042,7 @@ bool modernTypeOperateSprite(int nSprite, spritetype* pSprite, XSPRITE* pXSprite pXSprite->Proximity = 1; break; default: - actExplodeSprite(pSprite); + actExplodeSprite(&bloodActors[pSprite->index]); break; } } @@ -7911,6 +7912,34 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, SPRITEMASS& w, SPR return arc; } +FSerializer& Serialize(FSerializer& arc, const char* keyname, OBJECTS_TO_TRACK& w, OBJECTS_TO_TRACK* def) +{ + static OBJECTS_TO_TRACK nul; + if (arc.isReading()) w = {}; + if (arc.BeginObject(keyname)) + { + arc("type", w.type, &nul.type) + ("index", w.index, &nul.index) + ("xrepeat", w.cmd, &nul.cmd) + .EndObject(); + } + return arc; +} + +FSerializer& Serialize(FSerializer& arc, const char* keyname, TRCONDITION& w, TRCONDITION* def) +{ + static TRCONDITION nul; + if (arc.isReading()) w = {}; + if (arc.BeginObject(keyname)) + { + arc("length", w.length, &nul.length) + ("xindex", w.xindex, &nul.xindex) + .Array("obj", w.obj, w.length) + .EndObject(); + } + return arc; +} + void SerializeNNExts(FSerializer& arc) { if (arc.BeginObject("nnexts")) @@ -7937,6 +7966,8 @@ void SerializeNNExts(FSerializer& arc) ("impactspritescount", gImpactSpritesCount) .Array("impactspriteslist", gImpactSpritesList, gImpactSpritesCount) ("eventredirects", gEventRedirectsUsed) + ("trconditioncount", gTrackingCondsCount) + .Array("trcondition", gCondition, gTrackingCondsCount) .EndObject(); } } diff --git a/source/games/blood/src/nnexts.h b/source/games/blood/src/nnexts.h index 044758c2a..9f7abfd70 100644 --- a/source/games/blood/src/nnexts.h +++ b/source/games/blood/src/nnexts.h @@ -235,14 +235,14 @@ struct TRPLAYERCTRL { // this one for controlling the player using triggers (mov }; struct OBJECTS_TO_TRACK { - signed int type: 3; - unsigned int index: 16; - unsigned int cmd: 8; + int8_t type; + uint8_t cmd; + int index; }; struct TRCONDITION { - signed int xindex: 16; - unsigned int length: 8; + signed int xindex; + unsigned int length; OBJECTS_TO_TRACK obj[kMaxTracedObjects]; }; diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index c0b0b7ca4..3233f6e71 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -660,7 +660,8 @@ void playerStart(int nPlayer, int bNewLevel) pStartZone = &gStartZone[Random(8)]; } - spritetype *pSprite = actSpawnSprite_(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1); + auto actor = actSpawnSprite(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1); + spritetype* pSprite = &actor->s(); assert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites); XSPRITE *pXSprite = &xsprite[pSprite->extra]; pPlayer->pSprite = pSprite; @@ -977,20 +978,56 @@ char PickupItem(PLAYER *pPlayer, spritetype *pItem) { } } return 0; - case kItemFlagA: + case kItemFlagA: { if (gGameOptions.nGameType != 3) return 0; - evKill(pItem->index, 3, kCallbackReturnFlag); - pPlayer->hasFlag |= 1; - pPlayer->used2[0] = pItem->index; gBlueFlagDropped = false; + const bool enemyTeam = (pPlayer->teamId&1) == 1; + if (!enemyTeam && (pItem->owner >= 0) && (pItem->owner < kMaxSprites)) { + pPlayer->hasFlag &= ~1; + pPlayer->used2[0] = -1; + spritetype* pOwner = &sprite[pItem->owner]; + XSPRITE* pXOwner = &xsprite[pOwner->extra]; + trTriggerSprite(pOwner->index, pXOwner, kCmdOn); + sprintf(buffer, "%s returned Blue Flag", PlayerName(pPlayer->nPlayer)); + sndStartSample(8003, 255, 2, 0); + viewSetMessage(buffer); + break; + } + pPlayer->hasFlag |= 1; + pPlayer->used2[0] = pItem->owner; + if (enemyTeam) + { + sprintf(buffer, "%s stole Blue Flag", PlayerName(pPlayer->nPlayer)); + sndStartSample(8007, 255, 2, 0); + viewSetMessage(buffer); + } break; - case kItemFlagB: + } + case kItemFlagB: { if (gGameOptions.nGameType != 3) return 0; - evKill(pItem->index, 3, kCallbackReturnFlag); - pPlayer->hasFlag |= 2; - pPlayer->used2[1] = pItem->index; gRedFlagDropped = false; + const bool enemyTeam = (pPlayer->teamId&1) == 0; + if (!enemyTeam && (pItem->owner >= 0) && (pItem->owner < kMaxSprites)) { + pPlayer->hasFlag &= ~2; + pPlayer->used2[1] = -1; + spritetype* pOwner = &sprite[pItem->owner]; + XSPRITE* pXOwner = &xsprite[pOwner->extra]; + trTriggerSprite(pOwner->index, pXOwner, kCmdOn); + sprintf(buffer, "%s returned Red Flag", PlayerName(pPlayer->nPlayer)); + sndStartSample(8002, 255, 2, 0); + viewSetMessage(buffer); + break; + } + pPlayer->hasFlag |= 2; + pPlayer->used2[1] = pItem->owner; + if (enemyTeam) + { + sprintf(buffer, "%s stole Red Flag", PlayerName(pPlayer->nPlayer)); + sndStartSample(8006, 255, 2, 0); + viewSetMessage(buffer); + } break; + } case kItemArmorBasic: case kItemArmorBody: case kItemArmorFire: diff --git a/source/games/blood/src/qav.cpp b/source/games/blood/src/qav.cpp index 33c8d1750..43f18258a 100644 --- a/source/games/blood/src/qav.cpp +++ b/source/games/blood/src/qav.cpp @@ -40,9 +40,20 @@ extern void (*qavClientCallback[])(int, void *); // //========================================================================== -enum +using QAVPrevTileFinder = TILE_FRAME* (*)(FRAMEINFO* const thisFrame, FRAMEINFO* const prevFrame, const int& i); + +struct QAVInterpProps { - kQAVIsLoopable = 1 << 0, + QAVPrevTileFinder PrevTileFinder; + bool loopable; + TMap> IgnoreData; + + bool CanInterpFrameTile(const int& nFrame, const int& i) + { + // Check whether the current frame's tile is skippable. + auto thisFrame = IgnoreData.CheckKey(nFrame); + return thisFrame ? !thisFrame->Contains(i) : true; + } }; static TMap qavPrevTileFinders; @@ -79,7 +90,7 @@ static void qavInitTileFinderMap() }); } -QAVPrevTileFinder qavGetInterpType(const FString& type) +static QAVPrevTileFinder qavGetInterpType(const FString& type) { if (!qavPrevTileFinders.CountUsed()) qavInitTileFinderMap(); return *qavPrevTileFinders.CheckKey(type); @@ -92,7 +103,7 @@ bool GameInterface::IsQAVInterpTypeValid(const FString& type) void GameInterface::AddQAVInterpProps(const int& res_id, const FString& interptype, const bool& loopable, const TMap>& ignoredata) { - qavInterpProps.Insert(res_id, { loopable ? kQAVIsLoopable : 0, qavGetInterpType(interptype), ignoredata }); + qavInterpProps.Insert(res_id, { qavGetInterpType(interptype), loopable, ignoredata }); } void GameInterface::RemoveQAVInterpProps(const int& res_id) @@ -101,25 +112,21 @@ void GameInterface::RemoveQAVInterpProps(const int& res_id) } -void DrawFrame(double x, double y, double z, double a, TILE_FRAME *pTile, int stat, int shade, int palnum, bool to3dview) +void DrawFrame(double x, double y, double z, double a, double alpha, int picnum, int stat, int shade, int palnum, bool to3dview) { - stat |= pTile->stat; - if (palnum <= 0) palnum = pTile->palnum; - if (!to3dview) { - auto tex = tileGetTexture(pTile->picnum); + auto tex = tileGetTexture(picnum); double scale = z * (1. / 65536.); double angle = a * BAngToDegree; int renderstyle = (stat & RS_NOMASK)? STYLE_Normal : STYLE_Translucent; - double alpha = (stat & RS_TRANS1)? glblend[0].def[!!(stat & RS_TRANS2)].alpha : 1.; int pin = (stat & kQavOrientationLeft)? -1 : (stat & RS_ALIGN_R)? 1:0; auto translation = TRANSLATION(Translation_Remap, palnum); bool topleft = !!(stat & RS_TOPLEFT); bool xflip = !!(stat & 0x100); // repurposed flag bool yflip = !!(stat & RS_YFLIP); - auto color = shadeToLight(pTile->shade + shade); + auto color = shadeToLight(shade); DrawTexture(twod, tex, x, y, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_Rotate, angle, DTA_LegacyRenderStyle, renderstyle, DTA_Alpha, alpha, DTA_Pin, pin, DTA_TranslationIndex, translation, DTA_TopLeft, topleft, DTA_CenterOffsetRel, !topleft, DTA_FullscreenScale, FSMode_Fit320x200, DTA_FlipOffsets, true, DTA_Color, color, @@ -135,20 +142,24 @@ void DrawFrame(double x, double y, double z, double a, TILE_FRAME *pTile, int st if ((stat & kQavOrientationLeft)) stat |= RS_ALIGN_L; stat &= ~kQavOrientationLeft; - hud_drawsprite(x, y, z, a, pTile->picnum, pTile->shade + shade, palnum, stat); + hud_drawsprite(x, y, z, a, picnum, shade, palnum, stat, alpha); } } + +static QAVInterpProps forcedinterpdata{qavGetInterpType("picnum")}; + void QAV::Draw(double x, double y, int ticks, int stat, int shade, int palnum, bool to3dview, double const smoothratio) { assert(ticksPerFrame > 0); - auto const interpdata = qavInterpProps.CheckKey(res_id); + QAVInterpProps* interpdata = qavInterpProps.CheckKey(res_id); + if (!interpdata && cl_bloodqavforcedinterp) interpdata = &forcedinterpdata; auto const nFrame = clamp(ticks / ticksPerFrame, 0, nFrames - 1); FRAMEINFO* const thisFrame = &frames[nFrame]; - auto const oFrame = clamp((nFrame == 0 && (interpdata && (interpdata->flags & kQAVIsLoopable)) ? nFrames : nFrame) - 1, 0, nFrames - 1); + auto const oFrame = clamp((nFrame == 0 && interpdata && interpdata->loopable ? nFrames : nFrame) - 1, 0, nFrames - 1); FRAMEINFO* const prevFrame = &frames[oFrame]; bool const interpolate = interpdata && cl_hudinterpolation && cl_bloodqavinterp && (nFrames > 1) && (nFrame != oFrame) && (smoothratio != MaxSmoothRatio); @@ -164,6 +175,9 @@ void QAV::Draw(double x, double y, int ticks, int stat, int shade, int palnum, b double tileY = y; double tileZ; double tileA; + double tileAlpha; + int tileShade; + auto const tileStat = stat | thisTile->stat; if (prevTile) { @@ -171,6 +185,10 @@ void QAV::Draw(double x, double y, int ticks, int stat, int shade, int palnum, b tileY += interpolatedvaluef(prevTile->y, thisTile->y, smoothratio); tileZ = interpolatedvaluef(prevTile->z, thisTile->z, smoothratio); tileA = interpolatedangle(buildang(prevTile->angle), buildang(thisTile->angle), smoothratio).asbuildf(); + tileShade = interpolatedvalue(prevTile->shade, thisTile->shade, smoothratio) + shade; + auto prevAlpha = ((stat | prevTile->stat) & RS_TRANS1) ? glblend[0].def[!!((stat | prevTile->stat) & RS_TRANS2)].alpha : 1.; + auto thisAlpha = (tileStat & RS_TRANS1) ? glblend[0].def[!!(tileStat & RS_TRANS2)].alpha : 1.; + tileAlpha = interpolatedvaluef(prevAlpha, thisAlpha, smoothratio); } else { @@ -178,9 +196,11 @@ void QAV::Draw(double x, double y, int ticks, int stat, int shade, int palnum, b tileY += thisTile->y; tileZ = thisTile->z; tileA = thisTile->angle; + tileShade = thisTile->shade + shade; + tileAlpha = (tileStat & RS_TRANS1) ? glblend[0].def[!!(tileStat & RS_TRANS2)].alpha : 1.; } - DrawFrame(tileX, tileY, tileZ, tileA, thisTile, stat, shade, palnum, to3dview); + DrawFrame(tileX, tileY, tileZ, tileA, tileAlpha, thisTile->picnum, tileStat, tileShade, (palnum <= 0 ? thisTile->palnum : palnum), to3dview); } } } @@ -266,7 +286,7 @@ void qavProcessTicker(QAV* const pQAV, int* duration, int* lastTick) *duration = ClipLow(*duration, 0); } -void qavProcessTimer(PLAYER* const pPlayer, QAV* const pQAV, int* duration, double* smoothratio, bool const fixedduration) +void qavProcessTimer(PLAYER* const pPlayer, QAV* const pQAV, int* duration, double* smoothratio, bool const fixedduration, bool const ignoreWeaponTimer) { // Process if not paused. if (!paused) @@ -274,7 +294,7 @@ void qavProcessTimer(PLAYER* const pPlayer, QAV* const pQAV, int* duration, doub // Process clock based on QAV's ticrate and last tick value. qavProcessTicker(pQAV, &pPlayer->qavTimer, &pPlayer->qavLastTick); - if (pPlayer->weaponTimer == 0) + if (pPlayer->weaponTimer == 0 && pPlayer->qavTimer == 0 && !ignoreWeaponTimer) { // Check if we're playing an idle QAV as per the ticker's weapon timer. *duration = fixedduration ? pQAV->duration - 1 : I_GetBuildTime() % pQAV->duration; diff --git a/source/games/blood/src/qav.h b/source/games/blood/src/qav.h index 50c6a74f1..aa0e2bad5 100644 --- a/source/games/blood/src/qav.h +++ b/source/games/blood/src/qav.h @@ -238,25 +238,9 @@ struct QAV void Precache(int palette = 0); }; -using QAVPrevTileFinder = TILE_FRAME* (*)(FRAMEINFO* const thisFrame, FRAMEINFO* const prevFrame, const int& i); - -struct QAVInterpProps -{ - int flags; - QAVPrevTileFinder PrevTileFinder; - TMap> IgnoreData; - - bool CanInterpFrameTile(const int& nFrame, const int& i) - { - // Check whether the current frame's tile is skippable. - auto thisFrame = IgnoreData.CheckKey(nFrame); - return thisFrame ? !thisFrame->Contains(i) : true; - } -}; - QAV* getQAV(int res_id); void qavProcessTicker(QAV* const pQAV, int* duration, int* lastTick); -void qavProcessTimer(PLAYER* const pPlayer, QAV* const pQAV, int* duration, double* smoothratio, bool const fixedduration = false); +void qavProcessTimer(PLAYER* const pPlayer, QAV* const pQAV, int* duration, double* smoothratio, bool const fixedduration = false, bool const ignoreWeaponTimer = false); inline bool qavIsOriginal(const int& res_id) { diff --git a/source/games/blood/src/seq.cpp b/source/games/blood/src/seq.cpp index 0bf150b03..c886849b0 100644 --- a/source/games/blood/src/seq.cpp +++ b/source/games/blood/src/seq.cpp @@ -482,6 +482,11 @@ SEQINST* GetInstance(DBloodActor* actor) return activeList.get(3, actor->s().index); } +int seqGetStatus(DBloodActor* actor) +{ + return seqGetStatus(3, actor->s().index); +} + void seqKill(int type, int nXIndex) { activeList.remove(type, nXIndex); diff --git a/source/games/blood/src/seq.h b/source/games/blood/src/seq.h index 66031819f..3d547e9ef 100644 --- a/source/games/blood/src/seq.h +++ b/source/games/blood/src/seq.h @@ -107,6 +107,7 @@ void seqKill(int a1, int a2); void seqKill(DBloodActor* actor); void seqKillAll(void); int seqGetStatus(int a1, int a2); +int seqGetStatus(DBloodActor*); int seqGetID(int a1, int a2); void seqProcess(int a1); diff --git a/source/games/blood/src/sfx.cpp b/source/games/blood/src/sfx.cpp index f1dd417a6..8e0bdc95f 100644 --- a/source/games/blood/src/sfx.cpp +++ b/source/games/blood/src/sfx.cpp @@ -169,14 +169,7 @@ void sfxPlay3DSound(int x, int y, int z, int soundId, int nSector) if (chan) chan->UserData = nSector; } -enum EPlayFlags -{ - FX_GlobalChannel = 1, - FX_SoundMatch = 2, - FX_ChannelMatch = 4, -}; - -void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int a3, int a4, int pitch, int volume) +void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int playchannel, int playflags, int pitch, int volume) { if (!SoundEnabled() || soundId < 0 || !pSprite) return; auto sid = soundEngine->FindSoundByResID(soundId); @@ -188,17 +181,17 @@ void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int a3, int a4, int pitc sid = getSfx(sid, attenuation, pitch, volume); if (volume == -1) volume = 80; - if (a3 >= 0) + if (playchannel >= 0) { - a3++; // This is to make 0 a valid channel value. + playchannel++; // This is to make 0 a valid channel value. if (soundEngine->EnumerateChannels([=](FSoundChan* chan) -> int { if (chan->SourceType != SOURCE_Actor) return false; // other source types are not our business. - if (chan->EntChannel == a3 && (chan->Source == pSprite || (a4 & FX_GlobalChannel) != 0)) + if (chan->EntChannel == playchannel && (chan->Source == pSprite || (playflags & FX_GlobalChannel) != 0)) { - if ((a4 & FX_ChannelMatch) != 0 && chan->EntChannel == a3) + if ((playflags & FX_ChannelMatch) != 0 && chan->EntChannel == playchannel) return true; - if ((a4 & FX_SoundMatch) != 0 && chan->OrgID == sid) + if ((playflags & FX_SoundMatch) != 0 && chan->OrgID == sid) return true; soundEngine->StopChannel(chan); return -1; @@ -209,10 +202,10 @@ void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int a3, int a4, int pitc } auto sfx = soundEngine->GetSfx(sid); - EChanFlags flags = a3 == -1 ? CHANF_OVERLAP : CHANF_NONE; + EChanFlags flags = playchannel == -1 ? CHANF_OVERLAP : CHANF_NONE; if (sfx && sfx->LoopStart >= 0) flags |= CHANF_LOOP; - soundEngine->StartSound(SOURCE_Actor, pSprite, &svec, a3, flags, sid, volume * (0.8f / 80.f), attenuation, nullptr, pitch / 65536.f); + soundEngine->StartSound(SOURCE_Actor, pSprite, &svec, playchannel, flags, sid, volume * (0.8f / 80.f), attenuation, nullptr, pitch / 65536.f); } void sfxPlay3DSound(spritetype* pSprite, int soundId, int a3, int a4) diff --git a/source/games/blood/src/sound.h b/source/games/blood/src/sound.h index 7b7659e2f..1d4845e7b 100644 --- a/source/games/blood/src/sound.h +++ b/source/games/blood/src/sound.h @@ -62,4 +62,12 @@ void ambProcess(void); void ambKillAll(void); void ambInit(void); +enum EPlayFlags +{ + FX_GlobalChannel = 1, + FX_SoundMatch = 2, + FX_ChannelMatch = 4, +}; + + END_BLD_NS diff --git a/source/games/blood/src/triggers.cpp b/source/games/blood/src/triggers.cpp index e535f9552..28aa333de 100644 --- a/source/games/blood/src/triggers.cpp +++ b/source/games/blood/src/triggers.cpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "compat.h" #include "blood.h" +#include "misc.h" #include "d_net.h" BEGIN_BLD_NS @@ -206,14 +207,16 @@ void LifeLeechOperate(spritetype *pSprite, XSPRITE *pXSprite, EVENT event) PLAYER *pPlayer = &gPlayer[nPlayer]; if (pPlayer->pXSprite->health > 0) { + evKill(pSprite->index, 3); pPlayer->ammoCount[8] = ClipHigh(pPlayer->ammoCount[8]+pXSprite->data3, gAmmoInfo[8].max); pPlayer->hasWeapon[9] = 1; if (pPlayer->curWeapon != kWeapLifeLeech) { + if (!VanillaMode() && checkFired6or7(pPlayer)) // if tnt/spray is actively used, do not switch weapon + break; pPlayer->weaponState = 0; pPlayer->nextWeapon = 9; } - evKill(pSprite->index, 3); } } break; @@ -495,7 +498,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) case kThingArmedTNTStick: case kThingArmedTNTBundle: case kThingArmedSpray: - actExplodeSprite(pSprite); + actExplodeSprite(&bloodActors[pSprite->index]); break; case kTrapExploder: switch (event.cmd) { @@ -504,13 +507,13 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) break; default: pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE; - actExplodeSprite(pSprite); + actExplodeSprite(&bloodActors[pSprite->index]); break; } break; case kThingArmedRemoteBomb: if (pSprite->statnum != kStatRespawn) { - if (event.cmd != kCmdOn) actExplodeSprite(pSprite); + if (event.cmd != kCmdOn) actExplodeSprite(&bloodActors[pSprite->index]); else { sfxPlay3DSound(pSprite, 454, 0, 0); evPost(nSprite, 3, 18, kCmdOff); @@ -531,7 +534,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) pXSprite->Proximity = 1; break; default: - actExplodeSprite(pSprite); + actExplodeSprite(&bloodActors[pSprite->index]); break; } } diff --git a/source/games/blood/src/view.cpp b/source/games/blood/src/view.cpp index 23c6e3952..61036221b 100644 --- a/source/games/blood/src/view.cpp +++ b/source/games/blood/src/view.cpp @@ -359,43 +359,23 @@ void viewUpdateDelirium(void) void viewUpdateShake(int& cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, double& pshakeX, double& pshakeY) { - int shakeHoriz = 0; - int shakeAngle = 0; - int shakeX = 0; - int shakeY = 0; - int shakeZ = 0; - int shakeBobX = 0; - int shakeBobY = 0; - if (gView->flickerEffect) + auto doEffect = [&](const int& effectType) { - int nValue = ClipHigh(gView->flickerEffect * 8, 2000); - shakeHoriz += QRandom2(nValue >> 8); - shakeAngle += QRandom2(nValue >> 8); - shakeX += QRandom2(nValue >> 4); - shakeY += QRandom2(nValue >> 4); - shakeZ += QRandom2(nValue); - shakeBobX += QRandom2(nValue); - shakeBobY += QRandom2(nValue); - } - if (gView->quakeEffect) - { - int nValue = ClipHigh(gView->quakeEffect * 8, 2000); - shakeHoriz += QRandom2(nValue >> 8); - shakeAngle += QRandom2(nValue >> 8); - shakeX += QRandom2(nValue >> 4); - shakeY += QRandom2(nValue >> 4); - shakeZ += QRandom2(nValue); - shakeBobX += QRandom2(nValue); - shakeBobY += QRandom2(nValue); - } - cH += buildhoriz(shakeHoriz); - cA += buildang(shakeAngle); - cX += shakeX; - cY += shakeY; - cZ += shakeZ; - pshakeX += shakeBobX; - pshakeY += shakeBobY; + if (effectType) + { + int nValue = ClipHigh(effectType * 8, 2000); + cH += buildfhoriz(QRandom2F(nValue * (1. / 256.))); + cA += buildfang(QRandom2F(nValue * (1. / 256.))); + cX += QRandom2(nValue >> 4); + cY += QRandom2(nValue >> 4); + cZ += QRandom2(nValue); + pshakeX += QRandom2F(nValue); + pshakeY += QRandom2F(nValue); + } + }; + doEffect(gView->flickerEffect); + doEffect(gView->quakeEffect); } @@ -483,7 +463,7 @@ void SetupView(int &cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, int& nSe } viewUpdateShake(cX, cY, cZ, cA, cH, shakeX, shakeY); - cH += buildhoriz(MulScale(0x40000000 - Cos(gView->tiltEffect << 2), 30, 30)); + cH += q16horiz(xs_CRoundToInt(MulScaleF(double(0x40000000) - bcosf(gView->tiltEffect << 2, 16), 30, 14))); if (gViewPos == 0) { if (cl_viewhbob) @@ -495,7 +475,7 @@ void SetupView(int &cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, int& nSe { cZ += bobHeight; } - cZ += xs_CRoundToInt(cH.asq16() / 6553.6); + cZ += xs_CRoundToInt(cH.asq16() * (1. / 6553.6)); cameradist = -1; cameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16); } diff --git a/source/games/blood/src/view.h b/source/games/blood/src/view.h index b3d55e111..aefecfdb4 100644 --- a/source/games/blood/src/view.h +++ b/source/games/blood/src/view.h @@ -177,4 +177,6 @@ inline void viewBackupSpriteLoc(int nSprite, spritetype *pSprite) } } +void viewBackupSpriteLoc(DBloodActor* actor); + END_BLD_NS diff --git a/source/games/blood/src/weapon.cpp b/source/games/blood/src/weapon.cpp index 424d69dde..714028243 100644 --- a/source/games/blood/src/weapon.cpp +++ b/source/games/blood/src/weapon.cpp @@ -140,7 +140,7 @@ enum nClientAltFireNapalm, }; -static bool checkFired6or7(PLAYER *pPlayer) +bool checkFired6or7(PLAYER *pPlayer) { switch (pPlayer->curWeapon) { @@ -228,23 +228,35 @@ void SpawnShellEject(PLAYER *pPlayer, int a2, int a3) void WeaponInit(void) { - for (int i = 0; i < kQAVEnd; i++) + auto doInit = [](const int base) { - auto pQAV = getQAV(i); - if (!pQAV) - I_Error("Could not load QAV %d\n", i); - pQAV->nSprite = -1; - } + for (int i = base; i < (kQAVEnd + base); i++) + { + auto pQAV = getQAV(i); + if (!pQAV) + I_Error("Could not load QAV %d\n", i); + pQAV->nSprite = -1; + } + }; + + doInit(0); + doInit(10000); } void WeaponPrecache() { - for (int i = 0; i < kQAVEnd; i++) + auto doPrecache = [](const int base) { - auto pQAV = getQAV(i); - if (pQAV) - pQAV->Precache(); - } + for (int i = base; i < (kQAVEnd + base); i++) + { + auto pQAV = getQAV(i); + if (pQAV) + pQAV->Precache(); + } + }; + + doPrecache(0); + doPrecache(10000); } void WeaponDraw(PLAYER *pPlayer, int shade, double xpos, double ypos, int palnum) @@ -256,7 +268,7 @@ void WeaponDraw(PLAYER *pPlayer, int shade, double xpos, double ypos, int palnum int duration; double smoothratio; - qavProcessTimer(pPlayer, pQAV, &duration, &smoothratio, pPlayer->weaponState == -1 || (pPlayer->curWeapon == kWeapShotgun && pPlayer->weaponState == 7)); + qavProcessTimer(pPlayer, pQAV, &duration, &smoothratio, pPlayer->weaponState == -1, pPlayer->curWeapon == kWeapShotgun && pPlayer->weaponState == 7); pQAV->x = int(xpos); pQAV->y = int(ypos); @@ -297,6 +309,14 @@ static void StartQAV(PLAYER *pPlayer, int nWeaponQAV, int callback = -1, bool lo pPlayer->weaponTimer -= 4; } +static void SetQAV(PLAYER *pPlayer, int nWeaponQAV) +{ + assert(nWeaponQAV < kQAVEnd); + pPlayer->weaponQav = qavGetCorrectID(nWeaponQAV); + pPlayer->qavTimer = 0; + pPlayer->qavLastTick = 0; +} + struct WEAPONTRACK { int aimSpeedHorz; @@ -833,7 +853,7 @@ void WeaponUpdateState(PLAYER *pPlayer) switch (lastWeapon) { case kWeapPitchFork: - pPlayer->weaponQav = qavGetCorrectID(kQAVFORKIDLE); + SetQAV(pPlayer, kQAVFORKIDLE); break; case kWeapSpraycan: switch (vb) @@ -849,15 +869,15 @@ void WeaponUpdateState(PLAYER *pPlayer) StartQAV(pPlayer, kQAVCANPREF); } else - pPlayer->weaponQav = qavGetCorrectID(kQAVLITEIDLE); + SetQAV(pPlayer, kQAVLITEIDLE); break; case 3: - pPlayer->weaponQav = qavGetCorrectID(kQAVCANIDLE); + SetQAV(pPlayer, kQAVCANIDLE); break; case 4: if (CheckAmmo(pPlayer, 6, 1)) { - pPlayer->weaponQav = qavGetCorrectID(kQAVCANIDLE); + SetQAV(pPlayer, kQAVCANIDLE); pPlayer->weaponState = 3; } else @@ -890,10 +910,10 @@ void WeaponUpdateState(PLAYER *pPlayer) StartQAV(pPlayer, kQAVBUNUP); } else - pPlayer->weaponQav = qavGetCorrectID(kQAVLITEIDLE); + SetQAV(pPlayer, kQAVLITEIDLE); break; case 3: - pPlayer->weaponQav = qavGetCorrectID(kQAVBUNIDLE); + SetQAV(pPlayer, kQAVBUNIDLE); break; } break; @@ -901,7 +921,7 @@ void WeaponUpdateState(PLAYER *pPlayer) switch (vb) { case 7: - pPlayer->weaponQav = qavGetCorrectID(kQAVPROXIDLE); + SetQAV(pPlayer, kQAVPROXIDLE); break; case 8: pPlayer->weaponState = 7; @@ -913,10 +933,10 @@ void WeaponUpdateState(PLAYER *pPlayer) switch (vb) { case 10: - pPlayer->weaponQav = qavGetCorrectID(kQAVREMIDLE1); + SetQAV(pPlayer, kQAVREMIDLE1); break; case 11: - pPlayer->weaponQav = qavGetCorrectID(kQAVREMIDLE2); + SetQAV(pPlayer, kQAVREMIDLE2); break; case 12: if (pPlayer->ammoCount[11] > 0) @@ -939,7 +959,7 @@ void WeaponUpdateState(PLAYER *pPlayer) pPlayer->weaponState = 1; break; case 7: - pPlayer->weaponQav = qavGetCorrectID(kQAV2SHOTI); + SetQAV(pPlayer, kQAV2SHOTI); break; case 1: if (CheckAmmo(pPlayer, 2, 1)) @@ -952,25 +972,25 @@ void WeaponUpdateState(PLAYER *pPlayer) pPlayer->weaponState = 2; } else - pPlayer->weaponQav = qavGetCorrectID(kQAVSHOTI3); + SetQAV(pPlayer, kQAVSHOTI3); break; case 2: - pPlayer->weaponQav = qavGetCorrectID(kQAVSHOTI2); + SetQAV(pPlayer, kQAVSHOTI2); break; case 3: - pPlayer->weaponQav = qavGetCorrectID(kQAVSHOTI1); + SetQAV(pPlayer, kQAVSHOTI1); break; } break; case kWeapTommyGun: if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) { - pPlayer->weaponQav = qavGetCorrectID(kQAV2TOMIDLE); + SetQAV(pPlayer, kQAV2TOMIDLE); pPlayer->weaponState = 1; } else { - pPlayer->weaponQav = qavGetCorrectID(kQAVTOMIDLE); + SetQAV(pPlayer, kQAVTOMIDLE); pPlayer->weaponState = 0; } break; @@ -978,33 +998,33 @@ void WeaponUpdateState(PLAYER *pPlayer) if (powerupCheck(pPlayer, kPwUpTwoGuns)) { if (vb == 3 && checkAmmo2(pPlayer, 1, 2)) - pPlayer->weaponQav = qavGetCorrectID(kQAVFLAR2I); + SetQAV(pPlayer, kQAVFLAR2I); else { - pPlayer->weaponQav = qavGetCorrectID(kQAVFLARIDLE); + SetQAV(pPlayer, kQAVFLARIDLE); pPlayer->weaponState = 2; } } else - pPlayer->weaponQav = qavGetCorrectID(kQAVFLARIDLE); + SetQAV(pPlayer, kQAVFLARIDLE); break; case kWeapVoodooDoll: if (pXSprite->height < 256 && pPlayer->swayHeight != 0) StartQAV(pPlayer, kQAVVDIDLE2); else - pPlayer->weaponQav = qavGetCorrectID(kQAVVDIDLE1); + SetQAV(pPlayer, kQAVVDIDLE1); break; case kWeapTeslaCannon: switch (vb) { case 2: if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) - pPlayer->weaponQav = qavGetCorrectID(kQAV2SGUNIDL); + SetQAV(pPlayer, kQAV2SGUNIDL); else - pPlayer->weaponQav = qavGetCorrectID(kQAVSGUNIDL1); + SetQAV(pPlayer, kQAVSGUNIDL1); break; case 3: - pPlayer->weaponQav = qavGetCorrectID(kQAVSGUNIDL2); + SetQAV(pPlayer, kQAVSGUNIDL2); break; } break; @@ -1013,12 +1033,12 @@ void WeaponUpdateState(PLAYER *pPlayer) { case 3: if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer,4, 4))) - pPlayer->weaponQav = qavGetCorrectID(kQAV2NAPIDLE); + SetQAV(pPlayer, kQAV2NAPIDLE); else - pPlayer->weaponQav = qavGetCorrectID(kQAVNAPIDLE); + SetQAV(pPlayer, kQAVNAPIDLE); break; case 2: - pPlayer->weaponQav = qavGetCorrectID(kQAVNAPIDLE); + SetQAV(pPlayer, kQAVNAPIDLE); break; } break; @@ -1026,12 +1046,12 @@ void WeaponUpdateState(PLAYER *pPlayer) switch (vb) { case 2: - pPlayer->weaponQav = qavGetCorrectID(kQAVSTAFIDL1); + SetQAV(pPlayer, kQAVSTAFIDL1); break; } break; case kWeapBeast: - pPlayer->weaponQav = qavGetCorrectID(kQAVBSTIDLE); + SetQAV(pPlayer, kQAVBSTIDLE); break; } } @@ -1586,7 +1606,7 @@ void FireTesla(int nTrigger, PLAYER *pPlayer) if (!checkAmmo2(pPlayer, 7, pMissile->ammouse)) { pPlayer->weaponState = -1; - pPlayer->weaponQav = qavGetCorrectID(kQAVSGUNIDL2); + SetQAV(pPlayer, kQAVSGUNIDL2); pPlayer->flashEffect = 0; return; } @@ -2029,7 +2049,8 @@ void WeaponProcess(PLAYER *pPlayer) { pPlayer->weaponTimer -= 4; bool bShoot = pPlayer->input.actions & SB_FIRE; bool bShoot2 = pPlayer->input.actions & SB_ALTFIRE; - if ((bShoot || bShoot2) && pPlayer->weaponQav == qavGetCorrectID(kQAVVDIDLE2)) pPlayer->weaponTimer = 0; + const int prevNewWeaponVal = pPlayer->input.getNewWeapon(); // used to fix scroll issue for banned weapons + if ((bShoot || bShoot2 || prevNewWeaponVal) && pPlayer->weaponQav == qavGetCorrectID(kQAVVDIDLE2)) pPlayer->weaponTimer = 0; if (pPlayer->qavLoop && pPlayer->pXSprite->health > 0) { if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1)) @@ -2084,7 +2105,6 @@ void WeaponProcess(PLAYER *pPlayer) { return; break; } - const int prevNewWeaponVal = pPlayer->input.getNewWeapon(); // used to fix scroll issue for banned weapons if (VanillaMode()) { if (pPlayer->nextWeapon) @@ -2355,7 +2375,7 @@ void WeaponProcess(PLAYER *pPlayer) { switch (pPlayer->weaponState) { case 7: - pPlayer->weaponQav = qavGetCorrectID(kQAVPROXIDLE); + SetQAV(pPlayer, kQAVPROXIDLE); pPlayer->weaponState = 9; pPlayer->throwTime = PlayClock; return; @@ -2365,7 +2385,7 @@ void WeaponProcess(PLAYER *pPlayer) { switch (pPlayer->weaponState) { case 10: - pPlayer->weaponQav = qavGetCorrectID(kQAVREMIDLE1); + SetQAV(pPlayer, kQAVREMIDLE1); pPlayer->weaponState = 13; pPlayer->throwTime = PlayClock; return; diff --git a/source/games/sw/src/scrip2.cpp b/source/games/sw/src/scrip2.cpp index 5f316c9fa..e73d93dae 100644 --- a/source/games/sw/src/scrip2.cpp +++ b/source/games/sw/src/scrip2.cpp @@ -509,7 +509,7 @@ void LoadCustomInfoFromScript(const char *filename) curep = sc.Number; if (sc.ParseError) curep = -1; - else if ((unsigned)--curep >= 2u) + else if ((unsigned)curep > 2u) { sc.ScriptMessage("Episode number %d not in range 1-2\n", curep + 1); curep = -1; @@ -808,8 +808,8 @@ void LoadCustomInfoFromScript(const char *filename) break; } } - auto vol0 = MustFindVolume(0); - auto vol1 = MustFindVolume(1); + auto vol0 = MustFindVolume(1); + auto vol1 = MustFindVolume(2); auto map1 = FindMapByLevelNum(1); auto map5 = FindMapByLevelNum(5); if (vol0 && map1) vol0->startmap = map1->labelName; diff --git a/wadsrc/static/engine/grpinfo.txt b/wadsrc/static/engine/grpinfo.txt index be490ee49..1806623ff 100644 --- a/wadsrc/static/engine/grpinfo.txt +++ b/wadsrc/static/engine/grpinfo.txt @@ -374,8 +374,9 @@ grpinfo grpinfo { - name "Duke: Alien World Order" - dependency DUKE15_CRC + name "Duke: Alien World Order" + dependency DUKE15_CRC + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON mustcontain "FIREFLYTROOPER.CON", "FLAMETHROWER.CON", "music/E5L1_BulletDam.ogg", "sound/VO_E5L1_Duke_CreamAndSugar.ogg" gamefilter "Duke.Worldtour" GameID "DukeWorldTour" @@ -462,22 +463,6 @@ grpinfo GameID "Blood" } -grpinfo -{ - name "BLOOD: One Unit Whole Blood" - size 9570681 - crc BLOOD_CRC - defname "blood.def" - scriptname "BLOOD.INI" - flags GAMEFLAG_BLOOD - dependency 0 - loadgrp "SOUNDS.RFF", "GUI.RFF" - gamefilter "Blood.Blood" - FgColor 0 - BkColor 0x7f002f - GameID "Blood" -} - grpinfo { // This is for identifying older Blood versions. Since I have no information, all I can do is testing for a few known files. diff --git a/wadsrc/static/filter/blood/engine/engine.def b/wadsrc/static/filter/blood/engine/engine.def index 3078a4d0c..a3d5be401 100644 --- a/wadsrc/static/filter/blood/engine/engine.def +++ b/wadsrc/static/filter/blood/engine/engine.def @@ -662,45 +662,24 @@ defineqav 10092 { } defineqav 10093 { file "qavs/BSTUP.QAV" - interpolate { - type "index" - } } defineqav 10094 { file "qavs/BSTIDLE.QAV" - interpolate { - type "index" - } } defineqav 10095 { file "qavs/BSTATAK1.QAV" - interpolate { - type "index" - } } defineqav 10096 { file "qavs/BSTATAK2.QAV" - interpolate { - type "index" - } } defineqav 10097 { file "qavs/BSTATAK3.QAV" - interpolate { - type "index" - } } defineqav 10098 { file "qavs/BSTATAK4.QAV" - interpolate { - type "index" - } } defineqav 10099 { file "qavs/BSTDOWN.QAV" - interpolate { - type "index" - } } defineqav 10100 { file "qavs/VDUP.QAV" diff --git a/wadsrc/static/filter/blood/qavs/FLAR2FIR.QAV b/wadsrc/static/filter/blood/qavs/FLAR2FIR.QAV index 770bd0773..2c7c17962 100644 Binary files a/wadsrc/static/filter/blood/qavs/FLAR2FIR.QAV and b/wadsrc/static/filter/blood/qavs/FLAR2FIR.QAV differ diff --git a/wadsrc/static/filter/blood/qavs/FLARFIR2.QAV b/wadsrc/static/filter/blood/qavs/FLARFIR2.QAV index d235c29ff..275c4c507 100644 Binary files a/wadsrc/static/filter/blood/qavs/FLARFIR2.QAV and b/wadsrc/static/filter/blood/qavs/FLARFIR2.QAV differ diff --git a/wadsrc/static/filter/blood/qavs/VDFIRE1.QAV b/wadsrc/static/filter/blood/qavs/VDFIRE1.QAV index e156e5f5f..90b179d4a 100644 Binary files a/wadsrc/static/filter/blood/qavs/VDFIRE1.QAV and b/wadsrc/static/filter/blood/qavs/VDFIRE1.QAV differ diff --git a/wadsrc/static/filter/blood/qavs/VDFIRE2.QAV b/wadsrc/static/filter/blood/qavs/VDFIRE2.QAV index 835120ce9..766a2fc92 100644 Binary files a/wadsrc/static/filter/blood/qavs/VDFIRE2.QAV and b/wadsrc/static/filter/blood/qavs/VDFIRE2.QAV differ diff --git a/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt index e44fe042d..98f54fd46 100644 --- a/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt +++ b/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt @@ -63,7 +63,6 @@ map { 2, 2 } { interbackground = "LEVELMAP09" rr_startsound = 176 - clearweapons } map { 2, 3 } diff --git a/wadsrc/static/zscript/engine/ui/menu/listmenu.zs b/wadsrc/static/zscript/engine/ui/menu/listmenu.zs index 14cd07569..cc4656d95 100644 --- a/wadsrc/static/zscript/engine/ui/menu/listmenu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/listmenu.zs @@ -260,7 +260,7 @@ class ListMenu : Menu { for(int i=0;i