- Player control: more strict rules for copying properties of xsprite

- Fix for custom health when respawning enemy
- Fix for custom dude when respawning it
- Conditions: added way to refresh sprite index in tracking conditions

# Conflicts:
#	source/blood/src/actor.cpp
#	source/blood/src/aiunicult.cpp
#	source/blood/src/aiunicult.h
#	source/blood/src/dude.cpp
#	source/blood/src/dude.h
This commit is contained in:
NoOneBlood 2020-04-04 22:48:23 +03:00 committed by Christoph Oelckers
parent 8b0da35cb8
commit 64de30209b
11 changed files with 241 additions and 122 deletions

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -66,4 +66,6 @@ inline DUDEINFO *getDudeInfo(int const nType)
return &fakeDudeInfo;
}
int dudeGetStartHp(spritetype* pDude);
END_BLD_NS

View file

@ -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];

View file

@ -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
////////////////////////////////////////////////////////////////////////

View file

@ -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;