diff --git a/source/blood/src/actor.cpp b/source/blood/src/actor.cpp index 829a34a35..e49b40984 100644 --- a/source/blood/src/actor.cpp +++ b/source/blood/src/actor.cpp @@ -2547,7 +2547,7 @@ void actInit(bool bSaveLoad) { // by NoOne: init code for all my stuff if (gModernMap) { - + // reset counters gProxySpritesCount = gSightSpritesCount = gPhysSpritesCount = 0; @@ -3092,6 +3092,25 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, int nXSprite = pSprite->extra; dassert(nXSprite > 0); XSPRITE *pXSprite = &xsprite[pSprite->extra]; + + // kMaxSprites - 1 = custom dude had once life leech + if (pSprite->owner >= 0 && pSprite->owner != (kMaxSprites - 1)) { + switch (sprite[pSprite->owner].type) { + case kDudeModernCustom: + case kDudeModernCustomBurning: + for (int i = 0; i <= gGameOptions.nDifficulty; i++) { + if (!IsDudeSprite(pSprite) || gGenDudeExtra[pSprite->owner].slave[i] == pSprite->index || pXSprite->health <= 0) { + gGenDudeExtra[pSprite->owner].slave[i] = -1; + gGenDudeExtra[pSprite->owner].slaveCount = ClipRange(gGenDudeExtra[pSprite->owner].slaveCount - 1, 0, gGameOptions.nDifficulty + 1); + //viewSetSystemMessage("REMOVING %d FROM %d, COUNT: %d", pSprite->index, sprite[pSprite->owner].type, gGenDudeExtra[pSprite->owner].slaveCount); + break; + } + } + break; + } + } + + switch (pSprite->type) { case kDudeModernCustom: { removeDudeStuff(pSprite); @@ -3143,7 +3162,7 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, int seqId = pXSprite->data2 + 18; if (!gSysRes.Lookup(seqId, "SEQ")) { seqKill(3, nXSprite); - sfxPlayGDXGenDudeSound(pSprite, 10); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndTransforming); spritetype* pEffect = gFX.fxSpawn((FX_ID)52, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->ang); if (pEffect != NULL) { pEffect->cstat = CSTAT_SPRITE_ALIGNMENT_FACING; @@ -3168,7 +3187,7 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, return; } seqSpawn(seqId, 3, nXSprite, -1); - sfxPlayGDXGenDudeSound(pSprite, 10); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndTransforming); return; } break; @@ -3268,7 +3287,7 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, switch (pSprite->type) { case kDudeModernCustom: case kDudeModernCustomBurning: - sfxPlayGDXGenDudeSound(pSprite, 4); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndDeathExplode); break; case kDudeCultistTommy: case kDudeCultistShotgun: @@ -3387,7 +3406,7 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, seqSpawn(dudeInfo[nType].seqStartID+15, 3, nXSprite, nDudeToGibClient2); break; case kDudeModernCustom: - sfxPlayGDXGenDudeSound(pSprite, 2); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndDeathNormal); if (nSeq == 3) { bool seq15 = gSysRes.Lookup(pXSprite->data2 + 15, "SEQ"); bool seq16 = gSysRes.Lookup(pXSprite->data2 + 16, "SEQ"); @@ -3406,7 +3425,7 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, case kDudeModernCustomBurning: { - sfxPlayGDXGenDudeSound(pSprite, 4); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndDeathExplode); damageType = DAMAGE_TYPE_3; if (Chance(0x4000)) { @@ -3628,21 +3647,6 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, break; } - // kMaxSprites - 1 = custom dude had once life leech - if (pSprite->owner != -1 && pSprite->owner != (kMaxSprites - 1)) { - //int owner = actSpriteIdToOwnerId(pSprite->xvel); - int owner = pSprite->owner; - - switch (sprite[owner].type) { - case kDudeModernCustom: - case kDudeModernCustomBurning: - if (owner != -1) gDudeExtra[sprite[owner].extra].at6.u1.at4--; - break; - default: - break; - } - } - if (damageType == DAMAGE_TYPE_3) { DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type-kDudeBase]; @@ -3672,7 +3676,7 @@ int actDamageSprite(int nSource, spritetype *pSprite, DAMAGE_TYPE damageType, in nSource = pSprite->index; PLAYER *pSourcePlayer = NULL; - if (IsPlayerSprite(&sprite[nSource])) pSourcePlayer = &gPlayer[sprite[nSource].type - kDudePlayer1]; + if (IsPlayerSprite(&sprite[nSource])) pSourcePlayer = &gPlayer[sprite[nSource].type - kDudePlayer1]; if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pSourcePlayer, pSprite)) return 0; switch (pSprite->statnum) { @@ -3684,7 +3688,9 @@ int actDamageSprite(int nSource, spritetype *pSprite, DAMAGE_TYPE damageType, in } int nType = pSprite->type - kDudeBase; int nDamageFactor = dudeInfo[nType].at70[damageType]; - + if (pSprite->type == kDudeModernCustom) + nDamageFactor = gGenDudeExtra[pSprite->index].dmgControl[damageType]; + if (!nDamageFactor) return 0; else if (nDamageFactor != 256) damage = mulscale8(damage, nDamageFactor); @@ -4493,7 +4499,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite) if ((nHWall = gSpriteHit[nXSprite].hit & 0x3fff) >= 0 && wall[nHWall].extra >= 0) { XWALL* pXHWall = &xwall[wall[nHWall].extra]; if (pXHWall->triggerTouch && !pXHWall->isTriggered && (!pXHWall->dudeLockout || IsPlayerSprite(pSprite))) - trTriggerWall(nHWall, pXHWall, kCmdWallTouch); + trTriggerWall(nHWall, pXHWall, kCmdWallTouch, nSprite); } } } @@ -4794,7 +4800,7 @@ void MoveDude(spritetype *pSprite) if (pHitWall->extra > 0) pHitXWall = &xwall[pHitWall->extra]; if (pDudeInfo->lockOut && pHitXWall && pHitXWall->triggerPush && !pHitXWall->key && !pHitXWall->dudeLockout && !pHitXWall->state && !pHitXWall->busy && !pPlayer) - trTriggerWall(nHitWall, pHitXWall, kCmdWallPush); + trTriggerWall(nHitWall, pHitXWall, kCmdWallPush, nSprite); if (pHitWall->nextsector != -1) { sectortype *pHitSector = §or[pHitWall->nextsector]; @@ -4802,7 +4808,7 @@ void MoveDude(spritetype *pSprite) if (pHitSector->extra > 0) pHitXSector = &xsector[pHitSector->extra]; if (pDudeInfo->lockOut && pHitXSector && pHitXSector->Wallpush && !pHitXSector->Key && !pHitXSector->dudeLockout && !pHitXSector->state && !pHitXSector->busy && !pPlayer) - trTriggerSector(pHitWall->nextsector, pHitXSector, kCmdSectorPush); + trTriggerSector(pHitWall->nextsector, pHitXSector, kCmdSectorPush, nSprite); if (top < pHitSector->ceilingz || bottom > pHitSector->floorz) { // ??? @@ -4828,7 +4834,7 @@ void MoveDude(spritetype *pSprite) else pXSector = NULL; if (pXSector && pXSector->Exit && (pPlayer || !pXSector->dudeLockout)) - trTriggerSector(pSprite->sectnum, pXSector, kCmdSectorExit); + trTriggerSector(pSprite->sectnum, pXSector, kCmdSectorExit, nSprite); ChangeSpriteSect(nSprite, nSector); nXSector = sector[nSector].extra; @@ -4837,7 +4843,7 @@ void MoveDude(spritetype *pSprite) if (sector[nSector].type == kSectorTeleport) pXSector->data = pPlayer ? nSprite : -1; - trTriggerSector(nSector, pXSector, kCmdSectorEnter); + trTriggerSector(nSector, pXSector, kCmdSectorEnter, nSprite); } nSector = pSprite->sectnum; @@ -5340,7 +5346,7 @@ int MoveMissile(spritetype *pSprite) XWALL *pXWall = &xwall[pWall->extra]; if (pXWall->triggerVector) { - trTriggerWall(gHitInfo.hitwall, pXWall, kCmdWallImpact); + trTriggerWall(gHitInfo.hitwall, pXWall, kCmdWallImpact, nSprite); if (!(pWall->cstat&64)) { vdi = -1; @@ -5514,7 +5520,7 @@ void actExplodeSprite(spritetype *pSprite) sfxPlay3DSound(pSprite, nSnd, -1, 0); } break; - case kThingPodGreenBall: + case kThingPodFireBall: nType = 3; seqSpawn(9, 3, nXSprite, -1); sfxPlay3DSound(pSprite, 307, -1, 0); @@ -5890,7 +5896,7 @@ void actProcessSprites(void) actDamageSprite(actSpriteOwnerToSpriteId(pSprite), pObject, DAMAGE_TYPE_0, 12); } break; - case kThingPodFireBall: + case kThingPodGreenBall: if ((hit&0xc000) == 0x4000) { sub_2A620(actSpriteOwnerToSpriteId(pSprite), pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 200, 1, 20, DAMAGE_TYPE_3, 6, 0, 0, 0); @@ -5907,7 +5913,7 @@ void actProcessSprites(void) evPost(pSprite->index, 3, 0, kCallbackFXPodBloodSplat); } break; - case kThingPodGreenBall: + case kThingPodFireBall: { int nObject = hit & 0x3fff; if ((hit&0xc000) != 0xc000 && (nObject < 0 || nObject >= 4096)) @@ -5969,7 +5975,7 @@ void actProcessSprites(void) if (nWall == -1) break; XWALL *pXWall = &xwall[wall[nWall].extra]; - trTriggerWall(nWall, pXWall, kCmdWallImpact); + trTriggerWall(nWall, pXWall, kCmdWallImpact, nSprite); } for (int nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2]) @@ -6172,7 +6178,7 @@ void actProcessSprites(void) pXSprite->burnSource = -1; pXSprite->data1 = pXIncarnation->data1; - short oldData2 = pXSprite->data2; pXSprite->data2 = pXIncarnation->data2; // seq new seqId and save old one. + pXSprite->data2 = pXIncarnation->data2; // if incarnation is active dude, it's sndStartId will be stored in sysData1, otherwise it will be data3 if (pIncarnation->statnum == kStatDude && pIncarnation->type == kDudeModernCustom) pXSprite->sysData1 = pXIncarnation->sysData1; @@ -6206,8 +6212,7 @@ void actProcessSprites(void) case kDudeModernCustom: case kDudeModernCustomBurning: seqId = getSeqStartId(pXSprite); - if (seqId != oldData2) - getSpriteMassBySize(pSprite); // create or refresh mass cache + getSpriteMassBySize(pSprite); // create or refresh mass cache fallthrough__; // go below default: seqSpawn(seqId, 3, nXSprite, -1); @@ -6946,7 +6951,7 @@ void actFireVector(spritetype *pShooter, int a2, int a3, int a4, int a5, int a6, { XWALL *pXWall = &xwall[nXWall]; if (pXWall->triggerVector) - trTriggerWall(nWall, pXWall, kCmdWallImpact); + trTriggerWall(nWall, pXWall, kCmdWallImpact, nShooter); } break; } @@ -7553,7 +7558,6 @@ int getSpriteMassBySize(spritetype* pSprite) { int mass = 0; int seqId = -1; Seq* pSeq = NULL; if (IsDudeSprite(pSprite)) { - switch (pSprite->type) { case kDudePodMother: // fake dude, no seq break; @@ -7562,7 +7566,6 @@ int getSpriteMassBySize(spritetype* pSprite) { seqId = xsprite[pSprite->extra].data2; break; default: - seqId = dudeInfo[pSprite->type - kDudeBase].seqStartID; break; } @@ -7647,6 +7650,7 @@ int getSpriteMassBySize(spritetype* pSprite) { cached->picnum = pSprite->picnum; cached->seqId = seqId; cached->clipdist = pSprite->clipdist; + viewSetSystemMessage("MASS: %d", cached->mass); return cached->mass; } diff --git a/source/blood/src/actor.h b/source/blood/src/actor.h index 453f5e6eb..42b785ca8 100644 --- a/source/blood/src/actor.h +++ b/source/blood/src/actor.h @@ -288,6 +288,6 @@ extern short gPhysSpritesList[kMaxSuperXSprites]; extern short gProxySpritesCount; extern short gSightSpritesCount; extern short gPhysSpritesCount; -//extern short gQavPlayerIndex; +extern int DudeDifficulty[]; END_BLD_NS diff --git a/source/blood/src/ai.cpp b/source/blood/src/ai.cpp index 4e09740c7..bf54680de 100644 --- a/source/blood/src/ai.cpp +++ b/source/blood/src/ai.cpp @@ -65,6 +65,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "sfx.h" #include "trig.h" #include "triggers.h" +#include "view.h" BEGIN_BLD_NS @@ -421,12 +422,12 @@ void aiActivateDude(spritetype *pSprite, XSPRITE *pXSprite) else { aiNewState(pSprite, pXSprite, &GDXGenDudeSearchL); if (Chance(0x4000)) - sfxPlayGDXGenDudeSound(pSprite, 0); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndTargetSpot); } } else { if (Chance(0x4000)) - sfxPlayGDXGenDudeSound(pSprite, 0); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndTargetSpot); if (spriteIsUnderwater(pSprite, false)) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); @@ -1025,7 +1026,7 @@ int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_T break; case kDudeModernCustomBurning: if (Chance(0x2000) && gDudeExtra[pSprite->extra].at0 < (int)gFrameClock) { - sfxPlayGDXGenDudeSound(pSprite, 3); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndBurning); gDudeExtra[pSprite->extra].at0 = (int)gFrameClock + 360; } if (pXSprite->burnTime == 0) pXSprite->burnTime = 2400; @@ -1058,7 +1059,7 @@ int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_T && gSysRes.Lookup(pXSprite->data2 + 3, "SEQ")) { aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1); - sfxPlayGDXGenDudeSound(pSprite, 3); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndBurning); pSprite->type = kDudeModernCustomBurning; if (pXSprite->data2 == kDefaultAnimationBase) // don't inherit palette for burning if using default animation @@ -1083,7 +1084,7 @@ int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_T else aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeDmgD); if (Chance(0x0200)) - sfxPlayGDXGenDudeSound(pSprite, 1); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndGotHit); } else if (sub_5BDA8(pSprite, 13)) aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeDmgW); @@ -1132,7 +1133,7 @@ void RecoilDude(spritetype *pSprite, XSPRITE *pXSprite) int mass = getSpriteMassBySize(pSprite); int chance4 = getRecoilChance(pSprite); bool chance3 = Chance(chance4); if (pDudeExtra->at4 && (inIdle(pXSprite->aiState) || mass < 155 || (mass >= 155 && chance3)) && !spriteIsUnderwater(pSprite, false)) { - sfxPlayGDXGenDudeSound(pSprite, 1); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndGotHit); if (gSysRes.Lookup(pXSprite->data2 + 4, "SEQ")) { GDXGenDudeRTesla.at18 = (Chance(chance4 * 2) ? &GDXGenDudeDodgeL : &GDXGenDudeDodgeDmgL); @@ -1145,13 +1146,13 @@ void RecoilDude(spritetype *pSprite, XSPRITE *pXSprite) } if (inDodge(pXSprite->aiState)) { - sfxPlayGDXGenDudeSound(pSprite, 1); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndGotHit); break; } if (inIdle(pXSprite->aiState) || chance3 || Chance(getRecoilChance(pSprite)) || (!dudeIsMelee(pXSprite) && mass < 155)) { - sfxPlayGDXGenDudeSound(pSprite, 1); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndGotHit); if (canDuck(pSprite) && (Chance(chance4) || gGameOptions.nDifficulty == 0)) aiNewState(pSprite, pXSprite, &GDXGenDudeRecoilD); else if (canSwim(pSprite) && spriteIsUnderwater(pSprite, false)) aiNewState(pSprite, pXSprite, &GDXGenDudeRecoilW); @@ -1454,32 +1455,43 @@ void sub_5F15C(spritetype *pSprite, XSPRITE *pXSprite) void aiProcessDudes(void) { - for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) - { + for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { spritetype *pSprite = &sprite[nSprite]; - if (pSprite->flags&32) - continue; + if (pSprite->flags & 32) continue; int nXSprite = pSprite->extra; XSPRITE *pXSprite = &xsprite[nXSprite]; DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; if (IsPlayerSprite(pSprite) || pXSprite->health == 0) continue; + pXSprite->stateTimer = ClipLow(pXSprite->stateTimer-4, 0); if (pXSprite->aiState->at10) pXSprite->aiState->at10(pSprite, pXSprite); if (pXSprite->aiState->at14 && (gFrame&3) == (nSprite&3)) pXSprite->aiState->at14(pSprite, pXSprite); - if (pXSprite->stateTimer == 0 && pXSprite->aiState->at18) - { + if (pXSprite->stateTimer == 0 && pXSprite->aiState->at18) { if (pXSprite->aiState->at8 > 0) aiNewState(pSprite, pXSprite, pXSprite->aiState->at18); else if (seqGetStatus(3, nXSprite) < 0) aiNewState(pSprite, pXSprite, pXSprite->aiState->at18); } - if (pXSprite->health > 0 && ((pDudeInfo->hinderDamage<<4) <= cumulDamage[nXSprite])) - { + + if (pXSprite->health > 0 && ((pDudeInfo->hinderDamage << 4) <= cumulDamage[nXSprite])) { pXSprite->data3 = cumulDamage[nXSprite]; RecoilDude(pSprite, pXSprite); } + + if (pSprite->type >= kDudeVanillaMax) { + switch (pSprite->type) { + case kDudeModernCustom: + case kDudeModernCustomBurning: + GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; + if (pExtra->slaveCount > 0) updateTargetOfSlaves(pSprite); + if (pExtra->nLifeLeech >= 0) updateTargetOfLeech(pSprite); + break; + } + } + + } memset(cumulDamage, 0, sizeof(cumulDamage)); } @@ -1505,13 +1517,11 @@ void aiInitSprite(spritetype *pSprite) pDudeExtra->at4 = 0; pDudeExtra->at0 = 0; switch (pSprite->type) { - case kDudeModernCustom: - { + case kDudeModernCustom: { DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; - pDudeExtraE->at8 = 0; - pDudeExtraE->at0 = 0; - pDudeExtraE->at4 = 0; + pDudeExtraE->at8 = pDudeExtraE->at0 = 0; aiNewState(pSprite, pXSprite, &GDXGenDudeIdleL); + genDudePrepare(pSprite); break; } case kDudeModernCustomBurning: diff --git a/source/blood/src/ai.h b/source/blood/src/ai.h index 7843d7a06..e6f8789b1 100644 --- a/source/blood/src/ai.h +++ b/source/blood/src/ai.h @@ -103,6 +103,7 @@ void sub_5F15C(spritetype *pSprite, XSPRITE *pXSprite); void aiProcessDudes(void); void aiInit(void); void aiInitSprite(spritetype *pSprite); +bool CanMove(spritetype* pSprite, int a2, int nAngle, int nRange); // By NoOne: this function required for kModernDudeTargetChanger void aiSetGenIdleState(spritetype* pSprite, XSPRITE* pXSprite); diff --git a/source/blood/src/aicult.cpp b/source/blood/src/aicult.cpp index 317bfae86..086bc27f4 100644 --- a/source/blood/src/aicult.cpp +++ b/source/blood/src/aicult.cpp @@ -181,7 +181,7 @@ static void ThrowSeqCallback(int, int nXSprite) if (v4) xsprite[pMissile->extra].Impact = 1; else - evPost(pMissile->index, 3, 120*(1+Random(2)), kCmdOn); + evPost(pMissile->index, 3, 120*(1+Random(2)), kCmdOn, nSprite); } static void sub_68170(int, int nXSprite) @@ -194,7 +194,7 @@ static void sub_68170(int, int nXSprite) nMissile = kThingArmedTNTBundle; sfxPlay3DSound(pSprite, 455, -1, 0); spritetype *pMissile = actFireThing(pSprite, 0, 0, gDudeSlope[nXSprite]-9460, nMissile, 0x133333); - evPost(pMissile->index, 3, 120*(2+Random(2)), kCmdOn); + evPost(pMissile->index, 3, 120*(2+Random(2)), kCmdOn, nSprite); } static void sub_68230(int, int nXSprite) diff --git a/source/blood/src/aipod.cpp b/source/blood/src/aipod.cpp index b2af4f987..84ffe6ae8 100644 --- a/source/blood/src/aipod.cpp +++ b/source/blood/src/aipod.cpp @@ -126,7 +126,7 @@ static void sub_6FFA0(int, int nXSprite) sfxPlay3DSound(pSprite, 2474, -1, 0); else sfxPlay3DSound(pSprite, 2475, -1, 0); - pMissile = actFireThing(pSprite, 0, -8000, dz/128-14500, kThingPodFireBall, (nDist2<<23)/120); + pMissile = actFireThing(pSprite, 0, -8000, dz/128-14500, kThingPodGreenBall, (nDist2<<23)/120); } if (pMissile) seqSpawn(68, 3, pMissile->extra, -1); @@ -136,7 +136,7 @@ static void sub_6FFA0(int, int nXSprite) if (pDudeInfo->seeDist*0.1 < nDist) { sfxPlay3DSound(pSprite, 2454, -1, 0); - pMissile = actFireThing(pSprite, 0, -8000, dz/128-14500, kThingPodGreenBall, (nDist2<<23)/120); + pMissile = actFireThing(pSprite, 0, -8000, dz/128-14500, kThingPodFireBall, (nDist2<<23)/120); } if (pMissile) seqSpawn(22, 3, pMissile->extra, -1); diff --git a/source/blood/src/aiunicult.cpp b/source/blood/src/aiunicult.cpp index 803e6d0d0..a626ee08e 100644 --- a/source/blood/src/aiunicult.cpp +++ b/source/blood/src/aiunicult.cpp @@ -46,6 +46,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "trig.h" #include "triggers.h" #include "endgame.h" +#include "view.h" BEGIN_BLD_NS @@ -58,7 +59,6 @@ static void thinkSearch(spritetype*, XSPRITE*); static void thinkGoto(spritetype*, XSPRITE*); static void thinkChase(spritetype*, XSPRITE*); static void forcePunch(spritetype*, XSPRITE*); -static void thinkTransform(spritetype*, XSPRITE*); static int nGDXGenDudeAttack1 = seqRegisterClient(GDXCultistAttack1); static int nGDXGenDudePunch = seqRegisterClient(punchCallback); @@ -107,6 +107,8 @@ GENDUDESND gCustomDudeSnd[] = { { 9008, 0, 17, false }, // transforming in other dude }; +GENDUDEEXTRA gGenDudeExtra[kMaxSprites]; + static void forcePunch(spritetype* pSprite, XSPRITE* pXSprite) { // Required for those who don't have fire trigger in punch seq and for default animation @@ -123,6 +125,12 @@ static void forcePunch(spritetype* pSprite, XSPRITE* pXSprite) { } +void genDudeUpdate(spritetype* pSprite) { + for (int i = 0; i < kGenDudePropertyMax; i++) { + if (gGenDudeExtra[pSprite->index].updReq[i]) + genDudePrepare(pSprite, i); + } +} static void punchCallback(int, int nXIndex) { XSPRITE* pXSprite = &xsprite[nXIndex]; @@ -141,7 +149,7 @@ static void punchCallback(int, int nXIndex) { int dy = Sin(pSprite->ang) >> 16; int dz = nZOffset1 - nZOffset2; - if (!sfxPlayGDXGenDudeSound(pSprite, 9)) + if (!sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndAttackMelee)) sfxPlay3DSound(pSprite, 530, 1, 0); actFireVector(pSprite, 0, 0, dx, dy, dz,VECTOR_TYPE_22); @@ -149,79 +157,76 @@ static void punchCallback(int, int nXIndex) { } static void GDXCultistAttack1(int, int nXIndex) { - XSPRITE* pXSprite = &xsprite[nXIndex]; - int nSprite = pXSprite->reference; - spritetype* pSprite = &sprite[nSprite]; - int dx, dy, dz; int weapon = pXSprite->data1; - if (weapon >= 0 && weapon < kVectorMax) { - - int vector = pXSprite->data1; - dx = Cos(pSprite->ang) >> 16; - dy = Sin(pSprite->ang) >> 16; - dz = gDudeSlope[nXIndex]; + if (!(nXIndex >= 0 && nXIndex < kMaxXSprites)) { + consoleSysMsg("nXIndex >= 0 && nXIndex < kMaxXSprites"); + return; + } + + XSPRITE* pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; + + if (!(nSprite >= 0 && nSprite < kMaxSprites)) { + consoleSysMsg("nIndex >= 0 && nIndex < kMaxSprites"); + return; + } - VECTORDATA* pVectorData = &gVectorData[vector]; + spritetype* pSprite = &sprite[nSprite]; int dx, dy, dz; + xvel[pSprite->index] = yvel[pSprite->index] = 0; + + short curWeapon = gGenDudeExtra[nSprite].curWeapon; + short disperion = gGenDudeExtra[nSprite].baseDispersion; + + if (curWeapon >= 0 && curWeapon < kVectorMax) { + + dx = Cos(pSprite->ang) >> 16; dy = Sin(pSprite->ang) >> 16; dz = gDudeSlope[nXIndex]; + + VECTORDATA* pVectorData = &gVectorData[curWeapon]; int vdist = pVectorData->maxDist; // dispersal modifiers here in case if non-melee enemy if (vdist <= 0 || vdist > 1280) { - dx += Random3(3000 - 1000 * gGameOptions.nDifficulty); - dy += Random3(3000 - 1000 * gGameOptions.nDifficulty); - dz += Random3(1000 - 500 * gGameOptions.nDifficulty); + dx += Random3(disperion); dy += Random3(disperion); + dz += Random3(disperion); } - actFireVector(pSprite, 0, 0, dx, dy, dz,(VECTOR_TYPE)vector); - if (!sfxPlayGDXGenDudeSound(pSprite,7)) - sfxPlayVectorSound(pSprite,vector); + actFireVector(pSprite, 0, 0, dx, dy, dz,(VECTOR_TYPE)curWeapon); + if (!sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndAttackNormal)) + sfxPlayVectorSound(pSprite, curWeapon); - } else if (weapon >= kDudeBase && weapon < kDudeMax) { + } else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { - spritetype* pSpawned = NULL; int dist = pSprite->clipdist * 6; - if ((pSpawned = actSpawnDude(pSprite, weapon, dist, 0)) == NULL) - return; - - gDudeExtra[pSprite->extra].at6.u1.at4++; - pSpawned->owner = nSprite; - pSpawned->x += dist + (Random3(dist)); - if (pSpawned->extra > -1) { - xsprite[pSpawned->extra].target = pXSprite->target; - if (pXSprite->target > -1) - aiActivateDude(pSpawned, &xsprite[pSpawned->extra]); - } - gKillMgr.sub_263E0(1); - - if (!sfxPlayGDXGenDudeSound(pSprite, 7)) - sfxPlay3DSoundCP(pSprite, 379, 1, 0, 0x10000 - Random3(0x3000)); - - if (Chance(0x5500)) { - int state = checkAttackState(pSprite, pXSprite); - switch (state) { - case 1: - aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeW); - break; - case 2: - aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeD); - break; - default: - aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeL); - break; + spritetype* pSpawned = NULL; int dist = pSprite->clipdist << 4; + short slaveCnt = gGenDudeExtra[pSprite->index].slaveCount; + if (slaveCnt <= gGameOptions.nDifficulty && (pSpawned = actSpawnDude(pSprite, curWeapon, dist + Random(dist), 0)) != NULL) { + + pSpawned->owner = nSprite; + + if (pSpawned->extra > -1) { + xsprite[pSpawned->extra].target = pXSprite->target; + if (pXSprite->target > -1) + aiActivateDude(pSpawned, &xsprite[pSpawned->extra]); } + + gKillMgr.sub_263E0(1); + gGenDudeExtra[pSprite->index].slave[slaveCnt] = pSpawned->index; + gGenDudeExtra[pSprite->index].slaveCount++; + + if (!sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndAttackNormal)) + sfxPlay3DSoundCP(pSprite, 379, 1, 0, 0x10000 - Random3(0x3000)); } - } else if (weapon >= kMissileBase && weapon < kMissileMax) { + } else if (curWeapon >= kMissileBase && curWeapon < kMissileMax) { dx = Cos(pSprite->ang) >> 16; dy = Sin(pSprite->ang) >> 16; dz = gDudeSlope[nXIndex]; // dispersal modifiers here - dx += Random3(3000 - 1000 * gGameOptions.nDifficulty); - dy += Random3(3000 - 1000 * gGameOptions.nDifficulty); - dz += Random3(1000 - 500 * gGameOptions.nDifficulty); + dx += Random3(disperion); dy += Random3(disperion); + dz += Random3(disperion >> 1); - actFireMissile(pSprite, 0, 0, dx, dy, dz, weapon); - if (!sfxPlayGDXGenDudeSound(pSprite,7)) - sfxPlayMissileSound(pSprite, weapon); + actFireMissile(pSprite, 0, 0, dx, dy, dz, curWeapon); + if (!sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndAttackNormal)) + sfxPlayMissileSound(pSprite, curWeapon); } } @@ -230,13 +235,11 @@ static void ThrowCallback1(int, int nXIndex) { } static void ThrowCallback2(int, int nXIndex) { - ThrowThing(nXIndex, true); + ThrowThing(nXIndex, false); } static void ThrowThing(int nXIndex, bool impact) { - XSPRITE* pXSprite = &xsprite[nXIndex]; - int nSprite = pXSprite->reference; - spritetype* pSprite = &sprite[nSprite]; + XSPRITE* pXSprite = &xsprite[nXIndex]; spritetype* pSprite = &sprite[pXSprite->reference]; if (!(pXSprite->target >= 0 && pXSprite->target < kMaxSprites)) return; @@ -245,171 +248,170 @@ static void ThrowThing(int nXIndex, bool impact) { if (!(pTarget->type >= kDudeBase && pTarget->type < kDudeMax)) return; + short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon; + if (curWeapon < kThingBase || curWeapon >= kThingMax) + return; - int thingType = pXSprite->data1; - if (thingType >= kThingBase && thingType < kThingMax) { + THINGINFO* pThinkInfo = &thingInfo[curWeapon - kThingBase]; + if (!pThinkInfo->allowThrow) return; - THINGINFO* pThinkInfo = &thingInfo[thingType - kThingBase]; - if (pThinkInfo->allowThrow) { + if (!sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndAttackThrow)) + sfxPlay3DSound(pSprite, 455, -1, 0); + + int zThrow = 14500; + int dx = pTarget->x - pSprite->x; + int dy = pTarget->y - pSprite->y; + int dz = pTarget->z - pSprite->z; + int dist = approxDist(dx, dy); + + spritetype* pLeech = leechIsDropped(pSprite); + XSPRITE* pXLeech = (pLeech != NULL) ? &xsprite[pLeech->extra] : NULL; + + switch (curWeapon) { + case kModernThingEnemyLifeLeech: + case kThingDroppedLifeLeech: + zThrow = 5000; + // pickup life leech before throw it again + if (pLeech != NULL) removeLeech(pLeech); + break; + } - if (!sfxPlayGDXGenDudeSound(pSprite, 8)) - sfxPlay3DSound(pSprite, 455, -1, 0); + spritetype* pThing = NULL; + if ((pThing = actFireThing(pSprite, 0, 0, (dz / 128) - zThrow, curWeapon, divscale(dist / 540, 120, 23))) == NULL) return; + else if (pThinkInfo->picnum < 0 && pThing->type != kModernThingThrowableRock) pThing->picnum = 0; + + pThing->owner = pSprite->xvel; + + switch (curWeapon) { + case kThingNapalmBall: + pThing->xrepeat = pThing->yrepeat = 24; + xsprite[pThing->extra].data4 = 3 + gGameOptions.nDifficulty; + impact = true; + break; + case kModernThingThrowableRock: + int sPics[6]; + sPics[0] = 2406; sPics[1] = 2280; + sPics[2] = 2185; sPics[3] = 2155; + sPics[4] = 2620; sPics[5] = 3135; - int dx = pTarget->x - pSprite->x; - int dy = pTarget->y - pSprite->y; - int dz = pTarget->z - pSprite->z; + pThing->picnum = sPics[Random(5)]; + pThing->xrepeat = pThing->yrepeat = 24 + Random(42); + pThing->cstat |= 0x0001; + pThing->pal = 5; - int dist = approxDist(dx, dy); int zThrow = 14500; - spritetype* pThing = NULL; spritetype* pLeech = NULL; XSPRITE* pXLeech = NULL; - if (thingType == kModernThingEnemyLifeLeech) { - if ((pLeech = leechIsDropped(pSprite)) != NULL) { - // pickup life leech before throw it again - pXLeech = &xsprite[pLeech->extra]; - removeLeech(pLeech); - } + if (Chance(0x5000)) pThing->cstat |= 0x0004; + if (Chance(0x5000)) pThing->cstat |= 0x0008; - zThrow = 5000; - } - - pThing = actFireThing(pSprite, 0, 0, (dz / 128) - zThrow, thingType, divscale(dist / 540, 120, 23)); - if (pThing == NULL) return; - - if (pThinkInfo->picnum < 0 && pThing->type != kModernThingThrowableRock) pThing->picnum = 0; - pThing->owner = pSprite->xvel; - switch (thingType) { - case kThingNapalmBall: - impact = true; - pThing->xrepeat = 24; - pThing->yrepeat = 24; - xsprite[pThing->extra].data4 = 3 + gGameOptions.nDifficulty; - break; - - case kModernThingThrowableRock: - int sPics[6]; - sPics[0] = 2406; sPics[1] = 2280; - sPics[2] = 2185; sPics[3] = 2155; - sPics[4] = 2620; sPics[5] = 3135; - - pThing->picnum = sPics[Random(5)]; - pThing->pal = 5; - pThing->xrepeat = 24 + Random(42); - pThing->yrepeat = 24 + Random(42); - pThing->cstat |= 0x0001; - - if (Chance(0x5000)) pThing->cstat |= 0x0004; - if (Chance(0x5000)) pThing->cstat |= 0x0008; - - if (pThing->xrepeat > 60) xsprite[pThing->extra].data1 = 43; - else if (pThing->xrepeat > 40) xsprite[pThing->extra].data1 = 33; - else if (pThing->xrepeat > 30) xsprite[pThing->extra].data1 = 23; - else xsprite[pThing->extra].data1 = 12; - - impact = false; - return; - case kThingTNTBarrel: - case kThingArmedProxBomb: - case kThingArmedSpray: - impact = false; - break; - case kModernThingTNTProx: - xsprite[pThing->extra].state = 0; - xsprite[pThing->extra].Proximity = true; - return; - case kThingDroppedLifeLeech: - case kModernThingEnemyLifeLeech: - XSPRITE* pXThing = &xsprite[pThing->extra]; - if (pLeech != NULL) pXThing->health = pXLeech->health; - else pXThing->health = 300 * gGameOptions.nDifficulty; - - sfxPlay3DSound(pSprite, 490, -1, 0); - - if (gGameOptions.nDifficulty <= 2) pXThing->data3 = 32700; - else pXThing->data3 = Random(10); - pThing->pal = 6; - pXThing->target = pTarget->xvel; - pXThing->Proximity = true; - pXThing->stateTimer = 1; - evPost(pThing->xvel, 3, 80, kCallbackLeechStateTimer); - return; - } - - if (impact == true && dist <= 7680) xsprite[pThing->extra].Impact = true; - else { - xsprite[pThing->extra].Impact = false; - evPost(pThing->xvel, 3, 120 * Random(2) + 120, kCmdOn); - } + if (pThing->xrepeat > 60) xsprite[pThing->extra].data1 = 43; + else if (pThing->xrepeat > 40) xsprite[pThing->extra].data1 = 33; + else if (pThing->xrepeat > 30) xsprite[pThing->extra].data1 = 23; + else xsprite[pThing->extra].data1 = 12; return; - } + case kThingTNTBarrel: + case kThingArmedProxBomb: + case kThingArmedSpray: + impact = false; + break; + case kModernThingTNTProx: + xsprite[pThing->extra].state = 0; + xsprite[pThing->extra].Proximity = true; + return; + case kModernThingEnemyLifeLeech: + XSPRITE* pXThing = &xsprite[pThing->extra]; + if (pLeech != NULL) pXThing->health = pXLeech->health; + else pXThing->health = 300 * gGameOptions.nDifficulty; + sfxPlay3DSound(pSprite, 490, -1, 0); + + if (gGameOptions.nDifficulty <= 2) pXThing->data3 = 32700; + else pXThing->data3 = Random(10); + pThing->cstat &= ~CSTAT_SPRITE_BLOCK; + pThing->pal = 6; + pXThing->target = pTarget->xvel; + pXThing->Proximity = true; + pXThing->stateTimer = 1; + + gGenDudeExtra[pSprite->index].nLifeLeech = pThing->index; + evPost(pThing->xvel, 3, 80, kCallbackLeechStateTimer); + return; + } + + if (impact == true && dist <= 7680) xsprite[pThing->extra].Impact = true; + else { + xsprite[pThing->extra].Impact = false; + evPost(pThing->xvel, 3, 120 * Random(2) + 120, kCmdOn, pXSprite->reference); } } -static void thinkSearch( spritetype* pSprite, XSPRITE* pXSprite ) -{ - aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); +static void thinkSearch( spritetype* pSprite, XSPRITE* pXSprite ) { + + int velocity = ClipLow(pSprite->clipdist / 2, 1); + aiGenDudeChooseDirection(pSprite, pXSprite, pXSprite->goalAng, velocity, velocity); sub_5F15C(pSprite, pXSprite); } -static void thinkGoto( spritetype* pSprite, XSPRITE* pXSprite ) -{ - int dx, dy, dist; - dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); - DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; +static void thinkGoto(spritetype* pSprite, XSPRITE* pXSprite) { - dx = pXSprite->targetX - pSprite->x; - dy = pXSprite->targetY - pSprite->y; + if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { + consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); + return; + } + int dx = pXSprite->targetX - pSprite->x; + int dy = pXSprite->targetY - pSprite->y; int nAngle = getangle(dx, dy); - dist = approxDist(dx, dy); - aiChooseDirection(pSprite, pXSprite, nAngle); + int velocity = ClipLow(pSprite->clipdist / 2, 1); + aiGenDudeChooseDirection(pSprite, pXSprite, nAngle, velocity, velocity); // if reached target, change to search mode - if (dist < 5120 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery ) { - if(spriteIsUnderwater(pSprite,false)) - aiNewState(pSprite, pXSprite, &GDXGenDudeSearchW); - else - aiNewState(pSprite, pXSprite, &GDXGenDudeSearchL); + if (approxDist(dx, dy) < 5120 && klabs(pSprite->ang - nAngle) < dudeInfo[pSprite->type - kDudeBase].periphery) { + if (spriteIsUnderwater(pSprite, false)) aiNewState(pSprite, pXSprite, &GDXGenDudeSearchW); + else aiNewState(pSprite, pXSprite, &GDXGenDudeSearchL); } aiThinkTarget(pSprite, pXSprite); } -static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) -{ +static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { - if (pXSprite->target == -1) { + if (pSprite->type < kDudeBase || pSprite->type >= kDudeMax) return; + else if (pXSprite->target < 0 || pXSprite->target >= kMaxSprites) { if(spriteIsUnderwater(pSprite,false)) aiNewState(pSprite, pXSprite, &GDXGenDudeGotoW); else aiNewState(pSprite, pXSprite, &GDXGenDudeGotoL); return; - } dassert(pXSprite->target < kMaxSprites); - - - int dx, dy, dist; DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; - + } else { + + genDudeUpdate(pSprite); + + } + spritetype* pTarget = &sprite[pXSprite->target]; XSPRITE* pXTarget = (!IsDudeSprite(pTarget) || pTarget->extra < 0) ? NULL : &xsprite[pTarget->extra]; - // check target - dx = pTarget->x - pSprite->x; - dy = pTarget->y - pSprite->y; - aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); - - if ( pXTarget == NULL || pXTarget->health <= 0 ) - { + if (pXTarget == NULL || pXTarget->health <= 0) { // target is dead - if(spriteIsUnderwater(pSprite,false)) - aiNewState(pSprite, pXSprite, &GDXGenDudeSearchW); + if(spriteIsUnderwater(pSprite,false)) aiNewState(pSprite, pXSprite, &GDXGenDudeSearchW); else { aiNewState(pSprite, pXSprite, &GDXGenDudeSearchL); - sfxPlayGDXGenDudeSound(pSprite,5); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndTargetDead); } return; } + + // check target + int dx = pTarget->x - pSprite->x; int dy = pTarget->y - pSprite->y; + int dist = ClipLow((int)approxDist(dx, dy), 1); - if (IsPlayerSprite(pTarget)) - { + // quick hack to prevent spinning around or changing attacker's sprite angle on high movement speeds + // when attacking the target. It happens because vanilla function takes in account x and y velocity, + // so i use fake velocity with fixed value and pass it as argument. + int velocity = ClipLow(pSprite->clipdist / 2, 1); + aiGenDudeChooseDirection(pSprite, pXSprite, getangle(dx, dy), velocity, velocity); + // aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); + + if (IsPlayerSprite(pTarget)) { PLAYER* pPlayer = &gPlayer[ pTarget->type - kDudePlayer1 ]; if (powerupCheck(pPlayer, kPwUpShadowCloak) > 0) { if(spriteIsUnderwater(pSprite,false)) aiNewState(pSprite, pXSprite, &GDXGenDudeSearchW); @@ -418,8 +420,8 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) } } - dist = (int) approxDist(dx, dy); if (dist == 0) dist = 1; - int eyeAboveZ = pDudeInfo->eyeHeight * pSprite->yrepeat << 2; + DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; + int eyeAboveZ = pDudeInfo->eyeHeight * pSprite->yrepeat << 2; int seeDist = getSeeDist(pSprite, 51200, 35000, 85000); if (dist > pDudeInfo->seeDist || !cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - eyeAboveZ, pSprite->sectnum)) { @@ -431,38 +433,36 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) int nAngle = getangle(dx, dy); int losAngle = ((kAng180 + nAngle - pSprite->ang) & 2047) - kAng180; + // is the target visible? if (dist < pDudeInfo->seeDist && klabs(losAngle) <= pDudeInfo->periphery) { - if (pXSprite->target < 0) aiSetTarget(pXSprite, pXSprite->target); if (((int)gFrameClock & 64) == 0 && Chance(0x3000) && !spriteIsUnderwater(pSprite, false)) - sfxPlayGDXGenDudeSound(pSprite, 6); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndChasing); gDudeSlope[sprite[pXSprite->reference].extra] = (int)divscale(pTarget->z - pSprite->z, dist, 10); + + short curWeapon = gGenDudeExtra[pSprite->index].curWeapon; + spritetype* pLeech = leechIsDropped(pSprite); VECTORDATA* meleeVector = &gVectorData[22]; - spritetype* pLeech = NULL; VECTORDATA* meleeVector = &gVectorData[22]; - if (pXSprite->data1 >= kThingBase && pXSprite->data1 < kThingMax) { - if (pXSprite->data1 == kThingDroppedLifeLeech) pXSprite->data1 = kModernThingEnemyLifeLeech; - if ((pLeech = leechIsDropped(pSprite)) != NULL && xsprite[pLeech->extra].target != pXSprite->target) - xsprite[pLeech->extra].target = pXSprite->target; + if (pExtra->updReq[kGenDudePropertyAttack]) genDudePrepare(pSprite, kGenDudePropertyAttack); + if (curWeapon >= kThingBase && curWeapon < kThingMax) { if (klabs(losAngle) < kAng15) { - if (dist < 12264 && dist > 7680 && !spriteIsUnderwater(pSprite, false) && pXSprite->data1 != kModernThingEnemyLifeLeech) { + if (dist < 12264 && dist > 7680 && !spriteIsUnderwater(pSprite, false) && curWeapon != kModernThingEnemyLifeLeech) { int pHit = HitScan(pSprite, pSprite->z, dx, dy, 0, 16777280, 0); switch (pHit) { - case 0: - case 4: - return; - default: - aiNewState(pSprite, pXSprite, &GDXGenDudeThrow); - return; + case 0: + case 4: + return; + default: + aiNewState(pSprite, pXSprite, &GDXGenDudeThrow); + return; } - } - else if (dist > 4072 && dist <= 9072 && !spriteIsUnderwater(pSprite, false) && pSprite->owner != (kMaxSprites - 1)) { - switch (pXSprite->data1) { - case kModernThingEnemyLifeLeech: - { + } else if (dist > 4072 && dist <= 9072 && !spriteIsUnderwater(pSprite, false) && pSprite->owner != (kMaxSprites - 1)) { + switch (curWeapon) { + case kModernThingEnemyLifeLeech: { if (pLeech == NULL) { aiNewState(pSprite, pXSprite, &GDXGenDudeThrow2); GDXGenDudeThrow2.at18 = &GDXGenDudeDodgeL; @@ -471,38 +471,35 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) XSPRITE* pXLeech = &xsprite[pLeech->extra]; int ldist = getTargetDist(pTarget, pDudeInfo, pLeech); - //System.out.println("LDIST: "+ldist); if (ldist > 3 || !cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pLeech->x, pLeech->y, pLeech->z, pLeech->sectnum) || pXLeech->target == -1) { aiNewState(pSprite, pXSprite, &GDXGenDudeThrow2); GDXGenDudeThrow2.at18 = &GDXGenDudeDodgeL; - } - else { + } else { + GDXGenDudeThrow2.at18 = &GDXGenDudeChaseL; - if (pXLeech->target != pXSprite->target) pXLeech->target = pXSprite->target; if (dist > 5072 && Chance(0x5000)) { if (!canDuck(pSprite) || Chance(0x4000)) aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeL); else aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeD); - } - else { + } else { aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); } + } } return; case kModernThingThrowableRock: if (Chance(0x4000)) aiNewState(pSprite, pXSprite, &GDXGenDudeThrow2); - else sfxPlayGDXGenDudeSound(pSprite, 0); + else sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndTargetSpot); return; default: aiNewState(pSprite, pXSprite, &GDXGenDudeThrow2); return; } - } - else if (dist <= meleeVector->maxDist) { + } else if (dist <= meleeVector->maxDist) { if (spriteIsUnderwater(pSprite, false)) { if (Chance(0x9000)) aiNewState(pSprite, pXSprite, &GDXGenDudePunch); @@ -513,8 +510,7 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) else aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeL); return; - } - else { + } else { int state = checkAttackState(pSprite, pXSprite); if (state == 1) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); else if (state == 2) { @@ -526,114 +522,83 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) } } - } - else { + } else { - int defDist = 17920; int vdist = defDist; + int defDist = gGenDudeExtra[pSprite->index].fireDist; int vdist = defDist; - if (pXSprite->data1 > 0 && pXSprite->data1 < kVectorMax) { - - switch (pXSprite->data1) { - case 19: - pXSprite->data1 = 2; - break; - } - - VECTORDATA* pVectorData = &gVectorData[pXSprite->data1]; - vdist = pVectorData->maxDist; - if (vdist <= 0 || vdist > defDist) + if (curWeapon > 0 && curWeapon < kVectorMax) { + if ((vdist = gVectorData[curWeapon].maxDist) <= 0 || vdist > defDist) vdist = defDist; - } - else if (pXSprite->data1 >= kDudeBase && pXSprite->data1 < kDudeMax) { + } else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { - if (gDudeExtra[pSprite->extra].at6.u1.at4 > 0) { - updateTargetOfSlaves(pSprite); - if (pXSprite->target >= 0 && sprite[pXSprite->target].owner == pSprite->xvel) { - aiSetTarget(pXSprite, pSprite->x, pSprite->y, pSprite->z); + // don't attack slaves + if (pXSprite->target >= 0 && sprite[pXSprite->target].owner == pSprite->xvel) { + aiSetTarget(pXSprite, pSprite->x, pSprite->y, pSprite->z); + return; + } else if (gGenDudeExtra[pSprite->index].slaveCount > gGameOptions.nDifficulty || dist < meleeVector->maxDist) { + if (dist <= meleeVector->maxDist) { + aiNewState(pSprite, pXSprite, &GDXGenDudePunch); + return; + } else { + int state = checkAttackState(pSprite, pXSprite); + if (state == 1) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); + else if (state == 2) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseD); + else aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); return; } } - int state = checkAttackState(pSprite, pXSprite); - if (gDudeExtra[pSprite->extra].at6.u1.at4 <= gGameOptions.nDifficulty && dist > meleeVector->maxDist) { - vdist = (vdist / 2) + Random(vdist / 2); - } - else if (dist <= meleeVector->maxDist) { - aiNewState(pSprite, pXSprite, &GDXGenDudePunch); - return; - } - else { - - if (state == 1) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); - else if (state == 2) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseD); - else aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); - return; - } - - - - } - else if (pXSprite->data1 >= kMissileBase && pXSprite->data1 < kMissileMax) { + } else if (curWeapon >= kMissileBase && curWeapon < kMissileMax) { // special handling for flame, explosive and life leech missiles int state = checkAttackState(pSprite, pXSprite); - int mdist = (pXSprite->data1 != kMissileFlareAlt) ? 3000 : 2500; - switch (pXSprite->data1) { - case 315: - // pickup life leech if it was thrown previously - if ((pLeech = leechIsDropped(pSprite)) != NULL) removeLeech(pLeech); - break; - case kMissileFlareAlt: - case kMissileFireball: - case kMissileFireballNapam: - case kMissileFireballCerberus: - case kMissileFireballTchernobog: - if (dist > mdist || pXSprite->locked == 1) break; - else if (dist <= meleeVector->maxDist && Chance(0x9000)) - aiNewState(pSprite, pXSprite, &GDXGenDudePunch); - else if (state == 1) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); - else if (state == 2) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseD); - else aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); - return; - - - case kMissileFlameSpray: - case kMissileFlameHound: - if (spriteIsUnderwater(pSprite, false)) { - if (dist > meleeVector->maxDist) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); - else if (Chance(0x8000)) aiNewState(pSprite, pXSprite, &GDXGenDudePunch); - else aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeW); + int mdist = (curWeapon != kMissileFlareAlt) ? 3000 : 2500; + switch (curWeapon) { + case kMissileLifeLeechRegular: + // pickup life leech if it was thrown previously + if (pLeech != NULL) removeLeech(pLeech); + break; + case kMissileFlareAlt: + case kMissileFireball: + case kMissileFireballNapam: + case kMissileFireballCerberus: + case kMissileFireballTchernobog: + if (dist > mdist || pXSprite->locked == 1) break; + else if (dist <= meleeVector->maxDist && Chance(0x9000)) + aiNewState(pSprite, pXSprite, &GDXGenDudePunch); + else if (state == 1) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); + else if (state == 2) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseD); + else aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); return; + case kMissileFlameSpray: + case kMissileFlameHound: + if (spriteIsUnderwater(pSprite, false)) { + if (dist > meleeVector->maxDist) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); + else if (Chance(0x8000)) aiNewState(pSprite, pXSprite, &GDXGenDudePunch); + else aiNewState(pSprite, pXSprite, &GDXGenDudeDodgeW); + return; } vdist = 4200; if (((int)gFrameClock & 16) == 0) vdist += Random(800); break; } - - } - else if (pXSprite->data1 >= kTrapExploder && pXSprite->data1 < (kTrapExploder + kExplodeMax) - 1) { - - int nType = pXSprite->data1 - kTrapExploder; EXPLOSION* pExpl = &explodeInfo[nType]; - if (pExpl != NULL && CheckProximity(pSprite, pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pExpl->radius / 2) - && doExplosion(pSprite, nType)) { - - pXSprite->health = 1; - actDamageSprite(pSprite->xvel, pSprite, DAMAGE_TYPE_3, 65535); - - xvel[pSprite->xvel] = 0; - zvel[pSprite->xvel] = 0; - yvel[pSprite->xvel] = 0; + } else if (curWeapon >= kTrapExploder && curWeapon < (kTrapExploder + kExplodeMax) - 1) { + int nType = curWeapon - kTrapExploder; EXPLOSION* pExpl = &explodeInfo[nType]; + if (CheckProximity(pSprite, pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pExpl->radius / 2)) { + + xvel[pSprite->xvel] = zvel[pSprite->xvel] = yvel[pSprite->xvel] = 0; + + if (doExplosion(pSprite, nType) && pXSprite->health > 0) + actDamageSprite(pSprite->xvel, pSprite, DAMAGE_TYPE_3, 65535); } return; - - // scared dude - no weapon. Still can punch you sometimes. - } - else { + // scared dude - no weapon. Still can punch you sometimes. + } else { int state = checkAttackState(pSprite, pXSprite); if (Chance(0x0500) && !spriteIsUnderwater(pSprite, false)) - sfxPlayGDXGenDudeSound(pSprite, 6); + sfxPlayGDXGenDudeSound(pSprite, kGenDudeSndChasing); if (Chance(0x0200)) { if (dist <= meleeVector->maxDist) aiNewState(pSprite, pXSprite, &GDXGenDudePunch); @@ -648,47 +613,45 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) return; } - if (dist <= vdist && pXSprite->aiState == &GDXGenDudeChaseD) aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); int state = checkAttackState(pSprite, pXSprite); - if (dist < vdist && /*klabs(losAngle) < 32*/ klabs(losAngle) < kAng5) { + + int kAngle = (dist <= 2048) ? kAngle = pDudeInfo->periphery : kAng5; + if (dist < vdist && klabs(losAngle) < kAngle) { switch (state) { - case 1: - aiNewState(pSprite, pXSprite, &GDXGenDudeFireW); - pXSprite->aiState->at18 = &GDXGenDudeFireW; - return; - case 2: - aiNewState(pSprite, pXSprite, &GDXGenDudeFireD); - pXSprite->aiState->at18 = &GDXGenDudeFireD; - return; - default: - aiNewState(pSprite, pXSprite, &GDXGenDudeFireL); - pXSprite->aiState->at18 = &GDXGenDudeFireL; - return; + case 1: + aiNewState(pSprite, pXSprite, &GDXGenDudeFireW); + pXSprite->aiState->at18 = &GDXGenDudeFireW; + return; + case 2: + aiNewState(pSprite, pXSprite, &GDXGenDudeFireD); + pXSprite->aiState->at18 = &GDXGenDudeFireD; + return; + default: + aiNewState(pSprite, pXSprite, &GDXGenDudeFireL); + pXSprite->aiState->at18 = &GDXGenDudeFireL; + return; } - } - else { + } else { if (seqGetID(3, pSprite->extra) == pXSprite->data2 + (state < 3) ? 8 : 6) { if (state == 1) pXSprite->aiState->at18 = &GDXGenDudeChaseW; else if (state == 2) pXSprite->aiState->at18 = &GDXGenDudeChaseD; else pXSprite->aiState->at18 = &GDXGenDudeChaseL; - } - else if (state == 1 && pXSprite->aiState != &GDXGenDudeChaseW && pXSprite->aiState != &GDXGenDudeFireW) { + + } else if (state == 1 && pXSprite->aiState != &GDXGenDudeChaseW && pXSprite->aiState != &GDXGenDudeFireW) { aiNewState(pSprite, pXSprite, &GDXGenDudeChaseW); pXSprite->aiState->at18 = &GDXGenDudeFireW; - } - else if (state == 2 && pXSprite->aiState != &GDXGenDudeChaseD && pXSprite->aiState != &GDXGenDudeFireD) { + } else if (state == 2 && pXSprite->aiState != &GDXGenDudeChaseD && pXSprite->aiState != &GDXGenDudeFireD) { aiNewState(pSprite, pXSprite, &GDXGenDudeChaseD); pXSprite->aiState->at18 = &GDXGenDudeFireD; - } - else if (pXSprite->aiState != &GDXGenDudeChaseL && pXSprite->aiState != &GDXGenDudeFireL) { + } else if (pXSprite->aiState != &GDXGenDudeChaseL && pXSprite->aiState != &GDXGenDudeFireL) { aiNewState(pSprite, pXSprite, &GDXGenDudeChaseL); pXSprite->aiState->at18 = &GDXGenDudeFireL; } @@ -756,7 +719,7 @@ int getGenDudeMoveSpeed(spritetype* pSprite,int which, bool mul, bool shift) { if (pXSprite->busyTime > 0) speed /=3; if (speed > 0 && mul) { - //System.err.println(pXSprite.busyTime); + if (pXSprite->busyTime > 0) speed += (step * pXSprite->busyTime); } @@ -782,14 +745,48 @@ void aiGenDudeMoveForward(spritetype* pSprite, XSPRITE* pXSprite ) { int sin = Sin(pSprite->ang); int cos = Cos(pSprite->ang); - int frontSpeed = getGenDudeMoveSpeed(pSprite,0,true,false); + int frontSpeed = gGenDudeExtra[pSprite->index].frontSpeed; xvel[pSprite->xvel] += mulscale(cos, frontSpeed, 30); yvel[pSprite->xvel] += mulscale(sin, frontSpeed, 30); } + +void aiGenDudeChooseDirection(spritetype* pSprite, XSPRITE* pXSprite, int a3, int xvel, int yvel) { + if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { + consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); + return; + } + + int vc = ((a3 + 1024 - pSprite->ang) & 2047) - 1024; + int t1 = dmulscale30(xvel, Cos(pSprite->ang), yvel, Sin(pSprite->ang)); + int vsi = ((t1 * 15) >> 12) / 2; int v8 = (vc >= 0) ? 341 : -341; + + if (CanMove(pSprite, pXSprite->target, pSprite->ang + vc, vsi)) + pXSprite->goalAng = pSprite->ang + vc; + else if (CanMove(pSprite, pXSprite->target, pSprite->ang + vc / 2, vsi)) + pXSprite->goalAng = pSprite->ang + vc / 2; + else if (CanMove(pSprite, pXSprite->target, pSprite->ang - vc / 2, vsi)) + pXSprite->goalAng = pSprite->ang - vc / 2; + else if (CanMove(pSprite, pXSprite->target, pSprite->ang + v8, vsi)) + pXSprite->goalAng = pSprite->ang + v8; + else if (CanMove(pSprite, pXSprite->target, pSprite->ang, vsi)) + pXSprite->goalAng = pSprite->ang; + else if (CanMove(pSprite, pXSprite->target, pSprite->ang - v8, vsi)) + pXSprite->goalAng = pSprite->ang - v8; + else + pXSprite->goalAng = pSprite->ang + 341; + + pXSprite->dodgeDir = (Chance(0x8000)) ? 1 : -1; + + if (!CanMove(pSprite, pXSprite->target, pSprite->ang + pXSprite->dodgeDir * 512, 512)) { + pXSprite->dodgeDir = -pXSprite->dodgeDir; + if (!CanMove(pSprite, pXSprite->target, pSprite->ang + pXSprite->dodgeDir * 512, 512)) + pXSprite->dodgeDir = 0; + } +} bool sfxPlayGDXGenDudeSound(spritetype* pSprite, int mode) { - if (mode < 0 || mode >= kMaxGenDudeSndMode) return false; + if (mode < kGenDudeSndTargetSpot || mode >= kGenDudeSndMax) return false; GENDUDESND* sndInfo =& gCustomDudeSnd[mode]; bool gotSnd = false; short sndStartId = xsprite[pSprite->extra].sysData1; int rand = sndInfo->randomRange; int sndId = (sndStartId <= 0) ? sndInfo->defaultSndId : sndStartId + sndInfo->sndIdOffset; @@ -840,15 +837,12 @@ bool spriteIsUnderwater(spritetype* pSprite,bool oldWay) { return false; } - + spritetype* leechIsDropped(spritetype* pSprite) { - for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { - if (sprite[nSprite].type == kModernThingEnemyLifeLeech && sprite[nSprite].owner == pSprite->xvel) - return &sprite[nSprite]; - } - + short nLeech = gGenDudeExtra[pSprite->index].nLifeLeech; + if (nLeech >= 0 && nLeech < kMaxSprites) return &sprite[nLeech]; return NULL; - + } void removeDudeStuff(spritetype* pSprite) { @@ -883,11 +877,18 @@ void removeLeech(spritetype* pLeech, bool delSprite) { pEffect->xrepeat = repeat; pEffect->yrepeat = repeat; } + sfxPlay3DSoundCP(pLeech, 490, -1, 0,60000); + + if (pLeech->owner >= 0 && pLeech->owner < kMaxSprites) + gGenDudeExtra[sprite[pLeech->owner].index].nLifeLeech = -1; + if (delSprite) { pLeech->type = kSpriteDecoration; actPostSprite(pLeech->index, kStatFree); } + + } } @@ -895,6 +896,9 @@ void killDudeLeech(spritetype* pLeech) { if (pLeech != NULL) { actDamageSprite(pLeech->owner, pLeech, DAMAGE_TYPE_3, 65535); sfxPlay3DSoundCP(pLeech, 522, -1, 0, 60000); + + if (pLeech->owner >= 0 && pLeech->owner < kMaxSprites) + gGenDudeExtra[sprite[pLeech->owner].index].nLifeLeech = -1; } } @@ -915,30 +919,217 @@ XSPRITE* getNextIncarnation(XSPRITE* pXSprite) { } bool dudeIsMelee(XSPRITE* pXSprite) { - int meleeDist = 2048; int vdist = meleeDist; - if (pXSprite->data1 >= 0 && pXSprite->data1 < kVectorMax) { - int vector = pXSprite->data1; if (vector <= 0) vector = 2; - VECTORDATA pVectorData = gVectorData[vector]; - vdist = pVectorData.maxDist; + return gGenDudeExtra[sprite[pXSprite->reference].index].isMelee; +} - if (vdist > 0 && vdist <= meleeDist) - return true; +void scaleDamage(XSPRITE* pXSprite) { - } else { + short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon; + short* curScale = gGenDudeExtra[sprite[pXSprite->reference].index].dmgControl; + for (int i = 0; i < kDmgMax; i++) + curScale[i] = dudeInfo[kDudeModernCustom - kDudeBase].startDamage[i]; - if (pXSprite->data1 >= kTrapExploder && pXSprite->data1 < (kTrapExploder + kExplodeMax) - 1) - return true; + if (curWeapon > 0 && curWeapon < kVectorMax) { - /*switch (pXSprite->data1) { + switch (gVectorData[curWeapon].dmgType) { + case kDmgFall: + curScale[kDmgFall] = 64 + Random(64); + break; + case kDmgBurn: + curScale[kDmgBurn] = 64; + curScale[kDmgExplode] = 82; + break; + case kDmgBullet: + curScale[kDmgBullet] = 82 + Random(28); + break; + case kDmgExplode: + curScale[kDmgBurn] = 82; + curScale[kDmgExplode] = 64; + break; + case kDmgChoke: + curScale[kDmgChoke] = 16 + Random(16); + break; + case kDmgSpirit: + curScale[kDmgSpirit] = 32 + Random(10); + break; + case kDmgElectric: + curScale[kDmgElectric] = 64 + Random(16); + break; + } + + // just copy damage resistance of dude that should be summoned + } else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { + + for (int i = 0; i < kDmgMax; i++) + curScale[i] = dudeInfo[curWeapon - kDudeBase].startDamage[i]; + + // these does not like the explosions and burning + } else if (curWeapon >= kTrapExploder && curWeapon < (kTrapExploder + kExplodeMax) - 1) { + + curScale[kDmgBurn] = 255; curScale[kDmgExplode] = 512; + + } else if (curWeapon >= kMissileBase && curWeapon < kThingMax) { + + switch (curWeapon) { + case kMissileButcherKnife: + curScale[kDmgBullet] = 100; + fallthrough__; + case kMissileEctoSkull: + curScale[kDmgSpirit] = 32; + break; + case kMissileLifeLeechAltNormal: + case kMissileLifeLeechAltSmall: + case kMissileArcGargoyle: + curScale[kDmgSpirit] = 32; + curScale[kDmgElectric] = 52; + break; + case kMissileFlareRegular: + case kMissileFlareAlt: case kMissileFlameSpray: case kMissileFlameHound: - return true; - default: - return false; - }*/ + case kThingArmedSpray: + case kThingPodFireBall: + case kThingNapalmBall: + curScale[kDmgBurn] = 32; + curScale[kDmgExplode] = 50; + break; + case kMissileLifeLeechRegular: + case kThingDroppedLifeLeech: + case kModernThingEnemyLifeLeech: + curScale[kDmgSpirit] = 32 + Random(18); + curScale[kDmgBurn] = 60 + Random(4); + for (int i = 2; i < kDmgMax; i++) { + if (Chance(0x1000) && i != kDmgSpirit) + curScale[i] = 48 + Random(32); + } + break; + case kMissileFireball: + case kMissileFireballNapam: + case kMissileFireballCerberus: + case kMissileFireballTchernobog: + curScale[kDmgBurn] = 50; + curScale[kDmgExplode] = 32; + curScale[kDmgFall] = 65 + Random(15); + break; + case kThingTNTBarrel: + case kThingArmedProxBomb: + case kThingArmedRemoteBomb: + case kThingArmedTNTBundle: + case kThingArmedTNTStick: + case kModernThingTNTProx: + curScale[kDmgExplode] = 32; + curScale[kDmgFall] = 65 + Random(15); + break; + case kMissileTeslaAlt: + case kMissileTeslaRegular: + curScale[kDmgElectric] = 32 + Random(8); + break; + } + } - return false; + // add resistance if have an armor item to drop + if (pXSprite->dropMsg >= kItemArmorAsbest && pXSprite->dropMsg <= kItemArmorSuper) { + switch (pXSprite->dropMsg) { + case kItemArmorAsbest: + curScale[kDmgBurn] = 0; + curScale[kDmgExplode] -= 25; + break; + case kItemArmorBasic: + curScale[kDmgBurn] -= 10; + curScale[kDmgExplode] -= 10; + curScale[kDmgBullet] -= 10; + curScale[kDmgSpirit] -= 10; + break; + case kItemArmorBody: + curScale[kDmgBullet] -= 20; + break; + case kItemArmorFire: + curScale[kDmgBurn] -= 10; + curScale[kDmgExplode] -= 10; + break; + case kItemArmorSpirit: + curScale[kDmgSpirit] -= 20; + break; + case kItemArmorSuper: + curScale[kDmgBurn] -= 20; + curScale[kDmgExplode] -= 20; + curScale[kDmgBullet] -= 20; + curScale[kDmgSpirit] -= 20; + break; + } + } + + // take in account yrepeat of sprite + short yrepeat = sprite[pXSprite->reference].yrepeat; + if (yrepeat == 0) { + for (int i = 0; i < kDmgMax; i++) { + if (i != kDmgSpirit && i != kDmgChoke) curScale[i] = 500; + } + } else if (yrepeat < 64) { + for (int i = 0; i < kDmgMax; i++) curScale[i] += (64 - yrepeat); + } else if (yrepeat > 64) { + for (int i = 0; i < kDmgMax; i++) curScale[i] -= (yrepeat - 64); + } + + // finally, scale dmg for difficulty + for (int i = 0; i < kDmgMax; i++) + curScale[i] = mulscale8(DudeDifficulty[gGameOptions.nDifficulty], ClipLow(curScale[i], 1)); + + short* dc = curScale; + //viewSetSystemMessage("0: %d, 1: %d, 2: %d, 3: %d, 4: %d, 5: %d, 6: %d", dc[0], dc[1], dc[2], dc[3], dc[4], dc[5], dc[6]); +} + +int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp) { + // the faster fire rate, the less frames = more dispersion + Seq* pSeq = NULL; DICTNODE* hSeq = gSysRes.Lookup(xsprite[pSprite->extra].data2 + 6, "SEQ"); int disp = 1; + if ((pSeq = (Seq*)gSysRes.Load(hSeq)) != NULL) { + int nFrames = pSeq->nFrames; int ticks = pSeq->at8; int shots = 0; + for (int i = 0; i <= pSeq->nFrames; i++) { + if (pSeq->frames[i].at5_5) shots++; + } + + disp = (((shots * 1000) / nFrames) / ticks) * 20; + if (gGameOptions.nDifficulty > 0) + disp /= gGameOptions.nDifficulty + 1; + + //viewSetSystemMessage("DISP: %d FRAMES: %d SHOTS: %d TICKS %d", disp, nFrames, shots, ticks); + + } + + return ClipRange(disp, minDisp, maxDisp); +} + +// the distance counts from sprite size +int getRangeAttackDist(spritetype* pSprite, int minDist, int maxDist) { + short yrepeat = pSprite->yrepeat; int dist = 0; int seqId = xsprite[pSprite->extra].data2; + short mul = 550; int picnum = pSprite->picnum; + + if (yrepeat > 0) { + if (seqId >= 0) { + Seq* pSeq = NULL; DICTNODE* hSeq = gSysRes.Lookup(seqId, "SEQ"); + if (hSeq) { + pSeq = (Seq*)gSysRes.Load(hSeq); + picnum = seqGetTile(&pSeq->frames[0]); + } + } + dist = tilesiz[picnum].y << 8; + + if (yrepeat < 64) dist -= (64 - yrepeat) * mul; + else if (yrepeat > 64) dist += (yrepeat - 64) * (mul / 3); + } + + dist = ClipRange(dist, minDist, maxDist); + //viewSetSystemMessage("DIST: %d, SPRHEIGHT: %d: YREPEAT: %d PIC: %d", dist, tilesiz[pSprite->picnum].y, yrepeat, picnum); + return dist; +} + +// the distance counts from sprite size (the bigger sprite, the greater distance) +int getSeeDist(spritetype* pSprite, int startDist, int minDist, int maxDist) { + short y = tilesiz[pSprite->picnum].y; short yrepeat = pSprite->yrepeat; + int dist = 0; + + return dist; } int getBaseChanceModifier(int baseChance) { @@ -1028,76 +1219,85 @@ void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event) } bool doExplosion(spritetype* pSprite, int nType) { - spritetype* pExplosion = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 2, true); + spritetype* pExplosion = actSpawnSprite(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatExplosion, true); + if (pExplosion->extra < 0 || pExplosion->extra >= kMaxXSprites) + return false; + int nSeq = 4; int nSnd = 304; EXPLOSION* pExpl = &explodeInfo[nType]; - - pExplosion->yrepeat = pExpl->repeat; - pExplosion->xrepeat = pExpl->repeat; + pExplosion->type = nType; - pExplosion->cstat |= CSTAT_SPRITE_INVISIBLE | CSTAT_SPRITE_ALIGNMENT_SLAB; - pExplosion->owner = pSprite->xvel; + pExplosion->cstat |= CSTAT_SPRITE_INVISIBLE; + pExplosion->owner = pSprite->index; + pExplosion->shade = -127; - if (pExplosion->extra >= 0) { - xsprite[pExplosion->extra].target = 0; - xsprite[pExplosion->extra].data1 = pExpl->ticks; - xsprite[pExplosion->extra].data2 = pExpl->quakeEffect; - xsprite[pExplosion->extra].data3 = pExpl->flashEffect; + pExplosion->yrepeat = pExplosion->xrepeat = pExpl->repeat; + xsprite[pExplosion->extra].data1 = pExpl->ticks; + xsprite[pExplosion->extra].data2 = pExpl->quakeEffect; + xsprite[pExplosion->extra].data3 = pExpl->flashEffect; - if (nType == 0) { nSeq = 3; nSnd = 303; pExplosion->z = pSprite->z; } - else if (nType == 2) { nSeq = 4; nSnd = 305; } - else if (nType == 3) { nSeq = 9; nSnd = 307; } - else if (nType == 4) { nSeq = 5; nSnd = 307; } - else if (nType <= 6) { nSeq = 4; nSnd = 303; } - else if (nType == 7) { nSeq = 4; nSnd = 303; } + if (nType == 0) { nSeq = 3; nSnd = 303; } + else if (nType == 2) { nSeq = 4; nSnd = 305; } + else if (nType == 3) { nSeq = 9; nSnd = 307; } + else if (nType == 4) { nSeq = 5; nSnd = 307; } + else if (nType <= 6) { nSeq = 4; nSnd = 303; } + else if (nType == 7) { nSeq = 4; nSnd = 303; } + + seqSpawn(nSeq, 3, pExplosion->extra, -1); + sfxPlay3DSound(pExplosion, nSnd, -1, 0); - - if (fileExistsRFF(nSeq, "SEQ")) seqSpawn(nSeq, 3, pExplosion->extra, -1); - sfxPlay3DSound(pExplosion, nSnd, -1, 0); - - return true; - } - - return false; + return true; } +void updateTargetOfLeech(spritetype* pSprite) { + if (!(pSprite->extra >= 0 && pSprite->extra < kMaxXSprites)) { + consoleSysMsg("pSprite->extra >= 0 && pSprite->extra < kMaxXSprites"); + return; + } + + spritetype* pLeech = leechIsDropped(pSprite); + if (pLeech == NULL || pLeech->extra < 0) gGenDudeExtra[pSprite->index].nLifeLeech = -1; + else if (xsprite[pSprite->extra].target != xsprite[pLeech->extra].target) + xsprite[pLeech->extra].target = xsprite[pSprite->extra].target; +} + void updateTargetOfSlaves(spritetype* pSprite) { - for (short nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { - if (sprite[nSprite].owner != pSprite->xvel || sprite[nSprite].extra < 0 || !IsDudeSprite(&sprite[nSprite])) continue; - else if (xsprite[pSprite->extra].target != xsprite[sprite[nSprite].extra].target - && IsDudeSprite(&sprite[xsprite[pSprite->extra].target])) { - aiSetTarget(&xsprite[sprite[nSprite].extra], xsprite[pSprite->extra].target); - } - - if (xsprite[sprite[nSprite].extra].target >= 0) { - // don't attack mates - if (sprite[xsprite[sprite[nSprite].extra].target].owner == sprite[nSprite].owner) - aiSetTarget(&xsprite[sprite[nSprite].extra], pSprite->x, pSprite->y, pSprite->z); - } - - if (!isActive(sprite[nSprite].xvel) && xsprite[sprite[nSprite].extra].target >= 0) - aiActivateDude(&sprite[nSprite], &xsprite[sprite[nSprite].extra]); + if (!(pSprite->extra >= 0 && pSprite->extra < kMaxXSprites)) { + consoleSysMsg("pSprite->extra >= 0 && pSprite->extra < kMaxXSprites"); + return; } - return; -} + XSPRITE* pXSprite = &xsprite[pSprite->extra]; + + short* slave = gGenDudeExtra[pSprite->index].slave; + spritetype* pTarget = (pXSprite->target >= 0 && IsDudeSprite(&sprite[pXSprite->target])) ? &sprite[pXSprite->target] : NULL; + XSPRITE* pXTarget = (pTarget != NULL && pTarget->extra >= 0 && xsprite[pTarget->extra].health > 0) ? &xsprite[pTarget->extra] : NULL; -bool canSwim(spritetype* pSprite) { - return (gSysRes.Lookup(xsprite[pSprite->extra].data2 + 8, "SEQ") && gSysRes.Lookup(xsprite[pSprite->extra].data2 + 13, "SEQ") - && gSysRes.Lookup(xsprite[pSprite->extra].data2 + 17, "SEQ")); -} + for (int i = 0; i <= gGameOptions.nDifficulty; i++) { + if (slave[i] >= 0) { + spritetype* pSlave = &sprite[slave[i]]; + if (!IsDudeSprite(pSlave) || pSlave->extra < 0 || xsprite[pSlave->extra].health < 0) { + slave[i] = -1; + continue; + } + + XSPRITE* pXSlave = &xsprite[pSlave->index]; + if (pXTarget == NULL) aiSetTarget(pXSlave, pSprite->x, pSprite->y, pSprite->z); // try return to master + else if (pXSprite->target != pXSlave->target) + aiSetTarget(pXSlave, pXSprite->target); + // check if slave attacking another slave + if (pXSlave->target >= 0) { + if (sprite[pXSlave->target].owner == pSprite->index) + aiSetTarget(pXSlave, pSprite->x, pSprite->y, pSprite->z); // try return to master -bool canDuck(spritetype* pSprite) { - return (gSysRes.Lookup(xsprite[pSprite->extra].data2 + 8, "SEQ") && gSysRes.Lookup(xsprite[pSprite->extra].data2 + 14, "SEQ")); - -} - -bool CDCanMove(spritetype* pSprite) { - return (gSysRes.Lookup(xsprite[pSprite->extra].data2 + 9, "SEQ") && gSysRes.Lookup(xsprite[pSprite->extra].data2 + 13, "SEQ") - && gSysRes.Lookup(xsprite[pSprite->extra].data2 + 14, "SEQ")); - + } else { + // try return to master + aiSetTarget(pXSlave, pSprite->x, pSprite->y, pSprite->z); + } + } + } } bool inDodge(AISTATE* aiState) { @@ -1114,26 +1314,33 @@ int getSeqStartId(XSPRITE* pXSprite) { if (pXSprite->data2 > 0) { seqStartId = pXSprite->data2; // check for full set of animations - for (int i = seqStartId; i <= seqStartId + 19; i++) { + for (int i = seqStartId; i <= seqStartId + 24; i++) { // exceptions switch (i - seqStartId) { - case 3: // burning dude - case 4: // electrocution - case 8: // attack u/d - case 11: // reserved - case 12: // reserved - case 13: // move u - case 14: // move d - case 16: // burning death 2 - case 17: // idle w - case 18: // transformation in another dude - case 19: // reserved - continue; + case 3: // burning dude + case 4: // electrocution + case 8: // attack u/d + case 11: // reserved + case 12: // reserved + case 13: // move u + case 14: // move d + case 16: // burning death 2 + case 17: // idle w + case 18: // transformation in another dude + case 19: // reserved + case 20: // reserved + case 21: // reserved + case 22: // reserved + case 23: // reserved + case 24: // reserved + continue; } if (!gSysRes.Lookup(i, "SEQ")) { pXSprite->data2 = dudeInfo[sprite[pXSprite->reference].type - kDudeBase].seqStartID; + viewSetSystemMessage("No SEQ animation id %d found for custom dude #%d!", i, sprite[pXSprite->reference].index); + viewSetSystemMessage("SEQ base id: %d", pXSprite->data2); return pXSprite->data2; } @@ -1145,6 +1352,132 @@ int getSeqStartId(XSPRITE* pXSprite) { return seqStartId; } + +void genDudePrepare(spritetype* pSprite, int propId) { + if (!(pSprite->index >= 0 && pSprite->index < kMaxSprites)) { + consoleSysMsg("pSprite->index >= 0 && pSprite->index < kMaxSprites"); + return; + } else if (!(pSprite->extra >= 0 && pSprite->extra < kMaxXSprites)) { + consoleSysMsg("pSprite->extra >= 0 && pSprite->extra < kMaxXSprites"); + return; + } else if (pSprite->type != kDudeModernCustom) { + consoleSysMsg("pSprite->type != kDudeModernCustom"); + return; + } + + XSPRITE* pXSprite = &xsprite[pSprite->extra]; GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; + switch (propId) { + case kGenDudePropertyAll: + pExtra->updReq[propId] = false; + case kGenDudePropertyWeapon: + //viewSetSystemMessage("PROPERTY: WEAPONS"); + pExtra->curWeapon = pXSprite->data1; + switch (pXSprite->data1) { + case 19: + pExtra->curWeapon = 2; + break; + case 310: + pExtra->curWeapon = kMissileArcGargoyle; + break; + case kThingDroppedLifeLeech: + pExtra->curWeapon = kModernThingEnemyLifeLeech; + break; + } + pExtra->updReq[propId] = false; + if (propId) break; + case kGenDudePropertyDamage: + //viewSetSystemMessage("PROPERTY: DAMAGE SCALE"); + scaleDamage(pXSprite); + pExtra->updReq[propId] = false; + if (propId) break; + case kGenDudePropertyMass: { + //viewSetSystemMessage("PROPERTY: MASS"); + + // to ensure mass get's updated, let's clear all cache + SPRITEMASS* pMass = &gSpriteMass[pSprite->index]; + pMass->seqId = pMass->picnum = pMass->xrepeat = pMass->yrepeat = pMass->clipdist = 0; + pMass->mass = pMass->airVel = pMass->fraction = 0; + + getSpriteMassBySize(pSprite); + pExtra->updReq[propId] = false; + if (propId) break; + } + case kGenDudePropertyAttack: + //viewSetSystemMessage("PROPERTY: ATTACK"); + pExtra->fireDist = getRangeAttackDist(pSprite, 1200, 45000); + pExtra->throwDist = gGenDudeExtra[pSprite->index].fireDist; // temp + pExtra->baseDispersion = getDispersionModifier(pSprite, 200, 3500); + pExtra->updReq[propId] = false; + if (propId) break; + case kGenDudePropertyStates: + //viewSetSystemMessage("PROPERTY: STATES"); + pExtra->frontSpeed = getGenDudeMoveSpeed(pSprite, 0, true, false); + pExtra->canFly = false; + pExtra->canDuck = (gSysRes.Lookup(pXSprite->data2 + 8, "SEQ") && gSysRes.Lookup(pXSprite->data2 + 14, "SEQ")); + pExtra->canSwim = (gSysRes.Lookup(pXSprite->data2 + 8, "SEQ") && gSysRes.Lookup(pXSprite->data2 + 13, "SEQ") + && gSysRes.Lookup(pXSprite->data2 + 17, "SEQ")); + pExtra->updReq[propId] = false; + if (propId) break; + case kGenDudePropertyLeech: + //viewSetSystemMessage("PROPERTY: LEECH"); + pExtra->nLifeLeech = -1; + if (pSprite->owner != kMaxSprites - 1) { + for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { + if (sprite[nSprite].owner == pSprite->index && pSprite->type == kModernThingEnemyLifeLeech) { + pExtra->nLifeLeech = nSprite; + break; + } + } + } + pExtra->updReq[propId] = false; + if (propId) break; + case kGenDudePropertySlaves: + //viewSetSystemMessage("PROPERTY: SLAVES"); + pExtra->slaveCount = 0; memset(pExtra->slave, -1, sizeof(pExtra->slave)); + for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { + if (sprite[nSprite].owner != pSprite->xvel) continue; + else if (!IsDudeSprite(&sprite[nSprite]) || sprite[nSprite].extra < 0 || xsprite[sprite[nSprite].extra].health <= 0) { + sprite[nSprite].owner = -1; + continue; + } + + pExtra->slave[pExtra->slaveCount++] = nSprite; + if (pExtra->slaveCount > gGameOptions.nDifficulty) + break; + } + pExtra->updReq[propId] = false; + if (propId) break; + case kGenDudePropertyMelee: { + //viewSetSystemMessage("PROPERTY: MELEE"); + short curWeapon = pExtra->curWeapon; + pExtra->isMelee = false; int meleeDist = 2048; + if (curWeapon >= kTrapExploder && curWeapon < (kTrapExploder + kExplodeMax) - 1) { + pExtra->isMelee = true; + } else if (curWeapon >= 0 && curWeapon < kVectorMax) { + if (gVectorData[curWeapon].maxDist > 0 && gVectorData[curWeapon].maxDist <= meleeDist) + pExtra->isMelee = true; + } + pExtra->updReq[propId] = false; + } + break; + default: + viewSetSystemMessage("Unknown custom dude #%d property (%d)", pSprite->index, propId); + break; + } +} + +bool canSwim(spritetype* pSprite) { + return gGenDudeExtra[pSprite->index].canSwim; +} + +bool canDuck(spritetype* pSprite) { + return gGenDudeExtra[pSprite->index].canDuck; +} + +bool canWalk(spritetype* pSprite) { + return gGenDudeExtra[pSprite->index].canWalk; +} + ////////// END_BLD_NS diff --git a/source/blood/src/aiunicult.h b/source/blood/src/aiunicult.h index e991cb29e..1cfa1a113 100644 --- a/source/blood/src/aiunicult.h +++ b/source/blood/src/aiunicult.h @@ -27,8 +27,37 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -#define kMaxGenDudeSndMode 11 #define kDefaultAnimationBase 11520 +#define kGenDudeMaxSlaves 7 + +enum { +kGenDudeSndTargetSpot = 0, +kGenDudeSndGotHit = 1, +kGenDudeSndDeathNormal = 2, +kGenDudeSndBurning = 3, +kGenDudeSndDeathExplode = 4, +kGenDudeSndTargetDead = 5, +kGenDudeSndChasing = 6, +kGenDudeSndAttackNormal = 7, +kGenDudeSndAttackThrow = 8, +kGenDudeSndAttackMelee = 9, +kGenDudeSndTransforming = 10, +kGenDudeSndMax , +}; + +enum { +kGenDudePropertyAll = 0, +kGenDudePropertyWeapon = 1, +kGenDudePropertyDamage = 2, +kGenDudePropertyMass = 3, +kGenDudePropertyAttack = 4, +kGenDudePropertyStates = 5, +kGenDudePropertyLeech = 6, +kGenDudePropertySlaves = 7, +kGenDudePropertyMelee = 8, +kGenDudePropertyClipdist = 9, +kGenDudePropertyMax , +}; extern AISTATE GDXGenDudeIdleL; @@ -68,6 +97,28 @@ struct GENDUDESND extern GENDUDESND gCustomDudeSnd[]; +// temporary, until normal DUDEEXTRA gets refactored +struct GENDUDEEXTRA { + unsigned int fireDist; // counts from sprite size + unsigned int throwDist; // counts from sprite size + unsigned int frontSpeed; + unsigned short curWeapon; // data1 duplicate to avoid potential problems when changing data dynamically + unsigned short baseDispersion; + signed short nLifeLeech; // spritenum of dropped dude's leech + short dmgControl[kDamageMax]; // depends of current weapon, drop armor item and sprite yrepeat + short slave[kGenDudeMaxSlaves]; // index of the ones dude is summon + short slaveCount; + bool updReq[kGenDudePropertyMax]; // update requests + bool isMelee; + bool canWalk; + bool canDuck; + bool canSwim; + bool canFly; + +}; + +extern GENDUDEEXTRA gGenDudeExtra[]; + XSPRITE* getNextIncarnation(XSPRITE* pXSprite); void killDudeLeech(spritetype* pLeech); void removeLeech(spritetype* pLeech, bool delSprite = true); @@ -76,6 +127,7 @@ spritetype* leechIsDropped(spritetype* pSprite); bool spriteIsUnderwater(spritetype* pSprite, bool oldWay); bool sfxPlayGDXGenDudeSound(spritetype* pSprite, int mode); void aiGenDudeMoveForward(spritetype* pSprite, XSPRITE* pXSprite); +void aiGenDudeChooseDirection(spritetype* pSprite, XSPRITE* pXSprite, int a3, int aXvel = -1, int aYvel = -1); int getGenDudeMoveSpeed(spritetype* pSprite, int which, bool mul, bool shift); bool TargetNearThing(spritetype* pSprite, int thingType); int checkAttackState(spritetype* pSprite, XSPRITE* pXSprite); @@ -85,11 +137,18 @@ int getDodgeChance(spritetype* pSprite); int getRecoilChance(spritetype* pSprite); bool dudeIsMelee(XSPRITE* pXSprite); void updateTargetOfSlaves(spritetype* pSprite); +void updateTargetOfLeech(spritetype* pSprite); bool canSwim(spritetype* pSprite); bool canDuck(spritetype* pSprite); -bool CDCanMove(spritetype* pSprite); +bool canWalk(spritetype* pSprite); bool inDodge(AISTATE* aiState); bool inIdle(AISTATE* aiState); int getSeqStartId(XSPRITE* pXSprite); +int getSeeDist(spritetype* pSprite, int startDist, int minDist, int maxDist); +int getRangeAttackDist(spritetype* pSprite, int minDist = 1200, int maxDist = 80000); +int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp); +void scaleDamage(XSPRITE* pXSprite); +void genDudePrepare(spritetype* pSprite, int propId = kGenDudePropertyAll); +void genDudeUpdate(spritetype* pSprite); END_BLD_NS diff --git a/source/blood/src/callback.cpp b/source/blood/src/callback.cpp index 723653b06..16397feb2 100644 --- a/source/blood/src/callback.cpp +++ b/source/blood/src/callback.cpp @@ -358,7 +358,7 @@ void CounterCheck(int nSector) // 12 return; } else { //pXSector->waitTimeA = 0; //do not reset necessary objects counter to zero - trTriggerSector(nSector, pXSector, kCmdOn); + trTriggerSector(nSector, pXSector, kCmdOn, -1); pXSector->locked = 1; //lock sector, so it can be opened again later } } @@ -539,9 +539,9 @@ void fxPodBloodSplat(int nSprite) // 19 sfxPlay3DSound(pSprite, 385, nChannel, 1); } spritetype *pFX = NULL; - if (pSprite->type == 53 || pSprite->type == kThingPodFireBall) + if (pSprite->type == 53 || pSprite->type == kThingPodGreenBall) { - if (Chance(0x500) || pSprite->type == kThingPodFireBall) + if (Chance(0x500) || pSprite->type == kThingPodGreenBall) pFX = gFX.fxSpawn(FX_55, pSprite->sectnum, x, y, floorZ-64, 0); if (pFX) pFX->ang = nAngle; diff --git a/source/blood/src/common_game.h b/source/blood/src/common_game.h index 2e0b6b43f..60d93d6c8 100644 --- a/source/blood/src/common_game.h +++ b/source/blood/src/common_game.h @@ -88,6 +88,15 @@ void _consoleSysMsg(const char* pMessage, ...); #define kMaxPAL 5 #define kFreeQAVEntry 108 +#define kDmgFall 0 +#define kDmgBurn 1 +#define kDmgBullet 2 +#define kDmgExplode 3 +#define kDmgChoke 4 +#define kDmgSpirit 5 +#define kDmgElectric 6 +#define kDmgMax 7 + // MEDIUM ///////////////////////////////////////////////////// enum { kMediumNormal = 0, @@ -368,8 +377,8 @@ enum { kThingBloodChunks = 426, kThingZombieHead = 427, kThingNapalmBall = 428, - kThingPodGreenBall = 429, - kThingPodFireBall = 430, + kThingPodFireBall = 429, + kThingPodGreenBall = 430, kThingDroppedLifeLeech = 431, kThingVoodooHead = 432, // unused kModernThingTNTProx = 433, // gModernMap only - detects only players diff --git a/source/blood/src/db.cpp b/source/blood/src/db.cpp index 22d9ac786..1e37a3c1f 100644 --- a/source/blood/src/db.cpp +++ b/source/blood/src/db.cpp @@ -696,13 +696,14 @@ const int nXSectorSize = 60; const int nXSpriteSize = 56; const int nXWallSize = 24; -int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short *pSector, unsigned int *pCRC) -{ - char name2[BMAX_PATH]; - int16_t tpskyoff[256]; +int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short *pSector, unsigned int *pCRC) { + char name2[BMAX_PATH]; int16_t tpskyoff[256]; memset(show2dsector, 0, sizeof(show2dsector)); memset(show2dwall, 0, sizeof(show2dwall)); memset(show2dsprite, 0, sizeof(show2dsprite)); + + gModernMap = false; + #ifdef USE_OPENGL Polymost_prepare_loadboard(); #endif @@ -747,7 +748,6 @@ int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short // for maps wich created in PMAPEDIT BETA13 or higher versions. Since only minor version changed, // the map is still can be loaded with vanilla BLOOD / MAPEDIT and should work in other ports too. if ((header.version & 0x00ff) == 0x001) gModernMap = true; - else gModernMap = false; } else { initprintf("Map file is wrong version"); @@ -963,10 +963,6 @@ int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short xsector[sector[i].extra].reference = i; xsector[sector[i].extra].busy = xsector[sector[i].extra].state<<16; - // by NoOne: indicate if the map requires modern features to work properly - // for maps wich created in different editors (include vanilla MAPEDIT) or in PMAPEDIT version below than BETA13 - if (pXSector->rxID == kChannelMapExtended && pXSector->rxID == pXSector->txID && pXSector->command == kCmdModernFeaturesEnable) - gModernMap = true; } } for (int i = 0; i < numwalls; i++) @@ -1042,10 +1038,6 @@ int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short xwall[wall[i].extra].reference = i; xwall[wall[i].extra].busy = xwall[wall[i].extra].state << 16; - // by NoOne: indicate if the map requires modern features to work properly - // for maps wich created in different editors (include vanilla MAPEDIT) or in PMAPEDIT version below than BETA13 - if (pXWall->rxID == kChannelMapExtended && pXWall->rxID == pXWall->txID && pXWall->command == kCmdModernFeaturesEnable) - gModernMap = true; } } initspritelists(); @@ -1168,7 +1160,7 @@ int dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, short // by NoOne: indicate if the map requires modern features to work properly // for maps wich created in different editors (include vanilla MAPEDIT) or in PMAPEDIT version below than BETA13 - if (pXSprite->rxID == kChannelMapExtended && pXSprite->rxID == pXSprite->txID && pXSprite->command == kCmdModernFeaturesEnable) + if (!gModernMap && pXSprite->rxID == kChannelMapModernize && pXSprite->rxID == pXSprite->txID && pXSprite->command == kCmdModernFeaturesEnable) gModernMap = true; } if ((sprite[i].cstat & 0x30) == 0x30) diff --git a/source/blood/src/db.h b/source/blood/src/db.h index 23f15f426..b799342dd 100644 --- a/source/blood/src/db.h +++ b/source/blood/src/db.h @@ -31,6 +31,19 @@ BEGIN_BLD_NS // by NoOne additional non-thing proximity, sight and physics sprites #define kMaxSuperXSprites 128 +// by NoOne: functions to quckly check range of specifical arrays +inline bool xsprRangeIsFine(int nXindex) { + return (nXindex >= 0 && nXindex < kMaxXSprites); +} + +inline bool xsectRangeIsFine(int nXindex) { + return (nXindex >= 0 && nXindex < kMaxXSectors); +} + +inline bool xwallRangeIsFine(int nXindex) { + return (nXindex >= 0 && nXindex < kMaxXWalls); +} + extern bool gModernMap; #pragma pack(push, 1) @@ -58,7 +71,7 @@ struct XSPRITE { unsigned int respawnPending : 2; // respawnPending - signed int dropMsg : 8; // Drop Item + unsigned int dropMsg : 8; // Drop Item unsigned int Decoupled : 1; // Decoupled unsigned int triggerOnce : 1; // 1-shot unsigned int isTriggered : 1; // works in case if triggerOnce selected diff --git a/source/blood/src/dude.cpp b/source/blood/src/dude.cpp index 3102f2c9b..192a95d26 100644 --- a/source/blood/src/dude.cpp +++ b/source/blood/src/dude.cpp @@ -1570,7 +1570,7 @@ DUDEINFO dudeInfo[kDudeMax-kDudeBase] = 256, // angSpeed // 0, 7, -1, 18, // nGibType - 256, 256, 128, 256, 256, 256, 192, + 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/source/blood/src/eventq.cpp b/source/blood/src/eventq.cpp index a97e1b8ba..855da5259 100644 --- a/source/blood/src/eventq.cpp +++ b/source/blood/src/eventq.cpp @@ -349,80 +349,96 @@ char evGetSourceState(int nType, int nIndex) void evSend(int nIndex, int nType, int rxId, COMMAND_ID command, short causedBy) { EVENT event; event.index = nIndex; event.type = nType; event.cmd = command; event.causedBy = causedBy; - + switch (command) { - case kCmdState: - command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; - break; - case kCmdNotState: - command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; - break; + case kCmdState: + command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; + break; + case kCmdNotState: + command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; + break; } - + switch (rxId) { - case kChannelTextOver: - if (command >= kCmdNumberic) trTextOver(command - kCmdNumberic); - else viewSetSystemMessage("Invalid TextOver command by xobject #%d (object type %d)", nIndex, nType); - return; - case kChannelLevelExitNormal: - levelEndLevel(0); - return; - case kChannelLevelExitSecret: - levelEndLevel(1); - return; + case kChannelTextOver: + if (command >= kCmdNumberic) trTextOver(command - kCmdNumberic); + else viewSetSystemMessage("Invalid TextOver command by xobject #%d (object type %d)", nIndex, nType); + return; + case kChannelLevelExitNormal: + levelEndLevel(0); + return; + case kChannelLevelExitSecret: + levelEndLevel(1); + return; // By NoOne: finished level and load custom level ¹ via numbered command. - case kChannelModernEndLevelCustom: - if (command >= kCmdNumberic) levelEndLevelCustom(command - kCmdNumberic); - else viewSetSystemMessage("Invalid Level-Exit# command by xobject #%d (object type %d)", nIndex, nType); - return; - case kChannelSetTotalSecrets: - if (command >= kCmdNumberic) levelSetupSecret(command - kCmdNumberic); - else viewSetSystemMessage("Invalid Total-Secrets command by xobject #%d (object type %d)", nIndex, nType); - break; - case kChannelSecretFound: - if (command >= kCmdNumberic) levelTriggerSecret(command - kCmdNumberic); - else viewSetSystemMessage("Invalid Trigger-Secret command by xobject #%d (object type %d)", nIndex, nType); - break; - case kChannelRemoteBomb0: - case kChannelRemoteBomb1: - case kChannelRemoteBomb2: - case kChannelRemoteBomb3: - case kChannelRemoteBomb4: - case kChannelRemoteBomb5: - case kChannelRemoteBomb6: - case kChannelRemoteBomb7: - for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) + case kChannelModernEndLevelCustom: + if (command >= kCmdNumberic) levelEndLevelCustom(command - kCmdNumberic); + else viewSetSystemMessage("Invalid Level-Exit# command by xobject #%d (object type %d)", nIndex, nType); + return; + case kChannelSetTotalSecrets: + if (command >= kCmdNumberic) levelSetupSecret(command - kCmdNumberic); + else viewSetSystemMessage("Invalid Total-Secrets command by xobject #%d (object type %d)", nIndex, nType); + break; + case kChannelSecretFound: + if (command >= kCmdNumberic) levelTriggerSecret(command - kCmdNumberic); + else viewSetSystemMessage("Invalid Trigger-Secret command by xobject #%d (object type %d)", nIndex, nType); + break; + case kChannelRemoteBomb0: + case kChannelRemoteBomb1: + case kChannelRemoteBomb2: + case kChannelRemoteBomb3: + case kChannelRemoteBomb4: + case kChannelRemoteBomb5: + case kChannelRemoteBomb6: + case kChannelRemoteBomb7: + for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) + { + spritetype* pSprite = &sprite[nSprite]; + if (pSprite->flags & 32) + continue; + int nXSprite = pSprite->extra; + if (nXSprite > 0) { - spritetype *pSprite = &sprite[nSprite]; - if (pSprite->flags&32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) - { - XSPRITE *pXSprite = &xsprite[nXSprite]; - if (pXSprite->rxID == rxId) - trMessageSprite(nSprite, event); - } + XSPRITE* pXSprite = &xsprite[nXSprite]; + if (pXSprite->rxID == rxId) + trMessageSprite(nSprite, event); } - return; - case kChannelTeamAFlagCaptured: - case kChannelTeamBFlagCaptured: - for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nextspritestat[nSprite]) + } + return; + case kChannelTeamAFlagCaptured: + case kChannelTeamBFlagCaptured: + for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nextspritestat[nSprite]) + { + spritetype* pSprite = &sprite[nSprite]; + if (pSprite->flags & 32) + continue; + int nXSprite = pSprite->extra; + if (nXSprite > 0) { - spritetype *pSprite = &sprite[nSprite]; - if (pSprite->flags&32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) - { - XSPRITE *pXSprite = &xsprite[nXSprite]; - if (pXSprite->rxID == rxId) - trMessageSprite(nSprite, event); - } + XSPRITE* pXSprite = &xsprite[nXSprite]; + if (pXSprite->rxID == rxId) + trMessageSprite(nSprite, event); } - return; - default: - break; + } + return; + default: + break; + } + + //by NoOne: allow to send commands on player sprites + if (gModernMap) { + + PLAYER* pPlayer = NULL; + if (rxId >= kChannelPlayer0 && rxId <= kChannelPlayer7) { + if ((pPlayer = getPlayerById((kChannelPlayer0 - kChannelPlayer7) + kMaxPlayers)) != NULL) + trMessageSprite(pPlayer->nSprite, event); + } else if (rxId == kChannelAllPlayers) { + for (int i = 0; i < kMaxPlayers; i++) { + if ((pPlayer = getPlayerById(i)) != NULL) + trMessageSprite(pPlayer->nSprite, event); + } + } + } for (int i = bucketHead[rxId]; i < bucketHead[rxId+1]; i++) { diff --git a/source/blood/src/eventq.h b/source/blood/src/eventq.h index 6c87c3927..34fe642ab 100644 --- a/source/blood/src/eventq.h +++ b/source/blood/src/eventq.h @@ -27,32 +27,47 @@ BEGIN_BLD_NS enum { - kChannelZero = 0, - kChannelSetTotalSecrets, - kChannelSecretFound, - kChannelTextOver, - kChannelLevelExitNormal, - kChannelLevelExitSecret, - kChannelModernEndLevelCustom, // // custom level end - kChannelLevelStart, - kChannelLevelStartMatch, // DM and TEAMS - kChannelLevelStartCoop, - kChannelLevelStartTeamsOnly, - kChannelPlayerDeathTeamA = 15, - kChannelPlayerDeathTeamB, - kChannelMapExtended = 60, // map requires modern features to work properly - kChannelTeamAFlagCaptured = 80, - kChannelTeamBFlagCaptured, - kChannelRemoteBomb0 = 90, - kChannelRemoteBomb1, - kChannelRemoteBomb2, - kChannelRemoteBomb3, - kChannelRemoteBomb4, - kChannelRemoteBomb5, - kChannelRemoteBomb6, - kChannelRemoteBomb7, - kChannelUser = 100, - kChannelMax = 4096, +kChannelZero = 0, +kChannelSetTotalSecrets, +kChannelSecretFound, +kChannelTextOver, +kChannelLevelExitNormal, +kChannelLevelExitSecret, +kChannelModernEndLevelCustom, // custom level end +kChannelLevelStart, +kChannelLevelStartMatch, // DM and TEAMS +kChannelLevelStartCoop, +kChannelLevelStartTeamsOnly, +kChannelPlayerDeathTeamA = 15, +kChannelPlayerDeathTeamB, + +// by NoOne: RX channels of players to send commands on +///////////////////////////// +kChannelPlayer0 = 30, +kChannelPlayer1, +kChannelPlayer2, +kChannelPlayer3, +kChannelPlayer4, +kChannelPlayer5, +kChannelPlayer6, +kChannelPlayer7, +kChannelAllPlayers = kChannelPlayer0 + kMaxPlayers, +///////////////////////////// + +kChannelMapModernize = 60, // map requires modern features to work properly + +kChannelTeamAFlagCaptured = 80, +kChannelTeamBFlagCaptured, +kChannelRemoteBomb0 = 90, +kChannelRemoteBomb1, +kChannelRemoteBomb2, +kChannelRemoteBomb3, +kChannelRemoteBomb4, +kChannelRemoteBomb5, +kChannelRemoteBomb6, +kChannelRemoteBomb7, +kChannelUser = 100, +kChannelMax = 4096, }; struct RXBUCKET @@ -79,7 +94,7 @@ enum COMMAND_ID { kCmdCounterSector = 12, kCmdCallback = 20, kCmdRepeat = 21, - + kCmdSpritePush = 30, kCmdSpriteImpact = 31, kCmdSpritePickup = 32, @@ -87,7 +102,7 @@ enum COMMAND_ID { kCmdSpriteSight = 34, kCmdSpriteProximity = 35, kCmdSpriteExplode = 36, - + kCmdSectorPush = 40, kCmdSectorImpact = 41, kCmdSectorEnter = 42, @@ -99,11 +114,15 @@ enum COMMAND_ID { kCmdModernUse = 53, // used by most of modern types kCmdNumberic = 64, // 64: 0, 65: 1 and so on up to 255 - kCmdModernFeaturesEnable = 100, // must be in object with kChannelMapExtended RX / TX - kCmdModernFeaturesDisable = 200, // must be in object with kChannelMapExtended RX / TX + kCmdModernFeaturesEnable = 100, // must be in object with kChannelMapModernize RX / TX + kCmdModernFeaturesDisable = 200, // must be in object with kChannelMapModernize RX / TX kCmdNumbericMax = 255, }; +inline bool playerRXRngIsFine(int rx) { + return (rx >= kChannelPlayer0 && rx < kChannelPlayer7); +} + struct EVENT { unsigned int index: 14; // index unsigned int type: 3; // type @@ -114,8 +133,8 @@ struct EVENT { void evInit(void); char evGetSourceState(int nType, int nIndex); -void evSend(int nIndex, int nType, int rxId, COMMAND_ID command, short causedBy = -1); -void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command, short causedBy = -1); +void evSend(int nIndex, int nType, int rxId, COMMAND_ID command, short causedBy); +void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command, short causedBy); void evPost(int nIndex, int nType, unsigned int nDelta, CALLBACK_ID callback, short causedBy = -1); void evProcess(unsigned int nTime); void evKill(int a1, int a2); diff --git a/source/blood/src/gameutil.h b/source/blood/src/gameutil.h index 851fa66e1..74c6168be 100644 --- a/source/blood/src/gameutil.h +++ b/source/blood/src/gameutil.h @@ -50,6 +50,21 @@ enum { PARALLAXCLIP_FLOOR = 2, }; + +// by NoOne: functions to quckly check range of specifical arrays +inline bool spriRangeIsFine(int nIndex) { + return (nIndex >= 0 && nIndex < kMaxSprites); +} + +inline bool sectRangeIsFine(int nIndex) { + return (nIndex >= 0 && nIndex < kMaxSectors); +} + +inline bool wallRangeIsFine(int nIndex) { + return (nIndex >= 0 && nIndex < kMaxWalls); +} +/// + bool AreSectorsNeighbors(int sect1, int sect2); bool FindSector(int nX, int nY, int nZ, int *nSector); bool FindSector(int nX, int nY, int *nSector); diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index 9e3234690..9a6a361bf 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -315,6 +315,7 @@ void MyLoadSave::Load(void) Read(&gMapRev, sizeof(gMapRev)); Read(&gSongId, sizeof(gSkyCount)); Read(&gFogMode, sizeof(gFogMode)); + Read(&gModernMap, sizeof(gModernMap)); #ifdef YAX_ENABLE Read(&numyaxbunches, sizeof(numyaxbunches)); #endif @@ -420,6 +421,7 @@ void MyLoadSave::Save(void) Write(&gMapRev, sizeof(gMapRev)); Write(&gSongId, sizeof(gSkyCount)); Write(&gFogMode, sizeof(gFogMode)); + Write(&gModernMap, sizeof(gModernMap)); #ifdef YAX_ENABLE Write(&numyaxbunches, sizeof(numyaxbunches)); #endif diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp index 57ba93c81..570ecb558 100644 --- a/source/blood/src/player.cpp +++ b/source/blood/src/player.cpp @@ -126,13 +126,26 @@ int Handicap[] = { int gDefaultAccel[] = { // normal human - 0x4000, 0x1200, 0x2000, // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back) + 0x4000, 0x1200, 0x2000, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) // normal beast - 0x4000, 0x1200, 0x2000, // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back) + 0x4000, 0x1200, 0x2000, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) // shrink human - 10384, 2108, 2192, // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back) + 10384, 2108, 2192, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) // grown human - 19384, 5608, 11192 // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back) + 19384, 5608, 11192 // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) + +}; + +int gDefaultJumpZ[] = { + + // normal human + -0xbaaaa, -0x175555, 0x5b05, 0, 0, 0 // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) + // normal beast + -0xbaaaa, -0x175555, 0x5b05, 0, 0, 0 // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) + // shrink human + -200000, -0x175555, 0x5b05, 0, 0, 0 // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) + // grown human + -250000, -0x175555, 0x5b05, 0, 0, 0 // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) }; @@ -154,7 +167,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 0x1600, 0x1200, 0xc00, - 0x90 + 0x90, + gDefaultJumpZ[0], + gDefaultJumpZ[1], }, { gDefaultAccel[1], @@ -169,7 +184,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 0x1400, 0x1000, -0x600, - 0xb0 + 0xb0, + gDefaultJumpZ[2], + gDefaultJumpZ[3], }, { gDefaultAccel[2], @@ -184,7 +201,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 0x800, 0x600, -0x600, - 0xb0 + 0xb0, + gDefaultJumpZ[4], + gDefaultJumpZ[5], }, }, @@ -203,7 +222,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 0x1600, 0x1200, 0xc00, - 0x90 + 0x90, + gDefaultJumpZ[6], + gDefaultJumpZ[7], }, { gDefaultAccel[4], @@ -218,7 +239,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 0x1400, 0x1000, -0x600, - 0xb0 + 0xb0, + gDefaultJumpZ[8], + gDefaultJumpZ[9], }, { gDefaultAccel[5], @@ -233,7 +256,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 0x800, 0x600, -0x600, - 0xb0 + 0xb0, + gDefaultJumpZ[10], + gDefaultJumpZ[11], }, }, @@ -252,7 +277,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 5632, 4608, 3072, - 144 + 144, + gDefaultJumpZ[12], + gDefaultJumpZ[13], }, { gDefaultAccel[7], @@ -267,7 +294,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 5120, 4096, -1536, - 176 + 176, + gDefaultJumpZ[14], + gDefaultJumpZ[15], }, { gDefaultAccel[8], @@ -282,7 +311,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 2048, 1536, -1536, - 176 + 176, + gDefaultJumpZ[16], + gDefaultJumpZ[17], }, }, @@ -300,8 +331,10 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 80, 5632, 4608, - 3072, - 144 + 3072, + 144, + gDefaultJumpZ[18], + gDefaultJumpZ[19], }, { gDefaultAccel[10], @@ -316,7 +349,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 5120, 4096, -1536, - 176 + 176, + gDefaultJumpZ[20], + gDefaultJumpZ[21], }, { gDefaultAccel[11], @@ -331,7 +366,9 @@ POSTURE gPosture[kModeMax][kPostureMax] = { 2048, 1536, -1536, - 176 + 176, + gDefaultJumpZ[22], + gDefaultJumpZ[23], }, }, }; @@ -435,27 +472,48 @@ DAMAGEINFO damageInfo[7] = { { 0, 0, 0, 0, 0, 0, 0 } }; +TRPLAYERCTRL gPlayerCtrl[kMaxPlayers]; -QAVSCENE gQavScene[kMaxPlayers]; +QAV* qavSceneLoad(int qavId) { + QAV* pQav = NULL; DICTNODE* hQav = gSysRes.Lookup(qavId, "QAV"); + + if (hQav) pQav = (QAV*)gSysRes.Lock(hQav); + else viewSetSystemMessage("Failed to load QAV animation #%d", qavId); -void playQavScene(PLAYER* pPlayer) { - dassert(pPlayer != NULL); - if (pPlayer->weaponQav == -1) - return; - QAV * pQAV = gQavScene[pPlayer->nPlayer].qavId; - pQAV->nSprite = pPlayer->pSprite->index; - int nTicks = pQAV->at10 - pPlayer->weaponTimer; - pQAV->Play(nTicks - 4, nTicks, pPlayer->qavCallback, pPlayer); + return pQav; } -void startQavScene(PLAYER * pPlayer, int qavId, int a3, char a4) { - pPlayer->weaponQav = qavId; - pPlayer->weaponTimer = gQavScene[pPlayer->nPlayer].qavId->at10; - pPlayer->qavCallback = a3; - pPlayer->qavLoop = a4; - gQavScene[pPlayer->nPlayer].qavId->Preload(); - playQavScene(pPlayer); - pPlayer->weaponTimer -= 4; +void qavSceneDraw(PLAYER* pPlayer, int a2, int a3, int a4, int a5) { + if (pPlayer == NULL || pPlayer->sceneQav == -1) return; + + QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; + if (pQavScene->qavResrc != NULL) { + + QAV* pQAV = pQavScene->qavResrc; + int v4 = (pPlayer->weaponTimer == 0) ? (int)totalclock % pQAV->at10 : pQAV->at10 - pPlayer->weaponTimer; + + pQAV->x = a3; pQAV->y = a4; int flags = 2; + int nInv = powerupCheck(pPlayer, kPwUpShadowCloak); + if (nInv >= 120 * 8 || (nInv != 0 && ((int)totalclock & 32))) { + a2 = -128; + flags |= 1; + } + + pQAV->Draw(v4, flags, a2, a5); + } + +} + +void qavScenePlay(PLAYER* pPlayer) { + if (pPlayer == NULL || pPlayer->sceneQav == -1) return; + + QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; + if (pQavScene->qavResrc != NULL) { + QAV* pQAV = pQavScene->qavResrc; + pQAV->nSprite = pPlayer->pSprite->index; + int nTicks = pQAV->at10 - pPlayer->weaponTimer; + pQAV->Play(nTicks - 4, nTicks, pPlayer->qavCallback, pPlayer); + } } int powerupCheck(PLAYER *pPlayer, int nPowerUp) @@ -489,7 +547,6 @@ bool shrinkPlayerSize(PLAYER* pPlayer, int divider) { bool growPlayerSize(PLAYER* pPlayer, int multiplier) { pPlayer->pXSprite->scale = 256*multiplier; playerSetRace(pPlayer, kModeHumanGrown); - viewSetSystemMessage("%d", pPlayer->pXSprite->scale); return true; } @@ -514,9 +571,10 @@ PLAYER* getPlayerById(short id) { if (id < kMaxPlayers && id == i + 1) return &gPlayer[i]; // relative to connected players else if (id >= kDudePlayer1 && id <= kDudePlayer8 && id == gPlayer[i].pSprite->type) // absolute type return &gPlayer[i]; - - } + + if (id >= kDudePlayer1 && id <= kDudePlayer8) viewSetSystemMessage("There is no player #%d", (kDudePlayer8 - id) + kMaxPlayers); + else viewSetSystemMessage("There is no player #%d", id); } return NULL; } @@ -899,19 +957,13 @@ void playerStart(int nPlayer) GINPUT* pInput = &pPlayer->input; ZONE* pStartZone = NULL; - // reset qav player index - if (gModernMap) { - gQavScene[pPlayer->nPlayer].index = -1; - gQavScene[pPlayer->nPlayer].qavId = NULL; - } - // normal start position if (gGameOptions.nGameType <= 1) pStartZone = &gStartZone[nPlayer]; // By NoOne: let's check if there is positions of teams is specified // if no, pick position randomly, just like it works in vanilla. - else if (gGameOptions.nGameType == 3 && gTeamsSpawnUsed == true) { + else if (gModernMap && gGameOptions.nGameType == 3 && gTeamsSpawnUsed == true) { int maxRetries = 5; while (maxRetries-- > 0) { if (pPlayer->teamId == 0) pStartZone = &gStartZoneTeam1[Random(3)]; @@ -1077,15 +1129,39 @@ void playerReset(PLAYER *pPlayer) pPlayer->weaponQav = -1; pPlayer->qavLoop = 0; pPlayer->packItemId = -1; - for (int i = 0; i < 5; i++) - { + + for (int i = 0; i < 5; i++) { pPlayer->packSlots[i].isActive = 0; pPlayer->packSlots[i].curAmount = 0; } - for (int i = 0; i < 4; i++) { - for (int a = 0; a < 3; a++) - gPosture[i][a].frontAccel = gPosture[i][a].sideAccel = gPosture[i][a].backAccel = gDefaultAccel[a]; + ///////////////// + // reset qav scene + QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; + pQavScene->index = pQavScene->causedBy = pPlayer->sceneQav = -1; + pQavScene->qavResrc = NULL; + + // restore default movement speed + playerResetMoveSpeed(pPlayer); + + // restore default jump height + playerResetJumpHeight(pPlayer); + ///////////////// +} + +void playerResetMoveSpeed(PLAYER* pPlayer) { + for (int i = 0, k = 0; i < 4; i++) { + for (int a = 0; a < 3; a++, k++) + gPosture[i][a].frontAccel = gPosture[i][a].sideAccel = gPosture[i][a].backAccel = gDefaultAccel[k]; + } +} + +void playerResetJumpHeight(PLAYER* pPlayer) { + for (int i = 0, k = 0; i < 4; i++) { + for (int a = 0; a < 3; a++) { + gPosture[i][a].normalJumpZ = gDefaultJumpZ[k++]; + gPosture[i][a].pwupJumpZ = gDefaultJumpZ[k++]; + } } } @@ -1178,7 +1254,7 @@ char PickupItem(PLAYER *pPlayer, spritetype *pItem) { pPlayer->used2[1] = -1; dword_21EFB0[pPlayer->teamId] += 10; dword_21EFD0[pPlayer->teamId] += 240; - evSend(0, 0, 81, kCmdOn); + evSend(0, 0, 81, kCmdOn, pPlayer->nSprite); sprintf(buffer, "%s captured Red Flag!", gProfile[pPlayer->nPlayer].name); sndStartSample(8001, 255, 2, 0); viewSetMessage(buffer); @@ -1222,7 +1298,7 @@ char PickupItem(PLAYER *pPlayer, spritetype *pItem) { pPlayer->used2[0] = -1; dword_21EFB0[pPlayer->teamId] += 10; dword_21EFD0[pPlayer->teamId] += 240; - evSend(0, 0, 80, kCmdOn); + evSend(0, 0, 80, kCmdOn, pPlayer->nSprite); sprintf(buffer, "%s captured Blue Flag!", gProfile[pPlayer->nPlayer].name); sndStartSample(8000, 255, 2, 0); viewSetMessage(buffer); @@ -1383,7 +1459,7 @@ void PickUp(PLAYER *pPlayer, spritetype *pSprite) if (nType >= kItemBase && nType <= kItemMax) { pickedUp = PickupItem(pPlayer, pSprite); - if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gItemText[nType - 100]); + if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gItemText[nType - kItemBase]); } else if (nType >= kItemAmmoBase && nType < kItemAmmoMax) { pickedUp = PickupAmmo(pPlayer, pSprite); @@ -1658,30 +1734,26 @@ void ProcessInput(PLAYER *pPlayer) if (!pInput->buttonFlags.jump) pPlayer->cantJump = 0; - switch (pPlayer->posture) - { + switch (pPlayer->posture) { case 1: if (pInput->buttonFlags.jump) - zvel[nSprite] -= 0x5b05; + zvel[nSprite] -= pPosture->normalJumpZ;//0x5b05; if (pInput->buttonFlags.crouch) - zvel[nSprite] += 0x5b05; + zvel[nSprite] += pPosture->normalJumpZ;//0x5b05; break; case 2: if (!pInput->buttonFlags.crouch) pPlayer->posture = 0; break; default: - if (!pPlayer->cantJump && pInput->buttonFlags.jump && pXSprite->height == 0) - { + if (!pPlayer->cantJump && pInput->buttonFlags.jump && pXSprite->height == 0) { sfxPlay3DSound(pSprite, 700, 0, 0); - if (packItemActive(pPlayer, 4)) - zvel[nSprite] = -0x175555; - else - zvel[nSprite] = -0xbaaaa; - + if (packItemActive(pPlayer, 4)) zvel[nSprite] = pPosture->pwupJumpZ;// -0x175555; + else zvel[nSprite] = pPosture->normalJumpZ;//-0xbaaaa; - if (isShrinked(pPlayer->pSprite)) zvel[nSprite] -= -200000; - else if (isGrown(pPlayer->pSprite)) zvel[nSprite] += -250000; + + if (isShrinked(pPlayer->pSprite)) zvel[nSprite] -= gPosture[kModeHumanShrink][pPlayer->posture].normalJumpZ;//-200000; + else if (isGrown(pPlayer->pSprite)) zvel[nSprite] += gPosture[kModeHumanGrown][pPlayer->posture].normalJumpZ; //-250000; pPlayer->cantJump = 1; } @@ -1708,7 +1780,7 @@ void ProcessInput(PLAYER *pPlayer) sndStartSample(3062, 255, 2, 0); } if (!key || pPlayer->hasKey[key]) - trTriggerSector(a2, pXSector, kCmdSpritePush); + trTriggerSector(a2, pXSector, kCmdSpritePush, nSprite); else if (pPlayer == gMe) { viewSetMessage("That requires a key."); @@ -1726,7 +1798,7 @@ void ProcessInput(PLAYER *pPlayer) sndStartSample(3062, 255, 2, 0); } if (!key || pPlayer->hasKey[key]) - trTriggerWall(a2, pXWall, kCmdWallPush); + trTriggerWall(a2, pXWall, kCmdWallPush, pPlayer->nSprite); else if (pPlayer == gMe) { viewSetMessage("That requires a key."); @@ -2106,23 +2178,23 @@ void FragPlayer(PLAYER *pPlayer, int nSprite) pSprite = &sprite[nSprite]; if (pSprite && IsPlayerSprite(pSprite)) { - PLAYER *pKiller = &gPlayer[pSprite->type-kDudePlayer1]; + PLAYER *pKiller = &gPlayer[pSprite->type - kDudePlayer1]; playerFrag(pKiller, pPlayer); int nTeam1 = pKiller->teamId&1; int nTeam2 = pPlayer->teamId&1; if (nTeam1 == 0) { if (nTeam1 != nTeam2) - evSend(0, 0, 15, kCmdToggle); + evSend(0, 0, 15, kCmdToggle, pKiller->nSprite); else - evSend(0, 0, 16, kCmdToggle); + evSend(0, 0, 16, kCmdToggle, pKiller->nSprite); } else { if (nTeam1 == nTeam2) - evSend(0, 0, 16, kCmdToggle); + evSend(0, 0, 16, kCmdToggle, pKiller->nSprite); else - evSend(0, 0, 15, kCmdToggle); + evSend(0, 0, 15, kCmdToggle, pKiller->nSprite); } } } @@ -2304,7 +2376,7 @@ int playerDamageSprite(int nSource, PLAYER *pPlayer, DAMAGE_TYPE nDamageType, in { powerupClear(pPlayer); if (nXSector > 0 && xsector[nXSector].Exit) - trTriggerSector(pSprite->sectnum, &xsector[nXSector], kCmdSectorExit); + trTriggerSector(pSprite->sectnum, &xsector[nXSector], kCmdSectorExit, nSprite); pSprite->flags |= 7; for (int p = connecthead; p >= 0; p = connectpoint2[p]) { @@ -2427,15 +2499,43 @@ public: void PlayerLoadSave::Load(void) { + + const char buffer[2048] = ""; Read(dword_21EFB0, sizeof(dword_21EFB0)); Read(&gNetPlayers, sizeof(gNetPlayers)); Read(&gProfile, sizeof(gProfile)); Read(&gPlayer, sizeof(gPlayer)); - for (int i = 0; i < gNetPlayers; i++) - { + + Read((void*)&buffer, sizeof(kPlayerCtrlSigStart)); + Read(&gPlayerCtrl, sizeof(gPlayerCtrl)); + Read((void*)&buffer, sizeof(kPlayerCtrlSigEnd)); + + for (int i = 0; i < gNetPlayers; i++) { gPlayer[i].pSprite = &sprite[gPlayer[i].nSprite]; gPlayer[i].pXSprite = &xsprite[gPlayer[i].pSprite->extra]; gPlayer[i].pDudeInfo = &dudeInfo[gPlayer[i].pSprite->type-kDudeBase]; + + // by NoOne: load qav scene + if (gPlayer[i].sceneQav != -1) { + if (gPlayerCtrl[i].qavScene.qavResrc == NULL) + gPlayer[i].sceneQav = -1; + else { + QAV* pQav = qavSceneLoad(gPlayer[i].sceneQav); + if (pQav) { + gPlayerCtrl[i].qavScene.qavResrc = pQav; + gPlayerCtrl[i].qavScene.qavResrc->Preload(); + } else { + gPlayer[i].sceneQav = -1; + } + } + } + + // by NoOne: load posture info + /*for (int a = 0; a < kModeMax; a++) { + for (int b = 0; b < kPostureMax; b++) { + gPosture[a][b] = gPlayerCtrl[i].posture[a][b]; + } + }*/ } } @@ -2445,6 +2545,20 @@ void PlayerLoadSave::Save(void) Write(&gNetPlayers, sizeof(gNetPlayers)); Write(&gProfile, sizeof(gProfile)); Write(&gPlayer, sizeof(gPlayer)); + + + ////// by NoOne: copy posture to TRPLAYERCTRL before saving the game + /*for (int i = 0; i < gNetPlayers; i++) { + for (int a = 0; a < kModeMax; a++) { + for (int b = 0; b < kPostureMax; b++) { + gPlayerCtrl[i].posture[a][b] = gPosture[a][b]; + } + } + }*/ + Write((void*)kPlayerCtrlSigStart, sizeof(kPlayerCtrlSigStart)); + Write(&gPlayerCtrl, sizeof(gPlayerCtrl)); + Write((void*)kPlayerCtrlSigEnd, sizeof(kPlayerCtrlSigEnd)); + ////// } static PlayerLoadSave *myLoadSave; diff --git a/source/blood/src/player.h b/source/blood/src/player.h index 378ad611d..7bf385759 100644 --- a/source/blood/src/player.h +++ b/source/blood/src/player.h @@ -77,7 +77,7 @@ struct PLAYER { int qavCallback; bool isRunning; int posture; // stand, crouch, swim - int unused1; // --> useless + int sceneQav; // by NoOne: used to keep qav id int bobPhase; int bobAmp; int bobHeight; @@ -174,6 +174,10 @@ struct PLAYER { // by NoOne: defaut player movement speeds of all move states for gPosture extern int gDefaultAccel[12]; + +// by NoOne: defaut player jump heights of all move states for gPosture +extern int gDefaultJumpZ[24]; + struct POSTURE { int frontAccel; int sideAccel; @@ -187,6 +191,8 @@ struct POSTURE { int weaponAboveZ; int xOffset; int zOffset; + int normalJumpZ; + int pwupJumpZ; }; extern POSTURE gPosture[kModeMax][kPostureMax]; @@ -210,12 +216,19 @@ struct POWERUPINFO { int maxTime; }; - +// by NoOne: this one stores qavs anims that can be played by trigger struct QAVSCENE { - short index = -1; - QAV* qavId = NULL; + short index = -1; // index of sprite which triggered qav scene + QAV* qavResrc = NULL; + short causedBy = -1; }; -extern QAVSCENE gQavScene[kMaxPlayers]; + +// by NoOne: this one for controlling the player using triggers (movement speed, jumps and other stuff) +struct TRPLAYERCTRL { + QAVSCENE qavScene; +}; + +extern TRPLAYERCTRL gPlayerCtrl[kMaxPlayers]; extern PLAYER gPlayer[kMaxPlayers]; extern PLAYER *gMe, *gView; @@ -310,7 +323,10 @@ bool growPlayerSize(PLAYER* pPlayer, int multiplier); bool resetPlayerSize(PLAYER* pPlayer); void deactivateSizeShrooms(PLAYER* pPlayer); PLAYER* getPlayerById(short id); -void startQavScene(PLAYER* pPlayer, int qavId, int a3, char a4); -void playQavScene(PLAYER* pPlayer); +QAV* qavSceneLoad(int qavId); +void qavScenePlay(PLAYER* pPlayer); +void qavSceneDraw(PLAYER* pPlayer, int a2, int a3, int a4, int a5); +void playerResetMoveSpeed(PLAYER* pPlayer); +void playerResetJumpHeight(PLAYER* pPlayer); END_BLD_NS diff --git a/source/blood/src/seq.cpp b/source/blood/src/seq.cpp index f7394f920..6c3290e2f 100644 --- a/source/blood/src/seq.cpp +++ b/source/blood/src/seq.cpp @@ -113,12 +113,12 @@ void UpdateSprite(int nXSprite, SEQFRAME *pFrame) int scale = xsprite[nXSprite].scale; // SEQ size scaling if (pFrame->at2_0) { - if (scale) pSprite->xrepeat = mulscale8(pFrame->at2_0, scale); + if (scale) pSprite->xrepeat = ClipRange(mulscale8(pFrame->at2_0, scale), 0, 255); else pSprite->xrepeat = pFrame->at2_0; } if (pFrame->at3_0) { - if (scale) pSprite->yrepeat = mulscale8(pFrame->at3_0, scale); + if (scale) pSprite->yrepeat = ClipRange(mulscale8(pFrame->at3_0, scale), 0, 255); else pSprite->yrepeat = pFrame->at3_0; } diff --git a/source/blood/src/seq.h b/source/blood/src/seq.h index 988e43fe4..cbb799147 100644 --- a/source/blood/src/seq.h +++ b/source/blood/src/seq.h @@ -70,11 +70,11 @@ struct ACTIVE struct SEQINST { DICTNODE *hSeq; - Seq *pSequence; // mass + Seq *pSequence; int at8; int atc; short at10; - unsigned char frameIndex; // at12 + unsigned char frameIndex; char at13; void Update(ACTIVE *pActive); }; diff --git a/source/blood/src/triggers.cpp b/source/blood/src/triggers.cpp index 070017428..b52a78347 100644 --- a/source/blood/src/triggers.cpp +++ b/source/blood/src/triggers.cpp @@ -148,7 +148,7 @@ char modernTypeSetSpriteState(int nSprite, XSPRITE *pXSprite, int nState, short return 1; } -char SetWallState(int nWall, XWALL *pXWall, int nState) +char SetWallState(int nWall, XWALL *pXWall, int nState, short causedBy) { if ((pXWall->busy&0xffff) == 0 && pXWall->state == nState) return 0; @@ -156,18 +156,18 @@ char SetWallState(int nWall, XWALL *pXWall, int nState) pXWall->state = nState; evKill(nWall, 0); if (pXWall->restState != nState && pXWall->waitTime > 0) - evPost(nWall, 0, (pXWall->waitTime*120) / 10, pXWall->restState ? kCmdOn : kCmdOff); + evPost(nWall, 0, (pXWall->waitTime*120) / 10, pXWall->restState ? kCmdOn : kCmdOff, causedBy); if (pXWall->txID) { if (pXWall->command != kCmdLink && pXWall->triggerOn && pXWall->state) - evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command); + evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command, causedBy); if (pXWall->command != kCmdLink && pXWall->triggerOff && !pXWall->state) - evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command); + evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command, causedBy); } return 1; } -char SetSectorState(int nSector, XSECTOR *pXSector, int nState) +char SetSectorState(int nSector, XSECTOR *pXSector, int nState, short causedBy) { if ((pXSector->busy&0xffff) == 0 && pXSector->state == nState) return 0; @@ -177,26 +177,26 @@ char SetSectorState(int nSector, XSECTOR *pXSector, int nState) if (nState == 1) { if (pXSector->command != kCmdLink && pXSector->triggerOn && pXSector->txID) - evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command); + evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command, causedBy); if (pXSector->stopOn) { pXSector->stopOn = 0; pXSector->stopOff = 0; } else if (pXSector->reTriggerA) - evPost(nSector, 6, (pXSector->waitTimeA * 120) / 10, kCmdOff); + evPost(nSector, 6, (pXSector->waitTimeA * 120) / 10, kCmdOff, causedBy); } else { if (pXSector->command != kCmdLink && pXSector->triggerOff && pXSector->txID) - evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command); + evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command, causedBy); if (pXSector->stopOff) { pXSector->stopOn = 0; pXSector->stopOff = 0; } else if (pXSector->reTriggerB) - evPost(nSector, 6, (pXSector->waitTimeB * 120) / 10, kCmdOn); + evPost(nSector, 6, (pXSector->waitTimeB * 120) / 10, kCmdOn, causedBy); } return 1; } @@ -368,7 +368,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) { spritetype *pSprite = &sprite[nSprite]; - //if (pSprite->type != 706 && pSprite->type != 707 && pSprite->type != 37) + //if (pSprite->type != 706 && pSprite->type != 707) //viewSetSystemMessage("SPRITE %d (TYPE %d), EVENT INITED BY: %d", nSprite, pSprite->type, event.causedBy); if (gModernMap) { @@ -397,8 +397,30 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) } if (gModernMap) { + switch (pSprite->type) { + // allow triggering players + case kDudePlayer1: + case kDudePlayer2: + case kDudePlayer3: + case kDudePlayer4: + case kDudePlayer5: + case kDudePlayer6: + case kDudePlayer7: + case kDudePlayer8: + switch (event.cmd) { + case kCmdOff: + SetSpriteState(nSprite, pXSprite, 0, event.causedBy); + break; + case kCmdOn: + SetSpriteState(nSprite, pXSprite, 1, event.causedBy); + break; + default: + SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy); + break; + } + return; // add linking for path markers and stacks feature case kMarkerLowWater: case kMarkerUpWater: @@ -557,10 +579,14 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) case kMarkerWarpDest: if (pXSprite->txID <= 0) { - if (SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy) == 1) - if (pXSprite->data1 > 0) useTeleportTarget(pXSprite, NULL); - else if (pXSprite->data1 == 0 && event.causedBy >= 0) - useTeleportTarget(pXSprite, &sprite[event.causedBy]); + if (SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy) == 1) { + if (pXSprite->data1 == 0 && spriRangeIsFine(event.causedBy)) useTeleportTarget(pXSprite, &sprite[event.causedBy]); + else if (pXSprite->data1 > 0) { + PLAYER* pPlayer = getPlayerById(pXSprite->data1); + if (pPlayer != NULL) + useTeleportTarget(pXSprite, pPlayer->pSprite); + } + } return; } modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy); @@ -569,9 +595,13 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) case kModernSpriteDamager: if (pXSprite->txID <= 0) { if (SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy) == 1) { - if (pXSprite->data1 > 0) useSpriteDamager(pXSprite, NULL); - else if (pXSprite->data1 == 0 && event.causedBy >= 0) + if (spriRangeIsFine(event.causedBy)) useSpriteDamager(pXSprite, &sprite[event.causedBy]); + else if (pXSprite->data1 > 0) { + PLAYER* pPlayer = getPlayerById(pXSprite->data1); + if (pPlayer != NULL) + useSpriteDamager(pXSprite, pPlayer->pSprite); + } } return; } @@ -587,8 +617,15 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy); return; - case kModernObjPicnumChanger: case kModernObjSizeChanger: + if (pXSprite->txID <= 0 && spriRangeIsFine(event.causedBy)) { + if (SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy) == 1) + useObjResizer(pXSprite, 3, event.causedBy); + return; + } + modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy); + return; + case kModernObjPicnumChanger: case kModernSectorFXChanger: case kModernObjDataChanger: case kModernConcussSprite: @@ -826,159 +863,228 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) } return; case kModernPlayerControl: // WIP - PLAYER* pPlayer = NULL; - if ((pPlayer = getPlayerById(pXSprite->data1)) == NULL || pPlayer->pXSprite->health <= 0) return; - else if (event.cmd < kCmdNumberic) { // play qav as weapon qav + PLAYER* pPlayer = NULL; int nPlayer = pXSprite->data1; + if (pXSprite->data1 == 0 && spriRangeIsFine(event.causedBy)) + nPlayer = sprite[event.causedBy].type; + + if ((pPlayer = getPlayerById(nPlayer)) == NULL || pPlayer->pXSprite->health <= 0) + return; + + TRPLAYERCTRL* pCtrl = pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; + if (event.cmd >= kCmdNumberic) { switch (event.cmd) { - case kCmdOff: - if (gQavScene[pPlayer->nPlayer].index == nSprite) { - viewSetSystemMessage("OFF %d", gQavScene[pPlayer->nPlayer].index); - - gQavScene[pPlayer->nPlayer].index = -1; - gQavScene[pPlayer->nPlayer].qavId = NULL; - pPlayer->weaponQav = -1; - // restore weapon - pPlayer->input.newWeapon = pPlayer->curWeapon = pXSprite->dropMsg; - WeaponRaise(pPlayer); - - } + case kCmdNumberic + 3:// start playing qav scene + viewSetSystemMessage("START QAV %d, %d", pXSprite->rxID, pXSprite->txID); + if (pCtrl->qavScene.index != nSprite || pXSprite->Interrutable) + trPlayerCtrlStartScene(pXSprite, pPlayer, event.causedBy); break; - case kCmdOn: { - if (gQavScene[pPlayer->nPlayer].index == nSprite && !pXSprite->Interrutable) break; - QAV* pQav = NULL; DICTNODE* hQav = gSysRes.Lookup(pXSprite->data2, "QAV"); - if (hQav) { - - // save current weapon - pXSprite->dropMsg = pPlayer->curWeapon; + case kCmdNumberic + 4: // stop playing qav scene + viewSetSystemMessage("STOP QAV", gPlayerCtrl[pPlayer->nPlayer].qavScene.index); + if (pCtrl->qavScene.index == nSprite) + trPlayerCtrlStopScene(pXSprite, pPlayer); + break; + } - short nIndex = gQavScene[pPlayer->nPlayer].index; - if (nIndex > -1 && nIndex != nSprite && sprite[nIndex].extra >= 0) - pXSprite->dropMsg = xsprite[sprite[nIndex].extra].dropMsg; - - if (nIndex < 0) - WeaponLower(pPlayer); - - pQav = (QAV*)gSysRes.Load(hQav); - pXSprite->sysData1 = ClipLow((pQav->at10 * pXSprite->waitTime) / 4, 0); // how many times animation should be played - gQavScene[pPlayer->nPlayer].index = nSprite; - - gQavScene[pPlayer->nPlayer].qavId = pQav; - gQavScene[pPlayer->nPlayer].qavId->Preload(); + return; + } - pPlayer->weaponQav = pXSprite->data2; - pPlayer->weaponTimer = gQavScene[pPlayer->nPlayer].qavId->at10; - pPlayer->qavCallback = (pXSprite->data3 > 0) ? ClipRange(pXSprite->data3 - 1, 0, 32) : -1; - pPlayer->qavLoop = false; - - //startQavScene(pPlayer, pXSprite->data2, -1, 0); - - - //viewSetSystemMessage("QAV %d", pXSprite->data2); - - } else { - viewSetSystemMessage("Failed to load QAV animation #%d", pXSprite->data2); + /// !!! COMMANDS OF THE CURRENT SPRITE, NOT OF THE EVENT !!! /// + switch (pXSprite->command) { + case kCmdLink: // copy properties of sprite to player + trPlayerCtrlLink(pXSprite, pPlayer); + break; + case kCmdNumberic: // player life form + if (pXSprite->data2 >= kModeHuman || pXSprite->data2 <= kModeHumanShrink) { + playerSetRace(pPlayer, pXSprite->data2); + switch (pPlayer->lifeMode) { + case kModeHuman: + case kModeBeast: + resetPlayerSize(pPlayer); + break; + case kModeHumanShrink: + shrinkPlayerSize(pPlayer, 2); + break; + case kModeHumanGrown: + growPlayerSize(pPlayer, 2); + break; } + } + break; + + case kCmdNumberic + 1: // 65 + // player movement speed (for all players ATM) + if (valueIsBetween(pXSprite->data2, -1, 32767)) { + for (int i = 0, speed = pXSprite->data2 << 1, k = 0; i < kModeMax; i++) { + for (int a = 0; a < kPostureMax; a++, k++) { + int defSpeed = gDefaultAccel[k]; + if (pXSprite->data1 == 100) + gPosture[i][a].frontAccel = gPosture[i][a].sideAccel = gPosture[i][a].backAccel = defSpeed; + else if (speed >= 0) + gPosture[i][a].frontAccel = gPosture[i][a].sideAccel = gPosture[i][a].backAccel = ClipRange(mulscale8(defSpeed, speed), 0, 65535); + } + } + viewSetSystemMessage("MOVEMENT: %d %d %d", pXSprite->rxID,pSprite->index, gPosture[0][0].frontAccel); + } + + // player jump height (for all players ATM) + if (valueIsBetween(pXSprite->data3, -1, 32767)) { + for (int i = 0, jump = pXSprite->data3 * 3, k = 0; i < kModeMax; i++) { + for (int a = 0; a < kPostureMax; a++) { + int njmp = gDefaultJumpZ[k++]; int pjmp = gDefaultJumpZ[k++]; + if (a != kPostureStand) continue; + else if (pXSprite->data3 == 100) { + gPosture[i][a].normalJumpZ = njmp; + gPosture[i][a].pwupJumpZ = pjmp; + } else if (jump >= 0) { + gPosture[i][a].normalJumpZ = ClipRange(mulscale8(njmp, jump), -0x200000, 0); + gPosture[i][a].pwupJumpZ = ClipRange(mulscale8(pjmp, jump), -0x200000, 0); + } + } + } + viewSetSystemMessage("JUMPING: %d", gPosture[0][0].normalJumpZ); + } + break; + + case kCmdNumberic + 2: // 66 + // player screen effects + if (pXSprite->data3 < 0) break; + switch (pXSprite->data2) { + case 1: // tilting + pPlayer->tiltEffect = pXSprite->data3; + break; + case 2: // pain + pPlayer->painEffect = pXSprite->data3; + break; + case 3: // blind + pPlayer->blindEffect = pXSprite->data3; + break; + case 4: // pickup + pPlayer->pickupEffect = pXSprite->data3; + break; + case 5: // quakeEffect + pPlayer->quakeEffect = pXSprite->data3; + break; + case 6: // visibility + pPlayer->visibility = pXSprite->data3; + break; + case 7: // delirium + pPlayer->pwUpTime[kPwUpDeliriumShroom] = ClipHigh(pXSprite->data3 << 1, 432000); break; } - default: - if (gQavScene[pPlayer->nPlayer].index != nSprite) evPost(nSprite, 3, 0, kCmdOn, event.causedBy); - else evPost(nSprite, 3, 0, kCmdOff, event.causedBy); - break; - } - } else if (event.cmd < kCmdNumbericMax) { - switch (event.cmd) { - case kCmdNumberic: // player life form - if (pXSprite->data2 >= kModeHuman || pXSprite->data2 <= kModeHumanShrink) { - playerSetRace(pPlayer, pXSprite->data2); - switch (pPlayer->lifeMode) { - case kModeHuman: - case kModeBeast: - resetPlayerSize(pPlayer); - break; - case kModeHumanShrink: - shrinkPlayerSize(pPlayer, 2); - break; - case kModeHumanGrown: - growPlayerSize(pPlayer, 2); - break; - } + break; + + case kCmdNumberic + 3: // 67 + // start playing qav scene + if (pCtrl->qavScene.index != nSprite || pXSprite->Interrutable) + trPlayerCtrlStartScene(pXSprite, pPlayer, event.causedBy); + break; + + case kCmdNumberic + 4: // 68 + // stop playing qav scene + if (pCtrl->qavScene.index == nSprite) + trPlayerCtrlStopScene(pXSprite, pPlayer); + break; + + case kCmdNumberic + 5: // 69 + // set player sprite and look angles + if (pXSprite->data2 == 0) { + + // look angle + if (valueIsBetween(pXSprite->data3, -128, 128)) { + CONSTEXPR int upAngle = 289; CONSTEXPR int downAngle = -347; + CONSTEXPR double lookStepUp = 4.0 * upAngle / 60.0; + CONSTEXPR double lookStepDown = -4.0 * downAngle / 60.0; + + int look = pXSprite->data3 << 5; + if (look > 0) pPlayer->q16look = fix16_min(mulscale8(F16(lookStepUp), look), F16(upAngle)); + else if (look < 0) pPlayer->q16look = -fix16_max(mulscale8(F16(lookStepDown), abs(look)), F16(downAngle)); + else pPlayer->q16look = 0; } - break; - case kCmdNumberic + 1: // player movement speed (for all players ATM) - for (int i = (pSprite->flags & kModernTypeFlag1) ? pPlayer->lifeMode : 0; i < 4; i++) { - for (int a = 0; a < 3; a++) { - int speed = pXSprite->data2 << 1; - if (speed > 0) speed = ClipRange(mulscale8(gDefaultAccel[a], speed), 0, 65535); - else if (speed < 0) speed = gDefaultAccel[a]; + // angle + if ((pSprite->flags & kModernTypeFlag1) && valueIsBetween(pXSprite->data4, 0, kAng180)) + pPlayer->pSprite->ang = pXSprite->data4; + else if (pXSprite->data4 == 1) + pPlayer->pSprite->ang = pSprite->ang; - gPosture[i][a].frontAccel = gPosture[i][a].sideAccel = gPosture[i][a].backAccel = speed; - //viewSetSystemMessage("%d", speed); - } + } + //viewSetSystemMessage("ANGLE: %d, SLOPE: %d", pPlayer->pSprite->ang, pPlayer->q16look); + break; - if (pSprite->flags & kModernTypeFlag1) // for current lifeform only - break; - } - viewSetSystemMessage("MOVEMENT: %d", gPosture[0][0].frontAccel); - break; - case kCmdNumberic + 2: // player screen effects - if (pXSprite->data3 < 0) break; - switch (pXSprite->data2) { - case 1: // tilting - pPlayer->tiltEffect = pXSprite->data3; - break; - case 2: // pain - pPlayer->painEffect = pXSprite->data3; - break; - case 3: // blind - pPlayer->blindEffect = pXSprite->data3; - break; - case 4: // pickup - pPlayer->pickupEffect = pXSprite->data3; - break; - case 5: // quakeEffect - pPlayer->quakeEffect = pXSprite->data3; - break; - case 6: // visibility - pPlayer->visibility = pXSprite->data3; - break; - case 7: // delirium - pPlayer->pwUpTime[kPwUpDeliriumShroom] = ClipHigh(pXSprite->data3 << 1, 432000); - if (pXSprite->data3 > 0) powerupActivate(pPlayer, kPwUpDeliriumShroom); - else powerupDeactivate(pPlayer, kPwUpDeliriumShroom); - break; - } - break; - case kCmdNumberic + 6: // erase player stuff... - switch (pXSprite->data2) { - case 0: - case 1: { - // erase all weapons except pitchfork - WeaponLower(pPlayer); + case kCmdNumberic + 6: // 70 + // erase player stuff... + switch (pXSprite->data2) { + // erase all + case 0: + // erase all weapons except pitchfork + case 1: + WeaponLower(pPlayer); - for (int i = 0; i < 14; i++) { - pPlayer->hasWeapon[i] = false; - // also erase ammo - if (i < 12 && pXSprite->data3 == 1) - pPlayer->ammoCount[i] = 0; + for (int i = 0; i < 14; i++) { + pPlayer->hasWeapon[i] = false; + // also erase ammo + if (i < 12 && pXSprite->data3 == 1) + pPlayer->ammoCount[i] = 0; + } + + pPlayer->hasWeapon[1] = true; + pPlayer->curWeapon = 0; + pPlayer->nextWeapon = 1; + + WeaponRaise(pPlayer); + if (pXSprite->data2) break; + // erase all armor + case 2: + for (int i = 0; i < 3; i++) pPlayer->armor[i] = 0; + if (pXSprite->data2) break; + // erase all pack items + case 3: + for (int i = 0; i < 5; i++) { + pPlayer->packSlots[i].isActive = pPlayer->packSlots[i].curAmount = 0; + pPlayer->packItemId = -1; + } + if (pXSprite->data2) break; + // erase all keys + case 4: + for (int i = 0; i < 8; i++) pPlayer->hasKey[i] = false; + if (pXSprite->data2) break; + } + break; + + case kCmdNumberic + 7: // 71 + // give something to player... + switch (pXSprite->data2) { + case 1: // give N weapon and default ammo for it + case 2: // give just N ammo for selected weapon + if (pXSprite->data3 <= 0 || pXSprite->data3 > 12) break; + for (int i = 0; i < 12; i++) { + if (gWeaponItemData[i].type != pXSprite->data3) continue; + + WEAPONITEMDATA* pWeaponData = &gWeaponItemData[i]; int nAmmoType = pWeaponData->ammoType; + if (pXSprite->data2 == 1) { + pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + pWeaponData->count, gAmmoInfo[nAmmoType].max); + } else { + pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + pXSprite->data3, gAmmoInfo[nAmmoType].max); + break; } + + pPlayer->hasWeapon[pXSprite->data2] = true; - pPlayer->hasWeapon[1] = true; - pPlayer->curWeapon = 0; - pPlayer->nextWeapon = 1; - - WeaponRaise(pPlayer); + if (pXSprite->data4 == 0) { // switch on it + if (pPlayer->sceneQav >= 0) { + XSPRITE* pXScene = &xsprite[sprite[pCtrl->qavScene.index].extra]; + pXScene->dropMsg = pXSprite->data3; + } else if (pPlayer->curWeapon != pXSprite->data3) { + pPlayer->input.newWeapon = pXSprite->data3; + WeaponRaise(pPlayer); + } + } break; } - case 2: - // erase all armor - for (int i = 0; i < 3; i++) - pPlayer->armor[i] = 0; - break; - } - break; - } + break; + } + break; } return; } @@ -1341,8 +1447,172 @@ void useConcussSprite(XSPRITE* pXSource, spritetype* pSprite) { //debrisConcuss(pXSource->reference, nIndex, pSprite->x - 100, pSprite->y - 100, pSprite->z - 100, pXSource->data1); } +void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy) { + + int nSource = sprite[pXSource->reference].index; TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; + QAV* pQav = qavSceneLoad(pXSource->data2); + if (pQav != NULL) { + + // save current weapon + pXSource->dropMsg = pPlayer->curWeapon; + + short nIndex = pCtrl->qavScene.index; + if (nIndex > -1 && nIndex != nSource && sprite[nIndex].extra >= 0) + pXSource->dropMsg = xsprite[sprite[nIndex].extra].dropMsg; + + if (nIndex < 0) + WeaponLower(pPlayer); + + pXSource->sysData1 = ClipLow((pQav->at10 * pXSource->waitTime) / 4, 0); // how many times animation should be played + + pCtrl->qavScene.index = nSource; + pCtrl->qavScene.qavResrc = pQav; + pCtrl->qavScene.causedBy = causedBy; + + pCtrl->qavScene.qavResrc->Preload(); + + pPlayer->sceneQav = pXSource->data2; + pPlayer->weaponTimer = pCtrl->qavScene.qavResrc->at10; + pPlayer->qavCallback = (pXSource->data3 > 0) ? ClipRange(pXSource->data3 - 1, 0, 32) : -1; + pPlayer->qavLoop = false; + + } + +} + +void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer) { + + TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; + viewSetSystemMessage("OFF %d", pCtrl->qavScene.index); + + pXSource->sysData1 = 0; + pCtrl->qavScene.index = -1; + pCtrl->qavScene.qavResrc = NULL; + pPlayer->sceneQav = -1; + + // restore weapon + if (pPlayer->pXSprite->health > 0) { + int oldWeapon = (pXSource->dropMsg != 0) ? pXSource->dropMsg : 1; + pPlayer->input.newWeapon = pPlayer->curWeapon = oldWeapon; + WeaponRaise(pPlayer); + } + +} + +void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer) { + + pPlayer->pXSprite->txID = pXSource->txID; + pPlayer->pXSprite->command = pXSource->data2; + pPlayer->pXSprite->triggerOn = pXSource->triggerOn; + pPlayer->pXSprite->triggerOff = pXSource->triggerOff; + pPlayer->pXSprite->busyTime = pXSource->busyTime; + pPlayer->pXSprite->waitTime = pXSource->waitTime; + pPlayer->pXSprite->restState = pXSource->restState; + + pPlayer->pXSprite->Push = pXSource->Push; + pPlayer->pXSprite->Impact = pXSource->Impact; + pPlayer->pXSprite->Vector = pXSource->Vector; + pPlayer->pXSprite->Touch = pXSource->Touch; + pPlayer->pXSprite->Sight = pXSource->Sight; + pPlayer->pXSprite->Proximity = pXSource->Proximity; + + pPlayer->pXSprite->Decoupled = pXSource->Decoupled; + pPlayer->pXSprite->Interrutable = pXSource->Interrutable; + pPlayer->pXSprite->DudeLockout = pXSource->DudeLockout; + + //pPlayer->pXSprite->data1 = pXSource->data1; + //pPlayer->pXSprite->data2 = pXSource->data2; + //pPlayer->pXSprite->data3 = pXSource->data3; + //pPlayer->pXSprite->data4 = pXSource->data4; + + pPlayer->pXSprite->key = pXSource->key; + pPlayer->pXSprite->dropMsg = pXSource->dropMsg; + +} + +void useObjResizer(XSPRITE* pXSource, short objType, int objIndex) { + switch (objType) { + // for sectors + case 6: + if (valueIsBetween(pXSource->data1, -1, 32767)) + sector[objIndex].floorxpanning = ClipRange(pXSource->data1, 0, 255); + + if (valueIsBetween(pXSource->data2, -1, 32767)) + sector[objIndex].floorypanning = ClipRange(pXSource->data2, 0, 255); + + if (valueIsBetween(pXSource->data3, -1, 32767)) + sector[objIndex].ceilingxpanning = ClipRange(pXSource->data3, 0, 255); + + if (valueIsBetween(pXSource->data4, -1, 65535)) + sector[objIndex].ceilingypanning = ClipRange(pXSource->data4, 0, 255); + break; + // for sprites + case 3: + + /*PLAYER* pPlayer = NULL; + if (playerRXRngIsFine(pXSource->txID) && (pPlayer = getPlayerById((kChannelPlayer0 - kChannelPlayer7) + kMaxPlayers)) != NULL) + objIndex = pPlayer->nSprite;*/ + + // resize by seq scaling + if (sprite[pXSource->reference].flags & kModernTypeFlag1) { + if (valueIsBetween(pXSource->data1, -255, 32767)) { + int mulDiv = (valueIsBetween(pXSource->data2, 0, 257)) ? pXSource->data2 : 256; + if (pXSource->data1 > 0) xsprite[sprite[objIndex].extra].scale = mulDiv * ClipHigh(pXSource->data1, 25); + else if (pXSource->data1 < 0) xsprite[sprite[objIndex].extra].scale = mulDiv / ClipHigh(abs(pXSource->data1), 25); + else xsprite[sprite[objIndex].extra].scale = 0; + + // request properties update for custom dude + switch (sprite[objIndex].type) { + case kDudeModernCustom: + case kDudeModernCustomBurning: + gGenDudeExtra[objIndex].updReq[kGenDudePropertyAttack] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyMass] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyDamage] = true; + break; + } + } + + // resize by repeats + } else { + + if (valueIsBetween(pXSource->data1, -1, 32767)) + sprite[objIndex].xrepeat = ClipRange(pXSource->data1, 0, 255); + + if (valueIsBetween(pXSource->data2, -1, 32767)) + sprite[objIndex].yrepeat = ClipRange(pXSource->data2, 0, 255); + + } + + + if (valueIsBetween(pXSource->data3, -1, 32767)) + sprite[objIndex].xoffset = ClipRange(pXSource->data3, 0, 255); + + if (valueIsBetween(pXSource->data4, -1, 65535)) + sprite[objIndex].yoffset = ClipRange(pXSource->data4, 0, 255); + break; + + // for walls + case 0: + if (valueIsBetween(pXSource->data1, -1, 32767)) + wall[objIndex].xrepeat = ClipRange(pXSource->data1, 0, 255); + + if (valueIsBetween(pXSource->data2, -1, 32767)) + wall[objIndex].yrepeat = ClipRange(pXSource->data2, 0, 255); + + if (valueIsBetween(pXSource->data3, -1, 32767)) + wall[objIndex].xpanning = ClipRange(pXSource->data3, 0, 255); + + if (valueIsBetween(pXSource->data4, -1, 65535)) + wall[objIndex].ypanning = ClipRange(pXSource->data4, 0, 255); + break; + } + +} + void usePropertiesChanger(XSPRITE* pXSource, short objType, int objIndex) { + spritetype* pSource = &sprite[pXSource->reference]; + switch (objType) { // for walls @@ -1350,13 +1620,18 @@ void usePropertiesChanger(XSPRITE* pXSource, short objType, int objIndex) { walltype* pWall = &wall[objIndex]; int old = -1; // data3 = set wall hitag - if (valueIsBetween(pXSource->data3, -1, 32767)) - pWall->hitag = pXSource->data3; + if (valueIsBetween(pXSource->data3, -1, 32767)) { + if ((pSource->flags & kModernTypeFlag1)) pWall->hitag = pWall->hitag |= pXSource->data3; + else pWall->hitag = pXSource->data3; + } // data4 = set wall cstat if (valueIsBetween(pXSource->data4, -1, 65535)) { old = pWall->cstat; - pWall->cstat = pXSource->data4; // set new cstat + + // set new cstat + if ((pSource->flags & kModernTypeFlag1)) pWall->cstat = pWall->cstat |= pXSource->data4; // relative + else pWall->cstat = pXSource->data4; // absolute // and hanlde exceptions if ((old & 0x2) && !(pWall->cstat & 0x2)) pWall->cstat |= 0x2; // kWallBottomSwap @@ -1385,7 +1660,11 @@ void usePropertiesChanger(XSPRITE* pXSource, short objType, int objIndex) { // data3 = set sprite hitag if (valueIsBetween(pXSource->data3, -1, 32767)) { - old = pSprite->hitag; pSprite->hitag = pXSource->data3; // set new hitag + old = pSprite->hitag; + + // set new hitag + if ((pSource->flags & kModernTypeFlag1)) pSprite->hitag = pSource->hitag |= pXSource->data3; // relative + else pSprite->hitag = pXSource->data3; // absolute // and handle exceptions if ((old & kHitagFree) && !(pSprite->hitag & kHitagFree)) pSprite->hitag |= kHitagFree; @@ -1511,7 +1790,10 @@ void usePropertiesChanger(XSPRITE* pXSource, short objType, int objIndex) { if (valueIsBetween(pXSource->data4, -1, 65535)) { old = pSprite->cstat; - pSprite->cstat = pXSource->data4; // set new cstat + + // set new cstat + if ((pSource->flags & kModernTypeFlag1)) pSprite->cstat = pSprite->cstat |= pXSource->data4; // relative + else pSprite->cstat = pXSource->data4; // absolute // and handle exceptions if ((old & 0x1000) && !(pSprite->cstat & 0x1000)) pSprite->cstat |= 0x1000; //kSpritePushable @@ -1552,12 +1834,16 @@ void usePropertiesChanger(XSPRITE* pXSource, short objType, int objIndex) { } // data3 = sector ceil cstat - if (valueIsBetween(pXSource->data3, -1, 32767)) - sector[objIndex].ceilingstat = pXSource->data3; + if (valueIsBetween(pXSource->data3, -1, 32767)) { + if ((pSource->flags & kModernTypeFlag1)) sector[objIndex].ceilingstat = sector[objIndex].ceilingstat |= pXSource->data3; + else sector[objIndex].ceilingstat = pXSource->data3; + } // data4 = sector floor cstat - if (valueIsBetween(pXSource->data4, -1, 65535)) - sector[objIndex].floorstat = pXSource->data4; + if (valueIsBetween(pXSource->data4, -1, 65535)) { + if ((pSource->flags & kModernTypeFlag1)) sector[objIndex].floorstat = sector[objIndex].floorstat |= pXSource->data4; + else sector[objIndex].floorstat = pXSource->data4; + } break; @@ -1580,27 +1866,6 @@ void useTeleportTarget(XSPRITE* pXSource, spritetype* pSprite) { spritetype* pSource = &sprite[pXSource->reference]; XSECTOR* pXSector = (sector[pSource->sectnum].extra >= 0) ? &xsector[sector[pSource->sectnum].extra] : NULL; - if (pSprite == NULL) { - - if (pXSource->data1 > 0) { - for (int i = connecthead; i >= 0; i = connectpoint2[i]) { - - if (pXSource->data1 < kMaxPlayers) // relative to connected players - if (pXSource->data1 != (i + 1)) - continue; - else if (pXSource->data1 < (kDudePlayer1 + kMaxPlayers)) // absolute type - if (pXSource->data1 >= kDudePlayer1 && (pXSource->data1 + (kDudePlayer1 - 1)) == gPlayer[i].pSprite->type) - continue; - - useTeleportTarget(pXSource, gPlayer[i].pSprite); - return; - - } - } - - return; - } - pSprite->x = pSource->x; pSprite->y = pSource->y; pSprite->z += (sector[pSource->sectnum].floorz - sector[pSprite->sectnum].floorz); @@ -1611,10 +1876,8 @@ void useTeleportTarget(XSPRITE* pXSource, spritetype* pSprite) { if (pXSector != NULL && pXSector->Underwater) xsprite[pSprite->extra].medium = kMediumWater; else xsprite[pSprite->extra].medium = kMediumNormal; - if (pXSource->data2 == 1) - pSprite->ang = pSource->ang; - - if (pXSource->data3 == 1) + if (pXSource->data2 == 1) pSprite->ang = pSource->ang; + if (pXSource->data3 == 1) xvel[pSprite->xvel] = yvel[pSprite->xvel] = zvel[pSprite->xvel] = 0; viewBackupSpriteLoc(pSprite->xvel, pSprite); @@ -1627,9 +1890,8 @@ void useTeleportTarget(XSPRITE* pXSource, spritetype* pSprite) { PLAYER* pPlayer = &gPlayer[pSprite->type - kDudePlayer1]; playerResetInertia(pPlayer); - if (pXSource->data2 == 1) { + if (pXSource->data2 == 1) pPlayer->zViewVel = pPlayer->zWeaponVel = 0; - } } } @@ -1639,24 +1901,29 @@ void useEffectGen(XSPRITE * pXSource, spritetype * pSprite) { if (pSprite->extra < 0) return; int fxId = pXSource->data2 + Random(pXSource->data3); - int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); spritetype * pEffect = NULL; + int pos, top, bottom; GetSpriteExtents(pSprite, &top, &bottom); spritetype* pEffect = NULL; - if (fxId > 0 && fxId < 57 && (pEffect = gFX.fxSpawn((FX_ID) fxId, pSprite->sectnum, pSprite->x, pSprite->y, top, 0)) != NULL) { - if ((pEffect->cstat & CSTAT_SPRITE_ALIGNMENT_WALL) && (pEffect->cstat & CSTAT_SPRITE_ONE_SIDED)) - pEffect->cstat &= ~CSTAT_SPRITE_ONE_SIDED; - - if (pSprite->flags & kModernTypeFlag1) { - if (pEffect->pal <= 0) pEffect->pal = pSprite->pal; - if (pEffect->xrepeat <= 0) pEffect->xrepeat = pSprite->xrepeat; - if (pEffect->yrepeat <= 0) pEffect->yrepeat = pSprite->yrepeat; - if (pEffect->shade == 0) pEffect->shade = pSprite->shade; - } + // select where exactly effect should be spawned + switch (pXSource->data4) { + case 1: + pos = bottom; // bottom of sprite + break; + default: + pos = top; // top of sprite + break; } - if (pXSource->data4 > 0) - sfxPlay3DSound(pSprite, pXSource->data4, -1, 0); - // kModernTypeFlag = 2: kill the sound before play again + if (fxId > 0 && fxId < 57 && (pEffect = gFX.fxSpawn((FX_ID) fxId, pSprite->sectnum, pSprite->x, pSprite->y, pos, 0)) != NULL) { + + if (pEffect->cstat & CSTAT_SPRITE_ONE_SIDED) pEffect->cstat &= ~CSTAT_SPRITE_ONE_SIDED; + if (pSprite->flags & kModernTypeFlag1) { + pEffect->pal = pSprite->pal; + pEffect->xrepeat = pSprite->xrepeat; + pEffect->yrepeat = pSprite->yrepeat; + pEffect->shade = pSprite->shade; + } + } } @@ -1740,17 +2007,10 @@ void useSectorWindGen(XSPRITE* pXSource, sectortype* pSector) { } void useSpriteDamager(XSPRITE* pXSource, spritetype* pSprite) { - int dmgType = (pXSource->data3 >= 7) ? Random(6) : ClipRange(pXSource->data3, 0, 6); - int dmg = (pXSource->data4 == 0) ? 65535 : ClipRange(pXSource->data4, 1, 65535); - - // just damage TX ID sprite - if (pSprite != NULL) actDamageSprite(pSprite->index, pSprite, (DAMAGE_TYPE) dmgType, dmg); - else if (pXSource->data1 > 0) { - - PLAYER* pPlayer = getPlayerById(pXSource->data1); - if (pPlayer != NULL) - actDamageSprite(sprite[pXSource->reference].index, pPlayer->pSprite, (DAMAGE_TYPE)dmgType, dmg); - + if (pSprite != NULL) { + int dmgType = (pXSource->data2 == 7) ? Random(6) : ClipRange(pXSource->data2, 0, 6); + int dmg = (pXSource->data3 == 0) ? 65535 : ClipRange(pXSource->data3, 1, 65535); + actDamageSprite(pSprite->index, pSprite, (DAMAGE_TYPE)dmgType, dmg); } } @@ -1883,13 +2143,13 @@ void OperateWall(int nWall, XWALL *pXWall, EVENT event) { case kSwitchOneWay: switch (event.cmd) { case kCmdOff: - SetWallState(nWall, pXWall, 0); + SetWallState(nWall, pXWall, 0, event.causedBy); break; case kCmdOn: - SetWallState(nWall, pXWall, 1); + SetWallState(nWall, pXWall, 1, event.causedBy); break; default: - SetWallState(nWall, pXWall, pXWall->restState ^ 1); + SetWallState(nWall, pXWall, pXWall->restState ^ 1, event.causedBy); break; } return; @@ -1906,13 +2166,13 @@ void OperateWall(int nWall, XWALL *pXWall, EVENT event) { switch (event.cmd) { case kCmdOn: case kCmdWallImpact: - bStatus = SetWallState(nWall, pXWall, 1); + bStatus = SetWallState(nWall, pXWall, 1, event.causedBy); break; case kCmdOff: - bStatus = SetWallState(nWall, pXWall, 0); + bStatus = SetWallState(nWall, pXWall, 0, event.causedBy); break; default: - bStatus = SetWallState(nWall, pXWall, pXWall->state ^ 1); + bStatus = SetWallState(nWall, pXWall, pXWall->state ^ 1, event.causedBy); break; } @@ -1929,13 +2189,13 @@ void OperateWall(int nWall, XWALL *pXWall, EVENT event) { default: switch (event.cmd) { case kCmdOff: - SetWallState(nWall, pXWall, 0); + SetWallState(nWall, pXWall, 0, event.causedBy); break; case kCmdOn: - SetWallState(nWall, pXWall, 1); + SetWallState(nWall, pXWall, 1, event.causedBy); break; default: - SetWallState(nWall, pXWall, pXWall->state ^ 1); + SetWallState(nWall, pXWall, pXWall->state ^ 1, event.causedBy); break; } return; @@ -2253,7 +2513,7 @@ int GetCrushedSpriteExtents(unsigned int nSector, int *pzTop, int *pzBot) return vc; } -int VCrushBusy(unsigned int nSector, unsigned int a2) +int VCrushBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); int nXSector = sector[nSector].extra; @@ -2282,17 +2542,17 @@ int VCrushBusy(unsigned int nSector, unsigned int a2) sector[nSector].floorz = v10; pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); return 3; } return 0; } -int VSpriteBusy(unsigned int nSector, unsigned int a2) +int VSpriteBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); int nXSector = sector[nSector].extra; @@ -2331,17 +2591,17 @@ int VSpriteBusy(unsigned int nSector, unsigned int a2) } pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); return 3; } return 0; } -int VDoorBusy(unsigned int nSector, unsigned int a2) +int VDoorBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); int nXSector = sector[nSector].extra; @@ -2430,17 +2690,17 @@ int VDoorBusy(unsigned int nSector, unsigned int a2) ZTranslateSector(nSector, pXSector, a2, nWave); pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); return 3; } return 0; } -int HDoorBusy(unsigned int nSector, unsigned int a2) +int HDoorBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); sectortype *pSector = §or[nSector]; @@ -2458,17 +2718,17 @@ int HDoorBusy(unsigned int nSector, unsigned int a2) ZTranslateSector(nSector, pXSector, a2, nWave); pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); return 3; } return 0; } -int RDoorBusy(unsigned int nSector, unsigned int a2) +int RDoorBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); sectortype *pSector = §or[nSector]; @@ -2485,17 +2745,17 @@ int RDoorBusy(unsigned int nSector, unsigned int a2) ZTranslateSector(nSector, pXSector, a2, nWave); pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); return 3; } return 0; } -int StepRotateBusy(unsigned int nSector, unsigned int a2) +int StepRotateBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); sectortype *pSector = §or[nSector]; @@ -2518,10 +2778,10 @@ int StepRotateBusy(unsigned int nSector, unsigned int a2) } pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); pXSector->data = vbp&2047; return 3; @@ -2529,7 +2789,7 @@ int StepRotateBusy(unsigned int nSector, unsigned int a2) return 0; } -int GenSectorBusy(unsigned int nSector, unsigned int a2) +int GenSectorBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); sectortype *pSector = §or[nSector]; @@ -2538,17 +2798,17 @@ int GenSectorBusy(unsigned int nSector, unsigned int a2) XSECTOR *pXSector = &xsector[nXSector]; pXSector->busy = a2; if (pXSector->command == kCmdLink && pXSector->txID) - evSend(nSector, 6, pXSector->txID, kCmdLink); + evSend(nSector, 6, pXSector->txID, kCmdLink, causedBy); if ((a2&0xffff) == 0) { - SetSectorState(nSector, pXSector, a2>>16); + SetSectorState(nSector, pXSector, a2>>16, causedBy); SectorEndSound(nSector, a2>>16); return 3; } return 0; } -int PathBusy(unsigned int nSector, unsigned int a2) +int PathBusy(unsigned int nSector, unsigned int a2, short causedBy) { dassert(nSector < (unsigned int)numsectors); sectortype *pSector = §or[nSector]; @@ -2566,7 +2826,7 @@ int PathBusy(unsigned int nSector, unsigned int a2) pXSector->busy = a2; if ((a2&0xffff) == 0) { - evPost(nSector, 6, (120*pXSprite2->waitTime)/10, kCmdOn); + evPost(nSector, 6, (120*pXSprite2->waitTime)/10, kCmdOn, causedBy); pXSector->state = 0; pXSector->busy = 0; if (pXSprite1->data4) @@ -2805,13 +3065,13 @@ void OperateSector(unsigned int nSector, XSECTOR *pXSector, EVENT event) switch (event.cmd) { case kCmdOff: - SetSectorState(nSector, pXSector, 0); + SetSectorState(nSector, pXSector, 0, event.causedBy); break; case kCmdOn: - SetSectorState(nSector, pXSector, 1); + SetSectorState(nSector, pXSector, 1, event.causedBy); break; default: - SetSectorState(nSector, pXSector, pXSector->state ^ 1); + SetSectorState(nSector, pXSector, pXSector->state ^ 1, event.causedBy); break; } @@ -2855,7 +3115,7 @@ void InitPath(unsigned int nSector, XSECTOR *pXSector) pXSector->marker0 = nSprite; basePath[nSector] = nSprite; if (pXSector->state) - evPost(nSector, 6, 0, kCmdOn); + evPost(nSector, 6, 0, kCmdOn, -1); } void LinkSector(int nSector, XSECTOR *pXSector, EVENT event) @@ -2864,18 +3124,18 @@ void LinkSector(int nSector, XSECTOR *pXSector, EVENT event) int nBusy = GetSourceBusy(event); switch (pSector->type) { case kSectorZMotionSprite: - VSpriteBusy(nSector, nBusy); + VSpriteBusy(nSector, nBusy, event.causedBy); break; case kSectorZMotion: - VDoorBusy(nSector, nBusy); + VDoorBusy(nSector, nBusy, event.causedBy); break; case kSectorSlideMarked: case kSectorSlide: - HDoorBusy(nSector, nBusy); + HDoorBusy(nSector, nBusy, event.causedBy); break; case kSectorRotateMarked: case kSectorRotate: - RDoorBusy(nSector, nBusy); + RDoorBusy(nSector, nBusy, event.causedBy); break; /* By NoOne: add link support for counter sectors so they can change necessary type and count of types*/ case kSectorCounter: @@ -2885,7 +3145,7 @@ void LinkSector(int nSector, XSECTOR *pXSector, EVENT event) default: pXSector->busy = nBusy; if ((pXSector->busy&0xffff) == 0) - SetSectorState(nSector, pXSector, nBusy>>16); + SetSectorState(nSector, pXSector, nBusy>>16, event.causedBy); break; } } @@ -3033,15 +3293,15 @@ void LinkSprite(int nSprite, XSPRITE *pXSprite, EVENT event) { } } -void LinkWall(int nWall, XWALL *pXWall, EVENT a3) +void LinkWall(int nWall, XWALL *pXWall, EVENT event) { - int nBusy = GetSourceBusy(a3); + int nBusy = GetSourceBusy(event); pXWall->busy = nBusy; if ((pXWall->busy & 0xffff) == 0) - SetWallState(nWall, pXWall, nBusy>>16); + SetWallState(nWall, pXWall, nBusy>>16, event.causedBy); } -void trTriggerSector(unsigned int nSector, XSECTOR *pXSector, int command) { +void trTriggerSector(unsigned int nSector, XSECTOR *pXSector, int command, short causedBy) { dassert(nSector < (unsigned int)numsectors); if (!pXSector->locked && !pXSector->isTriggered) { @@ -3049,11 +3309,12 @@ void trTriggerSector(unsigned int nSector, XSECTOR *pXSector, int command) { pXSector->isTriggered = 1; if (pXSector->decoupled && pXSector->txID > 0) - evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command); + evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command, causedBy); else { EVENT event; event.cmd = command; + event.causedBy = causedBy; OperateSector(nSector, pXSector, event); } @@ -3079,7 +3340,7 @@ void trMessageSector(unsigned int nSector, EVENT event) { } } -void trTriggerWall(unsigned int nWall, XWALL *pXWall, int command) { +void trTriggerWall(unsigned int nWall, XWALL *pXWall, int command, short causedBy) { dassert(nWall < (unsigned int)numwalls); if (!pXWall->locked && !pXWall->isTriggered) { @@ -3087,11 +3348,12 @@ void trTriggerWall(unsigned int nWall, XWALL *pXWall, int command) { pXWall->isTriggered = 1; if (pXWall->decoupled && pXWall->txID > 0) - evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command); + evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command, causedBy); else { EVENT event; event.cmd = command; + event.causedBy = causedBy; OperateWall(nWall, pXWall, event); } @@ -3118,7 +3380,7 @@ void trMessageWall(unsigned int nWall, EVENT event) { } } -void trTriggerSprite(unsigned int nSprite, XSPRITE *pXSprite, int command, int causedBy) { +void trTriggerSprite(unsigned int nSprite, XSPRITE *pXSprite, int command, short causedBy) { if (!pXSprite->locked && !pXSprite->isTriggered) { if (pXSprite->triggerOnce) @@ -3236,6 +3498,16 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { if (pXSource->data2 < 0) return; else if (type == 6) useSectorWindGen(pXSource, §or[nDest]); return; + } else if (pSource->type == kModernObjSizeChanger) { + /* - size and pan changer of sprite/wall/sector via TX ID - */ + /* - data1 = sprite xrepeat / wall xrepeat / floor xpan - */ + /* - data2 = sprite yrepeat / wall yrepeat / floor ypan - */ + /* - data3 = sprite xoffset / wall xoffset / ceil xpan - */ + /* - data3 = sprite yoffset / wall yoffset / ceil ypan - */ + + useObjResizer(pXSource, type, nDest); + return; + } else if (pSource->type == kModernObjDataAccumulator) { /* - Object Data Accumulator allows to perform sum and sub operations in data fields of object - */ /* - data1 = destination data index - */ @@ -3303,7 +3575,7 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { } } - setDataValueOfObject(type, nDest, pXSource->data1, data); + setDataValueOfObject(type, nDest, pXSource->data1, data, event.causedBy); return; @@ -3320,26 +3592,26 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { switch (type) { case 6: if ((pSource->flags & kModernTypeFlag1) || (pXSource->data1 != -1 && pXSource->data1 != 32767)) - setDataValueOfObject(type, nDest, 1, pXSource->data1); + setDataValueOfObject(type, nDest, 1, pXSource->data1, event.causedBy); break; case 3: if ((pSource->flags & kModernTypeFlag1) || (pXSource->data1 != -1 && pXSource->data1 != 32767)) - setDataValueOfObject(type, nDest, 1, pXSource->data1); + setDataValueOfObject(type, nDest, 1, pXSource->data1, event.causedBy); if ((pSource->flags & kModernTypeFlag1) || (pXSource->data2 != -1 && pXSource->data2 != 32767)) - setDataValueOfObject(type, nDest, 2, pXSource->data2); + setDataValueOfObject(type, nDest, 2, pXSource->data2, event.causedBy); if ((pSource->flags & kModernTypeFlag1) || (pXSource->data3 != -1 && pXSource->data3 != 32767)) - setDataValueOfObject(type, nDest, 3, pXSource->data3); + setDataValueOfObject(type, nDest, 3, pXSource->data3, event.causedBy); if ((pSource->flags & kModernTypeFlag1) || (pXSource->data4 != -1 && pXSource->data1 != 65535)) - setDataValueOfObject(type, nDest, 4, pXSource->data4); + setDataValueOfObject(type, nDest, 4, pXSource->data4, event.causedBy); break; case 0: if ((pSource->flags & kModernTypeFlag1) || (pXSource->data1 != -1 && pXSource->data1 != 32767)) - setDataValueOfObject(type, nDest, 1, pXSource->data1); + setDataValueOfObject(type, nDest, 1, pXSource->data1, event.causedBy); break; } @@ -3454,12 +3726,12 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { if (pXSource->data4 == 3) { aiSetTarget(pXSprite, pSprite->x, pSprite->y, pSprite->z); aiSetGenIdleState(pSprite, pXSprite); - if (pSprite->type == kDudeModernCustom) + if (pSprite->type == kDudeModernCustom && leechIsDropped(pSprite)) removeLeech(leechIsDropped(pSprite)); } else if (pXSource->data4 == 4) { aiSetTarget(pXSprite, pPlayer->x, pPlayer->y, pPlayer->z); - if (pSprite->type == kDudeModernCustom) + if (pSprite->type == kDudeModernCustom && leechIsDropped(pSprite)) removeLeech(leechIsDropped(pSprite)); } } @@ -3660,71 +3932,6 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { } } - } else if (pSource->type == kModernObjSizeChanger) { - - /* - size and pan changer of sprite/wall/sector via TX ID - */ - /* - data1 = sprite xrepeat / wall xrepeat / floor xpan - */ - /* - data2 = sprite yrepeat / wall yrepeat / floor ypan - */ - /* - data3 = sprite xoffset / wall xoffset / ceil xpan - */ - /* - data3 = sprite yoffset / wall yoffset / ceil ypan - */ - - if (pXSource->data1 > 255) pXSource->data1 = 255; - if (pXSource->data2 > 255) pXSource->data2 = 255; - if (pXSource->data3 > 255) pXSource->data3 = 255; - if (valueIsBetween(pXSource->data4, 255, 65535)) - pXSource->data4 = 255; - - switch (type) { - // for sectors - case 6: - if (valueIsBetween(pXSource->data1, -1, 32767)) - sector[nDest].floorxpanning = pXSource->data1; - - if (valueIsBetween(pXSource->data2, -1, 32767)) - sector[nDest].floorypanning = pXSource->data2; - - if (valueIsBetween(pXSource->data3, -1, 32767)) - sector[nDest].ceilingxpanning = pXSource->data3; - - if (valueIsBetween(pXSource->data4, -1, 65535)) - sector[nDest].ceilingypanning = pXSource->data4; - break; - // for sprites - case 3: - if (valueIsBetween(pXSource->data1, -1, 32767)) { - if (pXSource->data1 < 1) sprite[nDest].xrepeat = 0; - else sprite[nDest].xrepeat = pXSource->data1; - } - - if (valueIsBetween(pXSource->data2, -1, 32767)) { - if (pXSource->data2 < 1) sprite[nDest].yrepeat = 0; - else sprite[nDest].yrepeat = pXSource->data2; - } - - if (valueIsBetween(pXSource->data3, -1, 32767)) - sprite[nDest].xoffset = pXSource->data3; - - if (valueIsBetween(pXSource->data4, -1, 65535)) - sprite[nDest].yoffset = pXSource->data4; - - break; - // for walls - case 0: - if (valueIsBetween(pXSource->data1, -1, 32767)) - wall[nDest].xrepeat = pXSource->data1; - - if (valueIsBetween(pXSource->data2, -1, 32767)) - wall[nDest].yrepeat = pXSource->data2; - - if (valueIsBetween(pXSource->data3, -1, 32767)) - wall[nDest].xpanning = pXSource->data3; - - if (valueIsBetween(pXSource->data4, -1, 65535)) - wall[nDest].ypanning = pXSource->data4; - - break; - } - } else if (pSource->type == kModernObjPicnumChanger) { /* - picnum changer can change picnum of sprite/wall/sector via TX ID - */ @@ -3878,17 +4085,20 @@ bool isMatesHaveSameTarget(XSPRITE* pXLeader, spritetype* pTarget, int allow) { } bool isActive(int nSprite) { - spritetype* pDude = &sprite[nSprite]; XSPRITE* pXDude = &xsprite[pDude->extra]; - int stateType = pXDude->aiState->stateType; - switch (stateType) { + if (sprite[nSprite].extra < 0 || sprite[nSprite].extra >= kMaxXSprites) + return false; + + XSPRITE* pXDude = &xsprite[sprite[nSprite].extra]; + switch (pXDude->aiState->stateType) { case kAiStateIdle: case kAiStateGenIdle: case kAiStateSearch: case kAiStateMove: case kAiStateOther: return false; + default: + return true; } - return true; } bool dudeCanSeeTarget(XSPRITE* pXDude, DUDEINFO* pDudeInfo, spritetype* pTarget) { @@ -3994,7 +4204,7 @@ int getDataFieldOfObject(int objType, int objIndex, int dataIndex) { } } -bool setDataValueOfObject(int objType, int objIndex, int dataIndex, int value) { +bool setDataValueOfObject(int objType, int objIndex, int dataIndex, int value, int causedBy) { switch (objType) { case 3: switch (dataIndex) { @@ -4002,23 +4212,35 @@ bool setDataValueOfObject(int objType, int objIndex, int dataIndex, int value) { xsprite[sprite[objIndex].extra].data1 = value; switch (sprite[objIndex].type) { case kSwitchCombo: - if (xsprite[sprite[objIndex].extra].data1 == xsprite[sprite[objIndex].extra].data2) - SetSpriteState(objIndex, &xsprite[sprite[objIndex].extra], 1, -1); - else - SetSpriteState(objIndex, &xsprite[sprite[objIndex].extra], 0, -1); + if (value == xsprite[sprite[objIndex].extra].data2) SetSpriteState(objIndex, &xsprite[sprite[objIndex].extra], 1, causedBy); + else SetSpriteState(objIndex, &xsprite[sprite[objIndex].extra], 0, causedBy); + break; + case kDudeModernCustom: + case kDudeModernCustomBurning: + gGenDudeExtra[objIndex].updReq[kGenDudePropertyWeapon] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyMelee] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyDamage] = true; break; } return true; case 2: xsprite[sprite[objIndex].extra].data2 = value; - return true; - case 3: switch (sprite[objIndex].type) { case kDudeModernCustom: - xsprite[sprite[objIndex].extra].sysData1 = value; + case kDudeModernCustomBurning: + gGenDudeExtra[objIndex].updReq[kGenDudePropertyMass] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyDamage] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyStates] = true; + gGenDudeExtra[objIndex].updReq[kGenDudePropertyAttack] = true; break; - default: - xsprite[sprite[objIndex].extra].data3 = value; + } + return true; + case 3: + xsprite[sprite[objIndex].extra].data3 = value; + switch (sprite[objIndex].type) { + case kDudeModernCustom: + case kDudeModernCustomBurning: + xsprite[sprite[objIndex].extra].sysData1 = value; break; } return true; @@ -4293,7 +4515,7 @@ void AlignSlopes(void) } } -int(*gBusyProc[])(unsigned int, unsigned int) = +int(*gBusyProc[])(unsigned int, unsigned int, short) = { VCrushBusy, VSpriteBusy, @@ -4313,19 +4535,18 @@ void trProcessBusy(void) { int oldBusy = gBusy[i].at8; gBusy[i].at8 = ClipRange(oldBusy+gBusy[i].at4*4, 0, 65536); - int nStatus = gBusyProc[gBusy[i].atc](gBusy[i].at0, gBusy[i].at8); - switch (nStatus) - { - case 1: - gBusy[i].at8 = oldBusy; - break; - case 2: - gBusy[i].at8 = oldBusy; - gBusy[i].at4 = -gBusy[i].at4; - break; - case 3: - gBusy[i] = gBusy[--gBusyCount]; - break; + int nStatus = gBusyProc[gBusy[i].atc](gBusy[i].at0, gBusy[i].at8, -1); + switch (nStatus) { + case 1: + gBusy[i].at8 = oldBusy; + break; + case 2: + gBusy[i].at8 = oldBusy; + gBusy[i].at4 = -gBusy[i].at4; + break; + case 3: + gBusy[i] = gBusy[--gBusyCount]; + break; } } ProcessMotion(); @@ -4483,17 +4704,17 @@ void trInit(void) } } - evSend(0, 0, kChannelLevelStart, kCmdOn); + evSend(0, 0, kChannelLevelStart, kCmdOn, -1); switch (gGameOptions.nGameType) { case 1: - evSend(0, 0, kChannelLevelStartCoop, kCmdOn); + evSend(0, 0, kChannelLevelStartCoop, kCmdOn, -1); break; case 2: - evSend(0, 0, kChannelLevelStartMatch, kCmdOn); + evSend(0, 0, kChannelLevelStartMatch, kCmdOn, -1); break; case 3: - evSend(0, 0, kChannelLevelStartMatch, kCmdOn); - evSend(0, 0, kChannelLevelStartTeamsOnly, kCmdOn); + evSend(0, 0, kChannelLevelStartMatch, kCmdOn, -1); + evSend(0, 0, kChannelLevelStartTeamsOnly, kCmdOn, -1); break; } } diff --git a/source/blood/src/triggers.h b/source/blood/src/triggers.h index 57d2d378a..a82f1ad55 100644 --- a/source/blood/src/triggers.h +++ b/source/blood/src/triggers.h @@ -29,13 +29,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "db.h" #include "eventq.h" #include "dude.h" +#include "player.h" BEGIN_BLD_NS -void trTriggerSector(unsigned int nSector, XSECTOR *pXSector, int command); +#define kPlayerCtrlSigStart "<<<>>>" // save game TRPLAYERCTRL block end + +void trTriggerSector(unsigned int nSector, XSECTOR *pXSector, int command, short causedBy); void trMessageSector(unsigned int nSector, EVENT event); -void trTriggerWall(unsigned int nWall, XWALL *pXWall, int command); +void trTriggerWall(unsigned int nWall, XWALL *pXWall, int command, short causedBy); void trMessageWall(unsigned int nWall, EVENT event); -void trTriggerSprite(unsigned int nSprite, XSPRITE *pXSprite, int command, int causedBy); +void trTriggerSprite(unsigned int nSprite, XSPRITE *pXSprite, int command, short causedBy); void trMessageSprite(unsigned int nSprite, EVENT event); void trProcessBusy(void); void trInit(void); @@ -65,7 +69,7 @@ void freeTargets(int nSprite); void freeAllTargets(XSPRITE* pXSource); bool affectedByTargetChg(XSPRITE* pXDude); int getDataFieldOfObject(int objType, int objIndex, int dataIndex); -bool setDataValueOfObject(int objType, int objIndex, int dataIndex, int value); +bool setDataValueOfObject(int objType, int objIndex, int dataIndex, int value, int causedBy); bool goalValueIsReached(XSPRITE* pXSprite); bool getDudesForTargetChg(XSPRITE* pXSprite); void stopWindOnSectors(XSPRITE* pXSource); @@ -75,8 +79,13 @@ void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index); void useSpriteDamager(XSPRITE* pXSource, spritetype* pSprite); void useTeleportTarget(XSPRITE* pXSource, spritetype* pSprite); void usePropertiesChanger(XSPRITE* pXSource, short objType, int objIndex); +void useObjResizer(XSPRITE* pXSource, short objType, int objIndex); void TeleFrag(int nKiller, int nSector); bool valueIsBetween(int val, int min, int max); + +void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer); +void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy); +void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer); // ------------------------------------------------------- END_BLD_NS diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp index 0b63656b2..ef6829df5 100644 --- a/source/blood/src/view.cpp +++ b/source/blood/src/view.cpp @@ -387,21 +387,22 @@ void fakeProcessInput(PLAYER *pPlayer, GINPUT *pInput) { case 1: if (predict.at71) - predict.at64 -= 0x5b05; + predict.at64 -= pPosture->normalJumpZ;//0x5b05; if (pInput->buttonFlags.crouch) - predict.at64 += 0x5b05; + predict.at64 += pPosture->normalJumpZ;//0x5b05; break; case 2: if (!pInput->buttonFlags.crouch) predict.at48 = 0; break; default: - if (!predict.at6f && predict.at71 && predict.at6a == 0) - { - if (packItemActive(pPlayer, 4)) - predict.at64 = -0x175555; - else - predict.at64 = -0xbaaaa; + if (!predict.at6f && predict.at71 && predict.at6a == 0) { + if (packItemActive(pPlayer, 4)) predict.at64 = pPosture->pwupJumpZ;//-0x175555; + else predict.at64 = pPosture->normalJumpZ;//-0xbaaaa; + + if (isShrinked(pPlayer->pSprite)) zvel[pPlayer->nSprite] -= gPosture[kModeHumanShrink][pPlayer->posture].normalJumpZ;//-200000; + else if (isGrown(pPlayer->pSprite)) zvel[pPlayer->nSprite] += gPosture[kModeHumanGrown][pPlayer->posture].normalJumpZ; //-250000; + predict.at6f = 1; } if (pInput->buttonFlags.crouch) @@ -3458,18 +3459,22 @@ RORHACK: } cX = (v4c>>8)+160; cY = (v48>>8)+220+(zDelta>>7); - int nShade = sector[nSectnum].floorshade; - int nPalette = 0; - if (sector[gView->pSprite->sectnum].extra > 0) - { + int nShade = sector[nSectnum].floorshade; int nPalette = 0; + if (sector[gView->pSprite->sectnum].extra > 0) { sectortype *pSector = §or[gView->pSprite->sectnum]; XSECTOR *pXSector = &xsector[pSector->extra]; if (pXSector->color) - { nPalette = pSector->floorpal; - } } - WeaponDraw(gView, nShade, cX, cY, nPalette); + + if (gView->sceneQav < 0) WeaponDraw(gView, nShade, cX, cY, nPalette); + else if (gView->pXSprite->health > 0) qavSceneDraw(gView, nShade, cX, cY, nPalette); + else { + gView->sceneQav = gView->weaponQav = -1; + gView->weaponTimer = gView->curWeapon = 0; + } + + } if (gViewPos == 0 && gView->pXSprite->burnTime > 60) { diff --git a/source/blood/src/warp.cpp b/source/blood/src/warp.cpp index 0c5bc5195..5707d89ee 100644 --- a/source/blood/src/warp.cpp +++ b/source/blood/src/warp.cpp @@ -48,7 +48,7 @@ void warpInit(void) gUpperLink[i] = -1; gLowerLink[i] = -1; } - int team1 = 0; int team2 = 0; // increment if team start positions specified. + int team1 = 0; int team2 = 0; gTeamsSpawnUsed = false; // increment if team start positions specified. for (int nSprite = 0; nSprite < kMaxSprites; nSprite++) { if (sprite[nSprite].statnum < kMaxStatus) { @@ -134,11 +134,14 @@ void warpInit(void) } } } + // check if there is enough start positions for teams, if any used if (team1 > 0 || team2 > 0) { gTeamsSpawnUsed = true; - //if (team1 < kMaxPlayers / 2 || team2 < kMaxPlayers / 2) - //_ShowMessageWindow("At least 4 spawn positions for each team is recommended."); + if (team1 < kMaxPlayers / 2 || team2 < kMaxPlayers / 2) { + viewSetSystemMessage("At least 4 spawn positions for each team is recommended."); + viewSetSystemMessage("Team A positions: %d, Team B positions: %d.", team1, team2); + } } for (int i = 0; i < kMaxSectors; i++) diff --git a/source/blood/src/weapon.cpp b/source/blood/src/weapon.cpp index 4da65d188..835891043 100644 --- a/source/blood/src/weapon.cpp +++ b/source/blood/src/weapon.cpp @@ -233,17 +233,17 @@ void WeaponDraw(PLAYER *pPlayer, int a2, int a3, int a4, int a5) dassert(pPlayer != NULL); if (pPlayer->weaponQav == -1) return; - QAV *pQAV = weaponQAV[pPlayer->weaponQav]; + QAV * pQAV = weaponQAV[pPlayer->weaponQav]; int v4; if (pPlayer->weaponTimer == 0) - v4 = (int)totalclock%pQAV->at10; + v4 = (int)totalclock % pQAV->at10; else - v4 = pQAV->at10-pPlayer->weaponTimer; + v4 = pQAV->at10 - pPlayer->weaponTimer; pQAV->x = a3; pQAV->y = a4; int flags = 2; int nInv = powerupCheck(pPlayer, kPwUpShadowCloak); - if (nInv >= 120*8 || (nInv != 0 && ((int)totalclock&32))) + if (nInv >= 120 * 8 || (nInv != 0 && ((int)totalclock & 32))) { a2 = -128; flags |= 1; @@ -996,7 +996,7 @@ void ThrowCan(int, PLAYER *pPlayer) if (pSprite) { sfxPlay3DSound(pSprite, 441, 0, 0); - evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn); + evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite); int nXSprite = pSprite->extra; XSPRITE *pXSprite = &xsprite[nXSprite]; pXSprite->Impact = 1; @@ -1011,7 +1011,7 @@ void DropCan(int, PLAYER *pPlayer) spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0); if (pSprite) { - evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn); + evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 6, gAmmoItemData[0].count); } } @@ -1020,7 +1020,7 @@ void ExplodeCan(int, PLAYER *pPlayer) { sfxKill3DSound(pPlayer->pSprite, -1, 441); spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedSpray, 0); - evPost(pSprite->index, 3, 0, kCmdOn); + evPost(pSprite->index, 3, 0, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 6, gAmmoItemData[0].count); StartQAV(pPlayer, 15, -1); pPlayer->curWeapon = 0; @@ -1038,7 +1038,7 @@ void ThrowBundle(int, PLAYER *pPlayer) if (pPlayer->fuseTime < 0) pXSprite->Impact = 1; else - evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn); + evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 5, 1); pPlayer->throwPower = 0; } @@ -1047,7 +1047,7 @@ void DropBundle(int, PLAYER *pPlayer) { sfxKill3DSound(pPlayer->pSprite, 16, -1); spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0); - evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn); + evPost(pSprite->index, 3, pPlayer->fuseTime, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 5, 1); } @@ -1055,7 +1055,7 @@ void ExplodeBundle(int, PLAYER *pPlayer) { sfxKill3DSound(pPlayer->pSprite, 16, -1); spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedTNTBundle, 0); - evPost(pSprite->index, 3, 0, kCmdOn); + evPost(pSprite->index, 3, 0, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 5, 1); StartQAV(pPlayer, 24, -1, 0); pPlayer->curWeapon = 0; @@ -1067,7 +1067,7 @@ void ThrowProx(int, PLAYER *pPlayer) int nSpeed = mulscale16(pPlayer->throwPower, 0x177777)+0x66666; sfxPlay3DSound(pPlayer->pSprite, 455, 1, 0); spritetype *pSprite = playerFireThing(pPlayer, 0, -9460, kThingArmedProxBomb, nSpeed); - evPost(pSprite->index, 3, 240, kCmdOn); + evPost(pSprite->index, 3, 240, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 10, 1); pPlayer->throwPower = 0; } @@ -1075,7 +1075,7 @@ void ThrowProx(int, PLAYER *pPlayer) void DropProx(int, PLAYER *pPlayer) { spritetype *pSprite = playerFireThing(pPlayer, 0, 0, kThingArmedProxBomb, 0); - evPost(pSprite->index, 3, 240, kCmdOn); + evPost(pSprite->index, 3, 240, kCmdOn, pPlayer->nSprite); UseAmmo(pPlayer, 10, 1); } @@ -1102,7 +1102,7 @@ void DropRemote(int, PLAYER *pPlayer) void FireRemote(int, PLAYER *pPlayer) { - evSend(0, 0, 90+(pPlayer->pSprite->type-kDudePlayer1), kCmdOn); + evSend(0, 0, 90+(pPlayer->pSprite->type-kDudePlayer1), kCmdOn, pPlayer->nSprite); } #define kMaxShotgunBarrels 4 @@ -1927,23 +1927,25 @@ char sub_4F484(PLAYER *pPlayer) void WeaponProcess(PLAYER *pPlayer) { pPlayer->flashEffect = ClipLow(pPlayer->flashEffect - 1, 0); - - if (gModernMap && gQavScene[pPlayer->nPlayer].index >= 0) { - - int nIndex = gQavScene[pPlayer->nPlayer].index; - if (sprite[nIndex].extra >= 0 && pPlayer->pXSprite->health > 0) { + + if (gPlayerCtrl[pPlayer->nPlayer].qavScene.index >= 0 && pPlayer->pXSprite->health > 0) { + + QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; + + int nIndex = pQavScene->index; + if (sprite[nIndex].extra >= 0) { XSPRITE* pXSprite = &xsprite[sprite[nIndex].extra]; - //viewSetSystemMessage("%d", pXSprite->sysData1); if (pXSprite->waitTime > 0 && --pXSprite->sysData1 <= 0) { - evSend(nIndex, 3, pXSprite->txID, (COMMAND_ID)pXSprite->command, pPlayer->nSprite); - evPost(nIndex, 3, 0, kCmdOff, pPlayer->nSprite); + if (pXSprite->txID > 0) + evSend(nIndex, 3, pXSprite->txID, (COMMAND_ID) pXSprite->command, pQavScene->causedBy); + evPost(nIndex, 3, 0, (COMMAND_ID) (kCmdNumberic + 4), pQavScene->causedBy); } else { - playQavScene(pPlayer); + qavScenePlay(pPlayer); pPlayer->weaponTimer = ClipLow(pPlayer->weaponTimer -= 4, 0); } } else { - gQavScene[pPlayer->nPlayer].index = -1; - gQavScene[pPlayer->nPlayer].qavId = NULL; + pQavScene->index = pPlayer->sceneQav = -1; + pQavScene->qavResrc = NULL; } return;