From da47ebbd2c116b529502fc1995094f80037cac25 Mon Sep 17 00:00:00 2001 From: Grind Core Date: Thu, 5 Dec 2019 23:42:35 +0300 Subject: [PATCH] - 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 --- source/blood/src/actor.cpp | 253 +++++++------- source/blood/src/actor.h | 6 +- source/blood/src/ai.cpp | 21 +- source/blood/src/aiunicult.cpp | 587 ++++++++++++++++++++++++--------- source/blood/src/aiunicult.h | 39 ++- source/blood/src/asound.cpp | 3 +- source/blood/src/blood.cpp | 4 - source/blood/src/callback.cpp | 4 +- source/blood/src/common_game.h | 3 +- source/blood/src/db.h | 4 - source/blood/src/dude.cpp | 40 +-- source/blood/src/eventq.cpp | 14 +- source/blood/src/gameutil.cpp | 8 + source/blood/src/gameutil.h | 2 + source/blood/src/player.cpp | 22 +- source/blood/src/player.h | 1 + source/blood/src/screen.cpp | 14 +- source/blood/src/seq.cpp | 8 +- source/blood/src/triggers.cpp | 163 ++++----- source/blood/src/triggers.h | 3 + 20 files changed, 757 insertions(+), 442 deletions(-) diff --git a/source/blood/src/actor.cpp b/source/blood/src/actor.cpp index 1e83a2a4d..310f0aad1 100644 --- a/source/blood/src/actor.cpp +++ b/source/blood/src/actor.cpp @@ -1516,7 +1516,9 @@ MissileType missileInfo[] = { 40, (char)-16, 16, - 1207, 1207 + 1207, 1207, + false, false, false, false, false, true, false + }, // Regular flare { @@ -1527,7 +1529,8 @@ MissileType missileInfo[] = { 32, (char)-128, 32, - 420, 420 + 420, 420, + false, true, true, false, false, false, false }, // Tesla alt { @@ -1538,7 +1541,8 @@ MissileType missileInfo[] = { 32, (char)-128, 32, - 471, 471 + 471, 471, + false, false, false, false, false, false, true }, // Flare alt { @@ -1549,7 +1553,8 @@ MissileType missileInfo[] = { 32, (char)-128, 4, - 421, 421 + 421, 421, + false, true, false, true, false, false, false }, // Spray flame { @@ -1560,7 +1565,8 @@ MissileType missileInfo[] = { 24, (char)-128, 16, - 1309, 351 + 1309, 351, + false, true, false, false, false, false, false }, // Fireball { @@ -1571,7 +1577,8 @@ MissileType missileInfo[] = { 32, (char)-128, 32, - 480, 480 + 480, 480, + false, true, false, true, false, false, false }, // Tesla regular { @@ -1582,7 +1589,8 @@ MissileType missileInfo[] = { 32, (char)-128, 16, - 470, 470 + 470, 470, + false, false, false, false, false, false, true }, // EctoSkull { @@ -1593,7 +1601,8 @@ MissileType missileInfo[] = { 32, (char)-24, 32, - 489, 490 + 489, 490, + false, false, false, false, false, true, false }, // Hellhound flame { @@ -1604,7 +1613,8 @@ MissileType missileInfo[] = { 24, (char)-128, 16, - 462, 351 + 462, 351, + false, true, false, false, false, false, false }, // Puke { @@ -1615,7 +1625,8 @@ MissileType missileInfo[] = { 16, (char)-16, 16, - 1203, 172 + 1203, 172, + false, false, true, false, false, false, false }, // Reserved { @@ -1626,7 +1637,8 @@ MissileType missileInfo[] = { 8, (char)0, 16, - 0,0 + 0,0, + false, false, true, false, false, false, false }, // Stone gargoyle projectile { @@ -1637,7 +1649,8 @@ MissileType missileInfo[] = { 32, (char)-128, 16, - 1457, 249 + 1457, 249, + false, false, false, false, false, true, false }, // Napalm launcher { @@ -1648,7 +1661,8 @@ MissileType missileInfo[] = { 30, (char)-128, 24, - 480, 489 + 480, 489, + false, true, false, true, false, false, false }, // Cerberus fireball { @@ -1659,7 +1673,8 @@ MissileType missileInfo[] = { 30, (char)-128, 24, - 480, 489 + 480, 489, + false, false, false, true, false, false, false }, // Tchernobog fireball { @@ -1670,7 +1685,8 @@ MissileType missileInfo[] = { 24, (char)-128, 16, - 480, 489 + 480, 489, + false, false, false, true, false, false, false }, // Regular life leech { @@ -1681,7 +1697,8 @@ MissileType missileInfo[] = { 32, (char)-128, 16, - 491, 491 + 491, 491, + true, true, true, true, true, true, true }, // Dropped life leech (enough ammo) { @@ -1692,7 +1709,8 @@ MissileType missileInfo[] = { 16, (char)-128, 16, - 520, 520 + 520, 520, + false, false, false, false, false, true, false }, // Dropped life leech (no ammo) { @@ -1703,7 +1721,8 @@ MissileType missileInfo[] = { 32, (char)-128, 16, - 520, 520 + 520, 520, + false, false, false, false, false, true, false } }; @@ -2446,7 +2465,7 @@ struct POSTPONE { POSTPONE gPost[kMaxSprites]; -static char buffer[120]; +//static char buffer[120]; 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) { gGenDudeExtra[pSprite->owner].slave[i] = -1; gGenDudeExtra[pSprite->owner].slaveCount = ClipRange(gGenDudeExtra[pSprite->owner].slaveCount - 1, 0, gGameOptions.nDifficulty + 1); - //viewSetSystemMessage("REMOVING %d FROM %d, COUNT: %d", pSprite->index, sprite[pSprite->owner].type, gGenDudeExtra[pSprite->owner].slaveCount); break; } } @@ -3113,19 +3131,19 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, switch (pSprite->type) { case kDudeModernCustom: { + + GENDUDEEXTRA* pExtra = genDudeExtra(pSprite); removeDudeStuff(pSprite); if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) { - if (pXSprite->data1 >= kTrapExploder && pXSprite->data1 < (kTrapExploder + kExplodeMax) - 1 && - Chance(0x4000) && damageType != 5 && damageType != 4) { - + if (pExtra->weaponType == kGenDudeWeaponKamikaze && Chance(0x4000) && damageType != 5 && damageType != 4) { doExplosion(pSprite, pXSprite->data1 - kTrapExploder); if (Chance(0x9000)) damageType = (DAMAGE_TYPE) 3; } if (damageType == DAMAGE_TYPE_1) { - if ((gSysRes.Lookup(pXSprite->data2 + 15, "SEQ") || gSysRes.Lookup(pXSprite->data2 + 16, "SEQ")) && pXSprite->medium == kMediumNormal) { - if (gSysRes.Lookup(pXSprite->data2 + 3, "SEQ")) { + if (pExtra->availDeaths[DAMAGE_TYPE_1] && !spriteIsUnderwater(pSprite)) { + if (pExtra->canBurn) { pSprite->type = kDudeModernCustomBurning; if (pXSprite->data2 == kGenDudeDefaultSeq) // don't inherit palette for burning if using default animation pSprite->pal = 0; @@ -3158,11 +3176,12 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, pSprite->flags &= ~kPhysMove; xvel[pSprite->index] = yvel[pSprite->index] = 0; - - int seqId = pXSprite->data2 + 18; - if (!gSysRes.Lookup(seqId, "SEQ")) { + + playGenDudeSound(pSprite, kGenDudeSndTransforming); + int seqId = pXSprite->data2 + kGenDudeSeqTransform; + if (gSysRes.Lookup(seqId, "SEQ")) seqSpawn(seqId, 3, nXSprite, -1); + else { seqKill(3, nXSprite); - playGenDudeSound(pSprite, kGenDudeSndTransforming); spritetype* pEffect = gFX.fxSpawn((FX_ID)52, pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, pSprite->ang); if (pEffect != NULL) { pEffect->cstat = CSTAT_SPRITE_ALIGNMENT_FACING; @@ -3170,7 +3189,7 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, pEffect->xrepeat = pSprite->xrepeat; pEffect->yrepeat = pSprite->yrepeat; } - + GIBTYPE nGibType; for (int i = 0; i < 3; i++) { if (Chance(0x3000)) nGibType = GIBTYPE_6; @@ -3183,14 +3202,9 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, CGibVelocity gibVel(xvel[pSprite->index] >> 1, yvel[pSprite->index] >> 1, -0xccccc); GibSprite(pSprite, nGibType, &gibPos, &gibVel); } - - return; } - seqSpawn(seqId, 3, nXSprite, -1); - playGenDudeSound(pSprite, kGenDudeSndTransforming); - + pXSprite->sysData1 = kGenDudeTransformStatus; // in transform - return; } break; @@ -3289,9 +3303,14 @@ void actKillDude(int nKillerSprite, spritetype *pSprite, DAMAGE_TYPE damageType, nSeq = 2; switch (pSprite->type) { case kDudeModernCustom: - case kDudeModernCustomBurning: + case kDudeModernCustomBurning: { playGenDudeSound(pSprite, kGenDudeSndDeathExplode); + GENDUDEEXTRA* pExtra = genDudeExtra(pSprite); + if (!pExtra->availDeaths[damageType]) { + nSeq = 1; damageType = DAMAGE_TYPE_0; + } break; + } case kDudeCultistTommy: case kDudeCultistShotgun: case kDudeCultistTommyProne: @@ -4321,7 +4340,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite) } } - switch (pSprite->type) { + /*switch (pSprite->type) { case kDudeModernCustom: case kDudeModernCustomBurning: { @@ -4338,7 +4357,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite) } break; } - } + }*/ switch (pSprite2->type) { case kThingKickablePail: @@ -4395,7 +4414,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite) } } - switch (pSprite->type) { + /*switch (pSprite->type) { case kDudeModernCustom: case kDudeModernCustomBurning: { @@ -4411,7 +4430,7 @@ void ProcessTouchObjects(spritetype *pSprite, int nXSprite) } break; } - } + }*/ switch (pSprite2->type) { @@ -4848,7 +4867,7 @@ void MoveDude(spritetype *pSprite) ChangeSpriteSect(nSprite, nSector); 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 (sector[nSector].type == kSectorTeleport) @@ -5425,7 +5444,7 @@ void actExplodeSprite(spritetype *pSprite) return; sfxKill3DSound(pSprite, -1, -1); evKill(pSprite->index, 3); - int nType; + int nType = 1; switch (pSprite->type) { case kMissileFireballNapam: @@ -6151,10 +6170,11 @@ void actProcessSprites(void) } // By NoOne: handle incarnations of custom dude - if (pSprite->type == kDudeModernCustom && pXSprite->txID > 0 && pXSprite->health <= 0 && pXSprite->sysData1 == kGenDudeTransformStatus) { - xvel[pSprite->index] = ClipLow(xvel[pSprite->index] >> 4, 0); yvel[pSprite->index] = ClipLow(yvel[pSprite->index] >> 4, 0); + if (pSprite->type == kDudeModernCustom && pXSprite->txID > 0 && pXSprite->sysData1 == kGenDudeTransformStatus) { + xvel[pSprite->index] = yvel[pSprite->index] = 0; + if (seqGetStatus(3, nXSprite) < 0) { XSPRITE* pXIncarnation = getNextIncarnation(pXSprite); - if (seqGetStatus(3, nXSprite) < 0 && pXIncarnation != NULL) { + if (pXIncarnation != NULL) { spritetype* pIncarnation = &sprite[pXIncarnation->reference]; pXSprite->key = pXSprite->dropMsg = pXSprite->locked = 0; @@ -6189,11 +6209,11 @@ void actProcessSprites(void) pXSprite->data1 = pXIncarnation->data1; pXSprite->data2 = pXIncarnation->data2; - + // if incarnation is active dude, it's sndStartId will be stored in sysData1, otherwise it will be data3 if (pIncarnation->statnum == kStatDude && pIncarnation->type == kDudeModernCustom) pXSprite->sysData1 = pXIncarnation->sysData1; - else pXIncarnation->data3; - + else pXSprite->sysData1 = pXIncarnation->data3; + pXSprite->data4 = pXIncarnation->data4; pXSprite->dudeGuard = pXIncarnation->dudeGuard; @@ -6203,7 +6223,7 @@ void actProcessSprites(void) pXSprite->dropMsg = pXIncarnation->dropMsg; pXSprite->key = pXIncarnation->key; - + pXSprite->locked = pXIncarnation->locked; pXSprite->Decoupled = pXIncarnation->Decoupled; @@ -6212,8 +6232,8 @@ void actProcessSprites(void) // set hp 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; switch (pSprite->type) { case kDudePodMother: // fake dude @@ -6226,7 +6246,7 @@ void actProcessSprites(void) fallthrough__; // go below default: seqSpawn(seqId, 3, nXSprite, -1); - + // save target int target = pXSprite->target; @@ -6239,13 +6259,12 @@ void actProcessSprites(void) // finally activate it aiActivateDude(pSprite, pXSprite); - + break; } // remove the incarnation in case if non-locked if (pXIncarnation->locked == 0) { - pXIncarnation->txID = pIncarnation->type = 0; actPostSprite(pIncarnation->xvel, kStatFree); // or restore triggerOn and off options @@ -6253,14 +6272,13 @@ void actProcessSprites(void) pXIncarnation->triggerOn = triggerOn; pXIncarnation->triggerOff = triggerOff; } - } else { - + if (pXSprite->sysData1 == kGenDudeTransformStatus) pXSprite->sysData1 = 0; // just trigger dude death trTriggerSprite(nSprite, pXSprite, kCmdOff, pSprite->owner); - } } + } if (pSprite->type == kDudeCerberusTwoHead) { @@ -7339,89 +7357,58 @@ 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 dist_a_b(a, b); return dist_a_b(rng); } // tries to get random data field of sprite -int GetRandDataVal(int *rData, spritetype* pSprite) { - int temp[4]; - if (rData != NULL && pSprite != NULL) return -1; - 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; +int GetRandDataVal(int *rData) { + dassert(rData != NULL); + int random = 0, a = 0; int first = -1; int maxRetries = 10; + // randomize only in case if at least 2 data fields are not empty - int a = 1; int b = -1; - for (int i = 0; i <= 3; i++) { - if (rData[i] == 0) { - if (a++ > 2) - return -1; + for (int i = 0; i < 4; i++) { + if (rData[i] <= 0 && a++ > 1) return -1; + else if (first == -1) first = rData[i]; } - else if (b == -1) { - b++; - } - } // try randomize few times - int maxRetries = 10; while (maxRetries > 0) { - - // use true random only for single player mode - // 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); - } - + // use true random only for single player mode, otherwise use Blood's default one. + random = (gGameOptions.nGameType == 0 && !VanillaMode() && !DemoRecordStatus()) ? STD_Random(0, 3) : Random(3); if (rData[random] > 0) return rData[random]; - maxRetries--; + else maxRetries--; } // if nothing, get first found data value from top - return rData[b]; + return first; } // this function drops random item using random pickup generator(s) spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) { - spritetype* pSprite2 = 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; + spritetype* pSprite2 = NULL; int* rData = xspriData2Array(pSprite->extra); + int selected = -1; + if (rData != NULL) { + // randomize only in case if at least 2 data fields fits. for (int i = 0; i <= 3; i++) if (rData[i] < kItemWeaponBase || rData[i] >= kItemMax) rData[i] = 0; 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) { spritetype* pSource = pSprite; XSPRITE* pXSource = &xsprite[pSource->extra]; pSprite2 = actDropObject(pSprite, selected); if (pSprite2 != NULL) { - + pXSource->dropMsg = pSprite2->type; // store dropped item type in dropMsg pSprite2->x = pSource->x; pSprite2->y = pSource->y; @@ -7429,8 +7416,8 @@ spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) { if ((pSource->flags & kModernTypeFlag1) && (pXSource->txID > 0 || (pXSource->txID != 3 && pXSource->lockMsg > 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. pXSprite2->txID = pXSource->txID; @@ -7444,27 +7431,24 @@ spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem) { } } + } + return pSprite2; } // this function spawns random dude using dudeSpawn spritetype* spawnRandomDude(spritetype* pSprite) { spritetype* pSprite2 = NULL; - - if (pSprite->extra >= 0) { - 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; - + int* rData = xspriData2Array(pSprite->extra); int selected = -1; + if (rData != NULL) { // randomize only in case if at least 2 data fields fits. for (int i = 0; i <= 3; i++) if (rData[i] < kDudeBase || rData[i] >= kDudeMax) rData[i] = 0; - - if ((selected = GetRandDataVal(rData,NULL)) > 0) + + if ((selected = GetRandDataVal(rData)) > 0) pSprite2 = actSpawnDude(pSprite, selected, -1, 0); } - return pSprite2; } //------------------------- @@ -7529,7 +7513,7 @@ spritetype* actSpawnCustomDude(spritetype* pSprite, int nDist) { if (pSource->clipdist > 0) pDude->clipdist = pSource->clipdist; // 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); @@ -7666,7 +7650,6 @@ int getSpriteMassBySize(spritetype* pSprite) { cached->picnum = pSprite->picnum; cached->seqId = seqId; cached->clipdist = pSprite->clipdist; - //viewSetSystemMessage("MASS: %d", cached->mass); return cached->mass; } @@ -7738,9 +7721,9 @@ void debrisMove(int listIndex) { int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int moveHit = 0; - int floorDist = (bottom - pSprite->z) / 4; - int ceilDist = (pSprite->z - top) / 4; - int clipDist = pSprite->clipdist << 2; + //int floorDist = (bottom - pSprite->z) / 4; + //int ceilDist = (pSprite->z - top) / 4; + //int clipDist = pSprite->clipdist << 2; int tmpFraction = gSpriteMass[pSprite->extra].fraction; if (sector[nSector].extra >= 0 && xsector[sector[nSector].extra].Underwater) @@ -7826,7 +7809,7 @@ void debrisMove(int listIndex) { if (v20 > 0) { 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; if (velFloor[pSprite->sectnum] == 0 && klabs(zvel[nSprite]) < 0x10000) { @@ -7918,4 +7901,18 @@ bool ceilIsTooLow(spritetype* pSprite) { 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 diff --git a/source/blood/src/actor.h b/source/blood/src/actor.h index ef8759a8b..e72a43fd2 100644 --- a/source/blood/src/actor.h +++ b/source/blood/src/actor.h @@ -133,6 +133,7 @@ struct MissileType char shade; unsigned char clipDist; 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 @@ -268,8 +269,8 @@ void MakeSplash(spritetype *pSprite, XSPRITE *pXSprite); spritetype* DropRandomPickupObject(spritetype* pSprite, short prevItem); spritetype* spawnRandomDude(spritetype* pSprite); int GetDataVal(spritetype* pSprite, int data); -int my_random(int a, int b); -int GetRandDataVal(int *rData, spritetype* pSprite); +int STD_Random(int a, int b); +int GetRandDataVal(int *rData); bool sfxPlayMissileSound(spritetype* pSprite, int missileId); bool sfxPlayVectorSound(spritetype* pSprite, int vectorId); spritetype* actSpawnCustomDude(spritetype* pSprite, int nDist); @@ -280,6 +281,7 @@ int isDebris(int nSprite); int debrisGetFreeIndex(void); void debrisMove(int listIndex); 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 short gProxySpritesList[kMaxSuperXSprites]; diff --git a/source/blood/src/ai.cpp b/source/blood/src/ai.cpp index ab99b6e35..f31e17c9c 100644 --- a/source/blood/src/ai.cpp +++ b/source/blood/src/ai.cpp @@ -124,12 +124,6 @@ void aiNewState(spritetype *pSprite, XSPRITE *pXSprite, AISTATE *pAIState) 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) { int top, bottom; @@ -171,7 +165,7 @@ bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange) switch (pSprite->type) { case kDudeCerberusTwoHead: // Cerberus case kDudeCerberusOneHead: // 1 Head Cerberus - if (VanillaMode() || !dudeIsImmune(pSprite, pXSector->damageType)) + if (VanillaMode() || !isImmune(pSprite, pXSector->damageType)) Crusher = 1; break; default: @@ -233,7 +227,7 @@ bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange) break; case kDudeModernCustom: 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; fallthrough__; case kDudeZombieAxeNormal: @@ -1025,7 +1019,7 @@ int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_T else if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) { removeDudeStuff(pSprite); - if (pExtra->curWeapon >= kTrapExploder && pExtra->curWeapon < (kTrapExploder + kExplodeMax) - 1) + if (pExtra->weaponType == kGenDudeWeaponKamikaze) doExplosion(pSprite, pXSprite->data1 - kTrapExploder); 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); } } 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 (!canDuck(pSprite) || !sub_5BDA8(pSprite, 14)) aiGenDudeNewState(pSprite, &genDudeDodgeShortL); else aiGenDudeNewState(pSprite, &genDudeDodgeShortD); @@ -1136,7 +1130,7 @@ void RecoilDude(spritetype *pSprite, XSPRITE *pXSprite) } else if (!dudeIsMelee(pXSprite) || Chance(rChance >> 2)) { if (rState == 1) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeL : &genDudeDodgeShortL); 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; @@ -1619,10 +1613,7 @@ void aiInitSprite(spritetype *pSprite) break; } case kDudeGillBeast: - if (pXSector && pXSector->Underwater) - aiNewState(pSprite, pXSprite, &gillBeastIdle); - else - aiNewState(pSprite, pXSprite, &gillBeastIdle); + aiNewState(pSprite, pXSprite, &gillBeastIdle); break; case kDudeBat: { diff --git a/source/blood/src/aiunicult.cpp b/source/blood/src/aiunicult.cpp index a45377787..fdffd9ca6 100644 --- a/source/blood/src/aiunicult.cpp +++ b/source/blood/src/aiunicult.cpp @@ -89,6 +89,10 @@ AISTATE genDudeDodgeShortL = { kAiStateMove, 9, -1, 60, NULL, aiMoveDodge, NULL, AISTATE genDudeDodgeShortD = { kAiStateMove, 14, -1, 60, NULL, aiMoveDodge, NULL, &genDudeChaseD }; 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 genDudeChaseD = { kAiStateChase, 14, -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); } +/*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) { GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; @@ -195,37 +233,35 @@ static void genDudeAttack1(int, int nXIndex) { } XSPRITE* pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; - - if (!(nSprite >= 0 && nSprite < kMaxSprites)) { + if (pXSprite->target < 0) return; + else if (!(nSprite >= 0 && nSprite < kMaxSprites)) { consoleSysMsg("nIndex >= 0 && nIndex < kMaxSprites"); return; } - - spritetype* pSprite = &sprite[nSprite]; int dx, dy, dz; + + int dx, dy, dz; spritetype* pSprite = &sprite[nSprite]; xvel[pSprite->index] = yvel[pSprite->index] = 0; - - short curWeapon = gGenDudeExtra[nSprite].curWeapon; + 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]; - - VECTORDATA* pVectorData = &gVectorData[curWeapon]; - int vdist = pVectorData->maxDist; - // dispersal modifiers here in case if non-melee enemy - if (vdist <= 0 || vdist > 1280) { - dx += Random3(dispersion); dy += Random3(dispersion); - dz += Random3(dispersion); + if (!dudeIsMelee(pXSprite)) { + dx += Random3(dispersion); dy += Random3(dispersion); dz += Random3(dispersion); } actFireVector(pSprite, 0, 0, dx, dy, dz,(VECTOR_TYPE)curWeapon); if (!playGenDudeSound(pSprite, kGenDudeSndAttackNormal)) sfxPlayVectorSound(pSprite, curWeapon); - } else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { + } else if (weaponType == kGenDudeWeaponSummon) { spritetype* pSpawned = NULL; int dist = pSprite->clipdist << 4; short slaveCnt = gGenDudeExtra[pSprite->index].slaveCount; @@ -247,15 +283,12 @@ static void genDudeAttack1(int, int nXIndex) { sfxPlay3DSoundCP(pSprite, 379, 1, 0, 0x10000 - Random3(0x3000)); } - } else if (curWeapon >= kMissileBase && curWeapon < kMissileMax) { + } else if (weaponType == kGenDudeWeaponMissile) { - 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]; // dispersal modifiers here - dx += Random3(dispersion); dy += Random3(dispersion); - dz += Random3(dispersion >> 1); + dx += Random3(dispersion); dy += Random3(dispersion); dz += Random3(dispersion >> 1); actFireMissile(pSprite, 0, 0, dx, dy, dz, curWeapon); if (!playGenDudeSound(pSprite, kGenDudeSndAttackNormal)) @@ -282,13 +315,12 @@ static void ThrowThing(int nXIndex, bool impact) { return; short curWeapon = gGenDudeExtra[sprite[pXSprite->reference].index].curWeapon; - if (curWeapon < kThingBase || curWeapon >= kThingMax) - return; + short weaponType = gGenDudeExtra[sprite[pXSprite->reference].index].weaponType; + if (weaponType != kGenDudeWeaponThrow) return; const THINGINFO* pThinkInfo = &thingInfo[curWeapon - kThingBase]; if (!pThinkInfo->allowThrow) return; - - if (!playGenDudeSound(pSprite, kGenDudeSndAttackThrow)) + else if (!playGenDudeSound(pSprite, kGenDudeSndAttackThrow)) sfxPlay3DSound(pSprite, 455, -1, 0); int zThrow = 14500; @@ -377,6 +409,10 @@ static void ThrowThing(int nXIndex, bool impact) { } 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); sub_5F15C(pSprite, pXSprite); } @@ -415,28 +451,23 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { } - - 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(spriteIsUnderwater(pSprite,false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW); else aiGenDudeNewState(pSprite, &genDudeSearchShortL); return; + } else if (pXTarget->health <= 0) { // target is dead PLAYER* pPlayer = NULL; - if ((!IsPlayerSprite(pTarget) && pSprite->index == pTarget->owner) - || ((pPlayer = getPlayerById(pTarget->type)) != NULL && pPlayer->fraggerId == pSprite->index)) { - + if ((!IsPlayerSprite(pTarget)) || ((pPlayer = getPlayerById(pTarget->type)) != NULL && pPlayer->fraggerId == pSprite->index)) { playGenDudeSound(pSprite, kGenDudeSndTargetDead); - if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW); else aiGenDudeNewState(pSprite, &genDudeSearchShortL); } else if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeGotoW); else aiGenDudeNewState(pSprite, &genDudeGotoL); - return; } @@ -451,18 +482,17 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { if (inAttack(pXSprite->aiState)) xvelocity = yvelocity = ClipLow(pSprite->clipdist >> 1, 1); + //aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); aiGenDudeChooseDirection(pSprite, pXSprite, getangle(dx, dy), xvelocity, yvelocity); GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index]; if (!pExtra->canAttack) { - aiSetTarget(pXSprite, pSprite->index); + if (pExtra->canWalk) aiSetTarget(pXSprite, pSprite->index); if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeGotoW); else aiGenDudeNewState(pSprite, &genDudeGotoL); return; - } - - if (IsPlayerSprite(pTarget)) { - PLAYER* pPlayer = &gPlayer[ pTarget->type - kDudePlayer1 ]; + } else if (IsPlayerSprite(pTarget)) { + PLAYER* pPlayer = &gPlayer[pTarget->type - kDudePlayer1]; if (powerupCheck(pPlayer, kPwUpShadowCloak) > 0) { if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeSearchShortW); else aiGenDudeNewState(pSprite, &genDudeSearchShortL); @@ -471,7 +501,9 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { } 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, pSprite->x, pSprite->y, pSprite->z - eyeAboveZ, pSprite->sectnum)) { @@ -481,26 +513,25 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { return; } - int nAngle = getangle(dx, dy); - int losAngle = ((kAng180 + nAngle - pSprite->ang) & 2047) - kAng180; - // is the target visible? if (dist < pDudeInfo->seeDist && klabs(losAngle) <= pDudeInfo->periphery) { if (((int)gFrameClock & 64) == 0 && Chance(0x3000) && !spriteIsUnderwater(pSprite, false)) playGenDudeSound(pSprite, kGenDudeSndChasing); - gDudeSlope[sprite[pXSprite->reference].extra] = (int)divscale(pTarget->z - pSprite->z, dist, 10); - - short curWeapon = gGenDudeExtra[pSprite->index].curWeapon; + aiSetTarget(pXSprite, pXSprite->target); + gDudeSlope[pSprite->extra] = divscale(pTarget->z - pSprite->z, dist, 10); + + short curWeapon = gGenDudeExtra[pSprite->index].curWeapon; short weaponType = gGenDudeExtra[pSprite->index].weaponType; spritetype* pLeech = leechIsDropped(pSprite); VECTORDATA* meleeVector = &gVectorData[22]; - - if (pExtra->updReq[kGenDudePropertyAttack]) - genDudePrepare(pSprite, kGenDudePropertyAttack); - - if (curWeapon >= kThingBase && curWeapon < kThingMax) { + if (weaponType == kGenDudeWeaponThrow) { 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); switch (pHit) { case 0: @@ -575,16 +606,17 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { } 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 ((vdist = gVectorData[curWeapon].maxDist) <= 0 || vdist > defDist) + if (weaponType == kGenDudeWeaponHitscan) { + if ((vdist = gVectorData[curWeapon].maxDist) <= 0) vdist = defDist; - } else if (curWeapon >= kDudeBase && curWeapon < kDudeMax) { + } else if (weaponType == kGenDudeWeaponSummon) { // 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); return; } 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 int state = checkAttackState(pSprite, pXSprite); - int mdist = (curWeapon != kMissileFlareAlt) ? 3000 : 2500; switch (curWeapon) { case kMissileLifeLeechRegular: // pickup life leech if it was thrown previously if (pLeech != NULL) removeLeech(pLeech); + mdist = 1500; break; case kMissileFlareAlt: + mdist = 2500; + fallthrough__; case kMissileFireball: case kMissileFireballNapam: case kMissileFireballCerberus: case kMissileFireballTchernobog: + if (mdist == defDist) mdist = 3000; if (dist > mdist || pXSprite->locked == 1) break; else if (dist <= meleeVector->maxDist && Chance(0x9000)) aiGenDudeNewState(pSprite, &genDudePunch); @@ -623,19 +658,23 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { return; case kMissileFlameSpray: case kMissileFlameHound: + //viewSetSystemMessage("%d", pXTarget->burnTime); if (spriteIsUnderwater(pSprite, false)) { if (dist > meleeVector->maxDist) aiGenDudeNewState(pSprite, &genDudeChaseW); 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; } - - vdist = 4200; if (((int)gFrameClock & 16) == 0) vdist += Random(800); + vdist = 3500 + (gGameOptions.nDifficulty * 400); break; } - } else if (curWeapon >= kTrapExploder && curWeapon < (kTrapExploder + kExplodeMax) - 1) { + } else if (weaponType == kGenDudeWeaponKamikaze) { 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; if (doExplosion(pSprite, nType) && pXSprite->health > 0) actDamageSprite(pSprite->xvel, pSprite, DAMAGE_TYPE_3, 65535); @@ -643,30 +682,235 @@ static void thinkChase( spritetype* pSprite, XSPRITE* pXSprite ) { return; } - //if (dist <= vdist && pXSprite->aiState == &genDudeChaseD) - //aiGenDudeNewState(pSprite, &genDudeChaseL); - 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 (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) { case 1: aiGenDudeNewState(pSprite, &genDudeFireW); pXSprite->aiState->nextState = &genDudeFireW; - return; + break; case 2: aiGenDudeNewState(pSprite, &genDudeFireD); pXSprite->aiState->nextState = &genDudeFireD; - return; + break; default: aiGenDudeNewState(pSprite, &genDudeFireL); pXSprite->aiState->nextState = &genDudeFireL; - return; + break; } + } 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; else if (state == 2) pXSprite->aiState->nextState = &genDudeChaseD; else pXSprite->aiState->nextState = &genDudeChaseL; @@ -713,16 +957,6 @@ int checkAttackState(spritetype* pSprite, XSPRITE* pXSprite) { 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 int getGenDudeMoveSpeed(spritetype* pSprite,int which, bool mul, bool shift) { DUDEINFO* pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; @@ -802,7 +1036,7 @@ void aiGenDudeMoveForward(spritetype* pSprite, XSPRITE* pXSprite ) { int sin = Sin(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); 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 if (!gGenDudeExtra[pSprite->index].canWalk) { - if (pAIState == &genDudeDodgeL || pAIState == &genDudeDodgeShortL) + if (pAIState == &genDudeDodgeL || pAIState == &genDudeDodgeShortL || pAIState == &genDudeDodgeShorterL) pAIState = &genDudeRecoilL; - else if (pAIState == &genDudeDodgeD || pAIState == &genDudeDodgeShortD) + else if (pAIState == &genDudeDodgeD || pAIState == &genDudeDodgeShortD || pAIState == &genDudeDodgeShorterD) pAIState = &genDudeRecoilD; - else if (pAIState == &genDudeDodgeW || pAIState == &genDudeDodgeD) + else if (pAIState == &genDudeDodgeW || pAIState == &genDudeDodgeShortW || pAIState == &genDudeDodgeShorterW) pAIState = &genDudeRecoilW; - else if (pAIState == &genDudeSearchL) pAIState = &genDudeSearchNoWalkL; - else if (pAIState == &genDudeSearchW) pAIState = &genDudeSearchNoWalkW; + else if (pAIState == &genDudeSearchL || pAIState == &genDudeSearchShortL) + pAIState = &genDudeSearchNoWalkL; + + else if (pAIState == &genDudeSearchW || pAIState == &genDudeSearchShortW) + pAIState = &genDudeSearchNoWalkW; + else if (pAIState == &genDudeGotoL) pAIState = &genDudeIdleL; else if (pAIState == &genDudeGotoW) pAIState = &genDudeIdleW; else if (pAIState == &genDudeChaseL) pAIState = &genDudeChaseNoWalkL; @@ -879,6 +1117,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; 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; GENDUDESND* sndInfo =& gCustomDudeSnd[mode]; bool gotSnd = false; @@ -912,7 +1155,6 @@ bool playGenDudeSound(spritetype* pSprite, int mode, bool forceInterrupt) { pExtra->sndPlaying = false; break; } - //viewSetSystemMessage("PLAYING %d / %d / %d", Bonkle[i].atc, (sndId + a), Bonkle[i].at0); return true; } } @@ -1050,26 +1292,28 @@ bool dudeIsMelee(XSPRITE* pXSprite) { void scaleDamage(XSPRITE* pXSprite) { 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++) curScale[i] = dudeInfo[kDudeModernCustom - kDudeBase].startDamage[i]; // all enemies with vector weapons gets extra resistance to bullet damage - if (curWeapon > 0 && curWeapon < kVectorMax) { - //curScale[kDmgBullet] -= 10; + if (weaponType == kGenDudeWeaponHitscan) { + + curScale[kDmgBullet] -= 10; // 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++) curScale[i] = dudeInfo[curWeapon - kDudeBase].startDamage[i]; // 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; - } else if (curWeapon >= kMissileBase && curWeapon < kThingMax) { + } else if (weaponType == kGenDudeWeaponMissile || weaponType == kGenDudeWeaponThrow) { switch (curWeapon) { case kMissileButcherKnife: @@ -1092,7 +1336,7 @@ void scaleDamage(XSPRITE* pXSprite) { case kThingPodFireBall: case kThingNapalmBall: curScale[kDmgBurn] = 32; - curScale[kDmgExplode] = 50; + curScale[kDmgExplode] -= 20; break; case kMissileLifeLeechRegular: case kThingDroppedLifeLeech: @@ -1134,29 +1378,29 @@ void scaleDamage(XSPRITE* pXSprite) { switch (pXSprite->dropMsg) { case kItemArmorAsbest: curScale[kDmgBurn] = 0; - curScale[kDmgExplode] -= 25; + curScale[kDmgExplode] -= 30; break; case kItemArmorBasic: - curScale[kDmgBurn] -= 10; - curScale[kDmgExplode] -= 10; - curScale[kDmgBullet] -= 10; - curScale[kDmgSpirit] -= 10; + curScale[kDmgBurn] -= 15; + curScale[kDmgExplode] -= 15; + curScale[kDmgBullet] -= 15; + curScale[kDmgSpirit] -= 15; break; case kItemArmorBody: - curScale[kDmgBullet] -= 20; + curScale[kDmgBullet] -= 30; break; case kItemArmorFire: - curScale[kDmgBurn] -= 20; - curScale[kDmgExplode] -= 10; + curScale[kDmgBurn] -= 30; + curScale[kDmgExplode] -= 30; break; case kItemArmorSpirit: - curScale[kDmgSpirit] -= 20; + curScale[kDmgSpirit] -= 30; break; case kItemArmorSuper: - curScale[kDmgBurn] -= 20; - curScale[kDmgExplode] -= 20; - curScale[kDmgBullet] -= 20; - curScale[kDmgSpirit] -= 20; + curScale[kDmgBurn] -= 60; + curScale[kDmgExplode] -= 60; + curScale[kDmgBullet] -= 60; + curScale[kDmgSpirit] -= 60; break; } } @@ -1240,13 +1484,9 @@ void scaleDamage(XSPRITE* pXSprite) { for (int i = 0; i < kDmgMax; i++) curScale[i] = mulscale8(DudeDifficulty[gGameOptions.nDifficulty], ClipLow(curScale[i], 1)); - short* dc = curScale; - if (gGenDudeExtra[sprite[pXSprite->reference].index].canFly) { - curScale[0] = -1; - } - - 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]); + //short* dc = curScale; + //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) { @@ -1260,7 +1500,7 @@ int getDispersionModifier(spritetype* pSprite, int minDisp, int maxDisp) { disp = (((shots * 1000) / nFrames) / ticks) * 20; if (gGameOptions.nDifficulty > 0) - disp /= gGameOptions.nDifficulty + 1; + disp /= gGameOptions.nDifficulty; //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]); } } - dist = tilesiz[picnum].y << 8; + dist = tilesiz[picnum].y << 8; if (yrepeat < 64) dist -= (64 - yrepeat) * mul; else if (yrepeat > 64) dist += (yrepeat - 64) * (mul / 3); } @@ -1299,7 +1539,7 @@ int getBaseChanceModifier(int baseChance) { int getRecoilChance(spritetype* 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; int chance = ((baseChance / mass) << 7); @@ -1307,16 +1547,13 @@ int getRecoilChance(spritetype* pSprite) { } int getDodgeChance(spritetype* pSprite) { - int mass = getSpriteMassBySize(pSprite); int baseChance = getBaseChanceModifier(0x6000); - if (pSprite->extra >= 0) { - XSPRITE* pXSprite = &xsprite[pSprite->extra]; - baseChance += pXSprite->burnTime; - if (dudeIsMelee(pXSprite)) - baseChance = 0x1000; - } + XSPRITE* pXSprite = &xsprite[pSprite->extra]; int mass = getSpriteMassBySize(pSprite); + int baseChance = (!dudeIsMelee(pXSprite) ? 0x6000 : 0x1000); + baseChance = getBaseChanceModifier(baseChance) + pXSprite->data3; int chance = ((baseChance / mass) << 7); return chance; + } void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event) @@ -1327,7 +1564,7 @@ void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT event) } int nTarget = pXSprite->target; - if (spriRangeIsFine(nTarget)) { + if (spriRangeIsFine(nTarget) && nTarget != pSprite->owner) { spritetype* pTarget = &sprite[nTarget]; 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) { - return (aiState == &genDudeDodgeShortW || aiState == &genDudeDodgeShortL || aiState == &genDudeDodgeShortD); +short inDodge(AISTATE* aiState) { + 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) { @@ -1483,6 +1730,16 @@ short inSearch(AISTATE* aiState) { 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) { if (aiState == &genDudeRecoilL || aiState == &genDudeRecoilTesla) return 1; else if (aiState == &genDudeRecoilD) return 2; @@ -1512,7 +1769,6 @@ bool canWalk(spritetype* pSprite) { return gGenDudeExtra[pSprite->index].canWalk; } - int genDudeSeqStartId(XSPRITE* pXSprite) { if (genDudePrepare(&sprite[pXSprite->reference], kGenDudePropertyStates)) return pXSprite->data2; else return kGenDudeDefaultSeq; @@ -1539,11 +1795,13 @@ bool genDudePrepare(spritetype* pSprite, int propId) { switch (propId) { case kGenDudePropertyAll: case kGenDudePropertyInitVals: - pExtra->frontSpeed = getGenDudeMoveSpeed(pSprite, 0, true, false); + pExtra->moveSpeed = getGenDudeMoveSpeed(pSprite, 0, true, false); pExtra->initVals[0] = pSprite->xrepeat; pExtra->initVals[1] = pSprite->yrepeat; pExtra->initVals[2] = pSprite->clipdist; if (propId) break; + fallthrough__; + case kGenDudePropertyWeapon: { pExtra->curWeapon = 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")) 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; - if (pExtra->curWeapon >= kTrapExploder && pExtra->curWeapon < (kTrapExploder + kExplodeMax) - 1) pExtra->isMelee = true; - else if (pExtra->curWeapon >= 0 && pExtra->curWeapon < kVectorMax) { + if (pExtra->weaponType == kGenDudeWeaponKamikaze) pExtra->isMelee = true; + else if (pExtra->weaponType == kGenDudeWeaponHitscan) { if (gVectorData[pExtra->curWeapon].maxDist > 0 && gVectorData[pExtra->curWeapon].maxDist <= kGenDudeMaxMeleeDist) pExtra->isMelee = true; } if (propId) break; + fallthrough__; + } case kGenDudePropertyDmgScale: scaleDamage(pXSprite); if (propId) break; + fallthrough__; + 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]; pMass->seqId = pMass->picnum = pMass->xrepeat = pMass->yrepeat = pMass->clipdist = 0; pMass->mass = pMass->airVel = pMass->fraction = 0; - getSpriteMassBySize(pSprite); if (propId) break; + fallthrough__; } case kGenDudePropertyAttack: pExtra->fireDist = getRangeAttackDist(pSprite, 1200, 45000); pExtra->throwDist = pExtra->fireDist; // temp pExtra->baseDispersion = getDispersionModifier(pSprite, 200, 3500); if (propId) break; + fallthrough__; + case kGenDudePropertyStates: { pExtra->canFly = false; // 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; else seqStartId = pXSprite->data2; @@ -1595,18 +1867,26 @@ bool genDudePrepare(spritetype* pSprite, int propId) { switch (i - seqStartId) { case kGenDudeSeqIdleL: case kGenDudeSeqDeathDefault: + case kGenDudeSeqAttackNormalL: + case kGenDudeSeqAttackThrow: + case kGenDudeSeqAttackPunch: if (!gSysRes.Lookup(i, "SEQ")) { pXSprite->data2 = dudeInfo[pSprite->type - kDudeBase].seqStartID; viewSetSystemMessage("No SEQ animation id %d found for custom dude #%d!", i, pSprite->index); 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; case kGenDudeSeqDeathExplode: - if (gSysRes.Lookup(i, "SEQ")) pExtra->availDeaths[kDmgExplode] = 1; - else pExtra->availDeaths[kDmgExplode] = 0; - break; - case kGenDudeSeqAttackNormalL: - pExtra->canAttack = (gSysRes.Lookup(i, "SEQ") && pExtra->curWeapon > 0); + pExtra->availDeaths[kDmgExplode] = (bool)gSysRes.Lookup(i, "SEQ"); break; case kGenDudeSeqBurning: pExtra->canBurn = gSysRes.Lookup(i, "SEQ"); @@ -1636,19 +1916,6 @@ bool genDudePrepare(spritetype* pSprite, int propId) { } 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: pExtra->canDuck = (gSysRes.Lookup(i, "SEQ") && gSysRes.Lookup(seqStartId + 14, "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; + fallthrough__; } case kGenDudePropertyLeech: - //viewSetSystemMessage("PROPERTY: LEECH"); pExtra->nLifeLeech = -1; if (pSprite->owner != kMaxSprites - 1) { for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { @@ -1695,8 +1962,9 @@ bool genDudePrepare(spritetype* pSprite, int propId) { } } if (propId) break; + fallthrough__; + case kGenDudePropertySlaves: - //viewSetSystemMessage("PROPERTY: SLAVES"); pExtra->slaveCount = 0; memset(pExtra->slave, -1, sizeof(pExtra->slave)); for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { if (sprite[nSprite].owner != pSprite->xvel) continue; @@ -1710,6 +1978,8 @@ bool genDudePrepare(spritetype* pSprite, int propId) { break; } if (propId) break; + fallthrough__; + case kGenDudePropertySpriteSize: { if (seqGetStatus(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]; if (propId) break; + fallthrough__; } } diff --git a/source/blood/src/aiunicult.h b/source/blood/src/aiunicult.h index 04395f43d..4c3ed60f4 100644 --- a/source/blood/src/aiunicult.h +++ b/source/blood/src/aiunicult.h @@ -31,6 +31,9 @@ BEGIN_BLD_NS #define kGenDudeTransformStatus -222 #define kGenDudeUpdTimeRate 10 #define kGenDudeMaxMeleeDist 2048 +#define kGenDudeMinDispesion 200 +#define kGenDudeMaxDispersion 3500 +#define kGenDudeKlabsAng 56 enum { kGenDudeSeqIdleL = 0, @@ -90,6 +93,16 @@ kGenDudePropertyInitVals = 9, kGenDudePropertyMax , }; +enum { +kGenDudeWeaponNone = -1, +kGenDudeWeaponHitscan = 0, +kGenDudeWeaponMissile = 1, +kGenDudeWeaponThrow = 2, +kGenDudeWeaponSummon = 3, +kGenDudeWeaponKamikaze = 4, +kGenDudeWeaponMax , +}; + extern AISTATE genDudeIdleL; extern AISTATE genDudeIdleW; extern AISTATE genDudeSearchL; @@ -102,6 +115,9 @@ extern AISTATE genDudeDodgeW; extern AISTATE genDudeDodgeShortL; extern AISTATE genDudeDodgeShortD; extern AISTATE genDudeDodgeShortW; +extern AISTATE genDudeDodgeShorterL; +extern AISTATE genDudeDodgeShorterD; +extern AISTATE genDudeDodgeShorterW; extern AISTATE genDudeChaseL; extern AISTATE genDudeChaseD; extern AISTATE genDudeChaseW; @@ -134,20 +150,21 @@ extern GENDUDESND gCustomDudeSnd[]; // temporary, until normal DUDEEXTRA gets refactored 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 throwDist; // counts from sprite size - unsigned int frontSpeed; unsigned short curWeapon; // data1 duplicate to avoid potential problems when changing data dynamically + unsigned short weaponType; unsigned short baseDispersion; + unsigned short slaveCount; // how many dudes is summoned signed short nLifeLeech; // spritenum of dropped dude's leech - short slaveCount; - 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 + signed short slave[kGenDudeMaxSlaves]; // index of the ones dude is summon bool updReq[kGenDudePropertyMax]; // update requests 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 canBurn; // can turn in Burning dude or not bool canElectrocute; @@ -171,12 +188,11 @@ void removeLeech(spritetype* pLeech, bool delSprite = true); void removeDudeStuff(spritetype* pSprite); spritetype* leechIsDropped(spritetype* pSprite); 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 aiGenDudeChooseDirection(spritetype* pSprite, XSPRITE* pXSprite, int a3, int aXvel = -1, int aYvel = -1); void aiGenDudeNewState(spritetype* pSprite, AISTATE* pAIState); int getGenDudeMoveSpeed(spritetype* pSprite, int which, bool mul, bool shift); -bool TargetNearThing(spritetype* pSprite, int thingType); int checkAttackState(spritetype* pSprite, XSPRITE* pXSprite); bool doExplosion(spritetype* pSprite, int nType); void dudeLeechOperate(spritetype* pSprite, XSPRITE* pXSprite, EVENT a3); @@ -188,11 +204,12 @@ void updateTargetOfLeech(spritetype* pSprite); bool canSwim(spritetype* pSprite); bool canDuck(spritetype* pSprite); bool canWalk(spritetype* pSprite); -bool inDodge(AISTATE* aiState); +short inDodge(AISTATE* aiState); bool inIdle(AISTATE* aiState); bool inAttack(AISTATE* aiState); short inRecoil(AISTATE* aiState); short inSearch(AISTATE* aiState); +short inChase(AISTATE* aiState); short inDuck(AISTATE* aiState); int genDudeSeqStartId(XSPRITE* pXSprite); 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); void genDudeUpdate(spritetype* pSprite); void genDudeProcess(spritetype* pSprite, XSPRITE* pXSprite); - +bool genDudeAdjustSlope(spritetype* pSprite, XSPRITE* pXSprite, int dist, int weaponType, int by = 64); END_BLD_NS diff --git a/source/blood/src/asound.cpp b/source/blood/src/asound.cpp index 938afb12c..1604319ff 100644 --- a/source/blood/src/asound.cpp +++ b/source/blood/src/asound.cpp @@ -135,7 +135,8 @@ void ambInit(void) auto snd = soundEngine->FindSoundByResID(nSFX); if (!snd) { //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; } diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp index 0254bfd8e..d95e08720 100644 --- a/source/blood/src/blood.cpp +++ b/source/blood/src/blood.cpp @@ -605,10 +605,6 @@ void StartLevel(GAMEOPTIONS *gameOptions) pSprite->type = kSpriteDecoration; changespritestat(pSprite->index, kStatDecoration); break; - case kModernConcussSprite: - pSprite->type = kSpriteDecoration; - changespritestat(pSprite->index, kStatDecoration); - break; // also erase some modernized vanilla types which was not active case kMarkerWarpDest: if (pSprite->statnum != kStatMarker) pSprite->type = kSpriteDecoration; diff --git a/source/blood/src/callback.cpp b/source/blood/src/callback.cpp index 1357ab7b7..4dc66fb51 100644 --- a/source/blood/src/callback.cpp +++ b/source/blood/src/callback.cpp @@ -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. //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; if (!nType || !nReq) return; diff --git a/source/blood/src/common_game.h b/source/blood/src/common_game.h index 07f0dec8c..8f4ff8046 100644 --- a/source/blood/src/common_game.h +++ b/source/blood/src/common_game.h @@ -88,6 +88,8 @@ void _consoleSysMsg(const char* pMessage, ...); #define kMaxPAL 5 #define kFreeQAVEntry 108 +#define kUserPLUStart 15 + #define kDmgFall 0 #define kDmgBurn 1 #define kDmgBullet 2 @@ -197,7 +199,6 @@ enum { kModernObjDataAccumulator = 37, kModernEffectSpawner = 38, kModernWindGenerator = 39, - kModernConcussSprite = 712, /// WIP kModernPlayerControl = 500, /// WIP // decorations diff --git a/source/blood/src/db.h b/source/blood/src/db.h index 268621e84..d0f48c50f 100644 --- a/source/blood/src/db.h +++ b/source/blood/src/db.h @@ -44,10 +44,6 @@ inline bool xwallRangeIsFine(int nXindex) { 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; #pragma pack(push, 1) diff --git a/source/blood/src/dude.cpp b/source/blood/src/dude.cpp index 31e6d6f98..abe2446b7 100644 --- a/source/blood/src/dude.cpp +++ b/source/blood/src/dude.cpp @@ -1546,30 +1546,30 @@ DUDEINFO dudeInfo[kDudeMax-kDudeBase] = }, //254 - kDudeModernCustom { - 11520, // start sequence ID - 85, // start health - 75, // mass + 11520, // start sequence ID + 85, // start health + 75, // mass 120, - 48, // clip distance - 48, // eye above z + 48, // clip distance + 48, // eye above z 20, - 10240, // hear distance - 51200, // seeing distance - kAng120, // vision periphery + 10240, // hear distance + 51200, // seeing distance + kAng120, // vision periphery // 0, - 618, // melee distance - 5, // flee health - 5, // hinder damage - 0x0100, // change target chance - 0x0010, // change target to kin chance - 0x8000, // alertChance - 0, // lockout - 46603, // frontSpeed - 34952, // sideSpeed - 13981, // backSpeed - 256, // angSpeed + 618, // melee distance + 5, // flee health + 5, // hinder damage + 0x0100, // change target chance + 0x0010, // change target to kin chance + 0x8000, // alertChance + 0, // lockout + 46603, // frontSpeed + 34952, // sideSpeed + 13981, // backSpeed + 256, // angSpeed // 0, - 7, -1, 18, // nGibType + 7, -1, 18, // nGibType 128, 150, 128, 256, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/source/blood/src/eventq.cpp b/source/blood/src/eventq.cpp index 37d286ebd..ef70f5305 100644 --- a/source/blood/src/eventq.cpp +++ b/source/blood/src/eventq.cpp @@ -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; switch (command) { - case kCmdState: - command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; - break; - case kCmdNotState: - command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; - break; + case kCmdState: + command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; + break; + case kCmdNotState: + command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; + break; + default: + break; } switch (rxId) { diff --git a/source/blood/src/gameutil.cpp b/source/blood/src/gameutil.cpp index c324c9dc6..474f83840 100644 --- a/source/blood/src/gameutil.cpp +++ b/source/blood/src/gameutil.cpp @@ -908,4 +908,12 @@ int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pSector 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 diff --git a/source/blood/src/gameutil.h b/source/blood/src/gameutil.h index 74c6168be..4104d43a6 100644 --- a/source/blood/src/gameutil.h +++ b/source/blood/src/gameutil.h @@ -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); 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 picWidth(short nPic, short repeat); +int picHeight(short nPic, short repeat); END_BLD_NS diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp index 6853e0095..b51f1b7dc 100644 --- a/source/blood/src/player.cpp +++ b/source/blood/src/player.cpp @@ -1113,6 +1113,18 @@ void playerStart(int nPlayer) pPlayer->posture = 1; 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) @@ -1160,9 +1172,7 @@ void playerReset(PLAYER *pPlayer) ///////////////// // reset qav scene - QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; - pQavScene->index = pQavScene->causedBy = pPlayer->sceneQav = -1; - pQavScene->qavResrc = NULL; + playerResetQavScene(pPlayer); // restore default movement speed 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]; ClockTicks dword_21EFD0[8]; diff --git a/source/blood/src/player.h b/source/blood/src/player.h index c1cbe9413..d573704f9 100644 --- a/source/blood/src/player.h +++ b/source/blood/src/player.h @@ -332,5 +332,6 @@ void qavScenePlay(PLAYER* pPlayer); void qavSceneDraw(PLAYER* pPlayer, int a2, int a3, int a4, int a5); void playerResetMoveSpeed(PLAYER* pPlayer); void playerResetJumpHeight(PLAYER* pPlayer); +void playerResetQavScene(PLAYER* pPlayer); END_BLD_NS diff --git a/source/blood/src/screen.cpp b/source/blood/src/screen.cpp index f1f696dd9..4b8e56c35 100644 --- a/source/blood/src/screen.cpp +++ b/source/blood/src/screen.cpp @@ -104,8 +104,9 @@ void scrLoadPLUs(void) parallaxvisibility = 3072; 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"); if (!pPlu) ThrowError("%s.PLU not found", PLU[i].name); @@ -113,6 +114,15 @@ void scrLoadPLUs(void) ThrowError("Incorrect PLU size"); 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 palookupfog[1].r = 255; palookupfog[1].g = 255; diff --git a/source/blood/src/seq.cpp b/source/blood/src/seq.cpp index 819b58277..83b6b11a1 100644 --- a/source/blood/src/seq.cpp +++ b/source/blood/src/seq.cpp @@ -388,10 +388,8 @@ void seqSpawn(int a1, int a2, int a3, int a4) if (!pInst) return; DICTNODE *hSeq = gSysRes.Lookup(a1, "SEQ"); - if (!hSeq) { - viewSetSystemMessage("Missing sequence #%d", a1); - return; - } + if (!hSeq) + ThrowError("Missing sequence #%d", a1); int i = activeCount; if (pInst->at13) @@ -588,7 +586,7 @@ void SeqLoadSave::Load(void) int nSeq = pInst->at8; DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ"); if (!hSeq) { - viewSetSystemMessage("Missing sequence #%d", nSeq); + ThrowError("Missing sequence #%d", nSeq); continue; } Seq *pSeq = (Seq*)gSysRes.Lock(hSeq); diff --git a/source/blood/src/triggers.cpp b/source/blood/src/triggers.cpp index 6814d62c5..8e36d4005 100644 --- a/source/blood/src/triggers.cpp +++ b/source/blood/src/triggers.cpp @@ -69,6 +69,18 @@ int nUniMissileTrapClient = seqRegisterClient(UniMissileTrapSeqCallback); int nMGunFireClient = seqRegisterClient(MGunFireSeqCallback); 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) { switch (nType) @@ -368,9 +380,6 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) { 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) { switch (event.cmd) { 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 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. if (pXSprite->data1 > 0 && pXSprite->data2 <= 0 && pXSprite->data3 <= 0 && pXSprite->data4 > 0) { // data1 must be less than data4 if (pXSprite->data1 > pXSprite->data4) { short tmp = pXSprite->data1; - pXSprite->data1 = (short)pXSprite->data4; + pXSprite->data1 = pXSprite->data4; pXSprite->data4 = tmp; } int total = pXSprite->data4 - pXSprite->data1; while (maxRetries > 0) { - - // use true random only for single player mode - // 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()) tx = Random(total) + pXSprite->data1; - else { - rng.seed(std::random_device()()); - tx = (int)my_random(pXSprite->data1, pXSprite->data4); - } - + // use true random only for single player mode, otherwise use Blood's default one. + if (gGameOptions.nGameType == 0 && !VanillaMode() && !DemoRecordStatus()) tx = STD_Random(pXSprite->data1, pXSprite->data4); + else tx = Random(total) + pXSprite->data1; + if (tx != pXSprite->txID) break; maxRetries--; } } else { - while (maxRetries > 0) { - if ((tx = GetRandDataVal(NULL, pSprite)) > 0 && tx != pXSprite->txID) break; - maxRetries--; + + int* rData = xspriData2Array(pSprite->extra); + if (rData != NULL) { + while (maxRetries > 0) { + if ((tx = GetRandDataVal(rData)) > 0 && tx != pXSprite->txID) break; + maxRetries--; + } } + } if (tx > 0) { @@ -628,7 +635,6 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) case kModernObjPicnumChanger: case kModernSectorFXChanger: case kModernObjDataChanger: - case kModernConcussSprite: modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy); return; @@ -871,7 +877,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) else if (pXSprite->command < kCmdNumberic + 3 && pXSprite->command > kCmdNumberic + 4 && !modernTypeSetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1, event.causedBy)) return; - TRPLAYERCTRL* pCtrl = pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; + TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; if (event.cmd >= kCmdNumberic) { switch (event.cmd) { case kCmdNumberic + 3:// start playing qav scene @@ -1017,44 +1023,48 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) switch (pXSprite->data2) { // erase all case 0: + fallthrough__; + // erase weapons case 1: // erase all - if (pXSprite->data3 <= 0) { - 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 { + 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); if (pXSprite->data2) break; + fallthrough__; + // erase all armor case 2: for (int i = 0; i < 3; i++) pPlayer->armor[i] = 0; if (pXSprite->data2) break; + fallthrough__; + // erase all pack items case 3: 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; } if (pXSprite->data2) break; + fallthrough__; + // erase all keys case 4: for (int i = 0; i < 8; i++) pPlayer->hasKey[i] = false; if (pXSprite->data2) break; + fallthrough__; } break; @@ -1094,11 +1104,14 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) case kCmdNumberic + 8: // 72 // use inventory item 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 - if (pXSprite->data4 == 1) - pPlayer->packSlots[0].curAmount = pPlayer->packSlots[0].curAmount = 0; + if (pXSprite->data4 == 1) { + pPlayer->packSlots[invItem].isActive = false; + pPlayer->packSlots[invItem].curAmount = 0; + } } break; @@ -1290,8 +1303,12 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) pXSpawn->target = -1; aiActivateDude(pSpawn, pXSpawn); break; + default: + if (gModernMap && (pSprite->flags & kModernTypeFlag3)) aiActivateDude(pSpawn, pXSpawn); + break; } } + } } break; @@ -1350,6 +1367,7 @@ void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event) sfxPlay3DSound(pSprite, 452, 0, 0); evPost(nSprite, 3, 30, kCmdOff, event.causedBy); pXSprite->state = 1; + fallthrough__; case kCmdOn: sfxPlay3DSound(pSprite, 451, 0, 0); pXSprite->Proximity = 1; @@ -1451,19 +1469,6 @@ void stopWindOnSectors(XSPRITE* pXSource) { 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) { @@ -1501,7 +1506,7 @@ void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy) { void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer) { TRPLAYERCTRL* pCtrl = &gPlayerCtrl[pPlayer->nPlayer]; - viewSetSystemMessage("OFF %d", pCtrl->qavScene.index); + //viewSetSystemMessage("OFF %d", pCtrl->qavScene.index); pXSource->sysData1 = 0; pCtrl->qavScene.index = -1; @@ -2029,22 +2034,29 @@ void useSectorWindGen(XSPRITE* pXSource, sectortype* pSector) { void useSpriteDamager(XSPRITE* pXSource, spritetype* pSprite) { - spritetype* pSource = &sprite[pXSource->reference]; - if (pSprite != NULL && xspriIsFine(pSprite->index)) { - int dmg = (pXSource->data3 == 0) ? 65535 : ClipRange(pXSource->data3, 1, 65535); - int dmgType = ClipRange(pXSource->data2, 0, 6); - - if (pXSource->data2 == -1 && IsDudeSprite(pSprite)) { - xsprite[pSprite->extra].health = ClipLow(xsprite[pSprite->extra].health - dmg, 0); - if (xsprite[pSprite->extra].health == 0) - actKillDude(pSource->index, pSprite, (DAMAGE_TYPE)0, 65535); + if (pSprite != NULL && xspriRangeIsFine(pSprite->extra)) { + XSPRITE* pXSprite = &xsprite[pSprite->extra]; + DAMAGE_TYPE dmgType = (DAMAGE_TYPE) ClipRange(pXSource->data2, kDmgFall, kDmgElectric); + int dmg = (pXSource->data3 == 0) ? 65535 : ClipRange(pXSource->data3 << 1, 1, 65535); + if (pXSprite->data2 >= 0) actDamageSprite(pSource->index, pSprite, dmgType, dmg); + else if (pXSource->data2 == -1 && IsDudeSprite(pSprite)) { + PLAYER* pPlayer = getPlayerById(pSprite->type); + 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) { + if (pXSource->data2 > 0 && !gSysRes.Lookup(pXSource->data2, "SEQ")) { + consoleSysMsg("Missing sequence #%d",pXSource->data2); + return; + } + switch (objType) { case 6: if (pXSource->data2 <= 0) { @@ -2089,7 +2101,7 @@ void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index) { 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; cy = (wall[index].y + wall[wall[index].point2].y) >> 1; int nSector = sectorofwall(index); @@ -2099,9 +2111,6 @@ void useSeqSpawnerGen(XSPRITE* pXSource, int objType, int index) { getzsofslope(wall[index].nextsector, cx, cy, &ceilZ2, &floorZ2); ceilZ = ClipLow(ceilZ, ceilZ2); 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; sfxPlay3DSound(cx, cy, cz, pXSource->data4, nSector); @@ -3476,14 +3485,7 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { return; } - if (pSource->type == kModernConcussSprite) { - /* - 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) { + if (pSource->type == kMarkerWarpDest) { /* - Allows teleport any sprite from any location to the source destination - */ useTeleportTarget(pXSource, &sprite[nDest]); return; @@ -3503,7 +3505,6 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { } else if (pSource->type == kModernSeqSpawner) { /* - 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); return; } @@ -3635,7 +3636,7 @@ void pastePropertiesInObj(int type, int nDest, EVENT event) { if ((pSource->flags & kModernTypeFlag1) || (pXSource->data3 != -1 && pXSource->data3 != 32767)) 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); break; @@ -4838,7 +4839,7 @@ void ActivateGenerator(int nSprite) if (pXSprite->dropMsg > 0) { for (short nItem = headspritestat[kStatItem]; nItem >= 0; nItem = nextspritestat[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); deletesprite(nItem); break; diff --git a/source/blood/src/triggers.h b/source/blood/src/triggers.h index a82f1ad55..002fc2a91 100644 --- a/source/blood/src/triggers.h +++ b/source/blood/src/triggers.h @@ -86,6 +86,9 @@ bool valueIsBetween(int val, int min, int max); void trPlayerCtrlLink(XSPRITE* pXSource, PLAYER* pPlayer); void trPlayerCtrlStartScene(XSPRITE* pXSource, PLAYER* pPlayer, int causedBy); void trPlayerCtrlStopScene(XSPRITE* pXSource, PLAYER* pPlayer); + +extern int gRdata[4]; +int* xspriData2Array(int nXSprite); // ------------------------------------------------------- END_BLD_NS