Merge remote-tracking branch 'public/master'

This commit is contained in:
Mitchell Richters 2021-08-29 21:26:08 +10:00
commit 65d4919e5e
48 changed files with 2216 additions and 2126 deletions

View file

@ -24,13 +24,13 @@ jobs:
} }
- { - {
name: "macOS", name: "macOS",
os: macos-10.15, os: macos-11,
deps_cmdline: "brew install libvpx", deps_cmdline: "brew install libvpx",
build_type: "Release" build_type: "Release"
} }
- { - {
name: "macOS", name: "macOS",
os: macos-10.15, os: macos-11,
extra_options: "-DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF", extra_options: "-DDYN_FLUIDSYNTH=OFF -DDYN_OPENAL=OFF -DDYN_SNDFILE=OFF -DDYN_MPG123=OFF",
deps_cmdline: "brew install libvpx fluidsynth mpg123 libsndfile", deps_cmdline: "brew install libvpx fluidsynth mpg123 libsndfile",
build_type: "Debug" build_type: "Debug"
@ -54,14 +54,14 @@ jobs:
os: ubuntu-20.04, os: ubuntu-20.04,
extra_options: "-DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 \ 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", -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" build_type: "Debug"
} }
- { - {
name: "Linux Clang 11", name: "Linux Clang 12",
os: ubuntu-20.04, os: ubuntu-20.04,
extra_options: "-DCMAKE_C_COMPILER=clang-11 -DCMAKE_CXX_COMPILER=clang++-11", extra_options: "-DCMAKE_C_COMPILER=clang-12 -DCMAKE_CXX_COMPILER=clang++-12",
deps_cmdline: "sudo apt update && sudo apt install clang-11 libsdl2-dev libvpx-dev", deps_cmdline: "sudo apt update && sudo apt install clang-12 libsdl2-dev libvpx-dev",
build_type: "Release" build_type: "Release"
} }

View file

@ -837,6 +837,18 @@ public:
CopyNodes(o.Nodes, o.Size); 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) TMap &operator= (const TMap &o)
{ {
NumUsed = 0; NumUsed = 0;
@ -846,6 +858,12 @@ public:
return *this; return *this;
} }
TMap &operator= (TMap &&o)
{
TransferFrom(o);
return *this;
}
//======================================================================= //=======================================================================
// //
// TransferFrom // TransferFrom

View file

@ -2026,7 +2026,7 @@ static bool parseDefineQAVInterpolateIgnoreBlock(FScanner& sc, const int& res_id
if (sc.StartBraces(&blockend)) 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; return false;
} }
while (!sc.FoundEndBrace(blockend)) 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'. // Confirm we received something for 'frames' and 'tiles'.
if (scframes.IsEmpty() || sctiles.IsEmpty()) 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; return false;
} }
@ -2102,7 +2102,7 @@ static bool parseDefineQAVInterpolateBlock(FScanner& sc, const int& res_id, cons
if (sc.StartBraces(&blockend)) 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; return false;
} }
while (!sc.FoundEndBrace(blockend)) while (!sc.FoundEndBrace(blockend))
@ -2112,13 +2112,13 @@ static bool parseDefineQAVInterpolateBlock(FScanner& sc, const int& res_id, cons
{ {
if (interptype.IsNotEmpty()) 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; return false;
} }
sc.GetString(interptype); sc.GetString(interptype);
if (!gi->IsQAVInterpTypeValid(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; return false;
} }
} }

View file

@ -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_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_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_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_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_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") CUSTOM_CVARD(Int, cl_autoaim, 1, CVAR_ARCHIVE|CVAR_USERINFO, "enable/disable weapon autoaim")

View file

@ -32,8 +32,10 @@ EXTERN_CVAR(Bool, cl_bloodvanillabobbing)
EXTERN_CVAR(Bool, cl_bloodvanillaexplosions) EXTERN_CVAR(Bool, cl_bloodvanillaexplosions)
EXTERN_CVAR(Bool, cl_bloodvanillaenemies) EXTERN_CVAR(Bool, cl_bloodvanillaenemies)
EXTERN_CVAR(Bool, cl_bloodqavinterp) EXTERN_CVAR(Bool, cl_bloodqavinterp)
EXTERN_CVAR(Bool, cl_bloodqavforcedinterp)
EXTERN_CVAR(Bool, cl_bloodweapinterp) EXTERN_CVAR(Bool, cl_bloodweapinterp)
EXTERN_CVAR(Bool, cl_bloodoldweapbalance) EXTERN_CVAR(Bool, cl_bloodoldweapbalance)
EXTERN_CVAR(Bool, cl_loadingscreens)
EXTERN_CVAR(Bool, demorec_seeds_cvar) EXTERN_CVAR(Bool, demorec_seeds_cvar)
EXTERN_CVAR(Bool, demoplay_diffs) EXTERN_CVAR(Bool, demoplay_diffs)

View file

@ -222,10 +222,10 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
if (tocluster == nullptr || !CreateCutscene(&tocluster->intro, runner, toMap, !!fromMap)) if (tocluster == nullptr || !CreateCutscene(&tocluster->intro, runner, toMap, !!fromMap))
CreateCutscene(&globalCutscenes.DefaultMapIntro, 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, // 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. // 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); CreateCutscene(&globalCutscenes.LoadingScreen, runner, toMap, true);
} }
else if (isShareware()) else if (isShareware())

View file

@ -840,9 +840,6 @@ TArray<GrpEntry> GrpScan()
sortedFileList.Delete(0, findex + 1); sortedFileList.Delete(0, findex + 1);
sortedGroupList.Delete(0, gindex + 1); sortedGroupList.Delete(0, gindex + 1);
if (sortedGroupList.Size() == 0 || sortedFileList.Size() == 0)
return foundGames;
for (auto entry : sortedFileList) for (auto entry : sortedFileList)
{ {
GetCRC(entry, cachedCRCs); GetCRC(entry, cachedCRCs);

File diff suppressed because it is too large Load diff

View file

@ -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(int nSource, spritetype *pSprite, DAMAGE_TYPE a3, int a4);
int actDamageSprite(DBloodActor* pSource, DBloodActor* pTarget, DAMAGE_TYPE damageType, int damage); int actDamageSprite(DBloodActor* pSource, DBloodActor* pTarget, DAMAGE_TYPE damageType, int damage);
void actHitcodeToData(int a1, HITINFO *pHitInfo, DBloodActor **actor, walltype **a7 = nullptr); void actHitcodeToData(int a1, HITINFO *pHitInfo, DBloodActor **actor, walltype **a7 = nullptr);
void actAirDrag(spritetype *pSprite, int a2); void actAirDrag(DBloodActor *pSprite, int a2);
int MoveThing(spritetype *pSprite); void actExplodeSprite(DBloodActor *pSprite);
void MoveDude(spritetype *pSprite);
int MoveMissile(spritetype *pSprite);
void actExplodeSprite(spritetype *pSprite);
void actActivateGibObject(DBloodActor *actor); void actActivateGibObject(DBloodActor *actor);
bool IsUnderWater(spritetype *pSprite);
void actProcessSprites(void); 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); 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 *actSpawnDude(spritetype *pSource, short nType, int a3, int a4);
spritetype * actSpawnSprite(spritetype *pSource, int nStat); spritetype * actSpawnSprite(spritetype *pSource, int nStat);

View file

@ -33,7 +33,6 @@ BEGIN_BLD_NS
void RecoilDude(DBloodActor* actor); void RecoilDude(DBloodActor* actor);
int cumulDamage[kMaxXSprites]; int cumulDamage[kMaxXSprites];
DUDEEXTRA gDudeExtra[kMaxXSprites];
AISTATE genIdle = {kAiStateGenIdle, 0, -1, 0, NULL, NULL, NULL, NULL }; AISTATE genIdle = {kAiStateGenIdle, 0, -1, 0, NULL, NULL, NULL, NULL };
AISTATE genRecoil = {kAiStateRecoil, 5, -1, 20, NULL, NULL, NULL, &genIdle }; 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) 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) if (a3 == AI_SFX_PRIORITY_0)
sfxPlay3DSound(pSprite, a2, a4, 2); sfxPlay3DSound(pSprite, a2, a4, 2);
else if (a3 > pDudeExtra->prio || pDudeExtra->time <= PlayClock) else if (a3 > pDudeExtra->prio || pDudeExtra->time <= PlayClock)
@ -321,7 +321,7 @@ void aiActivateDude(DBloodActor* actor)
switch (pSprite->type) { switch (pSprite->type) {
case kDudePhantasm: case kDudePhantasm:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -340,7 +340,7 @@ void aiActivateDude(DBloodActor* actor)
case kDudeCultistTNT: case kDudeCultistTNT:
case kDudeCultistBeast: case kDudeCultistBeast:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->target == -1) { if (pXSprite->target == -1) {
@ -378,7 +378,7 @@ void aiActivateDude(DBloodActor* actor)
#ifdef NOONE_EXTENSIONS #ifdef NOONE_EXTENSIONS
case kDudeModernCustom: case kDudeModernCustom:
{ {
DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1* pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->target == -1) { if (pXSprite->target == -1) {
@ -397,7 +397,7 @@ void aiActivateDude(DBloodActor* actor)
break; break;
#endif #endif
case kDudeCultistTommyProne: { 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; pDudeExtraE->xval3 = 1; pDudeExtraE->xval1 = 0;
pSprite->type = kDudeCultistTommy; pSprite->type = kDudeCultistTommy;
if (pXSprite->target == -1) { if (pXSprite->target == -1) {
@ -430,7 +430,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeCultistShotgunProne: case kDudeCultistShotgunProne:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
pSprite->type = kDudeCultistShotgun; pSprite->type = kDudeCultistShotgun;
@ -474,7 +474,7 @@ void aiActivateDude(DBloodActor* actor)
break; break;
case kDudeBat: case kDudeBat:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -492,7 +492,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeBoneEel: case kDudeBoneEel:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -509,7 +509,7 @@ void aiActivateDude(DBloodActor* actor)
break; break;
} }
case kDudeGillBeast: { case kDudeGillBeast: {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
XSECTOR *pXSector = NULL; XSECTOR *pXSector = NULL;
if (sector[pSprite->sectnum].extra > 0) if (sector[pSprite->sectnum].extra > 0)
pXSector = &xsector[sector[pSprite->sectnum].extra]; pXSector = &xsector[sector[pSprite->sectnum].extra];
@ -537,7 +537,7 @@ void aiActivateDude(DBloodActor* actor)
break; break;
} }
case kDudeZombieAxeNormal: { case kDudeZombieAxeNormal: {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 1; pDudeExtraE->xval2 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->target == -1) if (pXSprite->target == -1)
@ -567,7 +567,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeZombieAxeBuried: case kDudeZombieAxeBuried:
{ {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 1; pDudeExtraE->xval2 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->aiState == &zombieEIdle) if (pXSprite->aiState == &zombieEIdle)
@ -576,7 +576,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeZombieAxeLaying: case kDudeZombieAxeLaying:
{ {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 1; pDudeExtraE->xval2 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->aiState == &zombieSIdle) if (pXSprite->aiState == &zombieSIdle)
@ -584,7 +584,7 @@ void aiActivateDude(DBloodActor* actor)
break; break;
} }
case kDudeZombieButcher: { case kDudeZombieButcher: {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 1; pDudeExtraE->xval2 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->target == -1) if (pXSprite->target == -1)
@ -612,7 +612,7 @@ void aiActivateDude(DBloodActor* actor)
aiNewState(actor, &zombieFBurnChase); aiNewState(actor, &zombieFBurnChase);
break; break;
case kDudeGargoyleFlesh: { case kDudeGargoyleFlesh: {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -630,7 +630,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeGargoyleStone: case kDudeGargoyleStone:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -747,7 +747,7 @@ void aiActivateDude(DBloodActor* actor)
} }
break; break;
case kDudeSpiderMother: { case kDudeSpiderMother: {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 1; pDudeExtraE->xval3 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
pSprite->flags |= 2; pSprite->flags |= 2;
@ -763,7 +763,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeTinyCaleb: case kDudeTinyCaleb:
{ {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 1; pDudeExtraE->xval2 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->target == -1) if (pXSprite->target == -1)
@ -796,7 +796,7 @@ void aiActivateDude(DBloodActor* actor)
} }
case kDudeBeast: case kDudeBeast:
{ {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 1; pDudeExtraE->xval2 = 1;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
if (pXSprite->target == -1) if (pXSprite->target == -1)
@ -944,9 +944,9 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
if (pSprite->type == kDudeModernCustomBurning) { if (pSprite->type == kDudeModernCustomBurning) {
if (Chance(0x2000) && gDudeExtra[pSprite->extra].time < PlayClock) { if (Chance(0x2000) && actor->dudeExtra.time < PlayClock) {
playGenDudeSound(pSprite, kGenDudeSndBurning); playGenDudeSound(pSprite, kGenDudeSndBurning);
gDudeExtra[pSprite->extra].time = PlayClock + 360; actor->dudeExtra.time = PlayClock + 360;
} }
if (pXSprite->burnTime == 0) pXSprite->burnTime = 2400; if (pXSprite->burnTime == 0) pXSprite->burnTime = 2400;
@ -992,7 +992,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
aiGenDudeNewState(pSprite, &genDudeBurnGoto); aiGenDudeNewState(pSprite, &genDudeBurnGoto);
actHealDude(pXSprite, dudeInfo[55].startHealth, dudeInfo[55].startHealth); actHealDude(pXSprite, dudeInfo[55].startHealth, dudeInfo[55].startHealth);
gDudeExtra[pSprite->extra].time = PlayClock + 360; actor->dudeExtra.time = PlayClock + 360;
evKill(nSprite, 3, kCallbackFXFlameLick); evKill(nSprite, 3, kCallbackFXFlameLick);
} }
@ -1030,7 +1030,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
if (nDmgType == kDamageTesla) if (nDmgType == kDamageTesla)
{ {
DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; DUDEEXTRA *pDudeExtra = &actor->dudeExtra;
pDudeExtra->recoil = 1; pDudeExtra->recoil = 1;
} }
const bool fixRandomCultist = !cl_bloodvanillaenemies && (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode(); // fix burning cultists randomly switching types underwater 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); aiNewState(actor, &cultistBurnGoto);
aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
aiPlay3DSound(pSprite, 1031+Random(2), AI_SFX_PRIORITY_2, -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); actHealDude(pXSprite, dudeInfo[40].startHealth, dudeInfo[40].startHealth);
evKill(nSprite, 3, kCallbackFXFlameLick); evKill(nSprite, 3, kCallbackFXFlameLick);
} }
@ -1066,16 +1066,16 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
pSprite->type = kDudeBurningInnocent; pSprite->type = kDudeBurningInnocent;
aiNewState(actor, &cultistBurnGoto); aiNewState(actor, &cultistBurnGoto);
aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); 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); actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
evKill(nSprite, 3, kCallbackFXFlameLick); evKill(nSprite, 3, kCallbackFXFlameLick);
} }
break; break;
case kDudeBurningCultist: 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); 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)) 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); aiNewState(actor, &cultistBurnGoto);
} }
aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); 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); actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
evKill(nSprite, 3, kCallbackFXFlameLick); evKill(nSprite, 3, kCallbackFXFlameLick);
} }
@ -1157,7 +1157,7 @@ void RecoilDude(DBloodActor* actor)
auto pXSprite = &actor->x(); auto pXSprite = &actor->x();
auto pSprite = &actor->s(); auto pSprite = &actor->s();
char v4 = Chance(0x8000); char v4 = Chance(0x8000);
DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; DUDEEXTRA *pDudeExtra = &actor->dudeExtra;
if (pSprite->statnum == kStatDude && (pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { if (pSprite->statnum == kStatDude && (pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
switch (pSprite->type) { switch (pSprite->type) {
@ -1573,15 +1573,15 @@ void aiInit(void)
void aiInitSprite(spritetype *pSprite) void aiInitSprite(spritetype *pSprite)
{ {
auto actor = &bloodActors[pSprite->index];
int nXSprite = pSprite->extra; int nXSprite = pSprite->extra;
XSPRITE *pXSprite = &xsprite[nXSprite]; XSPRITE *pXSprite = &xsprite[nXSprite];
auto actor = &bloodActors[pXSprite->reference];
int nSector = pSprite->sectnum; int nSector = pSprite->sectnum;
int nXSector = sector[nSector].extra; int nXSector = sector[nSector].extra;
XSECTOR *pXSector = NULL; XSECTOR *pXSector = NULL;
if (nXSector > 0) if (nXSector > 0)
pXSector = &xsector[nXSector]; pXSector = &xsector[nXSector];
DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; DUDEEXTRA *pDudeExtra = &actor->dudeExtra;
pDudeExtra->recoil = 0; pDudeExtra->recoil = 0;
pDudeExtra->time = 0; pDudeExtra->time = 0;
@ -1609,7 +1609,7 @@ void aiInitSprite(spritetype *pSprite)
case kDudeCultistTNT: case kDudeCultistTNT:
case kDudeCultistBeast: case kDudeCultistBeast:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &cultistIdle); aiNewState(actor, &cultistIdle);
@ -1617,7 +1617,7 @@ void aiInitSprite(spritetype *pSprite)
} }
case kDudeCultistTommyProne: case kDudeCultistTommyProne:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &fanaticProneIdle); aiNewState(actor, &fanaticProneIdle);
@ -1625,21 +1625,21 @@ void aiInitSprite(spritetype *pSprite)
} }
case kDudeCultistShotgunProne: case kDudeCultistShotgunProne:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &cultistProneIdle); aiNewState(actor, &cultistProneIdle);
break; break;
} }
case kDudeZombieButcher: { case kDudeZombieButcher: {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &zombieFIdle); aiNewState(actor, &zombieFIdle);
break; break;
} }
case kDudeZombieAxeNormal: { case kDudeZombieAxeNormal: {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &zombieAIdle); aiNewState(actor, &zombieAIdle);
@ -1647,7 +1647,7 @@ void aiInitSprite(spritetype *pSprite)
} }
case kDudeZombieAxeLaying: case kDudeZombieAxeLaying:
{ {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &zombieSIdle); aiNewState(actor, &zombieSIdle);
@ -1655,7 +1655,7 @@ void aiInitSprite(spritetype *pSprite)
break; break;
} }
case kDudeZombieAxeBuried: { case kDudeZombieAxeBuried: {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &zombieEIdle); aiNewState(actor, &zombieEIdle);
@ -1663,7 +1663,7 @@ void aiInitSprite(spritetype *pSprite)
} }
case kDudeGargoyleFlesh: case kDudeGargoyleFlesh:
case kDudeGargoyleStone: { case kDudeGargoyleStone: {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -1675,7 +1675,7 @@ void aiInitSprite(spritetype *pSprite)
aiNewState(actor, &gargoyleStatueIdle); aiNewState(actor, &gargoyleStatueIdle);
break; break;
case kDudeCerberusTwoHead: { case kDudeCerberusTwoHead: {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &cerberusIdle); aiNewState(actor, &cerberusIdle);
@ -1689,7 +1689,7 @@ void aiInitSprite(spritetype *pSprite)
break; break;
case kDudePhantasm: case kDudePhantasm:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -1704,7 +1704,7 @@ void aiInitSprite(spritetype *pSprite)
break; break;
case kDudeBoneEel: case kDudeBoneEel:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -1716,7 +1716,7 @@ void aiInitSprite(spritetype *pSprite)
break; break;
case kDudeBat: case kDudeBat:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -1727,7 +1727,7 @@ void aiInitSprite(spritetype *pSprite)
case kDudeSpiderRed: case kDudeSpiderRed:
case kDudeSpiderBlack: case kDudeSpiderBlack:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -1736,7 +1736,7 @@ void aiInitSprite(spritetype *pSprite)
} }
case kDudeSpiderMother: case kDudeSpiderMother:
{ {
DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; DUDEEXTRA_at6_u1 *pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = 0; pDudeExtraE->xval3 = 0;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
@ -1745,7 +1745,7 @@ void aiInitSprite(spritetype *pSprite)
} }
case kDudeTchernobog: case kDudeTchernobog:
{ {
DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2; DUDEEXTRA_at6_u2 *pDudeExtraE = &actor->dudeExtra.at6.u2;
pDudeExtraE->xval2 = 0; pDudeExtraE->xval2 = 0;
pDudeExtraE->xval1 = 0; pDudeExtraE->xval1 = 0;
aiNewState(actor, &tchernobogIdle); 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 END_BLD_NS

View file

@ -87,7 +87,6 @@ struct TARGETTRACK {
}; };
extern const int dword_138BB0[5]; extern const int dword_138BB0[5];
extern DUDEEXTRA gDudeExtra[];
bool dudeIsPlayingSeq(spritetype *pSprite, int nSeq); bool dudeIsPlayingSeq(spritetype *pSprite, int nSeq);
void aiPlay3DSound(spritetype *pSprite, int a2, AI_SFX_PRIORITY a3, int a4); void aiPlay3DSound(spritetype *pSprite, int a2, AI_SFX_PRIORITY a3, int a4);

View file

@ -84,7 +84,7 @@ static void batThinkTarget(DBloodActor* actor)
auto pSprite = &actor->s(); auto pSprite = &actor->s();
assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 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) if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10)
pDudeExtraE->xval2++; pDudeExtraE->xval2++;
else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3)
@ -433,7 +433,7 @@ void batMoveToCeil(DBloodActor* actor)
int nSector = pSprite->sectnum; int nSector = pSprite->sectnum;
if (z - pXSprite->targetZ < 0x1000) 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; pDudeExtraE->xval3 = 0;
pSprite->flags = 0; pSprite->flags = 0;
aiNewState(actor, &batIdle); aiNewState(actor, &batIdle);

View file

@ -95,7 +95,7 @@ static void eelThinkTarget(DBloodActor* actor)
auto pSprite = &actor->s(); auto pSprite = &actor->s();
assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); assert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 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) if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10)
pDudeExtraE->xval2++; pDudeExtraE->xval2++;
else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3)
@ -436,7 +436,7 @@ void eelMoveToCeil(DBloodActor* actor)
int nSector = pSprite->sectnum; int nSector = pSprite->sectnum;
if (z - pXSprite->targetZ < 0x1000) 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; pDudeExtraE->xval3 = 0;
pSprite->flags = 0; pSprite->flags = 0;
aiNewState(actor, &eelIdle); aiNewState(actor, &eelIdle);

View file

@ -266,7 +266,7 @@ static void cerberusThinkTarget(DBloodActor* actor)
return; return;
} }
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 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) if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10)
pDudeExtraE->xval2++; pDudeExtraE->xval2++;
else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3)

View file

@ -215,7 +215,7 @@ static void gargThinkTarget(DBloodActor* actor)
return; return;
} }
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 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) if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10)
pDudeExtraE->xval2++; pDudeExtraE->xval2++;
else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3)

View file

@ -191,7 +191,7 @@ static void ghostThinkTarget(DBloodActor* actor)
return; return;
} }
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 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) if (pDudeExtraE->xval3 && pDudeExtraE->xval2 < 10)
pDudeExtraE->xval2++; pDudeExtraE->xval2++;
else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3) else if (pDudeExtraE->xval2 >= 10 && pDudeExtraE->xval3)

View file

@ -146,7 +146,7 @@ void SpidBirthSeqCallback(int, DBloodActor* actor)
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
assert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); assert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
spritetype *pTarget = &sprite[pXSprite->target]; 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 dx = pXSprite->targetX-pSprite->x;
int dy = pXSprite->targetY-pSprite->y; int dy = pXSprite->targetY-pSprite->y;
int nAngle = getangle(dx, dy); int nAngle = getangle(dx, dy);

View file

@ -233,7 +233,7 @@ static void sub_725A4(DBloodActor* actor)
return; return;
} }
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 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) if (pDudeExtraE->xval2 && pDudeExtraE->xval1 < 10)
pDudeExtraE->xval1++; pDudeExtraE->xval1++;
else if (pDudeExtraE->xval1 >= 10 && pDudeExtraE->xval2) else if (pDudeExtraE->xval1 >= 10 && pDudeExtraE->xval2)

View file

@ -1593,8 +1593,10 @@ void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event)
} }
} }
bool doExplosion(spritetype* pSprite, int nType) { bool doExplosion(spritetype* pSprite, int nType)
spritetype* pExplosion = actSpawnSprite_(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatExplosion, true); {
auto actor = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatExplosion, true);
spritetype* pExplosion = &actor->s();
if (pExplosion->extra < 0 || pExplosion->extra >= kMaxXSprites) if (pExplosion->extra < 0 || pExplosion->extra >= kMaxXSprites)
return false; return false;
@ -2215,10 +2217,12 @@ void genDudePostDeath(spritetype* pSprite, DAMAGE_TYPE damageType, int damage) {
actPostSprite(pSprite->index, kStatThing); actPostSprite(pSprite->index, kStatThing);
} }
void aiGenDudeInitSprite(spritetype* pSprite, XSPRITE* pXSprite) { void aiGenDudeInitSprite(spritetype* pSprite, XSPRITE* pXSprite)
{
auto actor = &bloodActors[pSprite->index];
switch (pSprite->type) { switch (pSprite->type) {
case kDudeModernCustom: { case kDudeModernCustom: {
DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; DUDEEXTRA_at6_u1* pDudeExtraE = &actor->dudeExtra.at6.u1;
pDudeExtraE->xval3 = pDudeExtraE->xval1 = 0; pDudeExtraE->xval3 = pDudeExtraE->xval1 = 0;
aiGenDudeNewState(pSprite, &genDudeIdleL); aiGenDudeNewState(pSprite, &genDudeIdleL);
break; break;

View file

@ -4,6 +4,8 @@ BEGIN_BLD_NS
extern int cumulDamage[kMaxXSprites]; 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. // 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. // Note that the indexing is very inconsistent - partially by sprite index, partially by xsprite index.
class DBloodActor class DBloodActor
@ -13,6 +15,7 @@ class DBloodActor
public: public:
int dudeSlope; int dudeSlope;
DUDEEXTRA dudeExtra;
DBloodActor() :index(int(this - base())) { /*assert(index >= 0 && index < kMaxSprites);*/ } DBloodActor() :index(int(this - base())) { /*assert(index >= 0 && index < kMaxSprites);*/ }
DBloodActor& operator=(const DBloodActor& other) = default; DBloodActor& operator=(const DBloodActor& other) = default;
@ -20,6 +23,7 @@ public:
void Clear() void Clear()
{ {
dudeSlope = 0; dudeSlope = 0;
dudeExtra = {};
} }
bool hasX() { return sprite[index].extra > 0; } bool hasX() { return sprite[index].extra > 0; }
void addX() void addX()
@ -34,7 +38,6 @@ public:
int& zvel() { return Blood::zvel[index]; } int& zvel() { return Blood::zvel[index]; }
int& cumulDamage() { return Blood::cumulDamage[sprite[index].extra]; } int& cumulDamage() { return Blood::cumulDamage[sprite[index].extra]; }
DUDEEXTRA& dudeExtra() { return gDudeExtra[sprite[index].extra]; }
SPRITEMASS& spriteMass() { return gSpriteMass[sprite[index].extra]; } SPRITEMASS& spriteMass() { return gSpriteMass[sprite[index].extra]; }
GENDUDEEXTRA& genDudeExtra() { return Blood::gGenDudeExtra[index]; } GENDUDEEXTRA& genDudeExtra() { return Blood::gGenDudeExtra[index]; }
POINT3D& basePoint() { return Blood::baseSprite[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]; 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 END_BLD_NS

View file

@ -561,6 +561,11 @@ inline int QRandom2(int a1)
return MulScale(qrand(), a1, 14)-a1; return MulScale(qrand(), a1, 14)-a1;
} }
inline double QRandom2F(double a1)
{
return MulScaleF(qrand(), a1, 14)-a1;
}
template<class T> template<class T>
inline void SetBitString(T *pArray, int nIndex) inline void SetBitString(T *pArray, int nIndex)
{ {

View file

@ -165,7 +165,8 @@ spritetype * CFX::fxSpawn(FX_ID nFx, int nSector, int x, int y, int z, unsigned
return NULL; return NULL;
destroy(nSprite); 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->type = nFx;
pSprite->picnum = pFX->picnum; pSprite->picnum = pFX->picnum;
pSprite->cstat |= pFX->cstat; pSprite->cstat |= pFX->cstat;
@ -204,7 +205,7 @@ void CFX::fxProcess(void)
assert(nSector >= 0 && nSector < kMaxSectors); assert(nSector >= 0 && nSector < kMaxSectors);
assert(pSprite->type < kFXMax); assert(pSprite->type < kFXMax);
FXDATA *pFXData = &gFXData[pSprite->type]; FXDATA *pFXData = &gFXData[pSprite->type];
actAirDrag(pSprite, pFXData->drag); actAirDrag(&bloodActors[pSprite->index], pFXData->drag);
if (xvel[nSprite]) if (xvel[nSprite])
pSprite->x += xvel[nSprite]>>12; pSprite->x += xvel[nSprite]>>12;
if (yvel[nSprite]) if (yvel[nSprite])

View file

@ -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 if (TestBitString(sectbits, nNextSector)) // if we've already checked this sector, skip
continue; continue;
bool setSectBit = true; bool setSectBit = true;
bool withinRange = CheckProximityWall(pWall->point2, x, y, nDist); bool withinRange = false;
if (newSectCheckMethod && !withinRange) // if range check failed, try comparing midpoints/subdivides of wall span 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 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; const int nWallB = wall[nWallA].point2;
int x1 = wall[nWallA].x, y1 = wall[nWallA].y; int x1 = wall[nWallA].x, y1 = wall[nWallA].y;
int x2 = wall[nWallB].x, y2 = wall[nWallB].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); int nLength = approxDist(x1-x2, y1-y2);
const int nDist2 = (nDist+(nDist>>1))<<4; const int nDist4 = nDist<<4;
nLength = ClipRange(nLength / nDist2, 1, 4); // never split more than 4 times 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++) // subdivide span into smaller chunks towards direction 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 = (point1Dist < nDist4) || (point2Dist < nDist4); // check if both points of span is within radius
withinRange = CheckProximityPoint(xcenter, ycenter, 0, x, y, 0, nDist);
if (withinRange) if (withinRange)
break; break;
if (k == (nLength-1)) // reached end if (k == nLength) // reached end
break; break;
const bool bDir = approxDist(x-x1, y-y1) < approxDist(x-x2, y-y2); const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1;
if (bDir) // step closer and check again if (point1Dist < point2Dist) // shift closest side of wall towards target point, and refresh point distance values
{
x2 = xcenter, y2 = ycenter; x2 = xcenter, y2 = ycenter;
point2Dist = approxDist(x-x2, y-y2);
}
else else
{
x1 = xcenter, y1 = ycenter; 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 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) if (pSectBit)
SetBitString(pSectBit, nNextSector); SetBitString(pSectBit, nNextSector);
pSectors[n++] = nNextSector; pSectors[n++] = nNextSector;

View file

@ -120,7 +120,7 @@ static const char* DefFile(void)
for (int i = numlumps - 1; i >= 0; i--) for (int i = numlumps - 1; i >= 0; i--)
{ {
int fileno = fileSystem.GetFileContainer(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 fn = fileSystem.GetFileFullName(i, false);
FString ext = fn.Right(4); FString ext = fn.Right(4);
if (ext.CompareNoCase(".ini") == 0) if (ext.CompareNoCase(".ini") == 0)

View file

@ -423,6 +423,32 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, AISTATE*& w, AISTA
return arc; 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) FSerializer& Serialize(FSerializer& arc, const char* keyname, DBloodActor& w, DBloodActor* def)
{ {
static DBloodActor nul; 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. // The rest is only relevant if the actor has an xsprite.
if (w.s().extra > 0) if (w.s().extra > 0)
{ {
arc("dudeslope", w.dudeSlope, def->dudeSlope); arc("dudeslope", w.dudeSlope, def->dudeSlope)
("dudeextra", w.dudeExtra, def->dudeExtra);
} }
arc.EndObject(); arc.EndObject();
} }
@ -668,7 +695,6 @@ void SerializeSequences(FSerializer& arc);
void SerializeWarp(FSerializer& arc); void SerializeWarp(FSerializer& arc);
void SerializeTriggers(FSerializer& arc); void SerializeTriggers(FSerializer& arc);
void SerializeActor(FSerializer& arc); void SerializeActor(FSerializer& arc);
void SerializeAI(FSerializer& arc);
void SerializeGameStats(FSerializer& arc); void SerializeGameStats(FSerializer& arc);
void SerializePlayers(FSerializer& arc); void SerializePlayers(FSerializer& arc);
void SerializeView(FSerializer& arc); void SerializeView(FSerializer& arc);
@ -700,7 +726,6 @@ void GameInterface::SerializeGameState(FSerializer& arc)
SerializeState(arc); SerializeState(arc);
InitFreeList(nextXSprite, kMaxXSprites, activeXSprites); InitFreeList(nextXSprite, kMaxXSprites, activeXSprites);
SerializeActor(arc); SerializeActor(arc);
SerializeAI(arc);
SerializePlayers(arc); SerializePlayers(arc);
SerializeEvents(arc); SerializeEvents(arc);
SerializeGameStats(arc); SerializeGameStats(arc);

View file

@ -43,6 +43,7 @@ void HookReplaceFunctions();
struct PLAYER; struct PLAYER;
bool checkFired6or7(PLAYER *pPlayer);
void WeaponInit(void); void WeaponInit(void);
void WeaponDraw(PLAYER *pPlayer, int a2, double a3, double a4, int a5); void WeaponDraw(PLAYER *pPlayer, int a2, double a3, double a4, int a5);
void WeaponRaise(PLAYER *pPlayer); void WeaponRaise(PLAYER *pPlayer);

View file

@ -1286,7 +1286,7 @@ void nnExtProcessSuperSprites() {
} }
actAirDrag(pDebris, airVel); actAirDrag(&bloodActors[pDebris->index], airVel);
if (pXDebris->physAttr & kPhysDebrisTouch) { if (pXDebris->physAttr & kPhysDebrisTouch) {
PLAYER* pPlayer = NULL; PLAYER* pPlayer = NULL;
@ -1919,7 +1919,7 @@ void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer, bool checkCondition) {
for (unsigned k = 0; k < pCond->length; k++) { for (unsigned k = 0; k < pCond->length; k++) {
if (pCond->obj[k].type != OBJ_SPRITE || pCond->obj[k].index != pXSource->reference) continue; if (pCond->obj[k].type != OBJ_SPRITE || pCond->obj[k].index != pXSource->reference) continue;
pCond->obj[k].index = pPlayer->nSprite; pCond->obj[k].index = pPlayer->nSprite;
pCond->obj[k].cmd = pPlayer->pXSprite->command; pCond->obj[k].cmd = (uint8_t)pPlayer->pXSprite->command;
break; break;
} }
@ -2985,6 +2985,7 @@ void useSpriteDamager(XSPRITE* pXSource, int objType, int objIndex) {
} }
void damageSprites(XSPRITE* pXSource, spritetype* pSprite) { void damageSprites(XSPRITE* pXSource, spritetype* pSprite) {
auto actor = &bloodActors[pSprite->index];
spritetype* pSource = &sprite[pXSource->reference]; spritetype* pSource = &sprite[pXSource->reference];
if (!IsDudeSprite(pSprite) || !xspriRangeIsFine(pSprite->extra) || xsprite[pSprite->extra].health <= 0 || pXSource->data3 < 0) if (!IsDudeSprite(pSprite) || !xspriRangeIsFine(pSprite->extra) || xsprite[pSprite->extra].health <= 0 || pXSource->data3 < 0)
return; return;
@ -3078,7 +3079,7 @@ void damageSprites(XSPRITE* pXSource, spritetype* pSprite) {
if (forceRecoil && !pPlayer) { if (forceRecoil && !pPlayer) {
pXSprite->data3 = 32767; pXSprite->data3 = 32767;
gDudeExtra[pSprite->extra].recoil = (dmgType == kDmgElectric) ? 1 : 0; actor->dudeExtra.recoil = (dmgType == kDmgElectric) ? 1 : 0;
if (pXSprite->aiState->stateType != kAiStateRecoil) if (pXSprite->aiState->stateType != kAiStateRecoil)
RecoilDude(&bloodActors[pXSprite->reference]); RecoilDude(&bloodActors[pXSprite->reference]);
} }
@ -3569,8 +3570,8 @@ bool condCheckSector(XSPRITE* pXCond, int cmpOp, bool PUSH) {
SectIterator it(objIndex); SectIterator it(objIndex);
while ((nSprite = it.NextIndex()) >= 0) while ((nSprite = it.NextIndex()) >= 0)
{ {
if (!condCmp(sprite[var].type, arg1, arg2, cmpOp)) continue; if (!condCmp(sprite[nSprite].type, arg1, arg2, cmpOp)) continue;
else if (PUSH) condPush(pXCond, OBJ_SPRITE, var); else if (PUSH) condPush(pXCond, OBJ_SPRITE, nSprite);
return true; return true;
} }
return false; return false;
@ -5041,7 +5042,7 @@ bool modernTypeOperateSprite(int nSprite, spritetype* pSprite, XSPRITE* pXSprite
pXSprite->Proximity = 1; pXSprite->Proximity = 1;
break; break;
default: default:
actExplodeSprite(pSprite); actExplodeSprite(&bloodActors[pSprite->index]);
break; break;
} }
} }
@ -7911,6 +7912,34 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, SPRITEMASS& w, SPR
return arc; 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) void SerializeNNExts(FSerializer& arc)
{ {
if (arc.BeginObject("nnexts")) if (arc.BeginObject("nnexts"))
@ -7937,6 +7966,8 @@ void SerializeNNExts(FSerializer& arc)
("impactspritescount", gImpactSpritesCount) ("impactspritescount", gImpactSpritesCount)
.Array("impactspriteslist", gImpactSpritesList, gImpactSpritesCount) .Array("impactspriteslist", gImpactSpritesList, gImpactSpritesCount)
("eventredirects", gEventRedirectsUsed) ("eventredirects", gEventRedirectsUsed)
("trconditioncount", gTrackingCondsCount)
.Array("trcondition", gCondition, gTrackingCondsCount)
.EndObject(); .EndObject();
} }
} }

View file

@ -235,14 +235,14 @@ struct TRPLAYERCTRL { // this one for controlling the player using triggers (mov
}; };
struct OBJECTS_TO_TRACK { struct OBJECTS_TO_TRACK {
signed int type: 3; int8_t type;
unsigned int index: 16; uint8_t cmd;
unsigned int cmd: 8; int index;
}; };
struct TRCONDITION { struct TRCONDITION {
signed int xindex: 16; signed int xindex;
unsigned int length: 8; unsigned int length;
OBJECTS_TO_TRACK obj[kMaxTracedObjects]; OBJECTS_TO_TRACK obj[kMaxTracedObjects];
}; };

View file

@ -660,7 +660,8 @@ void playerStart(int nPlayer, int bNewLevel)
pStartZone = &gStartZone[Random(8)]; 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); assert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
XSPRITE *pXSprite = &xsprite[pSprite->extra]; XSPRITE *pXSprite = &xsprite[pSprite->extra];
pPlayer->pSprite = pSprite; pPlayer->pSprite = pSprite;
@ -977,20 +978,56 @@ char PickupItem(PLAYER *pPlayer, spritetype *pItem) {
} }
} }
return 0; return 0;
case kItemFlagA: case kItemFlagA: {
if (gGameOptions.nGameType != 3) return 0; if (gGameOptions.nGameType != 3) return 0;
evKill(pItem->index, 3, kCallbackReturnFlag);
pPlayer->hasFlag |= 1;
pPlayer->used2[0] = pItem->index;
gBlueFlagDropped = false; 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; break;
case kItemFlagB: }
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: {
if (gGameOptions.nGameType != 3) return 0; if (gGameOptions.nGameType != 3) return 0;
evKill(pItem->index, 3, kCallbackReturnFlag);
pPlayer->hasFlag |= 2;
pPlayer->used2[1] = pItem->index;
gRedFlagDropped = false; 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; 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 kItemArmorBasic:
case kItemArmorBody: case kItemArmorBody:
case kItemArmorFire: case kItemArmorFire:

View file

@ -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<int, TArray<int>> 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<FString, QAVPrevTileFinder> qavPrevTileFinders; static TMap<FString, QAVPrevTileFinder> qavPrevTileFinders;
@ -79,7 +90,7 @@ static void qavInitTileFinderMap()
}); });
} }
QAVPrevTileFinder qavGetInterpType(const FString& type) static QAVPrevTileFinder qavGetInterpType(const FString& type)
{ {
if (!qavPrevTileFinders.CountUsed()) qavInitTileFinderMap(); if (!qavPrevTileFinders.CountUsed()) qavInitTileFinderMap();
return *qavPrevTileFinders.CheckKey(type); 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<int, TArray<int>>& ignoredata) void GameInterface::AddQAVInterpProps(const int& res_id, const FString& interptype, const bool& loopable, const TMap<int, TArray<int>>& 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) 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) if (!to3dview)
{ {
auto tex = tileGetTexture(pTile->picnum); auto tex = tileGetTexture(picnum);
double scale = z * (1. / 65536.); double scale = z * (1. / 65536.);
double angle = a * BAngToDegree; double angle = a * BAngToDegree;
int renderstyle = (stat & RS_NOMASK)? STYLE_Normal : STYLE_Translucent; 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; int pin = (stat & kQavOrientationLeft)? -1 : (stat & RS_ALIGN_R)? 1:0;
auto translation = TRANSLATION(Translation_Remap, palnum); auto translation = TRANSLATION(Translation_Remap, palnum);
bool topleft = !!(stat & RS_TOPLEFT); bool topleft = !!(stat & RS_TOPLEFT);
bool xflip = !!(stat & 0x100); // repurposed flag bool xflip = !!(stat & 0x100); // repurposed flag
bool yflip = !!(stat & RS_YFLIP); 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, 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, 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; if ((stat & kQavOrientationLeft)) stat |= RS_ALIGN_L;
stat &= ~kQavOrientationLeft; 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) void QAV::Draw(double x, double y, int ticks, int stat, int shade, int palnum, bool to3dview, double const smoothratio)
{ {
assert(ticksPerFrame > 0); 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); auto const nFrame = clamp(ticks / ticksPerFrame, 0, nFrames - 1);
FRAMEINFO* const thisFrame = &frames[nFrame]; 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]; FRAMEINFO* const prevFrame = &frames[oFrame];
bool const interpolate = interpdata && cl_hudinterpolation && cl_bloodqavinterp && (nFrames > 1) && (nFrame != oFrame) && (smoothratio != MaxSmoothRatio); 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 tileY = y;
double tileZ; double tileZ;
double tileA; double tileA;
double tileAlpha;
int tileShade;
auto const tileStat = stat | thisTile->stat;
if (prevTile) 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); tileY += interpolatedvaluef(prevTile->y, thisTile->y, smoothratio);
tileZ = interpolatedvaluef(prevTile->z, thisTile->z, smoothratio); tileZ = interpolatedvaluef(prevTile->z, thisTile->z, smoothratio);
tileA = interpolatedangle(buildang(prevTile->angle), buildang(thisTile->angle), smoothratio).asbuildf(); 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 else
{ {
@ -178,9 +196,11 @@ void QAV::Draw(double x, double y, int ticks, int stat, int shade, int palnum, b
tileY += thisTile->y; tileY += thisTile->y;
tileZ = thisTile->z; tileZ = thisTile->z;
tileA = thisTile->angle; 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); *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. // Process if not paused.
if (!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. // Process clock based on QAV's ticrate and last tick value.
qavProcessTicker(pQAV, &pPlayer->qavTimer, &pPlayer->qavLastTick); 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. // Check if we're playing an idle QAV as per the ticker's weapon timer.
*duration = fixedduration ? pQAV->duration - 1 : I_GetBuildTime() % pQAV->duration; *duration = fixedduration ? pQAV->duration - 1 : I_GetBuildTime() % pQAV->duration;

View file

@ -238,25 +238,9 @@ struct QAV
void Precache(int palette = 0); 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<int, TArray<int>> 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); QAV* getQAV(int res_id);
void qavProcessTicker(QAV* const pQAV, int* duration, int* lastTick); 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) inline bool qavIsOriginal(const int& res_id)
{ {

View file

@ -482,6 +482,11 @@ SEQINST* GetInstance(DBloodActor* actor)
return activeList.get(3, actor->s().index); return activeList.get(3, actor->s().index);
} }
int seqGetStatus(DBloodActor* actor)
{
return seqGetStatus(3, actor->s().index);
}
void seqKill(int type, int nXIndex) void seqKill(int type, int nXIndex)
{ {
activeList.remove(type, nXIndex); activeList.remove(type, nXIndex);

View file

@ -107,6 +107,7 @@ void seqKill(int a1, int a2);
void seqKill(DBloodActor* actor); void seqKill(DBloodActor* actor);
void seqKillAll(void); void seqKillAll(void);
int seqGetStatus(int a1, int a2); int seqGetStatus(int a1, int a2);
int seqGetStatus(DBloodActor*);
int seqGetID(int a1, int a2); int seqGetID(int a1, int a2);
void seqProcess(int a1); void seqProcess(int a1);

View file

@ -169,14 +169,7 @@ void sfxPlay3DSound(int x, int y, int z, int soundId, int nSector)
if (chan) chan->UserData = nSector; if (chan) chan->UserData = nSector;
} }
enum EPlayFlags void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int playchannel, int playflags, int pitch, int volume)
{
FX_GlobalChannel = 1,
FX_SoundMatch = 2,
FX_ChannelMatch = 4,
};
void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int a3, int a4, int pitch, int volume)
{ {
if (!SoundEnabled() || soundId < 0 || !pSprite) return; if (!SoundEnabled() || soundId < 0 || !pSprite) return;
auto sid = soundEngine->FindSoundByResID(soundId); 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); sid = getSfx(sid, attenuation, pitch, volume);
if (volume == -1) volume = 80; 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 (soundEngine->EnumerateChannels([=](FSoundChan* chan) -> int
{ {
if (chan->SourceType != SOURCE_Actor) return false; // other source types are not our business. 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; return true;
if ((a4 & FX_SoundMatch) != 0 && chan->OrgID == sid) if ((playflags & FX_SoundMatch) != 0 && chan->OrgID == sid)
return true; return true;
soundEngine->StopChannel(chan); soundEngine->StopChannel(chan);
return -1; return -1;
@ -209,10 +202,10 @@ void sfxPlay3DSoundCP(spritetype* pSprite, int soundId, int a3, int a4, int pitc
} }
auto sfx = soundEngine->GetSfx(sid); 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; 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) void sfxPlay3DSound(spritetype* pSprite, int soundId, int a3, int a4)

View file

@ -62,4 +62,12 @@ void ambProcess(void);
void ambKillAll(void); void ambKillAll(void);
void ambInit(void); void ambInit(void);
enum EPlayFlags
{
FX_GlobalChannel = 1,
FX_SoundMatch = 2,
FX_ChannelMatch = 4,
};
END_BLD_NS END_BLD_NS

View file

@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "compat.h" #include "compat.h"
#include "blood.h" #include "blood.h"
#include "misc.h"
#include "d_net.h" #include "d_net.h"
BEGIN_BLD_NS BEGIN_BLD_NS
@ -206,14 +207,16 @@ void LifeLeechOperate(spritetype *pSprite, XSPRITE *pXSprite, EVENT event)
PLAYER *pPlayer = &gPlayer[nPlayer]; PLAYER *pPlayer = &gPlayer[nPlayer];
if (pPlayer->pXSprite->health > 0) if (pPlayer->pXSprite->health > 0)
{ {
evKill(pSprite->index, 3);
pPlayer->ammoCount[8] = ClipHigh(pPlayer->ammoCount[8]+pXSprite->data3, gAmmoInfo[8].max); pPlayer->ammoCount[8] = ClipHigh(pPlayer->ammoCount[8]+pXSprite->data3, gAmmoInfo[8].max);
pPlayer->hasWeapon[9] = 1; pPlayer->hasWeapon[9] = 1;
if (pPlayer->curWeapon != kWeapLifeLeech) if (pPlayer->curWeapon != kWeapLifeLeech)
{ {
if (!VanillaMode() && checkFired6or7(pPlayer)) // if tnt/spray is actively used, do not switch weapon
break;
pPlayer->weaponState = 0; pPlayer->weaponState = 0;
pPlayer->nextWeapon = 9; pPlayer->nextWeapon = 9;
} }
evKill(pSprite->index, 3);
} }
} }
break; break;
@ -495,7 +498,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
case kThingArmedTNTStick: case kThingArmedTNTStick:
case kThingArmedTNTBundle: case kThingArmedTNTBundle:
case kThingArmedSpray: case kThingArmedSpray:
actExplodeSprite(pSprite); actExplodeSprite(&bloodActors[pSprite->index]);
break; break;
case kTrapExploder: case kTrapExploder:
switch (event.cmd) { switch (event.cmd) {
@ -504,13 +507,13 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
break; break;
default: default:
pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE; pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE;
actExplodeSprite(pSprite); actExplodeSprite(&bloodActors[pSprite->index]);
break; break;
} }
break; break;
case kThingArmedRemoteBomb: case kThingArmedRemoteBomb:
if (pSprite->statnum != kStatRespawn) { if (pSprite->statnum != kStatRespawn) {
if (event.cmd != kCmdOn) actExplodeSprite(pSprite); if (event.cmd != kCmdOn) actExplodeSprite(&bloodActors[pSprite->index]);
else { else {
sfxPlay3DSound(pSprite, 454, 0, 0); sfxPlay3DSound(pSprite, 454, 0, 0);
evPost(nSprite, 3, 18, kCmdOff); evPost(nSprite, 3, 18, kCmdOff);
@ -531,7 +534,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
pXSprite->Proximity = 1; pXSprite->Proximity = 1;
break; break;
default: default:
actExplodeSprite(pSprite); actExplodeSprite(&bloodActors[pSprite->index]);
break; break;
} }
} }

View file

@ -359,43 +359,23 @@ void viewUpdateDelirium(void)
void viewUpdateShake(int& cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, double& pshakeX, double& pshakeY) void viewUpdateShake(int& cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, double& pshakeX, double& pshakeY)
{ {
int shakeHoriz = 0; auto doEffect = [&](const int& effectType)
int shakeAngle = 0;
int shakeX = 0;
int shakeY = 0;
int shakeZ = 0;
int shakeBobX = 0;
int shakeBobY = 0;
if (gView->flickerEffect)
{ {
int nValue = ClipHigh(gView->flickerEffect * 8, 2000); if (effectType)
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); int nValue = ClipHigh(effectType * 8, 2000);
shakeHoriz += QRandom2(nValue >> 8); cH += buildfhoriz(QRandom2F(nValue * (1. / 256.)));
shakeAngle += QRandom2(nValue >> 8); cA += buildfang(QRandom2F(nValue * (1. / 256.)));
shakeX += QRandom2(nValue >> 4); cX += QRandom2(nValue >> 4);
shakeY += QRandom2(nValue >> 4); cY += QRandom2(nValue >> 4);
shakeZ += QRandom2(nValue); cZ += QRandom2(nValue);
shakeBobX += QRandom2(nValue); pshakeX += QRandom2F(nValue);
shakeBobY += QRandom2(nValue); pshakeY += QRandom2F(nValue);
} }
cH += buildhoriz(shakeHoriz); };
cA += buildang(shakeAngle);
cX += shakeX;
cY += shakeY;
cZ += shakeZ;
pshakeX += shakeBobX;
pshakeY += shakeBobY;
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); 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 (gViewPos == 0)
{ {
if (cl_viewhbob) if (cl_viewhbob)
@ -495,7 +475,7 @@ void SetupView(int &cX, int& cY, int& cZ, binangle& cA, fixedhoriz& cH, int& nSe
{ {
cZ += bobHeight; cZ += bobHeight;
} }
cZ += xs_CRoundToInt(cH.asq16() / 6553.6); cZ += xs_CRoundToInt(cH.asq16() * (1. / 6553.6));
cameradist = -1; cameradist = -1;
cameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16); cameraclock = PlayClock + MulScale(4, (int)gInterpolate, 16);
} }

View file

@ -177,4 +177,6 @@ inline void viewBackupSpriteLoc(int nSprite, spritetype *pSprite)
} }
} }
void viewBackupSpriteLoc(DBloodActor* actor);
END_BLD_NS END_BLD_NS

View file

@ -140,7 +140,7 @@ enum
nClientAltFireNapalm, nClientAltFireNapalm,
}; };
static bool checkFired6or7(PLAYER *pPlayer) bool checkFired6or7(PLAYER *pPlayer)
{ {
switch (pPlayer->curWeapon) switch (pPlayer->curWeapon)
{ {
@ -228,23 +228,35 @@ void SpawnShellEject(PLAYER *pPlayer, int a2, int a3)
void WeaponInit(void) void WeaponInit(void)
{ {
for (int i = 0; i < kQAVEnd; i++) auto doInit = [](const int base)
{
for (int i = base; i < (kQAVEnd + base); i++)
{ {
auto pQAV = getQAV(i); auto pQAV = getQAV(i);
if (!pQAV) if (!pQAV)
I_Error("Could not load QAV %d\n", i); I_Error("Could not load QAV %d\n", i);
pQAV->nSprite = -1; pQAV->nSprite = -1;
} }
};
doInit(0);
doInit(10000);
} }
void WeaponPrecache() void WeaponPrecache()
{ {
for (int i = 0; i < kQAVEnd; i++) auto doPrecache = [](const int base)
{
for (int i = base; i < (kQAVEnd + base); i++)
{ {
auto pQAV = getQAV(i); auto pQAV = getQAV(i);
if (pQAV) if (pQAV)
pQAV->Precache(); pQAV->Precache();
} }
};
doPrecache(0);
doPrecache(10000);
} }
void WeaponDraw(PLAYER *pPlayer, int shade, double xpos, double ypos, int palnum) 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; int duration;
double smoothratio; 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->x = int(xpos);
pQAV->y = int(ypos); pQAV->y = int(ypos);
@ -297,6 +309,14 @@ static void StartQAV(PLAYER *pPlayer, int nWeaponQAV, int callback = -1, bool lo
pPlayer->weaponTimer -= 4; 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 struct WEAPONTRACK
{ {
int aimSpeedHorz; int aimSpeedHorz;
@ -833,7 +853,7 @@ void WeaponUpdateState(PLAYER *pPlayer)
switch (lastWeapon) switch (lastWeapon)
{ {
case kWeapPitchFork: case kWeapPitchFork:
pPlayer->weaponQav = qavGetCorrectID(kQAVFORKIDLE); SetQAV(pPlayer, kQAVFORKIDLE);
break; break;
case kWeapSpraycan: case kWeapSpraycan:
switch (vb) switch (vb)
@ -849,15 +869,15 @@ void WeaponUpdateState(PLAYER *pPlayer)
StartQAV(pPlayer, kQAVCANPREF); StartQAV(pPlayer, kQAVCANPREF);
} }
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVLITEIDLE); SetQAV(pPlayer, kQAVLITEIDLE);
break; break;
case 3: case 3:
pPlayer->weaponQav = qavGetCorrectID(kQAVCANIDLE); SetQAV(pPlayer, kQAVCANIDLE);
break; break;
case 4: case 4:
if (CheckAmmo(pPlayer, 6, 1)) if (CheckAmmo(pPlayer, 6, 1))
{ {
pPlayer->weaponQav = qavGetCorrectID(kQAVCANIDLE); SetQAV(pPlayer, kQAVCANIDLE);
pPlayer->weaponState = 3; pPlayer->weaponState = 3;
} }
else else
@ -890,10 +910,10 @@ void WeaponUpdateState(PLAYER *pPlayer)
StartQAV(pPlayer, kQAVBUNUP); StartQAV(pPlayer, kQAVBUNUP);
} }
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVLITEIDLE); SetQAV(pPlayer, kQAVLITEIDLE);
break; break;
case 3: case 3:
pPlayer->weaponQav = qavGetCorrectID(kQAVBUNIDLE); SetQAV(pPlayer, kQAVBUNIDLE);
break; break;
} }
break; break;
@ -901,7 +921,7 @@ void WeaponUpdateState(PLAYER *pPlayer)
switch (vb) switch (vb)
{ {
case 7: case 7:
pPlayer->weaponQav = qavGetCorrectID(kQAVPROXIDLE); SetQAV(pPlayer, kQAVPROXIDLE);
break; break;
case 8: case 8:
pPlayer->weaponState = 7; pPlayer->weaponState = 7;
@ -913,10 +933,10 @@ void WeaponUpdateState(PLAYER *pPlayer)
switch (vb) switch (vb)
{ {
case 10: case 10:
pPlayer->weaponQav = qavGetCorrectID(kQAVREMIDLE1); SetQAV(pPlayer, kQAVREMIDLE1);
break; break;
case 11: case 11:
pPlayer->weaponQav = qavGetCorrectID(kQAVREMIDLE2); SetQAV(pPlayer, kQAVREMIDLE2);
break; break;
case 12: case 12:
if (pPlayer->ammoCount[11] > 0) if (pPlayer->ammoCount[11] > 0)
@ -939,7 +959,7 @@ void WeaponUpdateState(PLAYER *pPlayer)
pPlayer->weaponState = 1; pPlayer->weaponState = 1;
break; break;
case 7: case 7:
pPlayer->weaponQav = qavGetCorrectID(kQAV2SHOTI); SetQAV(pPlayer, kQAV2SHOTI);
break; break;
case 1: case 1:
if (CheckAmmo(pPlayer, 2, 1)) if (CheckAmmo(pPlayer, 2, 1))
@ -952,25 +972,25 @@ void WeaponUpdateState(PLAYER *pPlayer)
pPlayer->weaponState = 2; pPlayer->weaponState = 2;
} }
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVSHOTI3); SetQAV(pPlayer, kQAVSHOTI3);
break; break;
case 2: case 2:
pPlayer->weaponQav = qavGetCorrectID(kQAVSHOTI2); SetQAV(pPlayer, kQAVSHOTI2);
break; break;
case 3: case 3:
pPlayer->weaponQav = qavGetCorrectID(kQAVSHOTI1); SetQAV(pPlayer, kQAVSHOTI1);
break; break;
} }
break; break;
case kWeapTommyGun: case kWeapTommyGun:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2)) if (powerupCheck(pPlayer, kPwUpTwoGuns) && checkAmmo2(pPlayer, 3, 2))
{ {
pPlayer->weaponQav = qavGetCorrectID(kQAV2TOMIDLE); SetQAV(pPlayer, kQAV2TOMIDLE);
pPlayer->weaponState = 1; pPlayer->weaponState = 1;
} }
else else
{ {
pPlayer->weaponQav = qavGetCorrectID(kQAVTOMIDLE); SetQAV(pPlayer, kQAVTOMIDLE);
pPlayer->weaponState = 0; pPlayer->weaponState = 0;
} }
break; break;
@ -978,33 +998,33 @@ void WeaponUpdateState(PLAYER *pPlayer)
if (powerupCheck(pPlayer, kPwUpTwoGuns)) if (powerupCheck(pPlayer, kPwUpTwoGuns))
{ {
if (vb == 3 && checkAmmo2(pPlayer, 1, 2)) if (vb == 3 && checkAmmo2(pPlayer, 1, 2))
pPlayer->weaponQav = qavGetCorrectID(kQAVFLAR2I); SetQAV(pPlayer, kQAVFLAR2I);
else else
{ {
pPlayer->weaponQav = qavGetCorrectID(kQAVFLARIDLE); SetQAV(pPlayer, kQAVFLARIDLE);
pPlayer->weaponState = 2; pPlayer->weaponState = 2;
} }
} }
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVFLARIDLE); SetQAV(pPlayer, kQAVFLARIDLE);
break; break;
case kWeapVoodooDoll: case kWeapVoodooDoll:
if (pXSprite->height < 256 && pPlayer->swayHeight != 0) if (pXSprite->height < 256 && pPlayer->swayHeight != 0)
StartQAV(pPlayer, kQAVVDIDLE2); StartQAV(pPlayer, kQAVVDIDLE2);
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVVDIDLE1); SetQAV(pPlayer, kQAVVDIDLE1);
break; break;
case kWeapTeslaCannon: case kWeapTeslaCannon:
switch (vb) switch (vb)
{ {
case 2: case 2:
if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns)) if (checkAmmo2(pPlayer, 7, 10) && powerupCheck(pPlayer, kPwUpTwoGuns))
pPlayer->weaponQav = qavGetCorrectID(kQAV2SGUNIDL); SetQAV(pPlayer, kQAV2SGUNIDL);
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVSGUNIDL1); SetQAV(pPlayer, kQAVSGUNIDL1);
break; break;
case 3: case 3:
pPlayer->weaponQav = qavGetCorrectID(kQAVSGUNIDL2); SetQAV(pPlayer, kQAVSGUNIDL2);
break; break;
} }
break; break;
@ -1013,12 +1033,12 @@ void WeaponUpdateState(PLAYER *pPlayer)
{ {
case 3: case 3:
if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer,4, 4))) if (powerupCheck(pPlayer, kPwUpTwoGuns) && (gInfiniteAmmo || CheckAmmo(pPlayer,4, 4)))
pPlayer->weaponQav = qavGetCorrectID(kQAV2NAPIDLE); SetQAV(pPlayer, kQAV2NAPIDLE);
else else
pPlayer->weaponQav = qavGetCorrectID(kQAVNAPIDLE); SetQAV(pPlayer, kQAVNAPIDLE);
break; break;
case 2: case 2:
pPlayer->weaponQav = qavGetCorrectID(kQAVNAPIDLE); SetQAV(pPlayer, kQAVNAPIDLE);
break; break;
} }
break; break;
@ -1026,12 +1046,12 @@ void WeaponUpdateState(PLAYER *pPlayer)
switch (vb) switch (vb)
{ {
case 2: case 2:
pPlayer->weaponQav = qavGetCorrectID(kQAVSTAFIDL1); SetQAV(pPlayer, kQAVSTAFIDL1);
break; break;
} }
break; break;
case kWeapBeast: case kWeapBeast:
pPlayer->weaponQav = qavGetCorrectID(kQAVBSTIDLE); SetQAV(pPlayer, kQAVBSTIDLE);
break; break;
} }
} }
@ -1586,7 +1606,7 @@ void FireTesla(int nTrigger, PLAYER *pPlayer)
if (!checkAmmo2(pPlayer, 7, pMissile->ammouse)) if (!checkAmmo2(pPlayer, 7, pMissile->ammouse))
{ {
pPlayer->weaponState = -1; pPlayer->weaponState = -1;
pPlayer->weaponQav = qavGetCorrectID(kQAVSGUNIDL2); SetQAV(pPlayer, kQAVSGUNIDL2);
pPlayer->flashEffect = 0; pPlayer->flashEffect = 0;
return; return;
} }
@ -2029,7 +2049,8 @@ void WeaponProcess(PLAYER *pPlayer) {
pPlayer->weaponTimer -= 4; pPlayer->weaponTimer -= 4;
bool bShoot = pPlayer->input.actions & SB_FIRE; bool bShoot = pPlayer->input.actions & SB_FIRE;
bool bShoot2 = pPlayer->input.actions & SB_ALTFIRE; 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 (pPlayer->qavLoop && pPlayer->pXSprite->health > 0)
{ {
if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1)) if (bShoot && CheckAmmo(pPlayer, pPlayer->weaponAmmo, 1))
@ -2084,7 +2105,6 @@ void WeaponProcess(PLAYER *pPlayer) {
return; return;
break; break;
} }
const int prevNewWeaponVal = pPlayer->input.getNewWeapon(); // used to fix scroll issue for banned weapons
if (VanillaMode()) if (VanillaMode())
{ {
if (pPlayer->nextWeapon) if (pPlayer->nextWeapon)
@ -2355,7 +2375,7 @@ void WeaponProcess(PLAYER *pPlayer) {
switch (pPlayer->weaponState) switch (pPlayer->weaponState)
{ {
case 7: case 7:
pPlayer->weaponQav = qavGetCorrectID(kQAVPROXIDLE); SetQAV(pPlayer, kQAVPROXIDLE);
pPlayer->weaponState = 9; pPlayer->weaponState = 9;
pPlayer->throwTime = PlayClock; pPlayer->throwTime = PlayClock;
return; return;
@ -2365,7 +2385,7 @@ void WeaponProcess(PLAYER *pPlayer) {
switch (pPlayer->weaponState) switch (pPlayer->weaponState)
{ {
case 10: case 10:
pPlayer->weaponQav = qavGetCorrectID(kQAVREMIDLE1); SetQAV(pPlayer, kQAVREMIDLE1);
pPlayer->weaponState = 13; pPlayer->weaponState = 13;
pPlayer->throwTime = PlayClock; pPlayer->throwTime = PlayClock;
return; return;

View file

@ -509,7 +509,7 @@ void LoadCustomInfoFromScript(const char *filename)
curep = sc.Number; curep = sc.Number;
if (sc.ParseError) curep = -1; 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); sc.ScriptMessage("Episode number %d not in range 1-2\n", curep + 1);
curep = -1; curep = -1;
@ -808,8 +808,8 @@ void LoadCustomInfoFromScript(const char *filename)
break; break;
} }
} }
auto vol0 = MustFindVolume(0); auto vol0 = MustFindVolume(1);
auto vol1 = MustFindVolume(1); auto vol1 = MustFindVolume(2);
auto map1 = FindMapByLevelNum(1); auto map1 = FindMapByLevelNum(1);
auto map5 = FindMapByLevelNum(5); auto map5 = FindMapByLevelNum(5);
if (vol0 && map1) vol0->startmap = map1->labelName; if (vol0 && map1) vol0->startmap = map1->labelName;

View file

@ -376,6 +376,7 @@ grpinfo
{ {
name "Duke: Alien World Order" name "Duke: Alien World Order"
dependency DUKE15_CRC dependency DUKE15_CRC
flags GAMEFLAG_DUKE|GAMEFLAG_ADDON
mustcontain "FIREFLYTROOPER.CON", "FLAMETHROWER.CON", "music/E5L1_BulletDam.ogg", "sound/VO_E5L1_Duke_CreamAndSugar.ogg" mustcontain "FIREFLYTROOPER.CON", "FLAMETHROWER.CON", "music/E5L1_BulletDam.ogg", "sound/VO_E5L1_Duke_CreamAndSugar.ogg"
gamefilter "Duke.Worldtour" gamefilter "Duke.Worldtour"
GameID "DukeWorldTour" GameID "DukeWorldTour"
@ -462,22 +463,6 @@ grpinfo
GameID "Blood" 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 grpinfo
{ {
// This is for identifying older Blood versions. Since I have no information, all I can do is testing for a few known files. // This is for identifying older Blood versions. Since I have no information, all I can do is testing for a few known files.

View file

@ -662,45 +662,24 @@ defineqav 10092 {
} }
defineqav 10093 { defineqav 10093 {
file "qavs/BSTUP.QAV" file "qavs/BSTUP.QAV"
interpolate {
type "index"
}
} }
defineqav 10094 { defineqav 10094 {
file "qavs/BSTIDLE.QAV" file "qavs/BSTIDLE.QAV"
interpolate {
type "index"
}
} }
defineqav 10095 { defineqav 10095 {
file "qavs/BSTATAK1.QAV" file "qavs/BSTATAK1.QAV"
interpolate {
type "index"
}
} }
defineqav 10096 { defineqav 10096 {
file "qavs/BSTATAK2.QAV" file "qavs/BSTATAK2.QAV"
interpolate {
type "index"
}
} }
defineqav 10097 { defineqav 10097 {
file "qavs/BSTATAK3.QAV" file "qavs/BSTATAK3.QAV"
interpolate {
type "index"
}
} }
defineqav 10098 { defineqav 10098 {
file "qavs/BSTATAK4.QAV" file "qavs/BSTATAK4.QAV"
interpolate {
type "index"
}
} }
defineqav 10099 { defineqav 10099 {
file "qavs/BSTDOWN.QAV" file "qavs/BSTDOWN.QAV"
interpolate {
type "index"
}
} }
defineqav 10100 { defineqav 10100 {
file "qavs/VDUP.QAV" file "qavs/VDUP.QAV"

View file

@ -63,7 +63,6 @@ map { 2, 2 }
{ {
interbackground = "LEVELMAP09" interbackground = "LEVELMAP09"
rr_startsound = 176 rr_startsound = 176
clearweapons
} }
map { 2, 3 } map { 2, 3 }

View file

@ -260,7 +260,7 @@ class ListMenu : Menu
{ {
for(int i=0;i<mDesc.mItems.Size(); i++) for(int i=0;i<mDesc.mItems.Size(); i++)
{ {
if (mDesc.mItems[i].CheckCoordinate(x, y)) if (mDesc.mItems[i].Selectable() && mDesc.mItems[i].CheckCoordinate(x, y))
{ {
if (i != mDesc.mSelectedItem) if (i != mDesc.mSelectedItem)
{ {