/ source / blood / src / nnextsif.cpp
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 = &sector[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 = &sector[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