adapted ai.cpp and callback.cpp.

This commit is contained in:
Christoph Oelckers 2023-10-15 19:33:33 +02:00
parent 62753c02f0
commit 4f338ab8a2
4 changed files with 99 additions and 221 deletions

View file

@ -197,8 +197,17 @@ bool CanMove(DBloodActor* actor, DBloodActor* target, DAngle nAngle, double nRan
if (Underwater)
return true;
break;
case kDudeCerberusTwoHead:
case kDudeCerberusOneHead:
if (VanillaMode())
{
if (Crusher)
return false;
if ((!pSector->hasX() || (!pSector->xs().Underwater && !pSector->xs().Depth)) && floorZ - bottom > 0x2000)
return false;
break;
}
[[fallthrough]];
case kDudeCerberusTwoHead:
// by NoOne: a quick fix for Cerberus spinning in E3M7-like maps, where damage sectors is used.
// 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
@ -222,9 +231,7 @@ bool CanMove(DBloodActor* actor, DBloodActor* target, DAngle nAngle, double nRan
break;
#ifdef NOONE_EXTENSIONS
case kDudeModernCustom:
if ((Crusher && !nnExtIsImmune(actor, pXSector->damageType)) || ((Water || Underwater) && !canSwim(actor))) return false;
return true;
[[fallthrough]];
return cdudeGet(actor)->CanMove(pSector, Crusher, Water, Underwater, Depth, bottom, floorZ);
#endif
case kDudeZombieAxeNormal:
case kDudePhantasm:
@ -420,25 +427,8 @@ void aiActivateDude(DBloodActor* actor)
}
#ifdef NOONE_EXTENSIONS
case kDudeModernCustom:
{
actor->dudeExtra.active = 1;
if (actor->GetTarget() == nullptr)
{
if (spriteIsUnderwater(actor, false)) aiGenDudeNewState(actor, &genDudeSearchW);
else aiGenDudeNewState(actor, &genDudeSearchL);
}
else
{
if (Chance(0x4000)) playGenDudeSound(actor,kGenDudeSndTargetSpot);
if (spriteIsUnderwater(actor, false)) aiGenDudeNewState(actor, &genDudeChaseW);
else aiGenDudeNewState(actor, &genDudeChaseL);
}
break;
}
if (actor->GetTarget() == nullptr) aiGenDudeNewState(actor, &genDudeBurnSearch);
else aiGenDudeNewState(actor, &genDudeBurnChase);
break;
cdudeGet(actor)->Activate();
return;
#endif
case kDudeCultistTommyProne:
{
@ -971,7 +961,7 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
auto pPlayer = getPlayer(source->GetType());
if (!pPlayer) return nDamage;
//if (powerupCheck(pPlayer, kPwUpShadowCloak)) pPlayer->pwUpTime[kPwUpShadowCloak] = 0;
if (readyForCrit(source, actor))
{
nDamage += aiDamageSprite(actor, source, nDmgType, nDamage * (10 - gGameOptions.nDifficulty));
@ -984,84 +974,13 @@ int aiDamageSprite(DBloodActor* source, DBloodActor* actor, DAMAGE_TYPE nDmgType
RecoilDude(actor);
}
}
DPrintf(DMSG_SPAMMY, "Player #%d does the critical damage to patrol dude #%d!", pPlayer->pnum + 1, actor->GetIndex());
}
return nDamage;
}
if (IsCustomDude(actor))
return cdudeGet(actor)->Damage(source, nDmgType, nDamage);
if (actor->GetType() == kDudeModernCustom)
{
GENDUDEEXTRA* pExtra = &actor->genDudeExtra;
if (nDmgType == kDamageBurn)
{
if (actor->xspr.health > (uint32_t)pDudeInfo->fleeHealth) return nDamage;
else if (actor->xspr.txID <= 0 || getNextIncarnation(actor) == nullptr)
{
removeDudeStuff(actor);
if (pExtra->weaponType == kGenDudeWeaponKamikaze)
doExplosion(actor, actor->xspr.data1 - kTrapExploder);
if (spriteIsUnderwater(actor))
{
actor->xspr.health = 0;
return nDamage;
}
if (actor->xspr.burnTime <= 0)
actor->xspr.burnTime = 1200;
if (pExtra->canBurn && pExtra->availDeaths[kDamageBurn] > 0) {
aiPlay3DSound(actor, 361, AI_SFX_PRIORITY_0, -1);
playGenDudeSound(actor,kGenDudeSndBurning);
//actor->ChangeType(kDudeModernCustomBurning);
if (actor->xspr.data2 == kGenDudeDefaultSeq) // don't inherit palette for burning if using default animation
actor->spr.pal = 0;
aiGenDudeNewState(actor, &genDudeBurnGoto);
actHealDude(actor, dudeInfo[55].startHealth, dudeInfo[55].startHealth);
actor->dudeExtra.time = PlayClock + 360;
evKillActor(actor, AF(fxFlameLick));
}
}
else
{
actKillDude(actor, actor, kDamageFall, 65535);
}
}
else if (canWalk(actor) && !inDodge(actor->xspr.aiState) && !inRecoil(actor->xspr.aiState))
{
if (!dudeIsMelee(actor))
{
if (inIdle(actor->xspr.aiState) || Chance(getDodgeChance(actor)))
{
if (!spriteIsUnderwater(actor))
{
if (!canDuck(actor) || !dudeIsPlayingSeq(actor, 14)) aiGenDudeNewState(actor, &genDudeDodgeShortL);
else aiGenDudeNewState(actor, &genDudeDodgeShortD);
if (Chance(0x0200))
playGenDudeSound(actor,kGenDudeSndGotHit);
}
else if (dudeIsPlayingSeq(actor, 13))
{
aiGenDudeNewState(actor, &genDudeDodgeShortW);
}
}
}
else if (Chance(0x0200))
{
playGenDudeSound(actor,kGenDudeSndGotHit);
}
}
return nDamage;
}
}
#endif
@ -1211,56 +1130,8 @@ void RecoilDude(DBloodActor* actor)
{
#ifdef NOONE_EXTENSIONS
case kDudeModernCustom:
{
GENDUDEEXTRA* pExtra = &actor->genDudeExtra;
int rChance = getRecoilChance(actor);
if (pExtra->canElectrocute && pDudeExtra->teslaHit && !spriteIsUnderwater(actor, false))
{
if (Chance(rChance << 3) || (dudeIsMelee(actor) && Chance(rChance << 4))) aiGenDudeNewState(actor, &genDudeRecoilTesla);
else if (pExtra->canRecoil && Chance(rChance)) aiGenDudeNewState(actor, &genDudeRecoilL);
else if (canWalk(actor))
{
if (Chance(rChance >> 2)) aiGenDudeNewState(actor, &genDudeDodgeL);
else if (Chance(rChance >> 1)) aiGenDudeNewState(actor, &genDudeDodgeShortL);
}
}
else if (pExtra->canRecoil && Chance(rChance))
{
if (inDuck(actor->xspr.aiState) && Chance(rChance >> 2)) aiGenDudeNewState(actor, &genDudeRecoilD);
else if (spriteIsUnderwater(actor, false)) aiGenDudeNewState(actor, &genDudeRecoilW);
else aiGenDudeNewState(actor, &genDudeRecoilL);
}
int rState = inRecoil(actor->xspr.aiState);
if (rState > 0)
{
if (!canWalk(actor))
{
if (rState == 1) actor->xspr.aiState->nextState = &genDudeChaseNoWalkL;
else if (rState == 2) actor->xspr.aiState->nextState = &genDudeChaseNoWalkD;
else actor->xspr.aiState->nextState = &genDudeChaseNoWalkW;
}
else if (!dudeIsMelee(actor) || Chance(rChance >> 2))
{
if (rState == 1) actor->xspr.aiState->nextState = (Chance(rChance) ? &genDudeDodgeL : &genDudeDodgeShortL);
else if (rState == 2) actor->xspr.aiState->nextState = (Chance(rChance) ? &genDudeDodgeD : &genDudeDodgeShortD);
else if (rState == 3) actor->xspr.aiState->nextState = (Chance(rChance) ? &genDudeDodgeW : &genDudeDodgeShortW);
}
else if (rState == 1) actor->xspr.aiState->nextState = &genDudeChaseL;
else if (rState == 2) actor->xspr.aiState->nextState = &genDudeChaseD;
else actor->xspr.aiState->nextState = &genDudeChaseW;
playGenDudeSound(actor,kGenDudeSndGotHit);
}
pDudeExtra->teslaHit = 0;
cdudeGet(actor)->Recoil();
break;
}
#endif
case kDudeCultistTommy:
case kDudeCultistShotgun:
@ -1570,9 +1441,14 @@ void aiProcessDudes(void)
BloodStatIterator it(kStatDude);
while (auto actor = it.Next())
{
if (actor->spr.flags & 32) continue;
DUDEINFO* pDudeInfo = getDudeInfo(actor);
if (actor->IsPlayerActor() || actor->xspr.health == 0) continue;
if (actor->spr.flags & kHitagFree || actor->IsPlayerActor()) continue;
if (IsCustomDude(actor))
{
cdudeGet(actor)->Process();
continue;
}
if (actor->xspr.health == 0) continue;
actor->xspr.stateTimer = ClipLow(actor->xspr.stateTimer - 4, 0);
@ -1585,42 +1461,18 @@ void aiProcessDudes(void)
callActorFunction(*actor->xspr.aiState->thinkFunc, actor);
}
#ifdef NOONE_EXTENSIONS
switch (actor->GetType()) {
case kDudeModernCustom:
if (actor->xspr.stateTimer == 0 && actor->xspr.aiState && actor->xspr.aiState->nextState) {
if (actor->xspr.aiState->stateTicks > 0)
aiNewState(actor, actor->xspr.aiState->nextState);
else if (seqGetStatus(actor) < 0)
aiNewState(actor, actor->xspr.aiState->nextState);
}
if (actor->xspr.health > 0 && ((getDudeInfo(actor)->hinderDamage << 4) <= actor->cumulDamage))
{
GENDUDEEXTRA* pExtra = &actor->genDudeExtra;
if (pExtra->slaveCount > 0) updateTargetOfSlaves(actor);
if (pExtra->pLifeLeech != nullptr) updateTargetOfLeech(actor);
if (actor->xspr.stateTimer == 0 && actor->xspr.aiState && actor->xspr.aiState->nextState
&& (actor->xspr.aiState->stateTicks > 0 || seqGetStatus(actor) < 0))
{
aiGenDudeNewState(actor, actor->xspr.aiState->nextState);
}
int hinder = ((pExtra->isMelee) ? 25 : 5) << 4;
if (actor->xspr.health <= 0 || hinder > actor->cumulDamage) break;
actor->xspr.data3 = actor->cumulDamage;
RecoilDude(actor);
break;
}
default:
#endif
if (actor->xspr.stateTimer == 0 && actor->xspr.aiState && actor->xspr.aiState->nextState) {
if (actor->xspr.aiState->stateTicks > 0)
aiNewState(actor, actor->xspr.aiState->nextState);
else if (seqGetStatus(actor) < 0)
aiNewState(actor, actor->xspr.aiState->nextState);
}
if (actor->xspr.health > 0 && ((pDudeInfo->hinderDamage << 4) <= actor->cumulDamage))
{
actor->xspr.data3 = actor->cumulDamage;
RecoilDude(actor);
}
#ifdef NOONE_EXTENSIONS
break;
}
#endif
}
it.Reset(kStatDude);
@ -1653,18 +1505,14 @@ void aiInitSprite(DBloodActor* actor)
#ifdef NOONE_EXTENSIONS
unsigned int stateTimer = 0;
DVector3 targetV(0,0,0);
DBloodActor* pTargetMarker = nullptr;
DBloodActor* pTarget = nullptr;
// dude patrol init
if (gModernMap)
{
// must keep it in case of loading save
if (actor->xspr.dudeFlag4 && actor->GetTarget() && actor->GetTarget()->GetType() == kMarkerPath)
{
stateTimer = actor->xspr.stateTimer;
pTargetMarker = actor->GetTarget();
targetV = actor->xspr.TargetPos;
}
stateTimer = actor->xspr.stateTimer;
pTarget = actor->GetTarget();
targetV = actor->xspr.TargetPos;
}
#endif
@ -1816,8 +1664,8 @@ void aiInitSprite(DBloodActor* actor)
#ifdef NOONE_EXTENSIONS
case kDudeModernCustom:
if (!gModernMap) break;
aiGenDudeInitSprite(actor);
genDudePrepare(actor, kGenDudePropertyAll);
CUSTOMDUDE_SETUP::Setup(actor);
cdudeGet(actor)->InitSprite();
break;
#endif
default:
@ -1878,34 +1726,48 @@ void aiInitSprite(DBloodActor* actor)
{
if (actor->xspr.dudeFlag4)
{
// restore dude's path
if (pTargetMarker)
bool patrol = false;
if (pTarget == nullptr)
{
actor->SetTarget(pTargetMarker);
actor->xspr.TargetPos = targetV;
}
patrol = true;
stateTimer = 0;
// reset target spot progress
actor->xspr.data3 = 0;
// make dude follow the markers
bool uwater = spriteIsUnderwater(actor);
if (actor->GetTarget() == nullptr || actor->GetTarget()->GetType() != kMarkerPath)
{
actor->SetTarget(nullptr);
// start new patrol
actor->xspr.target = -1;
aiPatrolSetMarker(actor);
}
if (stateTimer > 0)
else
{
if (uwater) aiPatrolState(actor, kAiStatePatrolWaitW);
else if (actor->xspr.modernFlags & kDudeFlagCrouch) aiPatrolState(actor, kAiStatePatrolWaitC);
else aiPatrolState(actor, kAiStatePatrolWaitL);
actor->xspr.stateTimer = stateTimer; // restore state timer
if (pTarget->IsDudeActor()) patrol = false;
else if (pTarget->GetType() == kMarkerPath)
{
// continue patrol
actor->xspr.target = pTarget;
actor->xspr.TargetPos = targetV;
patrol = true;
}
}
if (patrol)
{
// reset target spot progress
actor->xspr.data3 = 0;
// make dude follow the markers
bool uwater = spriteIsUnderwater(actor);
if (stateTimer > 0)
{
if (uwater) aiPatrolState(actor, kAiStatePatrolWaitW);
else if (actor->xspr.modernFlags & kDudeFlagCrouch) aiPatrolState(actor, kAiStatePatrolWaitC);
else aiPatrolState(actor, kAiStatePatrolWaitL);
actor->xspr.stateTimer = stateTimer; // restore state timer
}
else if (uwater) aiPatrolState(actor, kAiStatePatrolMoveW);
else if (actor->xspr.modernFlags & kDudeFlagCrouch) aiPatrolState(actor, kAiStatePatrolMoveC);
else aiPatrolState(actor, kAiStatePatrolMoveL);
}
else if (uwater) aiPatrolState(actor, kAiStatePatrolMoveW);
else if (actor->xspr.modernFlags & kDudeFlagCrouch) aiPatrolState(actor, kAiStatePatrolMoveC);
else aiPatrolState(actor, kAiStatePatrolMoveL);
}
}
#endif

View file

@ -264,8 +264,10 @@ DEF_ANIMATOR(fxPodBloodSpray) // 18
DEF_ANIMATOR(fxPodBloodSplat) // 19
DEF_ANIMATOR(LeechStateTimer) // 20
DEF_ANIMATOR(DropVoodooCb) // unused
DEF_ANIMATOR(callbackMakeMissileBlocking) // 23
DEF_ANIMATOR(callbackMissileBurst)
DEF_ANIMATOR(callbackMakeMissileBlocking) // 23
DEF_ANIMATOR(fxPodGreenBloodSpray) // 24
#ifdef NOONE_EXTENSIONS
DEF_ANIMATOR(forcePunch)

View file

@ -291,6 +291,13 @@ void Respawn(DBloodActor* actor) // 9
actor->xspr.health = getDudeInfo(actor)->startHealth << 4;
#ifdef NOONE_EXTENSIONS
// return dude to the patrol state
if (gModernMap && actor->xspr.dudeFlag4)
{
actor->xspr.data3 = 0;
actor->SetTarget(nullptr);
}
switch (actor->GetType()) {
default:
actor->clipdist = getDudeInfo(nType + kDudeBase)->fClipdist();
@ -302,11 +309,6 @@ void Respawn(DBloodActor* actor) // 9
break;
}
// return dude to the patrol state
if (gModernMap && actor->xspr.dudeFlag4) {
actor->xspr.data3 = 0;
actor->SetTarget(nullptr);
}
#else
actor->clipdist = getDudeInfo(nType + kDudeBase)->fClipdist();
if (getSequence(getDudeInfo(nType + kDudeBase)->seqStartID))
@ -888,5 +890,17 @@ void callbackMakeMissileBlocking(DBloodActor* actor) // 23
actor->spr.cstat |= CSTAT_SPRITE_BLOCK;
}
void fxPodGreenBloodSpray(DBloodActor* actor) // 24
{
auto pFX = gFX.fxSpawnActor(FX_53, actor->sector(), actor->spr.pos);
if (pFX)
{
pFX->spr.Angles.Yaw = nullAngle;
pFX->vel = actor->vel / 256.;
}
evPostActor(actor, 6, AF(fxPodGreenBloodSpray));
}
END_BLD_NS

View file

@ -1252,7 +1252,7 @@ class CUSTOMDUDE
void Process(void);
void ProcessEffects(void);
void Recoil(void);
int Damage(int nFrom, int nDmgType, int nDmg);
int Damage(DBloodActor* nFrom, int nDmgType, int nDmg);
void Kill(DBloodActor* nFrom, int nDmgType, int nDmg);
//----------------------------------------------------------------------------------------------------
char CanMove(sectortype* pXSect, char Crusher, char Water, char Uwater, char Depth, int bottom, int floorZ);
@ -1332,7 +1332,7 @@ class CUSTOMDUDE_SETUP
char IsFirst(CUSTOMDUDE* pCmp);
public:
char FindAiState(AISTATE stateArr[][kCdudePostureMax], int arrLen, AISTATE* pNeedle, int* nType, int* nPosture);
void Setup(spritetype* pSpr, XSPRITE* pXSpr);
static void Setup(DBloodActor* actor);
static void Setup(CUSTOMDUDE* pOver = nullptr);
};