nnextsif.cpp
1 //------------------------------------------------------------------------- 2 /* 3 Copyright (C) 2010-2019 EDuke32 developers and contributors 4 Copyright (C) 2019 Nuke.YKT 5 Copyright (C) NoOne 6 7 This file is part of NBlood. 8 9 NBlood is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public License version 2 11 as published by the Free Software Foundation. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 17 See the GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 */ 23 //------------------------------------------------------------------------- 24 25 26 /////////////////////////////////////////////////////////////////// 27 // This file provides modern features for mappers. 28 // For full documentation please visit http://cruo.bloodgame.ru/xxsystem 29 /////////////////////////////////////////////////////////////////// 30 31 #include "build.h" 32 #include "common_game.h" 33 #include "tile.h" 34 #include "seq.h" 35 #include "trig.h" 36 #include "view.h" 37 #include "endgame.h" 38 #include "aicdud.h" 39 #include "mmulti.h" 40 #include "nnexts.h" 41 #include "nnextsif.h" 42 43 #define kSerialStep 100000 44 45 #define kPushRange 3 46 #define kCmdPush 64 47 #define kCmdPop 100 48 49 50 enum ENUM_EVENT_OBJECT { 51 EVOBJ_SPRITE = OBJ_SPRITE, 52 EVOBJ_SECTOR = OBJ_SECTOR, 53 EVOBJ_WALL = OBJ_WALL, 54 }; 55 56 enum ENUM_CONDITION_SERIAL { 57 kSerialSector = kSerialStep + kMaxSectors + kMaxWalls + kMaxSprites, 58 kSerialWall = kSerialSector + kSerialStep, 59 kSerialSprite = kSerialWall + kSerialStep, 60 kSerialMax = kSerialSprite + kSerialStep, 61 }; 62 63 enum ENUM_CONDITION_ERROR { 64 kErrInvalidObject = 0, 65 kErrInvalidSerial, 66 kErrUnknownObject, 67 kErrInvalidArgsPass, 68 kErrObjectUnsupp, 69 kErrNotImplementedCond, 70 }; 71 72 enum ENUM_CONDITION_TYPE { 73 CNON = 0, 74 CGAM, 75 CMIX, 76 CWAL, 77 CSEC, 78 CPLY, 79 CDUDG, 80 CDUDC, 81 CSPR, 82 }; 83 84 struct CHECKFUNC_INFO 85 { 86 char (*pFunc)( void ); // function to call the condition 87 unsigned int type : 5; // type of condition 88 const char* name; // for errors output 89 }; 90 91 struct CONDITION_INFO 92 { 93 char (*pFunc)( void ); // condition function 94 unsigned int id : 10; // condition id 95 unsigned int type : 5; // type of condition 96 unsigned int isBool : 1; // false = do comparison using Cmp() 97 unsigned int xReq : 1; // is x-object required? 98 CHECKFUNC_INFO* pCaller = NULL; // provided for easy access and must be set during init 99 }; 100 101 struct TRACKING_CONDITION 102 { 103 unsigned int id : 16; // x-sprite index of condition 104 OBJECT_LIST* objects; // a dynamic list of objects it contains 105 }; 106 107 static const char* gErrors[] = 108 { 109 "Object #%d (objType: %d) is not a %s!", 110 "%d is not condition serial!", 111 "Unknown object type %d, index %d.", 112 "Invalid arguments passed.", 113 "Unsupported %s type %d, extra: %d.", 114 "Condition is not implemented.", 115 }; 116 117 /** GLOBAL coditions table 118 ********************************************************************************/ 119 static CONDITION_INFO* gConditions = NULL; 120 static unsigned int gNumConditions = 0; 121 122 123 124 /** TRACKING(LOOPED) coditions list 125 ********************************************************************************/ 126 static TRACKING_CONDITION* gTrackingConditionsList = NULL; 127 static unsigned int gTrackingConditionsListLength = 0; 128 129 130 131 /** Variables that is relative to current condition 132 ********************************************************************************/ 133 static spritetype* pCond = NULL; static XSPRITE* pXCond = NULL; // current condition 134 static int arg1 = 0, arg2 = 0, arg3 = 0; // arguments of current condition (data2, data3, data4) 135 static int cmpOp = 0; // current comparison operator (condition sprite cstat) 136 static char PUSH = false; // current stack push status (kCmdNumberic) 137 static CONDITION_INFO* pEntry = NULL; // current condition db entry 138 static EVENT* pEvent = NULL; // current event 139 140 static PLAYER* pPlayer = NULL; // player in the focus 141 static int objType = -1, objIndex = -1; // object in the focus 142 static spritetype* pSpr = NULL; static XSPRITE* pXSpr = NULL; // (x)sprite in the focus 143 static walltype* pWall = NULL; static XWALL* pXWall = NULL; // (x)wall in the focus 144 static sectortype* pSect = NULL; static XSECTOR* pXSect = NULL; // (x)sector in the focus 145 static char xAvail = false; // x-object indicator 146 147 148 149 /** INTERFACE functions 150 ********************************************************************************/ 151 static void Unserialize(int nSerial, int* oType, int* oIndex); 152 static char Cmp(int val, int nArg1, int nArg2); 153 static int Serialize(int oType, int oIndex); 154 static void Push(int oType, int oIndex); 155 static void TriggerObject(int nSerial); 156 static void Error(const char* pFormat, ...); 157 static void ReceiveObjects(EVENT* pFrom); 158 static char Cmp(int val); 159 static char DefaultResult(); 160 static void Restore(); 161 162 static char CheckCustomDude(); 163 static char CheckGeneric(); 164 static char CheckSector(); 165 static char CheckSprite(); 166 static char CheckObject(); 167 static char CheckPlayer(); 168 static char CheckWall(); 169 static char CheckDude(); 170 171 172 173 /** A LIST OF CONDITION FUNCTION CALLERS 174 ********************************************************************************/ 175 static CHECKFUNC_INFO gCheckFuncInfo[] = 176 { 177 { CheckGeneric, CGAM, "Game" }, 178 { CheckObject, CMIX, "Mixed" }, 179 { CheckWall, CWAL, "Wall" }, 180 { CheckSector, CSEC, "Sector" }, 181 { CheckPlayer, CPLY, "Player" }, 182 { CheckDude, CDUDG, "Dude" }, 183 { CheckCustomDude, CDUDC, "Custom Dude" }, 184 { CheckSprite, CSPR, "Sprite" }, 185 { CheckGeneric, CNON, "Unknown" }, 186 }; 187 188 static int qsSortCheckFuncInfo(CHECKFUNC_INFO* ref1, CHECKFUNC_INFO* ref2) 189 { 190 return ref1->type - ref2->type; 191 } 192 193 194 195 /** ERROR functions 196 ********************************************************************************/ 197 static char errCondNotImplemented(void) 198 { 199 Error(gErrors[kErrNotImplementedCond]); 200 return false; 201 } 202 203 204 /** HELPER functions 205 ********************************************************************************/ 206 static char helperChkSprite(int nSprite) 207 { 208 if (!rngok(nSprite, 0, kMaxSprites)) return false; 209 else if (PUSH) Push(EVOBJ_SPRITE, nSprite); 210 return true; 211 } 212 213 static char helperChkSector(int nSect) 214 { 215 if (!rngok(nSect, 0, numsectors)) return false; 216 else if (PUSH) Push(EVOBJ_SECTOR, nSect); 217 return true; 218 } 219 220 static char helperChkWall(int nWall) 221 { 222 if (!rngok(nWall, 0, numwalls)) return false; 223 else if (PUSH) Push(EVOBJ_WALL, nWall); 224 return true; 225 } 226 227 static char helperCmpHeight(char ceil) 228 { 229 int nHeigh1, nHeigh2; 230 switch (pSect->type) 231 { 232 case kSectorZMotion: 233 case kSectorRotate: 234 case kSectorSlide: 235 case kSectorSlideMarked: 236 case kSectorRotateMarked: 237 case kSectorRotateStep: 238 if (ceil) 239 { 240 nHeigh1 = ClipLow(abs(pXSect->onCeilZ - pXSect->offCeilZ), 1); 241 nHeigh2 = abs(pSect->ceilingz - pXSect->offCeilZ); 242 } 243 else 244 { 245 nHeigh1 = ClipLow(abs(pXSect->onFloorZ - pXSect->offFloorZ), 1); 246 nHeigh2 = abs(pSect->floorz - pXSect->offFloorZ); 247 } 248 return Cmp((kPercFull * nHeigh2) / nHeigh1); 249 default: 250 Error(gErrors[kErrObjectUnsupp], "sector", pSect->type, pSect->extra); 251 return false; 252 } 253 } 254 255 static char helperCmpData(int nData) 256 { 257 switch (objType) 258 { 259 case EVOBJ_WALL: 260 return Cmp(pXWall->data); 261 case EVOBJ_SPRITE: 262 switch (nData) 263 { 264 case 1: return Cmp(pXSpr->data1); 265 case 2: return Cmp(pXSpr->data2); 266 case 3: return Cmp(pXSpr->data3); 267 case 4: return Cmp(pXSpr->data4); 268 } 269 break; 270 case EVOBJ_SECTOR: 271 return Cmp(pXSect->data); 272 } 273 274 return Cmp(0); 275 } 276 277 static char helperCmpSeq(int (*pFunc) (int, int)) 278 { 279 switch (objType) 280 { 281 case EVOBJ_SPRITE: 282 return Cmp(pFunc(3, pSpr->extra)); 283 case EVOBJ_WALL: 284 switch (arg3) 285 { 286 default: return Cmp(pFunc(0, pWall->extra)) || Cmp(pFunc(4, pWall->extra)); 287 case 1: return Cmp(pFunc(0, pWall->extra)); 288 case 2: return Cmp(pFunc(4, pWall->extra)); // masked wall 289 } 290 break; 291 case EVOBJ_SECTOR: 292 switch (arg3) 293 { 294 default: return Cmp(pFunc(1, pSect->extra)) || Cmp(pFunc(2, pSect->extra)); 295 case 1: return Cmp(pFunc(1, pSect->extra)); 296 case 2: return Cmp(pFunc(2, pSect->extra)); 297 } 298 break; 299 } 300 301 return Cmp(0); 302 303 } 304 305 static char helperChkHitscan(int nWhat) 306 { 307 int nAng = pSpr->ang & kAngMask; 308 int nOldStat = pSpr->cstat; 309 int nHit = -1, nSlope = 0; 310 int nMask = -1; 311 312 if ((nOldStat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLOPE) 313 nSlope = spriteGetSlope(pSpr->index); 314 315 pSpr->cstat = 0; 316 if (nOldStat & CSTAT_SPRITE_YCENTER) 317 pSpr->cstat |= CSTAT_SPRITE_YCENTER; 318 319 if ((nOldStat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_FLOOR) 320 pSpr->cstat |= CSTAT_SPRITE_ALIGNMENT_FLOOR; 321 322 switch (arg1) 323 { 324 case 0: nMask = CLIPMASK0 | CLIPMASK1; break; 325 case 1: nMask = CLIPMASK0; break; 326 case 2: nMask = CLIPMASK1; break; 327 } 328 329 if ((pPlayer = getPlayerById(pSpr->type)) != NULL) 330 { 331 nHit = HitScan(pSpr, pPlayer->zWeapon, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, nMask, arg3 << 1); 332 } 333 else if (IsDudeSprite(pSpr)) 334 { 335 nHit = HitScan(pSpr, pSpr->z, Cos(nAng) >> 16, Sin(nAng) >> 16, gDudeSlope[pSpr->extra], nMask, arg3 << 1); 336 } 337 else if ((nOldStat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLOPE) 338 { 339 nSlope = (nOldStat & CSTAT_SPRITE_YFLIP) ? (0x08000 - abs(nSlope)) : -(0x08000 - abs(nSlope)); 340 nHit = HitScan(pSpr, pSpr->z, Cos(nAng) >> 16, Sin(nAng) >> 16, nSlope, nMask, arg3 << 1); 341 } 342 else if ((nOldStat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_FLOOR) 343 { 344 nSlope = (nOldStat & CSTAT_SPRITE_YFLIP) ? (0x10000) : -(0x10000); 345 nHit = HitScan(pSpr, pSpr->z, Cos(nAng) >> 16, Sin(nAng) >> 16, nSlope, nMask, arg3 << 1); 346 } 347 else 348 { 349 nHit = HitScan(pSpr, pSpr->z, Cos(nAng) >> 16, Sin(nAng) >> 16, 0, nMask, arg3 << 1); 350 } 351 352 pSpr->cstat = nOldStat; 353 354 if (nHit < 0) 355 return false; 356 357 switch (nWhat) 358 { 359 case 1: // ceil 360 if (nHit != 1) return false; 361 else if (PUSH) Push(EVOBJ_SECTOR, gHitInfo.hitsect); 362 return true; 363 case 2: // floor 364 if (nHit != 2) return false; 365 else if (PUSH) Push(EVOBJ_SECTOR, gHitInfo.hitsect); 366 return true; 367 case 3: // wall 368 if (nHit != 0 && nHit != 4) return false; 369 else if (PUSH) Push(EVOBJ_WALL, gHitInfo.hitwall); 370 return true; 371 case 4: // sprite 372 if (nHit != 3) return false; 373 else if (PUSH) Push(EVOBJ_SPRITE, gHitInfo.hitsprite); 374 return true; 375 case 5: // masked wall 376 if (nHit != 4) return false; 377 else if (PUSH) Push(EVOBJ_WALL, gHitInfo.hitwall); 378 return true; 379 } 380 381 Error(gErrors[kErrInvalidArgsPass]); 382 return false; 383 } 384 385 static char helperChkMarker(int nWhat) 386 { 387 int t; 388 if (pXSpr->dudeFlag4 && spriRangeIsFine(pXSpr->target) && sprite[pXSpr->target].type == kMarkerPath) 389 { 390 switch (nWhat) { 391 case 1: 392 if ((t = aiPatrolMarkerBusy(pSpr->index, pXSpr->target)) >= 0) 393 { 394 if (PUSH) Push(EVOBJ_SPRITE, t); 395 return true; 396 } 397 break; 398 case 2: 399 if (aiPatrolMarkerReached(pSpr, pXSpr)) 400 { 401 if (PUSH) Push(EVOBJ_SPRITE, pXSpr->target); 402 return true; 403 } 404 break; 405 default: 406 Error(gErrors[kErrInvalidArgsPass]); 407 break; 408 } 409 } 410 411 return false; 412 }; 413 414 static char helperChkTarget(int nWhat) 415 { 416 int t = 0; 417 if (spriRangeIsFine(pXSpr->target)) 418 { 419 spritetype* pTrgt = &sprite[pXSpr->target]; DUDEINFO* pInfo = getDudeInfo(pSpr->type); 420 int eyeAboveZ = pInfo->eyeHeight * pSpr->yrepeat << 2; 421 int dx = pTrgt->x - pSpr->x; int dy = pTrgt->y - pSpr->y; 422 423 switch (nWhat) { 424 case 1: 425 arg1 *= 512, arg2 *= 512; 426 t = Cmp(approxDist(dx, dy)); 427 break; 428 case 2: 429 case 3: 430 t = cansee(pSpr->x, pSpr->y, pSpr->z, pSpr->sectnum, pTrgt->x, pTrgt->y, pTrgt->z - eyeAboveZ, pTrgt->sectnum); 431 if (t > 0 && nWhat == 3) 432 { 433 t = ((1024 + getangle(dx, dy) - pSpr->ang) & 2047) - 1024; 434 t = (klabs(t) < ((arg1 <= 0) ? pInfo->periphery : ClipHigh(arg1, 2048))); 435 } 436 break; 437 default: 438 Error(gErrors[kErrInvalidArgsPass]); 439 break; 440 } 441 } 442 443 if (t <= 0) return false; 444 else if (PUSH) Push(EVOBJ_SPRITE, pXSpr->target); 445 return true; 446 }; 447 448 static char helperChkRoom(short* pArray, int nWhat) 449 { 450 int t = pArray[objIndex]; 451 if (t >= 0) 452 { 453 if (PUSH) 454 { 455 switch (nWhat) 456 { 457 case 1: 458 Push(EVOBJ_SPRITE, t); 459 break; 460 case 2: 461 t = sprite[t].owner & 0x3fff; 462 Push(EVOBJ_SPRITE, t); 463 break; 464 default: 465 t = sprite[t].owner & 0x3fff; 466 Push(EVOBJ_SECTOR, sprite[t].sectnum); 467 break; 468 } 469 } 470 471 return true; 472 } 473 474 return false; 475 } 476 477 478 /** GAME conditions 479 ********************************************************************************/ 480 static char gamCmpLevelMin(void) { return Cmp(gLevelTime / (kTicsPerSec * 60)); } 481 static char gamCmpLevelSec(void) { return Cmp((gLevelTime / kTicsPerSec) % 60); } 482 static char gamCmpLevelMsec(void) { return Cmp(((gLevelTime % kTicsPerSec) * 33) / 10); } 483 static char gamCmpLevelTime(void) { return Cmp(gLevelTime); } 484 static char gamCmpKillsTotal(void) { return Cmp(gKillMgr.at0); } 485 static char gamCmpKillsDone(void) { return Cmp(gKillMgr.at4); } 486 static char gamCmpSecretsTotal(void) { return Cmp(gSecretMgr.nAllSecrets); } 487 static char gamCmpSecretsDone(void) { return Cmp(gSecretMgr.nNormalSecretsFound); } 488 static char gamCmpVisibility(void) { return Cmp(gVisibility); } 489 static char gamChkGotpic(void) { return (rngok(arg3, 0, kMaxTiles) && TestBitString(gotpic, arg3)); } 490 static char gamChkChance(void) { return Chance((0x10000 * arg3) / kPercFull); } 491 static char gamCmpRandom(void) { return Cmp(nnExtRandom(arg1, arg2)); } 492 static char gamCmpStatnumCount(void) { return Cmp(gStatCount[ClipRange(arg3, 0, kMaxStatus)]); } 493 static char gamCmpNumsprites(void) { return Cmp(Numsprites); } 494 static char gamChkPlayerConnect(void) 495 { 496 int i; 497 for (i = connecthead; i >= 0; i = connectpoint2[i]) 498 { 499 if (gPlayer[i].nPlayer + 1 != arg3) continue; 500 else if (PUSH) Push(EVOBJ_SPRITE, gPlayer[i].nSprite); 501 return true; 502 } 503 504 return false; 505 } 506 static char gamChkSector(void) { return helperChkSector(arg1); } 507 static char gamChkWall(void) { return helperChkWall(arg1); } 508 static char gamChkSprite(void) { return helperChkSprite(arg1); } 509 510 511 512 /** SECTOR conditions 513 ********************************************************************************/ 514 static char sectCmpVisibility(void) { return Cmp(pSect->visibility); } 515 static char sectChkShow2dSector(void) { return TestBitString(show2dsector, objIndex); } 516 static char sectChkGotSector(void) { return TestBitString(gotsector, objIndex); } 517 static char sectCmpFloorSlope(void) { return Cmp(pSect->floorheinum); } 518 static char sectCmpCeilSlope(void) { return Cmp(pSect->ceilingheinum); } 519 static char sectChkSprTypeInSect(void) 520 { 521 int i; 522 for (i = headspritesect[objIndex]; i >= 0; i = nextspritesect[i]) 523 { 524 if (!Cmp(sprite[i].type)) continue; 525 else if (PUSH) Push(EVOBJ_SPRITE, i); 526 return true; 527 } 528 return false; 529 } 530 531 static char sectCmpFloorHeight(void) { return helperCmpHeight(false); } 532 static char sectCmpCeilHeight(void) { return helperCmpHeight(true); } 533 static char sectChkUnderwater(void) { return pXSect->Underwater; } 534 static char sectChkPaused(void) { return !pXSect->unused1; } 535 static char sectCmpDepth(void) { return Cmp(pXSect->Depth); } 536 static char sectChkUpperRoom(void) { return helperChkRoom(gUpperLink, arg3); } 537 static char sectChkLowerRoom(void) { return helperChkRoom(gLowerLink, arg3); } 538 539 540 541 /** WALL conditions 542 ********************************************************************************/ 543 static char wallCmpOverpicnum(void) { return Cmp(pWall->overpicnum); } 544 static char wallChkShow2dWall(void) { return TestBitString(show2dwall, objIndex); } 545 static char wallChkIsMirror(void) 546 { 547 #if 0 548 // new ROR code 549 int i = mirrorcnt; 550 while (--i >= 0) 551 { 552 if (mirror[i].type == 0 && mirror[i].id == objIndex) 553 return true; 554 } 555 #else 556 return (pWall->type != kWallStack && Cmp(pWall->picnum, 4080, (4080 + 16) - 1)); 557 #endif 558 } 559 560 static char wallChkSector(void) { return helperChkSector(sectorofwall(objIndex)); } 561 static char wallChkNextSector(void) { return helperChkSector(pWall->nextsector); } 562 static char wallChkNextWallSector(void) { return helperChkSector(sectorofwall(pWall->nextwall)); } 563 static char wallChkNextWall(void) { return helperChkWall(pWall->nextwall); } 564 static char wallChkPoint2(void) { return helperChkWall(pWall->point2); } 565 566 567 568 569 570 /** MIXED OBJECT conditions 571 ********************************************************************************/ 572 static char mixChkObjSect(void) { return (objType == EVOBJ_SECTOR && sectRangeIsFine(objIndex)); } 573 static char mixChkObjWall(void) { return (objType == EVOBJ_WALL && wallRangeIsFine(objIndex)); } 574 static char mixChkObjSpr(void) { return (objType == EVOBJ_SPRITE && spriRangeIsFine(objIndex)); } 575 static char mixChkXRange(void) 576 { 577 switch (objType) 578 { 579 case EVOBJ_WALL: return xwallRangeIsFine(pWall->extra); 580 case EVOBJ_SPRITE: return xspriRangeIsFine(pSpr->extra); 581 case EVOBJ_SECTOR: return xsectRangeIsFine(pSect->extra); 582 } 583 584 return false; 585 } 586 /**---------------------------------------------------------------------------**/ 587 static char mixCmpLotag(void) 588 { 589 switch (objType) 590 { 591 case EVOBJ_WALL: return Cmp(pWall->type); 592 case EVOBJ_SPRITE: return Cmp(pSpr->type); 593 case EVOBJ_SECTOR: return Cmp(pSect->type); 594 } 595 596 return Cmp(0); 597 } 598 /**---------------------------------------------------------------------------**/ 599 static char mixCmpPicSurface(void) 600 { 601 switch (objType) 602 { 603 case EVOBJ_WALL: return Cmp(surfType[pWall->picnum]); 604 case EVOBJ_SPRITE: return Cmp(surfType[pSpr->picnum]); 605 case EVOBJ_SECTOR: 606 switch (arg3) 607 { 608 default: return (Cmp(surfType[pSect->floorpicnum]) || Cmp(surfType[pSect->ceilingpicnum])); 609 case 1: return Cmp(surfType[pSect->floorpicnum]); 610 case 2: return Cmp(surfType[pSect->ceilingpicnum]); 611 } 612 } 613 614 return Cmp(0); 615 } 616 /**---------------------------------------------------------------------------**/ 617 static char mixCmpPic(void) 618 { 619 switch (objType) 620 { 621 case EVOBJ_WALL: return Cmp(pWall->picnum); 622 case EVOBJ_SPRITE: return Cmp(pSpr->picnum); 623 case EVOBJ_SECTOR: 624 switch (arg3) 625 { 626 default: return (Cmp(pSect->floorpicnum) || Cmp(pSect->ceilingpicnum)); 627 case 1: return Cmp(pSect->floorpicnum); 628 case 2: return Cmp(pSect->ceilingpicnum); 629 } 630 } 631 632 return Cmp(0); 633 } 634 /**---------------------------------------------------------------------------**/ 635 static char mixCmpPal(void) 636 { 637 switch (objType) 638 { 639 case EVOBJ_WALL: return Cmp(pWall->pal); 640 case EVOBJ_SPRITE: return Cmp(pSpr->pal); 641 case EVOBJ_SECTOR: 642 switch (arg3) 643 { 644 default: return (Cmp(pSect->floorpal) || Cmp(pSect->ceilingpal)); 645 case 1: return Cmp(pSect->floorpal); 646 case 2: return Cmp(pSect->ceilingpal); 647 } 648 } 649 650 return Cmp(0); 651 } 652 /**---------------------------------------------------------------------------**/ 653 static char mixCmpShade(void) 654 { 655 switch (objType) 656 { 657 case EVOBJ_WALL: return Cmp(pWall->shade); 658 case EVOBJ_SPRITE: return Cmp(pSpr->shade); 659 case EVOBJ_SECTOR: 660 switch (arg3) 661 { 662 default: return (Cmp(pSect->floorshade) || Cmp(pSect->ceilingshade)); 663 case 1: return Cmp(pSect->floorshade); 664 case 2: return Cmp(pSect->ceilingshade); 665 } 666 } 667 668 return Cmp(0); 669 } 670 /**---------------------------------------------------------------------------**/ 671 static char mixCmpCstat(void) 672 { 673 switch (objType) 674 { 675 case EVOBJ_WALL: return (bool)((arg3) ? Cmp(pWall->cstat & arg3) : (pWall->cstat & arg1)); 676 case EVOBJ_SPRITE: return (bool)((arg3) ? Cmp(pSpr->cstat & arg3) : (pSpr->cstat & arg1)); 677 case EVOBJ_SECTOR: // !!! 678 switch (arg3) 679 { 680 default: return ((pSect->floorstat & arg1) || (pSect->ceilingstat & arg1)); 681 case 1: return (pSect->floorstat & arg1); 682 case 2: return (pSect->ceilingstat & arg1); 683 } 684 } 685 686 return Cmp(0); 687 } 688 /**---------------------------------------------------------------------------**/ 689 static char mixCmpHitag(void) 690 { 691 switch (objType) 692 { 693 case EVOBJ_WALL: return (bool)((arg3) ? Cmp(pWall->hitag & arg3) : (pWall->hitag & arg1)); 694 case EVOBJ_SPRITE: return (bool)((arg3) ? Cmp(pSpr->hitag & arg3) : (pSpr->hitag & arg1)); 695 case EVOBJ_SECTOR: return (bool)((arg3) ? Cmp(pSect->hitag & arg3) : (pSect->hitag & arg1)); 696 } 697 698 return Cmp(0); 699 } 700 /**---------------------------------------------------------------------------**/ 701 static char mixCmpXrepeat(void) 702 { 703 switch (objType) 704 { 705 case EVOBJ_WALL: return Cmp(pWall->xrepeat); 706 case EVOBJ_SPRITE: return Cmp(pSpr->xrepeat); 707 case EVOBJ_SECTOR: return Cmp(pSect->floorxpanning); 708 } 709 710 return Cmp(0); 711 } 712 /**---------------------------------------------------------------------------**/ 713 static char mixCmpXoffset(void) 714 { 715 switch (objType) 716 { 717 case EVOBJ_WALL: return Cmp(pWall->xpanning); 718 case EVOBJ_SPRITE: return Cmp(pSpr->xoffset); 719 case EVOBJ_SECTOR: return Cmp(pSect->ceilingxpanning); 720 } 721 722 return Cmp(0); 723 } 724 /**---------------------------------------------------------------------------**/ 725 static char mixCmpYrepeat(void) 726 { 727 switch (objType) 728 { 729 case EVOBJ_WALL: return Cmp(pWall->yrepeat); 730 case EVOBJ_SPRITE: return Cmp(pSpr->yrepeat); 731 case EVOBJ_SECTOR: return Cmp(pSect->floorypanning); 732 } 733 734 return Cmp(0); 735 } 736 /**---------------------------------------------------------------------------**/ 737 static char mixCmpYoffset(void) 738 { 739 switch (objType) 740 { 741 case EVOBJ_WALL: return Cmp(pWall->ypanning); 742 case EVOBJ_SPRITE: return Cmp(pSpr->yoffset); 743 case EVOBJ_SECTOR: return Cmp(pSect->ceilingypanning); 744 } 745 746 return Cmp(0); 747 } 748 /**---------------------------------------------------------------------------**/ 749 static char mixCmpData1(void) { return helperCmpData(1); } 750 static char mixCmpData2(void) { return helperCmpData(2); } 751 static char mixCmpData3(void) { return helperCmpData(3); } 752 static char mixCmpData4(void) { return helperCmpData(4); } 753 static char mixCmpRXId(void) 754 { 755 switch (objType) 756 { 757 case EVOBJ_WALL: return Cmp(pXWall->rxID); 758 case EVOBJ_SPRITE: return Cmp(pXSpr->rxID); 759 case EVOBJ_SECTOR: return Cmp(pXSect->rxID); 760 } 761 762 return Cmp(0); 763 } 764 /**---------------------------------------------------------------------------**/ 765 static char mixCmpTXId(void) 766 { 767 switch (objType) 768 { 769 case EVOBJ_WALL: return Cmp(pXWall->txID); 770 case EVOBJ_SPRITE: return Cmp(pXSpr->txID); 771 case EVOBJ_SECTOR: return Cmp(pXSect->txID); 772 } 773 774 return Cmp(0); 775 } 776 /**---------------------------------------------------------------------------**/ 777 static char mixChkLock(void) 778 { 779 switch (objType) 780 { 781 case EVOBJ_WALL: return pXWall->locked; 782 case EVOBJ_SPRITE: return pXSpr->locked; 783 case EVOBJ_SECTOR: return pXSect->locked; 784 } 785 786 return false; 787 } 788 /**---------------------------------------------------------------------------**/ 789 static char mixChkTriggerOn(void) 790 { 791 switch (objType) 792 { 793 case EVOBJ_WALL: return pXWall->triggerOn; 794 case EVOBJ_SPRITE: return pXSpr->triggerOn; 795 case EVOBJ_SECTOR: return pXSect->triggerOn; 796 } 797 798 return false; 799 } 800 /**---------------------------------------------------------------------------**/ 801 static char mixChkTriggerOff(void) 802 { 803 switch (objType) 804 { 805 case EVOBJ_WALL: return pXWall->triggerOff; 806 case EVOBJ_SPRITE: return pXSpr->triggerOff; 807 case EVOBJ_SECTOR: return pXSect->triggerOff; 808 } 809 810 return false; 811 } 812 /**---------------------------------------------------------------------------**/ 813 static char mixChkTriggerOnce(void) 814 { 815 switch (objType) 816 { 817 case EVOBJ_WALL: return pXWall->triggerOnce; 818 case EVOBJ_SPRITE: return pXSpr->triggerOnce; 819 case EVOBJ_SECTOR: return pXSect->triggerOnce; 820 } 821 822 return false; 823 } 824 /**---------------------------------------------------------------------------**/ 825 static char mixChkIsTriggered(void) 826 { 827 switch (objType) 828 { 829 case EVOBJ_WALL: return pXWall->isTriggered; 830 case EVOBJ_SPRITE: return pXSpr->isTriggered; 831 case EVOBJ_SECTOR: return pXSect->isTriggered; 832 } 833 834 return false; 835 } 836 /**---------------------------------------------------------------------------**/ 837 static char mixChkState(void) 838 { 839 switch (objType) 840 { 841 case EVOBJ_WALL: return pXWall->state; 842 case EVOBJ_SPRITE: return pXSpr->state; 843 case EVOBJ_SECTOR: return pXSect->state; 844 } 845 846 return false; 847 } 848 /**---------------------------------------------------------------------------**/ 849 static char mixCmpBusy(void) 850 { 851 switch (objType) 852 { 853 case EVOBJ_WALL: return Cmp((kPercFull * pXWall->busy) / 65536); 854 case EVOBJ_SPRITE: return Cmp((kPercFull * pXSpr->busy) / 65536); 855 case EVOBJ_SECTOR: return Cmp((kPercFull * pXSect->busy) / 65536); 856 } 857 858 return Cmp(0); 859 } 860 /**---------------------------------------------------------------------------**/ 861 static char mixChkPlayerOnly(void) 862 { 863 switch (objType) 864 { 865 case EVOBJ_WALL: return pXWall->dudeLockout; 866 case EVOBJ_SPRITE: return pXSpr->DudeLockout; 867 case EVOBJ_SECTOR: return pXSect->dudeLockout; 868 } 869 870 return false; 871 } 872 /**---------------------------------------------------------------------------**/ 873 static char mixCmpSeqID(void) { return helperCmpSeq(seqGetID); } 874 static char mixCmpSeqFrame(void) { return helperCmpSeq(seqGetStatus); } 875 static char mixCmpObjIndex(void) { return Cmp(objIndex); } 876 static char mixCmpObjXIndex(void) 877 { 878 switch (objType) 879 { 880 case EVOBJ_WALL: return Cmp(pWall->extra); 881 case EVOBJ_SPRITE: return Cmp(pSpr->extra); 882 case EVOBJ_SECTOR: return Cmp(pSect->extra); 883 } 884 885 return Cmp(0); 886 } 887 888 889 static char mixCmpSerials(void) 890 { 891 unsigned int i = 0, d; 892 int serials[kPushRange + 1] = { pXCond->targetX, pXCond->targetY, pXCond->targetZ, pXCond->sysData1 }; 893 int t[3]; 894 895 while (i < LENGTH(t)) 896 { 897 d = getDigitFromValue(arg1, i); 898 if (!rngok(d, 1, LENGTH(serials))) 899 { 900 Error(gErrors[kErrInvalidArgsPass]); 901 return false; 902 } 903 904 t[i++] = serials[d - 1]; 905 } 906 907 return Cmp(t[0], t[1], t[2]); 908 909 } 910 static char mixChkEventCauser(void) { return helperChkSprite(pEvent->causer); } 911 static char mixCmpEventCmd(void) { return Cmp(pEvent->cmd); } 912 913 914 915 916 917 /** SPRITE conditions 918 ********************************************************************************/ 919 static char sprCmpAng(void) { return Cmp((arg3 == 0) ? (pSpr->ang & 2047) : pSpr->ang); } 920 static char sprChkShow2dSprite(void) { return TestBitString(show2dsprite, objIndex); } 921 static char sprCmpStatnum(void) { return Cmp(pSpr->statnum); } 922 static char sprChkRespawn(void) { return ((pSpr->flags & kHitagRespawn) || pSpr->statnum == kStatRespawn); } 923 static char sprCmpSlope(void) { return Cmp(spriteGetSlope(pSpr->index)); } 924 static char sprCmpClipdist(void) { return Cmp(pSpr->clipdist); } 925 static char sprChkOwner(void) { return helperChkSprite(pSpr->owner); } 926 static char sprChkSector(void) { return helperChkSector(pSpr->sectnum); } 927 static char sprCmpVelocityNew(void) 928 { 929 switch (arg3) 930 { 931 case 0: return (Cmp(xvel[pSpr->index]) || Cmp(yvel[pSpr->index]) || Cmp(zvel[pSpr->index])); 932 case 1: return Cmp(xvel[pSpr->index]); 933 case 2: return Cmp(yvel[pSpr->index]); 934 case 3: return Cmp(zvel[pSpr->index]); 935 } 936 937 Error(gErrors[kErrInvalidArgsPass]); 938 return false; 939 } 940 static char sprCmpChkVelocity(void) 941 { 942 if (arg3) 943 return sprCmpVelocityNew(); 944 945 switch (arg1) 946 { 947 case 0: return (xvel[pSpr->index] != 0 || yvel[pSpr->index] != 0 || zvel[pSpr->index] != 0); 948 case 1: return (xvel[pSpr->index] != 0); 949 case 2: return (yvel[pSpr->index] != 0); 950 case 3: return (zvel[pSpr->index] != 0); 951 } 952 953 Error(gErrors[kErrInvalidArgsPass]); 954 return false; 955 } 956 957 /**---------------------------------------------------------------------------**/ 958 static char sprChkUnderwater(void) { return isUnderwaterSector(pSpr->sectnum); } 959 static char sprChkDmgImmune(void) 960 { 961 int i; 962 if (arg1 == -1) 963 { 964 for (i = 0; i < kDmgMax; i++) 965 { 966 if (!nnExtIsImmune(pSpr, i, 0)) 967 return false; 968 } 969 970 return true; 971 } 972 973 return nnExtIsImmune(pSpr, arg1, 0); 974 } 975 /**---------------------------------------------------------------------------**/ 976 static char sprChkHitscanCeil(void) { return helperChkHitscan(1); } 977 static char sprChkHitscanFloor(void) { return helperChkHitscan(2); } 978 static char sprChkHitscanWall(void) { return helperChkHitscan(3); } 979 static char sprChkHitscanSpr(void) { return helperChkHitscan(4); } 980 static char sprChkHitscanMasked(void) { return helperChkHitscan(5); } 981 static char sprChkIsTarget(void) 982 { 983 int i; 984 for (i = headspritestat[kStatDude]; i >= 0; i = nextspritestat[i]) 985 { 986 if (pSpr->index == i) 987 continue; 988 989 spritetype* pDude = &sprite[i]; 990 if (IsDudeSprite(pDude) && xspriRangeIsFine(pDude->extra)) 991 { 992 XSPRITE* pXDude = &xsprite[pDude->extra]; 993 if (pXDude->health <= 0 || pXDude->target != pSpr->index) continue; 994 else if (PUSH) Push(EVOBJ_SPRITE, i); 995 return true; 996 } 997 } 998 return false; 999 } 1000 /**---------------------------------------------------------------------------**/ 1001 static char sprCmpHealth(void) 1002 { 1003 int t = 0; 1004 if (IsDudeSprite(pSpr)) 1005 t = (pXSpr->sysData2 > 0) ? ClipHigh(pXSpr->sysData2 << 4, 65535) : getDudeInfo(pSpr->type)->startHealth << 4; 1006 else if (pSpr->type == kThingBloodChunks) 1007 return Cmp(0); 1008 else if (IsThingSprite(pSpr)) 1009 t = thingInfo[pSpr->type - kThingBase].startHealth << 4; 1010 1011 t = (kPercFull * pXSpr->health) / ClipLow(t, 1); 1012 return Cmp((!t && pXSpr->health) ? 1 : t); 1013 } 1014 /**---------------------------------------------------------------------------**/ 1015 static char sprChkTouchCeil(void) 1016 { 1017 if ((gSpriteHit[pSpr->extra].ceilhit & 0xc000) != 0x4000) return false; 1018 else if (PUSH) Push(EVOBJ_SECTOR, gSpriteHit[pSpr->extra].ceilhit & 0x3fff); 1019 return true; 1020 } 1021 /**---------------------------------------------------------------------------**/ 1022 static char sprChkTouchFloor(void) 1023 { 1024 if ((gSpriteHit[pSpr->extra].florhit & 0xc000) != 0x4000) return false; 1025 else if (PUSH) Push(EVOBJ_SECTOR, gSpriteHit[pSpr->extra].florhit & 0x3fff); 1026 return true; 1027 } 1028 /**---------------------------------------------------------------------------**/ 1029 static char sprChkTouchWall(void) 1030 { 1031 if ((gSpriteHit[pSpr->extra].hit & 0xc000) != 0x8000) return false; 1032 else if (PUSH) Push(EVOBJ_WALL, gSpriteHit[pSpr->extra].hit & 0x3fff); 1033 return true; 1034 } 1035 /**---------------------------------------------------------------------------**/ 1036 static char sprChkTouchSpite(void) 1037 { 1038 int id = -1; 1039 SPRITEHIT* pHit; 1040 1041 if (xAvail) 1042 { 1043 pHit = &gSpriteHit[pSpr->extra]; 1044 switch (arg3) 1045 { 1046 case 0: 1047 case 1: 1048 if ((pHit->florhit & 0xc000) == 0xc000) id = pHit->florhit & 0x3fff; 1049 if (arg3 || id >= 0) break; 1050 fallthrough__; 1051 case 2: 1052 if ((pHit->hit & 0xc000) == 0xc000) id = pHit->hit & 0x3fff; 1053 if (arg3 || id >= 0) break; 1054 fallthrough__; 1055 case 3: 1056 if ((pHit->ceilhit & 0xc000) == 0xc000) id = pHit->ceilhit & 0x3fff; 1057 break; 1058 } 1059 } 1060 1061 // check if something touching this sprite 1062 if (id < 0 && sectRangeIsFine(pSpr->sectnum)) 1063 { 1064 for (id = headspritesect[pSpr->sectnum]; id >= 0; id = nextspritesect[id]) 1065 { 1066 if (sprite[id].extra <= 0) 1067 continue; 1068 1069 pHit = &gSpriteHit[sprite[id].extra]; 1070 if (arg3 == 1 || !arg3) 1071 { 1072 if ((pHit->ceilhit & 0xc000) == 0xc000 && (pHit->ceilhit & 0x3fff) == objIndex) 1073 break; 1074 } 1075 1076 if (arg3 == 2 || !arg3) 1077 { 1078 if ((pHit->hit & 0xc000) == 0xc000 && (pHit->hit & 0x3fff) == objIndex) 1079 break; 1080 } 1081 1082 if (arg3 == 3 || !arg3) 1083 { 1084 if ((pHit->florhit & 0xc000) == 0xc000 && (pHit->florhit & 0x3fff) == objIndex) 1085 break; 1086 } 1087 } 1088 } 1089 1090 if (id < 0) return false; 1091 else if (PUSH) Push(EVOBJ_SPRITE, id); 1092 return true; 1093 } 1094 /**---------------------------------------------------------------------------**/ 1095 static char sprCmpBurnTime(void) 1096 { 1097 int t = (IsDudeSprite(pSpr)) ? 2400 : 1200; 1098 if (!Cmp((kPercFull * pXSpr->burnTime) / t)) return false; 1099 else if (PUSH && spriRangeIsFine(pXSpr->burnSource)) Push(EVOBJ_SPRITE, pXSpr->burnSource); 1100 return true; 1101 } 1102 /**---------------------------------------------------------------------------**/ 1103 static char sprChkIsFlareStuck(void) 1104 { 1105 int i; 1106 for (i = headspritestat[kStatFlare]; i >= 0; i = nextspritestat[i]) 1107 { 1108 spritetype* pFlare = &sprite[i]; 1109 if (!xspriRangeIsFine(pFlare->extra) || (pFlare->flags & kHitagFree)) 1110 continue; 1111 1112 XSPRITE* pXFlare = &xsprite[pFlare->extra]; 1113 if (pXFlare->target != objIndex) continue; 1114 else if (PUSH) Push(EVOBJ_SPRITE, i); 1115 return true; 1116 } 1117 1118 return false; 1119 } 1120 static char sprCmpMass(void) { return Cmp(getSpriteMassBySize(pSpr)); } 1121 static char sprChkTarget(void) { return helperChkSprite(pXSpr->target); } 1122 1123 1124 1125 1126 /** GLOBAL DUDE functions 1127 ********************************************************************************/ 1128 static char gdudChkHaveTargets(void) 1129 { 1130 if (!spriRangeIsFine(pXSpr->target)) return false; 1131 else if (!IsDudeSprite(&sprite[pXSpr->target]) && sprite[pXSpr->target].type != kMarkerPath) return false; 1132 else if (PUSH) Push(EVOBJ_SPRITE, pXSpr->target); 1133 return true; 1134 }; 1135 static char gdudChkInAiFight(void) { return aiFightDudeIsAffected(pXSpr); }; 1136 static char gdudChkTargetDistance(void) { return helperChkTarget(1); }; 1137 static char gdudChkTargetCansee(void) { return helperChkTarget(2); }; 1138 static char gdudChkTargetCanseePerip(void) { return helperChkTarget(3); }; 1139 static char gdudChkFlagPatrol(void) { return pXSpr->dudeFlag4; }; 1140 static char gdudChkFlagDeaf(void) { return pXSpr->dudeDeaf; }; 1141 static char gdudChkFlagBlind(void) { return pXSpr->dudeGuard; }; 1142 static char gdudChkFlagAlarm(void) { return pXSpr->dudeAmbush; }; 1143 static char gdudChkFlagStealth(void) { return ((pXSpr->unused1 & kDudeFlagStealth) != 0); }; 1144 static char gdudChkMarkerBusy(void) { return helperChkMarker(1); }; 1145 static char gdudChkMarkerReached(void) { return helperChkMarker(2); }; 1146 static char gdudCmpSpotProgress(void) 1147 { 1148 if (!pXSpr->dudeFlag4 || !spriRangeIsFine(pXSpr->target) || sprite[pXSpr->target].type != kMarkerPath) return Cmp(0); 1149 else if (!(pXSpr->unused1 & kDudeFlagStealth) || !valueIsBetween(pXSpr->data3, 0, kMaxPatrolSpotValue)) return Cmp(0); 1150 else return Cmp((kPercFull * pXSpr->data3) / kMaxPatrolSpotValue); 1151 }; 1152 static char gdudChkLockout(void) { return getDudeInfo(pSpr->type)->lockOut; }; 1153 static char gdudCmpAiStateType(void) { return Cmp(pXSpr->aiState->stateType); }; 1154 static char gdudCmpAiStateTimer(void) { return Cmp(pXSpr->stateTimer); }; 1155 1156 1157 1158 1159 1160 /** CUSTOM DUDE functions 1161 ********************************************************************************/ 1162 static char cdudChkLeechThrown(void) 1163 { 1164 CUSTOMDUDE* pDude = cdudeGet(pSpr); 1165 if (!pDude->IsLeechBroken() && pDude->pXLeech) 1166 return helperChkSprite(pDude->pXLeech->reference); 1167 1168 return false; 1169 }; 1170 static char cdudChkLeechDead(void) 1171 { 1172 CUSTOMDUDE* pDude = cdudeGet(pSpr); 1173 if (pDude->IsLeechBroken()) return true; 1174 else if (PUSH && pDude->pXLeech) Push(EVOBJ_SPRITE, pDude->pXLeech->reference); 1175 return false; 1176 }; 1177 static char cdudCmpSummoned(void) 1178 { 1179 IDLIST* pSlaves = cdudeGet(pSpr)->slaves.list; 1180 if (!pSlaves) 1181 return Cmp(0); 1182 1183 return Cmp(pSlaves->Length()); 1184 }; 1185 static char cdudChkIfAble(void) 1186 { 1187 switch (arg3) 1188 { 1189 case 1: return false; 1190 case 2: return cdudeGet(pSpr)->CanBurn(); 1191 case 3: return cdudeGet(pSpr)->CanCrouch(); 1192 case 4: return cdudeGet(pSpr)->CanElectrocute(); 1193 case 5: return false; 1194 case 6: return cdudeGet(pSpr)->CanRecoil(); 1195 case 7: return cdudeGet(pSpr)->CanSwim(); 1196 case 8: return cdudeGet(pSpr)->CanMove(); 1197 default: 1198 Error(gErrors[kErrInvalidArgsPass]); 1199 break; 1200 } 1201 1202 return false; 1203 }; 1204 static char cdudCmpDispersion(void) 1205 { 1206 CUSTOMDUDE_WEAPON* pWeapon = cdudeGet(pSpr)->pWeapon; 1207 if (!pWeapon) 1208 return Cmp(0); 1209 1210 return Cmp(pWeapon->dispersion[0]); 1211 }; 1212 1213 1214 1215 1216 1217 /** PLAYER functions 1218 ********************************************************************************/ 1219 static char plyCmpConnected(void) 1220 { 1221 if (!Cmp(pPlayer->nPlayer + 1)) return false; 1222 else return helperChkSprite(pPlayer->nSprite); 1223 } 1224 static char plyCmpTeam(void) { return Cmp(pPlayer->teamId + 1); } 1225 static char plyChkHaveKey(void) { return (valueIsBetween(arg1, 0, 8) && pPlayer->hasKey[arg1 - 1]); } 1226 static char plyChkHaveWeapon(void) { return (valueIsBetween(arg1, 0, 15) && pPlayer->hasWeapon[arg1 - 1]); } 1227 static char plyCmpCurWeapon(void) { return Cmp(pPlayer->curWeapon); } 1228 static char plyCmpPackItemAmount(void) { return (valueIsBetween(arg1, 0, 6) && Cmp(pPlayer->packSlots[arg1 - 1].curAmount)); } 1229 static char plyChkPackItemActive(void) { return (valueIsBetween(arg1, 0, 6) && pPlayer->packSlots[arg1 - 1].isActive); } 1230 static char plyCmpPackItemSelect(void) { return Cmp(pPlayer->packItemId + 1); } 1231 static char plyCmpPowerupAmount(void) 1232 { 1233 int t; 1234 if (arg3 > 0 && arg3 <= (kMaxAllowedPowerup - (kMinAllowedPowerup << 1) + 1)) 1235 { 1236 t = (kMinAllowedPowerup + arg3) - 1; // allowable powerups 1237 return Cmp(pPlayer->pwUpTime[t] / kPercFull); 1238 } 1239 1240 Error("Unexpected powerup #%d", arg3); 1241 return false; 1242 } 1243 static char plyChkKillerSprite(void) { return helperChkSprite(pPlayer->fraggerId); } 1244 static char plyChkKeyPress(void) 1245 { 1246 switch (arg1) 1247 { 1248 case 1: return (pPlayer->input.forward > 0); // forward 1249 case 2: return (pPlayer->input.forward < 0); // backward 1250 case 3: return (pPlayer->input.strafe > 0); // left 1251 case 4: return (pPlayer->input.strafe < 0); // right 1252 case 5: return (pPlayer->input.buttonFlags.jump); // jump 1253 case 6: return (pPlayer->input.buttonFlags.crouch); // crouch 1254 case 7: return (pPlayer->input.buttonFlags.shoot); // normal fire weapon 1255 case 8: return (pPlayer->input.buttonFlags.shoot2); // alt fire weapon 1256 case 9: return (pPlayer->input.keyFlags.action); // use 1257 default: 1258 Error("Specify a correct key!"); 1259 return false; 1260 } 1261 } 1262 static char plyChkRunning(void) { return pPlayer->isRunning; } 1263 static char plyChkFalling(void) { return pPlayer->fallScream; } 1264 static char plyCmpLifeMode(void) { return Cmp(pPlayer->lifeMode + 1); } 1265 static char plyCmpPosture(void) { return Cmp(pPlayer->posture + 1); } 1266 static char plyCmpKillsCount(void) { return Cmp(pPlayer->fragCount); } 1267 static char plyChkAutoAimTarget(void) { return helperChkSprite(pPlayer->aimTarget); } 1268 static char plyChkVoodooTarget(void) { return helperChkSprite(pPlayer->voodooTarget); } 1269 1270 static char plyCmpQavWeapon(void) { return Cmp(pPlayer->weaponQav); } 1271 static char plyCmpQavScene(void) { return Cmp(pPlayer->sceneQav); } 1272 static char plyChkGodMode(void) { return (pPlayer->godMode || powerupCheck(pPlayer, kPwUpDeathMask)); } 1273 static char plyChkShrink(void) { return isShrinked(pSpr); } 1274 static char plyChkGrown(void) { return isGrown(pSpr); } 1275 static char plyCmpArmor(void) 1276 { 1277 if (valueIsBetween(arg3, 0, 4)) 1278 return Cmp((pPlayer->armor[arg3 - 1] * kPercFull) / 1600); 1279 1280 Error(gErrors[kErrInvalidArgsPass]); 1281 return false; 1282 } 1283 static char plyCmpAmmo(void) 1284 { 1285 if (valueIsBetween(arg3, 0, 12)) 1286 return Cmp(pPlayer->ammoCount[arg3 - 1]); 1287 1288 Error(gErrors[kErrInvalidArgsPass]); 1289 return false; 1290 } 1291 static char plyChkSpriteItOwns(void) 1292 { 1293 int i; 1294 if (rngok(arg3, 0, kMaxStatus + 1)) 1295 { 1296 for (i = headspritestat[arg3]; i >= 0; i = nextspritestat[i]) 1297 { 1298 if (!Cmp(sprite[i].type) || actSpriteOwnerToSpriteId(&sprite[i]) != pPlayer->nSprite) continue; 1299 else if (PUSH) Push(EVOBJ_SPRITE, i); 1300 return true; 1301 } 1302 1303 return false; 1304 } 1305 1306 Error(gErrors[kErrInvalidArgsPass]); 1307 return false; 1308 } 1309 1310 1311 /** TABLE OF CONDITION FUNCTIONS 1312 ********************************************************************************/ 1313 static CONDITION_INFO gConditionsList[] = 1314 { 1315 { gamCmpLevelMin, 1, CGAM, false, false }, // compare level minutes 1316 { gamCmpLevelSec, 2, CGAM, false, false }, // compare level seconds 1317 { gamCmpLevelMsec, 3, CGAM, false, false }, // compare level mseconds 1318 { gamCmpLevelTime, 4, CGAM, false, false }, // compare level time (unsafe) 1319 { gamCmpKillsTotal, 5, CGAM, false, false }, // compare current global kills counter 1320 { gamCmpKillsDone, 6, CGAM, false, false }, // compare total global kills counter 1321 { gamCmpSecretsDone, 7, CGAM, false, false }, // compare how many secrets found 1322 { gamCmpSecretsTotal, 8, CGAM, false, false }, // compare total secrets 1323 { gamCmpVisibility, 20, CGAM, false, false }, // compare global visibility value 1324 { gamChkGotpic, 21, CGAM, true, false }, // check gotpic 1325 { gamChkChance, 30, CGAM, true, false }, // check chance % 1326 { gamCmpRandom, 31, CGAM, false, false }, // compare random 1327 { gamCmpStatnumCount, 47, CGAM, false, false }, // compare counter of specific statnum sprites 1328 { gamCmpNumsprites, 48, CGAM, false, false }, // compare counter of total sprites 1329 { gamChkSector, 57, CGAM, true, false }, // get sector N if possible 1330 { gamChkWall, 58, CGAM, true, false }, // get wall N if possible 1331 { gamChkSprite, 59, CGAM, true, false }, // get sprite N if possible 1332 { gamChkPlayerConnect, 60, CGAM, true, false }, // check if player N connected 1333 /**--------------------------------------------------------------**/ 1334 { mixChkObjSect, 100, CMIX, true, false }, 1335 { mixChkObjWall, 105, CMIX, true, false }, 1336 { mixChkObjSpr, 110, CMIX, true, false }, 1337 { mixChkXRange, 115, CMIX, true, false }, 1338 { mixCmpLotag, 120, CMIX, false, false }, 1339 { mixCmpPicSurface, 124, CMIX, false, false }, 1340 { mixCmpPic, 125, CMIX, false, false }, 1341 { mixCmpPal, 126, CMIX, false, false }, 1342 { mixCmpShade, 127, CMIX, false, false }, 1343 { mixCmpCstat, 128, CMIX, false, false }, 1344 { mixCmpHitag, 129, CMIX, false, false }, 1345 { mixCmpXrepeat, 130, CMIX, false, false }, 1346 { mixCmpXoffset, 131, CMIX, false, false }, 1347 { mixCmpYrepeat, 132, CMIX, false, false }, 1348 { mixCmpYoffset, 133, CMIX, false, false }, 1349 { mixCmpData1, 141, CMIX, false, true }, 1350 { mixCmpData2, 142, CMIX, false, true }, 1351 { mixCmpData3, 143, CMIX, false, true }, 1352 { mixCmpData4, 144, CMIX, false, true }, 1353 { mixCmpRXId, 150, CMIX, false, true }, 1354 { mixCmpTXId, 151, CMIX, false, true }, 1355 { mixChkLock, 152, CMIX, true, true }, 1356 { mixChkTriggerOn, 153, CMIX, true, true }, 1357 { mixChkTriggerOff, 154, CMIX, true, true }, 1358 { mixChkTriggerOnce, 155, CMIX, true, true }, 1359 { mixChkIsTriggered, 156, CMIX, true, true }, 1360 { mixChkState, 157, CMIX, true, true }, 1361 { mixCmpBusy, 158, CMIX, false, true }, 1362 { mixChkPlayerOnly, 159, CMIX, true, true }, 1363 { mixCmpSeqID, 170, CMIX, false, true }, 1364 { mixCmpSeqFrame, 171, CMIX, false, true }, 1365 { mixCmpObjIndex, 195, CMIX, false, false }, 1366 { mixCmpObjXIndex, 196, CMIX, false, false }, 1367 { mixCmpSerials, 197, CMIX, false, false }, 1368 { mixChkEventCauser, 198, CMIX, true, false }, // check event causer 1369 { mixCmpEventCmd, 199, CMIX, false, false }, // this condition received N command? 1370 /**--------------------------------------------------------------**/ 1371 { wallCmpOverpicnum, 200, CWAL, false, false }, 1372 { wallChkShow2dWall, 201, CWAL, true, false }, // wall on the minimap 1373 { wallChkSector, 205, CWAL, true, false }, 1374 { wallChkIsMirror, 210, CWAL, true, false }, 1375 { wallChkNextSector, 215, CWAL, true, false }, 1376 { wallChkNextWall, 220, CWAL, true, false }, 1377 { wallChkPoint2, 221, CWAL, true, false }, 1378 { wallChkNextWallSector, 225, CWAL, true, false }, 1379 /**--------------------------------------------------------------**/ 1380 { sectCmpVisibility, 300, CSEC, false, false }, // compare visibility 1381 { sectChkShow2dSector, 301, CSEC, true, false }, // sector on the minimap 1382 { sectChkGotSector, 302, CSEC, true, false }, // sector on the screen 1383 { sectCmpFloorSlope, 305, CSEC, false, false }, // compare floor slope 1384 { sectCmpCeilSlope, 306, CSEC, false, false }, // compare ceil slope 1385 { sectChkSprTypeInSect, 310, CSEC, true, false }, // check is sprite with lotag N in sector 1386 { sectChkUnderwater, 350, CSEC, true, true }, // sector is underwater? 1387 { sectCmpDepth, 351, CSEC, false, true }, // compare depth level 1388 { sectCmpFloorHeight, 355, CSEC, false, true }, // compare floor height (in %) 1389 { sectCmpCeilHeight, 356, CSEC, false, true }, // compare ceil height (in %) 1390 { sectChkPaused, 357, CSEC, true, true }, // this sector in movement? 1391 { sectChkUpperRoom, 358, CSEC, true, false }, // get upper room sector or marker 1392 { sectChkLowerRoom, 359, CSEC, true, false }, // get lower room sector or marker 1393 /**--------------------------------------------------------------**/ 1394 { plyCmpConnected, 400, CPLY, false, false }, 1395 { plyCmpTeam, 401, CPLY, false, false }, 1396 { plyChkHaveKey, 402, CPLY, true, false }, 1397 { plyChkHaveWeapon, 403, CPLY, true, false }, 1398 { plyCmpCurWeapon, 404, CPLY, false, false }, 1399 { plyCmpPackItemAmount, 405, CPLY, false, false }, 1400 { plyChkPackItemActive, 406, CPLY, true, false }, 1401 { plyCmpPackItemSelect, 407, CPLY, false, false }, 1402 { plyCmpPowerupAmount, 408, CPLY, false, false }, 1403 { plyChkKillerSprite, 409, CPLY, true, false }, 1404 { plyChkKeyPress, 410, CPLY, true, false }, // check keys pressed 1405 { plyChkRunning, 411, CPLY, true, false }, 1406 { plyChkFalling, 412, CPLY, true, false }, 1407 { plyCmpLifeMode, 413, CPLY, false, false }, 1408 { plyCmpPosture, 414, CPLY, false, false }, 1409 { plyChkSpriteItOwns, 419, CPLY, true, false }, 1410 { plyCmpArmor, 420, CPLY, false, false }, // in % 1411 { plyCmpAmmo, 421, CPLY, false, false }, 1412 { plyCmpKillsCount, 430, CPLY, false, false }, 1413 { plyChkAutoAimTarget, 431, CPLY, true, false }, 1414 { plyChkVoodooTarget, 435, CPLY, true, false }, 1415 { plyCmpQavWeapon, 445, CPLY, false, false }, 1416 { plyCmpQavScene, 446, CPLY, false, false }, 1417 { plyChkGodMode, 447, CPLY, true, false }, 1418 { plyChkShrink, 448, CPLY, true, false }, 1419 { plyChkGrown, 449, CPLY, true, false }, 1420 /**--------------------------------------------------------------**/ 1421 { gdudChkHaveTargets, 450, CDUDG, true, true }, // dude have any targets? 1422 { gdudChkInAiFight, 451, CDUDG, true, true }, // dude affected by ai fight? 1423 { gdudChkTargetDistance, 452, CDUDG, true, true }, // distance to the target in a range? 1424 { gdudChkTargetCansee, 453, CDUDG, true, true }, // is the target visible? 1425 { gdudChkTargetCanseePerip, 454, CDUDG, true, true }, // is the target visible with periphery? 1426 { gdudChkFlagPatrol, 455, CDUDG, true, true }, 1427 { gdudChkFlagDeaf, 456, CDUDG, true, true }, 1428 { gdudChkFlagBlind, 457, CDUDG, true, true }, 1429 { gdudChkFlagAlarm, 458, CDUDG, true, true }, 1430 { gdudChkFlagStealth, 459, CDUDG, true, true }, 1431 { gdudChkMarkerBusy, 460, CDUDG, true, true }, // check if the marker is busy with another dude 1432 { gdudChkMarkerReached, 461, CDUDG, true, true }, // check if the marker is reached 1433 { gdudCmpSpotProgress, 462, CDUDG, false, true }, // compare spot progress value in % 1434 { gdudChkLockout, 465, CDUDG, true, true }, // dude allowed to interact with objects? 1435 { gdudCmpAiStateType, 466, CDUDG, false, true }, 1436 { gdudCmpAiStateTimer, 467, CDUDG, false, true }, 1437 /**--------------------------------------------------------------**/ 1438 { cdudChkLeechThrown, 470, CDUDC, true, false }, // life leech is thrown? 1439 { cdudChkLeechDead, 471, CDUDC, true, false }, // life leech is destroyed? 1440 { cdudCmpSummoned, 472, CDUDC, false, false }, // are required amount of dudes is summoned? 1441 { cdudChkIfAble, 473, CDUDC, true, false }, // check if dude can... 1442 { cdudCmpDispersion, 474, CDUDC, false, false }, // compare weapon dispersion 1443 /**--------------------------------------------------------------**/ 1444 { sprCmpAng, 500, CSPR, false, false }, // compare angle 1445 { sprChkShow2dSprite, 501, CSEC, true, false }, // sprite on the minimap 1446 { sprCmpStatnum, 505, CSPR, false, false }, // check statnum 1447 { sprChkRespawn, 506, CSPR, true, false }, // check if on respawn list 1448 { sprCmpSlope, 507, CSPR, false, false }, // compare slope 1449 { sprCmpClipdist, 510, CSPR, false, false }, // compare clipdist 1450 { sprChkOwner, 515, CSPR, true, false }, // check owner sprite 1451 { sprChkSector, 520, CSPR, true, false }, // stays in a sector? 1452 { sprCmpChkVelocity, 525, CSPR, true, false }, // check or compare velocity 1453 { sprCmpVelocityNew, 526, CSPR, false, false }, // compare velocity 1454 { sprChkUnderwater, 530, CSPR, true, false }, // sector of sprite is underwater? 1455 { sprChkDmgImmune, 531, CSPR, true, false }, // check if immune to N dmgType 1456 { sprChkHitscanCeil, 535, CSPR, true, false }, // hitscan: ceil? 1457 { sprChkHitscanFloor, 536, CSPR, true, false }, // hitscan: floor? 1458 { sprChkHitscanWall, 537, CSPR, true, false }, // hitscan: wall? 1459 { sprChkHitscanSpr, 538, CSPR, true, false }, // hitscan: sprite? 1460 { sprChkHitscanMasked, 539, CSPR, true, false }, // hitscan: masked wall? 1461 { sprChkIsTarget, 545, CSPR, true, false }, // this sprite is a target of some dude? 1462 { sprCmpHealth, 550, CSPR, false, true }, // compare hp (in %) 1463 { sprChkTouchCeil, 555, CSPR, true, true }, // touching ceil of sector? 1464 { sprChkTouchFloor, 556, CSPR, true, true }, // touching floor of sector? 1465 { sprChkTouchWall, 557, CSPR, true, true }, // touching walls of sector? 1466 { sprChkTouchSpite, 558, CSPR, true, false }, // touching another sprite? (allow no xAvail!) 1467 { sprCmpBurnTime, 565, CSPR, false, true }, // compare burn time (in %) 1468 { sprChkIsFlareStuck, 566, CSPR, true, false }, // any flares stuck in this sprite? 1469 { sprChkTarget, 569, CSPR, true, true }, // use with caution! 1470 { sprCmpMass, 570, CSPR, false, true }, // mass of the sprite in a range? 1471 }; 1472 1473 1474 1475 /** CONDITION INTERFACE FUNCTIONS 1476 ********************************************************************************/ 1477 static char DefaultResult() { return (pEntry->isBool) ? false : Cmp(0); } 1478 static char CheckGeneric() { return pEntry->pFunc(); } 1479 static char CheckSector() 1480 { 1481 if (objType == EVOBJ_SECTOR && rngok(objIndex, 0, numsectors)) 1482 { 1483 pSect = §or[objIndex]; 1484 if (pSect->extra > 0) 1485 { 1486 pXSect = &xsector[pSect->extra]; 1487 xAvail = true; 1488 } 1489 else 1490 { 1491 pXSect = NULL; 1492 xAvail = false; 1493 if (pEntry->xReq) 1494 return DefaultResult(); 1495 } 1496 } 1497 else 1498 { 1499 Error(gErrors[kErrInvalidObject], objIndex, objType, "sector"); 1500 return false; 1501 } 1502 1503 return pEntry->pFunc(); 1504 } 1505 1506 static char CheckWall() 1507 { 1508 if (objType == EVOBJ_WALL && rngok(objIndex, 0, numwalls)) 1509 { 1510 pWall = &wall[objIndex]; 1511 if (pWall->extra > 0) 1512 { 1513 pXWall = &xwall[pWall->extra]; 1514 xAvail = true; 1515 } 1516 else 1517 { 1518 pXWall = NULL; 1519 xAvail = false; 1520 if (pEntry->xReq) 1521 return DefaultResult(); 1522 } 1523 } 1524 else 1525 { 1526 Error(gErrors[kErrInvalidObject], objIndex, objType, "wall"); 1527 return false; 1528 } 1529 1530 return pEntry->pFunc(); 1531 } 1532 1533 static char CheckDude() 1534 { 1535 if (objType == EVOBJ_SPRITE && rngok(objIndex, 0, kMaxSprites)) 1536 { 1537 pSpr = &sprite[objIndex]; 1538 if ((!IsDudeSprite(pSpr) && pSpr->type != kThingBloodChunks) || IsPlayerSprite(pSpr)) 1539 { 1540 Error(gErrors[kErrInvalidObject], objIndex, objType, "dude"); 1541 return false; 1542 } 1543 1544 if (pSpr->extra > 0) 1545 { 1546 pXSpr = &xsprite[pSpr->extra]; 1547 xAvail = true; 1548 } 1549 else 1550 { 1551 pXSpr = NULL; 1552 xAvail = false; 1553 if (pEntry->xReq) 1554 return DefaultResult(); 1555 } 1556 } 1557 else 1558 { 1559 Error(gErrors[kErrInvalidObject], objIndex, objType, "sprite"); 1560 return false; 1561 } 1562 1563 return pEntry->pFunc(); 1564 } 1565 1566 static char CheckCustomDude() 1567 { 1568 if (objType == EVOBJ_SPRITE && rngok(objIndex, 0, kMaxSprites)) 1569 { 1570 pSpr = &sprite[objIndex]; 1571 switch (pSpr->type) { 1572 case kThingBloodChunks: 1573 if (pSpr->inittype == kDudeModernCustom) break; 1574 Error(gErrors[kErrInvalidObject], objIndex, objType, "custom dude"); 1575 return false; 1576 case kDudeModernCustom: 1577 break; 1578 default: 1579 Error(gErrors[kErrInvalidObject], objIndex, objType, "custom dude"); 1580 return false; 1581 } 1582 1583 if (pSpr->extra > 0) 1584 { 1585 pXSpr = &xsprite[pSpr->extra]; 1586 xAvail = true; 1587 } 1588 else 1589 { 1590 pXSpr = NULL; 1591 xAvail = false; 1592 if (pEntry->xReq) 1593 return DefaultResult(); 1594 } 1595 } 1596 else 1597 { 1598 Error(gErrors[kErrInvalidObject], objIndex, objType, "sprite"); 1599 return false; 1600 } 1601 1602 return pEntry->pFunc(); 1603 } 1604 1605 static char CheckPlayer() 1606 { 1607 int i; 1608 if (objType == EVOBJ_SPRITE && rngok(objIndex, 0, kMaxSprites)) 1609 { 1610 pSpr = &sprite[objIndex]; 1611 for (i = connecthead; i >= 0; i = connectpoint2[i]) 1612 { 1613 if (objIndex != gPlayer[i].nSprite) continue; 1614 pPlayer = &gPlayer[i]; 1615 break; 1616 } 1617 1618 // there is no point to check unlinked or disconnected players 1619 if (i < 0) 1620 return DefaultResult(); 1621 1622 if (pSpr->extra > 0) 1623 { 1624 pXSpr = &xsprite[pSpr->extra]; 1625 xAvail = true; 1626 } 1627 else 1628 { 1629 pXSpr = NULL; 1630 xAvail = false; 1631 if (pEntry->xReq) 1632 return DefaultResult(); 1633 } 1634 } 1635 else 1636 { 1637 Error(gErrors[kErrInvalidObject], objIndex, objType, "player"); 1638 return false; 1639 } 1640 1641 return pEntry->pFunc(); 1642 } 1643 1644 static char CheckSprite() 1645 { 1646 if (objType == EVOBJ_SPRITE && rngok(objIndex, 0, kMaxSprites)) 1647 { 1648 pSpr = &sprite[objIndex]; 1649 if (pSpr->extra > 0) 1650 { 1651 pXSpr = &xsprite[pSpr->extra]; 1652 xAvail = true; 1653 } 1654 else 1655 { 1656 pXSpr = NULL; 1657 xAvail = false; 1658 if (pEntry->xReq) 1659 return DefaultResult(); 1660 } 1661 } 1662 else 1663 { 1664 Error(gErrors[kErrInvalidObject], objIndex, objType, "sprite"); 1665 return false; 1666 } 1667 1668 return pEntry->pFunc(); 1669 } 1670 1671 static char CheckObject() 1672 { 1673 switch (objType) 1674 { 1675 case EVOBJ_WALL: return CheckWall(); 1676 case EVOBJ_SECTOR: return CheckSector(); 1677 case EVOBJ_SPRITE: return CheckSprite(); 1678 } 1679 1680 // conditions can only work with objects in the switch anyway... 1681 Error(gErrors[kErrUnknownObject], objType, objIndex); 1682 return false; 1683 } 1684 1685 static void Push(int oType, int oIndex) 1686 { 1687 // focus on object 1688 pXCond->targetX = Serialize(oType, oIndex); 1689 if (pXCond->command > kCmdPush) 1690 { 1691 // copy in additional slots 1692 switch (pXCond->command - kCmdPush) 1693 { 1694 case 1: 1695 pXCond->targetZ = pXCond->targetX; 1696 break; 1697 case 2: 1698 pXCond->sysData1 = pXCond->targetX; 1699 break; 1700 } 1701 } 1702 } 1703 1704 static void Restore() 1705 { 1706 int t; 1707 switch (pXCond->command - kCmdPop) 1708 { 1709 case 0: 1710 pXCond->targetX = pXCond->targetY; 1711 break; 1712 // additional slots leads to swapping 1713 // so object in the focus is not lost 1714 case 1: 1715 t = pXCond->targetZ; 1716 pXCond->targetX = pXCond->targetZ; 1717 pXCond->targetZ = t; 1718 break; 1719 case 2: 1720 t = pXCond->targetX; 1721 pXCond->targetX = pXCond->sysData1; 1722 pXCond->sysData1 = t; 1723 break; 1724 } 1725 } 1726 1727 static int Serialize(int oType, int oIndex) 1728 { 1729 switch (oType) 1730 { 1731 case EVOBJ_SECTOR: return kSerialSector + oIndex; 1732 case EVOBJ_WALL: return kSerialWall + oIndex; 1733 case EVOBJ_SPRITE: return kSerialSprite + oIndex; 1734 } 1735 1736 Error(gErrors[kErrUnknownObject], oType, oIndex); 1737 return -1; 1738 } 1739 1740 static void Unserialize(int nSerial, int* oType, int* oIndex) 1741 { 1742 if (rngok(nSerial, kSerialSector, kSerialWall)) 1743 { 1744 *oIndex = nSerial - kSerialSector; 1745 *oType = EVOBJ_SECTOR; 1746 } 1747 else if (rngok(nSerial, kSerialWall, kSerialSprite)) 1748 { 1749 *oIndex = nSerial - kSerialWall; 1750 *oType = EVOBJ_WALL; 1751 } 1752 else if (rngok(nSerial, kSerialSprite, kSerialMax)) 1753 { 1754 *oIndex = nSerial - kSerialSprite; 1755 *oType = EVOBJ_SPRITE; 1756 } 1757 else 1758 { 1759 Error(gErrors[kErrInvalidSerial], nSerial); 1760 } 1761 } 1762 1763 static char Cmp(int val) 1764 { 1765 if (cmpOp & 0x2000) 1766 return (cmpOp & CSTAT_SPRITE_BLOCK) ? (val > arg1) : (val >= arg1); // blue sprite 1767 else if (cmpOp & 0x4000) 1768 return (cmpOp & CSTAT_SPRITE_BLOCK) ? (val < arg1) : (val <= arg1); // green sprite 1769 else if (cmpOp & CSTAT_SPRITE_BLOCK) 1770 return (val >= arg1 && val <= arg2); 1771 else 1772 return (val == arg1); 1773 } 1774 1775 static char Cmp(int val, int nArg1, int nArg2) 1776 { 1777 arg1 = nArg1; 1778 arg2 = nArg2; 1779 1780 return Cmp(val); 1781 } 1782 1783 static void Error(const char* pFormat, ...) 1784 { 1785 char buffer[1024], buffer2[512], condType[32]; 1786 strcpy(condType, (pEntry) ? gCheckFuncInfo[pEntry->type].name : "Unknown"); 1787 Bstrupr(condType); 1788 1789 va_list args; 1790 va_start(args, pFormat); 1791 vsprintf(buffer2, pFormat, args); 1792 va_end(args); 1793 1794 Bsprintf(buffer, 1795 "\n" 1796 "ERROR IN %s CONDITION ID #%d:\n" 1797 "%s\n\n" 1798 "Debug information:\n" 1799 "--------------------------------------------\n" 1800 "Condition sprite = %d, RX ID = %d, TX ID = %d\n" 1801 "Arguments = %d, %d, %d\n" 1802 "Operator = %d\n", 1803 condType, pXCond->data1, buffer2, 1804 pXCond->reference, 1805 pXCond->rxID, 1806 pXCond->txID, 1807 arg1, 1808 arg2, 1809 arg3, 1810 cmpOp &= ~CSTAT_SPRITE_INVISIBLE 1811 ); 1812 1813 ThrowError(buffer); 1814 1815 } 1816 1817 static void ReceiveObjects(EVENT* pFrom) 1818 { 1819 char srcIsCondition = false; 1820 1821 objType = pFrom->type; objIndex = pFrom->index; 1822 if (objType == EVOBJ_SPRITE && pCond->index != objIndex) 1823 srcIsCondition = (sprite[objIndex].statnum == kStatModernCondition); 1824 1825 if (!srcIsCondition) 1826 { 1827 // save object serials in the "stack" 1828 pXCond->targetX = Serialize(objType, objIndex); 1829 pXCond->targetY = pXCond->targetX; 1830 pXCond->targetZ = pXCond->targetX; 1831 pXCond->sysData1 = pXCond->targetX; 1832 } 1833 else 1834 { 1835 // or grab serials of objects from previous conditions 1836 pXCond->targetX = xsprite[sprite[objIndex].extra].targetX; 1837 pXCond->targetY = xsprite[sprite[objIndex].extra].targetY; 1838 pXCond->targetZ = xsprite[sprite[objIndex].extra].targetZ; 1839 pXCond->sysData1 = xsprite[sprite[objIndex].extra].sysData1; 1840 } 1841 } 1842 1843 static void TriggerObject(int nSerial) 1844 { 1845 int oType, oIndex; 1846 Unserialize(nSerial, &oType, &oIndex); 1847 nnExtTriggerObject(oType, oIndex, pXCond->command, pCond->index); 1848 } 1849 1850 1851 1852 /** EXTERNAL CODE 1853 ********************************************************************************/ 1854 void conditionsInit(bool bSaveLoad) 1855 { 1856 unsigned int i, j; 1857 1858 if (!gNumConditions) 1859 { 1860 if (gConditions) 1861 { 1862 Bfree(gConditions); 1863 gConditions = NULL; 1864 } 1865 1866 // sort out *condition function callers* list the right way 1867 qsort(gCheckFuncInfo, LENGTH(gCheckFuncInfo), sizeof(gCheckFuncInfo[0]), (int(*)(const void*, const void*))qsSortCheckFuncInfo); 1868 1869 for (i = 0; i < LENGTH(gConditionsList); i++) 1870 { 1871 CONDITION_INFO* pTemp = &gConditionsList[i]; 1872 1873 // check for duplicates 1874 for (j = 0; j < LENGTH(gConditionsList); j++) 1875 { 1876 if (i != j) 1877 dassert(pTemp->id != gConditionsList[j].id); 1878 } 1879 1880 // find the highest condition id 1881 if (pTemp->id > gNumConditions) 1882 gNumConditions = pTemp->id; 1883 } 1884 1885 // allocate the list 1886 gNumConditions++; 1887 gConditions = (CONDITION_INFO*)Bmalloc(sizeof(CONDITION_INFO) * gNumConditions); 1888 dassert(gConditions != NULL); 1889 1890 // dummy template for everything 1891 for (i = 0; i < gNumConditions; i++) 1892 { 1893 CONDITION_INFO* pTemp = &gConditions[i]; 1894 pTemp->pFunc = errCondNotImplemented; pTemp->type = CNON; 1895 pTemp->pCaller = &gCheckFuncInfo[pTemp->type]; 1896 pTemp->isBool = false; 1897 pTemp->xReq = false; 1898 pTemp->id = i; 1899 } 1900 1901 for (i = 0; i < LENGTH(gConditionsList); i++) 1902 { 1903 CONDITION_INFO* pTemp = &gConditionsList[i]; 1904 1905 // proper caller info for each function 1906 for (j = 0; j < LENGTH(gCheckFuncInfo); j++) 1907 { 1908 if (gCheckFuncInfo[j].type != pTemp->type) continue; 1909 pTemp->pCaller = &gCheckFuncInfo[j]; 1910 break; 1911 } 1912 1913 dassert(j < LENGTH(gCheckFuncInfo)); 1914 1915 // sort out condition list the right way 1916 memcpy(&gConditions[pTemp->id], pTemp, sizeof(gConditionsList[0])); 1917 } 1918 } 1919 1920 // allocate tracking conditions and collect objects for it 1921 conditionsTrackingAlloc(bSaveLoad); 1922 } 1923 1924 void conditionsTrackingAlloc(bool bSaveLoad) 1925 { 1926 int nCount = 0, i, j, s, e; 1927 1928 conditionsTrackingClear(); 1929 for (i = headspritestat[kStatModernCondition]; i >= 0; i = nextspritestat[i]) 1930 { 1931 spritetype* pSprite = &sprite[i]; XSPRITE* pXSprite = &xsprite[pSprite->extra]; 1932 if (pSprite->extra <= 0 || !pXSprite->busyTime || pXSprite->isTriggered) 1933 continue; 1934 1935 gTrackingConditionsList = (TRACKING_CONDITION*)Brealloc(gTrackingConditionsList, (nCount + 1) * sizeof(TRACKING_CONDITION)); 1936 if (!gTrackingConditionsList) 1937 break; 1938 1939 TRACKING_CONDITION* pTr = &gTrackingConditionsList[nCount]; 1940 pTr->objects = new(OBJECT_LIST); 1941 1942 if (pXSprite->rxID >= kChannelUser) 1943 { 1944 for (j = 0; j < numsectors; j++) 1945 { 1946 for (s = headspritesect[j]; s >= 0; s = nextspritesect[s]) 1947 { 1948 spritetype* pObj = &sprite[s]; 1949 if (pObj->extra <= 0 || pObj->index == pSprite->index) 1950 continue; 1951 1952 XSPRITE* pXObj = &xsprite[pObj->extra]; 1953 if (pXObj->txID != pXSprite->rxID) 1954 continue; 1955 1956 // check exceptions 1957 switch (pObj->type) 1958 { 1959 case kModernCondition: 1960 case kModernConditionFalse: 1961 case kSwitchToggle: 1962 case kSwitchOneWay: 1963 break; 1964 case kModernPlayerControl: 1965 if (pObj->statnum == kStatModernPlayerLinker && bSaveLoad) 1966 { 1967 // assign player sprite after savegame loading 1968 pTr->objects->Add(EVOBJ_SPRITE, pXObj->sysData1); 1969 break; 1970 } 1971 fallthrough__; 1972 default: 1973 pTr->objects->Add(EVOBJ_SPRITE, pObj->index); 1974 break; 1975 } 1976 } 1977 1978 s = sector[j].wallptr; e = s + sector[j].wallnum - 1; 1979 while (s <= e) 1980 { 1981 walltype* pObj = &wall[s]; 1982 if (pObj->extra > 0 && xwall[pObj->extra].txID == pXSprite->rxID) 1983 { 1984 // check exceptions 1985 switch (pObj->type) 1986 { 1987 case kSwitchToggle: 1988 case kSwitchOneWay: 1989 break; 1990 default: 1991 pTr->objects->Add(EVOBJ_WALL, s); 1992 break; 1993 } 1994 } 1995 1996 s++; 1997 } 1998 1999 sectortype* pObj = §or[j]; 2000 if (pObj->extra > 0 && xsector[pObj->extra].txID == pXSprite->rxID) 2001 pTr->objects->Add(EVOBJ_SECTOR, j); 2002 } 2003 } 2004 2005 // allow self tracking 2006 if (!pTr->objects->Length()) 2007 pTr->objects->Add(EVOBJ_SPRITE, pSprite->index); 2008 2009 pTr->id = pSprite->extra; 2010 nCount++; 2011 } 2012 2013 if (!nCount) 2014 conditionsTrackingClear(); 2015 2016 gTrackingConditionsListLength = nCount; 2017 } 2018 2019 void conditionsTrackingClear() 2020 { 2021 int i = gTrackingConditionsListLength; 2022 if (gTrackingConditionsList) 2023 { 2024 while (--i >= 0) 2025 { 2026 TRACKING_CONDITION* pTr = &gTrackingConditionsList[i]; 2027 if (pTr->objects) 2028 { 2029 delete(pTr->objects); 2030 pTr->objects = NULL; 2031 } 2032 } 2033 2034 Bfree(gTrackingConditionsList); 2035 } 2036 2037 gTrackingConditionsList = NULL; 2038 gTrackingConditionsListLength = 0; 2039 2040 } 2041 2042 void conditionsTrackingProcess() 2043 { 2044 EVENT evn; OBJECT* o; 2045 evn.funcID = kCallbackMax; evn.cmd = kCmdOn; evn.causer = kCauserGame; 2046 TRACKING_CONDITION* pTr; 2047 XSPRITE* pXSpr; 2048 2049 int i = gTrackingConditionsListLength; 2050 int t; 2051 2052 while (--i >= 0) 2053 { 2054 pTr = &gTrackingConditionsList[i]; pXSpr = &xsprite[pTr->id]; 2055 if (pXSpr->locked || pXSpr->isTriggered || ++pXSpr->busy < pXSpr->busyTime) 2056 continue; 2057 2058 pXSpr->busy = 0; 2059 t = pTr->objects->Length(); o = pTr->objects->Ptr(); 2060 while (--t >= 0) 2061 { 2062 evn.type = o->type; evn.index = o->index; 2063 useCondition(&sprite[pXSpr->reference], pXSpr, &evn); 2064 o++; 2065 } 2066 } 2067 } 2068 2069 void conditionsLinkPlayer(XSPRITE* pXCtrl, PLAYER* pPlay) 2070 { 2071 int i = gTrackingConditionsListLength; 2072 int t; 2073 OBJECT* o; 2074 2075 // search for player control sprite and replace it with actual player sprite 2076 while (--i >= 0) 2077 { 2078 TRACKING_CONDITION* pTr = &gTrackingConditionsList[i]; 2079 XSPRITE* pXTrack = &xsprite[pTr->id]; 2080 if (pXTrack->rxID != pXCtrl->txID) continue; 2081 if ((t = pTr->objects->Length()) <= 0) 2082 continue; 2083 2084 o = pTr->objects->Ptr(); 2085 while (--t >= 0) 2086 { 2087 if (o->type == OBJ_SPRITE && o->index == pXCtrl->reference) 2088 { 2089 o->index = pPlay->nSprite; 2090 break; 2091 } 2092 2093 o++; 2094 } 2095 } 2096 } 2097 2098 // this updates index of object in all conditions 2099 void conditionsUpdateIndex(int oType, int oldIndex, int newIndex) 2100 { 2101 int i = gTrackingConditionsListLength; 2102 int t; 2103 OBJECT* o; 2104 2105 // update index in tracking conditions first 2106 while (--i >= 0) 2107 { 2108 TRACKING_CONDITION* pTr = &gTrackingConditionsList[i]; 2109 if ((t = pTr->objects->Length()) <= 0) 2110 continue; 2111 2112 o = pTr->objects->Ptr(); 2113 while (--t >= 0) 2114 { 2115 if (o->type == oType && o->index == oldIndex) 2116 { 2117 o->index = newIndex; 2118 break; 2119 } 2120 2121 o++; 2122 } 2123 } 2124 2125 2126 int oldSerial = Serialize(oType, oldIndex); 2127 int newSerial = Serialize(oType, newIndex); 2128 2129 // then update serials everywhere 2130 for (i = headspritestat[kStatModernCondition]; i >= 0; i = nextspritestat[i]) 2131 { 2132 XSPRITE* pXSpr = &xsprite[sprite[i].extra]; 2133 if (pXSpr->targetX == oldSerial) pXSpr->targetX = newSerial; 2134 if (pXSpr->targetY == oldSerial) pXSpr->targetY = newSerial; 2135 if (pXSpr->targetZ == oldSerial) pXSpr->targetZ = newSerial; 2136 if (pXSpr->sysData1 == oldSerial) pXSpr->sysData1 = newSerial; 2137 } 2138 2139 2140 return; 2141 } 2142 2143 // for showing errors in external code 2144 void conditionsError(XSPRITE* pXSprite, const char* pFormat, ...) 2145 { 2146 char buffer[512]; 2147 2148 pCond = &sprite[pXSprite->reference]; pXCond = pXSprite; 2149 arg1 = pXCond->data2, arg2 = pXCond->data3, arg3 = pXCond->data4; 2150 cmpOp = pCond->cstat; 2151 pEntry = NULL; 2152 2153 va_list args; 2154 va_start(args, pFormat); 2155 vsprintf(buffer, pFormat, args); 2156 va_end(args); 2157 2158 Error(buffer); 2159 } 2160 2161 2162 void useCondition(spritetype* pSource, XSPRITE* pXSource, EVENT* pEvn) 2163 { 2164 // if it's a tracking condition, it must ignore all the commands sent from objects 2165 if (pXSource->busyTime && pEvn->funcID != kCallbackMax) 2166 return; 2167 2168 bool ok = false; 2169 bool delayBefore = false, flag8 = false; 2170 pCond = pSource; pXCond = pXSource; 2171 pEntry = NULL; 2172 2173 if (pXCond->waitTime) 2174 { 2175 delayBefore = (pCond->flags & kModernTypeFlag64); 2176 flag8 = (pCond->flags & kModernTypeFlag8); 2177 2178 if (pXCond->restState == 0) 2179 { 2180 if (delayBefore) 2181 { 2182 // start waiting 2183 evPost(pCond->index, EVOBJ_SPRITE, EVTIME2TICKS(pXCond->waitTime), (COMMAND_ID)kCmdRepeat, pEvn->causer); 2184 pXCond->restState = 1; 2185 2186 if (flag8) 2187 ReceiveObjects(pEvn); // receive current objects and hold it 2188 2189 return; 2190 } 2191 else 2192 { 2193 ReceiveObjects(pEvn); // receive current objects and continue 2194 } 2195 } 2196 else if (pEvn->cmd == kCmdRepeat) 2197 { 2198 // finished the waiting 2199 if (delayBefore) 2200 pXCond->restState = 0; 2201 } 2202 else 2203 { 2204 if ((delayBefore && !flag8) || (!delayBefore && flag8)) 2205 ReceiveObjects(pEvn); // continue receiving actual objects while waiting 2206 2207 return; 2208 } 2209 } 2210 else 2211 { 2212 ReceiveObjects(pEvn); // receive current objects and continue 2213 } 2214 2215 if (pXCond->restState == 0) 2216 { 2217 if (!pXCond->data1) ok = true; 2218 else if (rngok(pXCond->data1, 0, gNumConditions)) 2219 { 2220 cmpOp = pCond->cstat; PUSH = rngok(pXCond->command, kCmdPush, kCmdPush + kPushRange); 2221 arg1 = pXCond->data2; arg2 = pXCond->data3; 2222 arg3 = pXCond->data4; pEvent = pEvn; 2223 2224 Unserialize(pXCond->targetX, &objType, &objIndex); 2225 pEntry = &gConditions[pXCond->data1]; 2226 ok = pEntry->pCaller->pFunc(); 2227 } 2228 else 2229 { 2230 errCondNotImplemented(); 2231 return; 2232 } 2233 2234 if ((ok) ^ (pCond->type == kModernConditionFalse)) 2235 { 2236 pXCond->state = 1; 2237 if (pXCond->waitTime && !delayBefore) // delay after checking 2238 { 2239 // start waiting 2240 evPost(pCond->index, EVOBJ_SPRITE, EVTIME2TICKS(pXCond->waitTime), (COMMAND_ID)kCmdRepeat, pEvn->causer); 2241 pXCond->restState = 1; 2242 return; 2243 } 2244 } 2245 else 2246 { 2247 pXCond->state = 0; 2248 } 2249 } 2250 else if (pEvn->cmd == kCmdRepeat) 2251 { 2252 pXCond->restState = 0; 2253 } 2254 else 2255 { 2256 return; 2257 } 2258 2259 if (pXCond->state) 2260 { 2261 // trigger once per result? 2262 if (pCond->flags & kModernTypeFlag4) 2263 { 2264 if (pXCond->unused2) 2265 return; 2266 2267 pXCond->unused2 = 1; 2268 } 2269 2270 if (pXCond->triggerOnce) 2271 #ifdef CONDITIONS_USE_BUBBLE_ACTION 2272 conditionsBubble(pXCond, conditionsSetIsTriggered, true); 2273 #else 2274 conditionsSetIsTriggered(pXCond, true); 2275 #endif 2276 2277 if (rngok(pXCond->command, kCmdPop, kCmdPop + kPushRange)) 2278 Restore(); // change focus to the saved object 2279 2280 // send command to rx bucket 2281 if (pXCond->txID) 2282 { 2283 Unserialize(pXCond->targetX, &objType, &objIndex); 2284 evSend(pCond->index, EVOBJ_SPRITE, pXCond->txID, (COMMAND_ID)pXCond->command, (objType == EVOBJ_SPRITE) ? objIndex : kCauserGame); 2285 } 2286 2287 if (pCond->flags) 2288 { 2289 // send it for object that currently in the focus 2290 if (pCond->flags & kModernTypeFlag1) 2291 { 2292 TriggerObject(pXCond->targetX); 2293 if ((pCond->flags & kModernTypeFlag2) && pXCond->targetX == pXCond->targetY) 2294 return; 2295 } 2296 2297 // send it for initial object 2298 if (pCond->flags & kModernTypeFlag2) 2299 { 2300 TriggerObject(pXCond->targetY); 2301 } 2302 } 2303 } 2304 else 2305 { 2306 pXCond->unused2 = 0; 2307 } 2308 } 2309 2310 #ifdef CONDITIONS_USE_BUBBLE_ACTION 2311 void conditionsBubble(XSPRITE* pXStart, void(*pActionFunc)(XSPRITE*, int), int nValue) 2312 { 2313 int i, j; 2314 2315 pActionFunc(pXStart, nValue); 2316 2317 // perform action for whole branch from bottom to top while there is no forks 2318 for (i = headspritestat[kStatModernCondition]; i >= 0; i = nextspritestat[i]) 2319 { 2320 XSPRITE* pXSprite = &xsprite[sprite[i].extra]; 2321 if (pXSprite->txID != pXStart->rxID) 2322 continue; 2323 2324 for (j = headspritestat[kStatModernCondition]; j >= 0; j = nextspritestat[j]) 2325 { 2326 XSPRITE* pXSprite2 = &xsprite[sprite[j].extra]; 2327 if (pXSprite2->rxID == pXStart->rxID && pXSprite2 != pXStart) 2328 { 2329 if (pActionFunc == conditionsSetIsTriggered) 2330 { 2331 if (pXSprite2->triggerOnce && ((nValue != 0 && !pXSprite2->isTriggered) || (nValue == 0 && pXSprite2->isTriggered))) 2332 break; // fork found 2333 } 2334 else if (pActionFunc == conditionsSetIsLocked) 2335 { 2336 if ((nValue != 0 && !pXSprite2->locked) || (nValue == 0 && pXSprite2->locked)) 2337 break; // fork found 2338 } 2339 else 2340 { 2341 break; // fork found 2342 } 2343 } 2344 } 2345 2346 if (j < 0) 2347 conditionsBubble(pXSprite, pActionFunc, nValue); 2348 } 2349 } 2350 void conditionsSetIsTriggered(XSPRITE* pXSprite, int nValue) 2351 { 2352 pXSprite->isTriggered = nValue; 2353 evKill(pXSprite->reference, OBJ_SPRITE); 2354 } 2355 2356 void conditionsSetIsLocked(XSPRITE* pXSprite, int nValue) 2357 { 2358 pXSprite->locked = nValue; 2359 pXSprite->restState = 0; 2360 pXSprite->state = 0; 2361 evKill(pXSprite->reference, OBJ_SPRITE); 2362 if (pXSprite->busyTime) 2363 pXSprite->busy = 0; 2364 } 2365 #endif