diff --git a/source/blood/src/actor.cpp b/source/blood/src/actor.cpp index af164ef36..03dd5b0b8 100644 --- a/source/blood/src/actor.cpp +++ b/source/blood/src/actor.cpp @@ -2469,7 +2469,7 @@ void actInit(bool bSaveLoad) { nnExtInitModernStuff(bSaveLoad); } #endif - + for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { switch (sprite[nSprite].type) { case kItemWeaponVoodooDoll: @@ -2587,15 +2587,7 @@ void actInit(bool bSaveLoad) { #endif xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0; - - #ifdef NOONE_EXTENSIONS - // add a way to set custom hp for every enemy - should work only if map just started and not loaded. - if (!gModernMap || pXSprite->data4 <= 0) pXSprite->health = dudeInfo[nType].startHealth << 4; - else pXSprite->health = ClipRange(pXSprite->data4 << 4, 1, 65535); - #else - pXSprite->health = dudeInfo[nType].startHealth << 4; - #endif - + pXSprite->health = dudeGetStartHp(pSprite); } if (gSysRes.Lookup(seqStartId, "SEQ")) seqSpawn(seqStartId, 3, pSprite->extra); @@ -3239,26 +3231,28 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, seqSpawn(dudeInfo[nType].seqStartID+15, 3, nXSprite, nDudeToGibClient2); break; #ifdef NOONE_EXTENSIONS - case kDudeModernCustom: + case kDudeModernCustom: { playGenDudeSound(pSprite, kGenDudeSndDeathNormal); + int dudeToGib = (actCheckRespawn(pSprite)) ? -1 : ((nSeq == 3) ? nDudeToGibClient2 : nDudeToGibClient1); if (nSeq == 3) { - + GENDUDEEXTRA* pExtra = genDudeExtra(pSprite); - if (pExtra->availDeaths[kDmgBurn] == 3) seqSpawn((15 + Random(2)) + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); - else if (pExtra->availDeaths[kDmgBurn] == 2) seqSpawn(16 + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); - else if (pExtra->availDeaths[kDmgBurn] == 1) seqSpawn(15 + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); - else if (gSysRes.Lookup(pXSprite->data2 + nSeq, "SEQ"))seqSpawn(nSeq + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); - else seqSpawn(1 + pXSprite->data2, 3, nXSprite, nDudeToGibClient2); + if (pExtra->availDeaths[kDmgBurn] == 3) seqSpawn((15 + Random(2)) + pXSprite->data2, 3, nXSprite, dudeToGib); + else if (pExtra->availDeaths[kDmgBurn] == 2) seqSpawn(16 + pXSprite->data2, 3, nXSprite, dudeToGib); + else if (pExtra->availDeaths[kDmgBurn] == 1) seqSpawn(15 + pXSprite->data2, 3, nXSprite, dudeToGib); + else if (gSysRes.Lookup(pXSprite->data2 + nSeq, "SEQ"))seqSpawn(nSeq + pXSprite->data2, 3, nXSprite, dudeToGib); + else seqSpawn(1 + pXSprite->data2, 3, nXSprite, dudeToGib); } else { - seqSpawn(nSeq + pXSprite->data2, 3, nXSprite, nDudeToGibClient1); + seqSpawn(nSeq + pXSprite->data2, 3, nXSprite, dudeToGib); } + genDudePostDeath(pSprite, damageType, damage); + return; - pXSprite->txID = 0; // to avoid second trigger. - break; - + } case kDudeModernCustomBurning: { playGenDudeSound(pSprite, kGenDudeSndDeathExplode); + int dudeToGib = (actCheckRespawn(pSprite)) ? -1 : nDudeToGibClient1; damageType = DAMAGE_TYPE_3; if (Chance(0x4000)) { @@ -3270,11 +3264,12 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, } GENDUDEEXTRA* pExtra = genDudeExtra(pSprite); - if (pExtra->availDeaths[kDmgBurn] == 3) seqSpawn((15 + Random(2)) + pXSprite->data2, 3, nXSprite, nDudeToGibClient1); - else if (pExtra->availDeaths[kDmgBurn] == 2) seqSpawn(16 + pXSprite->data2, 3, nXSprite, nDudeToGibClient1); - else if (pExtra->availDeaths[kDmgBurn] == 1) seqSpawn(15 + pXSprite->data2, 3, nXSprite, nDudeToGibClient1); - else seqSpawn(1 + pXSprite->data2, 3, nXSprite, nDudeToGibClient1); - break; + if (pExtra->availDeaths[kDmgBurn] == 3) seqSpawn((15 + Random(2)) + pXSprite->data2, 3, nXSprite, dudeToGib); + else if (pExtra->availDeaths[kDmgBurn] == 2) seqSpawn(16 + pXSprite->data2, 3, nXSprite, dudeToGib); + else if (pExtra->availDeaths[kDmgBurn] == 1) seqSpawn(15 + pXSprite->data2, 3, nXSprite, dudeToGib); + else seqSpawn(1 + pXSprite->data2, 3, nXSprite, dudeToGib); + genDudePostDeath(pSprite, damageType, damage); + return; } #endif case kDudeBurningZombieAxe: diff --git a/source/blood/src/ai.cpp b/source/blood/src/ai.cpp index e2ee1764c..c8604dfa9 100644 --- a/source/blood/src/ai.cpp +++ b/source/blood/src/ai.cpp @@ -1532,20 +1532,17 @@ void aiInitSprite(spritetype *pSprite) DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra]; pDudeExtra->at4 = 0; pDudeExtra->at0 = 0; + switch (pSprite->type) { -#ifdef NOONE_EXTENSIONS - case kDudeModernCustom: { - DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[nXSprite].at6.u1; - pDudeExtraE->at8 = pDudeExtraE->at0 = 0; - aiGenDudeNewState(pSprite, &genDudeIdleL); - genDudePrepare(pSprite); - break; - } + #ifdef NOONE_EXTENSIONS + case kDudeModernCustom: + aiGenDudeInitSprite(pSprite, pXSprite); + genDudePrepare(pSprite, kGenDudePropertyAll); + return; case kDudeModernCustomBurning: - aiGenDudeNewState(pSprite, &genDudeBurnGoto); - pXSprite->burnTime = 1200; - break; -#endif + aiGenDudeInitSprite(pSprite, pXSprite); + return; + #endif case kDudeCultistTommy: case kDudeCultistShotgun: case kDudeCultistTesla: diff --git a/source/blood/src/aiunicult.cpp b/source/blood/src/aiunicult.cpp index 78a764567..d881a90e5 100644 --- a/source/blood/src/aiunicult.cpp +++ b/source/blood/src/aiunicult.cpp @@ -53,6 +53,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "tile.h" #include "sound/s_soundinternal.h" +#include "gib.h" +#include "aiburn.h" + BEGIN_BLD_NS static void genDudeAttack1(int, int); static void punchCallback(int, int); @@ -174,7 +177,6 @@ bool genDudeAdjustSlope(spritetype* pSprite, XSPRITE* pXSprite, int dist, int we } GENDUDEEXTRA* genDudeExtra(spritetype* pGenDude) { - dassert(spriRangeIsFine(pGenDude->index)); return &gGenDudeExtra[pGenDude->index]; } @@ -1254,14 +1256,9 @@ XSPRITE* getNextIncarnation(XSPRITE* pXSprite) { if (rxBucket[i].type != 3 || rxBucket[i].index == pXSprite->reference) continue; - switch (sprite[rxBucket[i].index].statnum) { - case kStatDude: - case kStatInactive: // inactive (ambush) dudes - if (xsprite[sprite[rxBucket[i].index].extra].health > 0) + if (sprite[rxBucket[i].index].statnum == kStatInactive) return &xsprite[sprite[rxBucket[i].index].extra]; } - - } return NULL; } @@ -1650,9 +1647,7 @@ spritetype* genDudeSpawn(spritetype* pSprite, int nDist) { if (pSource->clipdist > 0) pDude->clipdist = pSource->clipdist; // inherit custom hp settings - if (pXSource->data4 <= 0) pXDude->health = dudeInfo[nType - kDudeBase].startHealth << 4; - else pXDude->health = ClipRange(pXSource->data4 << 4, 1, 65535); - + pXDude->health = dudeGetStartHp(pDude); if (pSource->flags & kModernTypeFlag1) { switch (pSource->type) { @@ -1720,7 +1715,7 @@ void genDudeTransform(spritetype* pSprite) { // trigger dude death before transform trTriggerSprite(pSprite->index, pXSprite, kCmdOff); - pSprite->type = pIncarnation->type; + pSprite->type = pSprite->inittype = pIncarnation->type; pSprite->flags = pIncarnation->flags; pSprite->pal = pIncarnation->pal; pSprite->shade = pIncarnation->shade; @@ -1735,17 +1730,18 @@ void genDudeTransform(spritetype* pSprite) { pXSprite->busyTime = pXIncarnation->busyTime; pXSprite->waitTime = pXIncarnation->waitTime; + // inherit respawn properties + pXSprite->respawn = pXIncarnation->respawn; + pXSprite->respawnPending = pXIncarnation->respawnPending; + pXSprite->burnTime = 0; pXSprite->burnSource = -1; pXSprite->data1 = pXIncarnation->data1; 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; - else pXSprite->sysData1 = pXIncarnation->data3; - - pXSprite->data4 = pXIncarnation->data4; + pXSprite->sysData1 = pXIncarnation->data3; + pXSprite->sysData2 = pXIncarnation->data4; pXSprite->dudeGuard = pXIncarnation->dudeGuard; pXSprite->dudeDeaf = pXIncarnation->dudeDeaf; @@ -1762,8 +1758,7 @@ void genDudeTransform(spritetype* pSprite) { pXIncarnation->key = pXIncarnation->dropMsg = 0; // set hp - if (pXSprite->data4 <= 0) pXSprite->health = dudeInfo[pSprite->type - kDudeBase].startHealth << 4; - else pXSprite->health = ClipRange(pXSprite->data4 << 4, 1, 65535); + pXSprite->health = dudeGetStartHp(pSprite); int seqId = dudeInfo[pSprite->type - kDudeBase].seqStartID; switch (pSprite->type) { @@ -1793,8 +1788,10 @@ void genDudeTransform(spritetype* pSprite) { break; } + pXIncarnation->triggerOn = triggerOn; + pXIncarnation->triggerOff = triggerOff; - // remove the incarnation in case if non-locked + /*// remove the incarnation in case if non-locked if (pXIncarnation->locked == 0) { pXIncarnation->txID = pIncarnation->type = 0; actPostSprite(pIncarnation->index, kStatFree); @@ -1802,7 +1799,7 @@ void genDudeTransform(spritetype* pSprite) { } else { pXIncarnation->triggerOn = triggerOn; pXIncarnation->triggerOff = triggerOff; - } + }*/ } @@ -2159,5 +2156,40 @@ bool genDudePrepare(spritetype* pSprite, int propId) { return true; } +void genDudePostDeath(spritetype* pSprite, DAMAGE_TYPE damageType, int damage) { + if (damageType == DAMAGE_TYPE_3) { + DUDEINFO* pDudeInfo = getDudeInfo(pSprite->type); + for (int i = 0; i < 3; i++) + if (pDudeInfo->nGibType[i] > -1) + GibSprite(pSprite, (GIBTYPE)pDudeInfo->nGibType[i], NULL, NULL); + + for (int i = 0; i < 4; i++) + fxSpawnBlood(pSprite, damage); + } + + gKillMgr.AddKill(pSprite); + + pSprite->type = kThingBloodChunks; + actPostSprite(pSprite->index, kStatThing); +} + +void aiGenDudeInitSprite(spritetype* pSprite, XSPRITE* pXSprite) { + switch (pSprite->type) { + case kDudeModernCustom: { + DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1; + pDudeExtraE->at8 = pDudeExtraE->at0 = 0; + aiGenDudeNewState(pSprite, &genDudeIdleL); + break; + } + case kDudeModernCustomBurning: + aiGenDudeNewState(pSprite, &genDudeBurnGoto); + pXSprite->burnTime = 1200; + break; + } + + pSprite->flags = 15; + return; +} + #endif END_BLD_NS diff --git a/source/blood/src/aiunicult.h b/source/blood/src/aiunicult.h index 181df429f..76e5c8618 100644 --- a/source/blood/src/aiunicult.h +++ b/source/blood/src/aiunicult.h @@ -163,6 +163,7 @@ struct GENDUDEEXTRA { unsigned short weaponType; unsigned short baseDispersion; unsigned short slaveCount; // how many dudes is summoned + //unsigned short incarnationsCount; signed short nLifeLeech; // spritenum of dropped dude's leech signed short slave[kGenDudeMaxSlaves]; // index of the ones dude is summon signed short dmgControl[kDamageMax]; // depends of current weapon, drop armor item, sprite yrepeat and surface type @@ -218,8 +219,10 @@ int genDudeSeqStartId(XSPRITE* pXSprite); int getRangeAttackDist(spritetype* pSprite, int minDist = 1200, int maxDist = 80000); int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp); void scaleDamage(XSPRITE* pXSprite); -bool genDudePrepare(spritetype* pSprite, int propId = kGenDudePropertyAll); +bool genDudePrepare(spritetype* pSprite, int propId); void genDudeUpdate(spritetype* pSprite); bool genDudeAdjustSlope(spritetype* pSprite, XSPRITE* pXSprite, int dist, int weaponType, int by = 64); +void genDudePostDeath(spritetype* pSprite, DAMAGE_TYPE damageType, int damage); +void aiGenDudeInitSprite(spritetype* pSprite, XSPRITE* pXSprite); #endif END_BLD_NS diff --git a/source/blood/src/callback.cpp b/source/blood/src/callback.cpp index 180867599..a6215716f 100644 --- a/source/blood/src/callback.cpp +++ b/source/blood/src/callback.cpp @@ -46,6 +46,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "triggers.h" #include "view.h" #include "nnexts.h" +#include "aiunicult.h" BEGIN_BLD_NS @@ -269,10 +270,17 @@ void Respawn(int nSprite) // 9 pSprite->y = baseSprite[nSprite].y; pSprite->z = baseSprite[nSprite].z; pSprite->cstat |= 0x1101; - pSprite->clipdist = getDudeInfo(nType+kDudeBase)->clipdist; - pXSprite->health = getDudeInfo(nType+kDudeBase)->startHealth<<4; - if (gSysRes.Lookup(getDudeInfo(nType+kDudeBase)->seqStartID, "SEQ")) - seqSpawn(getDudeInfo(nType+kDudeBase)->seqStartID, 3, pSprite->extra, -1); + pXSprite->health = dudeGetStartHp(pSprite); + switch (pSprite->type) { + default: + pSprite->clipdist = getDudeInfo(nType + kDudeBase)->clipdist; + if (gSysRes.Lookup(getDudeInfo(nType + kDudeBase)->seqStartID, "SEQ")) + seqSpawn(getDudeInfo(nType + kDudeBase)->seqStartID, 3, pSprite->extra, -1); + break; + case kDudeModernCustom: + seqSpawn(genDudeSeqStartId(pXSprite), 3, pSprite->extra, -1); + break; + } aiInitSprite(pSprite); pXSprite->key = 0; } else if (pSprite->type == kThingTNTBarrel) { @@ -719,28 +727,6 @@ void callbackCondition(int nSprite) { TRCONDITION* pCond = &gCondition[pXSprite->sysData1]; for (int i = 0; i < pCond->length; i++) { - /*if (pCond->obj[i].type == OBJ_SPRITE) { - spritetype* pObj = &sprite[pCond->obj[i].index]; - XSPRITE* pXObj = (xspriRangeIsFine(pObj->extra)) ? &xsprite[pObj->extra] : NULL; - if (gGameOptions.nGameType != 0) { - if (pObj->type != pObj->inittype && pObj->inittype >= kDudePlayer1 && pObj->inittype <= kDudePlayer8) { - PLAYER* pPlayer = getPlayerById(pObj->inittype); - if (pPlayer) { - pCond->obj[i].index = pPlayer->pSprite->index; - viewSetSystemMessage("RESET INDEX"); - } else { - viewSetSystemMessage("FAILED %d", pCond->obj[i].index); - continue; - } - } - } - //if (pObj->flags & kHitagRespawn) - //viewSetSystemMessage("TYPE: %d ON RESPAWN", pObj->type); - //if (pObj->flags & kHitagFree) { - // viewSetSystemMessage("TYPE: %d IS FREE", pObj->type); - // } - - }*/ EVENT evn; evn.index = pCond->obj[i].index; evn.type = pCond->obj[i].type; evn.cmd = pCond->obj[i].cmd; evn.funcID = kCallbackCondition; useCondition(pXSprite, evn); diff --git a/source/blood/src/db.h b/source/blood/src/db.h index e07e7d8fd..f6902e11e 100644 --- a/source/blood/src/db.h +++ b/source/blood/src/db.h @@ -112,8 +112,9 @@ struct XSPRITE { unsigned int stateTimer : 16; // ai timer AISTATE* aiState; // ai #ifdef NOONE_EXTENSIONS - signed int sysData1 : 16; // used to keep here various system data, so user can't change it in map editor - unsigned int physAttr : 12; // currently used by additional physics sprites to keep it's attributes. + signed int sysData1: 32; // used to keep here various system data, so user can't change it in map editor + signed int sysData2: 32; // + unsigned int physAttr : 32; // currently used by additional physics sprites to keep it's attributes. #endif signed int scale; // used for scaling SEQ size on sprites diff --git a/source/blood/src/dude.cpp b/source/blood/src/dude.cpp index 439295875..09bae5643 100644 --- a/source/blood/src/dude.cpp +++ b/source/blood/src/dude.cpp @@ -1733,4 +1733,19 @@ DUDEINFO gPlayerTemplate[4] = DUDEINFO fakeDudeInfo = {}; +int dudeGetStartHp(spritetype* pDude) { + + int hp = getDudeInfo(pDude->type)->startHealth << 4; + if (!hp) { + consoleSysMsg("Sprite #%d (type %d) is not a dude!", pDude->index, pDude->type); + return hp; + } + #ifdef NOONE_EXTENSIONS + // add a way to set custom hp for every enemy (data4 moved to sysData2) + else if (gModernMap && xsprite[pDude->extra].sysData2 > 0) + hp = ClipRange(xsprite[pDude->extra].sysData2 << 4, 1, 65535); + #endif + return hp; +} + END_BLD_NS diff --git a/source/blood/src/dude.h b/source/blood/src/dude.h index da4c728ef..4b760ab1d 100644 --- a/source/blood/src/dude.h +++ b/source/blood/src/dude.h @@ -66,4 +66,6 @@ inline DUDEINFO *getDudeInfo(int const nType) return &fakeDudeInfo; } +int dudeGetStartHp(spritetype* pDude); + END_BLD_NS diff --git a/source/blood/src/nnexts.cpp b/source/blood/src/nnexts.cpp index 043e4ff4c..af85b5973 100644 --- a/source/blood/src/nnexts.cpp +++ b/source/blood/src/nnexts.cpp @@ -380,6 +380,33 @@ void nnExtInitModernStuff(bool bSaveLoad) { } else { + /*// copy custom start health to avoid overwrite by kThingBloodChunks + if (IsDudeSprite(pSprite)) + pXSprite->sysData2 = pXSprite->data4; + + bool sysStat = false; + switch (pSprite->statnum) { + case kStatModernDudeTargetChanger: + if (pSprite->type != kModernDudeTargetChanger) sysStat = true; + break; + case kStatModernCondition: + if (pSprite->type != kModernCondition && pSprite->type != kModernConditionFalse) sysStat = true; + break; + case kStatModernEventRedirector: + if (pSprite->type != kModernRandomTX && pSprite->type != kModernSequentialTX) sysStat = true; + break; + case kStatModernPlayerLinker: + if (pSprite->type != kModernPlayerControl) sysStat = true; + break; + default: + if (pSprite->statnum < kStatModernBase || pSprite->statnum >= kStatModernMax) break; + ThrowError("Sprite status list number %d on sprite #%d is in a range of system reserved (%d - %d)!", pSprite->index, pSprite->statnum, kStatModernBase, kStatModernMax); + break; + } + + if (sysStat) + ThrowError("Sprite #%d: System status list number %d detected!", pSprite->index, pSprite->statnum); + */ switch (pSprite->type) { case kModernRandomTX: case kModernSequentialTX: @@ -412,10 +439,43 @@ void nnExtInitModernStuff(bool bSaveLoad) { case kModernThingTNTProx: pXSprite->Proximity = true; break; + case kDudeModernCustom: + if (pXSprite->txID <= 0) break; + for (int nSprite = headspritestat[kStatDude], found = 0; nSprite >= 0; nSprite = nextspritestat[nSprite]) { + XSPRITE* pXSpr = &xsprite[sprite[nSprite].extra]; + if (pXSpr->rxID != pXSprite->txID) continue; + else if (found) ThrowError("\nCustom dude (TX ID %d):\nOnly one incarnation allowed per channel!", pXSprite->txID); + changespritestat(nSprite, kStatInactive); + nSprite = headspritestat[kStatDude]; + found++; + } + break; + case kModernPlayerControl: + if (pXSprite->command != kCmdLink) break; + else if (pXSprite->data1 < 1 || pXSprite->data1 >= kMaxPlayers) + ThrowError("\nPlayer Control (SPRITE #%d):\nPlayer out of a range (data1 = %d)", pSprite->index, pXSprite->data1); + if (pXSprite->rxID && pXSprite->rxID != kChannelLevelStart) + ThrowError("\nPlayer Control (SPRITE #%d) with Link command should have no RX ID!", pSprite->index, pXSprite->data1); + + if (pXSprite->txID && pXSprite->txID < kChannelUser) + ThrowError("\nPlayer Control (SPRITE #%d):\nTX ID should be in range of %d and %d!", pSprite->index, kChannelUser, kChannelMax); + + // only one linker per player allowed + for (int nSprite = headspritestat[kStatModernPlayerLinker]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { + XSPRITE* pXCtrl = &xsprite[sprite[nSprite].extra]; + if (pXSprite->data1 == pXCtrl->data1) + ThrowError("\nPlayer Control (SPRITE #%d):\nPlayer %d already linked with different player control sprite #%d!", pSprite->index, pXSprite->data1, nSprite); + } + + pXSprite->rxID = kChannelLevelStart; + pXSprite->triggerOnce = true; + pXSprite->state = pXSprite->restState = pXSprite->waitTime = 0; + changespritestat(pSprite->index, kStatModernPlayerLinker); + break; case kModernCondition: if (pXSprite->waitTime > 0 && pXSprite->busyTime > 0) { pXSprite->busyTime += ((pXSprite->waitTime * 60) / 10); pXSprite->waitTime = 0; - consoleSysMsg("Summing busyTime and waitTime for tracking condition #d%, RX ID %d. Result = %d ticks", pSprite->index, pXSprite->rxID, pXSprite->busyTime); + consoleSysMsg("Summing busyTime and waitTime for tracking condition #%d, RX ID %d. Result = %d ticks", pSprite->index, pXSprite->rxID, pXSprite->busyTime); } pXSprite->Decoupled = false; // must go through operateSprite always @@ -430,7 +490,7 @@ void nnExtInitModernStuff(bool bSaveLoad) { break; } - // the following trigger flags are sensless to have together + // the following trigger flags are senseless to have together if ((pXSprite->Touch && (pXSprite->Proximity || pXSprite->Sight) && pXSprite->DudeLockout) || (pXSprite->Touch && pXSprite->Proximity && !pXSprite->Sight)) pXSprite->Touch = false; @@ -1194,8 +1254,11 @@ void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer) { } -void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer) { +void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer, bool checkCondition) { + // save player's sprite index to use it later with conditions + pXSource->sysData1 = pPlayer->nSprite; + pPlayer->pXSprite->txID = pXSource->txID; pPlayer->pXSprite->command = kCmdToggle; pPlayer->pXSprite->triggerOn = pXSource->triggerOn; @@ -1224,17 +1287,19 @@ void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer) { pPlayer->pXSprite->dropMsg = pXSource->dropMsg; // let's check if there is tracking condition expecting objects with this TX id - for (int i = bucketHead[pXSource->txID]; i < bucketHead[pXSource->txID + 1]; i++) { - if (sprite[rxBucket[i].index].type == kModernCondition) { + if (checkCondition && pXSource->txID >= kChannelUser) { + for (int i = bucketHead[pXSource->txID]; i < bucketHead[pXSource->txID + 1]; i++) { + if (sprite[rxBucket[i].index].type != kModernCondition) continue; + XSPRITE* pXCond = &xsprite[sprite[rxBucket[i].index].extra]; if (pXCond->busyTime <= 0) continue; - - // search for player control sprite and replace it with actual player sprite + TRCONDITION* pCond = &gCondition[pXCond->sysData1]; + // search for player control sprite and replace it with actual player sprite for (int k = 0; k < pCond->length; k++) { - if (pCond->obj[k].index != pXSource->reference) continue; + if (pCond->obj[k].type != OBJ_SPRITE || pCond->obj[k].index != pXSource->reference) continue; pCond->obj[k].index = pPlayer->nSprite; - pCond->obj[k].cmd = kCmdToggle; + pCond->obj[k].cmd = pPlayer->pXSprite->command; break; } } @@ -2434,12 +2499,9 @@ bool condCheckDude(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS) { ThrowError("\nDude conditions:\nObject #%d (objType: %d) is not a dude!", objIndex, objType); spritetype* pSpr = &sprite[objIndex]; - if (!xspriRangeIsFine(sprite[objIndex].extra)) { - // TO-DO: must search for respawned player / enemy - // there is currently serials for old sprite + if (!xspriRangeIsFine(sprite[objIndex].extra)) return false; - } - + XSPRITE* pXSpr = &xsprite[pSpr->extra]; if (pXSpr->health <= 0 || pSpr->type == kThingBloodChunks) return false; else if (cond < (kCondRange >> 1)) { @@ -2604,11 +2666,11 @@ bool condCheckSprite(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS) { switch (cond) { default: break; case 50: // compare hp (in %) - if (IsDudeSprite(pSpr)) var = ((pXSpr->data4 <= 0) ? getDudeInfo(pSpr->type)->startHealth : pXSpr->data4) << 4; + if (IsDudeSprite(pSpr)) var = dudeGetStartHp(pSpr); else if (condCmpne(arg1, arg2, cmpOp) && pSpr->type == kThingBloodChunks) return true; else if (pSpr->type >= kThingBase && pSpr->type < kThingMax) var = thingInfo[pSpr->type - kThingBase].startHealth << 4; - + return condCmp((100 * pXSpr->health) / ClipLow(var, 1), arg1, arg2, cmpOp); case 55: // touching ceil of sector? if ((gSpriteHit[pSpr->extra].ceilhit & 0xc000) != 0x4000) return false; @@ -2649,6 +2711,7 @@ bool condCheckSprite(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS) { return condCmp(getSpriteMassBySize(pSpr), arg1, arg2, cmpOp); // mass of the sprite in a range? } } else { + viewSetSystemMessage("!!!!!!!! %d", pSpr->type); switch (cond) { default: return false; case 50: @@ -2662,6 +2725,20 @@ bool condCheckSprite(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS) { return false; } +// this updates index of object in all tracking conditions +void condUpdateObjectIndex(int objType, int oldIndex, int newIndex) { + for (int i = 0; i <= gTrackingCondsCount; i++) { + TRCONDITION* pCond = &gCondition[i]; + for (int k = 0; k < pCond->length; k++) { + if (pCond->obj[k].type != objType || pCond->obj[k].index != oldIndex) continue; + pCond->obj[k].index = newIndex; + break; + } + } + + return; +} + bool valueIsBetween(int val, int min, int max) { return (val > min && val < max); } @@ -3451,7 +3528,9 @@ bool modernTypeOperateSprite(int nSprite, spritetype* pSprite, XSPRITE* pXSprite /// !!! COMMANDS OF THE CURRENT SPRITE, NOT OF THE EVENT !!! /// switch (pXSprite->command) { case kCmdLink: // copy properties of sprite to player - trPlayerCtrlLink(pXSprite, pPlayer); + if (pXSprite->isTriggered) break; + trPlayerCtrlLink(pXSprite, pPlayer, true); + pXSprite->isTriggered = true; break; case kCmdNumberic: // player life form if (pXSprite->data2 < kModeHuman || pXSprite->data2 > kModeHumanShrink) break; @@ -4119,10 +4198,9 @@ void useTargetChanger(XSPRITE* pXSource, spritetype* pSprite) { pXSprite->burnTime = 0; // heal dude a bit in case of friendly fire - if (pXSprite->data4 > 0 && pXSprite->health < pXSprite->data4) - actHealDude(pXSprite, receiveHp, pXSprite->data4); - else if (pXSprite->health < pDudeInfo->startHealth) - actHealDude(pXSprite, receiveHp, pDudeInfo->startHealth); + int startHp = dudeGetStartHp(pSprite); + if (pXSprite->health < startHp) actHealDude(pXSprite, receiveHp, startHp); + } else if (xsprite[pBurnSource->extra].health <= 0) { pXSprite->burnTime = 0; } @@ -4188,19 +4266,12 @@ void useTargetChanger(XSPRITE* pXSource, spritetype* pSprite) { spritetype* pMate = pTarget; XSPRITE* pXMate = pXTarget; // heal dude - if (pXSprite->data4 > 0 && pXSprite->health < pXSprite->data4) - actHealDude(pXSprite, receiveHp, pXSprite->data4); - else if (pXSprite->health < pDudeInfo->startHealth) - actHealDude(pXSprite, receiveHp, pDudeInfo->startHealth); + int startHp = dudeGetStartHp(pSprite); + if (pXSprite->health < startHp) actHealDude(pXSprite, receiveHp, startHp); // heal mate - if (pXMate->data4 > 0 && pXMate->health < pXMate->data4) - actHealDude(pXMate, receiveHp, pXMate->data4); - else { - DUDEINFO* pTDudeInfo = getDudeInfo(pMate->type); - if (pXMate->health < pTDudeInfo->startHealth) - actHealDude(pXMate, receiveHp, pTDudeInfo->startHealth); - } + startHp = dudeGetStartHp(pMate); + if (pXMate->health < startHp) actHealDude(pXMate, receiveHp, startHp); if (pXMate->target > -1 && sprite[pXMate->target].extra >= 0) { pTarget = &sprite[pXMate->target]; diff --git a/source/blood/src/nnexts.h b/source/blood/src/nnexts.h index a6dab3a57..3ffd65b30 100644 --- a/source/blood/src/nnexts.h +++ b/source/blood/src/nnexts.h @@ -64,9 +64,12 @@ BEGIN_BLD_NS #define kCondRange 100 // modern statnums enum { +kStatModernBase = 20, kStatModernDudeTargetChanger = 20, kStatModernCondition = 21, kStatModernEventRedirector = 22, +kStatModernPlayerLinker = 23, +kStatModernMax = 64, }; // modern sprite types @@ -277,7 +280,7 @@ void useRandomTx(XSPRITE* pXSource, COMMAND_ID cmd, bool setState); bool txIsRanged(XSPRITE* pXSource); void seqTxSendCmdAll(XSPRITE* pXSource, int nIndex, COMMAND_ID cmd, bool modernSend); // ------------------------------------------------------------------------- // -void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer); +void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer, bool checkCondition); void trPlayerCtrlSetRace(XSPRITE* pXSource, PLAYER* pPlayer); void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer); void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer); @@ -331,11 +334,11 @@ bool condPush(XSPRITE* pXSprite, int objType, int objIndex); bool condRestore(XSPRITE* pXSprite); bool condCmp(int val, int arg1, int arg2, int comOp); bool condCmpne(int arg1, int arg2, int comOp); -/*bool condCmpr(int val, int min, int max, int cmpOp);*/ bool condCheckMixed(XSPRITE* pXCond, EVENT event, int cmpOp, bool PUSH, bool RVRS); bool condCheckSector(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS); bool condCheckWall(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS); bool condCheckSprite(XSPRITE* pXCond, int cmpOp, bool PUSH, bool RVRS); +void condUpdateObjectIndex(int objType, int oldIndex, int newIndex); #endif //////////////////////////////////////////////////////////////////////// diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp index a5e36fbb4..f40fc8415 100644 --- a/source/blood/src/player.cpp +++ b/source/blood/src/player.cpp @@ -650,7 +650,7 @@ void playerStart(int nPlayer, int bNewLevel) PLAYER* pPlayer = &gPlayer[nPlayer]; GINPUT* pInput = &pPlayer->input; ZONE* pStartZone = NULL; - + // normal start position if (gGameOptions.nGameType <= 1) pStartZone = &gStartZone[nPlayer]; @@ -775,6 +775,20 @@ void playerStart(int nPlayer, int bNewLevel) pPlayer->weaponQav = -1; #ifdef NOONE_EXTENSIONS playerQavSceneReset(pPlayer); // reset qav scene + + // we must check if properties of old pPlayer->pXSprite was + // changed with kModernPlayerControl and copy it to the new x-sprite + if (gModernMap && gGameOptions.nGameType != 0) { + + for (int nSprite = headspritestat[kStatModernPlayerLinker]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { + XSPRITE* pXCtrl = &xsprite[sprite[nSprite].extra]; + if (pXCtrl->data1 != pPlayer->nPlayer + 1) continue; + int nSpriteOld = pXCtrl->sysData1; + trPlayerCtrlLink(pXCtrl, pPlayer, false); + if (pPlayer->pXSprite->txID >= kChannelUser && gTrackingCondsCount > 0) + condUpdateObjectIndex(OBJ_SPRITE, nSpriteOld, pXCtrl->sysData1); + } + } #endif pPlayer->hand = 0; pPlayer->nWaterPal = 0;