Merge pull request #512 from carnivoroussociety/master

[Blood] Add vanilla enemies cvar
This commit is contained in:
Mitchell Richters 2021-08-23 11:51:50 +10:00 committed by GitHub
commit 252a2387af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 105 additions and 74 deletions

View file

@ -84,7 +84,8 @@ CVARD(Bool, cl_exhumedoldturn, false, CVAR_ARCHIVE, "enable/disable legacy turni
CVARD(Bool, cl_hudinterpolation, true, CVAR_ARCHIVE, "enable/disable HUD (weapon drawer) interpolation") CVARD(Bool, cl_hudinterpolation, true, CVAR_ARCHIVE, "enable/disable HUD (weapon drawer) interpolation")
CVARD(Bool, cl_bloodvanillarun, true, CVAR_ARCHIVE, "enable/disable Blood's vanilla run mode") CVARD(Bool, cl_bloodvanillarun, true, CVAR_ARCHIVE, "enable/disable Blood's vanilla run mode")
CVARD(Bool, cl_bloodvanillabobbing, true, CVAR_ARCHIVE, "enable/disable Blood's vanilla bobbing while not using vanilla run mode") CVARD(Bool, cl_bloodvanillabobbing, true, CVAR_ARCHIVE, "enable/disable Blood's vanilla bobbing while not using vanilla run mode")
CVARD(Bool, cl_bloodvanillaexplosions, false, CVAR_ARCHIVE, "enable/disable Blood's original 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_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_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")

View file

@ -30,6 +30,7 @@ EXTERN_CVAR(Bool, cl_hudinterpolation)
EXTERN_CVAR(Bool, cl_bloodvanillarun) EXTERN_CVAR(Bool, cl_bloodvanillarun)
EXTERN_CVAR(Bool, cl_bloodvanillabobbing) EXTERN_CVAR(Bool, cl_bloodvanillabobbing)
EXTERN_CVAR(Bool, cl_bloodvanillaexplosions) EXTERN_CVAR(Bool, cl_bloodvanillaexplosions)
EXTERN_CVAR(Bool, cl_bloodvanillaenemies)
EXTERN_CVAR(Bool, cl_bloodqavinterp) EXTERN_CVAR(Bool, cl_bloodqavinterp)
EXTERN_CVAR(Bool, cl_bloodweapinterp) EXTERN_CVAR(Bool, cl_bloodweapinterp)
EXTERN_CVAR(Bool, cl_bloodoldweapbalance) EXTERN_CVAR(Bool, cl_bloodoldweapbalance)

View file

@ -2540,7 +2540,7 @@ void actInit(bool bSaveLoad)
{ {
if (act->s().type == kItemWeaponVoodooDoll) if (act->s().type == kItemWeaponVoodooDoll)
{ {
act->s().type = kAmmoItemVoodooDoll; act->s().type = kItemAmmoVoodooDoll;
break; break;
} }
} }
@ -2661,7 +2661,7 @@ void actRadiusDamage(DBloodActor* source, int x, int y, int z, int nSector, int
{ {
uint8_t sectmap[(kMaxSectors + 7) >> 3]; uint8_t sectmap[(kMaxSectors + 7) >> 3];
auto pOwner = source->GetOwner(); auto pOwner = source->GetOwner();
const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode() && !DemoRecordStatus(); // use new sector checking logic const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, nDist, sectmap, nullptr, newSectCheckMethod); GetClosestSpriteSectors(nSector, x, y, nDist, sectmap, nullptr, newSectCheckMethod);
nDist <<= 4; nDist <<= 4;
if (flags & 2) if (flags & 2)
@ -3092,7 +3092,7 @@ static bool actKillDudeStage1(DBloodActor* actor, DAMAGE_TYPE damageType)
break; break;
case kDudeTinyCaleb: case kDudeTinyCaleb:
if (VanillaMode() || DemoRecordStatus()) if (cl_bloodvanillaenemies || VanillaMode())
break; break;
if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal) if (damageType == kDamageBurn && pXSprite->medium == kMediumNormal)
{ {
@ -3936,7 +3936,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode)
actBurnSprite(missileActor->GetOwner(), actorHit, 360); actBurnSprite(missileActor->GetOwner(), actorHit, 360);
// by NoOne: make Life Leech heal user, just like it was in 1.0x versions // by NoOne: make Life Leech heal user, just like it was in 1.0x versions
if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus() && pDudeInfo != nullptr) if (gGameOptions.weaponsV10x && !VanillaMode() && pDudeInfo != nullptr)
{ {
if (missileOwner->IsDudeActor() && missileOwner->hasX() && missileOwner->x().health != 0) if (missileOwner->IsDudeActor() && missileOwner->hasX() && missileOwner->x().health != 0)
actHealDude(missileOwner, nDamage >> 2, getDudeInfo(missileOwner->s().type)->startHealth); actHealDude(missileOwner, nDamage >> 2, getDudeInfo(missileOwner->s().type)->startHealth);
@ -4012,7 +4012,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode)
break; break;
case kMissileFireball: case kMissileFireball:
case kMissileFireballNapam: case kMissileFireballNapalm:
if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo)) if (hitCode == 3 && pSpriteHit && (pThingInfo || pDudeInfo))
{ {
if (pThingInfo && pSpriteHit->type == kThingTNTBarrel && actorHit->x().burnTime == 0) if (pThingInfo && pSpriteHit->type == kThingTNTBarrel && actorHit->x().burnTime == 0)
@ -4042,7 +4042,7 @@ static void actImpactMissile(DBloodActor* missileActor, int hitCode)
actRadiusDamage(missileOwner, pMissile->x, pMissile->y, pMissile->z, pMissile->sectnum, 16, 20, 10, kDamageBullet, 6, 480); actRadiusDamage(missileOwner, pMissile->x, pMissile->y, pMissile->z, pMissile->sectnum, 16, 20, 10, kDamageBullet, 6, 480);
// by NoOne: allow additional bullet damage for Flare Gun // by NoOne: allow additional bullet damage for Flare Gun
if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus()) if (gGameOptions.weaponsV10x && !VanillaMode())
{ {
int nDamage = (20 + Random(10)) << 4; int nDamage = (20 + Random(10)) << 4;
actDamageSprite(missileOwner, actorHit, kDamageBullet, nDamage); actDamageSprite(missileOwner, actorHit, kDamageBullet, nDamage);
@ -4646,11 +4646,15 @@ int MoveThing(spritetype *pSprite)
assert(nSector >= 0 && nSector < kMaxSectors); assert(nSector >= 0 && nSector < kMaxSectors);
int top, bottom; int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom); GetSpriteExtents(pSprite, &top, &bottom);
const int bakCompat = enginecompatibility_mode;
if (xvel[nSprite] || yvel[nSprite]) if (xvel[nSprite] || yvel[nSprite])
{ {
short bakCstat = pSprite->cstat; short bakCstat = pSprite->cstat;
pSprite->cstat &= ~257; 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); 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; pSprite->cstat = bakCstat;
assert(nSector >= 0); assert(nSector >= 0);
if (pSprite->sectnum != nSector) if (pSprite->sectnum != nSector)
@ -5012,7 +5016,7 @@ void MoveDude(spritetype *pSprite)
} }
if (pPlayer && zvel[nSprite] > 0x155555 && !pPlayer->fallScream && pXSprite->height > 0) if (pPlayer && zvel[nSprite] > 0x155555 && !pPlayer->fallScream && pXSprite->height > 0)
{ {
const bool playerAlive = (pXSprite->health > 0) || VanillaMode() || DemoRecordStatus(); // only trigger falling scream if player is alive or vanilla mode const bool playerAlive = (pXSprite->health > 0) || VanillaMode(); // only trigger falling scream if player is alive or vanilla mode
if (playerAlive) if (playerAlive)
{ {
pPlayer->fallScream = 1; pPlayer->fallScream = 1;
@ -5110,7 +5114,7 @@ void MoveDude(spritetype *pSprite)
break; break;
case kDudeBurningCultist: case kDudeBurningCultist:
{ {
const bool fixRandomCultist = (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode() && !DemoRecordStatus(); // 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
if (Chance(chance)) if (Chance(chance))
pSprite->type = kDudeCultistTommy; pSprite->type = kDudeCultistTommy;
else else
@ -5427,6 +5431,8 @@ int MoveMissile(spritetype *pSprite)
int top, bottom; int top, bottom;
GetSpriteExtents(pSprite, &top, &bottom); GetSpriteExtents(pSprite, &top, &bottom);
int i = 1; 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) while (1)
{ {
int x = pSprite->x; int x = pSprite->x;
@ -5434,7 +5440,10 @@ int MoveMissile(spritetype *pSprite)
int z = pSprite->z; int z = pSprite->z;
int nSector2 = pSprite->sectnum; int nSector2 = pSprite->sectnum;
clipmoveboxtracenum = 1; 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); 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; clipmoveboxtracenum = 3;
short nSector = nSector2; short nSector = nSector2;
if (nSector2 < 0) if (nSector2 < 0)
@ -5547,7 +5556,7 @@ void actExplodeSprite(spritetype *pSprite)
switch (pSprite->type) switch (pSprite->type)
{ {
case kMissileFireballNapam: case kMissileFireballNapalm:
nType = kExplosionNapalm; nType = kExplosionNapalm;
seqSpawn(4, 3, nXSprite, -1); seqSpawn(4, 3, nXSprite, -1);
if (Chance(0x8000)) if (Chance(0x8000))
@ -6004,7 +6013,7 @@ void actProcessSprites(void)
// the new flag newSectCheckMethod for GetClosestSpriteSectors() does rectify these issues, but this may cause unintended side effects for level scripted explosions // 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 // so only allow this new checking method for dude spawned explosions
short gAffectedXWalls[kMaxXWalls]; short gAffectedXWalls[kMaxXWalls];
const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode() && !DemoRecordStatus(); // use new sector checking logic const bool newSectCheckMethod = !cl_bloodvanillaexplosions && pOwner && pOwner->IsDudeActor() && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, radius, sectmap, gAffectedXWalls, newSectCheckMethod); GetClosestSpriteSectors(nSector, x, y, radius, sectmap, gAffectedXWalls, newSectCheckMethod);
for (int i = 0; i < kMaxXWalls; i++) for (int i = 0; i < kMaxXWalls; i++)
@ -6197,7 +6206,7 @@ void actProcessSprites(void)
if (nXSprite > 0) if (nXSprite > 0)
{ {
XSPRITE *pXSprite = &xsprite[nXSprite]; XSPRITE *pXSprite = &xsprite[nXSprite];
const bool fixBurnGlitch = IsBurningDude(pSprite) && !VanillaMode() && !DemoRecordStatus(); // if enemies are burning, always apply burning damage per tick const bool fixBurnGlitch = !cl_bloodvanillaenemies && IsBurningDude(pSprite) && !VanillaMode(); // if enemies are burning, always apply burning damage per tick
if ((pXSprite->burnTime > 0) || fixBurnGlitch) if ((pXSprite->burnTime > 0) || fixBurnGlitch)
{ {
switch (pSprite->type) switch (pSprite->type)
@ -6710,7 +6719,7 @@ void actBuildMissile(spritetype* pMissile, int nXSprite, int nSprite) {
seqSpawn(2, 3, nXSprite, -1); seqSpawn(2, 3, nXSprite, -1);
sfxPlay3DSound(pMissile, 493, 0, 0); sfxPlay3DSound(pMissile, 493, 0, 0);
break; break;
case kMissileFireballNapam: case kMissileFireballNapalm:
seqSpawn(61, 3, nXSprite, nNapalmClient); seqSpawn(61, 3, nXSprite, nNapalmClient);
sfxPlay3DSound(pMissile, 441, 0, 0); sfxPlay3DSound(pMissile, 441, 0, 0);
break; break;

View file

@ -177,7 +177,7 @@ bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange)
// It makes ignore danger if enemy immune to N damageType. As result Cerberus start acting like // It makes ignore danger if enemy immune to N damageType. As result Cerberus start acting like
// in Blood 1.0 so it can move normally to player. It's up to you for adding rest of enemies here as // in Blood 1.0 so it can move normally to player. It's up to you for adding rest of enemies here as
// i don't think it will broke something in game. // i don't think it will broke something in game.
if (!VanillaMode() && Crusher && isImmune(pSprite, pXSector->damageType, 16)) return true; if (!cl_bloodvanillaenemies && !VanillaMode() && Crusher && isImmune(pSprite, pXSector->damageType, 16)) return true;
fallthrough__; fallthrough__;
case kDudeZombieButcher: case kDudeZombieButcher:
case kDudeSpiderBrown: case kDudeSpiderBrown:
@ -1033,7 +1033,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra];
pDudeExtra->recoil = 1; pDudeExtra->recoil = 1;
} }
const bool fixRandomCultist = (pSprite->inittype >= kDudeBase) && (pSprite->inittype < kDudeMax) && !VanillaMode() && !DemoRecordStatus(); // 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
switch (pSprite->type) switch (pSprite->type)
{ {
case kDudeCultistTommy: case kDudeCultistTommy:
@ -1110,7 +1110,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
case kDudeTinyCaleb: case kDudeTinyCaleb:
if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth/* && (pXSprite->at17_6 != 1 || pXSprite->at17_6 != 2)*/) if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth/* && (pXSprite->at17_6 != 1 || pXSprite->at17_6 != 2)*/)
{ {
if (!VanillaMode() && !DemoRecordStatus()) // fix burning sprite for tiny caleb if (!cl_bloodvanillaenemies && !VanillaMode()) // fix burning sprite for tiny caleb
{ {
pSprite->type = kDudeBurningTinyCaleb; pSprite->type = kDudeBurningTinyCaleb;
aiNewState(actor, &tinycalebBurnGoto); aiNewState(actor, &tinycalebBurnGoto);
@ -1480,7 +1480,7 @@ void aiLookForTarget(spritetype *pSprite, XSPRITE *pXSprite)
if (pXSprite->state) if (pXSprite->state)
{ {
uint8_t sectmap[(kMaxSectors+7)>>3]; uint8_t sectmap[(kMaxSectors+7)>>3];
const bool newSectCheckMethod = !VanillaMode() && !DemoRecordStatus(); // use new sector checking logic const bool newSectCheckMethod = !cl_bloodvanillaenemies && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(pSprite->sectnum, pSprite->x, pSprite->y, 400, sectmap, nullptr, newSectCheckMethod); GetClosestSpriteSectors(pSprite->sectnum, pSprite->x, pSprite->y, 400, sectmap, nullptr, newSectCheckMethod);
int nSprite2; int nSprite2;

View file

@ -95,7 +95,7 @@ void StompSeqCallback(int, DBloodActor* actor1)
int nSector = pSprite->sectnum; int nSector = pSprite->sectnum;
int v1c = 5+2*gGameOptions.nDifficulty; int v1c = 5+2*gGameOptions.nDifficulty;
int v10 = 25+30*gGameOptions.nDifficulty; int v10 = 25+30*gGameOptions.nDifficulty;
const bool newSectCheckMethod = !VanillaMode() && !DemoRecordStatus(); // use new sector checking logic const bool newSectCheckMethod = !cl_bloodvanillaenemies && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, vc, sectmap, nullptr, newSectCheckMethod); GetClosestSpriteSectors(nSector, x, y, vc, sectmap, nullptr, newSectCheckMethod);
char v4 = 0; char v4 = 0;
int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0); int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);

View file

@ -42,25 +42,22 @@ AISTATE spidGoto = { kAiStateMove, 7, -1, 600, NULL, aiMoveForward, spidThinkGot
AISTATE spidSearch = { kAiStateSearch, 7, -1, 1800, NULL, aiMoveForward, spidThinkSearch, &spidIdle }; AISTATE spidSearch = { kAiStateSearch, 7, -1, 1800, NULL, aiMoveForward, spidThinkSearch, &spidIdle };
AISTATE spidBite = { kAiStateChase, 6, nSpidBiteClient, 60, NULL, NULL, NULL, &spidChase }; AISTATE spidBite = { kAiStateChase, 6, nSpidBiteClient, 60, NULL, NULL, NULL, &spidChase };
AISTATE spidJump = { kAiStateChase, 8, nSpidJumpClient, 60, NULL, aiMoveForward, NULL, &spidChase }; AISTATE spidJump = { kAiStateChase, 8, nSpidJumpClient, 60, NULL, aiMoveForward, NULL, &spidChase };
AISTATE spid13A92C = { kAiStateOther, 0, dword_279B50, 60, NULL, NULL, NULL, &spidIdle }; AISTATE spidBirth = { kAiStateOther, 0, nSpidBirthClient, 60, NULL, NULL, NULL, &spidIdle };
static char sub_70D30(XSPRITE *pXDude, int a2, int a3) static char SpidPoisonPlayer(XSPRITE *pXDude, int nBlind, int max)
{ {
assert(pXDude != NULL); assert(pXDude != NULL);
int nDude = pXDude->reference; int nDude = pXDude->reference;
spritetype *pDude = &sprite[nDude]; spritetype *pDude = &sprite[nDude];
if (IsPlayerSprite(pDude)) if (IsPlayerSprite(pDude))
{ {
a2 <<= 4; nBlind <<= 4;
a3 <<= 4; max <<= 4;
if (IsPlayerSprite(pDude)) PLAYER *pPlayer = &gPlayer[pDude->type-kDudePlayer1];
if (pPlayer->blindEffect < max)
{ {
PLAYER *pPlayer = &gPlayer[pDude->type-kDudePlayer1]; pPlayer->blindEffect = ClipHigh(pPlayer->blindEffect+nBlind, max);
if (a3 > pPlayer->blindEffect) return 1;
{
pPlayer->blindEffect = ClipHigh(pPlayer->blindEffect+a2, a3);
return 1;
}
} }
} }
return 0; return 0;
@ -93,11 +90,11 @@ void SpidBiteSeqCallback(int, DBloodActor* actor)
break; break;
case kDudeSpiderRed: case kDudeSpiderRed:
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
if (Chance(0x5000)) sub_70D30(pXTarget, 4, 16); if (Chance(0x5000)) SpidPoisonPlayer(pXTarget, 4, 16);
break; break;
case kDudeSpiderBlack: case kDudeSpiderBlack:
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
sub_70D30(pXTarget, 8, 16); SpidPoisonPlayer(pXTarget, 8, 16);
break; break;
case kDudeSpiderMother: { case kDudeSpiderMother: {
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
@ -106,9 +103,9 @@ void SpidBiteSeqCallback(int, DBloodActor* actor)
dy += Random2(2000); dy += Random2(2000);
dz += Random2(2000); dz += Random2(2000);
actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite);
sub_70D30(pXTarget, 8, 16); SpidPoisonPlayer(pXTarget, 8, 16);
}
break; break;
}
} }
} }
@ -141,7 +138,7 @@ void SpidJumpSeqCallback(int, DBloodActor* actor)
} }
} }
void sub_71370(int, DBloodActor* actor) void SpidBirthSeqCallback(int, DBloodActor* actor)
{ {
XSPRITE* pXSprite = &actor->x(); XSPRITE* pXSprite = &actor->x();
spritetype* pSprite = &actor->s(); spritetype* pSprite = &actor->s();
@ -249,7 +246,7 @@ static void spidThinkChase(DBloodActor* actor)
if (nDist < 0x733 && nDist > 0x399 && abs(nDeltaAngle) < 85) if (nDist < 0x733 && nDist > 0x399 && abs(nDeltaAngle) < 85)
aiNewState(actor, &spidJump); aiNewState(actor, &spidJump);
else if (Chance(0x8000)) else if (Chance(0x8000))
aiNewState(actor, &spid13A92C); aiNewState(actor, &spidBirth);
break; break;
} }

View file

@ -313,7 +313,7 @@ extern AISTATE spidGoto;
extern AISTATE spidSearch; extern AISTATE spidSearch;
extern AISTATE spidBite; extern AISTATE spidBite;
extern AISTATE spidJump; extern AISTATE spidJump;
extern AISTATE spid13A92C; extern AISTATE spidBirth;
extern AISTATE tchernobogIdle; extern AISTATE tchernobogIdle;
extern AISTATE tchernobogSearch; extern AISTATE tchernobogSearch;

View file

@ -619,7 +619,7 @@ static void unicultThinkChase(DBloodActor* actor)
mdist = 2500; mdist = 2500;
fallthrough__; fallthrough__;
case kMissileFireball: case kMissileFireball:
case kMissileFireballNapam: case kMissileFireballNapalm:
case kMissileFireballCerberus: case kMissileFireballCerberus:
case kMissileFireballTchernobog: case kMissileFireballTchernobog:
if (mdist == defDist) mdist = 3000; if (mdist == defDist) mdist = 3000;
@ -834,7 +834,7 @@ static void unicultThinkChase(DBloodActor* actor)
case kMissileTeslaAlt: case kMissileTeslaAlt:
case kMissileFlareAlt: case kMissileFlareAlt:
case kMissileFireball: case kMissileFireball:
case kMissileFireballNapam: case kMissileFireballNapalm:
case kMissileFireballCerberus: case kMissileFireballCerberus:
case kMissileFireballTchernobog: { case kMissileFireballTchernobog: {
// allow attack if dude is far from object, but target is close to it // allow attack if dude is far from object, but target is close to it
@ -1328,7 +1328,7 @@ void scaleDamage(XSPRITE* pXSprite) {
curScale[kDmgSpirit] = 32 + Random(18); curScale[kDmgSpirit] = 32 + Random(18);
break; break;
case kMissileFireball: case kMissileFireball:
case kMissileFireballNapam: case kMissileFireballNapalm:
case kMissileFireballCerberus: case kMissileFireballCerberus:
case kMissileFireballTchernobog: case kMissileFireballTchernobog:
curScale[kDmgBurn] = 50; curScale[kDmgBurn] = 50;

View file

@ -172,17 +172,35 @@ enum {
// (weapons) // (weapons)
kItemWeaponBase = 40, kItemWeaponBase = 40,
kItemWeaponRandom = kItemWeaponBase,
kItemWeaponSawedoff = 41, kItemWeaponSawedoff = 41,
kItemWeaponTommygun = 42, kItemWeaponTommygun = 42,
kItemWeaponFlarePistol = 43,
kItemWeaponVoodooDoll = 44, kItemWeaponVoodooDoll = 44,
kItemWeaponTeslaCannon = 45,
kItemWeaponNapalmLauncher = 46,
kItemWeaponPitchfork = 47,
kItemWeaponSprayCan = 48,
kItemWeaponTNT = 49,
kItemWeaponLifeLeech = 50, kItemWeaponLifeLeech = 50,
kItemWeaponMax = 51, kItemWeaponMax = 51,
// items (ammos) // items (ammos)
kItemAmmoBase = 60, kItemAmmoBase = 60,
kItemAmmoSprayCan = kItemAmmoBase,
kItemAmmoTNTBundle = 62,
kItemAmmoTNTBox = 63,
kItemAmmoProxBombBundle = 64,
kItemAmmoRemoteBombBundle = 65,
kItemAmmoTrappedSoul = 66,
kItemAmmoSawedoffFew = 67, kItemAmmoSawedoffFew = 67,
kItemAmmoSawedoffBox = 68,
kItemAmmoTommygunFew = 69, kItemAmmoTommygunFew = 69,
kAmmoItemVoodooDoll = 70, kItemAmmoVoodooDoll = 70,
kItemAmmoTommygunDrum = 72,
kItemAmmoTeslaCharge = 73,
kItemAmmoFlares = 76,
kItemAmmoGasolineCan = 79,
kItemAmmoMax = 81, kItemAmmoMax = 81,
kItemBase = 100, kItemBase = 100,
@ -302,7 +320,7 @@ enum {
kMissilePukeGreen = 309, kMissilePukeGreen = 309,
kMissileUnused = 310, kMissileUnused = 310,
kMissileArcGargoyle = 311, kMissileArcGargoyle = 311,
kMissileFireballNapam = 312, kMissileFireballNapalm = 312,
kMissileFireballCerberus = 313, kMissileFireballCerberus = 313,
kMissileFireballTchernobog = 314, kMissileFireballTchernobog = 314,
kMissileLifeLeechRegular = 315, kMissileLifeLeechRegular = 315,

View file

@ -886,23 +886,20 @@ int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, uint8_t *pSect
int x2 = wall[nWallB].x, y2 = wall[nWallB].y; int x2 = wall[nWallB].x, y2 = wall[nWallB].y;
int nLength = approxDist(x1-x2, y1-y2); int nLength = approxDist(x1-x2, y1-y2);
const int nDist2 = (nDist+(nDist>>1))<<4; const int nDist2 = (nDist+(nDist>>1))<<4;
if (nLength > nDist2) // if span is greater than range * 1.5, test midsection 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
{ {
nLength = ClipRange(nLength / nDist2, 1, 4); // never split more than 4 times const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1;
for (int k = 0; k < nLength; k++) // subdivide span into smaller chunks towards direction withinRange = CheckProximityPoint(xcenter, ycenter, 0, x, y, 0, nDist);
{ if (withinRange)
const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1; break;
withinRange = CheckProximityPoint(xcenter, ycenter, 0, x, y, 0, nDist); if (k == (nLength-1)) // reached end
if (withinRange) break;
break; const bool bDir = approxDist(x-x1, y-y1) < approxDist(x-x2, y-y2);
if ((k+1) == nLength) // reached end, no point in calculating direction/center again if (bDir) // step closer and check again
break; x2 = xcenter, y2 = ycenter;
const bool bDir = approxDist(x-x1, y-y1) < approxDist(x-x2, y-y2); else
if (bDir) // step closer and check again x1 = xcenter, y1 = ycenter;
x2 = xcenter, y2 = ycenter;
else
x1 = xcenter, y1 = ycenter;
}
} }
} }
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

View file

@ -318,7 +318,7 @@ static AISTATE* allAIStates[] =
&spidSearch, &spidSearch,
&spidBite, &spidBite,
&spidJump, &spidJump,
&spid13A92C, &spidBirth,
&tchernobogIdle, &tchernobogIdle,
&tchernobogSearch, &tchernobogSearch,
&tchernobogChase, &tchernobogChase,

View file

@ -491,7 +491,7 @@ void nnExtInitModernStuff(bool bSaveLoad) {
nnExtResetGlobals(); nnExtResetGlobals();
// use true random only for single player mode, otherwise use Blood's default one. // use true random only for single player mode, otherwise use Blood's default one.
if (gGameOptions.nGameType == 0 && !VanillaMode() && !DemoRecordStatus()) { if (gGameOptions.nGameType == 0 && !VanillaMode()) {
gStdRandom.seed(std::random_device()()); gStdRandom.seed(std::random_device()());

View file

@ -75,7 +75,7 @@ static void (*seqClientCallback[])(int, DBloodActor*) = {
ratBiteSeqCallback, ratBiteSeqCallback,
SpidBiteSeqCallback, SpidBiteSeqCallback,
SpidJumpSeqCallback, SpidJumpSeqCallback,
sub_71370, SpidBirthSeqCallback,
sub_71BD4, sub_71BD4,
sub_720AC, sub_720AC,
sub_71A90, sub_71A90,

View file

@ -43,7 +43,7 @@ void sub_70284(int, DBloodActor*);
void ratBiteSeqCallback(int, DBloodActor*); void ratBiteSeqCallback(int, DBloodActor*);
void SpidBiteSeqCallback(int, DBloodActor*); void SpidBiteSeqCallback(int, DBloodActor*);
void SpidJumpSeqCallback(int, DBloodActor*); void SpidJumpSeqCallback(int, DBloodActor*);
void sub_71370(int, DBloodActor*); void SpidBirthSeqCallback(int, DBloodActor*);
void sub_71A90(int, DBloodActor*); void sub_71A90(int, DBloodActor*);
void sub_71BD4(int, DBloodActor*); void sub_71BD4(int, DBloodActor*);
void sub_720AC(int, DBloodActor*); void sub_720AC(int, DBloodActor*);
@ -104,7 +104,7 @@ enum
nRatBiteClient, nRatBiteClient,
nSpidBiteClient, nSpidBiteClient,
nSpidJumpClient, nSpidJumpClient,
dword_279B50, nSpidBirthClient,
dword_279B54, dword_279B54,
dword_279B58, dword_279B58,
dword_279B5C, dword_279B5C,

View file

@ -1466,6 +1466,7 @@ void OperateTeleport(unsigned int nSector, XSECTOR *pXSector)
{ {
playerResetInertia(pPlayer); playerResetInertia(pPlayer);
pPlayer->zViewVel = pPlayer->zWeaponVel = 0; pPlayer->zViewVel = pPlayer->zWeaponVel = 0;
pPlayer->angle.settarget(pSprite->ang, true);
} }
} }
} }

View file

@ -151,7 +151,7 @@ static bool checkFired6or7(PLAYER *pPlayer)
case 6: case 6:
return 1; return 1;
case 7: case 7:
if (VanillaMode() || DemoRecordStatus()) if (VanillaMode())
return 0; return 0;
return 1; return 1;
} }
@ -717,7 +717,7 @@ void WeaponLower(PLAYER *pPlayer)
} }
break; break;
case 7: // throwing ignited alt fire spray case 7: // throwing ignited alt fire spray
if (VanillaMode() || DemoRecordStatus() || (pPlayer->newWeapon != 0)) if (VanillaMode() || (pPlayer->newWeapon != 0))
break; break;
pPlayer->weaponState = 1; pPlayer->weaponState = 1;
StartQAV(pPlayer, 11, -1, 0); StartQAV(pPlayer, 11, -1, 0);
@ -1465,7 +1465,7 @@ void AltFireVoodoo(int nTrigger, PLAYER *pPlayer)
// by NoOne: trying to simulate v1.0x voodoo here. // by NoOne: trying to simulate v1.0x voodoo here.
// dunno how exactly it works, but at least it not spend all the ammo on alt fire // dunno how exactly it works, but at least it not spend all the ammo on alt fire
if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus()) { if (gGameOptions.weaponsV10x && !VanillaMode()) {
int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount); int nCount = ClipHigh(pPlayer->ammoCount[9], pPlayer->aimTargetsCount);
if (nCount > 0) if (nCount > 0)
{ {
@ -1622,7 +1622,7 @@ void FireNapalm(int nTrigger, PLAYER *pPlayer)
offset = 50; offset = 50;
break; break;
} }
playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapam); playerFireMissile(pPlayer, offset, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
sfxPlay3DSound(pSprite, 480, 2, 0); sfxPlay3DSound(pSprite, 480, 2, 0);
UseAmmo(pPlayer, 4, 1); UseAmmo(pPlayer, 4, 1);
pPlayer->flashEffect = 1; pPlayer->flashEffect = 1;
@ -1631,8 +1631,8 @@ void FireNapalm(int nTrigger, PLAYER *pPlayer)
void FireNapalm2(int , PLAYER *pPlayer) void FireNapalm2(int , PLAYER *pPlayer)
{ {
spritetype *pSprite = pPlayer->pSprite; spritetype *pSprite = pPlayer->pSprite;
playerFireMissile(pPlayer, -120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapam); playerFireMissile(pPlayer, -120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
playerFireMissile(pPlayer, 120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapam); playerFireMissile(pPlayer, 120, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, kMissileFireballNapalm);
sfxPlay3DSound(pSprite, 480, 2, 0); sfxPlay3DSound(pSprite, 480, 2, 0);
UseAmmo(pPlayer, 4, 2); UseAmmo(pPlayer, 4, 2);
pPlayer->flashEffect = 1; pPlayer->flashEffect = 1;
@ -2196,7 +2196,7 @@ void WeaponProcess(PLAYER *pPlayer) {
} }
if (pPlayer->newWeapon) if (pPlayer->newWeapon)
{ {
if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkFired6or7(pPlayer) && !VanillaMode() && !DemoRecordStatus()) // skip banned weapons when underwater and using next/prev weapon key inputs if (pPlayer->isUnderwater && BannedUnderwater(pPlayer->newWeapon) && !checkFired6or7(pPlayer) && !VanillaMode()) // skip banned weapons when underwater and using next/prev weapon key inputs
{ {
if (prevNewWeaponVal == WeaponSel_Next || prevNewWeaponVal == WeaponSel_Prev) // if player switched weapons if (prevNewWeaponVal == WeaponSel_Next || prevNewWeaponVal == WeaponSel_Prev) // if player switched weapons
{ {
@ -2247,7 +2247,7 @@ void WeaponProcess(PLAYER *pPlayer) {
pPlayer->newWeapon = kWeapRemote; pPlayer->newWeapon = kWeapRemote;
} }
} }
else if ((pPlayer->newWeapon == kWeapSpraycan) && !VanillaMode() && !DemoRecordStatus()) else if ((pPlayer->newWeapon == kWeapSpraycan) && !VanillaMode())
{ {
if ((pPlayer->curWeapon == kWeapSpraycan) && (pPlayer->weaponState == 2)) // fix spray can state glitch when switching from spray to tnt and back quickly if ((pPlayer->curWeapon == kWeapSpraycan) && (pPlayer->weaponState == 2)) // fix spray can state glitch when switching from spray to tnt and back quickly
{ {
@ -2570,10 +2570,10 @@ void WeaponProcess(PLAYER *pPlayer) {
case kWeapNapalm: case kWeapNapalm:
if (powerupCheck(pPlayer, kPwUpTwoGuns)) if (powerupCheck(pPlayer, kPwUpTwoGuns))
// by NoOne: allow napalm launcher alt fire act like in v1.0x versions // by NoOne: allow napalm launcher alt fire act like in v1.0x versions
if (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus()) StartQAV(pPlayer, kQAV2NAPFIR2, nClientFireNapalm2); if (gGameOptions.weaponsV10x && !VanillaMode()) StartQAV(pPlayer, kQAV2NAPFIR2, nClientFireNapalm2);
else StartQAV(pPlayer, kQAV2NAPFIRE, nClientAltFireNapalm, 0); else StartQAV(pPlayer, kQAV2NAPFIRE, nClientAltFireNapalm);
else else
StartQAV(pPlayer, kQAVNAPFIRE, (gGameOptions.weaponsV10x && !VanillaMode() && !DemoRecordStatus()) ? nClientFireNapalm : nClientAltFireNapalm); StartQAV(pPlayer, kQAVNAPFIRE, (gGameOptions.weaponsV10x && !VanillaMode()) ? nClientFireNapalm : nClientAltFireNapalm);
return; return;
case kWeapFlareGun: case kWeapFlareGun:
if (CheckAmmo(pPlayer, 1, 8)) if (CheckAmmo(pPlayer, 1, 8))
@ -2618,7 +2618,7 @@ void teslaHit(spritetype *pMissile, int a2)
int nDist = 300; int nDist = 300;
int nSector = pMissile->sectnum; int nSector = pMissile->sectnum;
int nOwner = pMissile->owner; int nOwner = pMissile->owner;
const bool newSectCheckMethod = !VanillaMode() && !DemoRecordStatus(); // use new sector checking logic const bool newSectCheckMethod = !cl_bloodvanillaexplosions && !VanillaMode(); // use new sector checking logic
GetClosestSpriteSectors(nSector, x, y, nDist, sectmap, nullptr, newSectCheckMethod); GetClosestSpriteSectors(nSector, x, y, nDist, sectmap, nullptr, newSectCheckMethod);
bool v4 = true; bool v4 = true;
DBloodActor* actor = nullptr; DBloodActor* actor = nullptr;

View file

@ -962,6 +962,12 @@ OptionValue "ExplosBehavior"
1, "$OPTVAL_OLD" 1, "$OPTVAL_OLD"
} }
OptionValue "EnemyBehavior"
{
0, "$OPTVAL_DEFAULT"
1, "$OPTVAL_OLD"
}
OptionMenu GameplayOptions protected OptionMenu GameplayOptions protected
{ {
@ -984,6 +990,7 @@ OptionMenu GameplayOptions protected
{ {
Option "$PLRMNU_EQUIP", "cl_weaponswitch", "WeapSwitch" Option "$PLRMNU_EQUIP", "cl_weaponswitch", "WeapSwitch"
Option "$EXPLOS_BEHAVIOR", "cl_bloodvanillaexplosions", "ExplosBehavior" Option "$EXPLOS_BEHAVIOR", "cl_bloodvanillaexplosions", "ExplosBehavior"
Option "$ENEMY_BEHAVIOR", "cl_bloodvanillaenemies", "EnemyBehavior"
} }
ifgame(Exhumed) ifgame(Exhumed)
{ {