- Rewrite true random, so it should work just fine now

- Way better AI for Custom Dude when attacking the target
- Minor warning fixes and other updates

# Conflicts:
#	source/blood/src/actor.cpp
#	source/blood/src/aiunicult.cpp
#	source/blood/src/aiunicult.h
#	source/blood/src/asound.cpp
#	source/blood/src/gameutil.cpp
#	source/blood/src/gameutil.h
#	source/blood/src/player.h
#	source/blood/src/triggers.h
This commit is contained in:
Grind Core 2019-12-05 23:42:35 +03:00 committed by Christoph Oelckers
parent a6bf1c3026
commit da47ebbd2c
20 changed files with 757 additions and 442 deletions

View file

@ -1516,7 +1516,9 @@ MissileType missileInfo[] = {
40, 40,
(char)-16, (char)-16,
16, 16,
1207, 1207 1207, 1207,
false, false, false, false, false, true, false
}, },
// Regular flare // Regular flare
{ {
@ -1527,7 +1529,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
32, 32,
420, 420 420, 420,
false, true, true, false, false, false, false
}, },
// Tesla alt // Tesla alt
{ {
@ -1538,7 +1541,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
32, 32,
471, 471 471, 471,
false, false, false, false, false, false, true
}, },
// Flare alt // Flare alt
{ {
@ -1549,7 +1553,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
4, 4,
421, 421 421, 421,
false, true, false, true, false, false, false
}, },
// Spray flame // Spray flame
{ {
@ -1560,7 +1565,8 @@ MissileType missileInfo[] = {
24, 24,
(char)-128, (char)-128,
16, 16,
1309, 351 1309, 351,
false, true, false, false, false, false, false
}, },
// Fireball // Fireball
{ {
@ -1571,7 +1577,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
32, 32,
480, 480 480, 480,
false, true, false, true, false, false, false
}, },
// Tesla regular // Tesla regular
{ {
@ -1582,7 +1589,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
16, 16,
470, 470 470, 470,
false, false, false, false, false, false, true
}, },
// EctoSkull // EctoSkull
{ {
@ -1593,7 +1601,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-24, (char)-24,
32, 32,
489, 490 489, 490,
false, false, false, false, false, true, false
}, },
// Hellhound flame // Hellhound flame
{ {
@ -1604,7 +1613,8 @@ MissileType missileInfo[] = {
24, 24,
(char)-128, (char)-128,
16, 16,
462, 351 462, 351,
false, true, false, false, false, false, false
}, },
// Puke // Puke
{ {
@ -1615,7 +1625,8 @@ MissileType missileInfo[] = {
16, 16,
(char)-16, (char)-16,
16, 16,
1203, 172 1203, 172,
false, false, true, false, false, false, false
}, },
// Reserved // Reserved
{ {
@ -1626,7 +1637,8 @@ MissileType missileInfo[] = {
8, 8,
(char)0, (char)0,
16, 16,
0,0 0,0,
false, false, true, false, false, false, false
}, },
// Stone gargoyle projectile // Stone gargoyle projectile
{ {
@ -1637,7 +1649,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
16, 16,
1457, 249 1457, 249,
false, false, false, false, false, true, false
}, },
// Napalm launcher // Napalm launcher
{ {
@ -1648,7 +1661,8 @@ MissileType missileInfo[] = {
30, 30,
(char)-128, (char)-128,
24, 24,
480, 489 480, 489,
false, true, false, true, false, false, false
}, },
// Cerberus fireball // Cerberus fireball
{ {
@ -1659,7 +1673,8 @@ MissileType missileInfo[] = {
30, 30,
(char)-128, (char)-128,
24, 24,
480, 489 480, 489,
false, false, false, true, false, false, false
}, },
// Tchernobog fireball // Tchernobog fireball
{ {
@ -1670,7 +1685,8 @@ MissileType missileInfo[] = {
24, 24,
(char)-128, (char)-128,
16, 16,
480, 489 480, 489,
false, false, false, true, false, false, false
}, },
// Regular life leech // Regular life leech
{ {
@ -1681,7 +1697,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
16, 16,
491, 491 491, 491,
true, true, true, true, true, true, true
}, },
// Dropped life leech (enough ammo) // Dropped life leech (enough ammo)
{ {
@ -1692,7 +1709,8 @@ MissileType missileInfo[] = {
16, 16,
(char)-128, (char)-128,
16, 16,
520, 520 520, 520,
false, false, false, false, false, true, false
}, },
// Dropped life leech (no ammo) // Dropped life leech (no ammo)
{ {
@ -1703,7 +1721,8 @@ MissileType missileInfo[] = {
32, 32,
(char)-128, (char)-128,
16, 16,
520, 520 520, 520,
false, false, false, false, false, true, false
} }
}; };
@ -2446,7 +2465,7 @@ struct POSTPONE {
POSTPONE gPost[kMaxSprites]; POSTPONE gPost[kMaxSprites];
static char buffer[120]; //static char buffer[120];
bool IsItemSprite(spritetype *pSprite) bool IsItemSprite(spritetype *pSprite)
{ {
@ -3102,7 +3121,6 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType,
if (!IsDudeSprite(pSprite) || gGenDudeExtra[pSprite->owner].slave[i] == pSprite->index || pXSprite->health <= 0) { if (!IsDudeSprite(pSprite) || gGenDudeExtra[pSprite->owner].slave[i] == pSprite->index || pXSprite->health <= 0) {
gGenDudeExtra[pSprite->owner].slave[i] = -1; gGenDudeExtra[pSprite->owner].slave[i] = -1;
gGenDudeExtra[pSprite->owner].slaveCount = ClipRange(gGenDudeExtra[pSprite->owner].slaveCount - 1, 0, gGameOptions.nDifficulty + 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;
} }
} }
@ -3113,19 +3131,19 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType,
switch (pSprite->type) { switch (pSprite->type) {
case kDudeModernCustom: { case kDudeModernCustom: {
GENDUDEEXTRA* pExtra = genDudeExtra(pSprite);
removeDudeStuff(pSprite); removeDudeStuff(pSprite);
if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) { if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) {
if (pXSprite->data1 >= kTrapExploder && pXSprite->data1 < (kTrapExploder + kExplodeMax) - 1 && if (pExtra->weaponType == kGenDudeWeaponKamikaze && Chance(0x4000) && damageType != 5 && damageType != 4) {
Chance(0x4000) && damageType != 5 && damageType != 4) {
doExplosion(pSprite, pXSprite->data1 - kTrapExploder); doExplosion(pSprite, pXSprite->data1 - kTrapExploder);
if (Chance(0x9000)) damageType = (DAMAGE_TYPE) 3; if (Chance(0x9000)) damageType = (DAMAGE_TYPE) 3;
} }
if (damageType == DAMAGE_TYPE_1) { if (damageType == DAMAGE_TYPE_1) {
if ((gSysRes.Lookup(pXSprite->data2 + 15, "SEQ") || gSysRes.Lookup(pXSprite->data2 + 16, "SEQ")) && pXSprite->medium == kMediumNormal) { if (pExtra->availDeaths[DAMAGE_TYPE_1] && !spriteIsUnderwater(pSprite)) {
if (gSysRes.Lookup(pXSprite->data2 + 3, "SEQ")) { if (pExtra->canBurn) {
pSprite->type = kDudeModernCustomBurning; pSprite->type = kDudeModernCustomBurning;
if (pXSprite->data2 == kGenDudeDefaultSeq) // don't inherit palette for burning if using default animation if (pXSprite->data2 == kGenDudeDefaultSeq) // don't inherit palette for burning if using default animation
pSprite->pal = 0; pSprite->pal = 0;
@ -3159,10 +3177,11 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType,
pSprite->flags &= ~kPhysMove; xvel[pSprite->index] = yvel[pSprite->index] = 0; pSprite->flags &= ~kPhysMove; xvel[pSprite->index] = yvel[pSprite->index] = 0;
int seqId = pXSprite->data2 + 18; playGenDudeSound(pSprite, kGenDudeSndTransforming);
if (!gSysRes.Lookup(seqId, "SEQ")) { int seqId = pXSprite->data2 + kGenDudeSeqTransform;
if (gSysRes.Lookup(seqId, "SEQ")) seqSpawn(seqId, 3, nXSprite, -1);
else {
seqKill(3, nXSprite); seqKill(3, nXSprite);
playGenDudeSound(pSprite, kGenDudeSndTransforming);
spritetype* pEffect = gFX.fxSpawn((FX_ID)52, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->ang); spritetype* pEffect = gFX.fxSpawn((FX_ID)52, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->ang);
if (pEffect != NULL) { if (pEffect != NULL) {
pEffect->cstat = CSTAT_SPRITE_ALIGNMENT_FACING; pEffect->cstat = CSTAT_SPRITE_ALIGNMENT_FACING;
@ -3183,14 +3202,9 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType,
CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0xccccc); CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0xccccc);
GibSprite(pSprite, nGibType, &gibPos, &gibVel); GibSprite(pSprite, nGibType, &gibPos, &gibVel);
} }
return;
} }
seqSpawn(seqId, 3, nXSprite, -1);
playGenDudeSound(pSprite, kGenDudeSndTransforming);
pXSprite->sysData1 = kGenDudeTransformStatus; // in transform pXSprite->sysData1 = kGenDudeTransformStatus; // in transform
return; return;
} }
break; break;
@ -3289,9 +3303,14 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType,
nSeq = 2; nSeq = 2;
switch (pSprite->type) { switch (pSprite->type) {
case kDudeModernCustom: case kDudeModernCustom:
case kDudeModernCustomBurning: case kDudeModernCustomBurning: {
playGenDudeSound(pSprite, kGenDudeSndDeathExplode); playGenDudeSound(pSprite, kGenDudeSndDeathExplode);
GENDUDEEXTRA* pExtra = genDudeExtra(pSprite);
if (!pExtra->availDeaths[damageType]) {
nSeq = 1; damageType = DAMAGE_TYPE_0;
}
break; break;
}
case kDudeCultistTommy: case kDudeCultistTommy:
case kDudeCultistShotgun: case kDudeCultistShotgun:
case kDudeCultistTommyProne: case kDudeCultistTommyProne:
@ -4321,7 +4340,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite)
} }
} }
switch (pSprite->type) { /*switch (pSprite->type) {
case kDudeModernCustom: case kDudeModernCustom:
case kDudeModernCustomBurning: case kDudeModernCustomBurning:
{ {
@ -4338,7 +4357,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite)
} }
break; break;
} }
} }*/
switch (pSprite2->type) { switch (pSprite2->type) {
case kThingKickablePail: case kThingKickablePail:
@ -4395,7 +4414,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite)
} }
} }
switch (pSprite->type) { /*switch (pSprite->type) {
case kDudeModernCustom: case kDudeModernCustom:
case kDudeModernCustomBurning: case kDudeModernCustomBurning:
{ {
@ -4411,7 +4430,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite)
} }
break; break;
} }
} }*/
switch (pSprite2->type) { switch (pSprite2->type) {
@ -4848,7 +4867,7 @@ void MoveDude(spritetype *pSprite)
ChangeSpriteSect(nSprite, nSector); ChangeSpriteSect(nSprite, nSector);
nXSector = sector[nSector].extra; nXSector = sector[nSector].extra;
pXSector = (nXSector > 0) ? pXSector = &xsector[nXSector] : NULL; pXSector = (nXSector > 0) ? &xsector[nXSector] : NULL;
if (pXSector && pXSector->Enter && (pPlayer || !pXSector->dudeLockout)) { if (pXSector && pXSector->Enter && (pPlayer || !pXSector->dudeLockout)) {
if (sector[nSector].type == kSectorTeleport) if (sector[nSector].type == kSectorTeleport)
@ -5425,7 +5444,7 @@ void actExplodeSprite(spritetype *pSprite)
return; return;
sfxKill3DSound(pSprite, -1, -1); sfxKill3DSound(pSprite, -1, -1);
evKill(pSprite->index, 3); evKill(pSprite->index, 3);
int nType; int nType = 1;
switch (pSprite->type) switch (pSprite->type)
{ {
case kMissileFireballNapam: case kMissileFireballNapam:
@ -6151,10 +6170,11 @@ void actProcessSprites(void)
} }
// By NoOne: handle incarnations of custom dude // By NoOne: handle incarnations of custom dude
if (pSprite->type == kDudeModernCustom && pXSprite->txID > 0 && pXSprite->health <= 0 && pXSprite->sysData1 == kGenDudeTransformStatus) { if (pSprite->type == kDudeModernCustom && pXSprite->txID > 0 && pXSprite->sysData1 == kGenDudeTransformStatus) {
xvel[pSprite->index] = ClipLow(xvel[pSprite->index] >> 4, 0); yvel[pSprite->index] = ClipLow(yvel[pSprite->index] >> 4, 0); xvel[pSprite->index] = yvel[pSprite->index] = 0;
if (seqGetStatus(3, nXSprite) < 0) {
XSPRITE* pXIncarnation = getNextIncarnation(pXSprite); XSPRITE* pXIncarnation = getNextIncarnation(pXSprite);
if (seqGetStatus(3, nXSprite) < 0 && pXIncarnation != NULL) { if (pXIncarnation != NULL) {
spritetype* pIncarnation = &sprite[pXIncarnation->reference]; spritetype* pIncarnation = &sprite[pXIncarnation->reference];
pXSprite->key = pXSprite->dropMsg = pXSprite->locked = 0; pXSprite->key = pXSprite->dropMsg = pXSprite->locked = 0;
@ -6192,7 +6212,7 @@ void actProcessSprites(void)
// if incarnation is active dude, it's sndStartId will be stored in sysData1, otherwise it will be data3 // 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; if (pIncarnation->statnum == kStatDude && pIncarnation->type == kDudeModernCustom) pXSprite->sysData1 = pXIncarnation->sysData1;
else pXIncarnation->data3; else pXSprite->sysData1 = pXIncarnation->data3;
pXSprite->data4 = pXIncarnation->data4; pXSprite->data4 = pXIncarnation->data4;
@ -6212,7 +6232,7 @@ void actProcessSprites(void)
// set hp // set hp
if (pXSprite->data4 <= 0) pXSprite->health = dudeInfo[pSprite->type - kDudeBase].startHealth << 4; if (pXSprite->data4 <= 0) pXSprite->health = dudeInfo[pSprite->type - kDudeBase].startHealth << 4;
else pXSprite->health = ClipRange(pXSprite->data4 << 4, 1, 65535); else pXSprite->health = ClipRange(pXSprite->data4 << 4, 1, 65535);
int seqId = dudeInfo[pSprite->type - kDudeBase].seqStartID; int seqId = dudeInfo[pSprite->type - kDudeBase].seqStartID;
switch (pSprite->type) { switch (pSprite->type) {
@ -6245,7 +6265,6 @@ void actProcessSprites(void)
// remove the incarnation in case if non-locked // remove the incarnation in case if non-locked
if (pXIncarnation->locked == 0) { if (pXIncarnation->locked == 0) {
pXIncarnation->txID = pIncarnation->type = 0; pXIncarnation->txID = pIncarnation->type = 0;
actPostSprite(pIncarnation->xvel, kStatFree); actPostSprite(pIncarnation->xvel, kStatFree);
// or restore triggerOn and off options // or restore triggerOn and off options
@ -6253,14 +6272,13 @@ void actProcessSprites(void)
pXIncarnation->triggerOn = triggerOn; pXIncarnation->triggerOn = triggerOn;
pXIncarnation->triggerOff = triggerOff; pXIncarnation->triggerOff = triggerOff;
} }
} else { } else {
if (pXSprite->sysData1 == kGenDudeTransformStatus) pXSprite->sysData1 = 0;
// just trigger dude death // just trigger dude death
trTriggerSprite(nSprite, pXSprite, kCmdOff, pSprite->owner); trTriggerSprite(nSprite, pXSprite, kCmdOff, pSprite->owner);
} }
} }
}
if (pSprite->type == kDudeCerberusTwoHead) if (pSprite->type == kDudeCerberusTwoHead)
{ {
@ -7339,76 +7357,45 @@ int GetDataVal(spritetype* pSprite, int data) {
} }
std::default_random_engine rng;
int my_random(int a, int b) {
int STD_Random(int a, int b) {
std::default_random_engine rng;
rng.seed(std::random_device()());
std::uniform_int_distribution<int> dist_a_b(a, b); std::uniform_int_distribution<int> dist_a_b(a, b);
return dist_a_b(rng); return dist_a_b(rng);
} }
// tries to get random data field of sprite // tries to get random data field of sprite
int GetRandDataVal(int *rData, spritetype* pSprite) { int GetRandDataVal(int *rData) {
int temp[4]; dassert(rData != NULL);
if (rData != NULL && pSprite != NULL) return -1; int random = 0, a = 0; int first = -1; int maxRetries = 10;
else if (pSprite != NULL) {
if (pSprite->extra < 0)
return -1;
if (rData == NULL)
rData = temp;
XSPRITE* pXSprite = &xsprite[pSprite->extra];
rData[0] = pXSprite->data1; rData[2] = pXSprite->data3;
rData[1] = pXSprite->data2; rData[3] = pXSprite->data4;
}
else if (rData == NULL) {
return -1;
}
int random = 0;
// randomize only in case if at least 2 data fields are not empty // randomize only in case if at least 2 data fields are not empty
int a = 1; int b = -1; for (int i = 0; i < 4; i++) {
for (int i = 0; i <= 3; i++) { if (rData[i] <= 0 && a++ > 1) return -1;
if (rData[i] == 0) { else if (first == -1) first = rData[i];
if (a++ > 2)
return -1;
} }
else if (b == -1) {
b++;
}
}
// try randomize few times // try randomize few times
int maxRetries = 10;
while (maxRetries > 0) { while (maxRetries > 0) {
// use true random only for single player mode, otherwise use Blood's default one.
// use true random only for single player mode random = (gGameOptions.nGameType == 0 && !VanillaMode() && !DemoRecordStatus()) ? STD_Random(0, 3) : Random(3);
// otherwise use Blood's default one. In the future it maybe possible to make
// host send info to clients about what was generated.
if (gGameOptions.nGameType != 0 || VanillaMode() || DemoRecordStatus()) random = Random(3);
else {
rng.seed(std::random_device()());
random = my_random(0, 4);
}
if (rData[random] > 0) return rData[random]; if (rData[random] > 0) return rData[random];
maxRetries--; else maxRetries--;
} }
// if nothing, get first found data value from top // if nothing, get first found data value from top
return rData[b]; return first;
} }
// this function drops random item using random pickup generator(s) // this function drops random item using random pickup generator(s)
spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) { spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) {
spritetype* pSprite2 = NULL; spritetype* pSprite2 = NULL; int* rData = xspriData2Array(pSprite->extra);
int selected = -1;
int rData[4]; int selected = -1; if (rData != NULL) {
rData[0] = xsprite[pSprite->extra].data1; rData[2] = xsprite[pSprite->extra].data3;
rData[1] = xsprite[pSprite->extra].data2; rData[3] = xsprite[pSprite->extra].data4;
// randomize only in case if at least 2 data fields fits. // randomize only in case if at least 2 data fields fits.
for (int i = 0; i <= 3; i++) for (int i = 0; i <= 3; i++)
@ -7416,7 +7403,7 @@ spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) {
rData[i] = 0; rData[i] = 0;
int maxRetries = 9; int maxRetries = 9;
while ((selected = GetRandDataVal(rData, NULL)) == prevItem) if (maxRetries-- <= 0) break; while ((selected = GetRandDataVal(rData)) == prevItem) if (maxRetries-- <= 0) break;
if (selected > 0) { if (selected > 0) {
spritetype* pSource = pSprite; XSPRITE* pXSource = &xsprite[pSource->extra]; spritetype* pSource = pSprite; XSPRITE* pXSource = &xsprite[pSource->extra];
pSprite2 = actDropObject(pSprite, selected); pSprite2 = actDropObject(pSprite, selected);
@ -7430,7 +7417,7 @@ spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) {
if ((pSource->flags & kModernTypeFlag1) && (pXSource->txID > 0 || (pXSource->txID != 3 && pXSource->lockMsg > 0)) && if ((pSource->flags & kModernTypeFlag1) && (pXSource->txID > 0 || (pXSource->txID != 3 && pXSource->lockMsg > 0)) &&
dbInsertXSprite(pSprite2->xvel) > 0) { dbInsertXSprite(pSprite2->xvel) > 0) {
XSPRITE * pXSprite2 = &xsprite[pSprite2->extra]; XSPRITE* pXSprite2 = &xsprite[pSprite2->extra];
// inherit spawn sprite trigger settings, so designer can send command when item picked up. // inherit spawn sprite trigger settings, so designer can send command when item picked up.
pXSprite2->txID = pXSource->txID; pXSprite2->txID = pXSource->txID;
@ -7444,27 +7431,24 @@ spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) {
} }
} }
}
return pSprite2; return pSprite2;
} }
// this function spawns random dude using dudeSpawn // this function spawns random dude using dudeSpawn
spritetype* spawnRandomDude(spritetype* pSprite) { spritetype* spawnRandomDude(spritetype* pSprite) {
spritetype* pSprite2 = NULL; spritetype* pSprite2 = NULL;
int* rData = xspriData2Array(pSprite->extra); int selected = -1;
if (pSprite->extra >= 0) { if (rData != NULL) {
int rData[4]; int selected = -1;
rData[0] = xsprite[pSprite->extra].data1; rData[2] = xsprite[pSprite->extra].data3;
rData[1] = xsprite[pSprite->extra].data2; rData[3] = xsprite[pSprite->extra].data4;
// randomize only in case if at least 2 data fields fits. // randomize only in case if at least 2 data fields fits.
for (int i = 0; i <= 3; i++) for (int i = 0; i <= 3; i++)
if (rData[i] < kDudeBase || rData[i] >= kDudeMax) if (rData[i] < kDudeBase || rData[i] >= kDudeMax)
rData[i] = 0; rData[i] = 0;
if ((selected = GetRandDataVal(rData,NULL)) > 0) if ((selected = GetRandDataVal(rData)) > 0)
pSprite2 = actSpawnDude(pSprite, selected, -1, 0); pSprite2 = actSpawnDude(pSprite, selected, -1, 0);
} }
return pSprite2; return pSprite2;
} }
//------------------------- //-------------------------
@ -7529,7 +7513,7 @@ spritetype* actSpawnCustomDude(spritetype* pSprite, int nDist) {
if (pSource->clipdist > 0) pDude->clipdist = pSource->clipdist; if (pSource->clipdist > 0) pDude->clipdist = pSource->clipdist;
// inherit custom hp settings // inherit custom hp settings
if (pXSource->data4 <= 0) pXDude->health = dudeInfo[nType].startHealth << 4; if (pXSource->data4 <= 0) pXDude->health = dudeInfo[nType - kDudeBase].startHealth << 4;
else pXDude->health = ClipRange(pXSource->data4 << 4, 1, 65535); else pXDude->health = ClipRange(pXSource->data4 << 4, 1, 65535);
@ -7666,7 +7650,6 @@ int getSpriteMassBySize(spritetype* pSprite) {
cached->picnum = pSprite->picnum; cached->seqId = seqId; cached->picnum = pSprite->picnum; cached->seqId = seqId;
cached->clipdist = pSprite->clipdist; cached->clipdist = pSprite->clipdist;
//viewSetSystemMessage("MASS: %d", cached->mass);
return cached->mass; return cached->mass;
} }
@ -7738,9 +7721,9 @@ void debrisMove(int listIndex) {
int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int top, bottom; GetSpriteExtents(pSprite, &top, &bottom);
int moveHit = 0; int moveHit = 0;
int floorDist = (bottom - pSprite->z) / 4; //int floorDist = (bottom - pSprite->z) / 4;
int ceilDist = (pSprite->z - top) / 4; //int ceilDist = (pSprite->z - top) / 4;
int clipDist = pSprite->clipdist << 2; //int clipDist = pSprite->clipdist << 2;
int tmpFraction = gSpriteMass[pSprite->extra].fraction; int tmpFraction = gSpriteMass[pSprite->extra].fraction;
if (sector[nSector].extra >= 0 && xsector[sector[nSector].extra].Underwater) if (sector[nSector].extra >= 0 && xsector[sector[nSector].extra].Underwater)
@ -7826,7 +7809,7 @@ void debrisMove(int listIndex) {
if (v20 > 0) { if (v20 > 0) {
pXDebris->physAttr |= kPhysFalling; pXDebris->physAttr |= kPhysFalling;
int vax = actFloorBounceVector((int*)& xvel[nSprite], (int*)& yvel[nSprite], (int*)& v20, pSprite->sectnum, tmpFraction); actFloorBounceVector((int*)& xvel[nSprite], (int*)& yvel[nSprite], (int*)& v20, pSprite->sectnum, tmpFraction);
zvel[nSprite] = v20; zvel[nSprite] = v20;
if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x10000) { if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x10000) {
@ -7918,4 +7901,18 @@ bool ceilIsTooLow(spritetype* pSprite) {
return false; return false;
} }
bool isImmune(spritetype* pSprite, int dmgType, int minScale) {
if (dmgType >= kDmgFall && dmgType < kDmgMax && pSprite->extra >= 0 && xsprite[pSprite->extra].locked != 1) {
if (pSprite->type >= kThingBase && pSprite->type < kThingMax)
return (thingInfo[pSprite->type - kThingBase].dmgControl[dmgType] <= minScale);
else if (IsDudeSprite(pSprite)) {
if (IsPlayerSprite(pSprite)) return (gPlayer[pSprite->type - kDudePlayer1].damageControl[dmgType] <= minScale);
else if (pSprite->type == kDudeModernCustom) return (gGenDudeExtra[pSprite->index].dmgControl[dmgType] <= minScale);
else return (dudeInfo[pSprite->type - kDudeBase].at70[dmgType] <= minScale);
}
}
return true;
}
END_BLD_NS END_BLD_NS

View file

@ -133,6 +133,7 @@ struct MissileType
char shade; char shade;
unsigned char clipDist; unsigned char clipDist;
int fireSound[2]; // By NoOne: predefined fire sounds. used by kDudeModernCustom, but can be used for something else. int fireSound[2]; // By NoOne: predefined fire sounds. used by kDudeModernCustom, but can be used for something else.
bool dmgType[kDamageMax]; // By NoOne: list of damages types missile can use
}; };
struct EXPLOSION struct EXPLOSION
@ -268,8 +269,8 @@ void MakeSplash(spritetype *pSprite, XSPRITE *pXSprite);
spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem); spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem);
spritetype* spawnRandomDude(spritetype* pSprite); spritetype* spawnRandomDude(spritetype* pSprite);
int GetDataVal(spritetype* pSprite, int data); int GetDataVal(spritetype* pSprite, int data);
int my_random(int a, int b); int STD_Random(int a, int b);
int GetRandDataVal(int *rData, spritetype* pSprite); int GetRandDataVal(int *rData);
bool sfxPlayMissileSound(spritetype* pSprite, int missileId); bool sfxPlayMissileSound(spritetype* pSprite, int missileId);
bool sfxPlayVectorSound(spritetype* pSprite, int vectorId); bool sfxPlayVectorSound(spritetype* pSprite, int vectorId);
spritetype* actSpawnCustomDude(spritetype* pSprite, int nDist); spritetype* actSpawnCustomDude(spritetype* pSprite, int nDist);
@ -280,6 +281,7 @@ int isDebris(int nSprite);
int debrisGetFreeIndex(void); int debrisGetFreeIndex(void);
void debrisMove(int listIndex); void debrisMove(int listIndex);
void debrisConcuss(int nOwner, int listIndex, int x, int y, int z, int dmg); void debrisConcuss(int nOwner, int listIndex, int x, int y, int z, int dmg);
bool isImmune(spritetype* pSprite, int dmgType, int minScale = 16);
extern SPRITEMASS gSpriteMass[kMaxXSprites]; extern SPRITEMASS gSpriteMass[kMaxXSprites];
extern short gProxySpritesList[kMaxSuperXSprites]; extern short gProxySpritesList[kMaxSuperXSprites];

View file

@ -124,12 +124,6 @@ void aiNewState(spritetype *pSprite, XSPRITE *pXSprite, AISTATE *pAIState)
pAIState->enterFunc(pSprite, pXSprite); pAIState->enterFunc(pSprite, pXSprite);
} }
bool dudeIsImmune(spritetype* pSprite, int dmgType) {
if (dmgType < 0 || dmgType > 6) return true;
else if (dudeInfo[pSprite->type - kDudeBase].startDamage[dmgType] == 0) return true;
else if (pSprite->extra >= 0 && xsprite[pSprite->extra].locked == 1) return true; // if dude is locked, it immune to any dmg.
return false;
}
bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange) bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange)
{ {
int top, bottom; int top, bottom;
@ -171,7 +165,7 @@ bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange)
switch (pSprite->type) { switch (pSprite->type) {
case kDudeCerberusTwoHead: // Cerberus case kDudeCerberusTwoHead: // Cerberus
case kDudeCerberusOneHead: // 1 Head Cerberus case kDudeCerberusOneHead: // 1 Head Cerberus
if (VanillaMode() || !dudeIsImmune(pSprite, pXSector->damageType)) if (VanillaMode() || !isImmune(pSprite, pXSector->damageType))
Crusher = 1; Crusher = 1;
break; break;
default: default:
@ -233,7 +227,7 @@ bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange)
break; break;
case kDudeModernCustom: case kDudeModernCustom:
case kDudeModernCustomBurning: case kDudeModernCustomBurning:
if ((Crusher && !dudeIsImmune(pSprite, pXSector->damageType)) || ((Water || Underwater) && !canSwim(pSprite))) return false; if ((Crusher && !isImmune(pSprite, pXSector->damageType)) || ((Water || Underwater) && !canSwim(pSprite))) return false;
return true; return true;
fallthrough__; fallthrough__;
case kDudeZombieAxeNormal: case kDudeZombieAxeNormal:
@ -1025,7 +1019,7 @@ int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_T
else if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) { else if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) {
removeDudeStuff(pSprite); removeDudeStuff(pSprite);
if (pExtra->curWeapon >= kTrapExploder && pExtra->curWeapon < (kTrapExploder + kExplodeMax) - 1) if (pExtra->weaponType == kGenDudeWeaponKamikaze)
doExplosion(pSprite, pXSprite->data1 - kTrapExploder); doExplosion(pSprite, pXSprite->data1 - kTrapExploder);
if (spriteIsUnderwater(pSprite, false)) { if (spriteIsUnderwater(pSprite, false)) {
@ -1056,7 +1050,7 @@ int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_T
actKillDude(nSource, pSprite, DAMAGE_TYPE_0, 65535); actKillDude(nSource, pSprite, DAMAGE_TYPE_0, 65535);
} }
} else if (canWalk(pSprite) && !inDodge(pXSprite->aiState) && !inRecoil(pXSprite->aiState)) { } else if (canWalk(pSprite) && !inDodge(pXSprite->aiState) && !inRecoil(pXSprite->aiState)) {
if (inIdle(pXSprite->aiState) || inSearch(pXSprite->aiState) || Chance(getDodgeChance(pSprite))) { if (inIdle(pXSprite->aiState) || Chance(getDodgeChance(pSprite))) {
if (!spriteIsUnderwater(pSprite, false)) { if (!spriteIsUnderwater(pSprite, false)) {
if (!canDuck(pSprite) || !sub_5BDA8(pSprite, 14)) aiGenDudeNewState(pSprite, &genDudeDodgeShortL); if (!canDuck(pSprite) || !sub_5BDA8(pSprite, 14)) aiGenDudeNewState(pSprite, &genDudeDodgeShortL);
else aiGenDudeNewState(pSprite, &genDudeDodgeShortD); else aiGenDudeNewState(pSprite, &genDudeDodgeShortD);
@ -1136,7 +1130,7 @@ void RecoilDude(spritetype *pSprite, XSPRITE *pXSprite)
} else if (!dudeIsMelee(pXSprite) || Chance(rChance >> 2)) { } else if (!dudeIsMelee(pXSprite) || Chance(rChance >> 2)) {
if (rState == 1) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeL : &genDudeDodgeShortL); if (rState == 1) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeL : &genDudeDodgeShortL);
else if (rState == 2) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeD : &genDudeDodgeShortD); else if (rState == 2) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeD : &genDudeDodgeShortD);
else if (rState == 1) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeW : &genDudeDodgeShortW); else if (rState == 3) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeW : &genDudeDodgeShortW);
} }
else if (rState == 1) pXSprite->aiState->nextState = &genDudeChaseL; else if (rState == 1) pXSprite->aiState->nextState = &genDudeChaseL;
@ -1619,10 +1613,7 @@ void aiInitSprite(spritetype *pSprite)
break; break;
} }
case kDudeGillBeast: case kDudeGillBeast:
if (pXSector && pXSector->Underwater) aiNewState(pSprite, pXSprite, &gillBeastIdle);
aiNewState(pSprite, pXSprite, &gillBeastIdle);
else
aiNewState(pSprite, pXSprite, &gillBeastIdle);
break; break;
case kDudeBat: case kDudeBat:
{ {

View file

@ -89,6 +89,10 @@ AISTATE genDudeDodgeShortL = { kAiStateMove, 9, -1, 60, NULL, aiMoveDodge, NULL,
AISTATE genDudeDodgeShortD = { kAiStateMove, 14, -1, 60, NULL, aiMoveDodge, NULL, &genDudeChaseD }; AISTATE genDudeDodgeShortD = { kAiStateMove, 14, -1, 60, NULL, aiMoveDodge, NULL, &genDudeChaseD };
AISTATE genDudeDodgeShortW = { kAiStateMove, 13, -1, 60, NULL, aiMoveDodge, NULL, &genDudeChaseW }; AISTATE genDudeDodgeShortW = { kAiStateMove, 13, -1, 60, NULL, aiMoveDodge, NULL, &genDudeChaseW };
// --------------------- // ---------------------
AISTATE genDudeDodgeShorterL = { kAiStateMove, 9, -1, 20, NULL, aiMoveDodge, NULL, &genDudeChaseL };
AISTATE genDudeDodgeShorterD = { kAiStateMove, 14, -1, 20, NULL, aiMoveDodge, NULL, &genDudeChaseD };
AISTATE genDudeDodgeShorterW = { kAiStateMove, 13, -1, 20, NULL, aiMoveDodge, NULL, &genDudeChaseW };
// ---------------------
AISTATE genDudeChaseL = { kAiStateChase, 9, -1, 0, NULL, aiGenDudeMoveForward, thinkChase, NULL }; AISTATE genDudeChaseL = { kAiStateChase, 9, -1, 0, NULL, aiGenDudeMoveForward, thinkChase, NULL };
AISTATE genDudeChaseD = { kAiStateChase, 14, -1, 0, NULL, aiGenDudeMoveForward, thinkChase, NULL }; AISTATE genDudeChaseD = { kAiStateChase, 14, -1, 0, NULL, aiGenDudeMoveForward, thinkChase, NULL };
AISTATE genDudeChaseW = { kAiStateChase, 13, -1, 0, NULL, aiGenDudeMoveForward, thinkChase, NULL }; AISTATE genDudeChaseW = { kAiStateChase, 13, -1, 0, NULL, aiGenDudeMoveForward, thinkChase, NULL };
@ -133,6 +137,40 @@ static void forcePunch(spritetype* pSprite, XSPRITE* pXSprite) {
punchCallback(0,pSprite->extra); punchCallback(0,pSprite->extra);
} }
/*bool sameFamily(spritetype* pDude1, spritetype* pDude2) {
return true;
}*/
bool genDudeAdjustSlope(spritetype* pSprite, XSPRITE* pXSprite, int dist, int weaponType, int by) {
if (spriRangeIsFine(pXSprite->target)) {
int fStart = 0; int fEnd = 0; GENDUDEEXTRA* pExtra = genDudeExtra(pSprite);
for (int i = -8191; i < 8192; i += by) {
HitScan(pSprite, pSprite->z, Cos(pSprite->ang) >> 16, Sin(pSprite->ang) >> 16, i, CLIPMASK0 | CLIPMASK1, dist);
if (!fStart && pXSprite->target == gHitInfo.hitsprite) fStart = i;
else if (fStart && pXSprite->target != gHitInfo.hitsprite) { fEnd = i; break; }
}
if (fStart != fEnd) {
if (weaponType == kGenDudeWeaponHitscan)
gDudeSlope[pSprite->extra] = fStart - ((fStart - fEnd) >> 2);
else if (weaponType == kGenDudeWeaponMissile) {
MissileType* pMissile = &missileInfo[pExtra->curWeapon - kMissileBase];
gDudeSlope[pSprite->extra] = (fStart - ((fStart - fEnd) >> 2)) - (pMissile->clipDist << 1);
}
//viewSetSystemMessage("!!!! FOUND, SLOPE %d, RANGE %d,%d", gDudeSlope[pSprite->extra], fStart, fEnd);
return true;
}
}
return false;
}
void genDudeProcess(spritetype* pSprite, XSPRITE* pXSprite) { void genDudeProcess(spritetype* pSprite, XSPRITE* pXSprite) {
GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index];
@ -195,37 +233,35 @@ static void genDudeAttack1(int, int nXIndex) {
} }
XSPRITE* pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; XSPRITE* pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference;
if (pXSprite->target < 0) return;
if (!(nSprite >= 0 && nSprite < kMaxSprites)) { else if (!(nSprite >= 0 && nSprite < kMaxSprites)) {
consoleSysMsg("nIndex >= 0 && nIndex < kMaxSprites"); consoleSysMsg("nIndex >= 0 && nIndex < kMaxSprites");
return; return;
} }
spritetype* pSprite = &sprite[nSprite]; int dx, dy, dz; int dx, dy, dz; spritetype* pSprite = &sprite[nSprite];
xvel[pSprite->index] = yvel[pSprite->index] = 0; xvel[pSprite->index] = yvel[pSprite->index] = 0;
short curWeapon = gGenDudeExtra[nSprite].curWeapon;
short dispersion = gGenDudeExtra[nSprite].baseDispersion; short dispersion = gGenDudeExtra[nSprite].baseDispersion;
if (inDuck(pXSprite->aiState)) dispersion = ClipLow(dispersion >> 1, 250); if (inDuck(pXSprite->aiState))
dispersion = ClipLow(dispersion >> 1, kGenDudeMinDispesion);
if (curWeapon >= 0 && curWeapon < kVectorMax) { //short dispersion = 1;
short curWeapon = gGenDudeExtra[nSprite].curWeapon; short weaponType = gGenDudeExtra[nSprite].weaponType;
if (weaponType == kGenDudeWeaponHitscan) {
dx = Cos(pSprite->ang) >> 16; dy = Sin(pSprite->ang) >> 16; dz = gDudeSlope[nXIndex]; 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 // dispersal modifiers here in case if non-melee enemy
if (vdist <= 0 || vdist > 1280) { if (!dudeIsMelee(pXSprite)) {
dx += Random3(dispersion); dy += Random3(dispersion); dx += Random3(dispersion); dy += Random3(dispersion); dz += Random3(dispersion);
dz += Random3(dispersion);
} }
actFireVector(pSprite, 0, 0, dx, dy, dz,(VECTOR_TYPE)curWeapon); actFireVector(pSprite, 0, 0, dx, dy, dz,(VECTOR_TYPE)curWeapon);
if (!playGenDudeSound(pSprite, kGenDudeSndAttackNormal)) if (!playGenDudeSound(pSprite, kGenDudeSndAttackNormal))
sfxPlayVectorSound(pSprite, curWeapon); sfxPlayVectorSound(pSprite, curWeapon);
} else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { } else if (weaponType == kGenDudeWeaponSummon) {
spritetype* pSpawned = NULL; int dist = pSprite->clipdist << 4; spritetype* pSpawned = NULL; int dist = pSprite->clipdist << 4;
short slaveCnt = gGenDudeExtra[pSprite->index].slaveCount; short slaveCnt = gGenDudeExtra[pSprite->index].slaveCount;
@ -247,15 +283,12 @@ static void genDudeAttack1(int, int nXIndex) {
sfxPlay3DSoundCP(pSprite, 379, 1, 0, 0x10000 - Random3(0x3000)); sfxPlay3DSoundCP(pSprite, 379, 1, 0, 0x10000 - Random3(0x3000));
} }
} else if (curWeapon >= kMissileBase && curWeapon < kMissileMax) { } else if (weaponType == kGenDudeWeaponMissile) {
dx = Cos(pSprite->ang) >> 16; dx = Cos(pSprite->ang) >> 16; dy = Sin(pSprite->ang) >> 16; dz = gDudeSlope[nXIndex];
dy = Sin(pSprite->ang) >> 16;
dz = gDudeSlope[nXIndex];
// dispersal modifiers here // dispersal modifiers here
dx += Random3(dispersion); dy += Random3(dispersion); dx += Random3(dispersion); dy += Random3(dispersion); dz += Random3(dispersion >> 1);
dz += Random3(dispersion >> 1);
actFireMissile(pSprite, 0, 0, dx, dy, dz, curWeapon); actFireMissile(pSprite, 0, 0, dx, dy, dz, curWeapon);
if (!playGenDudeSound(pSprite, kGenDudeSndAttackNormal)) if (!playGenDudeSound(pSprite, kGenDudeSndAttackNormal))
@ -282,13 +315,12 @@ static void ThrowThing(int nXIndex, bool impact) {
return; return;
short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon; short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon;
if (curWeapon < kThingBase || curWeapon >= kThingMax) short weaponType = gGenDudeExtra[sprite[pXSprite->reference].index].weaponType;
return; if (weaponType != kGenDudeWeaponThrow) return;
const THINGINFO* pThinkInfo = &thingInfo[curWeapon - kThingBase]; const THINGINFO* pThinkInfo = &thingInfo[curWeapon - kThingBase];
if (!pThinkInfo->allowThrow) return; if (!pThinkInfo->allowThrow) return;
else if (!playGenDudeSound(pSprite, kGenDudeSndAttackThrow))
if (!playGenDudeSound(pSprite, kGenDudeSndAttackThrow))
sfxPlay3DSound(pSprite, 455, -1, 0); sfxPlay3DSound(pSprite, 455, -1, 0);
int zThrow = 14500; int zThrow = 14500;
@ -377,6 +409,10 @@ static void ThrowThing(int nXIndex, bool impact) {
} }
static void thinkSearch( spritetype* pSprite, XSPRITE* pXSprite ) { static void thinkSearch( spritetype* pSprite, XSPRITE* pXSprite ) {
// TO DO: if can't see the target, but in fireDist range - stop moving and look around
//viewSetSystemMessage("IN SEARCH");
aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
sub_5F15C(pSprite, pXSprite); sub_5F15C(pSprite, pXSprite);
} }
@ -415,28 +451,23 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
} }
spritetype* pTarget = &sprite[pXSprite->target]; spritetype* pTarget = &sprite[pXSprite->target];
XSPRITE* pXTarget = (!IsDudeSprite(pTarget) || pTarget->extra < 0) ? NULL : &xsprite[pTarget->extra]; XSPRITE* pXTarget = (!IsDudeSprite(pTarget) || !xspriRangeIsFine(pTarget->extra)) ? NULL : &xsprite[pTarget->extra];
if (pXTarget == NULL) { // target lost if (pXTarget == NULL) { // target lost
if(spriteIsUnderwater(pSprite,false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW); if(spriteIsUnderwater(pSprite,false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW);
else aiGenDudeNewState(pSprite, &genDudeSearchShortL); else aiGenDudeNewState(pSprite, &genDudeSearchShortL);
return; return;
} else if (pXTarget->health <= 0) { // target is dead } else if (pXTarget->health <= 0) { // target is dead
PLAYER* pPlayer = NULL; PLAYER* pPlayer = NULL;
if ((!IsPlayerSprite(pTarget) && pSprite->index == pTarget->owner) if ((!IsPlayerSprite(pTarget)) || ((pPlayer = getPlayerById(pTarget->type)) != NULL && pPlayer->fraggerId == pSprite->index)) {
|| ((pPlayer = getPlayerById(pTarget->type)) != NULL && pPlayer->fraggerId == pSprite->index)) {
playGenDudeSound(pSprite, kGenDudeSndTargetDead); playGenDudeSound(pSprite, kGenDudeSndTargetDead);
if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW); if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW);
else aiGenDudeNewState(pSprite, &genDudeSearchShortL); else aiGenDudeNewState(pSprite, &genDudeSearchShortL);
} }
else if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeGotoW); else if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeGotoW);
else aiGenDudeNewState(pSprite, &genDudeGotoL); else aiGenDudeNewState(pSprite, &genDudeGotoL);
return; return;
} }
@ -451,18 +482,17 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
if (inAttack(pXSprite->aiState)) if (inAttack(pXSprite->aiState))
xvelocity = yvelocity = ClipLow(pSprite->clipdist >> 1, 1); xvelocity = yvelocity = ClipLow(pSprite->clipdist >> 1, 1);
//aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
aiGenDudeChooseDirection(pSprite, pXSprite, getangle(dx, dy), xvelocity, yvelocity); aiGenDudeChooseDirection(pSprite, pXSprite, getangle(dx, dy), xvelocity, yvelocity);
GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index];
if (!pExtra->canAttack) { if (!pExtra->canAttack) {
aiSetTarget(pXSprite, pSprite->index); if (pExtra->canWalk) aiSetTarget(pXSprite, pSprite->index);
if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeGotoW); if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeGotoW);
else aiGenDudeNewState(pSprite, &genDudeGotoL); else aiGenDudeNewState(pSprite, &genDudeGotoL);
return; return;
} } else if (IsPlayerSprite(pTarget)) {
PLAYER* pPlayer = &gPlayer[pTarget->type - kDudePlayer1];
if (IsPlayerSprite(pTarget)) {
PLAYER* pPlayer = &gPlayer[ pTarget->type - kDudePlayer1 ];
if (powerupCheck(pPlayer, kPwUpShadowCloak) > 0) { if (powerupCheck(pPlayer, kPwUpShadowCloak) > 0) {
if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW); if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW);
else aiGenDudeNewState(pSprite, &genDudeSearchShortL); else aiGenDudeNewState(pSprite, &genDudeSearchShortL);
@ -471,7 +501,9 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
} }
DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase];
int eyeAboveZ = pDudeInfo->eyeHeight * pSprite->yrepeat << 2; int losAngle = ((getangle(dx, dy) + 1024 - pSprite->ang) & 2047) - 1024;
int eyeAboveZ = (pDudeInfo->eyeHeight * pSprite->yrepeat) << 2;
if (dist > pDudeInfo->seeDist || !cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, if (dist > pDudeInfo->seeDist || !cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum,
pSprite->x, pSprite->y, pSprite->z - eyeAboveZ, pSprite->sectnum)) { pSprite->x, pSprite->y, pSprite->z - eyeAboveZ, pSprite->sectnum)) {
@ -481,26 +513,25 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
return; return;
} }
int nAngle = getangle(dx, dy);
int losAngle = ((kAng180 + nAngle - pSprite->ang) & 2047) - kAng180;
// is the target visible? // is the target visible?
if (dist < pDudeInfo->seeDist && klabs(losAngle) <= pDudeInfo->periphery) { if (dist < pDudeInfo->seeDist && klabs(losAngle) <= pDudeInfo->periphery) {
if (((int)gFrameClock & 64) == 0 && Chance(0x3000) && !spriteIsUnderwater(pSprite, false)) if (((int)gFrameClock & 64) == 0 && Chance(0x3000) && !spriteIsUnderwater(pSprite, false))
playGenDudeSound(pSprite, kGenDudeSndChasing); playGenDudeSound(pSprite, kGenDudeSndChasing);
gDudeSlope[sprite[pXSprite->reference].extra] = (int)divscale(pTarget->z - pSprite->z, dist, 10); aiSetTarget(pXSprite, pXSprite->target);
gDudeSlope[pSprite->extra] = divscale(pTarget->z - pSprite->z, dist, 10);
short curWeapon = gGenDudeExtra[pSprite->index].curWeapon; short curWeapon = gGenDudeExtra[pSprite->index].curWeapon; short weaponType = gGenDudeExtra[pSprite->index].weaponType;
spritetype* pLeech = leechIsDropped(pSprite); VECTORDATA* meleeVector = &gVectorData[22]; spritetype* pLeech = leechIsDropped(pSprite); VECTORDATA* meleeVector = &gVectorData[22];
if (weaponType == kGenDudeWeaponThrow) {
if (pExtra->updReq[kGenDudePropertyAttack])
genDudePrepare(pSprite, kGenDudePropertyAttack);
if (curWeapon >= kThingBase && curWeapon < kThingMax) {
if (klabs(losAngle) < kAng15) { if (klabs(losAngle) < kAng15) {
if (dist < 12264 && dist > 7680 && !spriteIsUnderwater(pSprite, false) && curWeapon != kModernThingEnemyLifeLeech) { if (!thingInfo[curWeapon - kThingBase].allowThrow) {
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
return;
} else if (dist < 12264 && dist > 7680 && !spriteIsUnderwater(pSprite, false) && curWeapon != kModernThingEnemyLifeLeech) {
int pHit = HitScan(pSprite, pSprite->z, dx, dy, 0, 16777280, 0); int pHit = HitScan(pSprite, pSprite->z, dx, dy, 0, 16777280, 0);
switch (pHit) { switch (pHit) {
case 0: case 0:
@ -575,16 +606,17 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
} else { } else {
int defDist = gGenDudeExtra[pSprite->index].fireDist; int vdist = defDist; int vdist; int mdist; int defDist;
defDist = vdist = mdist = gGenDudeExtra[pSprite->index].fireDist;
if (curWeapon > 0 && curWeapon < kVectorMax) { if (weaponType == kGenDudeWeaponHitscan) {
if ((vdist = gVectorData[curWeapon].maxDist) <= 0 || vdist > defDist) if ((vdist = gVectorData[curWeapon].maxDist) <= 0)
vdist = defDist; vdist = defDist;
} else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { } else if (weaponType == kGenDudeWeaponSummon) {
// don't attack slaves // don't attack slaves
if (pXSprite->target >= 0 && sprite[pXSprite->target].owner == pSprite->xvel) { if (pXSprite->target >= 0 && sprite[pXSprite->target].owner == pSprite->index) {
aiSetTarget(pXSprite, pSprite->x, pSprite->y, pSprite->z); aiSetTarget(pXSprite, pSprite->x, pSprite->y, pSprite->z);
return; return;
} else if (gGenDudeExtra[pSprite->index].slaveCount > gGameOptions.nDifficulty || dist < meleeVector->maxDist) { } else if (gGenDudeExtra[pSprite->index].slaveCount > gGameOptions.nDifficulty || dist < meleeVector->maxDist) {
@ -600,20 +632,23 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
} }
} }
} else if (curWeapon >= kMissileBase && curWeapon < kMissileMax) { } else if (weaponType == kGenDudeWeaponMissile) {
// special handling for flame, explosive and life leech missiles // special handling for flame, explosive and life leech missiles
int state = checkAttackState(pSprite, pXSprite); int state = checkAttackState(pSprite, pXSprite);
int mdist = (curWeapon != kMissileFlareAlt) ? 3000 : 2500;
switch (curWeapon) { switch (curWeapon) {
case kMissileLifeLeechRegular: case kMissileLifeLeechRegular:
// pickup life leech if it was thrown previously // pickup life leech if it was thrown previously
if (pLeech != NULL) removeLeech(pLeech); if (pLeech != NULL) removeLeech(pLeech);
mdist = 1500;
break; break;
case kMissileFlareAlt: case kMissileFlareAlt:
mdist = 2500;
fallthrough__;
case kMissileFireball: case kMissileFireball:
case kMissileFireballNapam: case kMissileFireballNapam:
case kMissileFireballCerberus: case kMissileFireballCerberus:
case kMissileFireballTchernobog: case kMissileFireballTchernobog:
if (mdist == defDist) mdist = 3000;
if (dist > mdist || pXSprite->locked == 1) break; if (dist > mdist || pXSprite->locked == 1) break;
else if (dist <= meleeVector->maxDist && Chance(0x9000)) else if (dist <= meleeVector->maxDist && Chance(0x9000))
aiGenDudeNewState(pSprite, &genDudePunch); aiGenDudeNewState(pSprite, &genDudePunch);
@ -623,19 +658,23 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
return; return;
case kMissileFlameSpray: case kMissileFlameSpray:
case kMissileFlameHound: case kMissileFlameHound:
//viewSetSystemMessage("%d", pXTarget->burnTime);
if (spriteIsUnderwater(pSprite, false)) { if (spriteIsUnderwater(pSprite, false)) {
if (dist > meleeVector->maxDist) aiGenDudeNewState(pSprite, &genDudeChaseW); if (dist > meleeVector->maxDist) aiGenDudeNewState(pSprite, &genDudeChaseW);
else if (Chance(0x8000)) aiGenDudeNewState(pSprite, &genDudePunch); else if (Chance(0x8000)) aiGenDudeNewState(pSprite, &genDudePunch);
else aiGenDudeNewState(pSprite, &genDudeDodgeW); else aiGenDudeNewState(pSprite, &genDudeDodgeShortW);
return;
} else if (dist <= 4000 && pXTarget->burnTime >= 2000 && pXTarget->burnSource == pSprite->index) {
if (dist > meleeVector->maxDist) aiGenDudeNewState(pSprite, &genDudeChaseL);
else aiGenDudeNewState(pSprite, &genDudePunch);
return; return;
} }
vdist = 3500 + (gGameOptions.nDifficulty * 400);
vdist = 4200; if (((int)gFrameClock & 16) == 0) vdist += Random(800);
break; break;
} }
} else if (curWeapon >= kTrapExploder && curWeapon < (kTrapExploder + kExplodeMax) - 1) { } else if (weaponType == kGenDudeWeaponKamikaze) {
int nType = curWeapon - kTrapExploder; EXPLOSION* pExpl = &explodeInfo[nType]; int nType = curWeapon - kTrapExploder; EXPLOSION* pExpl = &explodeInfo[nType];
if (CheckProximity(pSprite, pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pExpl->radius / 2)) { if (CheckProximity(pSprite, pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pExpl->radius >> 1)) {
xvel[pSprite->xvel] = zvel[pSprite->xvel] = yvel[pSprite->xvel] = 0; xvel[pSprite->xvel] = zvel[pSprite->xvel] = yvel[pSprite->xvel] = 0;
if (doExplosion(pSprite, nType) && pXSprite->health > 0) if (doExplosion(pSprite, nType) && pXSprite->health > 0)
actDamageSprite(pSprite->xvel, pSprite, DAMAGE_TYPE_3, 65535); actDamageSprite(pSprite->xvel, pSprite, DAMAGE_TYPE_3, 65535);
@ -643,30 +682,235 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) {
return; return;
} }
//if (dist <= vdist && pXSprite->aiState == &genDudeChaseD)
//aiGenDudeNewState(pSprite, &genDudeChaseL);
int state = checkAttackState(pSprite, pXSprite); int state = checkAttackState(pSprite, pXSprite);
int kAngle = (dist <= 2048) ? kAngle = pDudeInfo->periphery : kAng5; int kAngle = (dudeIsMelee(pXSprite) || dist <= kGenDudeMaxMeleeDist) ? pDudeInfo->periphery : kGenDudeKlabsAng;
if (dist < vdist && klabs(losAngle) < kAngle) { if (dist < vdist && klabs(losAngle) < kAngle) {
if (pExtra->canWalk) {
int objDist = -1; int targetDist = -1; int hit = -1;
if (weaponType == kGenDudeWeaponHitscan)
hit = HitScan(pSprite, pSprite->z, Cos(pSprite->ang) >> 16, Sin(pSprite->ang) >> 16, gDudeSlope[pSprite->extra], CLIPMASK1, dist);
else if (weaponType == kGenDudeWeaponMissile)
hit = HitScan(pSprite, pSprite->z, Cos(pSprite->ang) >> 16, Sin(pSprite->ang) >> 16, gDudeSlope[pSprite->extra], CLIPMASK0, dist);
if (hit >= 0) {
targetDist = dist - (pTarget->clipdist << 2);
objDist = approxDist(gHitInfo.hitx - pSprite->x, gHitInfo.hity - pSprite->y);
}
if (pXSprite->target != gHitInfo.hitsprite && targetDist > objDist) {
walltype* pHWall = NULL; XWALL* pXHWall = NULL;
spritetype* pHSprite = NULL; XSPRITE* pXHSprite = NULL;
bool hscn = false; bool blck = false; bool failed = false;
switch (hit) {
case 3:
pHSprite = &sprite[gHitInfo.hitsprite];
if (xspriRangeIsFine(pHSprite->extra)) pXHSprite = &xsprite[pHSprite->extra];
hscn = (pHSprite->cstat & CSTAT_SPRITE_BLOCK_HITSCAN); blck = (pHSprite->cstat & CSTAT_SPRITE_BLOCK);
break;
case 0:
case 4:
pHWall = &wall[gHitInfo.hitwall];
if (xwallRangeIsFine(pHWall->extra)) pXHWall = &xwall[pHWall->extra];
hscn = (pHWall->cstat & CSTAT_WALL_BLOCK_HITSCAN); blck = (pHWall->cstat & CSTAT_WALL_BLOCK);
break;
}
switch (hit) {
case 0:
//if (hit == 0) viewSetSystemMessage("WALL HIT %d", gHitInfo.hitwall);
fallthrough__;
case 1:
//if (hit == 1) viewSetSystemMessage("CEIL HIT %d", gHitInfo.hitsect);
fallthrough__;
case 2:
if (hit == 2) viewSetSystemMessage("FLOOR HIT %d", gHitInfo.hitsect);
if (weaponType != kGenDudeWeaponMissile && genDudeAdjustSlope(pSprite, pXSprite, dist, weaponType)
&& dist < (int)(6000 + Random(2000)) && pExtra->baseDispersion < kGenDudeMaxDispersion >> 1) break;
else if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
return;
case 3:
if (pHSprite->statnum == kStatFX || pHSprite->statnum == kStatProjectile || pHSprite->statnum == kStatDebris)
break;
if (IsDudeSprite(pHSprite) && (weaponType != kGenDudeWeaponHitscan || hscn)) {
// dodge a bit in sides
if (pXHSprite->target != pSprite->index) {
if (pExtra->baseDispersion < 1024 && weaponType != kGenDudeWeaponMissile) {
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeDodgeShorterW);
else if (inDuck(pXSprite->aiState)) aiGenDudeNewState(pSprite, &genDudeDodgeShorterD);
else aiGenDudeNewState(pSprite, &genDudeDodgeShorterL);
}
else if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeDodgeShortW);
else if (inDuck(pXSprite->aiState)) aiGenDudeNewState(pSprite, &genDudeDodgeShortD);
else aiGenDudeNewState(pSprite, &genDudeDodgeShortL);
switch (pHSprite->type) {
case kDudeModernCustom: // and make dude which could be hit to dodge too
if (!dudeIsMelee(pXHSprite) && Chance(dist << 4)) {
if (!inAttack(pXHSprite->aiState)) {
if (spriteIsUnderwater(pHSprite)) aiGenDudeNewState(pHSprite, &genDudeDodgeShorterW);
else if (inDuck(pXSprite->aiState)) aiGenDudeNewState(pHSprite, &genDudeDodgeShorterD);
else aiGenDudeNewState(pHSprite, &genDudeDodgeShorterL);
// preferable in opposite sides
if (Chance(0x8000)) {
if (pXSprite->dodgeDir == 1) pXHSprite->dodgeDir = -1;
else if (pXSprite->dodgeDir == -1) pXHSprite->dodgeDir = 1;
}
break;
}
if (pSprite->x < pHSprite->x) {
if (Chance(0x9000) && pTarget->x > pHSprite->x) pXSprite->dodgeDir = -1;
else pXSprite->dodgeDir = 1;
} else {
if (Chance(0x9000) && pTarget->x > pHSprite->x) pXSprite->dodgeDir = 1;
else pXSprite->dodgeDir = -1;
}
}
break;
default:
if (pSprite->x < pHSprite->x) {
if (Chance(0x9000) && pTarget->x > pHSprite->x) pXSprite->dodgeDir = -1;
else pXSprite->dodgeDir = 1;
} else {
if (Chance(0x9000) && pTarget->x > pHSprite->x) pXSprite->dodgeDir = 1;
else pXSprite->dodgeDir = -1;
}
break;
}
return;
}
break;
} else if (weaponType == kGenDudeWeaponHitscan && hscn) {
if (genDudeAdjustSlope(pSprite, pXSprite, dist, weaponType)) break;
VectorScan(pSprite, 0, 0, Cos(pSprite->ang) >> 16, Sin(pSprite->ang) >> 16, gDudeSlope[pSprite->extra], dist, 1);
if (pXSprite->target == gHitInfo.hitsprite) break;
bool immune = isImmune(pHSprite, gVectorData[curWeapon].dmgType);
if (!(pXHSprite != NULL && (!immune || (immune && pHSprite->statnum == kStatThing && pXHSprite->Vector)) && !pXHSprite->locked)) {
if ((approxDist(gHitInfo.hitx - pSprite->x, gHitInfo.hity - pSprite->y) <= 1500 && !blck)
|| (dist <= (int)(pExtra->fireDist / ClipLow(Random(4), 1)))) {
//viewSetSystemMessage("GO CHASE");
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
return;
}
int wd1 = picWidth(pHSprite->picnum, pHSprite->xrepeat);
int wd2 = picWidth(pSprite->picnum, pSprite->xrepeat);
if (wd1 < (wd2 << 3)) {
//viewSetSystemMessage("OBJ SIZE: %d DUDE SIZE: %d", wd1, wd2);
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeDodgeShorterW);
else if (inDuck(pXSprite->aiState)) aiGenDudeNewState(pSprite, &genDudeDodgeShorterD);
else aiGenDudeNewState(pSprite, &genDudeDodgeShorterL);
if (pSprite->x < pHSprite->x) {
if (Chance(0x3000) && pTarget->x > pHSprite->x) pXSprite->dodgeDir = -1;
else pXSprite->dodgeDir = 1;
} else {
if (Chance(0x3000) && pTarget->x > pHSprite->x) pXSprite->dodgeDir = 1;
else pXSprite->dodgeDir = -1;
}
if (((gSpriteHit[pSprite->extra].hit & 0xc000) == 0x8000) || ((gSpriteHit[pSprite->extra].hit & 0xc000) == 0xc000)) {
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
pXSprite->goalAng = Random(kAng360);
//viewSetSystemMessage("WALL OR SPRITE TOUCH");
}
} else {
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
//viewSetSystemMessage("TOO BIG OBJECT TO DODGE!!!!!!!!");
}
return;
}
break;
}
fallthrough__;
case 4:
if (hit == 4 && weaponType == kGenDudeWeaponHitscan && hscn) {
bool masked = (pHWall->cstat & CSTAT_WALL_MASKED);
if (masked) VectorScan(pSprite, 0, 0, Cos(pSprite->ang) >> 16, Sin(pSprite->ang) >> 16, gDudeSlope[pSprite->extra], dist, 1);
//viewSetSystemMessage("WALL VHIT: %d", gHitInfo.hitwall);
if ((pXSprite->target != gHitInfo.hitsprite) && (pHWall->type != kWallGib || !masked || pXHWall == NULL || !pXHWall->triggerVector || pXHWall->locked)) {
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
return;
}
} else if (hit >= 3 && weaponType == kGenDudeWeaponMissile && blck) {
switch (curWeapon) {
case kMissileLifeLeechRegular:
case kMissileTeslaAlt:
case kMissileFlareAlt:
case kMissileFireball:
case kMissileFireballNapam:
case kMissileFireballCerberus:
case kMissileFireballTchernobog: {
// allow attack if dude is far from object, but target is close to it
int dudeDist = approxDist(gHitInfo.hitx - pSprite->x, gHitInfo.hity - pSprite->y);
int targetDist = approxDist(gHitInfo.hitx - pTarget->x, gHitInfo.hity - pTarget->y);
if (dudeDist < mdist) {
//viewSetSystemMessage("DUDE CLOSE TO OBJ: %d, MDIST: %d", dudeDist, mdist);
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeChaseW);
else aiGenDudeNewState(pSprite, &genDudeChaseL);
return;
} else if (targetDist <= mdist >> 1) {
//viewSetSystemMessage("TARGET CLOSE TO OBJ: %d, MDIST: %d", targetDist, mdist >> 1);
break;
}
fallthrough__;
}
default:
//viewSetSystemMessage("DEF HIT: %d, MDIST: %d", hit, mdist);
if (hit == 4) failed = (pHWall->type != kWallGib || pXHWall == NULL || !pXHWall->triggerVector || pXHWall->locked);
else if (hit == 3 && (failed = (pHSprite->statnum != kStatThing || pXHSprite == NULL || pXHSprite->locked)) == false) {
// check also for damage resistance (all possible damages missile can use)
for (int i = 0; i < kDmgMax; i++) {
if (missileInfo[curWeapon - kMissileBase].dmgType[i] && (failed = isImmune(pHSprite, i)) == false)
break;
}
}
if (failed) {
if (spriteIsUnderwater(pSprite)) aiGenDudeNewState(pSprite, &genDudeSearchW);
else aiGenDudeNewState(pSprite, &genDudeSearchL);
return;
}
break;
}
}
break;
}
}
}
switch (state) { switch (state) {
case 1: case 1:
aiGenDudeNewState(pSprite, &genDudeFireW); aiGenDudeNewState(pSprite, &genDudeFireW);
pXSprite->aiState->nextState = &genDudeFireW; pXSprite->aiState->nextState = &genDudeFireW;
return; break;
case 2: case 2:
aiGenDudeNewState(pSprite, &genDudeFireD); aiGenDudeNewState(pSprite, &genDudeFireD);
pXSprite->aiState->nextState = &genDudeFireD; pXSprite->aiState->nextState = &genDudeFireD;
return; break;
default: default:
aiGenDudeNewState(pSprite, &genDudeFireL); aiGenDudeNewState(pSprite, &genDudeFireL);
pXSprite->aiState->nextState = &genDudeFireL; pXSprite->aiState->nextState = &genDudeFireL;
return; break;
} }
} else { } else {
if (seqGetID(3, pSprite->extra) == pXSprite->data2 + (state < 3) ? 8 : 6) { if (seqGetID(3, pSprite->extra) == pXSprite->data2 + ((state < 3) ? 8 : 6)) {
if (state == 1) pXSprite->aiState->nextState = &genDudeChaseW; if (state == 1) pXSprite->aiState->nextState = &genDudeChaseW;
else if (state == 2) pXSprite->aiState->nextState = &genDudeChaseD; else if (state == 2) pXSprite->aiState->nextState = &genDudeChaseD;
else pXSprite->aiState->nextState = &genDudeChaseL; else pXSprite->aiState->nextState = &genDudeChaseL;
@ -713,16 +957,6 @@ int checkAttackState(spritetype* pSprite, XSPRITE* pXSprite) {
return 0; return 0;
} }
bool TargetNearThing(spritetype* pSprite, int thingType) {
for ( int nSprite = headspritesect[pSprite->sectnum]; nSprite >= 0; nSprite = nextspritesect[nSprite] )
{
// check for required things or explosions in the same sector as the target
if ( sprite[nSprite].type == thingType || sprite[nSprite].statnum == kStatExplosion )
return true; // indicate danger
}
return false;
}
///// For gen dude ///// For gen dude
int getGenDudeMoveSpeed(spritetype* pSprite,int which, bool mul, bool shift) { int getGenDudeMoveSpeed(spritetype* pSprite,int which, bool mul, bool shift) {
DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase];
@ -802,7 +1036,7 @@ void aiGenDudeMoveForward(spritetype* pSprite, XSPRITE* pXSprite ) {
int sin = Sin(pSprite->ang); int sin = Sin(pSprite->ang);
int cos = Cos(pSprite->ang); int cos = Cos(pSprite->ang);
int frontSpeed = gGenDudeExtra[pSprite->index].frontSpeed; int frontSpeed = gGenDudeExtra[pSprite->index].moveSpeed;
xvel[pSprite->xvel] += mulscale(cos, frontSpeed, 30); xvel[pSprite->xvel] += mulscale(cos, frontSpeed, 30);
yvel[pSprite->xvel] += mulscale(sin, frontSpeed, 30); yvel[pSprite->xvel] += mulscale(sin, frontSpeed, 30);
} }
@ -855,17 +1089,21 @@ void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState) {
// redirect dudes which cannot walk to non-walk states // redirect dudes which cannot walk to non-walk states
if (!gGenDudeExtra[pSprite->index].canWalk) { if (!gGenDudeExtra[pSprite->index].canWalk) {
if (pAIState == &genDudeDodgeL || pAIState == &genDudeDodgeShortL) if (pAIState == &genDudeDodgeL || pAIState == &genDudeDodgeShortL || pAIState == &genDudeDodgeShorterL)
pAIState = &genDudeRecoilL; pAIState = &genDudeRecoilL;
else if (pAIState == &genDudeDodgeD || pAIState == &genDudeDodgeShortD) else if (pAIState == &genDudeDodgeD || pAIState == &genDudeDodgeShortD || pAIState == &genDudeDodgeShorterD)
pAIState = &genDudeRecoilD; pAIState = &genDudeRecoilD;
else if (pAIState == &genDudeDodgeW || pAIState == &genDudeDodgeD) else if (pAIState == &genDudeDodgeW || pAIState == &genDudeDodgeShortW || pAIState == &genDudeDodgeShorterW)
pAIState = &genDudeRecoilW; pAIState = &genDudeRecoilW;
else if (pAIState == &genDudeSearchL) pAIState = &genDudeSearchNoWalkL; else if (pAIState == &genDudeSearchL || pAIState == &genDudeSearchShortL)
else if (pAIState == &genDudeSearchW) pAIState = &genDudeSearchNoWalkW; pAIState = &genDudeSearchNoWalkL;
else if (pAIState == &genDudeSearchW || pAIState == &genDudeSearchShortW)
pAIState = &genDudeSearchNoWalkW;
else if (pAIState == &genDudeGotoL) pAIState = &genDudeIdleL; else if (pAIState == &genDudeGotoL) pAIState = &genDudeIdleL;
else if (pAIState == &genDudeGotoW) pAIState = &genDudeIdleW; else if (pAIState == &genDudeGotoW) pAIState = &genDudeIdleW;
else if (pAIState == &genDudeChaseL) pAIState = &genDudeChaseNoWalkL; else if (pAIState == &genDudeChaseL) pAIState = &genDudeChaseNoWalkL;
@ -880,6 +1118,11 @@ void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState) {
} }
if (!gGenDudeExtra[pSprite->index].canRecoil) {
if (pAIState == &genDudeRecoilL || pAIState == &genDudeRecoilD) pAIState = &genDudeIdleL;
else if (pAIState == &genDudeRecoilW) pAIState = &genDudeIdleW;
}
pXSprite->stateTimer = pAIState->stateTicks; pXSprite->aiState = pAIState; pXSprite->stateTimer = pAIState->stateTicks; pXSprite->aiState = pAIState;
int seqStartId = pXSprite->data2; int seqStartId = pXSprite->data2;
@ -892,7 +1135,7 @@ void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState) {
} }
bool playGenDudeSound(spritetype* pSprite, int mode, bool forceInterrupt) { bool playGenDudeSound(spritetype* pSprite, int mode) {
if (mode < kGenDudeSndTargetSpot || mode >= kGenDudeSndMax) return false; if (mode < kGenDudeSndTargetSpot || mode >= kGenDudeSndMax) return false;
GENDUDESND* sndInfo =& gCustomDudeSnd[mode]; bool gotSnd = false; GENDUDESND* sndInfo =& gCustomDudeSnd[mode]; bool gotSnd = false;
@ -912,7 +1155,6 @@ bool playGenDudeSound(spritetype* pSprite, int mode, bool forceInterrupt) {
pExtra->sndPlaying = false; pExtra->sndPlaying = false;
break; break;
} }
//viewSetSystemMessage("PLAYING %d / %d / %d", Bonkle[i].atc, (sndId + a), Bonkle[i].at0);
return true; return true;
} }
} }
@ -1050,26 +1292,28 @@ bool dudeIsMelee(XSPRITE* pXSprite) {
void scaleDamage(XSPRITE* pXSprite) { void scaleDamage(XSPRITE* pXSprite) {
short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon; short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon;
short* curScale = gGenDudeExtra[sprite[pXSprite->reference].index].dmgControl; short weaponType = gGenDudeExtra[sprite[pXSprite->reference].index].weaponType;
unsigned short* curScale = gGenDudeExtra[sprite[pXSprite->reference].index].dmgControl;
for (int i = 0; i < kDmgMax; i++) for (int i = 0; i < kDmgMax; i++)
curScale[i] = dudeInfo[kDudeModernCustom - kDudeBase].startDamage[i]; curScale[i] = dudeInfo[kDudeModernCustom - kDudeBase].startDamage[i];
// all enemies with vector weapons gets extra resistance to bullet damage // all enemies with vector weapons gets extra resistance to bullet damage
if (curWeapon > 0 && curWeapon < kVectorMax) { if (weaponType == kGenDudeWeaponHitscan) {
//curScale[kDmgBullet] -= 10;
curScale[kDmgBullet] -= 10;
// just copy damage resistance of dude that should be summoned // just copy damage resistance of dude that should be summoned
} else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { } else if (weaponType == kGenDudeWeaponSummon) {
for (int i = 0; i < kDmgMax; i++) for (int i = 0; i < kDmgMax; i++)
curScale[i] = dudeInfo[curWeapon - kDudeBase].startDamage[i]; curScale[i] = dudeInfo[curWeapon - kDudeBase].startDamage[i];
// these does not like the explosions and burning // these does not like the explosions and burning
} else if (curWeapon >= kTrapExploder && curWeapon < (kTrapExploder + kExplodeMax) - 1) { } else if (weaponType == kGenDudeWeaponKamikaze) {
curScale[kDmgBurn] = curScale[kDmgExplode] = 512; curScale[kDmgBurn] = curScale[kDmgExplode] = 512;
} else if (curWeapon >= kMissileBase && curWeapon < kThingMax) { } else if (weaponType == kGenDudeWeaponMissile || weaponType == kGenDudeWeaponThrow) {
switch (curWeapon) { switch (curWeapon) {
case kMissileButcherKnife: case kMissileButcherKnife:
@ -1092,7 +1336,7 @@ void scaleDamage(XSPRITE* pXSprite) {
case kThingPodFireBall: case kThingPodFireBall:
case kThingNapalmBall: case kThingNapalmBall:
curScale[kDmgBurn] = 32; curScale[kDmgBurn] = 32;
curScale[kDmgExplode] = 50; curScale[kDmgExplode] -= 20;
break; break;
case kMissileLifeLeechRegular: case kMissileLifeLeechRegular:
case kThingDroppedLifeLeech: case kThingDroppedLifeLeech:
@ -1134,29 +1378,29 @@ void scaleDamage(XSPRITE* pXSprite) {
switch (pXSprite->dropMsg) { switch (pXSprite->dropMsg) {
case kItemArmorAsbest: case kItemArmorAsbest:
curScale[kDmgBurn] = 0; curScale[kDmgBurn] = 0;
curScale[kDmgExplode] -= 25; curScale[kDmgExplode] -= 30;
break; break;
case kItemArmorBasic: case kItemArmorBasic:
curScale[kDmgBurn] -= 10; curScale[kDmgBurn] -= 15;
curScale[kDmgExplode] -= 10; curScale[kDmgExplode] -= 15;
curScale[kDmgBullet] -= 10; curScale[kDmgBullet] -= 15;
curScale[kDmgSpirit] -= 10; curScale[kDmgSpirit] -= 15;
break; break;
case kItemArmorBody: case kItemArmorBody:
curScale[kDmgBullet] -= 20; curScale[kDmgBullet] -= 30;
break; break;
case kItemArmorFire: case kItemArmorFire:
curScale[kDmgBurn] -= 20; curScale[kDmgBurn] -= 30;
curScale[kDmgExplode] -= 10; curScale[kDmgExplode] -= 30;
break; break;
case kItemArmorSpirit: case kItemArmorSpirit:
curScale[kDmgSpirit] -= 20; curScale[kDmgSpirit] -= 30;
break; break;
case kItemArmorSuper: case kItemArmorSuper:
curScale[kDmgBurn] -= 20; curScale[kDmgBurn] -= 60;
curScale[kDmgExplode] -= 20; curScale[kDmgExplode] -= 60;
curScale[kDmgBullet] -= 20; curScale[kDmgBullet] -= 60;
curScale[kDmgSpirit] -= 20; curScale[kDmgSpirit] -= 60;
break; break;
} }
} }
@ -1240,13 +1484,9 @@ void scaleDamage(XSPRITE* pXSprite) {
for (int i = 0; i < kDmgMax; i++) for (int i = 0; i < kDmgMax; i++)
curScale[i] = mulscale8(DudeDifficulty[gGameOptions.nDifficulty], ClipLow(curScale[i], 1)); curScale[i] = mulscale8(DudeDifficulty[gGameOptions.nDifficulty], ClipLow(curScale[i], 1));
short* dc = curScale; //short* dc = curScale;
if (gGenDudeExtra[sprite[pXSprite->reference].index].canFly) { //if (pXSprite->rxID == 788)
curScale[0] = -1; //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]);
}
if (pXSprite->rxID == 788)
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) { int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp) {
@ -1260,7 +1500,7 @@ int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp) {
disp = (((shots * 1000) / nFrames) / ticks) * 20; disp = (((shots * 1000) / nFrames) / ticks) * 20;
if (gGameOptions.nDifficulty > 0) if (gGameOptions.nDifficulty > 0)
disp /= gGameOptions.nDifficulty + 1; disp /= gGameOptions.nDifficulty;
//viewSetSystemMessage("DISP: %d FRAMES: %d SHOTS: %d TICKS %d", disp, nFrames, shots, ticks); //viewSetSystemMessage("DISP: %d FRAMES: %d SHOTS: %d TICKS %d", disp, nFrames, shots, ticks);
@ -1282,8 +1522,8 @@ int getRangeAttackDist(spritetype* pSprite, int minDist, int maxDist) {
picnum = seqGetTile(&pSeq->frames[0]); picnum = seqGetTile(&pSeq->frames[0]);
} }
} }
dist = tilesiz[picnum].y << 8;
dist = tilesiz[picnum].y << 8;
if (yrepeat < 64) dist -= (64 - yrepeat) * mul; if (yrepeat < 64) dist -= (64 - yrepeat) * mul;
else if (yrepeat > 64) dist += (yrepeat - 64) * (mul / 3); else if (yrepeat > 64) dist += (yrepeat - 64) * (mul / 3);
} }
@ -1299,7 +1539,7 @@ int getBaseChanceModifier(int baseChance) {
int getRecoilChance(spritetype* pSprite) { int getRecoilChance(spritetype* pSprite) {
XSPRITE* pXSprite = &xsprite[pSprite->extra]; int mass = getSpriteMassBySize(pSprite); XSPRITE* pXSprite = &xsprite[pSprite->extra]; int mass = getSpriteMassBySize(pSprite);
int baseChance = (!dudeIsMelee(pXSprite) ? 0x8000 : 0x5000); int baseChance = (!dudeIsMelee(pXSprite) ? 0x8000 : 0x4000);
baseChance = getBaseChanceModifier(baseChance) + pXSprite->data3; baseChance = getBaseChanceModifier(baseChance) + pXSprite->data3;
int chance = ((baseChance / mass) << 7); int chance = ((baseChance / mass) << 7);
@ -1307,16 +1547,13 @@ int getRecoilChance(spritetype* pSprite) {
} }
int getDodgeChance(spritetype* pSprite) { int getDodgeChance(spritetype* pSprite) {
int mass = getSpriteMassBySize(pSprite); int baseChance = getBaseChanceModifier(0x6000); XSPRITE* pXSprite = &xsprite[pSprite->extra]; int mass = getSpriteMassBySize(pSprite);
if (pSprite->extra >= 0) { int baseChance = (!dudeIsMelee(pXSprite) ? 0x6000 : 0x1000);
XSPRITE* pXSprite = &xsprite[pSprite->extra]; baseChance = getBaseChanceModifier(baseChance) + pXSprite->data3;
baseChance += pXSprite->burnTime;
if (dudeIsMelee(pXSprite))
baseChance = 0x1000;
}
int chance = ((baseChance / mass) << 7); int chance = ((baseChance / mass) << 7);
return chance; return chance;
} }
void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event) void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event)
@ -1327,7 +1564,7 @@ void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event)
} }
int nTarget = pXSprite->target; int nTarget = pXSprite->target;
if (spriRangeIsFine(nTarget)) { if (spriRangeIsFine(nTarget) && nTarget != pSprite->owner) {
spritetype* pTarget = &sprite[nTarget]; spritetype* pTarget = &sprite[nTarget];
if (pTarget->statnum == kStatDude && !(pTarget->flags & 32) && pTarget->extra > 0 && pTarget->extra < kMaxXSprites && !pXSprite->stateTimer) { if (pTarget->statnum == kStatDude && !(pTarget->flags & 32) && pTarget->extra > 0 && pTarget->extra < kMaxXSprites && !pXSprite->stateTimer) {
@ -1465,8 +1702,18 @@ void updateTargetOfSlaves(spritetype* pSprite) {
} }
} }
bool inDodge(AISTATE* aiState) { short inDodge(AISTATE* aiState) {
return (aiState == &genDudeDodgeShortW || aiState == &genDudeDodgeShortL || aiState == &genDudeDodgeShortD); if (aiState == &genDudeDodgeL) return 1;
else if (aiState == &genDudeDodgeD) return 2;
else if (aiState == &genDudeDodgeW) return 3;
else if (aiState == &genDudeDodgeShortL) return 4;
else if (aiState == &genDudeDodgeShortD) return 5;
else if (aiState == &genDudeDodgeShortW) return 6;
else if (aiState == &genDudeDodgeShorterL) return 7;
else if (aiState == &genDudeDodgeShorterD) return 8;
else if (aiState == &genDudeDodgeShorterW) return 9;
return 0;
} }
bool inIdle(AISTATE* aiState) { bool inIdle(AISTATE* aiState) {
@ -1483,6 +1730,16 @@ short inSearch(AISTATE* aiState) {
return 0; return 0;
} }
short inChase(AISTATE* aiState) {
if (aiState == &genDudeChaseL) return 1;
else if (aiState == &genDudeChaseD) return 2;
else if (aiState == &genDudeChaseW) return 3;
else if (aiState == &genDudeChaseNoWalkL) return 4;
else if (aiState == &genDudeChaseNoWalkD) return 5;
else if (aiState == &genDudeChaseNoWalkW) return 6;
else return 0;
}
short inRecoil(AISTATE* aiState) { short inRecoil(AISTATE* aiState) {
if (aiState == &genDudeRecoilL || aiState == &genDudeRecoilTesla) return 1; if (aiState == &genDudeRecoilL || aiState == &genDudeRecoilTesla) return 1;
else if (aiState == &genDudeRecoilD) return 2; else if (aiState == &genDudeRecoilD) return 2;
@ -1512,7 +1769,6 @@ bool canWalk(spritetype* pSprite) {
return gGenDudeExtra[pSprite->index].canWalk; return gGenDudeExtra[pSprite->index].canWalk;
} }
int genDudeSeqStartId(XSPRITE* pXSprite) { int genDudeSeqStartId(XSPRITE* pXSprite) {
if (genDudePrepare(&sprite[pXSprite->reference], kGenDudePropertyStates)) return pXSprite->data2; if (genDudePrepare(&sprite[pXSprite->reference], kGenDudePropertyStates)) return pXSprite->data2;
else return kGenDudeDefaultSeq; else return kGenDudeDefaultSeq;
@ -1539,11 +1795,13 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
switch (propId) { switch (propId) {
case kGenDudePropertyAll: case kGenDudePropertyAll:
case kGenDudePropertyInitVals: case kGenDudePropertyInitVals:
pExtra->frontSpeed = getGenDudeMoveSpeed(pSprite, 0, true, false); pExtra->moveSpeed = getGenDudeMoveSpeed(pSprite, 0, true, false);
pExtra->initVals[0] = pSprite->xrepeat; pExtra->initVals[0] = pSprite->xrepeat;
pExtra->initVals[1] = pSprite->yrepeat; pExtra->initVals[1] = pSprite->yrepeat;
pExtra->initVals[2] = pSprite->clipdist; pExtra->initVals[2] = pSprite->clipdist;
if (propId) break; if (propId) break;
fallthrough__;
case kGenDudePropertyWeapon: { case kGenDudePropertyWeapon: {
pExtra->curWeapon = pXSprite->data1; pExtra->curWeapon = pXSprite->data1;
switch (pXSprite->data1) { switch (pXSprite->data1) {
@ -1556,38 +1814,52 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
if (pExtra->curWeapon > 0 && gSysRes.Lookup(pXSprite->data2 + kGenDudeSeqAttackNormalL, "SEQ")) if (pExtra->curWeapon > 0 && gSysRes.Lookup(pXSprite->data2 + kGenDudeSeqAttackNormalL, "SEQ"))
pExtra->canAttack = true; pExtra->canAttack = true;
pExtra->weaponType = kGenDudeWeaponNone;
if (pExtra->curWeapon > 0 && pExtra->curWeapon < kVectorMax) pExtra->weaponType = kGenDudeWeaponHitscan;
else if (pExtra->curWeapon >= kDudeBase && pExtra->curWeapon < kDudeMax) pExtra->weaponType = kGenDudeWeaponSummon;
else if (pExtra->curWeapon >= kMissileBase && pExtra->curWeapon < kMissileMax) pExtra->weaponType = kGenDudeWeaponMissile;
else if (pExtra->curWeapon >= kThingBase && pExtra->curWeapon < kThingMax) pExtra->weaponType = kGenDudeWeaponThrow;
else if (pExtra->curWeapon >= kTrapExploder && pExtra->curWeapon < (kTrapExploder + kExplodeMax) - 1)
pExtra->weaponType = kGenDudeWeaponKamikaze;
pExtra->isMelee = false; pExtra->isMelee = false;
if (pExtra->curWeapon >= kTrapExploder && pExtra->curWeapon < (kTrapExploder + kExplodeMax) - 1) pExtra->isMelee = true; if (pExtra->weaponType == kGenDudeWeaponKamikaze) pExtra->isMelee = true;
else if (pExtra->curWeapon >= 0 && pExtra->curWeapon < kVectorMax) { else if (pExtra->weaponType == kGenDudeWeaponHitscan) {
if (gVectorData[pExtra->curWeapon].maxDist > 0 && gVectorData[pExtra->curWeapon].maxDist <= kGenDudeMaxMeleeDist) if (gVectorData[pExtra->curWeapon].maxDist > 0 && gVectorData[pExtra->curWeapon].maxDist <= kGenDudeMaxMeleeDist)
pExtra->isMelee = true; pExtra->isMelee = true;
} }
if (propId) break; if (propId) break;
fallthrough__;
} }
case kGenDudePropertyDmgScale: case kGenDudePropertyDmgScale:
scaleDamage(pXSprite); scaleDamage(pXSprite);
if (propId) break; if (propId) break;
fallthrough__;
case kGenDudePropertyMass: { case kGenDudePropertyMass: {
// to ensure mass get's updated, let's clear all cache // to ensure mass gets updated, let's clear all cache
SPRITEMASS* pMass = &gSpriteMass[pSprite->index]; SPRITEMASS* pMass = &gSpriteMass[pSprite->index];
pMass->seqId = pMass->picnum = pMass->xrepeat = pMass->yrepeat = pMass->clipdist = 0; pMass->seqId = pMass->picnum = pMass->xrepeat = pMass->yrepeat = pMass->clipdist = 0;
pMass->mass = pMass->airVel = pMass->fraction = 0; pMass->mass = pMass->airVel = pMass->fraction = 0;
getSpriteMassBySize(pSprite); getSpriteMassBySize(pSprite);
if (propId) break; if (propId) break;
fallthrough__;
} }
case kGenDudePropertyAttack: case kGenDudePropertyAttack:
pExtra->fireDist = getRangeAttackDist(pSprite, 1200, 45000); pExtra->fireDist = getRangeAttackDist(pSprite, 1200, 45000);
pExtra->throwDist = pExtra->fireDist; // temp pExtra->throwDist = pExtra->fireDist; // temp
pExtra->baseDispersion = getDispersionModifier(pSprite, 200, 3500); pExtra->baseDispersion = getDispersionModifier(pSprite, 200, 3500);
if (propId) break; if (propId) break;
fallthrough__;
case kGenDudePropertyStates: { case kGenDudePropertyStates: {
pExtra->canFly = false; pExtra->canFly = false;
// check the animation // check the animation
int seqStartId = dudeInfo[pSprite->type - kDudeBase].seqStartID; int seqStartId = -1;
if (pXSprite->data2 <= 0) seqStartId = pXSprite->data2 = dudeInfo[pSprite->type - kDudeBase].seqStartID; if (pXSprite->data2 <= 0) seqStartId = pXSprite->data2 = dudeInfo[pSprite->type - kDudeBase].seqStartID;
else seqStartId = pXSprite->data2; else seqStartId = pXSprite->data2;
@ -1595,18 +1867,26 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
switch (i - seqStartId) { switch (i - seqStartId) {
case kGenDudeSeqIdleL: case kGenDudeSeqIdleL:
case kGenDudeSeqDeathDefault: case kGenDudeSeqDeathDefault:
case kGenDudeSeqAttackNormalL:
case kGenDudeSeqAttackThrow:
case kGenDudeSeqAttackPunch:
if (!gSysRes.Lookup(i, "SEQ")) { if (!gSysRes.Lookup(i, "SEQ")) {
pXSprite->data2 = dudeInfo[pSprite->type - kDudeBase].seqStartID; pXSprite->data2 = dudeInfo[pSprite->type - kDudeBase].seqStartID;
viewSetSystemMessage("No SEQ animation id %d found for custom dude #%d!", i, pSprite->index); viewSetSystemMessage("No SEQ animation id %d found for custom dude #%d!", i, pSprite->index);
viewSetSystemMessage("SEQ base id: %d", seqStartId); viewSetSystemMessage("SEQ base id: %d", seqStartId);
} else if ((i - seqStartId) == kGenDudeSeqAttackPunch) {
pExtra->forcePunch = true; // required for those who don't have fire trigger in punch seq and for default animation
Seq* pSeq = NULL; DICTNODE* hSeq = gSysRes.Lookup(i, "SEQ");
pSeq = (Seq*)gSysRes.Load(hSeq);
for (int i = 0; i < pSeq->nFrames; i++) {
if (!pSeq->frames[i].at5_5) continue;
pExtra->forcePunch = false;
break;
}
} }
break; break;
case kGenDudeSeqDeathExplode: case kGenDudeSeqDeathExplode:
if (gSysRes.Lookup(i, "SEQ")) pExtra->availDeaths[kDmgExplode] = 1; pExtra->availDeaths[kDmgExplode] = (bool)gSysRes.Lookup(i, "SEQ");
else pExtra->availDeaths[kDmgExplode] = 0;
break;
case kGenDudeSeqAttackNormalL:
pExtra->canAttack = (gSysRes.Lookup(i, "SEQ") && pExtra->curWeapon > 0);
break; break;
case kGenDudeSeqBurning: case kGenDudeSeqBurning:
pExtra->canBurn = gSysRes.Lookup(i, "SEQ"); pExtra->canBurn = gSysRes.Lookup(i, "SEQ");
@ -1636,19 +1916,6 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
} }
break; break;
} }
case kGenDudeSeqAttackPunch: {
pExtra->forcePunch = true; // required for those who don't have fire trigger in punch seq and for default animation
Seq* pSeq = NULL; DICTNODE* hSeq = gSysRes.Lookup(i, "SEQ");
if (hSeq != NULL) {
pSeq = (Seq*)gSysRes.Load(hSeq);
for (int i = 0; i < pSeq->nFrames; i++) {
if (!pSeq->frames[i].at5_5) continue;
pExtra->forcePunch = false;
break;
}
}
break;
}
case kGenDudeSeqAttackNormalDW: case kGenDudeSeqAttackNormalDW:
pExtra->canDuck = (gSysRes.Lookup(i, "SEQ") && gSysRes.Lookup(seqStartId + 14, "SEQ")); pExtra->canDuck = (gSysRes.Lookup(i, "SEQ") && gSysRes.Lookup(seqStartId + 14, "SEQ"));
pExtra->canSwim = (gSysRes.Lookup(i, "SEQ") && gSysRes.Lookup(seqStartId + 13, "SEQ") pExtra->canSwim = (gSysRes.Lookup(i, "SEQ") && gSysRes.Lookup(seqStartId + 13, "SEQ")
@ -1682,9 +1949,9 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
} }
} }
if (propId) break; if (propId) break;
fallthrough__;
} }
case kGenDudePropertyLeech: case kGenDudePropertyLeech:
//viewSetSystemMessage("PROPERTY: LEECH");
pExtra->nLifeLeech = -1; pExtra->nLifeLeech = -1;
if (pSprite->owner != kMaxSprites - 1) { if (pSprite->owner != kMaxSprites - 1) {
for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
@ -1695,8 +1962,9 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
} }
} }
if (propId) break; if (propId) break;
fallthrough__;
case kGenDudePropertySlaves: case kGenDudePropertySlaves:
//viewSetSystemMessage("PROPERTY: SLAVES");
pExtra->slaveCount = 0; memset(pExtra->slave, -1, sizeof(pExtra->slave)); pExtra->slaveCount = 0; memset(pExtra->slave, -1, sizeof(pExtra->slave));
for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
if (sprite[nSprite].owner != pSprite->xvel) continue; if (sprite[nSprite].owner != pSprite->xvel) continue;
@ -1710,6 +1978,8 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
break; break;
} }
if (propId) break; if (propId) break;
fallthrough__;
case kGenDudePropertySpriteSize: { case kGenDudePropertySpriteSize: {
if (seqGetStatus(3, pSprite->extra) == -1) if (seqGetStatus(3, pSprite->extra) == -1)
seqSpawn(pXSprite->data2 + pXSprite->aiState->seqId, 3, pSprite->extra, -1); seqSpawn(pXSprite->data2 + pXSprite->aiState->seqId, 3, pSprite->extra, -1);
@ -1727,6 +1997,7 @@ bool genDudePrepare(spritetype* pSprite, int propId) {
else pSprite->clipdist = pExtra->initVals[2]; else pSprite->clipdist = pExtra->initVals[2];
if (propId) break; if (propId) break;
fallthrough__;
} }
} }

View file

@ -31,6 +31,9 @@ BEGIN_BLD_NS
#define kGenDudeTransformStatus -222 #define kGenDudeTransformStatus -222
#define kGenDudeUpdTimeRate 10 #define kGenDudeUpdTimeRate 10
#define kGenDudeMaxMeleeDist 2048 #define kGenDudeMaxMeleeDist 2048
#define kGenDudeMinDispesion 200
#define kGenDudeMaxDispersion 3500
#define kGenDudeKlabsAng 56
enum { enum {
kGenDudeSeqIdleL = 0, kGenDudeSeqIdleL = 0,
@ -90,6 +93,16 @@ kGenDudePropertyInitVals = 9,
kGenDudePropertyMax , kGenDudePropertyMax ,
}; };
enum {
kGenDudeWeaponNone = -1,
kGenDudeWeaponHitscan = 0,
kGenDudeWeaponMissile = 1,
kGenDudeWeaponThrow = 2,
kGenDudeWeaponSummon = 3,
kGenDudeWeaponKamikaze = 4,
kGenDudeWeaponMax ,
};
extern AISTATE genDudeIdleL; extern AISTATE genDudeIdleL;
extern AISTATE genDudeIdleW; extern AISTATE genDudeIdleW;
extern AISTATE genDudeSearchL; extern AISTATE genDudeSearchL;
@ -102,6 +115,9 @@ extern AISTATE genDudeDodgeW;
extern AISTATE genDudeDodgeShortL; extern AISTATE genDudeDodgeShortL;
extern AISTATE genDudeDodgeShortD; extern AISTATE genDudeDodgeShortD;
extern AISTATE genDudeDodgeShortW; extern AISTATE genDudeDodgeShortW;
extern AISTATE genDudeDodgeShorterL;
extern AISTATE genDudeDodgeShorterD;
extern AISTATE genDudeDodgeShorterW;
extern AISTATE genDudeChaseL; extern AISTATE genDudeChaseL;
extern AISTATE genDudeChaseD; extern AISTATE genDudeChaseD;
extern AISTATE genDudeChaseW; extern AISTATE genDudeChaseW;
@ -134,20 +150,21 @@ extern GENDUDESND gCustomDudeSnd[];
// temporary, until normal DUDEEXTRA gets refactored // temporary, until normal DUDEEXTRA gets refactored
struct GENDUDEEXTRA { struct GENDUDEEXTRA {
unsigned short initVals[3]; // xrepeat, yrepeat, clipdist
unsigned short availDeaths[kDamageMax]; // list of seqs with deaths for each damage type
unsigned short dmgControl[kDamageMax]; // depends of current weapon, drop armor item, sprite yrepeat and surface type
unsigned int moveSpeed;
unsigned int fireDist; // counts from sprite size unsigned int fireDist; // counts from sprite size
unsigned int throwDist; // 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 curWeapon; // data1 duplicate to avoid potential problems when changing data dynamically
unsigned short weaponType;
unsigned short baseDispersion; unsigned short baseDispersion;
unsigned short slaveCount; // how many dudes is summoned
signed short nLifeLeech; // spritenum of dropped dude's leech signed short nLifeLeech; // spritenum of dropped dude's leech
short slaveCount; signed short slave[kGenDudeMaxSlaves]; // index of the ones dude is summon
short slave[kGenDudeMaxSlaves]; // index of the ones dude is summon
short dmgControl[kDamageMax]; // depends of current weapon, drop armor item, sprite yrepeat and surface type
short availDeaths[kDamageMax]; // list of seqs with deaths for each damage type
short initVals[3]; // xrepeat, yrepeat, clipdist
bool forcePunch; // indicate if there is no fire trigger in punch state seq
bool updReq[kGenDudePropertyMax]; // update requests bool updReq[kGenDudePropertyMax]; // update requests
bool sndPlaying; // indicate if sound of AISTATE currently playing bool sndPlaying; // indicate if sound of AISTATE currently playing
bool forcePunch; // indicate if there is no fire trigger in punch state seq
bool isMelee; bool isMelee;
bool canBurn; // can turn in Burning dude or not bool canBurn; // can turn in Burning dude or not
bool canElectrocute; bool canElectrocute;
@ -171,12 +188,11 @@ void removeLeech(spritetype* pLeech, bool delSprite = true);
void removeDudeStuff(spritetype* pSprite); void removeDudeStuff(spritetype* pSprite);
spritetype* leechIsDropped(spritetype* pSprite); spritetype* leechIsDropped(spritetype* pSprite);
bool spriteIsUnderwater(spritetype* pSprite, bool oldWay = false); bool spriteIsUnderwater(spritetype* pSprite, bool oldWay = false);
bool playGenDudeSound(spritetype* pSprite, int mode, bool forceInterrupt = false); bool playGenDudeSound(spritetype* pSprite, int mode);
void aiGenDudeMoveForward(spritetype* pSprite, XSPRITE* pXSprite); void aiGenDudeMoveForward(spritetype* pSprite, XSPRITE* pXSprite);
void aiGenDudeChooseDirection(spritetype* pSprite, XSPRITE* pXSprite, int a3, int aXvel = -1, int aYvel = -1); void aiGenDudeChooseDirection(spritetype* pSprite, XSPRITE* pXSprite, int a3, int aXvel = -1, int aYvel = -1);
void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState); void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState);
int getGenDudeMoveSpeed(spritetype* pSprite, int which, bool mul, bool shift); int getGenDudeMoveSpeed(spritetype* pSprite, int which, bool mul, bool shift);
bool TargetNearThing(spritetype* pSprite, int thingType);
int checkAttackState(spritetype* pSprite, XSPRITE* pXSprite); int checkAttackState(spritetype* pSprite, XSPRITE* pXSprite);
bool doExplosion(spritetype* pSprite, int nType); bool doExplosion(spritetype* pSprite, int nType);
void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT a3); void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT a3);
@ -188,11 +204,12 @@ void updateTargetOfLeech(spritetype* pSprite);
bool canSwim(spritetype* pSprite); bool canSwim(spritetype* pSprite);
bool canDuck(spritetype* pSprite); bool canDuck(spritetype* pSprite);
bool canWalk(spritetype* pSprite); bool canWalk(spritetype* pSprite);
bool inDodge(AISTATE* aiState); short inDodge(AISTATE* aiState);
bool inIdle(AISTATE* aiState); bool inIdle(AISTATE* aiState);
bool inAttack(AISTATE* aiState); bool inAttack(AISTATE* aiState);
short inRecoil(AISTATE* aiState); short inRecoil(AISTATE* aiState);
short inSearch(AISTATE* aiState); short inSearch(AISTATE* aiState);
short inChase(AISTATE* aiState);
short inDuck(AISTATE* aiState); short inDuck(AISTATE* aiState);
int genDudeSeqStartId(XSPRITE* pXSprite); int genDudeSeqStartId(XSPRITE* pXSprite);
int getRangeAttackDist(spritetype* pSprite, int minDist = 1200, int maxDist = 80000); int getRangeAttackDist(spritetype* pSprite, int minDist = 1200, int maxDist = 80000);
@ -201,5 +218,5 @@ void scaleDamage(XSPRITE* pXSprite);
bool genDudePrepare(spritetype* pSprite, int propId = kGenDudePropertyAll); bool genDudePrepare(spritetype* pSprite, int propId = kGenDudePropertyAll);
void genDudeUpdate(spritetype* pSprite); void genDudeUpdate(spritetype* pSprite);
void genDudeProcess(spritetype* pSprite, XSPRITE* pXSprite); void genDudeProcess(spritetype* pSprite, XSPRITE* pXSprite);
bool genDudeAdjustSlope(spritetype* pSprite, XSPRITE* pXSprite, int dist, int weaponType, int by = 64);
END_BLD_NS END_BLD_NS

View file

@ -135,7 +135,8 @@ void ambInit(void)
auto snd = soundEngine->FindSoundByResID(nSFX); auto snd = soundEngine->FindSoundByResID(nSFX);
if (!snd) { if (!snd) {
//ThrowError("Missing sound #%d used in ambient sound generator %d\n", nSFX); //ThrowError("Missing sound #%d used in ambient sound generator %d\n", nSFX);
viewSetSystemMessage("Missing sound #%d used in ambient sound generator #%d\n", nSFX); viewSetSystemMessage("Missing sound #%d used in ambient sound generator #%d\n", nSFX, nSprite);
actPostSprite(nSprite, kStatDecoration);
continue; continue;
} }

View file

@ -605,10 +605,6 @@ void StartLevel(GAMEOPTIONS *gameOptions)
pSprite->type = kSpriteDecoration; pSprite->type = kSpriteDecoration;
changespritestat(pSprite->index, kStatDecoration); changespritestat(pSprite->index, kStatDecoration);
break; break;
case kModernConcussSprite:
pSprite->type = kSpriteDecoration;
changespritestat(pSprite->index, kStatDecoration);
break;
// also erase some modernized vanilla types which was not active // also erase some modernized vanilla types which was not active
case kMarkerWarpDest: case kMarkerWarpDest:
if (pSprite->statnum != kStatMarker) pSprite->type = kSpriteDecoration; if (pSprite->statnum != kStatMarker) pSprite->type = kSpriteDecoration;

View file

@ -346,7 +346,9 @@ void CounterCheck(int nSector) // 12
// By NoOne: remove check below, so every sector can be counter if command 12 (this callback) received. // By NoOne: remove check below, so every sector can be counter if command 12 (this callback) received.
//if (pSector->type != kSectorCounter) return; //if (pSector->type != kSectorCounter) return;
if (sector[nSector].extra <= 0) return; XSECTOR *pXSector = &xsector[sector[nSector].extra]; if (sector[nSector].extra <= 0) return;
XSECTOR *pXSector = &xsector[sector[nSector].extra];
int nReq = pXSector->waitTimeA; int nType = pXSector->data; int nCount = 0; int nReq = pXSector->waitTimeA; int nType = pXSector->data; int nCount = 0;
if (!nType || !nReq) return; if (!nType || !nReq) return;

View file

@ -88,6 +88,8 @@ void _consoleSysMsg(const char* pMessage, ...);
#define kMaxPAL 5 #define kMaxPAL 5
#define kFreeQAVEntry 108 #define kFreeQAVEntry 108
#define kUserPLUStart 15
#define kDmgFall 0 #define kDmgFall 0
#define kDmgBurn 1 #define kDmgBurn 1
#define kDmgBullet 2 #define kDmgBullet 2
@ -197,7 +199,6 @@ enum {
kModernObjDataAccumulator = 37, kModernObjDataAccumulator = 37,
kModernEffectSpawner = 38, kModernEffectSpawner = 38,
kModernWindGenerator = 39, kModernWindGenerator = 39,
kModernConcussSprite = 712, /// WIP
kModernPlayerControl = 500, /// WIP kModernPlayerControl = 500, /// WIP
// decorations // decorations

View file

@ -44,10 +44,6 @@ inline bool xwallRangeIsFine(int nXindex) {
return (nXindex >= 0 && nXindex < kMaxXWalls); return (nXindex >= 0 && nXindex < kMaxXWalls);
} }
inline bool xspriIsFine(int nIndex) {
return (nIndex >= 0 && nIndex < kMaxSprites && !(sprite[nIndex].flags & 32) && sprite[nIndex].statnum != kStatFree);
}
extern bool gModernMap; extern bool gModernMap;
#pragma pack(push, 1) #pragma pack(push, 1)

View file

@ -1546,30 +1546,30 @@ DUDEINFO dudeInfo[kDudeMax-kDudeBase] =
}, },
//254 - kDudeModernCustom //254 - kDudeModernCustom
{ {
11520, // start sequence ID 11520, // start sequence ID
85, // start health 85, // start health
75, // mass 75, // mass
120, 120,
48, // clip distance 48, // clip distance
48, // eye above z 48, // eye above z
20, 20,
10240, // hear distance 10240, // hear distance
51200, // seeing distance 51200, // seeing distance
kAng120, // vision periphery kAng120, // vision periphery
// 0, // 0,
618, // melee distance 618, // melee distance
5, // flee health 5, // flee health
5, // hinder damage 5, // hinder damage
0x0100, // change target chance 0x0100, // change target chance
0x0010, // change target to kin chance 0x0010, // change target to kin chance
0x8000, // alertChance 0x8000, // alertChance
0, // lockout 0, // lockout
46603, // frontSpeed 46603, // frontSpeed
34952, // sideSpeed 34952, // sideSpeed
13981, // backSpeed 13981, // backSpeed
256, // angSpeed 256, // angSpeed
// 0, // 0,
7, -1, 18, // nGibType 7, -1, 18, // nGibType
128, 150, 128, 256, 128, 128, 128, 128, 150, 128, 256, 128, 128, 128,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,

View file

@ -356,12 +356,14 @@ 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; EVENT event; event.index = nIndex; event.type = nType; event.cmd = command; event.causedBy = causedBy;
switch (command) { switch (command) {
case kCmdState: case kCmdState:
command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff;
break; break;
case kCmdNotState: case kCmdNotState:
command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn;
break; break;
default:
break;
} }
switch (rxId) { switch (rxId) {

View file

@ -908,4 +908,12 @@ int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pSector
return n; return n;
} }
int picWidth(short nPic, short repeat) {
return ClipLow((tilesiz[nPic].y * repeat) >> 2, 0);
}
int picHeight(short nPic, short repeat) {
return ClipLow((tilesiz[nPic].y * repeat) << 2, 0);
}
END_BLD_NS END_BLD_NS

View file

@ -83,5 +83,7 @@ int GetDistToLine(int x1, int y1, int x2, int y2, int x3, int y3);
unsigned int ClipMove(int *x, int *y, int *z, int *nSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask); unsigned int ClipMove(int *x, int *y, int *z, int *nSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask);
int GetClosestSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit); int GetClosestSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit);
int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit, short *a8); int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit, short *a8);
int picWidth(short nPic, short repeat);
int picHeight(short nPic, short repeat);
END_BLD_NS END_BLD_NS

View file

@ -1113,6 +1113,18 @@ void playerStart(int nPlayer)
pPlayer->posture = 1; pPlayer->posture = 1;
pPlayer->pXSprite->medium = kMediumWater; pPlayer->pXSprite->medium = kMediumWater;
} }
/////////////////
// reset qav scene
playerResetQavScene(pPlayer);
// restore default movement speed
playerResetMoveSpeed(pPlayer);
// restore default jump height
playerResetJumpHeight(pPlayer);
/////////////////
} }
void playerReset(PLAYER *pPlayer) void playerReset(PLAYER *pPlayer)
@ -1160,9 +1172,7 @@ void playerReset(PLAYER *pPlayer)
///////////////// /////////////////
// reset qav scene // reset qav scene
QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; playerResetQavScene(pPlayer);
pQavScene->index = pQavScene->causedBy = pPlayer->sceneQav = -1;
pQavScene->qavResrc = NULL;
// restore default movement speed // restore default movement speed
playerResetMoveSpeed(pPlayer); playerResetMoveSpeed(pPlayer);
@ -1188,6 +1198,12 @@ void playerResetJumpHeight(PLAYER* pPlayer) {
} }
} }
void playerResetQavScene(PLAYER* pPlayer) {
QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene;
pQavScene->index = pQavScene->causedBy = pPlayer->sceneQav = -1;
pQavScene->qavResrc = NULL;
}
int dword_21EFB0[8]; int dword_21EFB0[8];
ClockTicks dword_21EFD0[8]; ClockTicks dword_21EFD0[8];

View file

@ -332,5 +332,6 @@ void qavScenePlay(PLAYER* pPlayer);
void qavSceneDraw(PLAYER* pPlayer, int a2, int a3, int a4, int a5); void qavSceneDraw(PLAYER* pPlayer, int a2, int a3, int a4, int a5);
void playerResetMoveSpeed(PLAYER* pPlayer); void playerResetMoveSpeed(PLAYER* pPlayer);
void playerResetJumpHeight(PLAYER* pPlayer); void playerResetJumpHeight(PLAYER* pPlayer);
void playerResetQavScene(PLAYER* pPlayer);
END_BLD_NS END_BLD_NS

View file

@ -104,8 +104,9 @@ void scrLoadPLUs(void)
parallaxvisibility = 3072; parallaxvisibility = 3072;
return; return;
} }
for (int i = 0; i < 15; i++)
{ // load default palookups
for (int i = 0; i < 15; i++) {
DICTNODE *pPlu = gSysRes.Lookup(PLU[i].name, "PLU"); DICTNODE *pPlu = gSysRes.Lookup(PLU[i].name, "PLU");
if (!pPlu) if (!pPlu)
ThrowError("%s.PLU not found", PLU[i].name); ThrowError("%s.PLU not found", PLU[i].name);
@ -113,6 +114,15 @@ void scrLoadPLUs(void)
ThrowError("Incorrect PLU size"); ThrowError("Incorrect PLU size");
palookup[PLU[i].id] = (char*)gSysRes.Lock(pPlu); palookup[PLU[i].id] = (char*)gSysRes.Lock(pPlu);
} }
// by NoOne: load user palookups
for (int i = kUserPLUStart; i < MAXPALOOKUPS; i++) {
DICTNODE* pPlu = gSysRes.Lookup(i, "PLU");
if (!pPlu) continue;
else if (pPlu->size / 256 != 64) { consoleSysMsg("Incorrect filesize of PLU#%d", i); }
else palookup[i] = (char*)gSysRes.Lock(pPlu);
}
#ifdef USE_OPENGL #ifdef USE_OPENGL
palookupfog[1].r = 255; palookupfog[1].r = 255;
palookupfog[1].g = 255; palookupfog[1].g = 255;

View file

@ -388,10 +388,8 @@ void seqSpawn(int a1, int a2, int a3, int a4)
if (!pInst) return; if (!pInst) return;
DICTNODE *hSeq = gSysRes.Lookup(a1, "SEQ"); DICTNODE *hSeq = gSysRes.Lookup(a1, "SEQ");
if (!hSeq) { if (!hSeq)
viewSetSystemMessage("Missing sequence #%d", a1); ThrowError("Missing sequence #%d", a1);
return;
}
int i = activeCount; int i = activeCount;
if (pInst->at13) if (pInst->at13)
@ -588,7 +586,7 @@ void SeqLoadSave::Load(void)
int nSeq = pInst->at8; int nSeq = pInst->at8;
DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ"); DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ");
if (!hSeq) { if (!hSeq) {
viewSetSystemMessage("Missing sequence #%d", nSeq); ThrowError("Missing sequence #%d", nSeq);
continue; continue;
} }
Seq *pSeq = (Seq*)gSysRes.Lock(hSeq); Seq *pSeq = (Seq*)gSysRes.Lock(hSeq);

View file

@ -69,6 +69,18 @@ int nUniMissileTrapClient = seqRegisterClient(UniMissileTrapSeqCallback);
int nMGunFireClient = seqRegisterClient(MGunFireSeqCallback); int nMGunFireClient = seqRegisterClient(MGunFireSeqCallback);
int nMGunOpenClient = seqRegisterClient(MGunOpenSeqCallback); int nMGunOpenClient = seqRegisterClient(MGunOpenSeqCallback);
int gRdata[4];
int* xspriData2Array(int nXSprite) {
if (xspriRangeIsFine(nXSprite)) {
gRdata[0] = xsprite[nXSprite].data1; gRdata[2] = xsprite[nXSprite].data3;
gRdata[1] = xsprite[nXSprite].data2; gRdata[3] = xsprite[nXSprite].data4;
return gRdata;
}
memset(gRdata, 0, sizeof(gRdata));
return NULL;
}
unsigned int GetWaveValue(unsigned int nPhase, int nType) unsigned int GetWaveValue(unsigned int nPhase, int nType)
{ {
switch (nType) switch (nType)
@ -368,9 +380,6 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
{ {
spritetype *pSprite = &sprite[nSprite]; spritetype *pSprite = &sprite[nSprite];
//if (pSprite->type != 706 && pSprite->type != 707)
//viewSetSystemMessage("SPRITE %d (TYPE %d), EVENT INITED BY: %d", nSprite, pSprite->type, event.causedBy);
if (gModernMap) { if (gModernMap) {
switch (event.cmd) { switch (event.cmd) {
case kCmdUnlock: case kCmdUnlock:
@ -441,39 +450,37 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
// Random Event Switch takes random data field and uses it as TX ID // Random Event Switch takes random data field and uses it as TX ID
case kModernRandomTX: { case kModernRandomTX: {
std::default_random_engine rng; int tx = 0; int maxRetries = 10; int tx = 0; int maxRetries = 10;
// set range of TX ID if data2 and data3 is empty. // set range of TX ID if data2 and data3 is empty.
if (pXSprite->data1 > 0 && pXSprite->data2 <= 0 && pXSprite->data3 <= 0 && pXSprite->data4 > 0) { if (pXSprite->data1 > 0 && pXSprite->data2 <= 0 && pXSprite->data3 <= 0 && pXSprite->data4 > 0) {
// data1 must be less than data4 // data1 must be less than data4
if (pXSprite->data1 > pXSprite->data4) { if (pXSprite->data1 > pXSprite->data4) {
short tmp = pXSprite->data1; short tmp = pXSprite->data1;
pXSprite->data1 = (short)pXSprite->data4; pXSprite->data1 = pXSprite->data4;
pXSprite->data4 = tmp; pXSprite->data4 = tmp;
} }
int total = pXSprite->data4 - pXSprite->data1; int total = pXSprite->data4 - pXSprite->data1;
while (maxRetries > 0) { while (maxRetries > 0) {
// use true random only for single player mode, otherwise use Blood's default one.
// use true random only for single player mode if (gGameOptions.nGameType == 0 && !VanillaMode() && !DemoRecordStatus()) tx = STD_Random(pXSprite->data1, pXSprite->data4);
// otherwise use Blood's default one. In the future it maybe possible to make else tx = Random(total) + pXSprite->data1;
// host send info to clients about what was generated.
if (gGameOptions.nGameType != 0 || VanillaMode() || DemoRecordStatus()) tx = Random(total) + pXSprite->data1;
else {
rng.seed(std::random_device()());
tx = (int)my_random(pXSprite->data1, pXSprite->data4);
}
if (tx != pXSprite->txID) break; if (tx != pXSprite->txID) break;
maxRetries--; maxRetries--;
} }
} else { } else {
while (maxRetries > 0) {
if ((tx = GetRandDataVal(NULL, pSprite)) > 0 && tx != pXSprite->txID) break; int* rData = xspriData2Array(pSprite->extra);
maxRetries--; if (rData != NULL) {
while (maxRetries > 0) {
if ((tx = GetRandDataVal(rData)) > 0 && tx != pXSprite->txID) break;
maxRetries--;
}
} }
} }
if (tx > 0) { if (tx > 0) {
@ -628,7 +635,6 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
case kModernObjPicnumChanger: case kModernObjPicnumChanger:
case kModernSectorFXChanger: case kModernSectorFXChanger:
case kModernObjDataChanger: case kModernObjDataChanger:
case kModernConcussSprite:
modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy); modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy);
return; return;
@ -871,7 +877,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
else if (pXSprite->command < kCmdNumberic + 3 && pXSprite->command > kCmdNumberic + 4 else if (pXSprite->command < kCmdNumberic + 3 && pXSprite->command > kCmdNumberic + 4
&& !modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy)) return; && !modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy)) return;
TRPLAYERCTRL* pCtrl = pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer];
if (event.cmd >= kCmdNumberic) { if (event.cmd >= kCmdNumberic) {
switch (event.cmd) { switch (event.cmd) {
case kCmdNumberic + 3:// start playing qav scene case kCmdNumberic + 3:// start playing qav scene
@ -1017,44 +1023,48 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
switch (pXSprite->data2) { switch (pXSprite->data2) {
// erase all // erase all
case 0: case 0:
fallthrough__;
// erase weapons // erase weapons
case 1: case 1:
// erase all // erase all
if (pXSprite->data3 <= 0) { WeaponLower(pPlayer);
WeaponLower(pPlayer);
for (int i = 0; i < 14; i++) {
pPlayer->hasWeapon[i] = false;
// also erase ammo
if (i < 12) pPlayer->ammoCount[i] = 0;
}
pPlayer->hasWeapon[1] = true;
pPlayer->curWeapon = 0;
pPlayer->nextWeapon = 1;
WeaponRaise(pPlayer);
// erase just specified
} else {
for (int i = 0; i < 14; i++) {
pPlayer->hasWeapon[i] = false;
// also erase ammo
if (i < 12) pPlayer->ammoCount[i] = 0;
} }
pPlayer->hasWeapon[1] = true;
pPlayer->curWeapon = 0;
pPlayer->nextWeapon = 1;
WeaponRaise(pPlayer);
if (pXSprite->data2) break; if (pXSprite->data2) break;
fallthrough__;
// erase all armor // erase all armor
case 2: case 2:
for (int i = 0; i < 3; i++) pPlayer->armor[i] = 0; for (int i = 0; i < 3; i++) pPlayer->armor[i] = 0;
if (pXSprite->data2) break; if (pXSprite->data2) break;
fallthrough__;
// erase all pack items // erase all pack items
case 3: case 3:
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
pPlayer->packSlots[i].isActive = pPlayer->packSlots[i].curAmount = 0; pPlayer->packSlots[i].isActive = false;
pPlayer->packSlots[i].curAmount = 0;
pPlayer->packItemId = -1; pPlayer->packItemId = -1;
} }
if (pXSprite->data2) break; if (pXSprite->data2) break;
fallthrough__;
// erase all keys // erase all keys
case 4: case 4:
for (int i = 0; i < 8; i++) pPlayer->hasKey[i] = false; for (int i = 0; i < 8; i++) pPlayer->hasKey[i] = false;
if (pXSprite->data2) break; if (pXSprite->data2) break;
fallthrough__;
} }
break; break;
@ -1094,11 +1104,14 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
case kCmdNumberic + 8: // 72 case kCmdNumberic + 8: // 72
// use inventory item // use inventory item
if (pXSprite->data2 > 0 && pXSprite->data2 <= 5) { if (pXSprite->data2 > 0 && pXSprite->data2 <= 5) {
packUseItem(pPlayer, pXSprite->data2 - 1); unsigned int invItem = pXSprite->data2 - 1;
packUseItem(pPlayer, invItem);
// force remove after use // force remove after use
if (pXSprite->data4 == 1) if (pXSprite->data4 == 1) {
pPlayer->packSlots[0].curAmount = pPlayer->packSlots[0].curAmount = 0; pPlayer->packSlots[invItem].isActive = false;
pPlayer->packSlots[invItem].curAmount = 0;
}
} }
break; break;
@ -1290,8 +1303,12 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
pXSpawn->target = -1; pXSpawn->target = -1;
aiActivateDude(pSpawn, pXSpawn); aiActivateDude(pSpawn, pXSpawn);
break; break;
default:
if (gModernMap && (pSprite->flags & kModernTypeFlag3)) aiActivateDude(pSpawn, pXSpawn);
break;
} }
} }
} }
} }
break; break;
@ -1350,6 +1367,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
sfxPlay3DSound(pSprite, 452, 0, 0); sfxPlay3DSound(pSprite, 452, 0, 0);
evPost(nSprite, 3, 30, kCmdOff, event.causedBy); evPost(nSprite, 3, 30, kCmdOff, event.causedBy);
pXSprite->state = 1; pXSprite->state = 1;
fallthrough__;
case kCmdOn: case kCmdOn:
sfxPlay3DSound(pSprite, 451, 0, 0); sfxPlay3DSound(pSprite, 451, 0, 0);
pXSprite->Proximity = 1; pXSprite->Proximity = 1;
@ -1451,19 +1469,6 @@ void stopWindOnSectors(XSPRITE* pXSource) {
pXSector->windVel = 0; pXSector->windVel = 0;
} }
} }
/// WIP ////////////////////////////////////////////////////////
void useConcussSprite(XSPRITE* pXSource, spritetype* pSprite) {
spritetype* pSource = &sprite[pXSource->reference];
int nIndex = isDebris(pSprite->index);
//ThrowError("%d", gPhysSpritesList[nIndex]);
//int size = (tilesiz[pSprite->picnum].x * pSprite->xrepeat * tilesiz[pSprite->picnum].y * pSprite->yrepeat) >> 1;
//int t = scale(pXSource->data1, size, gSpriteMass[pSprite->extra].mass);
//xvel[pSprite->xvel] += mulscale16(t, pSprite->x);
//yvel[pSprite->xvel] += mulscale16(t, pSprite->y);
//zvel[pSprite->xvel] += mulscale16(t, pSprite->z);
//debrisConcuss(pXSource->reference, nIndex, pSprite->x - 100, pSprite->y - 100, pSprite->z - 100, pXSource->data1);
}
void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy) { void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy) {
@ -1501,7 +1506,7 @@ void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy) {
void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer) { void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer) {
TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer];
viewSetSystemMessage("OFF %d", pCtrl->qavScene.index); //viewSetSystemMessage("OFF %d", pCtrl->qavScene.index);
pXSource->sysData1 = 0; pXSource->sysData1 = 0;
pCtrl->qavScene.index = -1; pCtrl->qavScene.index = -1;
@ -2029,22 +2034,29 @@ void useSectorWindGen(XSPRITE* pXSource, sectortype* pSector) {
void useSpriteDamager(XSPRITE* pXSource, spritetype* pSprite) { void useSpriteDamager(XSPRITE* pXSource, spritetype* pSprite) {
spritetype* pSource = &sprite[pXSource->reference]; spritetype* pSource = &sprite[pXSource->reference];
if (pSprite != NULL && xspriIsFine(pSprite->index)) { if (pSprite != NULL && xspriRangeIsFine(pSprite->extra)) {
int dmg = (pXSource->data3 == 0) ? 65535 : ClipRange(pXSource->data3, 1, 65535); XSPRITE* pXSprite = &xsprite[pSprite->extra];
int dmgType = ClipRange(pXSource->data2, 0, 6); DAMAGE_TYPE dmgType = (DAMAGE_TYPE) ClipRange(pXSource->data2, kDmgFall, kDmgElectric);
int dmg = (pXSource->data3 == 0) ? 65535 : ClipRange(pXSource->data3 << 1, 1, 65535);
if (pXSource->data2 == -1 && IsDudeSprite(pSprite)) { if (pXSprite->data2 >= 0) actDamageSprite(pSource->index, pSprite, dmgType, dmg);
xsprite[pSprite->extra].health = ClipLow(xsprite[pSprite->extra].health - dmg, 0); else if (pXSource->data2 == -1 && IsDudeSprite(pSprite)) {
if (xsprite[pSprite->extra].health == 0) PLAYER* pPlayer = getPlayerById(pSprite->type);
actKillDude(pSource->index, pSprite, (DAMAGE_TYPE)0, 65535); if (pPlayer == NULL || !pPlayer->godMode) {
xsprite[pSprite->extra].health = ClipLow(xsprite[pSprite->extra].health - dmg, 0);
if (xsprite[pSprite->extra].health == 0)
actKillDude(pSource->index, pSprite, dmgType, 65535);
}
} }
else actDamageSprite(pSource->index, pSprite, (DAMAGE_TYPE)dmgType, dmg);
} }
} }
void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index) { void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index) {
if (pXSource->data2 > 0 && !gSysRes.Lookup(pXSource->data2, "SEQ")) {
consoleSysMsg("Missing sequence #%d",pXSource->data2);
return;
}
switch (objType) { switch (objType) {
case 6: case 6:
if (pXSource->data2 <= 0) { if (pXSource->data2 <= 0) {
@ -2089,7 +2101,7 @@ void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index) {
if (pXSource->data4 > 0) { if (pXSource->data4 > 0) {
int cx, cy, cz, wx, wy, wz; int cx, cy, cz;
cx = (wall[index].x + wall[wall[index].point2].x) >> 1; cx = (wall[index].x + wall[wall[index].point2].x) >> 1;
cy = (wall[index].y + wall[wall[index].point2].y) >> 1; cy = (wall[index].y + wall[wall[index].point2].y) >> 1;
int nSector = sectorofwall(index); int nSector = sectorofwall(index);
@ -2099,9 +2111,6 @@ void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index) {
getzsofslope(wall[index].nextsector, cx, cy, &ceilZ2, &floorZ2); getzsofslope(wall[index].nextsector, cx, cy, &ceilZ2, &floorZ2);
ceilZ = ClipLow(ceilZ, ceilZ2); ceilZ = ClipLow(ceilZ, ceilZ2);
floorZ = ClipHigh(floorZ, floorZ2); floorZ = ClipHigh(floorZ, floorZ2);
wz = floorZ - ceilZ;
wx = wall[wall[index].point2].x - wall[index].x;
wy = wall[wall[index].point2].y - wall[index].y;
cz = (ceilZ + floorZ) >> 1; cz = (ceilZ + floorZ) >> 1;
sfxPlay3DSound(cx, cy, cz, pXSource->data4, nSector); sfxPlay3DSound(cx, cy, cz, pXSource->data4, nSector);
@ -3476,14 +3485,7 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) {
return; return;
} }
if (pSource->type == kModernConcussSprite) { if (pSource->type == kMarkerWarpDest) {
/* - Concussing any physics affected sprite with give strength - */
if (type != 3) return;
else if ((sprite[nDest].flags & kPhysMove) || (sprite[nDest].flags & kPhysGravity) || isDebris(nDest))
useConcussSprite(pXSource, &sprite[nDest]);
return;
} else if (pSource->type == kMarkerWarpDest) {
/* - Allows teleport any sprite from any location to the source destination - */ /* - Allows teleport any sprite from any location to the source destination - */
useTeleportTarget(pXSource, &sprite[nDest]); useTeleportTarget(pXSource, &sprite[nDest]);
return; return;
@ -3503,7 +3505,6 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) {
} }
else if (pSource->type == kModernSeqSpawner) { else if (pSource->type == kModernSeqSpawner) {
/* - SEQ Spawner takes data2 as SEQ ID and spawns it on it's or TX ID sprite - */ /* - SEQ Spawner takes data2 as SEQ ID and spawns it on it's or TX ID sprite - */
if (pXSource->data2 > 0 && !gSysRes.Lookup(pXSource->data2, "SEQ")) return;
useSeqSpawnerGen(pXSource, type, nDest); useSeqSpawnerGen(pXSource, type, nDest);
return; return;
} }
@ -3635,7 +3636,7 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) {
if ((pSource->flags & kModernTypeFlag1) || (pXSource->data3 != -1 && pXSource->data3 != 32767)) if ((pSource->flags & kModernTypeFlag1) || (pXSource->data3 != -1 && pXSource->data3 != 32767))
setDataValueOfObject(type, nDest, 3, pXSource->data3, event.causedBy); setDataValueOfObject(type, nDest, 3, pXSource->data3, event.causedBy);
if ((pSource->flags & kModernTypeFlag1) || (pXSource->data4 != -1 && pXSource->data1 != 65535)) if ((pSource->flags & kModernTypeFlag1) || pXSource->data4 != 65535)
setDataValueOfObject(type, nDest, 4, pXSource->data4, event.causedBy); setDataValueOfObject(type, nDest, 4, pXSource->data4, event.causedBy);
break; break;
@ -4838,7 +4839,7 @@ void ActivateGenerator(int nSprite)
if (pXSprite->dropMsg > 0) { if (pXSprite->dropMsg > 0) {
for (short nItem = headspritestat[kStatItem]; nItem >= 0; nItem = nextspritestat[nItem]) { for (short nItem = headspritestat[kStatItem]; nItem >= 0; nItem = nextspritestat[nItem]) {
spritetype* pItem = &sprite[nItem]; spritetype* pItem = &sprite[nItem];
if (pItem->type == pXSprite->dropMsg && pItem->x == pSprite->x && pItem->y == pSprite->y && pItem->z == pSprite->z) { if ((unsigned int)pItem->type == pXSprite->dropMsg && pItem->x == pSprite->x && pItem->y == pSprite->y && pItem->z == pSprite->z) {
gFX.fxSpawn((FX_ID)29, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0); gFX.fxSpawn((FX_ID)29, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 0);
deletesprite(nItem); deletesprite(nItem);
break; break;

View file

@ -86,6 +86,9 @@ bool valueIsBetween(int val, int min, int max);
void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer); void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer);
void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy); void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy);
void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer); void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer);
extern int gRdata[4];
int* xspriData2Array(int nXSprite);
// ------------------------------------------------------- // -------------------------------------------------------
END_BLD_NS END_BLD_NS