2020-02-07 19:47:43 +00:00
//-------------------------------------------------------------------------
/*
Copyright ( C ) 2010 - 2019 EDuke32 developers and contributors
Copyright ( C ) 2019 Nuke . YKT
Copyright ( C ) NoOne
This file is part of NBlood .
NBlood is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
//-------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////
// This file provides modern features for mappers.
// For full documentation please visit http://cruo.bloodgame.ru/xxsystem
///////////////////////////////////////////////////////////////////
# include "ns.h"
# include "nnexts.h"
# ifdef NOONE_EXTENSIONS
# include <random>
2020-02-11 22:15:25 +00:00
# include "loadsave.h"
2020-02-07 19:47:43 +00:00
# include "aiunicult.h"
# include "triggers.h"
# include "sectorfx.h"
# include "globals.h"
# include "endgame.h"
# include "weapon.h"
# include "mmulti.h"
# include "view.h"
# include "tile.h"
# include "trig.h"
# include "sfx.h"
# include "seq.h"
# include "ai.h"
BEGIN_BLD_NS
bool gModernMap = false ;
bool gAllowTrueRandom = false ;
2020-03-01 20:36:28 +00:00
bool gEventRedirectsUsed = false ;
2020-02-07 19:47:43 +00:00
SPRITEMASS gSpriteMass [ ] ; // cache for getSpriteMassBySize();
short gProxySpritesList [ ] ; // list of additional sprites which can be triggered by Proximity
short gProxySpritesCount ; // current count
short gSightSpritesList [ ] ; // list of additional sprites which can be triggered by Sight
short gSightSpritesCount ; // current count
short gPhysSpritesList [ ] ; // list of additional sprites which can be affected by physics
short gPhysSpritesCount ; // current count
2020-03-01 20:36:28 +00:00
short gImpactSpritesList [ ] ;
short gImpactSpritesCount ;
2020-03-13 20:59:13 +00:00
2020-02-07 19:47:43 +00:00
TRPLAYERCTRL gPlayerCtrl [ kMaxPlayers ] ;
2020-03-13 20:59:13 +00:00
TRCONDITION gCondition [ kMaxTrackingConditions ] ;
short gTrackingCondsCount ;
2020-02-07 19:47:43 +00:00
std : : default_random_engine gStdRandom ;
VECTORINFO_EXTRA gVectorInfoExtra [ ] = {
1207 , 1207 , 1001 , 1001 , 4001 , 4002 ,
431 , 431 , 1002 , 1002 , 359 , 359 ,
521 , 521 , 513 , 513 , 499 , 499 ,
9012 , 9014 , 1101 , 1101 , 1207 , 1207 ,
499 , 495 , 495 , 496 , 9013 , 499 ,
1307 , 1308 , 499 , 499 , 499 , 499 ,
499 , 499 , 499 , 499 , 351 , 351 ,
0 , 0 , 357 , 499
} ;
MISSILEINFO_EXTRA gMissileInfoExtra [ ] = {
2020-03-01 20:36:28 +00:00
1207 , 1207 , false , false , false , false , false , true , false , true ,
420 , 420 , false , true , true , false , false , false , false , true ,
471 , 471 , false , false , false , false , false , false , true , false ,
421 , 421 , false , true , false , true , false , false , false , false ,
1309 , 351 , false , true , false , false , false , false , false , true ,
480 , 480 , false , true , false , true , false , false , false , false ,
470 , 470 , false , false , false , false , false , false , true , true ,
489 , 490 , false , false , false , false , false , true , false , true ,
462 , 351 , false , true , false , false , false , false , false , true ,
1203 , 172 , false , false , true , false , false , false , false , true ,
0 , 0 , false , false , true , false , false , false , false , true ,
1457 , 249 , false , false , false , false , false , true , false , true ,
480 , 489 , false , true , false , true , false , false , false , false ,
480 , 489 , false , false , false , true , false , false , false , false ,
480 , 489 , false , false , false , true , false , false , false , false ,
491 , 491 , true , true , true , true , true , true , true , true ,
520 , 520 , false , false , false , false , false , true , false , true ,
520 , 520 , false , false , false , false , false , true , false , true ,
2020-02-07 19:47:43 +00:00
} ;
THINGINFO_EXTRA gThingInfoExtra [ ] = {
true , true , true , false , false ,
false , false , false , false , false ,
false , false , false , false , false ,
true , false , false , true , true ,
true , true , false , false , false ,
false , false , true , true , true ,
true , true , true , true , true ,
true ,
} ;
2020-03-01 20:36:28 +00:00
DUDEINFO_EXTRA gDudeInfoExtra [ ] = {
false , false , false , false , false , false , false , false , false , false , true , false ,
false , false , false , false , true , false , true , true , false , true , false , false ,
true , false , false , false , false , false , false , true , true , false , true , true ,
false , true , true , false , true , true , false , true , true , false , true , false ,
false , true , true , true , true , true , false , true , true , false , false , true ,
false , true , true , false , false , true , false , true , true , false , false , false ,
false , false , false , false , false , false , false , false , false , false , false , false ,
false , false , false , false , false , false , false , false , false , false , false , false ,
false , false , false , false , false , false , false , false , false , false , false , false ,
false , false , false , false , false , false , false , false , false , false , false , false ,
false , false , false , false , false , false , false , true , false , false , true , false ,
false , false , false , false , false , false , false , false , false , false , false , false ,
false , true , false , false , true , false , false , false , false , false , false , false ,
false , false , false , false , false , false ,
} ;
2020-02-07 19:47:43 +00:00
// for actor.cpp
//-------------------------------------------------------------------------
2020-02-15 21:53:21 +00:00
bool nnExtIsImmune ( spritetype * pSprite , int dmgType , int minScale ) {
2020-02-07 19:47:43 +00:00
if ( dmgType > = kDmgFall & & dmgType < kDmgMax & & pSprite - > extra > = 0 & & xsprite [ pSprite - > extra ] . locked ! = 1 ) {
if ( pSprite - > type > = kThingBase & & pSprite - > type < kThingMax )
return ( thingInfo [ pSprite - > type - kThingBase ] . dmgControl [ dmgType ] < = minScale ) ;
else if ( IsDudeSprite ( pSprite ) ) {
2020-05-05 18:50:14 +00:00
if ( IsPlayerSprite ( pSprite ) ) return ( gPlayer [ pSprite - > type - kDudePlayer1 ] . damageControl [ dmgType ] ) ;
2020-02-07 19:47:43 +00:00
else if ( pSprite - > type = = kDudeModernCustom ) return ( gGenDudeExtra [ pSprite - > index ] . dmgControl [ dmgType ] < = minScale ) ;
2020-02-08 20:13:29 +00:00
else return ( getDudeInfo ( pSprite - > type ) - > at70 [ dmgType ] < = minScale ) ;
2020-02-07 19:47:43 +00:00
}
}
return true ;
}
bool nnExtEraseModernStuff ( spritetype * pSprite , XSPRITE * pXSprite ) {
bool erased = false ;
switch ( pSprite - > type ) {
// erase all modern types if the map is not extended
case kModernCustomDudeSpawn :
case kModernRandomTX :
case kModernSequentialTX :
case kModernSeqSpawner :
case kModernObjPropertiesChanger :
case kModernObjPicnumChanger :
case kModernObjSizeChanger :
case kModernDudeTargetChanger :
case kModernSectorFXChanger :
case kModernObjDataChanger :
case kModernSpriteDamager :
case kModernObjDataAccumulator :
case kModernEffectSpawner :
case kModernWindGenerator :
case kModernPlayerControl :
2020-03-13 20:59:13 +00:00
case kModernCondition :
case kModernConditionFalse :
2020-02-07 19:47:43 +00:00
pSprite - > type = kSpriteDecoration ;
erased = true ;
break ;
case kItemModernMapLevel :
case kDudeModernCustom :
case kDudeModernCustomBurning :
case kModernThingTNTProx :
case kModernThingEnemyLifeLeech :
pSprite - > type = kSpriteDecoration ;
changespritestat ( pSprite - > index , kStatDecoration ) ;
erased = true ;
break ;
// also erase some modernized vanilla types which was not active
case kMarkerWarpDest :
if ( pSprite - > statnum = = kStatMarker ) break ;
pSprite - > type = kSpriteDecoration ;
erased = true ;
break ;
}
if ( pXSprite - > Sight ) {
pXSprite - > Sight = false ; // it does not work in vanilla at all
erased = true ;
}
if ( pXSprite - > Proximity ) {
// proximity works only for things and dudes in vanilla
switch ( pSprite - > statnum ) {
case kStatThing :
case kStatDude :
break ;
default :
pXSprite - > Proximity = false ;
erased = true ;
break ;
}
}
return erased ;
}
2020-03-30 19:54:28 +00:00
void nnExtTriggerObject ( int objType , int objIndex , int command ) {
switch ( objType ) {
case OBJ_SECTOR :
if ( ! xsectRangeIsFine ( sector [ objIndex ] . extra ) ) break ;
trTriggerSector ( objIndex , & xsector [ sector [ objIndex ] . extra ] , command ) ;
break ;
case OBJ_WALL :
if ( ! xwallRangeIsFine ( wall [ objIndex ] . extra ) ) break ;
trTriggerWall ( objIndex , & xwall [ wall [ objIndex ] . extra ] , command ) ;
break ;
case OBJ_SPRITE :
if ( ! xspriRangeIsFine ( sprite [ objIndex ] . extra ) ) break ;
trTriggerSprite ( objIndex , & xsprite [ sprite [ objIndex ] . extra ] , command ) ;
break ;
}
return ;
}
2020-03-01 20:36:28 +00:00
void nnExtResetGlobals ( ) {
gAllowTrueRandom = gEventRedirectsUsed = false ;
// reset counters
gProxySpritesCount = gSightSpritesCount = gPhysSpritesCount = gImpactSpritesCount = 0 ;
// fill arrays with negative values to avoid index 0 situation
memset ( gSightSpritesList , - 1 , sizeof ( gSightSpritesList ) ) ; memset ( gProxySpritesList , - 1 , sizeof ( gProxySpritesList ) ) ;
memset ( gPhysSpritesList , - 1 , sizeof ( gPhysSpritesList ) ) ; memset ( gImpactSpritesList , - 1 , sizeof ( gImpactSpritesList ) ) ;
2020-03-13 20:59:13 +00:00
// reset tracking conditions, if any
if ( gTrackingCondsCount > 0 ) {
2020-05-19 19:42:29 +00:00
for ( int i = 0 ; i < gTrackingCondsCount ; i + + ) {
2020-03-13 20:59:13 +00:00
TRCONDITION * pCond = & gCondition [ i ] ;
for ( int k = 0 ; k < pCond - > length ; k + + ) {
pCond - > obj [ k ] . index = pCond - > obj [ k ] . cmd = 0 ;
pCond - > obj [ k ] . type = - 1 ;
}
pCond - > length = 0 ;
}
gTrackingCondsCount = 0 ;
}
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
void nnExtInitModernStuff ( bool bSaveLoad ) {
2020-03-01 20:36:28 +00:00
nnExtResetGlobals ( ) ;
2020-02-07 19:47:43 +00:00
// use true random only for single player mode, otherwise use Blood's default one.
2020-05-19 19:42:29 +00:00
/*if (gGameOptions.nGameType == 0 && !VanillaMode() && !DemoRecordStatus()) {
2020-02-07 19:47:43 +00:00
gStdRandom . seed ( std : : random_device ( ) ( ) ) ;
// since true random is not working if compiled with old mingw versions, we should
// check if it works in game and if not - switch to using in-game random function.
for ( int i = kMaxRandomizeRetries ; i > = 0 ; i - - ) {
std : : uniform_int_distribution < int > dist_a_b ( 0 , 100 ) ;
if ( gAllowTrueRandom | | i < = 0 ) break ;
else if ( dist_a_b ( gStdRandom ) ! = 0 )
gAllowTrueRandom = true ;
}
2020-05-19 19:42:29 +00:00
} */
2020-02-07 19:47:43 +00:00
2020-05-19 19:42:29 +00:00
if ( ! gAllowTrueRandom ) Printf ( " > STD randomness is not available, using in-game random function(s) " ) ;
else Printf ( " > Using STD randomness function(s) . " ) ;
2020-03-01 20:36:28 +00:00
2020-02-07 19:47:43 +00:00
for ( int i = 0 ; i < kMaxXSprites ; i + + ) {
if ( xsprite [ i ] . reference < 0 ) continue ;
XSPRITE * pXSprite = & xsprite [ i ] ; spritetype * pSprite = & sprite [ pXSprite - > reference ] ;
2020-05-05 18:50:14 +00:00
2020-02-07 19:47:43 +00:00
switch ( pSprite - > type ) {
2020-03-01 20:36:28 +00:00
case kModernRandomTX :
case kModernSequentialTX :
2020-03-13 20:59:13 +00:00
if ( pSprite - > flags & kModernTypeFlag2 ) gEventRedirectsUsed = true ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
getSpriteMassBySize ( pSprite ) ; // create mass cache
break ;
2020-03-13 20:59:13 +00:00
case kModernCondition :
case kModernConditionFalse :
if ( ! bSaveLoad ) {
2020-04-09 18:51:25 +00:00
pSprite - > yvel = pSprite - > type ; // store it here, because inittype gets cleared later.
pSprite - > type = kModernCondition ;
2020-03-30 19:54:28 +00:00
if ( ! pXSprite - > rxID ) {
ThrowError ( " \n The condition must have RX ID! \n SPRITE #%d " , pSprite - > index ) ;
} else if ( ! pXSprite - > txID & & ! pSprite - > hitag ) {
consoleSysMsg ( " Inactive condition: RX ID %d, SPRITE #%d " , pXSprite - > rxID , pSprite - > index ) ;
2020-04-09 18:51:25 +00:00
break ;
2020-03-30 19:54:28 +00:00
}
2020-04-09 18:51:25 +00:00
2020-03-13 20:59:13 +00:00
}
// collect objects for tracking conditions
if ( pXSprite - > busyTime < = 0 ) break ;
else if ( gTrackingCondsCount > = kMaxTrackingConditions )
ThrowError ( " \n Max (%d) tracking conditions reached! " , kMaxTrackingConditions ) ;
int tracedObjects = 0 ; TRCONDITION * pCond = & gCondition [ gTrackingCondsCount ] ;
for ( int i = 0 ; i < kMaxXSprites ; i + + ) {
2020-05-05 18:50:14 +00:00
if ( ! spriRangeIsFine ( xsprite [ i ] . reference ) | | xsprite [ i ] . txID ! = pXSprite - > rxID | | xsprite [ i ] . reference = = pSprite - > index )
continue ;
XSPRITE * pXSpr = & xsprite [ i ] ; spritetype * pSpr = & sprite [ pXSpr - > reference ] ;
int index = pXSpr - > reference ; int cmd = pXSpr - > command ;
if ( pSpr - > statnum = = kStatModernPlayerLinker & & pXSpr - > isTriggered & & bSaveLoad ) {
// assign player sprite after savegame loading
index = pXSpr - > sysData1 ;
cmd = xsprite [ sprite [ index ] . extra ] . command ;
}
if ( pSpr - > type = = kModernCondition )
ThrowError ( " \n Tracking condition always must be first in condition sequence! \n RX ID: %d, SPRITE #%d " , pXSpr - > rxID , pXSpr - > reference ) ;
if ( tracedObjects > = kMaxTracedObjects )
2020-03-13 20:59:13 +00:00
ThrowError ( " \n Max (%d) objects to track reached for condition #%d, rx id: %d! " , pSprite - > index , pXSprite - > rxID ) ;
2020-05-05 18:50:14 +00:00
pCond - > obj [ tracedObjects ] . type = OBJ_SPRITE ;
pCond - > obj [ tracedObjects ] . index = index ;
pCond - > obj [ tracedObjects + + ] . cmd = cmd ;
2020-03-13 20:59:13 +00:00
}
for ( int i = 0 ; i < kMaxXSectors ; i + + ) {
if ( ! sectRangeIsFine ( xsector [ i ] . reference ) | | xsector [ i ] . txID ! = pXSprite - > rxID ) continue ;
else if ( tracedObjects < kMaxTracedObjects ) {
pCond - > obj [ tracedObjects ] . type = OBJ_SECTOR ;
pCond - > obj [ tracedObjects ] . index = xsector [ i ] . reference ;
pCond - > obj [ tracedObjects + + ] . cmd = xsector [ i ] . command ;
} else {
ThrowError ( " \n Max (%d) objects to track reached for condition #%d, rx id: %d! " , pSprite - > index , pXSprite - > rxID ) ;
}
}
for ( int i = 0 ; i < kMaxXWalls ; i + + ) {
if ( ! sectRangeIsFine ( xwall [ i ] . reference ) | | xwall [ i ] . txID ! = pXSprite - > rxID ) continue ;
else if ( tracedObjects < kMaxTracedObjects ) {
pCond - > obj [ tracedObjects ] . type = OBJ_WALL ;
pCond - > obj [ tracedObjects ] . index = xwall [ i ] . reference ;
pCond - > obj [ tracedObjects + + ] . cmd = xwall [ i ] . command ;
} else {
ThrowError ( " \n Max (%d) objects to track reached for condition #%d, rx id: %d! " , pSprite - > index , pXSprite - > rxID ) ;
}
}
pCond - > length = tracedObjects ;
if ( tracedObjects = = 0 )
consoleSysMsg ( " No objects to track found for condition #%d, rx id: %d! " , pSprite - > index , pXSprite - > rxID ) ;
pXSprite - > sysData1 = gTrackingCondsCount + + ;
break ;
2020-02-07 19:47:43 +00:00
}
// init after loading save file
if ( bSaveLoad ) {
// add in list of physics affected sprites
if ( pXSprite - > physAttr ! = 0 ) {
//xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 0;
gPhysSpritesList [ gPhysSpritesCount + + ] = pSprite - > index ; // add sprite index
getSpriteMassBySize ( pSprite ) ; // create mass cache
}
if ( pXSprite - > data3 ! = pXSprite - > sysData1 ) {
switch ( pSprite - > statnum ) {
case kStatDude :
switch ( pSprite - > type ) {
case kDudeModernCustom :
case kDudeModernCustomBurning :
pXSprite - > data3 = pXSprite - > sysData1 ; // move sndStartId back from sysData1 to data3
break ;
}
break ;
}
}
} else {
2020-04-09 18:51:25 +00:00
// copy custom start health to avoid overwrite by kThingBloodChunks
2020-04-04 19:48:23 +00:00
if ( IsDudeSprite ( pSprite ) )
pXSprite - > sysData2 = pXSprite - > data4 ;
2020-04-09 18:51:25 +00:00
// check reserved statnums
if ( pSprite - > statnum > = kStatModernBase & & pSprite - > statnum < kStatModernMax ) {
bool sysStat = true ;
switch ( pSprite - > statnum ) {
case kStatModernDudeTargetChanger :
2020-05-05 18:50:14 +00:00
sysStat = ( pSprite - > type = = kModernDudeTargetChanger ) ;
2020-04-09 18:51:25 +00:00
break ;
case kStatModernCondition :
2020-05-05 18:50:14 +00:00
sysStat = ( pSprite - > type = = kModernCondition | | pSprite - > type = = kModernConditionFalse ) ;
2020-04-09 18:51:25 +00:00
break ;
case kStatModernEventRedirector :
2020-05-05 18:50:14 +00:00
sysStat = ( pSprite - > type = = kModernRandomTX | | pSprite - > type = = kModernSequentialTX ) ;
break ;
case kStatModernSeqSpawner :
sysStat = ( pSprite - > type = = kModernSeqSpawner ) ;
2020-04-09 18:51:25 +00:00
break ;
case kStatModernPlayerLinker :
2020-05-05 18:50:14 +00:00
case kStatModernQavScene :
sysStat = ( pSprite - > type = = kModernPlayerControl ) ;
2020-04-09 18:51:25 +00:00
break ;
}
2020-04-04 19:48:23 +00:00
2020-04-09 18:51:25 +00:00
if ( sysStat )
ThrowError ( " Sprite status list number %d on sprite #%d is in a range of reserved (%d - %d)! " , pSprite - > index , pSprite - > statnum , kStatModernBase , kStatModernMax ) ;
}
2020-04-07 20:30:00 +00:00
2020-02-07 19:47:43 +00:00
switch ( pSprite - > type ) {
2020-03-13 20:59:13 +00:00
case kModernRandomTX :
case kModernSequentialTX :
2020-05-05 18:50:14 +00:00
if ( pXSprite - > command ! = kCmdLink ) break ;
2020-03-13 20:59:13 +00:00
// add statnum for faster redirects search
changespritestat ( pSprite - > index , kStatModernEventRedirector ) ;
break ;
2020-02-07 19:47:43 +00:00
case kModernWindGenerator :
pSprite - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
break ;
case kModernDudeTargetChanger :
case kModernObjDataAccumulator :
case kModernRandom :
case kModernRandom2 :
pSprite - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
pSprite - > cstat | = CSTAT_SPRITE_INVISIBLE ;
switch ( pSprite - > type ) {
// add statnum for faster dude searching
case kModernDudeTargetChanger :
changespritestat ( pSprite - > index , kStatModernDudeTargetChanger ) ;
if ( pXSprite - > busyTime < = 0 ) pXSprite - > busyTime = 5 ;
2020-05-05 18:50:14 +00:00
pXSprite - > command = kCmdLink ;
2020-02-07 19:47:43 +00:00
break ;
// remove kStatItem status from random item generators
case kModernRandom :
case kModernRandom2 :
changespritestat ( pSprite - > index , kStatDecoration ) ;
break ;
}
break ;
2020-05-05 18:50:14 +00:00
case kModernSeqSpawner :
changespritestat ( pSprite - > index , kStatModernSeqSpawner ) ;
break ;
2020-02-07 19:47:43 +00:00
case kModernThingTNTProx :
pXSprite - > Proximity = true ;
break ;
2020-04-04 19:48:23 +00:00
case kDudeModernCustom :
if ( pXSprite - > txID < = 0 ) break ;
for ( int nSprite = headspritestat [ kStatDude ] , found = 0 ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
XSPRITE * pXSpr = & xsprite [ sprite [ nSprite ] . extra ] ;
if ( pXSpr - > rxID ! = pXSprite - > txID ) continue ;
else if ( found ) ThrowError ( " \n Custom dude (TX ID %d): \n Only one incarnation allowed per channel! " , pXSprite - > txID ) ;
changespritestat ( nSprite , kStatInactive ) ;
nSprite = headspritestat [ kStatDude ] ;
found + + ;
}
break ;
case kModernPlayerControl :
2020-05-05 18:50:14 +00:00
switch ( pXSprite - > command ) {
case kCmdLink :
if ( pXSprite - > data1 < 1 | | pXSprite - > data1 > = kMaxPlayers )
2020-04-04 19:48:23 +00:00
ThrowError ( " \n Player Control (SPRITE #%d): \n Player out of a range (data1 = %d) " , pSprite - > index , pXSprite - > data1 ) ;
2020-05-05 18:50:14 +00:00
//if (numplayers < pXSprite->data1)
//ThrowError("\nPlayer Control (SPRITE #%d):\n There is no player #%d", pSprite->index, pXSprite->data1);
2020-04-04 19:48:23 +00:00
if ( pXSprite - > rxID & & pXSprite - > rxID ! = kChannelLevelStart )
ThrowError ( " \n Player Control (SPRITE #%d) with Link command should have no RX ID! " , pSprite - > index , pXSprite - > data1 ) ;
2020-05-05 18:50:14 +00:00
2020-04-04 19:48:23 +00:00
if ( pXSprite - > txID & & pXSprite - > txID < kChannelUser )
ThrowError ( " \n Player Control (SPRITE #%d): \n TX ID should be in range of %d and %d! " , pSprite - > index , kChannelUser , kChannelMax ) ;
// only one linker per player allowed
for ( int nSprite = headspritestat [ kStatModernPlayerLinker ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
XSPRITE * pXCtrl = & xsprite [ sprite [ nSprite ] . extra ] ;
if ( pXSprite - > data1 = = pXCtrl - > data1 )
ThrowError ( " \n Player Control (SPRITE #%d): \n Player %d already linked with different player control sprite #%d! " , pSprite - > index , pXSprite - > data1 , nSprite ) ;
}
2020-05-05 18:50:14 +00:00
pXSprite - > sysData1 = - 1 ;
pSprite - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
2020-04-04 19:48:23 +00:00
changespritestat ( pSprite - > index , kStatModernPlayerLinker ) ;
2020-05-05 18:50:14 +00:00
break ;
case 67 : // play qav animation
if ( pXSprite - > txID & & ! pXSprite - > waitTime ) pXSprite - > waitTime = 1 ;
changespritestat ( pSprite - > index , kStatModernQavScene ) ;
break ;
}
2020-04-04 19:48:23 +00:00
break ;
2020-03-01 20:36:28 +00:00
case kModernCondition :
2020-03-30 19:54:28 +00:00
if ( pXSprite - > waitTime > 0 & & pXSprite - > busyTime > 0 ) {
2020-05-05 18:50:14 +00:00
pXSprite - > busyTime + = ClipHigh ( ( ( pXSprite - > waitTime * 120 ) / 10 ) , 4095 ) ; pXSprite - > waitTime = 0 ;
2020-04-04 19:48:23 +00:00
consoleSysMsg ( " Summing busyTime and waitTime for tracking condition #%d, RX ID %d. Result = %d ticks " , pSprite - > index , pXSprite - > rxID , pXSprite - > busyTime ) ;
2020-03-30 19:54:28 +00:00
}
2020-03-13 20:59:13 +00:00
pXSprite - > Decoupled = false ; // must go through operateSprite always
2020-05-05 18:50:14 +00:00
pXSprite - > Sight = pXSprite - > Impact = pXSprite - > Touch = pXSprite - > triggerOff = false ;
pXSprite - > Proximity = pXSprite - > Push = pXSprite - > Vector = pXSprite - > triggerOn = false ;
2020-03-13 20:59:13 +00:00
pXSprite - > state = pXSprite - > restState = 0 ;
2020-03-30 19:54:28 +00:00
pXSprite - > targetX = pXSprite - > targetY = pXSprite - > targetZ = pXSprite - > target = - 1 ;
2020-03-13 20:59:13 +00:00
changespritestat ( pSprite - > index , kStatModernCondition ) ;
pSprite - > cstat | = CSTAT_SPRITE_INVISIBLE ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
2020-04-04 19:48:23 +00:00
// the following trigger flags are senseless to have together
2020-03-01 20:36:28 +00:00
if ( ( pXSprite - > Touch & & ( pXSprite - > Proximity | | pXSprite - > Sight ) & & pXSprite - > DudeLockout )
2020-03-04 23:26:37 +00:00
| | ( pXSprite - > Touch & & pXSprite - > Proximity & & ! pXSprite - > Sight ) ) pXSprite - > Touch = false ;
2020-03-01 20:36:28 +00:00
if ( pXSprite - > Proximity & & pXSprite - > Sight & & pXSprite - > DudeLockout )
pXSprite - > Proximity = false ;
2020-02-07 19:47:43 +00:00
// very quick fix for floor sprites with Touch trigger flag if their Z is equals sector floorz / ceilgz
2020-03-01 20:36:28 +00:00
if ( pSprite - > sectnum > = 0 & & pXSprite - > Touch & & ( pSprite - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) ) {
2020-02-07 19:47:43 +00:00
if ( pSprite - > z = = sector [ pSprite - > sectnum ] . floorz ) pSprite - > z - - ;
else if ( pSprite - > z = = sector [ pSprite - > sectnum ] . ceilingz ) pSprite - > z + + ;
}
}
// make Proximity flag work not just for dudes and things...
if ( pXSprite - > Proximity & & gProxySpritesCount < kMaxSuperXSprites ) {
switch ( pSprite - > statnum ) {
2020-03-01 20:36:28 +00:00
case kStatFX : case kStatExplosion : case kStatItem :
case kStatPurge : case kStatSpares : case kStatFlare :
case kStatInactive : case kStatFree : case kStatMarker :
case kStatPathMarker : case kStatThing : case kStatDude :
2020-05-05 18:50:14 +00:00
case kStatModernPlayerLinker :
2020-03-01 20:36:28 +00:00
break ;
default :
2020-02-07 19:47:43 +00:00
gProxySpritesList [ gProxySpritesCount + + ] = pSprite - > index ;
if ( gProxySpritesCount = = kMaxSuperXSprites )
ThrowError ( " Max (%d) *additional* Proximity sprites reached! " , kMaxSuperXSprites ) ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
}
// make Sight flag work not just for dudes and things...
if ( pXSprite - > Sight & & gSightSpritesCount < kMaxSuperXSprites ) {
switch ( pSprite - > statnum ) {
2020-03-01 20:36:28 +00:00
case kStatFX : case kStatExplosion : case kStatItem :
case kStatPurge : case kStatSpares : case kStatFlare :
case kStatInactive : case kStatFree : case kStatMarker :
2020-05-05 18:50:14 +00:00
case kStatPathMarker : case kStatModernPlayerLinker :
2020-03-01 20:36:28 +00:00
break ;
default :
gSightSpritesList [ gSightSpritesCount + + ] = pSprite - > index ;
if ( gSightSpritesCount = = kMaxSuperXSprites )
ThrowError ( " Max (%d) Sight sprites reached! " , kMaxSuperXSprites ) ;
break ;
}
}
// make Impact flag work for sprites that affected by explosions...
if ( pXSprite - > Impact & & gImpactSpritesCount < kMaxSuperXSprites ) {
switch ( pSprite - > statnum ) {
case kStatFX : case kStatExplosion : case kStatItem :
case kStatPurge : case kStatSpares : case kStatFlare :
case kStatInactive : case kStatFree : case kStatMarker :
2020-05-05 18:50:14 +00:00
case kStatPathMarker : case kStatModernPlayerLinker :
2020-03-01 20:36:28 +00:00
break ;
default :
gImpactSpritesList [ gImpactSpritesCount + + ] = pSprite - > index ;
if ( gImpactSpritesCount = = kMaxSuperXSprites )
ThrowError ( " Max (%d) *additional* Impact sprites reached! " , kMaxSuperXSprites ) ;
break ;
2020-02-07 19:47:43 +00:00
}
}
}
2020-05-05 18:50:14 +00:00
2020-05-19 19:42:29 +00:00
if ( ! bSaveLoad ) {
// let's try to find "else" and "else if" of conditions here
spritetype * pCond = NULL ; XSPRITE * pXCond = NULL ;
bool found = false ; int rx = 0 ; int sum1 = 0 ; int sum2 = 0 ;
for ( int i = headspritestat [ kStatModernCondition ] ; i > = 0 ; ) {
pCond = & sprite [ i ] ; pXCond = & xsprite [ pCond - > extra ] ;
sum1 = pXCond - > locked + pXCond - > busyTime + pXCond - > waitTime + pXCond - > data1 ;
if ( ! found ) rx = pXCond - > rxID ;
for ( int a = i ; a > = 0 ; a = nextspritestat [ a ] , found = false ) {
spritetype * pCond2 = & sprite [ a ] ; XSPRITE * pXCond2 = & xsprite [ pCond2 - > extra ] ;
sum2 = pXCond2 - > locked + pXCond2 - > busyTime + pXCond2 - > waitTime + pXCond2 - > data1 ;
if ( pXCond2 - > rxID ! = rx | | pCond2 - > index = = pCond - > index | | sum1 ! = sum2 ) continue ;
else if ( ( pCond2 - > type ! = pCond - > type ) ^ ( pCond2 - > cstat ! = pCond - > cstat ) ) {
initprintf ( " > ELSE IF found for condition #%d (RX ID: %d, CONDID: %d) \n " , i , rx , pXCond - > data1 ) ;
pXCond2 - > rxID = pXCond2 - > busyTime = 0 ;
pXCond - > sysData2 = pCond2 - > index ;
i = a ; found = true ;
break ;
}
}
if ( ! found ) i = nextspritestat [ i ] ;
}
}
// collect objects for tracking conditions
for ( int i = headspritestat [ kStatModernCondition ] ; i > = 0 ; i = nextspritestat [ i ] ) {
spritetype * pSprite = & sprite [ i ] ; XSPRITE * pXSprite = & xsprite [ pSprite - > extra ] ;
if ( pXSprite - > busyTime < = 0 ) continue ;
else if ( gTrackingCondsCount > = kMaxTrackingConditions )
ThrowError ( " \n Max (%d) tracking conditions reached! " , kMaxTrackingConditions ) ;
int count = 0 ;
TRCONDITION * pCond = & gCondition [ gTrackingCondsCount ] ;
for ( int i = 0 ; i < kMaxXSprites ; i + + ) {
if ( ! spriRangeIsFine ( xsprite [ i ] . reference ) | | xsprite [ i ] . txID ! = pXSprite - > rxID | | xsprite [ i ] . reference = = pSprite - > index )
continue ;
XSPRITE * pXSpr = & xsprite [ i ] ; spritetype * pSpr = & sprite [ pXSpr - > reference ] ;
int index = pXSpr - > reference ; int cmd = pXSpr - > command ;
switch ( pSpr - > type ) {
case kSwitchToggle : // exceptions
case kSwitchOneWay : // exceptions
continue ;
case kModernPlayerControl :
if ( pSpr - > statnum ! = kStatModernPlayerLinker | | ! bSaveLoad ) break ;
// assign player sprite after savegame loading
index = pXSpr - > sysData1 ;
cmd = xsprite [ sprite [ index ] . extra ] . command ;
break ;
}
if ( pSpr - > type = = kModernCondition | | pSpr - > type = = kModernConditionFalse )
condError ( pXSprite , " Tracking condition always must be first in condition sequence! " ) ;
if ( count > = kMaxTracedObjects )
condError ( pXSprite , " Max(% d) objects to track reached for condition # % d, rx id : % d! " ) ;
pCond - > obj [ count ] . type = OBJ_SPRITE ;
pCond - > obj [ count ] . index = index ;
pCond - > obj [ count + + ] . cmd = cmd ;
}
for ( int i = 0 ; i < kMaxXSectors ; i + + ) {
if ( ! sectRangeIsFine ( xsector [ i ] . reference ) | | xsector [ i ] . txID ! = pXSprite - > rxID ) continue ;
else if ( count > = kMaxTracedObjects )
condError ( pXSprite , " Max(% d) objects to track reached for condition # % d, rx id : % d! " ) ;
pCond - > obj [ count ] . type = OBJ_SECTOR ;
pCond - > obj [ count ] . index = xsector [ i ] . reference ;
pCond - > obj [ count + + ] . cmd = xsector [ i ] . command ;
}
for ( int i = 0 ; i < kMaxXWalls ; i + + ) {
if ( ! wallRangeIsFine ( xwall [ i ] . reference ) | | xwall [ i ] . txID ! = pXSprite - > rxID )
continue ;
walltype * pWall = & wall [ xwall [ i ] . reference ] ;
switch ( pWall - > type ) {
case kSwitchToggle : // exceptions
case kSwitchOneWay : // exceptions
continue ;
}
if ( count > = kMaxTracedObjects )
condError ( pXSprite , " Max(% d) objects to track reached for condition # % d, rx id : % d! " ) ;
pCond - > obj [ count ] . type = OBJ_WALL ;
pCond - > obj [ count ] . index = xwall [ i ] . reference ;
pCond - > obj [ count + + ] . cmd = xwall [ i ] . command ;
}
if ( count = = 0 )
consoleSysMsg ( " No objects to track found for condition #%d, rx id: %d! " , pSprite - > index , pXSprite - > rxID ) ;
pCond - > length = count ;
pCond - > xindex = pSprite - > extra ;
gTrackingCondsCount + + ;
}
2020-02-07 19:47:43 +00:00
}
// The following functions required for random event features
//-------------------------
int nnExtRandom ( int a , int b ) {
2020-03-01 20:36:28 +00:00
if ( ! gAllowTrueRandom ) return Random ( ( ( b + 1 ) - a ) ) + a ;
2020-02-07 19:47:43 +00:00
// used for better randomness in single player
std : : uniform_int_distribution < int > dist_a_b ( a , b ) ;
return dist_a_b ( gStdRandom ) ;
}
int GetDataVal ( spritetype * pSprite , int data ) {
if ( pSprite - > extra > = 0 ) {
switch ( data ) {
case 0 : return xsprite [ pSprite - > extra ] . data1 ;
case 1 : return xsprite [ pSprite - > extra ] . data2 ;
case 2 : return xsprite [ pSprite - > extra ] . data3 ;
case 3 : return xsprite [ pSprite - > extra ] . data4 ;
}
}
return - 1 ;
}
// tries to get random data field of sprite
int randomGetDataValue ( XSPRITE * pXSprite , int randType ) {
if ( pXSprite = = NULL ) return - 1 ;
int random = 0 ; int bad = 0 ; int maxRetries = kMaxRandomizeRetries ;
int rData [ 4 ] ;
rData [ 0 ] = pXSprite - > data1 ; rData [ 2 ] = pXSprite - > data3 ;
rData [ 1 ] = pXSprite - > data2 ; rData [ 3 ] = pXSprite - > data4 ;
// randomize only in case if at least 2 data fields fits.
for ( int i = 0 ; i < 4 ; i + + ) {
switch ( randType ) {
case kRandomizeItem :
if ( rData [ i ] > = kItemWeaponBase & & rData [ i ] < kItemMax ) break ;
else bad + + ;
break ;
case kRandomizeDude :
if ( rData [ i ] > = kDudeBase & & rData [ i ] < kDudeMax ) break ;
else bad + + ;
break ;
case kRandomizeTX :
if ( rData [ i ] > kChannelZero & & rData [ i ] < kChannelUserMax ) break ;
else bad + + ;
break ;
default :
bad + + ;
break ;
}
}
if ( bad < 3 ) {
// try randomize few times
while ( maxRetries > 0 ) {
random = nnExtRandom ( 0 , 3 ) ;
if ( rData [ random ] > 0 ) return rData [ random ] ;
else maxRetries - - ;
}
}
return - 1 ;
}
// this function drops random item using random pickup generator(s)
spritetype * randomDropPickupObject ( spritetype * pSource , short prevItem ) {
spritetype * pSprite2 = NULL ; int selected = - 1 ; int maxRetries = 9 ;
if ( xspriRangeIsFine ( pSource - > extra ) ) {
XSPRITE * pXSource = & xsprite [ pSource - > extra ] ;
while ( ( selected = randomGetDataValue ( pXSource , kRandomizeItem ) ) = = prevItem ) if ( maxRetries - - < = 0 ) break ;
if ( selected > 0 ) {
pSprite2 = actDropObject ( pSource , selected ) ;
if ( pSprite2 ! = NULL ) {
pXSource - > dropMsg = pSprite2 - > type ; // store dropped item type in dropMsg
pSprite2 - > x = pSource - > x ;
pSprite2 - > y = pSource - > y ;
pSprite2 - > z = pSource - > z ;
if ( ( pSource - > flags & kModernTypeFlag1 ) & & ( pXSource - > txID > 0 | | ( pXSource - > txID ! = 3 & & pXSource - > lockMsg > 0 ) ) & &
dbInsertXSprite ( pSprite2 - > index ) > 0 ) {
XSPRITE * pXSprite2 = & xsprite [ pSprite2 - > extra ] ;
// inherit spawn sprite trigger settings, so designer can send command when item picked up.
pXSprite2 - > txID = pXSource - > txID ;
pXSprite2 - > command = pXSource - > command ;
pXSprite2 - > triggerOn = pXSource - > triggerOn ;
pXSprite2 - > triggerOff = pXSource - > triggerOff ;
pXSprite2 - > Pickup = true ;
}
}
}
}
return pSprite2 ;
}
// this function spawns random dude using dudeSpawn
spritetype * randomSpawnDude ( spritetype * pSource ) {
spritetype * pSprite2 = NULL ; int selected = - 1 ;
if ( xspriRangeIsFine ( pSource - > extra ) ) {
XSPRITE * pXSource = & xsprite [ pSource - > extra ] ;
if ( ( selected = randomGetDataValue ( pXSource , kRandomizeDude ) ) > 0 )
pSprite2 = actSpawnDude ( pSource , selected , - 1 , 0 ) ;
}
return pSprite2 ;
}
//-------------------------
void nnExtProcessSuperSprites ( ) {
// process additional proximity sprites
if ( gProxySpritesCount > 0 ) {
for ( int i = 0 ; i < gProxySpritesCount ; i + + ) {
if ( sprite [ gProxySpritesList [ i ] ] . extra < 0 ) continue ;
XSPRITE * pXProxSpr = & xsprite [ sprite [ gProxySpritesList [ i ] ] . extra ] ;
if ( ! pXProxSpr - > Proximity | | ( ! pXProxSpr - > Interrutable & & pXProxSpr - > state ! = pXProxSpr - > restState ) | | pXProxSpr - > locked = = 1
| | pXProxSpr - > isTriggered ) continue ; // don't process locked or triggered sprites
int x = sprite [ gProxySpritesList [ i ] ] . x ; int y = sprite [ gProxySpritesList [ i ] ] . y ;
int z = sprite [ gProxySpritesList [ i ] ] . z ; int index = sprite [ gProxySpritesList [ i ] ] . index ;
int sectnum = sprite [ gProxySpritesList [ i ] ] . sectnum ;
if ( ! pXProxSpr - > DudeLockout ) {
for ( int nAffected = headspritestat [ kStatDude ] ; nAffected > = 0 ; nAffected = nextspritestat [ nAffected ] ) {
if ( ( sprite [ nAffected ] . flags & 32 ) | | xsprite [ sprite [ nAffected ] . extra ] . health < = 0 ) continue ;
else if ( CheckProximity ( & sprite [ nAffected ] , x , y , z , sectnum , 96 ) ) {
trTriggerSprite ( index , pXProxSpr , kCmdSpriteProximity ) ;
break ;
}
}
} else {
for ( int a = connecthead ; a > = 0 ; a = connectpoint2 [ a ] ) {
if ( gPlayer [ a ] . pXSprite - > health > 0 & & CheckProximity ( gPlayer [ a ] . pSprite , x , y , z , sectnum , 96 ) ) {
trTriggerSprite ( index , pXProxSpr , kCmdSpriteProximity ) ;
break ;
}
}
}
}
}
// process sight sprites (for players only)
if ( gSightSpritesCount > 0 ) {
for ( int i = 0 ; i < gSightSpritesCount ; i + + ) {
if ( sprite [ gSightSpritesList [ i ] ] . extra < 0 ) continue ;
XSPRITE * pXSightSpr = & xsprite [ sprite [ gSightSpritesList [ i ] ] . extra ] ;
if ( ! pXSightSpr - > Sight | | ( ! pXSightSpr - > Interrutable & & pXSightSpr - > state ! = pXSightSpr - > restState ) | | pXSightSpr - > locked = = 1 | |
pXSightSpr - > isTriggered ) continue ; // don't process locked or triggered sprites
int x = sprite [ gSightSpritesList [ i ] ] . x ; int y = sprite [ gSightSpritesList [ i ] ] . y ;
int z = sprite [ gSightSpritesList [ i ] ] . z ; int index = sprite [ gSightSpritesList [ i ] ] . index ;
int sectnum = sprite [ gSightSpritesList [ i ] ] . sectnum ;
for ( int a = connecthead ; a > = 0 ; a = connectpoint2 [ a ] ) {
spritetype * pPlaySprite = gPlayer [ a ] . pSprite ;
if ( gPlayer [ a ] . pXSprite - > health > 0 & & cansee ( x , y , z , sectnum , pPlaySprite - > x , pPlaySprite - > y , pPlaySprite - > z , pPlaySprite - > sectnum ) ) {
trTriggerSprite ( index , pXSightSpr , kCmdSpriteSight ) ;
break ;
}
}
}
}
// process Debris sprites for movement
if ( gPhysSpritesCount > 0 ) {
for ( int i = 0 ; i < gPhysSpritesCount ; i + + ) {
if ( gPhysSpritesList [ i ] = = - 1 ) continue ;
else if ( sprite [ gPhysSpritesList [ i ] ] . statnum = = kStatFree | | ( sprite [ gPhysSpritesList [ i ] ] . flags & kHitagFree ) ! = 0 ) {
gPhysSpritesList [ i ] = - 1 ;
continue ;
}
XSPRITE * pXDebris = & xsprite [ sprite [ gPhysSpritesList [ i ] ] . extra ] ;
if ( ! ( pXDebris - > physAttr & kPhysMove ) & & ! ( pXDebris - > physAttr & kPhysGravity ) ) {
gPhysSpritesList [ i ] = - 1 ;
continue ;
}
spritetype * pDebris = & sprite [ gPhysSpritesList [ i ] ] ;
XSECTOR * pXSector = ( sector [ pDebris - > sectnum ] . extra > = 0 ) ? & xsector [ sector [ pDebris - > sectnum ] . extra ] : NULL ;
viewBackupSpriteLoc ( pDebris - > index , pDebris ) ;
int airVel = gSpriteMass [ pDebris - > extra ] . airVel ;
if ( pXSector ! = NULL ) {
if ( pXSector - > Underwater ) airVel < < = 6 ;
if ( pXSector - > panVel ! = 0 ) {
int top , bottom ;
GetSpriteExtents ( pDebris , & top , & bottom ) ;
if ( getflorzofslope ( pDebris - > sectnum , pDebris - > x , pDebris - > y ) < = bottom )
{
int angle = pXSector - > panAngle ;
int speed = 0 ;
if ( pXSector - > panAlways | | pXSector - > state | | pXSector - > busy )
{
speed = pXSector - > panVel < < 9 ;
if ( ! pXSector - > panAlways & & pXSector - > busy )
speed = mulscale16 ( speed , pXSector - > busy ) ;
}
if ( sector [ pDebris - > sectnum ] . floorstat & 64 )
angle = ( angle + GetWallAngle ( sector [ pDebris - > sectnum ] . wallptr ) + 512 ) & 2047 ;
int dx = mulscale30 ( speed , Cos ( angle ) ) ;
int dy = mulscale30 ( speed , Sin ( angle ) ) ;
xvel [ pDebris - > index ] + = dx ;
yvel [ pDebris - > index ] + = dy ;
}
}
}
actAirDrag ( pDebris , airVel ) ;
if ( ( ( pDebris - > index > > 8 ) & 15 ) = = ( gFrame & 15 ) & & ( pXDebris - > physAttr & kPhysGravity ) )
pXDebris - > physAttr | = kPhysFalling ;
if ( ( pXDebris - > physAttr & 4 ) = = 0 & & xvel [ pDebris - > index ] = = 0 & & yvel [ pDebris - > index ] = = 0 & &
zvel [ pDebris - > index ] = = 0 & & velFloor [ pDebris - > sectnum ] = = 0 & & velCeil [ pDebris - > sectnum ] = = 0 )
continue ;
debrisMove ( i ) ;
}
}
}
// this function plays sound predefined in missile info
void sfxPlayMissileSound ( spritetype * pSprite , int missileId ) {
MISSILEINFO_EXTRA * pMissType = & gMissileInfoExtra [ missileId - kMissileBase ] ;
sfxPlay3DSound ( pSprite , Chance ( 0x5000 ) ? pMissType - > fireSound [ 0 ] : pMissType - > fireSound [ 1 ] , - 1 , 0 ) ;
}
// this function plays sound predefined in vector info
void sfxPlayVectorSound ( spritetype * pSprite , int vectorId ) {
VECTORINFO_EXTRA * pVectorData = & gVectorInfoExtra [ vectorId ] ;
sfxPlay3DSound ( pSprite , Chance ( 0x5000 ) ? pVectorData - > fireSound [ 0 ] : pVectorData - > fireSound [ 1 ] , - 1 , 0 ) ;
}
int getSpriteMassBySize ( spritetype * pSprite ) {
int mass = 0 ; int seqId = - 1 ; int clipDist = pSprite - > clipdist ; Seq * pSeq = NULL ;
2020-03-01 20:36:28 +00:00
if ( pSprite - > extra < 0 ) {
ThrowError ( " getSpriteMassBySize: pSprite->extra < 0 " ) ;
} else if ( IsDudeSprite ( pSprite ) ) {
2020-02-07 19:47:43 +00:00
switch ( pSprite - > type ) {
case kDudePodMother : // fake dude, no seq
break ;
case kDudeModernCustom :
case kDudeModernCustomBurning :
seqId = xsprite [ pSprite - > extra ] . data2 ;
clipDist = gGenDudeExtra [ pSprite - > index ] . initVals [ 2 ] ;
break ;
default :
2020-02-08 20:13:29 +00:00
seqId = getDudeInfo ( pSprite - > type ) - > seqStartID ;
2020-02-07 19:47:43 +00:00
break ;
}
2020-03-01 20:36:28 +00:00
} else {
2020-02-07 19:47:43 +00:00
seqId = seqGetID ( 3 , pSprite - > extra ) ;
}
SPRITEMASS * cached = & gSpriteMass [ pSprite - > extra ] ;
if ( ( ( seqId > = 0 & & seqId = = cached - > seqId ) | | pSprite - > picnum = = cached - > picnum ) & & pSprite - > xrepeat = = cached - > xrepeat & &
pSprite - > yrepeat = = cached - > yrepeat & & clipDist = = cached - > clipdist ) {
return cached - > mass ;
}
short picnum = pSprite - > picnum ;
short massDiv = 30 ; short addMul = 2 ; short subMul = 2 ;
if ( seqId > = 0 ) {
DICTNODE * hSeq = gSysRes . Lookup ( seqId , " SEQ " ) ;
if ( hSeq )
{
pSeq = ( Seq * ) gSysRes . Load ( hSeq ) ;
picnum = seqGetTile ( & pSeq - > frames [ 0 ] ) ;
} else
picnum = pSprite - > picnum ;
}
clipDist = ClipLow ( pSprite - > clipdist , 1 ) ;
short x = tilesiz [ picnum ] . x ; short y = tilesiz [ picnum ] . y ;
short xrepeat = pSprite - > xrepeat ; short yrepeat = pSprite - > yrepeat ;
// take surface type into account
switch ( tileGetSurfType ( pSprite - > index + 0xc000 ) ) {
case 1 : massDiv = 16 ; break ; // stone
case 2 : massDiv = 18 ; break ; // metal
case 3 : massDiv = 21 ; break ; // wood
case 4 : massDiv = 25 ; break ; // flesh
case 5 : massDiv = 28 ; break ; // water
case 6 : massDiv = 26 ; break ; // dirt
case 7 : massDiv = 27 ; break ; // clay
case 8 : massDiv = 35 ; break ; // snow
case 9 : massDiv = 22 ; break ; // ice
case 10 : massDiv = 37 ; break ; // leaves
case 11 : massDiv = 33 ; break ; // cloth
case 12 : massDiv = 36 ; break ; // plant
case 13 : massDiv = 24 ; break ; // goo
case 14 : massDiv = 23 ; break ; // lava
}
mass = ( ( x + y ) * ( clipDist / 2 ) ) / massDiv ;
if ( xrepeat > 64 ) mass + = ( ( xrepeat - 64 ) * addMul ) ;
else if ( xrepeat < 64 & & mass > 0 ) {
for ( int i = 64 - xrepeat ; i > 0 ; i - - ) {
if ( ( mass - = subMul ) < = 100 & & subMul - - < = 1 ) {
mass - = i ;
break ;
}
}
}
if ( yrepeat > 64 ) mass + = ( ( yrepeat - 64 ) * addMul ) ;
else if ( yrepeat < 64 & & mass > 0 ) {
for ( int i = 64 - yrepeat ; i > 0 ; i - - ) {
if ( ( mass - = subMul ) < = 100 & & subMul - - < = 1 ) {
mass - = i ;
break ;
}
}
}
if ( mass < = 0 ) cached - > mass = 1 + Random ( 10 ) ;
else cached - > mass = ClipRange ( mass , 1 , 65535 ) ;
cached - > airVel = ClipRange ( 400 - cached - > mass , 32 , 400 ) ;
cached - > fraction = ClipRange ( 60000 - ( cached - > mass < < 7 ) , 8192 , 60000 ) ;
cached - > xrepeat = pSprite - > xrepeat ; cached - > yrepeat = pSprite - > yrepeat ;
cached - > picnum = pSprite - > picnum ; cached - > seqId = seqId ;
cached - > clipdist = pSprite - > clipdist ;
return cached - > mass ;
}
int debrisGetIndex ( int nSprite ) {
if ( sprite [ nSprite ] . extra < 0 | | xsprite [ sprite [ nSprite ] . extra ] . physAttr = = 0 )
return - 1 ;
for ( int i = 0 ; i < gPhysSpritesCount ; i + + ) {
if ( gPhysSpritesList [ i ] ! = nSprite ) continue ;
return i ;
}
return - 1 ;
}
int debrisGetFreeIndex ( void ) {
for ( int i = 0 ; i < kMaxSuperXSprites ; i + + ) {
if ( gPhysSpritesList [ i ] = = - 1 | | sprite [ gPhysSpritesList [ i ] ] . statnum = = kStatFree ) return i ;
else if ( ( sprite [ gPhysSpritesList [ i ] ] . flags & kHitagFree ) | | sprite [ gPhysSpritesList [ i ] ] . extra < 0 ) return i ;
else if ( xsprite [ sprite [ gPhysSpritesList [ i ] ] . extra ] . physAttr = = 0 ) return i ;
}
return - 1 ;
}
void debrisConcuss ( int nOwner , int listIndex , int x , int y , int z , int dmg ) {
spritetype * pSprite = ( gPhysSpritesList [ listIndex ] > = 0 ) ? & sprite [ gPhysSpritesList [ listIndex ] ] : NULL ;
if ( pSprite ! = NULL & & pSprite - > extra > = 0 & & pSprite - > extra < kMaxXSprites ) {
int dx = pSprite - > x - x ; int dy = pSprite - > y - y ; int dz = ( pSprite - > z - z ) > > 4 ;
dmg = scale ( 0x40000 , dmg , 0x40000 + dx * dx + dy * dy + dz * dz ) ;
int size = ( tilesiz [ pSprite - > picnum ] . x * pSprite - > xrepeat * tilesiz [ pSprite - > picnum ] . y * pSprite - > yrepeat ) > > 1 ;
if ( xsprite [ pSprite - > extra ] . physAttr & kPhysDebrisExplode ) {
if ( gSpriteMass [ pSprite - > extra ] . mass > 0 ) {
int t = scale ( dmg , size , gSpriteMass [ pSprite - > extra ] . mass ) ;
xvel [ pSprite - > index ] + = mulscale16 ( t , dx ) ;
yvel [ pSprite - > index ] + = mulscale16 ( t , dy ) ;
zvel [ pSprite - > index ] + = mulscale16 ( t , dz ) ;
}
if ( pSprite - > type > = kThingBase & & pSprite - > type < kThingMax )
//actPostSprite(pSprite->index, kStatThing); // !!! (does not working here) if it was a thing, return it's statnum back
changespritestat ( pSprite - > index , kStatThing ) ;
}
actDamageSprite ( nOwner , pSprite , DAMAGE_TYPE_3 , dmg ) ;
return ;
}
}
void debrisMove ( int listIndex ) {
if ( ! ( sprite [ gPhysSpritesList [ listIndex ] ] . extra > 0 & & sprite [ gPhysSpritesList [ listIndex ] ] . extra < kMaxXSprites ) ) {
gPhysSpritesList [ listIndex ] = - 1 ;
return ;
} else if ( ! ( sprite [ gPhysSpritesList [ listIndex ] ] . sectnum > = 0 & & sprite [ gPhysSpritesList [ listIndex ] ] . sectnum < kMaxSectors ) ) {
gPhysSpritesList [ listIndex ] = - 1 ;
return ;
}
int nSprite = gPhysSpritesList [ listIndex ] ;
int nXSprite = sprite [ nSprite ] . extra ; XSPRITE * pXDebris = & xsprite [ nXSprite ] ;
spritetype * pSprite = & sprite [ nSprite ] ; int nSector = pSprite - > sectnum ;
int top , bottom ; GetSpriteExtents ( pSprite , & top , & bottom ) ;
int moveHit = 0 ;
//int floorDist = (bottom - pSprite->z) / 4;
//int ceilDist = (pSprite->z - top) / 4;
//int clipDist = pSprite->clipdist << 2;
int tmpFraction = gSpriteMass [ pSprite - > extra ] . fraction ;
if ( sector [ nSector ] . extra > = 0 & & xsector [ sector [ nSector ] . extra ] . Underwater )
tmpFraction > > = 1 ;
if ( xvel [ pSprite - > index ] ! = 0 | | yvel [ pSprite - > index ] ! = 0 ) {
short oldcstat = pSprite - > cstat ;
pSprite - > cstat & = ~ ( CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN ) ;
moveHit = gSpriteHit [ nXSprite ] . hit = ClipMove ( ( int * ) & pSprite - > x , ( int * ) & pSprite - > y , ( int * ) & pSprite - > z , & nSector , xvel [ nSprite ] > > 12 ,
yvel [ nSprite ] > > 12 , pSprite - > clipdist < < 2 , ( pSprite - > z - top ) / 4 , ( bottom - pSprite - > z ) / 4 , CLIPMASK0 ) ;
pSprite - > cstat = oldcstat ;
dassert ( nSector > = 0 ) ;
if ( pSprite - > sectnum ! = nSector ) {
dassert ( nSector > = 0 & & nSector < kMaxSectors ) ;
ChangeSpriteSect ( nSprite , nSector ) ;
}
if ( ( gSpriteHit [ nXSprite ] . hit & 0xc000 ) = = 0x8000 ) {
int nHitWall = gSpriteHit [ nXSprite ] . hit & 0x3fff ;
actWallBounceVector ( ( int * ) & xvel [ nSprite ] , ( int * ) & yvel [ nSprite ] , nHitWall , tmpFraction ) ;
}
} else {
dassert ( nSector > = 0 & & nSector < kMaxSectors ) ;
FindSector ( pSprite - > x , pSprite - > y , pSprite - > z , & nSector ) ;
}
if ( zvel [ nSprite ] )
pSprite - > z + = zvel [ nSprite ] > > 8 ;
int ceilZ , ceilHit , floorZ , floorHit ;
GetZRange ( pSprite , & ceilZ , & ceilHit , & floorZ , & floorHit , pSprite - > clipdist < < 2 , CLIPMASK0 ) ;
GetSpriteExtents ( pSprite , & top , & bottom ) ;
if ( ( pXDebris - > physAttr & kPhysGravity ) & & bottom < floorZ ) {
pSprite - > z + = 455 ;
zvel [ nSprite ] + = 58254 ;
}
int warp = CheckLink ( pSprite ) ;
if ( warp ! = 0 ) {
GetZRange ( pSprite , & ceilZ , & ceilHit , & floorZ , & floorHit , pSprite - > clipdist < < 2 , CLIPMASK0 ) ;
if ( ! ( pSprite - > cstat & CSTAT_SPRITE_INVISIBLE ) ) {
switch ( warp ) {
case kMarkerUpWater :
case kMarkerUpGoo :
int pitch = ( 150000 - ( gSpriteMass [ pSprite - > extra ] . mass < < 9 ) ) + Random3 ( 8192 ) ;
sfxPlay3DSoundCP ( pSprite , 720 , - 1 , 0 , pitch , 75 - Random ( 40 ) ) ;
if ( sector [ pSprite - > sectnum ] . extra < 0 | | ! xsector [ sector [ pSprite - > sectnum ] . extra ] . Underwater )
evKill ( pSprite - > index , 3 , kCallbackEnemeyBubble ) ;
else {
if ( Chance ( 0x8000 ) )
evPost ( pSprite - > index , 3 , 0 , kCallbackEnemeyBubble ) ;
for ( int i = 2 ; i < = 5 ; i + + ) {
if ( Chance ( 0x3000 * i ) )
evPost ( pSprite - > index , 3 , 0 , kCallbackEnemeyBubble ) ;
}
}
break ;
}
}
}
GetSpriteExtents ( pSprite , & top , & bottom ) ;
if ( ( floorHit & 0xe000 ) = = 0xc000 ) {
if ( ( sprite [ floorHit & 0x1fff ] . cstat & 0x30 ) = = 0x20 )
if ( klabs ( bottom - floorZ ) < 1024 ) floorZ - = 1024 ;
}
if ( bottom > = floorZ ) {
gSpriteHit [ nXSprite ] . florhit = floorHit ;
pSprite - > z + = floorZ - bottom ;
int v20 = zvel [ nSprite ] - velFloor [ pSprite - > sectnum ] ;
if ( v20 > 0 ) {
pXDebris - > physAttr | = kPhysFalling ;
actFloorBounceVector ( ( int * ) & xvel [ nSprite ] , ( int * ) & yvel [ nSprite ] , ( int * ) & v20 , pSprite - > sectnum , tmpFraction ) ;
zvel [ nSprite ] = v20 ;
if ( velFloor [ pSprite - > sectnum ] = = 0 & & klabs ( zvel [ nSprite ] ) < 0x10000 ) {
zvel [ nSprite ] = 0 ;
pXDebris - > physAttr & = ~ kPhysFalling ;
}
moveHit = 0x4000 | nSector ;
} else if ( zvel [ nSprite ] = = 0 )
pXDebris - > physAttr & = ~ kPhysFalling ;
} else {
gSpriteHit [ nXSprite ] . florhit = 0 ;
if ( pXDebris - > physAttr & kPhysGravity )
pXDebris - > physAttr | = kPhysFalling ;
}
if ( top < = ceilZ ) {
gSpriteHit [ nXSprite ] . ceilhit = ceilHit ;
pSprite - > z + = ClipLow ( ceilZ - top , 0 ) ;
if ( zvel [ nSprite ] < 0 )
{
xvel [ nSprite ] = mulscale16 ( xvel [ nSprite ] , 0xc000 ) ;
yvel [ nSprite ] = mulscale16 ( yvel [ nSprite ] , 0xc000 ) ;
zvel [ nSprite ] = mulscale16 ( - zvel [ nSprite ] , 0x4000 ) ;
}
} else {
gSpriteHit [ nXSprite ] . ceilhit = 0 ;
}
if ( bottom > = floorZ ) {
int nVel = approxDist ( xvel [ nSprite ] , yvel [ nSprite ] ) ;
int nVelClipped = ClipHigh ( nVel , 0x11111 ) ;
if ( ( floorHit & 0xc000 ) = = 0xc000 ) {
int nHitSprite = floorHit & 0x3fff ;
if ( ( sprite [ nHitSprite ] . cstat & 0x30 ) = = 0 )
{
xvel [ nSprite ] + = mulscale ( 4 , pSprite - > x - sprite [ nHitSprite ] . x , 2 ) ;
yvel [ nSprite ] + = mulscale ( 4 , pSprite - > y - sprite [ nHitSprite ] . y , 2 ) ;
moveHit = gSpriteHit [ nXSprite ] . hit ;
}
}
if ( nVel > 0 )
{
int t = divscale16 ( nVelClipped , nVel ) ;
xvel [ nSprite ] - = mulscale16 ( t , xvel [ nSprite ] ) ;
yvel [ nSprite ] - = mulscale16 ( t , yvel [ nSprite ] ) ;
}
}
if ( xvel [ nSprite ] | | yvel [ nSprite ] )
pSprite - > ang = getangle ( xvel [ nSprite ] , yvel [ nSprite ] ) ;
if ( moveHit ! = 0 & & pXDebris - > Impact & & pXDebris - > locked ! = 1 & & ! pXDebris - > isTriggered ) {
if ( ! pXDebris - > Interrutable & & pXDebris - > state ! = pXDebris - > restState ) return ;
if ( pSprite - > type > = kThingBase & & pSprite - > type < kThingMax )
// if thing was turned in debris, change it's stat back so it will do on impact what it supposed to do...
//actPostSprite(nSprite, kStatThing); // !!!! not working here for some reason
changespritestat ( nSprite , kStatThing ) ;
if ( pXDebris - > state = = 1 ) trTriggerSprite ( pSprite - > index , pXDebris , kCmdOff ) ;
else trTriggerSprite ( pSprite - > index , pXDebris , kCmdOn ) ;
}
}
bool ceilIsTooLow ( spritetype * pSprite ) {
if ( pSprite ! = NULL ) {
sectortype * pSector = & sector [ pSprite - > sectnum ] ;
int a = pSector - > ceilingz - pSector - > floorz ;
int top , bottom ;
GetSpriteExtents ( pSprite , & top , & bottom ) ;
int b = top - bottom ;
if ( a > b ) return true ;
}
return false ;
}
void aiSetGenIdleState ( spritetype * pSprite , XSPRITE * pXSprite ) {
switch ( pSprite - > type ) {
case kDudeModernCustom :
case kDudeModernCustomBurning :
aiGenDudeNewState ( pSprite , & genIdle ) ;
break ;
default :
aiNewState ( pSprite , pXSprite , & genIdle ) ;
break ;
}
}
// this function stops wind on all TX sectors affected by WindGen after it goes off state.
void windGenStopWindOnSectors ( XSPRITE * pXSource ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
2020-03-01 20:36:28 +00:00
if ( pXSource - > txID < = 0 & & xsectRangeIsFine ( sector [ pSource - > sectnum ] . extra ) ) {
xsector [ sector [ pSource - > sectnum ] . extra ] . windVel = 0 ;
2020-02-07 19:47:43 +00:00
return ;
}
for ( int i = bucketHead [ pXSource - > txID ] ; i < bucketHead [ pXSource - > txID + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SECTOR ) continue ;
XSECTOR * pXSector = & xsector [ sector [ rxBucket [ i ] . index ] . extra ] ;
2020-03-01 20:36:28 +00:00
if ( ( pXSector - > state = = 1 & & ! pXSector - > windAlways )
| | ( ( pSource - > flags & kModernTypeFlag1 ) & & ! ( pSource - > flags & kModernTypeFlag2 ) ) ) {
pXSector - > windVel = 0 ;
}
}
2020-04-07 20:30:00 +00:00
// check redirected TX buckets
int rx = - 1 ; XSPRITE * pXRedir = NULL ;
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , sprite [ pXSource - > reference ] . extra , pXRedir , & rx ) ) ! = NULL ) {
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SECTOR ) continue ;
XSECTOR * pXSector = & xsector [ sector [ rxBucket [ i ] . index ] . extra ] ;
if ( ( pXSector - > state = = 1 & & ! pXSector - > windAlways ) | | ( pSource - > flags & kModernTypeFlag2 ) )
pXSector - > windVel = 0 ;
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
}
}
2020-05-05 18:50:14 +00:00
/*XSPRITE* trPlayerCtrlFindNextScene(XSPRITE* pXScene) {
int curIndex = pXScene - > reference ; bool oldFound = false ;
for ( int i = bucketHead [ pXScene - > txID ] ; i < bucketHead [ pXScene - > txID + 1 ] ; i + + ) {
if ( sprite [ rxBucket [ i ] . index ] . type ! = kModernPlayerControl ) continue ;
else if ( rxBucket [ i ] . index = = curIndex ) {
oldFound = true ;
continue ;
}
XSPRITE * pXSpr = & xsprite [ sprite [ rxBucket [ i ] . index ] . extra ] ;
if ( oldFound & & pXSpr - > command = = 67 & & pXSpr - > data1 = = pXScene - > data1 & & ! pXSpr - > locked )
return pXSpr ;
}
} */
2020-02-07 19:47:43 +00:00
void trPlayerCtrlStartScene ( XSPRITE * pXSource , PLAYER * pPlayer ) {
int nSource = sprite [ pXSource - > reference ] . index ; TRPLAYERCTRL * pCtrl = & gPlayerCtrl [ pPlayer - > nPlayer ] ;
QAV * pQav = playerQavSceneLoad ( pXSource - > data2 ) ;
if ( pQav ! = NULL ) {
// save current weapon
pXSource - > dropMsg = pPlayer - > curWeapon ;
short nIndex = pCtrl - > qavScene . index ;
if ( nIndex > - 1 & & nIndex ! = nSource & & sprite [ nIndex ] . extra > = 0 )
pXSource - > dropMsg = xsprite [ sprite [ nIndex ] . extra ] . dropMsg ;
if ( nIndex < 0 )
WeaponLower ( pPlayer ) ;
pXSource - > sysData1 = ClipLow ( ( pQav - > at10 * pXSource - > waitTime ) / 4 , 0 ) ; // how many times animation should be played
pCtrl - > qavScene . index = nSource ;
pCtrl - > qavScene . qavResrc = pQav ;
pCtrl - > qavScene . dummy = - 1 ;
pCtrl - > qavScene . qavResrc - > Preload ( ) ;
pPlayer - > sceneQav = pXSource - > data2 ;
pPlayer - > weaponTimer = pCtrl - > qavScene . qavResrc - > at10 ;
pPlayer - > qavCallback = ( pXSource - > data3 > 0 ) ? ClipRange ( pXSource - > data3 - 1 , 0 , 32 ) : - 1 ;
pPlayer - > qavLoop = false ;
}
}
2020-05-05 18:50:14 +00:00
void trPlayerCtrlStopScene ( PLAYER * pPlayer ) {
2020-02-07 19:47:43 +00:00
TRPLAYERCTRL * pCtrl = & gPlayerCtrl [ pPlayer - > nPlayer ] ;
2020-05-05 18:50:14 +00:00
int scnIndex = pCtrl - > qavScene . index ; XSPRITE * pXSource = NULL ;
if ( spriRangeIsFine ( scnIndex ) ) {
pXSource = & xsprite [ sprite [ scnIndex ] . extra ] ;
pXSource - > sysData1 = 0 ;
}
2020-02-07 19:47:43 +00:00
2020-05-05 18:50:14 +00:00
if ( pCtrl - > qavScene . index > = 0 ) {
2020-02-07 19:47:43 +00:00
pCtrl - > qavScene . index = - 1 ;
pCtrl - > qavScene . qavResrc = NULL ;
pPlayer - > sceneQav = - 1 ;
// restore weapon
if ( pPlayer - > pXSprite - > health > 0 ) {
2020-05-05 18:50:14 +00:00
int oldWeapon = ( pXSource & & pXSource - > dropMsg ! = 0 ) ? pXSource - > dropMsg : 1 ;
2020-02-07 19:47:43 +00:00
pPlayer - > input . newWeapon = pPlayer - > curWeapon = oldWeapon ;
WeaponRaise ( pPlayer ) ;
}
2020-05-05 18:50:14 +00:00
}
2020-02-07 19:47:43 +00:00
}
2020-04-04 19:48:23 +00:00
void trPlayerCtrlLink ( XSPRITE * pXSource , PLAYER * pPlayer , bool checkCondition ) {
2020-02-07 19:47:43 +00:00
2020-05-05 18:50:14 +00:00
// save player's sprite index to let the tracking condition know it after savegame loading...
2020-04-04 19:48:23 +00:00
pXSource - > sysData1 = pPlayer - > nSprite ;
2020-03-13 20:59:13 +00:00
pPlayer - > pXSprite - > txID = pXSource - > txID ;
pPlayer - > pXSprite - > command = kCmdToggle ;
pPlayer - > pXSprite - > triggerOn = pXSource - > triggerOn ;
pPlayer - > pXSprite - > triggerOff = pXSource - > triggerOff ;
pPlayer - > pXSprite - > busyTime = pXSource - > busyTime ;
pPlayer - > pXSprite - > waitTime = pXSource - > waitTime ;
pPlayer - > pXSprite - > restState = pXSource - > restState ;
pPlayer - > pXSprite - > Push = pXSource - > Push ;
pPlayer - > pXSprite - > Impact = pXSource - > Impact ;
pPlayer - > pXSprite - > Vector = pXSource - > Vector ;
pPlayer - > pXSprite - > Touch = pXSource - > Touch ;
pPlayer - > pXSprite - > Sight = pXSource - > Sight ;
pPlayer - > pXSprite - > Proximity = pXSource - > Proximity ;
pPlayer - > pXSprite - > Decoupled = pXSource - > Decoupled ;
pPlayer - > pXSprite - > Interrutable = pXSource - > Interrutable ;
pPlayer - > pXSprite - > DudeLockout = pXSource - > DudeLockout ;
pPlayer - > pXSprite - > data1 = pXSource - > data1 ;
pPlayer - > pXSprite - > data2 = pXSource - > data2 ;
pPlayer - > pXSprite - > data3 = pXSource - > data3 ;
pPlayer - > pXSprite - > data4 = pXSource - > data4 ;
pPlayer - > pXSprite - > key = pXSource - > key ;
pPlayer - > pXSprite - > dropMsg = pXSource - > dropMsg ;
// let's check if there is tracking condition expecting objects with this TX id
2020-04-04 19:48:23 +00:00
if ( checkCondition & & pXSource - > txID > = kChannelUser ) {
for ( int i = bucketHead [ pXSource - > txID ] ; i < bucketHead [ pXSource - > txID + 1 ] ; i + + ) {
if ( sprite [ rxBucket [ i ] . index ] . type ! = kModernCondition ) continue ;
2020-03-13 20:59:13 +00:00
XSPRITE * pXCond = & xsprite [ sprite [ rxBucket [ i ] . index ] . extra ] ;
if ( pXCond - > busyTime < = 0 ) continue ;
2020-04-04 19:48:23 +00:00
2020-03-13 20:59:13 +00:00
TRCONDITION * pCond = & gCondition [ pXCond - > sysData1 ] ;
2020-04-04 19:48:23 +00:00
// search for player control sprite and replace it with actual player sprite
2020-03-13 20:59:13 +00:00
for ( int k = 0 ; k < pCond - > length ; k + + ) {
2020-04-04 19:48:23 +00:00
if ( pCond - > obj [ k ] . type ! = OBJ_SPRITE | | pCond - > obj [ k ] . index ! = pXSource - > reference ) continue ;
2020-03-13 20:59:13 +00:00
pCond - > obj [ k ] . index = pPlayer - > nSprite ;
2020-04-04 19:48:23 +00:00
pCond - > obj [ k ] . cmd = pPlayer - > pXSprite - > command ;
2020-03-13 20:59:13 +00:00
break ;
}
}
}
2020-02-07 19:47:43 +00:00
}
void trPlayerCtrlSetRace ( XSPRITE * pXSource , PLAYER * pPlayer ) {
playerSetRace ( pPlayer , pXSource - > data2 ) ;
switch ( pPlayer - > lifeMode ) {
case kModeHuman :
case kModeBeast :
playerSizeReset ( pPlayer ) ;
break ;
case kModeHumanShrink :
playerSizeShrink ( pPlayer , 2 ) ;
break ;
case kModeHumanGrown :
playerSizeGrow ( pPlayer , 2 ) ;
break ;
}
}
void trPlayerCtrlSetMoveSpeed ( XSPRITE * pXSource , PLAYER * pPlayer ) {
2020-05-05 18:50:14 +00:00
int speed = ClipRange ( pXSource - > data2 , 0 , 500 ) ;
2020-02-07 19:47:43 +00:00
for ( int i = 0 ; i < kModeMax ; i + + ) {
for ( int a = 0 ; a < kPostureMax ; a + + ) {
2020-05-05 18:50:14 +00:00
POSTURE * curPosture = & pPlayer - > pPosture [ i ] [ a ] ; POSTURE * defPosture = & gPostureDefaults [ i ] [ a ] ;
curPosture - > frontAccel = ( defPosture - > frontAccel * speed ) / kPercentFull ;
curPosture - > sideAccel = ( defPosture - > sideAccel * speed ) / kPercentFull ;
curPosture - > backAccel = ( defPosture - > backAccel * speed ) / kPercentFull ;
2020-02-07 19:47:43 +00:00
}
}
}
void trPlayerCtrlSetJumpHeight ( XSPRITE * pXSource , PLAYER * pPlayer ) {
2020-05-05 18:50:14 +00:00
int jump = ClipRange ( pXSource - > data3 , 0 , 500 ) ;
2020-02-07 19:47:43 +00:00
for ( int i = 0 ; i < kModeMax ; i + + ) {
2020-05-05 18:50:14 +00:00
POSTURE * curPosture = & pPlayer - > pPosture [ i ] [ kPostureStand ] ; POSTURE * defPosture = & gPostureDefaults [ i ] [ kPostureStand ] ;
curPosture - > normalJumpZ = ( defPosture - > normalJumpZ * jump ) / kPercentFull ;
curPosture - > pwupJumpZ = ( defPosture - > pwupJumpZ * jump ) / kPercentFull ;
2020-02-07 19:47:43 +00:00
}
}
void trPlayerCtrlSetScreenEffect ( XSPRITE * pXSource , PLAYER * pPlayer ) {
2020-05-05 18:50:14 +00:00
int eff = ClipLow ( pXSource - > data2 , 0 ) ; int time = ( eff > 0 ) ? pXSource - > data3 : 0 ;
switch ( eff ) {
case 0 : // clear all
2020-02-07 19:47:43 +00:00
case 1 : // tilting
2020-05-05 18:50:14 +00:00
pPlayer - > tiltEffect = ClipRange ( time , 0 , 220 ) ;
if ( eff ) break ;
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case 2 : // pain
2020-05-05 18:50:14 +00:00
pPlayer - > painEffect = ClipRange ( time , 0 , 2048 ) ;
if ( eff ) break ;
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case 3 : // blind
2020-05-05 18:50:14 +00:00
pPlayer - > blindEffect = ClipRange ( time , 0 , 2048 ) ;
if ( eff ) break ;
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case 4 : // pickup
2020-05-05 18:50:14 +00:00
pPlayer - > pickupEffect = ClipRange ( time , 0 , 2048 ) ;
if ( eff ) break ;
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case 5 : // quakeEffect
2020-05-05 18:50:14 +00:00
pPlayer - > quakeEffect = ClipRange ( time , 0 , 2048 ) ;
if ( eff ) break ;
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case 6 : // visibility
2020-05-05 18:50:14 +00:00
pPlayer - > visibility = ClipRange ( time , 0 , 2048 ) ;
if ( eff ) break ;
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case 7 : // delirium
2020-05-05 18:50:14 +00:00
pPlayer - > pwUpTime [ kPwUpDeliriumShroom ] = ClipHigh ( time < < 1 , 432000 ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
void trPlayerCtrlSetLookAngle ( XSPRITE * pXSource , PLAYER * pPlayer ) {
2020-04-01 11:55:36 +00:00
int upAngle = 289 ; int downAngle = - 347 ;
double lookStepUp = 4.0 * upAngle / 60.0 ;
double lookStepDown = - 4.0 * downAngle / 60.0 ;
2020-02-07 19:47:43 +00:00
int look = pXSource - > data2 < < 5 ;
2020-03-31 21:30:54 +00:00
if ( look > 0 ) pPlayer - > q16look = fix16_min ( mulscale8 ( fix16_from_dbl ( lookStepUp ) , look ) , fix16_from_int ( upAngle ) ) ;
else if ( look < 0 ) pPlayer - > q16look = - fix16_max ( mulscale8 ( fix16_from_dbl ( lookStepDown ) , abs ( look ) ) , fix16_from_int ( downAngle ) ) ;
2020-02-07 19:47:43 +00:00
else pPlayer - > q16look = 0 ;
}
void trPlayerCtrlEraseStuff ( XSPRITE * pXSource , PLAYER * pPlayer ) {
switch ( pXSource - > data2 ) {
case 0 : // erase all
fallthrough__ ;
case 1 : // erase weapons
WeaponLower ( pPlayer ) ;
for ( int i = 0 ; i < 14 ; i + + ) {
pPlayer - > hasWeapon [ i ] = false ;
// also erase ammo
if ( i < 12 ) pPlayer - > ammoCount [ i ] = 0 ;
}
pPlayer - > hasWeapon [ 1 ] = true ;
pPlayer - > curWeapon = 0 ;
pPlayer - > nextWeapon = 1 ;
WeaponRaise ( pPlayer ) ;
if ( pXSource - > data2 ) break ;
fallthrough__ ;
case 2 : // erase all armor
for ( int i = 0 ; i < 3 ; i + + ) pPlayer - > armor [ i ] = 0 ;
if ( pXSource - > data2 ) break ;
fallthrough__ ;
case 3 : // erase all pack items
for ( int i = 0 ; i < 5 ; i + + ) {
pPlayer - > packSlots [ i ] . isActive = false ;
pPlayer - > packSlots [ i ] . curAmount = 0 ;
}
2020-05-05 18:50:14 +00:00
pPlayer - > packItemId = - 1 ;
2020-02-07 19:47:43 +00:00
if ( pXSource - > data2 ) break ;
fallthrough__ ;
case 4 : // erase all keys
for ( int i = 0 ; i < 8 ; i + + ) pPlayer - > hasKey [ i ] = false ;
if ( pXSource - > data2 ) break ;
}
}
void trPlayerCtrlGiveStuff ( XSPRITE * pXSource , PLAYER * pPlayer , TRPLAYERCTRL * pCtrl ) {
2020-05-05 18:50:14 +00:00
int weapon = pXSource - > data3 ;
2020-02-07 19:47:43 +00:00
switch ( pXSource - > data2 ) {
case 1 : // give N weapon and default ammo for it
case 2 : // give just N ammo for selected weapon
2020-05-05 18:50:14 +00:00
if ( weapon < = 0 | | weapon > 13 ) {
consoleSysMsg ( " Weapon #%d is out of a weapons range! " ) ;
break ;
} else if ( pXSource - > data2 = = 2 & & pXSource - > data4 = = 0 ) {
consoleSysMsg ( " Zero ammo for weapon #%d is specyfied! " ) ;
break ;
}
switch ( weapon ) {
case 11 : // remote bomb
case 12 : // prox bomb
pPlayer - > hasWeapon [ weapon ] = true ;
weapon - - ;
pPlayer - > ammoCount [ weapon ] = ClipHigh ( pPlayer - > ammoCount [ weapon ] + ( ( pXSource - > data2 = = 2 ) ? pXSource - > data4 : 1 ) , gAmmoInfo [ weapon ] . max ) ;
weapon + + ;
break ;
default :
for ( int i = 0 ; i < 11 ; i + + ) {
if ( gWeaponItemData [ i ] . type ! = weapon ) continue ;
2020-02-07 19:47:43 +00:00
2020-02-11 22:15:25 +00:00
const WEAPONITEMDATA * pWeaponData = & gWeaponItemData [ i ] ; int nAmmoType = pWeaponData - > ammoType ;
2020-05-05 18:50:14 +00:00
switch ( pXSource - > data2 ) {
case 1 :
pPlayer - > hasWeapon [ weapon ] = true ;
if ( pPlayer - > ammoCount [ nAmmoType ] > = pWeaponData - > count ) break ;
2020-02-07 19:47:43 +00:00
pPlayer - > ammoCount [ nAmmoType ] = ClipHigh ( pPlayer - > ammoCount [ nAmmoType ] + pWeaponData - > count , gAmmoInfo [ nAmmoType ] . max ) ;
2020-05-05 18:50:14 +00:00
break ;
case 2 :
2020-02-07 19:47:43 +00:00
pPlayer - > ammoCount [ nAmmoType ] = ClipHigh ( pPlayer - > ammoCount [ nAmmoType ] + pXSource - > data4 , gAmmoInfo [ nAmmoType ] . max ) ;
break ;
}
2020-05-05 18:50:14 +00:00
break ;
}
break ;
}
if ( pPlayer - > hasWeapon [ weapon ] & & pXSource - > data4 = = 0 ) { // switch on it
2020-02-07 19:47:43 +00:00
pPlayer - > nextWeapon = 0 ;
if ( pPlayer - > sceneQav > = 0 & & spriRangeIsFine ( pCtrl - > qavScene . index ) ) {
XSPRITE * pXScene = & xsprite [ sprite [ pCtrl - > qavScene . index ] . extra ] ;
2020-05-05 18:50:14 +00:00
pXScene - > dropMsg = weapon ;
} else if ( pPlayer - > curWeapon ! = weapon ) {
pPlayer - > input . newWeapon = weapon ;
2020-02-07 19:47:43 +00:00
WeaponRaise ( pPlayer ) ;
}
}
break ;
}
}
2020-05-05 18:50:14 +00:00
void trPlayerCtrlUsePackItem ( XSPRITE * pXSource , PLAYER * pPlayer , int evCmd ) {
2020-02-07 19:47:43 +00:00
unsigned int invItem = pXSource - > data2 - 1 ;
2020-05-05 18:50:14 +00:00
switch ( evCmd ) {
case kCmdOn :
if ( ! pPlayer - > packSlots [ invItem ] . isActive ) packUseItem ( pPlayer , invItem ) ;
break ;
case kCmdOff :
if ( pPlayer - > packSlots [ invItem ] . isActive ) packUseItem ( pPlayer , invItem ) ;
break ;
default :
2020-02-07 19:47:43 +00:00
packUseItem ( pPlayer , invItem ) ;
2020-05-05 18:50:14 +00:00
break ;
}
2020-02-07 19:47:43 +00:00
2020-05-05 18:50:14 +00:00
switch ( pXSource - > data4 ) {
case 2 : // both
case 0 : // switch on it
if ( pPlayer - > packSlots [ invItem ] . curAmount > 0 ) pPlayer - > packItemId = invItem ;
if ( ! pXSource - > data4 ) break ;
fallthrough__ ;
case 1 : // force remove after use
2020-02-07 19:47:43 +00:00
pPlayer - > packSlots [ invItem ] . isActive = false ;
pPlayer - > packSlots [ invItem ] . curAmount = 0 ;
2020-05-05 18:50:14 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
}
void useObjResizer ( XSPRITE * pXSource , short objType , int objIndex ) {
switch ( objType ) {
// for sectors
case 6 :
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
sector [ objIndex ] . floorxpanning = ClipRange ( pXSource - > data1 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
sector [ objIndex ] . floorypanning = ClipRange ( pXSource - > data2 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
sector [ objIndex ] . ceilingxpanning = ClipRange ( pXSource - > data3 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
sector [ objIndex ] . ceilingypanning = ClipRange ( pXSource - > data4 , 0 , 255 ) ;
break ;
// for sprites
case 3 :
// resize by seq scaling
if ( sprite [ pXSource - > reference ] . flags & kModernTypeFlag1 ) {
if ( valueIsBetween ( pXSource - > data1 , - 255 , 32767 ) ) {
int mulDiv = ( valueIsBetween ( pXSource - > data2 , 0 , 257 ) ) ? pXSource - > data2 : 256 ;
if ( pXSource - > data1 > 0 ) xsprite [ sprite [ objIndex ] . extra ] . scale = mulDiv * ClipHigh ( pXSource - > data1 , 25 ) ;
else if ( pXSource - > data1 < 0 ) xsprite [ sprite [ objIndex ] . extra ] . scale = mulDiv / ClipHigh ( abs ( pXSource - > data1 ) , 25 ) ;
else xsprite [ sprite [ objIndex ] . extra ] . scale = 0 ;
// request properties update for custom dude
switch ( sprite [ objIndex ] . type ) {
case kDudeModernCustom :
case kDudeModernCustomBurning :
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertySpriteSize ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyAttack ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyMass ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyDmgScale ] = true ;
evPost ( objIndex , 3 , kGenDudeUpdTimeRate , kCallbackGenDudeUpdate ) ;
break ;
}
}
// resize by repeats
} else {
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
sprite [ objIndex ] . xrepeat = ClipRange ( pXSource - > data1 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
sprite [ objIndex ] . yrepeat = ClipRange ( pXSource - > data2 , 0 , 255 ) ;
}
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
sprite [ objIndex ] . xoffset = ClipRange ( pXSource - > data3 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
sprite [ objIndex ] . yoffset = ClipRange ( pXSource - > data4 , 0 , 255 ) ;
break ;
case OBJ_WALL :
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
wall [ objIndex ] . xrepeat = ClipRange ( pXSource - > data1 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
wall [ objIndex ] . yrepeat = ClipRange ( pXSource - > data2 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
wall [ objIndex ] . xpanning = ClipRange ( pXSource - > data3 , 0 , 255 ) ;
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
wall [ objIndex ] . ypanning = ClipRange ( pXSource - > data4 , 0 , 255 ) ;
break ;
}
}
void usePropertiesChanger ( XSPRITE * pXSource , short objType , int objIndex ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
switch ( objType ) {
case OBJ_WALL : {
walltype * pWall = & wall [ objIndex ] ; int old = - 1 ;
// data3 = set wall hitag
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) ) {
if ( ( pSource - > flags & kModernTypeFlag1 ) ) pWall - > hitag = pWall - > hitag | = pXSource - > data3 ;
else pWall - > hitag = pXSource - > data3 ;
}
// data4 = set wall cstat
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) ) {
old = pWall - > cstat ;
// set new cstat
if ( ( pSource - > flags & kModernTypeFlag1 ) ) pWall - > cstat = pWall - > cstat | = pXSource - > data4 ; // relative
else pWall - > cstat = pXSource - > data4 ; // absolute
// and hanlde exceptions
if ( ( old & 0x2 ) & & ! ( pWall - > cstat & 0x2 ) ) pWall - > cstat | = 0x2 ; // kWallBottomSwap
if ( ( old & 0x4 ) & & ! ( pWall - > cstat & 0x4 ) ) pWall - > cstat | = 0x4 ; // kWallBottomOrg, kWallOutsideOrg
if ( ( old & 0x20 ) & & ! ( pWall - > cstat & 0x20 ) ) pWall - > cstat | = 0x20 ; // kWallOneWay
if ( old & 0xc000 ) {
if ( ! ( pWall - > cstat & 0xc000 ) )
pWall - > cstat | = 0xc000 ; // kWallMoveMask
if ( ( old & 0x0 ) & & ! ( pWall - > cstat & 0x0 ) ) pWall - > cstat | = 0x0 ; // kWallMoveNone
else if ( ( old & 0x4000 ) & & ! ( pWall - > cstat & 0x4000 ) ) pWall - > cstat | = 0x4000 ; // kWallMoveForward
else if ( ( old & 0x8000 ) & & ! ( pWall - > cstat & 0x8000 ) ) pWall - > cstat | = 0x8000 ; // kWallMoveBackward
}
}
}
break ;
case OBJ_SPRITE : {
spritetype * pSprite = & sprite [ objIndex ] ; bool thing2debris = false ;
XSPRITE * pXSprite = & xsprite [ pSprite - > extra ] ; int old = - 1 ;
// data3 = set sprite hitag
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) ) {
old = pSprite - > flags ;
// set new hitag
if ( ( pSource - > flags & kModernTypeFlag1 ) ) pSprite - > flags = pSource - > flags | = pXSource - > data3 ; // relative
else pSprite - > flags = pXSource - > data3 ; // absolute
// and handle exceptions
if ( ( old & kHitagFree ) & & ! ( pSprite - > flags & kHitagFree ) ) pSprite - > flags | = kHitagFree ;
if ( ( old & kHitagRespawn ) & & ! ( pSprite - > flags & kHitagRespawn ) ) pSprite - > flags | = kHitagRespawn ;
// prepare things for different (debris) physics.
if ( pSprite - > statnum = = kStatThing & & debrisGetFreeIndex ( ) > = 0 ) thing2debris = true ;
}
// data2 = sprite physics settings
if ( ( pXSource - > data2 > = 0 & & pXSource - > data3 < = 33 ) | | thing2debris ) {
switch ( pSprite - > statnum ) {
case kStatDude : // dudes already treating in game
case kStatFree :
case kStatMarker :
case kStatPathMarker : // path marker
break ;
default :
// store physics attributes in xsprite to avoid setting hitag for modern types!
int flags = ( pXSprite - > physAttr ! = 0 ) ? pXSprite - > physAttr : 0 ;
if ( thing2debris ) {
// converting thing to debris
if ( ( pSprite - > flags & kPhysMove ) ! = 0 ) flags | = kPhysMove ;
else flags & = ~ kPhysMove ;
if ( ( pSprite - > flags & kPhysGravity ) ! = 0 ) flags | = ( kPhysGravity | kPhysFalling ) ;
else flags & = ~ ( kPhysGravity | kPhysFalling ) ;
pSprite - > flags & = ~ ( kPhysMove | kPhysGravity | kPhysFalling ) ;
xvel [ objIndex ] = yvel [ objIndex ] = zvel [ objIndex ] = 0 ; pXSprite - > restState = pXSprite - > state ;
} else {
// first digit of data2: set main physics attributes
switch ( pXSource - > data2 ) {
case 0 :
flags & = ~ kPhysMove ;
flags & = ~ ( kPhysGravity | kPhysFalling ) ;
break ;
case 1 : case 10 : case 11 : case 12 : case 13 :
flags | = kPhysMove ;
flags & = ~ ( kPhysGravity | kPhysFalling ) ;
break ;
case 2 : case 20 : case 21 : case 22 : case 23 :
flags & = ~ kPhysMove ;
flags | = ( kPhysGravity | kPhysFalling ) ;
break ;
case 3 : case 30 : case 31 : case 32 : case 33 :
flags | = kPhysMove ;
flags | = ( kPhysGravity | kPhysFalling ) ;
break ;
}
// second digit of data2: set physics flags
switch ( pXSource - > data2 ) {
case 0 : case 1 : case 2 : case 3 :
case 10 : case 20 : case 30 :
flags & = ~ kPhysDebrisVector ;
flags & = ~ kPhysDebrisExplode ;
break ;
case 11 : case 21 : case 31 :
flags | = kPhysDebrisVector ;
flags & = ~ kPhysDebrisExplode ;
break ;
case 12 : case 22 : case 32 :
flags & = ~ kPhysDebrisVector ;
flags | = kPhysDebrisExplode ;
break ;
case 13 : case 23 : case 33 :
flags | = kPhysDebrisVector ;
flags | = kPhysDebrisExplode ;
break ;
}
}
int nIndex = debrisGetIndex ( objIndex ) ; // check if there is no sprite in list
// adding physics sprite in list
if ( ( flags & kPhysGravity ) ! = 0 | | ( flags & kPhysMove ) ! = 0 ) {
if ( nIndex ! = - 1 ) pXSprite - > physAttr = flags ; // just update physics attributes
else if ( ( nIndex = debrisGetFreeIndex ( ) ) < 0 )
viewSetSystemMessage ( " Max (%d) Physics affected sprites reached! " , kMaxSuperXSprites ) ;
else {
pXSprite - > physAttr = flags ; // update physics attributes
// allow things to became debris, so they use different physics...
if ( pSprite - > statnum = = kStatThing ) changespritestat ( objIndex , 0 ) ;
//actPostSprite(nDest, kStatDecoration); // !!!! not working here for some reason
gPhysSpritesList [ nIndex ] = objIndex ;
if ( nIndex > = gPhysSpritesCount ) gPhysSpritesCount + + ;
getSpriteMassBySize ( pSprite ) ; // create physics cache
}
// removing physics from sprite in list (don't remove sprite from list)
} else if ( nIndex ! = - 1 ) {
pXSprite - > physAttr = flags ;
xvel [ objIndex ] = yvel [ objIndex ] = zvel [ objIndex ] = 0 ;
if ( pSprite - > lotag > = kThingBase & & pSprite - > lotag < kThingMax )
changespritestat ( objIndex , kStatThing ) ; // if it was a thing - restore statnum
}
break ;
}
}
// data4 = sprite cstat
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) ) {
old = pSprite - > cstat ;
// set new cstat
2020-05-05 18:50:14 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) ) pSprite - > cstat | = pXSource - > data4 ; // relative
2020-02-07 19:47:43 +00:00
else pSprite - > cstat = pXSource - > data4 ; // absolute
2020-03-01 20:36:28 +00:00
// and handle exceptions
2020-02-07 19:47:43 +00:00
if ( ( old & 0x1000 ) & & ! ( pSprite - > cstat & 0x1000 ) ) pSprite - > cstat | = 0x1000 ; //kSpritePushable
if ( ( old & 0x80 ) & & ! ( pSprite - > cstat & 0x80 ) ) pSprite - > cstat | = 0x80 ; // kSpriteOriginAlign
if ( old & 0x6000 ) {
if ( ! ( pSprite - > cstat & 0x6000 ) )
pSprite - > cstat | = 0x6000 ; // kSpriteMoveMask
if ( ( old & 0x0 ) & & ! ( pSprite - > cstat & 0x0 ) ) pSprite - > cstat | = 0x0 ; // kSpriteMoveNone
else if ( ( old & 0x2000 ) & & ! ( pSprite - > cstat & 0x2000 ) ) pSprite - > cstat | = 0x2000 ; // kSpriteMoveForward, kSpriteMoveFloor
else if ( ( old & 0x4000 ) & & ! ( pSprite - > cstat & 0x4000 ) ) pSprite - > cstat | = 0x4000 ; // kSpriteMoveReverse, kSpriteMoveCeiling
}
}
}
break ;
case OBJ_SECTOR : {
2020-05-05 18:50:14 +00:00
sectortype * pSector = & sector [ objIndex ] ;
2020-02-07 19:47:43 +00:00
XSECTOR * pXSector = & xsector [ sector [ objIndex ] . extra ] ;
// data1 = sector underwater status and depth level
2020-05-05 18:50:14 +00:00
if ( pXSource - > data1 > = 0 & & pXSource - > data1 < 2 ) {
pXSector - > Underwater = ( pXSource - > data1 ) ? true : false ;
spritetype * pLower = ( gLowerLink [ objIndex ] > = 0 ) ? & sprite [ gLowerLink [ objIndex ] ] : NULL ;
XSPRITE * pXLower = NULL ; spritetype * pUpper = NULL ; XSPRITE * pXUpper = NULL ;
if ( pLower ) {
pXLower = & xsprite [ pLower - > extra ] ;
// must be sure we found exact same upper link
for ( int i = 0 ; i < kMaxSectors ; i + + ) {
if ( gUpperLink [ i ] < 0 | | xsprite [ sprite [ gUpperLink [ i ] ] . extra ] . data1 ! = pXLower - > data1 ) continue ;
pUpper = & sprite [ gUpperLink [ i ] ] ; pXUpper = & xsprite [ pUpper - > extra ] ;
break ;
}
}
// treat sectors that have links, so warp can detect underwater status properly
if ( pLower ) {
if ( pXSector - > Underwater ) {
switch ( pLower - > type ) {
case kMarkerLowStack :
case kMarkerLowLink :
pXLower - > sysData1 = pLower - > type ;
pLower - > type = kMarkerLowWater ;
break ;
default :
if ( pSector - > ceilingpicnum < 4080 | | pSector - > ceilingpicnum > 4095 ) pXLower - > sysData1 = kMarkerLowLink ;
else pXLower - > sysData1 = kMarkerLowStack ;
break ;
}
}
else if ( pXLower - > sysData1 > 0 ) pLower - > type = pXLower - > sysData1 ;
else if ( pSector - > ceilingpicnum < 4080 | | pSector - > ceilingpicnum > 4095 ) pLower - > type = kMarkerLowLink ;
else pLower - > type = kMarkerLowStack ;
}
if ( pUpper ) {
if ( pXSector - > Underwater ) {
switch ( pUpper - > type ) {
case kMarkerUpStack :
case kMarkerUpLink :
pXUpper - > sysData1 = pUpper - > type ;
pUpper - > type = kMarkerUpWater ;
break ;
default :
if ( pSector - > floorpicnum < 4080 | | pSector - > floorpicnum > 4095 ) pXUpper - > sysData1 = kMarkerUpLink ;
else pXUpper - > sysData1 = kMarkerUpStack ;
break ;
}
}
else if ( pXUpper - > sysData1 > 0 ) pUpper - > type = pXUpper - > sysData1 ;
else if ( pSector - > floorpicnum < 4080 | | pSector - > floorpicnum > 4095 ) pUpper - > type = kMarkerUpLink ;
else pUpper - > type = kMarkerUpStack ;
}
// search for dudes in this sector and change their underwater status
for ( int nSprite = headspritesect [ objIndex ] ; nSprite > = 0 ; nSprite = nextspritesect [ nSprite ] ) {
spritetype * pSpr = & sprite [ nSprite ] ;
if ( pSpr - > statnum ! = kStatDude | | ! IsDudeSprite ( pSpr ) | | ! xspriRangeIsFine ( pSpr - > extra ) )
continue ;
PLAYER * pPlayer = getPlayerById ( pSpr - > type ) ;
if ( pXSector - > Underwater ) {
if ( pLower )
xsprite [ pSpr - > extra ] . medium = ( pLower - > type = = kMarkerUpGoo ) ? kMediumGoo : kMediumWater ;
if ( pPlayer ) {
int waterPal = kMediumWater ;
if ( pLower ) {
if ( pXLower - > data2 > 0 ) waterPal = pXLower - > data2 ;
else if ( pLower - > type = = kMarkerUpGoo ) waterPal = kMediumGoo ;
}
pPlayer - > nWaterPal = waterPal ;
pPlayer - > posture = kPostureSwim ;
pPlayer - > pXSprite - > burnTime = 0 ;
}
} else {
xsprite [ pSpr - > extra ] . medium = kMediumNormal ;
if ( pPlayer ) {
pPlayer - > posture = ( ! pPlayer - > input . buttonFlags . crouch ) ? kPostureStand : kPostureCrouch ;
pPlayer - > nWaterPal = 0 ;
}
}
}
}
2020-02-07 19:47:43 +00:00
else if ( pXSource - > data1 > 9 ) pXSector - > Depth = 7 ;
else if ( pXSource - > data1 > 1 ) pXSector - > Depth = pXSource - > data1 - 2 ;
// data2 = sector visibility
2020-05-05 18:50:14 +00:00
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
sector [ objIndex ] . visibility = ClipRange ( pXSource - > data2 , 0 , 234 ) ;
2020-02-07 19:47:43 +00:00
// data3 = sector ceil cstat
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) ) {
2020-05-05 18:50:14 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) ) sector [ objIndex ] . ceilingstat | = pXSource - > data3 ;
2020-02-07 19:47:43 +00:00
else sector [ objIndex ] . ceilingstat = pXSource - > data3 ;
}
// data4 = sector floor cstat
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) ) {
2020-05-05 18:50:14 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) ) sector [ objIndex ] . floorstat | = pXSource - > data4 ;
2020-02-07 19:47:43 +00:00
else sector [ objIndex ] . floorstat = pXSource - > data4 ;
}
}
break ;
// no TX id
case - 1 :
// data2 = global visibility
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
gVisibility = ClipRange ( pXSource - > data2 , 0 , 4096 ) ;
break ;
}
}
void useTeleportTarget ( XSPRITE * pXSource , spritetype * pSprite ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ; PLAYER * pPlayer = getPlayerById ( pSprite - > type ) ;
XSECTOR * pXSector = ( sector [ pSource - > sectnum ] . extra > = 0 ) ? & xsector [ sector [ pSource - > sectnum ] . extra ] : NULL ;
2020-03-01 20:36:28 +00:00
bool isDude = ( ! pPlayer & & IsDudeSprite ( pSprite ) ) ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pSprite - > sectnum ! = pSource - > sectnum )
changespritesect ( pSprite - > index , pSource - > sectnum ) ;
pSprite - > x = pSource - > x ; pSprite - > y = pSource - > y ; pSprite - > z = pSource - > z ;
// make sure sprites aren't in the floor or ceiling
int zTop , zBot ; GetSpriteExtents ( pSprite , & zTop , & zBot ) ;
pSprite - > z + = ClipLow ( sector [ pSprite - > sectnum ] . ceilingz - zTop , 0 ) ;
pSprite - > z + = ClipHigh ( sector [ pSprite - > sectnum ] . floorz - zBot , 0 ) ;
2020-02-07 19:47:43 +00:00
if ( pSource - > flags & kModernTypeFlag1 ) // force telefrag
TeleFrag ( pSprite - > index , pSource - > sectnum ) ;
2020-03-01 20:36:28 +00:00
if ( pSprite - > flags & kPhysGravity )
pSprite - > flags | = kPhysFalling ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pXSector ) {
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pXSector - > Enter & & ( pPlayer | | ( isDude & & ! pXSector - > dudeLockout ) ) )
trTriggerSector ( pSource - > sectnum , pXSector , kCmdSectorEnter ) ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pXSector - > Underwater ) {
2020-05-05 18:50:14 +00:00
spritetype * pLink = ( gLowerLink [ pSource - > sectnum ] > = 0 ) ? & sprite [ gLowerLink [ pSource - > sectnum ] ] : NULL ;
2020-02-07 19:47:43 +00:00
if ( pLink ) {
2020-03-01 20:36:28 +00:00
// must be sure we found exact same upper link
for ( int i = 0 ; i < kMaxSectors ; i + + ) {
2020-05-05 18:50:14 +00:00
if ( gUpperLink [ i ] < 0 | | xsprite [ sprite [ gUpperLink [ i ] ] . extra ] . data1 ! = xsprite [ pLink - > extra ] . data1 ) continue ;
2020-03-01 20:36:28 +00:00
pLink = & sprite [ gUpperLink [ i ] ] ;
break ;
}
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
if ( pLink )
xsprite [ pSprite - > extra ] . medium = ( pLink - > type = = kMarkerUpGoo ) ? kMediumGoo : kMediumWater ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pPlayer ) {
int waterPal = kMediumWater ;
if ( pLink ) {
if ( xsprite [ pLink - > extra ] . data2 > 0 ) waterPal = xsprite [ pLink - > extra ] . data2 ;
else if ( pLink - > type = = kMarkerUpGoo ) waterPal = kMediumGoo ;
}
pPlayer - > nWaterPal = waterPal ;
pPlayer - > posture = kPostureSwim ;
pPlayer - > pXSprite - > burnTime = 0 ;
}
} else {
xsprite [ pSprite - > extra ] . medium = kMediumNormal ;
if ( pPlayer ) {
pPlayer - > posture = ( ! pPlayer - > input . buttonFlags . crouch ) ? kPostureStand : kPostureCrouch ;
pPlayer - > nWaterPal = 0 ;
}
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
}
if ( pSprite - > statnum = = kStatDude & & IsDudeSprite ( pSprite ) & & ! IsPlayerSprite ( pSprite ) ) {
XSPRITE * pXDude = & xsprite [ pSprite - > extra ] ;
int x = pXDude - > targetX ; int y = pXDude - > targetY ; int z = pXDude - > targetZ ;
int target = pXDude - > target ;
aiInitSprite ( pSprite ) ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( target > = 0 ) {
pXDude - > targetX = x ; pXDude - > targetY = y ; pXDude - > targetZ = z ;
pXDude - > target = target ; aiActivateDude ( pSprite , pXDude ) ;
}
2020-02-07 19:47:43 +00:00
}
if ( pXSource - > data2 = = 1 ) {
if ( pPlayer ) pPlayer - > q16ang = fix16_from_int ( pSource - > ang ) ;
2020-03-01 20:36:28 +00:00
else if ( isDude ) xsprite [ pSprite - > extra ] . goalAng = pSprite - > ang = pSource - > ang ;
2020-02-07 19:47:43 +00:00
else pSprite - > ang = pSource - > ang ;
}
if ( pXSource - > data3 = = 1 )
xvel [ pSprite - > index ] = yvel [ pSprite - > index ] = zvel [ pSprite - > index ] = 0 ;
viewBackupSpriteLoc ( pSprite - > index , pSprite ) ;
if ( pXSource - > data4 > 0 )
sfxPlay3DSound ( pSource , pXSource - > data4 , - 1 , 0 ) ;
if ( pPlayer ) {
playerResetInertia ( pPlayer ) ;
if ( pXSource - > data2 = = 1 )
pPlayer - > zViewVel = pPlayer - > zWeaponVel = 0 ;
}
2020-03-01 20:36:28 +00:00
2020-02-07 19:47:43 +00:00
}
void useEffectGen ( XSPRITE * pXSource , spritetype * pSprite ) {
if ( pSprite = = NULL ) pSprite = & sprite [ pXSource - > reference ] ;
2020-03-01 20:36:28 +00:00
int fxId = ( pXSource - > data3 < = 0 ) ? pXSource - > data2 : pXSource - > data2 + Random ( pXSource - > data3 + 1 ) ;
if ( xspriRangeIsFine ( pSprite - > extra ) & & valueIsBetween ( fxId , 0 , kFXMax ) ) {
int pos , top , bottom ; GetSpriteExtents ( pSprite , & top , & bottom ) ;
spritetype * pSource = & sprite [ pXSource - > reference ] ;
spritetype * pEffect = NULL ;
// select where exactly effect should be spawned
switch ( pXSource - > data4 ) {
case 1 :
pos = bottom ;
break ;
2020-05-19 19:42:29 +00:00
case 2 : // middle
pos = pSprite - > z + ( tilesiz [ pSprite - > picnum ] . y / 2 + picanm [ pSprite - > picnum ] . yofs ) ;
2020-03-01 20:36:28 +00:00
break ;
case 3 :
case 4 :
2020-03-05 20:14:45 +00:00
if ( ! sectRangeIsFine ( pSprite - > sectnum ) ) pos = top ;
else pos = ( pXSource - > data4 = = 3 ) ? sector [ pSprite - > sectnum ] . floorz : sector [ pSprite - > sectnum ] . ceilingz ;
2020-03-01 20:36:28 +00:00
break ;
default :
pos = top ;
break ;
}
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( ( pEffect = gFX . fxSpawn ( ( FX_ID ) fxId , pSprite - > sectnum , pSprite - > x , pSprite - > y , pos , 0 ) ) ! = NULL ) {
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
pEffect - > owner = pSource - > index ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pSource - > flags & kModernTypeFlag1 ) {
pEffect - > pal = pSource - > pal ;
pEffect - > xrepeat = pSource - > xrepeat ;
pEffect - > yrepeat = pSource - > yrepeat ;
pEffect - > shade = pSource - > shade ;
}
2020-02-07 19:47:43 +00:00
2020-05-05 18:50:14 +00:00
if ( pSource - > flags & kModernTypeFlag2 ) {
pEffect - > cstat = pSource - > cstat ;
if ( pEffect - > cstat & CSTAT_SPRITE_INVISIBLE )
pEffect - > cstat & = ~ CSTAT_SPRITE_INVISIBLE ;
}
2020-03-01 20:36:28 +00:00
if ( pEffect - > cstat & CSTAT_SPRITE_ONE_SIDED )
pEffect - > cstat & = ~ CSTAT_SPRITE_ONE_SIDED ;
2020-02-07 19:47:43 +00:00
}
}
}
void useSectorWindGen ( XSPRITE * pXSource , sectortype * pSector ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
2020-03-01 20:36:28 +00:00
XSECTOR * pXSector = NULL ; int nXSector = 0 ;
if ( pSector ! = NULL ) {
2020-02-07 19:47:43 +00:00
pXSector = & xsector [ pSector - > extra ] ;
nXSector = sector [ pXSector - > reference ] . extra ;
2020-03-01 20:36:28 +00:00
} else if ( xsectRangeIsFine ( sector [ pSource - > sectnum ] . extra ) ) {
pXSector = & xsector [ sector [ pSource - > sectnum ] . extra ] ;
nXSector = sector [ pXSector - > reference ] . extra ;
} else {
int nXSector = dbInsertXSector ( pSource - > sectnum ) ;
pXSector = & xsector [ nXSector ] ; pXSector - > windAlways = 1 ;
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) )
pXSector - > panAlways = pXSector - > windAlways = 1 ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
short windVel = ClipRange ( pXSource - > data2 , 0 , 32767 ) ;
switch ( pXSource - > data1 ) {
default :
pXSector - > windVel = windVel ;
break ;
case 1 :
case 3 :
2020-04-12 20:30:49 +00:00
pXSector - > windVel = nnExtRandom ( 0 , windVel ) ;
2020-03-01 20:36:28 +00:00
break ;
}
int ang = pSource - > ang ;
if ( pXSource - > data4 < = 0 ) {
switch ( pXSource - > data1 ) {
case 2 :
case 3 :
while ( pSource - > ang = = ang )
2020-04-12 20:30:49 +00:00
pSource - > ang = nnExtRandom ( - kAng360 , kAng360 ) & 2047 ;
2020-03-01 20:36:28 +00:00
break ;
}
}
else if ( pSource - > cstat & 0x2000 ) pSource - > ang + = pXSource - > data4 ;
else if ( pSource - > cstat & 0x4000 ) pSource - > ang - = pXSource - > data4 ;
else if ( pXSource - > sysData1 = = 0 ) {
if ( ( ang + = pXSource - > data4 ) > = kAng180 ) pXSource - > sysData1 = 1 ;
pSource - > ang = ClipHigh ( ang , kAng180 ) ;
} else {
if ( ( ang - = pXSource - > data4 ) < = - kAng180 ) pXSource - > sysData1 = 0 ;
pSource - > ang = ClipLow ( ang , - kAng180 ) ;
2020-02-07 19:47:43 +00:00
}
pXSector - > windAng = pSource - > ang ;
if ( pXSource - > data3 > 0 & & pXSource - > data3 < 4 ) {
switch ( pXSource - > data3 ) {
case 1 :
pXSector - > panFloor = true ;
pXSector - > panCeiling = false ;
break ;
case 2 :
pXSector - > panFloor = false ;
pXSector - > panCeiling = true ;
break ;
case 3 :
pXSector - > panFloor = true ;
pXSector - > panCeiling = true ;
break ;
}
short oldPan = pXSector - > panVel ;
pXSector - > panAngle = pXSector - > windAng ;
pXSector - > panVel = pXSector - > windVel ;
// add to panList if panVel was set to 0 previously
2020-04-09 18:51:25 +00:00
if ( oldPan = = 0 & & pXSector - > panVel ! = 0 & & panCount < kMaxXSectors ) {
2020-02-07 19:47:43 +00:00
int i ;
for ( i = 0 ; i < panCount ; i + + ) {
if ( panList [ i ] ! = nXSector ) continue ;
break ;
}
if ( i = = panCount )
panList [ panCount + + ] = nXSector ;
}
}
}
void useSpriteDamager ( XSPRITE * pXSource , spritetype * pSprite ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
2020-05-05 18:50:14 +00:00
if ( ! IsDudeSprite ( pSprite ) | | ! xspriRangeIsFine ( pSprite - > extra ) | | xsprite [ pSprite - > extra ] . health < = 0 | | pXSource - > data3 < 0 )
return ;
XSPRITE * pXSprite = & xsprite [ pSprite - > extra ] ; PLAYER * pPlayer = getPlayerById ( pSprite - > type ) ;
int dmgType = ( pXSource - > data2 > = kDmgFall ) ? ClipHigh ( pXSource - > data2 , kDmgElectric ) : - 1 ;
int dmg = pXSprite - > health < < 4 ; int armor [ 3 ] ;
bool godMode = ( pPlayer & & ( ( dmgType > = 0 & & pPlayer - > damageControl [ dmgType ] ) | | powerupCheck ( pPlayer , kPwUpDeathMask ) | | pPlayer - > godMode
/*|| seqGetID(3, pSprite->extra) == getDudeInfo(pSprite->type)->seqStartID + 16*/ ) ) ; // kneeling
if ( godMode | | pXSprite - > locked ) return ;
else if ( pXSource - > data3 ) {
if ( pSource - > flags & kModernTypeFlag1 ) dmg = ClipHigh ( pXSource - > data3 < < 1 , 65535 ) ;
else if ( pXSprite - > sysData2 > 0 ) dmg = ( ClipHigh ( pXSprite - > sysData2 < < 4 , 65535 ) * pXSource - > data3 ) / kPercentFull ;
else dmg = ( ( getDudeInfo ( pSprite - > type ) - > startHealth < < 4 ) * pXSource - > data3 ) / kPercentFull ;
2020-02-07 19:47:43 +00:00
}
2020-05-05 18:50:14 +00:00
if ( dmgType > = kDmgFall ) {
if ( dmg < pXSprite - > health < < 4 ) {
if ( nnExtIsImmune ( pSprite , dmgType , 0 ) ) {
consoleSysMsg ( " Dude type %d is immune to damage type %d! " , pSprite - > type , dmgType ) ;
return ;
2020-02-07 19:47:43 +00:00
}
2020-05-05 18:50:14 +00:00
if ( pPlayer ) {
playerDamageArmor ( pPlayer , ( DAMAGE_TYPE ) dmgType , dmg ) ;
for ( int i = 0 ; i < 3 ; armor [ i ] = pPlayer - > armor [ i ] , pPlayer - > armor [ i ] = 0 , i + + ) ;
actDamageSprite ( pSource - > index , pSprite , ( DAMAGE_TYPE ) dmgType , dmg ) ;
for ( int i = 0 ; i < 3 ; pPlayer - > armor [ i ] = armor [ i ] , i + + ) ;
} else {
actDamageSprite ( pSource - > index , pSprite , ( DAMAGE_TYPE ) dmgType , dmg ) ;
2020-02-07 19:47:43 +00:00
}
2020-05-05 18:50:14 +00:00
2020-02-07 19:47:43 +00:00
}
2020-05-05 18:50:14 +00:00
else if ( ! pPlayer ) actKillDude ( pSource - > index , pSprite , ( DAMAGE_TYPE ) dmgType , dmg ) ;
else playerDamageSprite ( pSource - > index , pPlayer , ( DAMAGE_TYPE ) dmgType , dmg ) ;
}
else if ( ( pXSprite - > health = ClipLow ( pXSprite - > health - dmg , 1 ) ) > 16 ) return ;
else if ( ! pPlayer ) actKillDude ( pSource - > index , pSprite , DAMAGE_TYPE_2 , dmg ) ;
else playerDamageSprite ( pSource - > index , pPlayer , DAMAGE_TYPE_2 , dmg ) ;
return ;
2020-02-07 19:47:43 +00:00
}
void useSeqSpawnerGen ( XSPRITE * pXSource , int objType , int index ) {
2020-05-05 18:50:14 +00:00
2020-02-07 19:47:43 +00:00
if ( pXSource - > data2 > 0 & & ! gSysRes . Lookup ( pXSource - > data2 , " SEQ " ) ) {
consoleSysMsg ( " Missing sequence #%d " , pXSource - > data2 ) ;
return ;
}
switch ( objType ) {
case OBJ_SECTOR :
if ( pXSource - > data2 < = 0 ) {
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 1 )
seqKill ( 2 , sector [ index ] . extra ) ;
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 2 )
seqKill ( 1 , sector [ index ] . extra ) ;
} else {
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 1 )
seqSpawn ( pXSource - > data2 , 2 , sector [ index ] . extra , - 1 ) ;
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 2 )
seqSpawn ( pXSource - > data2 , 1 , sector [ index ] . extra , - 1 ) ;
}
return ;
case OBJ_WALL :
if ( pXSource - > data2 < = 0 ) {
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 1 )
seqKill ( 0 , wall [ index ] . extra ) ;
if ( ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 2 ) & & ( wall [ index ] . cstat & CSTAT_WALL_MASKED ) )
seqKill ( 4 , wall [ index ] . extra ) ;
} else {
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 1 )
seqSpawn ( pXSource - > data2 , 0 , wall [ index ] . extra , - 1 ) ;
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 2 ) {
if ( wall [ index ] . nextwall < 0 ) {
if ( pXSource - > data3 = = 3 )
seqSpawn ( pXSource - > data2 , 0 , wall [ index ] . extra , - 1 ) ;
} else {
if ( ! ( wall [ index ] . cstat & CSTAT_WALL_MASKED ) )
wall [ index ] . cstat | = CSTAT_WALL_MASKED ;
seqSpawn ( pXSource - > data2 , 4 , wall [ index ] . extra , - 1 ) ;
}
}
if ( pXSource - > data4 > 0 ) {
int cx , cy , cz ;
cx = ( wall [ index ] . x + wall [ wall [ index ] . point2 ] . x ) > > 1 ;
cy = ( wall [ index ] . y + wall [ wall [ index ] . point2 ] . y ) > > 1 ;
int nSector = sectorofwall ( index ) ;
int32_t ceilZ , floorZ ;
getzsofslope ( nSector , cx , cy , & ceilZ , & floorZ ) ;
int32_t ceilZ2 , floorZ2 ;
getzsofslope ( wall [ index ] . nextsector , cx , cy , & ceilZ2 , & floorZ2 ) ;
ceilZ = ClipLow ( ceilZ , ceilZ2 ) ;
floorZ = ClipHigh ( floorZ , floorZ2 ) ;
cz = ( ceilZ + floorZ ) > > 1 ;
sfxPlay3DSound ( cx , cy , cz , pXSource - > data4 , nSector ) ;
}
}
return ;
case OBJ_SPRITE :
if ( pXSource - > data2 < = 0 ) seqKill ( 3 , sprite [ index ] . extra ) ;
else {
seqSpawn ( pXSource - > data2 , 3 , sprite [ index ] . extra , - 1 ) ;
2020-05-05 18:50:14 +00:00
if ( pXSource - > data4 > 0 )
sfxPlay3DSound ( & sprite [ index ] , pXSource - > data4 , - 1 , 0 ) ;
2020-02-07 19:47:43 +00:00
}
return ;
}
}
2020-03-30 19:54:28 +00:00
int condSerialize ( int objType , int objIndex ) {
switch ( objType ) {
case OBJ_SECTOR : return kCondSerialSector + objIndex ;
case OBJ_WALL : return kCondSerialWall + objIndex ;
case OBJ_SPRITE : return kCondSerialSprite + objIndex ;
}
ThrowError ( " Unknown object type %d, index %d " , objType , objIndex )
return - 1 ;
}
void condUnserialize ( int serial , int * objType , int * objIndex ) {
if ( serial > = kCondSerialSector & & serial < kCondSerialWall ) {
* objIndex = serial - kCondSerialSector ;
* objType = OBJ_SECTOR ;
} else if ( serial > = kCondSerialWall & & serial < kCondSerialSprite ) {
* objIndex = serial - kCondSerialWall ;
* objType = OBJ_WALL ;
} else if ( serial > = kCondSerialSprite & & serial < kCondSerialMax ) {
* objIndex = serial - kCondSerialSprite ;
* objType = OBJ_SPRITE ;
} else {
ThrowError ( " %d is not condition serial! " ) ;
}
}
2020-03-13 20:59:13 +00:00
bool condPush ( XSPRITE * pXSprite , int objType , int objIndex ) {
2020-03-30 19:54:28 +00:00
pXSprite - > targetX = condSerialize ( objType , objIndex ) ;
2020-03-13 20:59:13 +00:00
return true ;
}
2020-03-19 19:35:31 +00:00
bool condRestore ( XSPRITE * pXSprite ) {
2020-03-30 19:54:28 +00:00
pXSprite - > targetX = pXSprite - > targetY ;
2020-03-19 19:35:31 +00:00
return true ;
}
2020-03-30 19:54:28 +00:00
/*XSPRITE* condGetElse(XSPRITE* pXSprite) {
spritetype * pCond = & sprite [ pXSprite - > reference ] ;
for ( int i = bucketHead [ pXSprite - > rxID ] ; i < bucketHead [ pXSprite - > rxID + 1 ] ; i + + ) {
if ( rxBucket [ i ] . index = = pCond - > index ) continue ;
else if ( rxBucket [ i ] . type = = OBJ_SPRITE & & xspriRangeIsFine ( sprite [ rxBucket [ i ] . index ] . extra ) ) {
spritetype * pElse = & sprite [ rxBucket [ i ] . index ] ;
if ( pElse - > type ! = kModernCondition | | pElse - > yvel = = pCond - > yvel )
continue ;
XSPRITE * pXElse = & xsprite [ pElse - > extra ] ;
if ( pXElse - > data1 = = pXSprite - > data1 & & pXElse - > data2 = = pXSprite - > data2 & & pXElse - > data3 = = pXSprite - > data3
& & pXElse - > data4 = = pXSprite - > data4 ) return pXElse ;
}
2020-03-19 19:35:31 +00:00
}
2020-03-30 19:54:28 +00:00
return NULL ;
} */
// normal comparison
bool condCmp ( int val , int arg1 , int arg2 , int comOp ) {
if ( comOp & 0x2000 ) return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( val > arg1 ) : ( val > = arg1 ) ; // blue sprite
else if ( comOp & 0x4000 ) return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( val < arg1 ) : ( val < = arg1 ) ; // green sprite
else return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( val > = arg1 & & val < = arg2 ) : ( val = = arg1 ) ;
2020-03-19 19:35:31 +00:00
}
2020-03-30 19:54:28 +00:00
// no extra comparison (val always = 0)?
bool condCmpne ( int arg1 , int arg2 , int comOp ) {
2020-05-05 18:50:14 +00:00
if ( comOp & 0x2000 ) return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( 0 > arg1 ) : ( 0 > = arg1 ) ; // blue sprite
else if ( comOp & 0x4000 ) return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( 0 < arg1 ) : ( 0 < = arg1 ) ; // green sprite
else return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( 0 > = arg1 & & 0 < = arg2 ) : ( 0 = = arg1 ) ;
}
// bool comparison
bool condCmpb ( int val , int arg1 , int arg2 , int comOp ) {
2020-04-07 20:30:00 +00:00
2020-05-05 18:50:14 +00:00
arg1 = ClipRange ( arg1 , 0 , 1 ) ; arg2 = ClipRange ( arg2 , 0 , 1 ) ;
if ( comOp & 0x2000 ) return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( val > 0 ) : ( val > = 0 ) ; // blue sprite
else if ( comOp & 0x4000 ) return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( val < arg1 ) : ( val < = arg1 ) ; // green sprite
else return ( comOp & CSTAT_SPRITE_BLOCK ) ? ( val > = arg1 & & val < = arg2 ) : ( val = = arg1 ) ;
2020-04-07 20:30:00 +00:00
2020-03-13 20:59:13 +00:00
}
2020-05-05 18:50:14 +00:00
void condError ( XSPRITE * pXCond , const char * pzFormat , . . . ) {
char buffer [ 256 ] ; char buffer2 [ 512 ] ;
Bsprintf ( buffer , " \n CONDITION RX: %d, TX: %d, SPRITE: #%d RETURNS: \n " , pXCond - > rxID , pXCond - > txID , pXCond - > reference ) ;
va_list args ;
va_start ( args , pzFormat ) ;
vsprintf ( buffer2 , pzFormat , args ) ;
ThrowError ( Bstrcat ( buffer , buffer2 ) ) ;
}
bool condCheckMixed ( XSPRITE * pXCond , EVENT event , int cmpOp , bool PUSH ) {
2020-03-13 20:59:13 +00:00
UNREFERENCED_PARAMETER ( PUSH ) ;
//int var = -1;
int cond = pXCond - > data1 - kCondMixedBase ; int arg1 = pXCond - > data2 ;
2020-04-01 20:19:50 +00:00
int arg2 = pXCond - > data3 ; int arg3 = pXCond - > data4 ;
2020-03-13 20:59:13 +00:00
2020-03-30 19:54:28 +00:00
int objType = - 1 ; int objIndex = - 1 ;
condUnserialize ( pXCond - > targetX , & objType , & objIndex ) ;
2020-03-13 20:59:13 +00:00
switch ( cond ) {
2020-03-30 19:54:28 +00:00
case 0 : return ( objType = = OBJ_SECTOR & & sectRangeIsFine ( objIndex ) ) ; // is a sector?
2020-04-01 20:19:50 +00:00
case 5 : return ( objType = = OBJ_WALL & & wallRangeIsFine ( objIndex ) ) ; // is a wall?
case 10 : return ( objType = = OBJ_SPRITE & & spriRangeIsFine ( objIndex ) ) ; // is a sprite?
case 15 : // x-index is fine?
switch ( objType ) {
case OBJ_WALL : return xwallRangeIsFine ( wall [ objIndex ] . extra ) ;
case OBJ_SPRITE : return xspriRangeIsFine ( sprite [ objIndex ] . extra ) ;
case OBJ_SECTOR : return xsectRangeIsFine ( sector [ objIndex ] . extra ) ;
}
break ;
2020-03-30 19:54:28 +00:00
case 20 : // type in a range?
2020-03-13 20:59:13 +00:00
switch ( objType ) {
case OBJ_WALL :
2020-04-01 20:19:50 +00:00
return condCmp ( wall [ objIndex ] . type , arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
case OBJ_SPRITE :
2020-04-01 20:19:50 +00:00
return condCmp ( ( sprite [ objIndex ] . type ! = kThingBloodChunks ) ? sprite [ objIndex ] . type : sprite [ objIndex ] . inittype , arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
case OBJ_SECTOR :
2020-04-01 20:19:50 +00:00
return condCmp ( sector [ objIndex ] . type , arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
}
break ;
2020-05-05 18:50:14 +00:00
case 25 : case 26 : case 27 :
case 28 : case 29 : case 30 :
case 31 : case 32 : case 33 :
2020-04-01 20:19:50 +00:00
switch ( objType ) {
case OBJ_WALL : {
walltype * pObj = & wall [ objIndex ] ;
switch ( cond ) {
case 25 : return condCmp ( pObj - > picnum , arg1 , arg2 , cmpOp ) ;
case 26 : return condCmp ( pObj - > pal , arg1 , arg2 , cmpOp ) ;
case 27 : return condCmp ( pObj - > shade , arg1 , arg2 , cmpOp ) ;
case 28 : return ( pObj - > cstat & arg1 ) ;
case 29 : return ( pObj - > hitag & arg1 ) ;
case 30 : return condCmp ( pObj - > xrepeat , arg1 , arg2 , cmpOp ) ;
case 31 : return condCmp ( pObj - > xpanning , arg1 , arg2 , cmpOp ) ;
case 32 : return condCmp ( pObj - > yrepeat , arg1 , arg2 , cmpOp ) ;
case 33 : return condCmp ( pObj - > ypanning , arg1 , arg2 , cmpOp ) ;
}
break ;
}
case OBJ_SPRITE : {
spritetype * pObj = & sprite [ objIndex ] ;
switch ( cond ) {
case 25 : return condCmp ( pObj - > picnum , arg1 , arg2 , cmpOp ) ;
case 26 : return condCmp ( pObj - > pal , arg1 , arg2 , cmpOp ) ;
case 27 : return condCmp ( pObj - > shade , arg1 , arg2 , cmpOp ) ;
case 28 : return ( pObj - > cstat & arg1 ) ;
case 29 : return ( pObj - > hitag & arg1 ) ;
case 30 : return condCmp ( pObj - > xrepeat , arg1 , arg2 , cmpOp ) ;
case 31 : return condCmp ( pObj - > xoffset , arg1 , arg2 , cmpOp ) ;
case 32 : return condCmp ( pObj - > yrepeat , arg1 , arg2 , cmpOp ) ;
case 33 : return condCmp ( pObj - > yoffset , arg1 , arg2 , cmpOp ) ;
}
break ;
}
case OBJ_SECTOR : {
sectortype * pObj = & sector [ objIndex ] ;
switch ( cond ) {
2020-05-05 18:50:14 +00:00
case 25 :
switch ( arg3 ) {
default : return ( condCmp ( pObj - > floorpicnum , arg1 , arg2 , cmpOp ) | | condCmp ( pObj - > ceilingpicnum , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( pObj - > floorpicnum , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( pObj - > ceilingpicnum , arg1 , arg2 , cmpOp ) ;
}
break ;
case 26 :
switch ( arg3 ) {
default : return ( condCmp ( pObj - > floorpal , arg1 , arg2 , cmpOp ) | | condCmp ( pObj - > ceilingpal , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( pObj - > floorpal , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( pObj - > ceilingpal , arg1 , arg2 , cmpOp ) ;
}
break ;
case 27 :
switch ( arg3 ) {
default : return ( condCmp ( pObj - > floorshade , arg1 , arg2 , cmpOp ) | | condCmp ( pObj - > ceilingshade , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( pObj - > floorshade , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( pObj - > ceilingshade , arg1 , arg2 , cmpOp ) ;
}
break ;
case 28 :
switch ( arg3 ) {
default : return ( ( pObj - > floorstat & arg1 ) | | ( pObj - > ceilingshade & arg1 ) ) ;
case 1 : return ( pObj - > floorstat & arg1 ) ;
case 2 : return ( pObj - > ceilingshade & arg1 ) ;
}
break ;
2020-04-01 20:19:50 +00:00
case 29 : return ( pObj - > hitag & arg1 ) ;
case 30 : return condCmp ( pObj - > floorxpanning , arg1 , arg2 , cmpOp ) ;
case 31 : return condCmp ( pObj - > ceilingxpanning , arg1 , arg2 , cmpOp ) ;
case 32 : return condCmp ( pObj - > floorypanning , arg1 , arg2 , cmpOp ) ;
case 33 : return condCmp ( pObj - > ceilingypanning , arg1 , arg2 , cmpOp ) ;
}
break ;
}
}
2020-04-07 20:30:00 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 41 : case 42 : case 43 :
case 44 : case 50 : case 51 :
case 52 : case 53 : case 54 :
case 55 : case 56 : case 57 :
case 58 : case 59 : case 70 :
case 71 :
2020-03-13 20:59:13 +00:00
switch ( objType ) {
case OBJ_WALL : {
XWALL * pXObj = ( xwallRangeIsFine ( wall [ objIndex ] . extra ) ) ? & xwall [ wall [ objIndex ] . extra ] : NULL ;
2020-05-05 18:50:14 +00:00
if ( ! pXObj ) return condCmpne ( arg1 , arg2 , cmpOp ) ;
switch ( cond ) {
case 41 : return condCmp ( pXObj - > data , arg1 , arg2 , cmpOp ) ;
case 50 : return condCmp ( pXObj - > rxID , arg1 , arg2 , cmpOp ) ;
case 51 : return condCmp ( pXObj - > txID , arg1 , arg2 , cmpOp ) ;
case 52 : return condCmpb ( pXObj - > locked , arg1 , arg2 , cmpOp ) ;
case 53 : return condCmpb ( pXObj - > triggerOn , arg1 , arg2 , cmpOp ) ;
case 54 : return condCmpb ( pXObj - > triggerOff , arg1 , arg2 , cmpOp ) ;
case 55 : return condCmpb ( pXObj - > triggerOnce , arg1 , arg2 , cmpOp ) ;
case 56 : return condCmpb ( pXObj - > isTriggered , arg1 , arg2 , cmpOp ) ;
case 57 :
return condCmpb ( pXObj - > state , arg1 , arg2 , cmpOp ) ;
case 58 : return condCmp ( ( kPercentFull * pXObj - > busy ) / 65536 , arg1 , arg2 , cmpOp ) ;
case 59 : return condCmpb ( pXObj - > dudeLockout , arg1 , arg2 , cmpOp ) ;
case 70 :
switch ( arg3 ) {
default : return ( condCmp ( seqGetID ( 0 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) | | condCmp ( seqGetID ( 4 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( seqGetID ( 0 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( seqGetID ( 4 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
}
break ;
case 71 :
switch ( arg3 ) {
default : return ( condCmp ( seqGetStatus ( 0 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) | | condCmp ( seqGetStatus ( 4 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( seqGetStatus ( 0 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( seqGetStatus ( 4 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
}
break ;
}
2020-03-13 20:59:13 +00:00
break ;
}
case OBJ_SPRITE : {
XSPRITE * pXObj = ( xspriRangeIsFine ( sprite [ objIndex ] . extra ) ) ? & xsprite [ sprite [ objIndex ] . extra ] : NULL ;
2020-05-05 18:50:14 +00:00
if ( ! pXObj ) return condCmpne ( arg1 , arg2 , cmpOp ) ;
switch ( cond ) {
case 41 : case 42 :
case 43 : case 44 :
return condCmp ( getDataFieldOfObject ( OBJ_SPRITE , objIndex , 1 + cond - 41 ) , arg1 , arg2 , cmpOp ) ;
case 50 : return condCmp ( pXObj - > rxID , arg1 , arg2 , cmpOp ) ;
case 51 : return condCmp ( pXObj - > txID , arg1 , arg2 , cmpOp ) ;
case 52 : return condCmpb ( pXObj - > locked , arg1 , arg2 , cmpOp ) ;
case 53 : return condCmpb ( pXObj - > triggerOn , arg1 , arg2 , cmpOp ) ;
case 54 : return condCmpb ( pXObj - > triggerOff , arg1 , arg2 , cmpOp ) ;
case 55 : return condCmpb ( pXObj - > triggerOnce , arg1 , arg2 , cmpOp ) ;
case 56 : return condCmpb ( pXObj - > isTriggered , arg1 , arg2 , cmpOp ) ;
case 57 : return condCmpb ( pXObj - > state , arg1 , arg2 , cmpOp ) ;
case 58 : return condCmp ( ( kPercentFull * pXObj - > busy ) / 65536 , arg1 , arg2 , cmpOp ) ;
case 59 : return condCmpb ( pXObj - > DudeLockout , arg1 , arg2 , cmpOp ) ;
case 70 : return condCmp ( seqGetID ( 3 , sprite [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
case 71 : return condCmp ( seqGetStatus ( 3 , sprite [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
}
2020-03-13 20:59:13 +00:00
break ;
}
case OBJ_SECTOR : {
XSECTOR * pXObj = ( xsectRangeIsFine ( sector [ objIndex ] . extra ) ) ? & xsector [ sector [ objIndex ] . extra ] : NULL ;
2020-05-05 18:50:14 +00:00
if ( ! pXObj ) return condCmpne ( arg1 , arg2 , cmpOp ) ;
switch ( cond ) {
case 41 : return condCmp ( pXObj - > data , arg1 , arg2 , cmpOp ) ;
case 50 : return condCmp ( pXObj - > rxID , arg1 , arg2 , cmpOp ) ;
case 51 : return condCmp ( pXObj - > txID , arg1 , arg2 , cmpOp ) ;
case 52 : return condCmpb ( pXObj - > locked , arg1 , arg2 , cmpOp ) ;
case 53 : return condCmpb ( pXObj - > triggerOn , arg1 , arg2 , cmpOp ) ;
case 54 : return condCmpb ( pXObj - > triggerOff , arg1 , arg2 , cmpOp ) ;
case 55 : return condCmpb ( pXObj - > triggerOnce , arg1 , arg2 , cmpOp ) ;
case 56 : return condCmpb ( pXObj - > isTriggered , arg1 , arg2 , cmpOp ) ;
case 57 : return condCmpb ( pXObj - > state , arg1 , arg2 , cmpOp ) ;
case 58 : return condCmp ( ( kPercentFull * pXObj - > busy ) / 65536 , arg1 , arg2 , cmpOp ) ;
case 59 : return condCmpb ( pXObj - > dudeLockout , arg1 , arg2 , cmpOp ) ;
case 70 :
switch ( arg3 ) {
default : return ( condCmp ( seqGetID ( 1 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) | | condCmp ( seqGetID ( 2 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( seqGetID ( 1 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( seqGetID ( 2 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
}
2020-03-13 20:59:13 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 71 :
switch ( arg3 ) {
default : return ( condCmp ( seqGetStatus ( 1 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) | | condCmp ( seqGetStatus ( 2 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( seqGetStatus ( 1 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( seqGetStatus ( 2 , wall [ objIndex ] . extra ) , arg1 , arg2 , cmpOp ) ;
}
break ;
}
break ;
2020-03-13 20:59:13 +00:00
}
}
break ;
2020-03-30 19:54:28 +00:00
case 99 : return condCmp ( event . cmd , arg1 , arg2 , cmpOp ) ; // this codition received specified command?
2020-03-13 20:59:13 +00:00
}
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Mixed: Unexpected condition id (%d)! " , cond ) ;
2020-03-13 20:59:13 +00:00
return false ;
}
2020-05-05 18:50:14 +00:00
bool condCheckSector ( XSPRITE * pXCond , int cmpOp , bool PUSH ) {
2020-03-13 20:59:13 +00:00
int var = - 1 ;
int cond = pXCond - > data1 - kCondSectorBase ; int arg1 = pXCond - > data2 ;
2020-04-07 20:30:00 +00:00
int arg2 = pXCond - > data3 ; //int arg3 = pXCond->data4;
2020-03-30 19:54:28 +00:00
int objType = - 1 ; int objIndex = - 1 ;
condUnserialize ( pXCond - > targetX , & objType , & objIndex ) ;
2020-03-13 20:59:13 +00:00
if ( objType ! = OBJ_SECTOR | | ! sectRangeIsFine ( objIndex ) )
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Sector conditions: \n Object #%d (objType: %d) is not a sector! " , objIndex , objType ) ;
2020-03-13 20:59:13 +00:00
sectortype * pSect = & sector [ objIndex ] ;
XSECTOR * pXSect = ( xsectRangeIsFine ( pSect - > extra ) ) ? & xsector [ pSect - > extra ] : NULL ;
if ( cond < ( kCondRange > > 1 ) ) {
switch ( cond ) {
2020-04-01 20:19:50 +00:00
default : break ;
case 0 : return condCmp ( pSect - > visibility , arg1 , arg2 , cmpOp ) ;
case 5 : return condCmp ( pSect - > floorheinum , arg1 , arg2 , cmpOp ) ;
case 6 : return condCmp ( pSect - > ceilingheinum , arg1 , arg2 , cmpOp ) ;
case 10 : // required sprite type is in current sector?
for ( var = headspritesect [ objIndex ] ; var > = 0 ; var = nextspritesect [ var ] ) {
if ( ! condCmp ( sprite [ var ] . type , arg1 , arg2 , cmpOp ) ) continue ;
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , var ) ;
return true ;
}
return false ;
2020-03-13 20:59:13 +00:00
}
} else if ( pXSect ) {
switch ( cond ) {
2020-03-30 19:54:28 +00:00
default : break ;
2020-03-13 20:59:13 +00:00
case 50 : return pXSect - > Underwater ;
2020-04-01 20:19:50 +00:00
case 55 : // compare floor height (in %)
case 56 : { // compare ceil height (in %)
2020-03-30 19:54:28 +00:00
int h = 0 ; int curH = 0 ;
switch ( pSect - > type ) {
case kSectorZMotion :
case kSectorRotate :
case kSectorSlide :
if ( cond = = 60 ) {
h = ClipLow ( abs ( pXSect - > onFloorZ - pXSect - > offFloorZ ) , 1 ) ;
curH = abs ( pSect - > floorz - pXSect - > offFloorZ ) ;
} else {
h = ClipLow ( abs ( pXSect - > onCeilZ - pXSect - > offCeilZ ) , 1 ) ;
curH = abs ( pSect - > ceilingz - pXSect - > offCeilZ ) ;
}
2020-05-05 18:50:14 +00:00
return condCmp ( ( kPercentFull * curH ) / h , arg1 , arg2 , cmpOp ) ;
2020-03-30 19:54:28 +00:00
default :
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Sector conditions: \n Usupported sector type %d " , pSect - > type ) ;
2020-03-30 19:54:28 +00:00
return false ;
}
}
2020-03-13 20:59:13 +00:00
}
} else {
switch ( cond ) {
2020-03-19 19:35:31 +00:00
default : return false ;
2020-05-05 18:50:14 +00:00
case 55 :
case 56 :
2020-03-30 19:54:28 +00:00
return condCmpne ( arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
}
}
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Sector conditions: Unexpected condition id (%d)! " , cond ) ;
2020-03-05 20:46:05 +00:00
return false ;
}
2020-05-05 18:50:14 +00:00
bool condCheckWall ( XSPRITE * pXCond , int cmpOp , bool PUSH ) {
UNREFERENCED_PARAMETER ( PUSH ) ;
2020-03-13 20:59:13 +00:00
int var = - 1 ;
int cond = pXCond - > data1 - kCondWallBase ; int arg1 = pXCond - > data2 ;
2020-04-07 20:30:00 +00:00
int arg2 = pXCond - > data3 ; //int arg3 = pXCond->data4;
2020-03-30 19:54:28 +00:00
int objType = - 1 ; int objIndex = - 1 ;
condUnserialize ( pXCond - > targetX , & objType , & objIndex ) ;
2020-03-13 20:59:13 +00:00
if ( objType ! = OBJ_WALL | | ! wallRangeIsFine ( objIndex ) )
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Wall conditions: \n Object #%d (objType: %d) is not a wall! " , objIndex , objType ) ;
2020-03-13 20:59:13 +00:00
walltype * pWall = & wall [ objIndex ] ;
2020-04-07 20:30:00 +00:00
//XWALL* pXWall = (xwallRangeIsFine(pWall->extra)) ? &xwall[pWall->extra] : NULL;
2020-03-13 20:59:13 +00:00
if ( cond < ( kCondRange > > 1 ) ) {
switch ( cond ) {
2020-03-30 19:54:28 +00:00
default : break ;
2020-04-01 20:19:50 +00:00
case 0 :
return condCmp ( pWall - > overpicnum , arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
case 5 :
2020-04-01 20:19:50 +00:00
if ( ! sectRangeIsFine ( ( var = sectorofwall ( objIndex ) ) ) ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , var ) ;
2020-03-13 20:59:13 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 10 : // this wall is a mirror? // must be as constants here
return ( pWall - > type ! = kWallStack & & condCmp ( pWall - > picnum , 4080 , ( 4080 + 16 ) - 1 , 0 ) ) ;
case 15 :
2020-03-30 19:54:28 +00:00
if ( ! sectRangeIsFine ( pWall - > nextsector ) ) return false ;
2020-03-13 20:59:13 +00:00
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , pWall - > nextsector ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 20 :
if ( ! wallRangeIsFine ( pWall - > nextwall ) ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_WALL , pWall - > nextwall ) ;
2020-03-13 20:59:13 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 25 : // next wall belongs to sector?
2020-03-19 19:35:31 +00:00
if ( ! sectRangeIsFine ( var = sectorofwall ( pWall - > nextwall ) ) ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , var ) ;
return true ;
2020-05-19 19:42:29 +00:00
/*case 57: // someone touching this wall?
for ( int i = headspritestat [ kStatDude ] ; i > = 0 ; i = nextspritestat [ i ] ) {
if ( ! xspriRangeIsFine ( sprite [ i ] . extra ) | | ( gSpriteHit [ sprite [ i ] . extra ] . hit & 0xc000 ) ! = 0x8000 ) continue ;
else if ( ( gSpriteHit [ sprite [ i ] . extra ] . hit & 0x3fff ) ! = objIndex ) continue ;
else if ( PUSH ) {
condPush ( pXCond , OBJ_SPRITE , i ) ;
return true ;
}
}
return false ; */
2020-03-13 20:59:13 +00:00
}
}
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Wall conditions: Unexpected condition id (%d)! " , cond ) ;
2020-03-13 20:59:13 +00:00
return false ;
}
2020-05-05 18:50:14 +00:00
bool condCheckPlayer ( XSPRITE * pXCond , int cmpOp , bool PUSH ) {
2020-03-19 19:35:31 +00:00
int var = - 1 ; PLAYER * pPlayer = NULL ;
2020-05-05 18:50:14 +00:00
int cond = pXCond - > data1 - kCondPlayerBase ; int arg1 = pXCond - > data2 ;
2020-03-19 19:35:31 +00:00
int arg2 = pXCond - > data3 ; int arg3 = pXCond - > data4 ;
2020-05-05 18:50:14 +00:00
2020-03-30 19:54:28 +00:00
int objType = - 1 ; int objIndex = - 1 ;
condUnserialize ( pXCond - > targetX , & objType , & objIndex ) ;
2020-05-05 18:50:14 +00:00
if ( objType = = OBJ_SPRITE ) {
for ( int i = 0 ; i < kMaxPlayers ; i + + ) {
if ( objIndex ! = gPlayer [ i ] . nSprite ) continue ;
pPlayer = & gPlayer [ i ] ;
break ;
}
}
2020-04-04 19:48:23 +00:00
2020-05-05 18:50:14 +00:00
if ( ! spriRangeIsFine ( objIndex ) | | ! pPlayer )
condError ( pXCond , " \n Player conditions: \n Object #%d (objType: %d) is not a player! " , objIndex , objType ) ;
spritetype * pSpr = pPlayer - > pSprite ; //XSPRITE* pXSpr = pPlayer->pXSprite;
2020-03-19 19:35:31 +00:00
switch ( cond ) {
2020-05-05 18:50:14 +00:00
case 0 : // check if this player is connected
if ( ! condCmp ( pPlayer - > nPlayer + 1 , arg1 , arg2 , cmpOp ) | | ! spriRangeIsFine ( pPlayer - > nSprite ) ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , pPlayer - > nSprite ) ;
return ( pPlayer - > nPlayer > = 0 ) ;
case 1 : return condCmp ( ( gGameOptions . nGameType ! = 3 ) ? 0 : pPlayer - > teamId + 1 , arg1 , arg2 , cmpOp ) ; // compare team
case 2 : return ( arg1 > 0 & & arg1 < 8 & & pPlayer - > hasKey [ arg1 - 1 ] ) ;
case 3 : return ( arg1 > 0 & & arg1 < 15 & & pPlayer - > hasWeapon [ arg1 - 1 ] ) ;
2020-03-30 19:54:28 +00:00
case 4 : return condCmp ( pPlayer - > curWeapon , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
case 5 : return ( arg1 > 0 & & arg1 < 6 & & condCmp ( pPlayer - > packSlots [ arg1 - 1 ] . curAmount , arg2 , arg3 , cmpOp ) ) ;
case 6 : return ( arg1 > 0 & & arg1 < 6 & & pPlayer - > packSlots [ arg1 - 1 ] . isActive ) ;
case 7 : return condCmp ( pPlayer - > packItemId + 1 , arg1 , arg2 , cmpOp ) ;
2020-03-30 19:54:28 +00:00
case 8 : // check for powerup amount in %
2020-05-05 18:50:14 +00:00
if ( arg3 > 0 & & arg3 < 30 ) var = ( 12 + arg3 ) - 1 ; // allowable powerups
else condError ( pXCond , " Unexpected powerup #%d " , arg3 ) ;
return condCmp ( ( kPercentFull * pPlayer - > pwUpTime [ var ] ) / gPowerUpInfo [ var ] . bonusTime , arg1 , arg2 , cmpOp ) ;
case 9 :
if ( ! spriRangeIsFine ( pPlayer - > fraggerId ) ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , pPlayer - > fraggerId ) ;
return true ;
2020-03-19 19:35:31 +00:00
case 10 : // check keys pressed
switch ( arg1 ) {
2020-05-05 18:50:14 +00:00
case 1 : return ( pPlayer - > input . forward > 0 ) ; // forward
case 2 : return ( pPlayer - > input . forward < 0 ) ; // backward
case 3 : return ( pPlayer - > input . strafe > 0 ) ; // left
case 4 : return ( pPlayer - > input . strafe < 0 ) ; // right
case 5 : return ( pPlayer - > input . buttonFlags . jump ) ; // jump
case 6 : return ( pPlayer - > input . buttonFlags . crouch ) ; // crouch
case 7 : return ( pPlayer - > input . buttonFlags . shoot ) ; // normal fire weapon
case 8 : return ( pPlayer - > input . buttonFlags . shoot2 ) ; // alt fire weapon
2020-03-19 19:35:31 +00:00
default :
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Player conditions: \n Specify a correct key! " ) ;
2020-03-19 19:35:31 +00:00
break ;
}
return false ;
case 11 : return ( pPlayer - > isRunning ) ;
case 12 : return ( pPlayer - > fallScream ) ; // falling in abyss?
2020-05-05 18:50:14 +00:00
case 13 : return condCmp ( pPlayer - > lifeMode + 1 , arg1 , arg2 , cmpOp ) ;
case 14 : return condCmp ( pPlayer - > posture + 1 , arg1 , arg2 , cmpOp ) ;
2020-03-30 19:54:28 +00:00
case 46 : return condCmp ( pPlayer - > sceneQav , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
case 47 : return ( pPlayer - > godMode | | powerupCheck ( pPlayer , kPwUpDeathMask ) ) ;
2020-03-19 19:35:31 +00:00
case 48 : return isShrinked ( pSpr ) ;
case 49 : return isGrown ( pSpr ) ;
2020-05-05 18:50:14 +00:00
}
2020-03-19 19:35:31 +00:00
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Player conditions: \n Unexpected condition #%d! " , cond ) ;
return false ;
}
bool condCheckDude ( XSPRITE * pXCond , int cmpOp , bool PUSH ) {
UNREFERENCED_PARAMETER ( cmpOp ) ;
//int var = -1; PLAYER* pPlayer = NULL;
int cond = pXCond - > data1 - kCondDudeBase ; //int arg1 = pXCond->data2;
//int arg2 = pXCond->data3; int arg3 = pXCond->data4;
int objType = - 1 ; int objIndex = - 1 ;
condUnserialize ( pXCond - > targetX , & objType , & objIndex ) ;
if ( objType ! = OBJ_SPRITE | | ! spriRangeIsFine ( objIndex ) )
condError ( pXCond , " Dude conditions: \n Object #%d (objType: %d) is not a dude! " , objIndex , objType ) ;
spritetype * pSpr = & sprite [ objIndex ] ; int nType = pSpr - > type ;
if ( nType = = kThingBloodChunks | | ! xspriRangeIsFine ( pSpr - > extra ) ) {
nType = pSpr - > inittype ;
if ( nType > = kDudeBase & & nType < = kDudeMax & & ( nType < kDudePlayer1 | | nType > kDudePlayer8 ) ) return false ;
else condError ( pXCond , " Dude conditions: \n Object #%d (objType: %d) is not an enemy ! " , objIndex, objType) ;
} else if ( IsDudeSprite ( pSpr ) ) {
XSPRITE * pXSpr = & xsprite [ pSpr - > extra ] ;
if ( pSpr - > flags & kHitagRespawn | | pSpr - > statnum = = kStatRespawn ) return false ;
else if ( IsPlayerSprite ( pSpr ) ) condError ( pXCond , " Dude conditions: \n Object #%d (objType: %d) is not an enemy! " , objIndex , objType ) ;
2020-03-19 19:35:31 +00:00
switch ( cond ) {
default : break ;
2020-05-05 18:50:14 +00:00
case 0 : // dude have any targets?
2020-03-19 19:35:31 +00:00
if ( ! spriRangeIsFine ( pXSpr - > target ) ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , pXSpr - > target ) ;
return true ;
2020-05-05 18:50:14 +00:00
case 1 : return aiFightDudeIsAffected ( pXSpr ) ; // dude affected by ai fight?
2020-03-19 19:35:31 +00:00
}
}
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Dude conditions: \n Unexpected condition #%d! " , cond ) ;
2020-03-19 19:35:31 +00:00
return false ;
}
2020-05-05 18:50:14 +00:00
bool condCheckSprite ( XSPRITE * pXCond , int cmpOp , bool PUSH ) {
UNREFERENCED_PARAMETER ( PUSH ) ;
2020-04-07 20:30:00 +00:00
2020-03-30 19:54:28 +00:00
int var = - 1 ; PLAYER * pPlayer = NULL ; bool retn = false ;
2020-03-13 20:59:13 +00:00
int cond = pXCond - > data1 - kCondSpriteBase ; int arg1 = pXCond - > data2 ;
int arg2 = pXCond - > data3 ; int arg3 = pXCond - > data4 ;
2020-03-30 19:54:28 +00:00
int objType = - 1 ; int objIndex = - 1 ;
condUnserialize ( pXCond - > targetX , & objType , & objIndex ) ;
2020-03-13 20:59:13 +00:00
if ( objType ! = OBJ_SPRITE | | ! spriRangeIsFine ( objIndex ) )
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Sprite condition %d: \n Object #%d (objType: %d) is not a sprite! " , cond , objIndex , objType ) ;
2020-03-13 20:59:13 +00:00
spritetype * pSpr = & sprite [ objIndex ] ;
XSPRITE * pXSpr = ( xspriRangeIsFine ( pSpr - > extra ) ) ? & xsprite [ pSpr - > extra ] : NULL ;
if ( cond < ( kCondRange > > 1 ) ) {
switch ( cond ) {
2020-03-30 19:54:28 +00:00
default : break ;
2020-04-01 20:19:50 +00:00
case 0 : return condCmp ( ( pSpr - > ang & 2047 ) , arg1 , arg2 , cmpOp ) ;
case 5 : return condCmp ( pSpr - > statnum , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
case 6 : return ( ( pSpr - > flags & kHitagRespawn ) & & ( pSpr - > statnum & kStatRespawn ) ) ;
2020-04-01 20:19:50 +00:00
case 10 : return condCmp ( pSpr - > clipdist , arg1 , arg2 , cmpOp ) ;
case 15 :
2020-03-19 19:35:31 +00:00
if ( ! spriRangeIsFine ( pSpr - > owner ) ) return false ;
2020-03-13 20:59:13 +00:00
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , pSpr - > owner ) ;
2020-03-19 19:35:31 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 20 : // stays in a sector?
2020-03-19 19:35:31 +00:00
if ( ! sectRangeIsFine ( pSpr - > sectnum ) ) return false ;
2020-03-13 20:59:13 +00:00
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , pSpr - > sectnum ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 25 :
2020-03-30 19:54:28 +00:00
switch ( arg1 ) {
2020-04-01 20:19:50 +00:00
case 0 : return ( xvel [ pSpr - > index ] | | yvel [ pSpr - > index ] | | zvel [ pSpr - > index ] ) ;
2020-03-30 19:54:28 +00:00
case 1 : return ( xvel [ pSpr - > index ] ) ;
case 2 : return ( yvel [ pSpr - > index ] ) ;
case 3 : return ( zvel [ pSpr - > index ] ) ;
}
break ;
2020-04-01 20:19:50 +00:00
case 30 :
2020-03-19 19:35:31 +00:00
if ( ! spriteIsUnderwater ( pSpr ) & & ! spriteIsUnderwater ( pSpr , true ) ) return false ;
2020-03-13 20:59:13 +00:00
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , pSpr - > sectnum ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 35 : // hitscan: ceil?
case 36 : // hitscan: floor?
case 37 : // hitscan: wall?
case 38 : // hitscan: sprite?
2020-03-13 20:59:13 +00:00
switch ( arg1 ) {
case 0 : arg1 = CLIPMASK0 | CLIPMASK1 ; break ;
case 1 : arg1 = CLIPMASK0 ; break ;
case 2 : arg1 = CLIPMASK1 ; break ;
}
if ( ( pPlayer = getPlayerById ( pSpr - > type ) ) ! = NULL )
var = HitScan ( pSpr , pPlayer - > zWeapon - pSpr - > z , pPlayer - > aim . dx , pPlayer - > aim . dy , pPlayer - > aim . dz , arg1 , arg3 < < 1 ) ;
else if ( IsDudeSprite ( pSpr ) )
var = HitScan ( pSpr , pSpr - > z , Cos ( pSpr - > ang ) > > 16 , Sin ( pSpr - > ang ) > > 16 , gDudeSlope [ pSpr - > extra ] , arg1 , arg3 < < 1 ) ;
else
var = HitScan ( pSpr , pSpr - > z , Cos ( pSpr - > ang ) > > 16 , Sin ( pSpr - > ang ) > > 16 , 0 , arg1 , arg3 < < 1 ) ;
2020-03-19 19:35:31 +00:00
2020-03-30 19:54:28 +00:00
if ( var > = 0 ) {
switch ( cond ) {
2020-04-01 20:19:50 +00:00
case 35 : retn = ( var = = 1 ) ; break ;
case 36 : retn = ( var = = 2 ) ; break ;
case 37 : retn = ( var = = 0 | | var = = 4 ) ; break ;
case 38 : retn = ( var = = 3 ) ; break ;
2020-03-30 19:54:28 +00:00
}
2020-03-19 19:35:31 +00:00
2020-03-30 19:54:28 +00:00
if ( PUSH ) {
switch ( var ) {
case 3 :
condPush ( pXCond , OBJ_SPRITE , gHitInfo . hitsprite ) ;
break ;
case 0 : case 4 :
condPush ( pXCond , OBJ_WALL , gHitInfo . hitwall ) ;
break ;
case 1 : case 2 :
condPush ( pXCond , OBJ_SECTOR , gHitInfo . hitsect ) ;
break ;
}
}
2020-03-13 20:59:13 +00:00
}
2020-03-30 19:54:28 +00:00
return retn ;
2020-04-01 20:19:50 +00:00
case 45 : // this sprite is a target of some dude?
for ( int nSprite = headspritestat [ kStatDude ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
if ( pSpr - > index = = nSprite ) continue ;
spritetype * pDude = & sprite [ nSprite ] ;
if ( IsDudeSprite ( pDude ) & & xspriRangeIsFine ( pDude - > extra ) ) {
XSPRITE * pXDude = & xsprite [ pDude - > extra ] ;
if ( pXDude - > health < = 0 | | pXDude - > target ! = pSpr - > index ) continue ;
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , nSprite ) ;
return true ;
}
}
return false ;
2020-03-13 20:59:13 +00:00
}
} else if ( pXSpr ) {
switch ( cond ) {
2020-03-30 19:54:28 +00:00
default : break ;
case 50 : // compare hp (in %)
2020-04-07 20:30:00 +00:00
if ( IsDudeSprite ( pSpr ) ) var = ( pXSpr - > sysData2 > 0 ) ? ClipRange ( pXSpr - > sysData2 < < 4 , 1 , 65535 ) : getDudeInfo ( pSpr - > type ) - > startHealth < < 4 ;
2020-05-05 18:50:14 +00:00
else if ( pSpr - > type = = kThingBloodChunks ) return condCmpne ( arg1 , arg2 , cmpOp ) ;
else if ( pSpr - > type > = kThingBase & & pSpr - > type < kThingMax ) var = thingInfo [ pSpr - > type - kThingBase ] . startHealth < < 4 ;
return condCmp ( ( kPercentFull * pXSpr - > health ) / ClipLow ( var , 1 ) , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
case 55 : // touching ceil of sector?
2020-03-13 20:59:13 +00:00
if ( ( gSpriteHit [ pSpr - > extra ] . ceilhit & 0xc000 ) ! = 0x4000 ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , gSpriteHit [ pSpr - > extra ] . ceilhit & 0x3fff ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 56 : // touching floor of sector?
2020-03-30 19:54:28 +00:00
if ( ( gSpriteHit [ pSpr - > extra ] . florhit & 0xc000 ) ! = 0x4000 ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SECTOR , gSpriteHit [ pSpr - > extra ] . florhit & 0x3fff ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 57 : // touching walls of sector?
2020-03-13 20:59:13 +00:00
if ( ( gSpriteHit [ pSpr - > extra ] . hit & 0xc000 ) ! = 0x8000 ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_WALL , gSpriteHit [ pSpr - > extra ] . hit & 0x3fff ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 58 : // touching another sprite?
2020-03-13 20:59:13 +00:00
switch ( arg3 ) {
case 0 :
case 1 :
if ( ( gSpriteHit [ pSpr - > extra ] . florhit & 0xc000 ) = = 0xc000 ) var = gSpriteHit [ pSpr - > extra ] . florhit & 0x3fff ;
if ( arg3 | | var > = 0 ) break ;
fallthrough__ ;
case 2 :
if ( ( gSpriteHit [ pSpr - > extra ] . hit & 0xc000 ) = = 0xc000 ) var = gSpriteHit [ pSpr - > extra ] . hit & 0x3fff ;
if ( arg3 | | var > = 0 ) break ;
fallthrough__ ;
case 3 :
if ( ( gSpriteHit [ pSpr - > extra ] . ceilhit & 0xc000 ) = = 0xc000 ) var = gSpriteHit [ pSpr - > extra ] . ceilhit & 0x3fff ;
break ;
}
2020-03-30 19:54:28 +00:00
if ( var < 0 ) return false ;
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , var ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 65 : // compare burn time (in %)
2020-03-30 19:54:28 +00:00
var = ( IsDudeSprite ( pSpr ) ) ? 2400 : 1200 ;
2020-05-05 18:50:14 +00:00
if ( ! condCmp ( ( kPercentFull * pXSpr - > burnTime ) / var , arg1 , arg2 , cmpOp ) ) return false ;
2020-03-13 20:59:13 +00:00
else if ( PUSH ) condPush ( pXCond , OBJ_SPRITE , pXSpr - > burnSource ) ;
return true ;
2020-04-01 20:19:50 +00:00
case 70 :
2020-03-30 19:54:28 +00:00
return condCmp ( getSpriteMassBySize ( pSpr ) , arg1 , arg2 , cmpOp ) ; // mass of the sprite in a range?
2020-03-13 20:59:13 +00:00
}
} else {
switch ( cond ) {
2020-03-19 19:35:31 +00:00
default : return false ;
2020-03-13 20:59:13 +00:00
case 50 :
2020-05-05 18:50:14 +00:00
case 65 :
case 70 :
2020-03-30 19:54:28 +00:00
return condCmpne ( arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
}
}
2020-05-05 18:50:14 +00:00
condError ( pXCond , " Sprite conditions: Unexpected condition id (%d)! " , cond ) ;
2020-03-13 20:59:13 +00:00
return false ;
2020-03-05 20:46:05 +00:00
}
2020-05-05 18:50:14 +00:00
// this updates index of object in all conditions
2020-04-04 19:48:23 +00:00
void condUpdateObjectIndex ( int objType , int oldIndex , int newIndex ) {
2020-05-05 18:50:14 +00:00
int oldSerial = condSerialize ( objType , oldIndex ) ;
int newSerial = condSerialize ( objType , newIndex ) ;
for ( int nSpr = headspritestat [ kStatModernCondition ] ; nSpr > = 0 ; nSpr = nextspritestat [ nSpr ] ) {
XSPRITE * pXCond = & xsprite [ sprite [ nSpr ] . extra ] ;
if ( pXCond - > busyTime > 0 ) {
TRCONDITION * pCond = & gCondition [ pXCond - > sysData1 ] ;
2020-04-04 19:48:23 +00:00
for ( int k = 0 ; k < pCond - > length ; k + + ) {
if ( pCond - > obj [ k ] . type ! = objType | | pCond - > obj [ k ] . index ! = oldIndex ) continue ;
pCond - > obj [ k ] . index = newIndex ;
break ;
}
2020-05-05 18:50:14 +00:00
}
if ( pXCond - > targetX = = oldSerial ) pXCond - > targetX = newSerial ;
if ( pXCond - > targetY = = oldSerial ) pXCond - > targetY = newSerial ;
2020-04-04 19:48:23 +00:00
}
return ;
}
2020-02-07 19:47:43 +00:00
bool valueIsBetween ( int val , int min , int max ) {
2020-03-01 20:36:28 +00:00
return ( val > min & & val < max ) ;
2020-02-07 19:47:43 +00:00
}
char modernTypeSetSpriteState ( int nSprite , XSPRITE * pXSprite , int nState ) {
if ( ( pXSprite - > busy & 0xffff ) = = 0 & & pXSprite - > state = = nState )
return 0 ;
pXSprite - > busy = nState < < 16 ; pXSprite - > state = nState ;
evKill ( nSprite , 3 ) ;
if ( pXSprite - > restState ! = nState & & pXSprite - > waitTime > 0 )
evPost ( nSprite , 3 , ( pXSprite - > waitTime * 120 ) / 10 , pXSprite - > restState ? kCmdOn : kCmdOff ) ;
if ( pXSprite - > txID ! = 0 & & ( ( pXSprite - > triggerOn & & pXSprite - > state ) | | ( pXSprite - > triggerOff & & ! pXSprite - > state ) ) )
modernTypeSendCommand ( nSprite , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
return 1 ;
}
void modernTypeSendCommand ( int nSprite , int destChannel , COMMAND_ID command ) {
switch ( command ) {
case kCmdLink :
evSend ( nSprite , 3 , destChannel , kCmdModernUse ) ; // just send command to change properties
return ;
case kCmdUnlock :
evSend ( nSprite , 3 , destChannel , command ) ; // send normal command first
evSend ( nSprite , 3 , destChannel , kCmdModernUse ) ; // then send command to change properties
return ;
default :
evSend ( nSprite , 3 , destChannel , kCmdModernUse ) ; // send first command to change properties
evSend ( nSprite , 3 , destChannel , command ) ; // then send normal command
return ;
}
}
// this function used by various new modern types.
void modernTypeTrigger ( int destObjType , int destObjIndex , EVENT event ) {
if ( event . type ! = OBJ_SPRITE ) return ;
spritetype * pSource = & sprite [ event . index ] ;
if ( ! xspriRangeIsFine ( pSource - > extra ) ) return ;
XSPRITE * pXSource = & xsprite [ pSource - > extra ] ;
switch ( destObjType ) {
case OBJ_SECTOR :
if ( ! xsectRangeIsFine ( sector [ destObjIndex ] . extra ) ) return ;
break ;
case OBJ_WALL :
if ( ! xwallRangeIsFine ( wall [ destObjIndex ] . extra ) ) return ;
break ;
case OBJ_SPRITE :
if ( ! xspriRangeIsFine ( sprite [ destObjIndex ] . extra ) ) return ;
2020-03-01 20:36:28 +00:00
else if ( sprite [ destObjIndex ] . flags & kHitagFree ) return ;
/*switch (pSource->type) {
case kModernEffectSpawner :
case kModernWindGenerator :
switch ( sprite [ destObjIndex ] . type ) {
case kModernEffectSpawner :
case kModernWindGenerator :
viewSetSystemMessage ( " SRC %d, DEST %d " , Numsprites , sprite [ destObjIndex ] . type ) ;
break ;
}
break ;
} */
// allow redirect events received from some modern types.
// example: it allows to spawn FX effect if event was received from kModernEffectGen
// on many TX channels instead of just one.
switch ( sprite [ destObjIndex ] . type ) {
case kModernRandomTX :
case kModernSequentialTX :
spritetype * pSpr = & sprite [ destObjIndex ] ; XSPRITE * pXSpr = & xsprite [ pSpr - > extra ] ;
2020-05-05 18:50:14 +00:00
if ( pXSpr - > command ! = kCmdLink ) break ; // no redirect mode detected
else if ( ! pXSpr - > locked ) {
2020-03-01 20:36:28 +00:00
switch ( pSpr - > type ) {
case kModernRandomTX :
useRandomTx ( pXSpr , ( COMMAND_ID ) pXSource - > command , false ) ; // set random TX id
break ;
case kModernSequentialTX :
if ( pSpr - > flags & kModernTypeFlag1 ) {
seqTxSendCmdAll ( pXSpr , pSource - > index , ( COMMAND_ID ) pXSource - > command , true ) ;
return ;
}
useSequentialTx ( pXSpr , ( COMMAND_ID ) pXSource - > command , false ) ; // set next TX id
break ;
}
2020-05-05 18:50:14 +00:00
if ( pXSpr - > txID > 0 & & pXSpr - > txID < kChannelUserMax ) {
2020-03-01 20:36:28 +00:00
modernTypeSendCommand ( pSource - > index , pXSpr - > txID , ( COMMAND_ID ) pXSource - > command ) ;
2020-05-05 18:50:14 +00:00
}
2020-03-01 20:36:28 +00:00
return ;
}
2020-04-07 20:30:00 +00:00
break ;
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
break ;
default :
return ;
}
switch ( pSource - > type ) {
// allows teleport any sprite from any location to the source destination
case kMarkerWarpDest :
if ( destObjType ! = OBJ_SPRITE ) break ;
useTeleportTarget ( pXSource , & sprite [ destObjIndex ] ) ;
break ;
case kModernSpriteDamager :
// damages xsprite via TX ID
if ( destObjType ! = OBJ_SPRITE ) break ;
useSpriteDamager ( pXSource , & sprite [ destObjIndex ] ) ;
break ;
// can spawn any effect passed in data2 on it's or txID sprite
case kModernEffectSpawner :
if ( destObjType ! = OBJ_SPRITE | | pXSource - > data2 < 0 | | pXSource - > data2 > = kFXMax ) break ;
useEffectGen ( pXSource , & sprite [ destObjIndex ] ) ;
break ;
// takes data2 as SEQ ID and spawns it on it's or TX ID object
case kModernSeqSpawner :
useSeqSpawnerGen ( pXSource , destObjType , destObjIndex ) ;
break ;
// creates wind on TX ID sector
case kModernWindGenerator :
if ( destObjType ! = OBJ_SECTOR | | pXSource - > data2 < 0 ) break ;
useSectorWindGen ( pXSource , & sector [ destObjIndex ] ) ;
break ;
// size and pan changer of sprite/wall/sector via TX ID
case kModernObjSizeChanger :
useObjResizer ( pXSource , destObjType , destObjIndex ) ;
break ;
// iterate data filed value of destination object
case kModernObjDataAccumulator :
useIncDecGen ( pXSource , destObjType , destObjIndex ) ;
break ;
// change data field value of destination object
case kModernObjDataChanger :
useDataChanger ( pXSource , destObjType , destObjIndex ) ;
break ;
// change sector lighting dynamically
case kModernSectorFXChanger :
if ( destObjType ! = OBJ_SECTOR ) break ;
useSectorLigthChanger ( pXSource , & xsector [ sector [ destObjIndex ] . extra ] ) ;
break ;
// change target of dudes and make it fight
case kModernDudeTargetChanger :
if ( destObjType ! = OBJ_SPRITE ) break ;
useTargetChanger ( pXSource , & sprite [ destObjIndex ] ) ;
break ;
// change picture and palette of TX ID object
case kModernObjPicnumChanger :
usePictureChanger ( pXSource , destObjType , destObjIndex ) ;
break ;
// change various properties
case kModernObjPropertiesChanger :
usePropertiesChanger ( pXSource , destObjType , destObjIndex ) ;
break ;
}
}
// the following functions required for kModernDudeTargetChanger
//---------------------------------------
spritetype * aiFightGetTargetInRange ( spritetype * pSprite , int minDist , int maxDist , short data , short teamMode ) {
2020-02-08 20:13:29 +00:00
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ; XSPRITE * pXSprite = & xsprite [ pSprite - > extra ] ;
2020-02-07 19:47:43 +00:00
spritetype * pTarget = NULL ; XSPRITE * pXTarget = NULL ; spritetype * cTarget = NULL ;
for ( int nSprite = headspritestat [ kStatDude ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
pTarget = & sprite [ nSprite ] ; pXTarget = & xsprite [ pTarget - > extra ] ;
if ( ! aiFightDudeCanSeeTarget ( pXSprite , pDudeInfo , pTarget ) ) continue ;
int dist = aiFightGetTargetDist ( pSprite , pDudeInfo , pTarget ) ;
if ( dist < minDist | | dist > maxDist ) continue ;
else if ( pXSprite - > target = = pTarget - > index ) return pTarget ;
else if ( ! IsDudeSprite ( pTarget ) | | pTarget - > index = = pSprite - > index | | IsPlayerSprite ( pTarget ) ) continue ;
else if ( IsBurningDude ( pTarget ) | | ! IsKillableDude ( pTarget ) | | pTarget - > owner = = pSprite - > index ) continue ;
else if ( ( teamMode = = 1 & & aiFightIsMateOf ( pXSprite , pXTarget ) ) | | aiFightMatesHaveSameTarget ( pXSprite , pTarget , 1 ) ) continue ;
else if ( data = = 666 | | pXTarget - > data1 = = data ) {
if ( pXSprite - > target > 0 ) {
cTarget = & sprite [ pXSprite - > target ] ;
int fineDist1 = aiFightGetFineTargetDist ( pSprite , cTarget ) ;
int fineDist2 = aiFightGetFineTargetDist ( pSprite , pTarget ) ;
if ( fineDist1 < fineDist2 )
continue ;
}
return pTarget ;
}
}
return NULL ;
}
spritetype * aiFightTargetIsPlayer ( XSPRITE * pXSprite ) {
if ( pXSprite - > target > = 0 ) {
if ( IsPlayerSprite ( & sprite [ pXSprite - > target ] ) )
return & sprite [ pXSprite - > target ] ;
}
return NULL ;
}
spritetype * aiFightGetMateTargets ( XSPRITE * pXSprite ) {
int rx = pXSprite - > rxID ; spritetype * pMate = NULL ; XSPRITE * pXMate = NULL ;
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type = = OBJ_SPRITE ) {
pMate = & sprite [ rxBucket [ i ] . index ] ;
if ( pMate - > extra < 0 | | pMate - > index = = sprite [ pXSprite - > reference ] . index | | ! IsDudeSprite ( pMate ) )
continue ;
pXMate = & xsprite [ pMate - > extra ] ;
if ( pXMate - > target > - 1 ) {
if ( ! IsPlayerSprite ( & sprite [ pXMate - > target ] ) )
return & sprite [ pXMate - > target ] ;
}
}
}
return NULL ;
}
bool aiFightMatesHaveSameTarget ( XSPRITE * pXLeader , spritetype * pTarget , int allow ) {
int rx = pXLeader - > rxID ; spritetype * pMate = NULL ; XSPRITE * pXMate = NULL ;
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SPRITE )
continue ;
pMate = & sprite [ rxBucket [ i ] . index ] ;
if ( pMate - > extra < 0 | | pMate - > index = = sprite [ pXLeader - > reference ] . index | | ! IsDudeSprite ( pMate ) )
continue ;
pXMate = & xsprite [ pMate - > extra ] ;
if ( pXMate - > target = = pTarget - > index & & allow - - < = 0 )
return true ;
}
return false ;
}
bool aiFightDudeCanSeeTarget ( XSPRITE * pXDude , DUDEINFO * pDudeInfo , spritetype * pTarget ) {
spritetype * pDude = & sprite [ pXDude - > reference ] ;
int dx = pTarget - > x - pDude - > x ; int dy = pTarget - > y - pDude - > y ;
// check target
if ( approxDist ( dx , dy ) < pDudeInfo - > seeDist ) {
int eyeAboveZ = pDudeInfo - > eyeHeight * pDude - > yrepeat < < 2 ;
// is there a line of sight to the target?
if ( cansee ( pDude - > x , pDude - > y , pDude - > z , pDude - > sectnum , pTarget - > x , pTarget - > y , pTarget - > z - eyeAboveZ , pTarget - > sectnum ) ) {
/*int nAngle = getangle(dx, dy);
int losAngle = ( ( 1024 + nAngle - pDude - > ang ) & 2047 ) - 1024 ;
// is the target visible?
if ( klabs ( losAngle ) < 2048 ) // 360 deg periphery here*/
return true ;
}
2020-02-08 20:13:29 +00:00
2020-02-07 19:47:43 +00:00
}
return false ;
}
// this function required if monsters in genIdle ai state. It wakes up monsters
// when kModernDudeTargetChanger goes to off state, so they won't ignore the world.
void aiFightActivateDudes ( int rx ) {
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
spritetype * pDude = & sprite [ rxBucket [ i ] . index ] ; XSPRITE * pXDude = & xsprite [ pDude - > extra ] ;
if ( ! IsDudeSprite ( pDude ) | | pXDude - > aiState - > stateType ! = kAiStateGenIdle ) continue ;
aiInitSprite ( pDude ) ;
}
}
// this function sets target to -1 for all dudes that hunting for nSprite
void aiFightFreeTargets ( int nSprite ) {
for ( int nTarget = headspritestat [ kStatDude ] ; nTarget > = 0 ; nTarget = nextspritestat [ nTarget ] ) {
if ( ! IsDudeSprite ( & sprite [ nTarget ] ) | | sprite [ nTarget ] . extra < 0 ) continue ;
else if ( xsprite [ sprite [ nTarget ] . extra ] . target = = nSprite )
aiSetTarget ( & xsprite [ sprite [ nTarget ] . extra ] , sprite [ nTarget ] . x , sprite [ nTarget ] . y , sprite [ nTarget ] . z ) ;
}
return ;
}
// this function sets target to -1 for all targets that hunting for dudes affected by selected kModernDudeTargetChanger
void aiFightFreeAllTargets ( XSPRITE * pXSource ) {
if ( pXSource - > txID < = 0 ) return ;
for ( int i = bucketHead [ pXSource - > txID ] ; i < bucketHead [ pXSource - > txID + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type = = OBJ_SPRITE & & sprite [ rxBucket [ i ] . index ] . extra > = 0 )
aiFightFreeTargets ( rxBucket [ i ] . index ) ;
}
return ;
}
2020-03-30 19:54:28 +00:00
2020-02-07 19:47:43 +00:00
bool aiFightDudeIsAffected ( XSPRITE * pXDude ) {
if ( pXDude - > rxID < = 0 | | pXDude - > locked = = 1 ) return false ;
for ( int nSprite = headspritestat [ kStatModernDudeTargetChanger ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
XSPRITE * pXSprite = ( sprite [ nSprite ] . extra > = 0 ) ? & xsprite [ sprite [ nSprite ] . extra ] : NULL ;
if ( pXSprite = = NULL | | pXSprite - > txID < = 0 | | pXSprite - > state ! = 1 ) continue ;
for ( int i = bucketHead [ pXSprite - > txID ] ; i < bucketHead [ pXSprite - > txID + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
spritetype * pSprite = & sprite [ rxBucket [ i ] . index ] ;
if ( pSprite - > extra < 0 | | ! IsDudeSprite ( pSprite ) ) continue ;
else if ( pSprite - > index = = sprite [ pXDude - > reference ] . index ) return true ;
}
}
return false ;
}
2020-03-01 20:36:28 +00:00
bool aiFightIsMateOf ( XSPRITE * pXDude , XSPRITE * pXSprite ) {
return ( pXDude - > rxID = = pXSprite - > rxID ) ;
}
2020-02-07 19:47:43 +00:00
// this function tells if there any dude found for kModernDudeTargetChanger
bool aiFightGetDudesForBattle ( XSPRITE * pXSprite ) {
2020-03-01 20:36:28 +00:00
2020-02-07 19:47:43 +00:00
for ( int i = bucketHead [ pXSprite - > txID ] ; i < bucketHead [ pXSprite - > txID + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
else if ( IsDudeSprite ( & sprite [ rxBucket [ i ] . index ] ) & &
xsprite [ sprite [ rxBucket [ i ] . index ] . extra ] . health > 0 ) return true ;
}
2020-04-07 20:30:00 +00:00
// check redirected TX buckets
int rx = - 1 ; XSPRITE * pXRedir = NULL ;
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , sprite [ pXSprite - > reference ] . extra , pXRedir , & rx ) ) ! = NULL ) {
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
else if ( IsDudeSprite ( & sprite [ rxBucket [ i ] . index ] ) & &
xsprite [ sprite [ rxBucket [ i ] . index ] . extra ] . health > 0 ) return true ;
2020-03-01 20:36:28 +00:00
}
}
2020-02-07 19:47:43 +00:00
return false ;
}
void aiFightAlarmDudesInSight ( spritetype * pSprite , int max ) {
spritetype * pDude = NULL ; XSPRITE * pXDude = NULL ;
XSPRITE * pXSprite = & xsprite [ pSprite - > extra ] ;
2020-02-08 20:13:29 +00:00
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ;
2020-02-07 19:47:43 +00:00
for ( int nSprite = headspritestat [ kStatDude ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
pDude = & sprite [ nSprite ] ;
if ( pDude - > index = = pSprite - > index | | ! IsDudeSprite ( pDude ) | | pDude - > extra < 0 )
continue ;
pXDude = & xsprite [ pDude - > extra ] ;
if ( aiFightDudeCanSeeTarget ( pXSprite , pDudeInfo , pDude ) ) {
if ( pXDude - > target ! = - 1 | | pXDude - > rxID > 0 )
continue ;
aiSetTarget ( pXDude , pDude - > x , pDude - > y , pDude - > z ) ;
aiActivateDude ( pDude , pXDude ) ;
if ( max - - < 1 )
break ;
}
}
}
bool aiFightIsAnnoyingUnit ( spritetype * pDude ) {
2020-03-01 20:36:28 +00:00
return ( IsDudeSprite ( pDude ) & & gDudeInfoExtra [ pDude - > type - kDudeBase ] . annoying ) ;
2020-02-07 19:47:43 +00:00
}
bool aiFightUnitCanFly ( spritetype * pDude ) {
2020-03-01 20:36:28 +00:00
return ( IsDudeSprite ( pDude ) & & gDudeInfoExtra [ pDude - > type - kDudeBase ] . flying ) ;
2020-02-07 19:47:43 +00:00
}
bool aiFightIsMeleeUnit ( spritetype * pDude ) {
2020-03-01 20:36:28 +00:00
if ( pDude - > type = = kDudeModernCustom ) return ( pDude - > extra > = 0 & & dudeIsMelee ( & xsprite [ pDude - > extra ] ) ) ;
else return ( IsDudeSprite ( pDude ) & & gDudeInfoExtra [ pDude - > type - kDudeBase ] . melee ) ;
2020-02-07 19:47:43 +00:00
}
int aiFightGetTargetDist ( spritetype * pSprite , DUDEINFO * pDudeInfo , spritetype * pTarget ) {
int x = pTarget - > x ; int y = pTarget - > y ;
int dx = x - pSprite - > x ; int dy = y - pSprite - > y ;
int dist = approxDist ( dx , dy ) ;
if ( dist < = pDudeInfo - > meleeDist ) return 0 ;
if ( dist > = pDudeInfo - > seeDist ) return 13 ;
if ( dist < = pDudeInfo - > seeDist / 12 ) return 1 ;
if ( dist < = pDudeInfo - > seeDist / 11 ) return 2 ;
if ( dist < = pDudeInfo - > seeDist / 10 ) return 3 ;
if ( dist < = pDudeInfo - > seeDist / 9 ) return 4 ;
if ( dist < = pDudeInfo - > seeDist / 8 ) return 5 ;
if ( dist < = pDudeInfo - > seeDist / 7 ) return 6 ;
if ( dist < = pDudeInfo - > seeDist / 6 ) return 7 ;
if ( dist < = pDudeInfo - > seeDist / 5 ) return 8 ;
if ( dist < = pDudeInfo - > seeDist / 4 ) return 9 ;
if ( dist < = pDudeInfo - > seeDist / 3 ) return 10 ;
if ( dist < = pDudeInfo - > seeDist / 2 ) return 11 ;
return 12 ;
}
int aiFightGetFineTargetDist ( spritetype * pSprite , spritetype * pTarget ) {
int x = pTarget - > x ; int y = pTarget - > y ;
int dx = x - pSprite - > x ; int dy = y - pSprite - > y ;
int dist = approxDist ( dx , dy ) ;
return dist ;
}
bool modernTypeOperateSprite ( int nSprite , spritetype * pSprite , XSPRITE * pXSprite , EVENT event ) {
2020-03-30 19:54:28 +00:00
if ( event . cmd > = kCmdLock & & event . cmd < = kCmdToggleLock ) {
switch ( event . cmd ) {
case kCmdLock :
pXSprite - > locked = 1 ;
2020-04-01 20:19:50 +00:00
break ;
2020-03-30 19:54:28 +00:00
case kCmdUnlock :
pXSprite - > locked = 0 ;
break ;
case kCmdToggleLock :
pXSprite - > locked = pXSprite - > locked ^ 1 ;
break ;
}
2020-04-01 20:19:50 +00:00
switch ( pSprite - > type ) {
case kModernCondition :
if ( ! pXSprite - > locked ) {
2020-05-05 18:50:14 +00:00
if ( pXSprite - > busyTime > 0 )
evPost ( pSprite - > index , OBJ_SPRITE , 0 , kCallbackCondition ) ;
2020-04-01 20:19:50 +00:00
} else {
pXSprite - > state = 0 ;
evKill ( pSprite - > index , OBJ_SPRITE ) ;
}
2020-03-30 19:54:28 +00:00
break ;
}
2020-04-01 20:19:50 +00:00
2020-03-30 19:54:28 +00:00
return true ;
}
2020-03-01 20:36:28 +00:00
if ( pSprite - > statnum = = kStatDude & & IsDudeSprite ( pSprite ) ) {
switch ( event . cmd ) {
case kCmdOff :
if ( pXSprite - > state ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
if ( ! pXSprite - > state ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
2020-03-19 19:35:31 +00:00
if ( IsPlayerSprite ( pSprite ) | | pXSprite - > health < = 0 ) break ;
2020-03-01 20:36:28 +00:00
switch ( pXSprite - > aiState - > stateType ) {
case kAiStateIdle :
case kAiStateGenIdle :
aiActivateDude ( pSprite , pXSprite ) ;
break ;
}
break ;
default :
if ( ! pXSprite - > state ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
return true ;
}
2020-02-07 19:47:43 +00:00
switch ( pSprite - > type ) {
default :
return false ; // no modern type found to work with, go normal OperateSprite();
2020-03-13 20:59:13 +00:00
case kModernCondition : // WIP
2020-05-05 18:50:14 +00:00
if ( ! pXSprite - > isTriggered ) useCondition ( pSprite , pXSprite , event ) ;
2020-02-07 19:47:43 +00:00
return true ;
// add spawn random dude feature - works only if at least 2 data fields are not empty.
case kMarkerDudeSpawn :
if ( gGameOptions . nMonsterSettings & & pXSprite - > data1 > = kDudeBase & & pXSprite - > data1 < kDudeVanillaMax ) {
spritetype * pSpawn = NULL ;
if ( ( pSpawn = randomSpawnDude ( pSprite ) ) = = NULL )
return false ; // go normal OperateSprite();
XSPRITE * pXSpawn = & xsprite [ pSpawn - > extra ] ;
gKillMgr . sub_263E0 ( 1 ) ;
switch ( pXSprite - > data1 ) {
case kDudeBurningInnocent :
case kDudeBurningCultist :
case kDudeBurningZombieButcher :
case kDudeBurningTinyCaleb :
case kDudeBurningBeast :
2020-02-08 20:13:29 +00:00
pXSpawn - > health = getDudeInfo ( pXSprite - > data1 ) - > startHealth < < 4 ;
2020-02-07 19:47:43 +00:00
pXSpawn - > burnTime = 10 ;
pXSpawn - > target = - 1 ;
aiActivateDude ( pSpawn , pXSpawn ) ;
break ;
default :
if ( pSprite - > flags & kModernTypeFlag3 ) aiActivateDude ( pSpawn , pXSpawn ) ;
break ;
}
}
return true ;
2020-03-01 20:36:28 +00:00
case kModernRandomTX : // random Event Switch takes random data field and uses it as TX ID
case kModernSequentialTX : // sequential Switch takes values from data fields starting from data1 and uses it as TX ID
2020-05-05 18:50:14 +00:00
if ( pXSprite - > command = = kCmdLink ) return true ; // work as event redirector
2020-03-01 20:36:28 +00:00
switch ( pSprite - > type ) {
case kModernRandomTX :
2020-05-05 18:50:14 +00:00
useRandomTx ( pXSprite , ( COMMAND_ID ) event . cmd , true ) ;
2020-03-01 20:36:28 +00:00
break ;
case kModernSequentialTX :
2020-05-05 18:50:14 +00:00
if ( ! ( pSprite - > flags & kModernTypeFlag1 ) ) useSequentialTx ( pXSprite , ( COMMAND_ID ) event . cmd , true ) ;
else seqTxSendCmdAll ( pXSprite , pSprite - > index , ( COMMAND_ID ) event . cmd , false ) ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
return true ;
2020-02-07 19:47:43 +00:00
case kMarkerWarpDest :
case kModernSpriteDamager :
if ( pXSprite - > txID < = 0 ) {
PLAYER * pPlayer = getPlayerById ( pXSprite - > data1 ) ;
if ( pPlayer ! = NULL & & SetSpriteState ( nSprite , pXSprite , pXSprite - > state ^ 1 ) = = 1 & & pXSprite - > data1 > 0 ) {
switch ( pSprite - > type ) {
case kMarkerWarpDest :
useTeleportTarget ( pXSprite , pPlayer - > pSprite ) ;
break ;
case kModernSpriteDamager :
useSpriteDamager ( pXSprite , pPlayer - > pSprite ) ;
break ;
}
}
return true ;
}
2020-03-01 20:36:28 +00:00
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case kModernObjPropertiesChanger :
if ( pXSprite - > txID < = 0 ) {
if ( SetSpriteState ( nSprite , pXSprite , pXSprite - > state ^ 1 ) = = 1 )
usePropertiesChanger ( pXSprite , - 1 , - 1 ) ;
return true ;
}
2020-03-01 20:36:28 +00:00
fallthrough__ ;
2020-02-07 19:47:43 +00:00
case kModernObjSizeChanger :
case kModernObjPicnumChanger :
case kModernSectorFXChanger :
case kModernObjDataChanger :
modernTypeSetSpriteState ( nSprite , pXSprite , pXSprite - > state ^ 1 ) ;
return true ;
case kModernCustomDudeSpawn :
if ( gGameOptions . nMonsterSettings & & genDudeSpawn ( pSprite , - 1 ) ! = NULL ) gKillMgr . sub_263E0 ( 1 ) ;
return true ;
case kModernSeqSpawner :
case kModernEffectSpawner :
switch ( event . cmd ) {
case kCmdOff :
if ( pXSprite - > state = = 1 ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
evKill ( nSprite , 3 ) ; // queue overflow protect
if ( pXSprite - > state = = 0 ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
2020-05-05 18:50:14 +00:00
if ( pSprite - > type = = kModernSeqSpawner ) seqSpawnerOffSameTx ( pXSprite ) ;
2020-02-07 19:47:43 +00:00
fallthrough__ ;
case kCmdRepeat :
if ( pXSprite - > txID > 0 ) modernTypeSendCommand ( nSprite , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
else if ( pSprite - > type = = kModernSeqSpawner ) useSeqSpawnerGen ( pXSprite , 3 , pSprite - > index ) ;
else useEffectGen ( pXSprite , NULL ) ;
if ( pXSprite - > busyTime > 0 )
evPost ( nSprite , 3 , ClipLow ( ( int ( pXSprite - > busyTime ) + Random2 ( pXSprite - > data1 ) ) * 120 / 10 , 0 ) , kCmdRepeat ) ;
break ;
default :
if ( pXSprite - > state = = 0 ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
return true ;
case kModernWindGenerator :
switch ( event . cmd ) {
case kCmdOff :
windGenStopWindOnSectors ( pXSprite ) ;
if ( pXSprite - > state = = 1 ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
evKill ( nSprite , 3 ) ; // queue overflow protect
if ( pXSprite - > state = = 0 ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
fallthrough__ ;
case kCmdRepeat :
if ( pXSprite - > txID > 0 ) modernTypeSendCommand ( nSprite , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
else useSectorWindGen ( pXSprite , NULL ) ;
if ( pXSprite - > busyTime > 0 ) evPost ( nSprite , 3 , pXSprite - > busyTime , kCmdRepeat ) ;
break ;
default :
if ( pXSprite - > state = = 0 ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
return true ;
case kModernDudeTargetChanger :
// this one is required if data4 of generator was dynamically changed
// it turns monsters in normal idle state instead of genIdle, so they not ignore the world.
if ( pXSprite - > dropMsg = = 3 & & 3 ! = pXSprite - > data4 )
aiFightActivateDudes ( pXSprite - > txID ) ;
switch ( event . cmd ) {
case kCmdOff :
if ( pXSprite - > data4 = = 3 ) aiFightActivateDudes ( pXSprite - > txID ) ;
if ( pXSprite - > state = = 1 ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
evKill ( nSprite , 3 ) ; // queue overflow protect
if ( pXSprite - > state = = 0 ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
fallthrough__ ;
case kCmdRepeat :
if ( pXSprite - > txID < = 0 | | ! aiFightGetDudesForBattle ( pXSprite ) ) {
aiFightFreeAllTargets ( pXSprite ) ;
evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
} else {
modernTypeSendCommand ( nSprite , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
}
if ( pXSprite - > busyTime > 0 ) evPost ( nSprite , 3 , pXSprite - > busyTime , kCmdRepeat ) ;
break ;
default :
if ( pXSprite - > state = = 0 ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
pXSprite - > dropMsg = pXSprite - > data4 ;
return true ;
case kModernObjDataAccumulator :
switch ( event . cmd ) {
case kCmdOff :
if ( pXSprite - > state = = 1 ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
evKill ( nSprite , 3 ) ; // queue overflow protect
if ( pXSprite - > state = = 0 ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
fallthrough__ ;
case kCmdRepeat :
// force OFF after *all* TX objects reach the goal value
if ( pSprite - > flags = = kModernTypeFlag0 & & incDecGoalValueIsReached ( pXSprite ) ) {
evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
2020-04-07 20:30:00 +00:00
modernTypeSendCommand ( nSprite , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
if ( pXSprite - > busyTime > 0 ) evPost ( nSprite , 3 , pXSprite - > busyTime , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
if ( pXSprite - > state = = 0 ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
return true ;
case kModernRandom :
case kModernRandom2 :
switch ( event . cmd ) {
case kCmdOff :
if ( pXSprite - > state = = 1 ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
evKill ( nSprite , 3 ) ; // queue overflow protect
if ( pXSprite - > state = = 0 ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
fallthrough__ ;
case kCmdRepeat :
useRandomItemGen ( pSprite , pXSprite ) ;
if ( pXSprite - > busyTime > 0 )
evPost ( nSprite , 3 , ( 120 * pXSprite - > busyTime ) / 10 , kCmdRepeat ) ;
break ;
default :
if ( pXSprite - > state = = 0 ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
return true ;
case kModernThingTNTProx :
if ( pSprite - > statnum ! = kStatRespawn ) {
switch ( event . cmd ) {
case kCmdSpriteProximity :
if ( pXSprite - > state ) break ;
sfxPlay3DSound ( pSprite , 452 , 0 , 0 ) ;
evPost ( nSprite , 3 , 30 , kCmdOff ) ;
pXSprite - > state = 1 ;
fallthrough__ ;
case kCmdOn :
sfxPlay3DSound ( pSprite , 451 , 0 , 0 ) ;
pXSprite - > Proximity = 1 ;
break ;
default :
actExplodeSprite ( pSprite ) ;
break ;
}
}
return true ;
case kModernThingEnemyLifeLeech :
dudeLeechOperate ( pSprite , pXSprite , event ) ;
return true ;
case kModernPlayerControl : { // WIP
2020-05-05 18:50:14 +00:00
PLAYER * pPlayer = NULL ; int cmd = ( event . cmd > = kCmdNumberic ) ? event . cmd : pXSprite - > command ;
if ( ( pPlayer = getPlayerById ( pXSprite - > data1 ) ) = = NULL
| | ( ( cmd < 3 | | cmd > 4 ) & & ! modernTypeSetSpriteState ( nSprite , pXSprite , pXSprite - > state ^ 1 ) ) )
return true ;
2020-02-07 19:47:43 +00:00
TRPLAYERCTRL * pCtrl = & gPlayerCtrl [ pPlayer - > nPlayer ] ;
2020-05-05 18:50:14 +00:00
/// !!! COMMANDS OF THE CURRENT SPRITE, NOT OF THE EVENT !!! ///
if ( ( cmd - = kCmdNumberic ) < 0 ) return true ;
else if ( pPlayer - > pXSprite - > health < = 0 ) {
switch ( cmd ) {
case 36 :
actHealDude ( pPlayer - > pXSprite , ( ( pXSprite - > data2 > 0 ) ? ClipHigh ( pXSprite - > data2 , 200 ) : getDudeInfo ( pPlayer - > pSprite - > type ) - > startHealth ) , 200 ) ;
pPlayer - > curWeapon = 1 ;
break ;
2020-02-07 19:47:43 +00:00
}
2020-05-05 18:50:14 +00:00
2020-02-07 19:47:43 +00:00
return true ;
2020-05-05 18:50:14 +00:00
2020-02-07 19:47:43 +00:00
}
2020-05-05 18:50:14 +00:00
switch ( cmd ) {
case 0 : // 64 (player life form)
if ( pXSprite - > data2 < kModeHuman | | pXSprite - > data2 > kModeHumanGrown ) break ;
2020-02-07 19:47:43 +00:00
else trPlayerCtrlSetRace ( pXSprite , pPlayer ) ;
break ;
2020-05-05 18:50:14 +00:00
case 1 : // 65 (move speed and jump height)
2020-02-07 19:47:43 +00:00
// player movement speed (for all races and postures)
if ( valueIsBetween ( pXSprite - > data2 , - 1 , 32767 ) )
trPlayerCtrlSetMoveSpeed ( pXSprite , pPlayer ) ;
// player jump height (for all races and stand posture only)
if ( valueIsBetween ( pXSprite - > data3 , - 1 , 32767 ) )
trPlayerCtrlSetJumpHeight ( pXSprite , pPlayer ) ;
break ;
2020-05-05 18:50:14 +00:00
case 2 : // 66 (player screen effects)
2020-02-07 19:47:43 +00:00
if ( pXSprite - > data3 < 0 ) break ;
else trPlayerCtrlSetScreenEffect ( pXSprite , pPlayer ) ;
break ;
2020-05-05 18:50:14 +00:00
case 3 : // 67 (start playing qav scene)
2020-02-07 19:47:43 +00:00
if ( pCtrl - > qavScene . index = = nSprite & & ! pXSprite - > Interrutable ) break ;
else trPlayerCtrlStartScene ( pXSprite , pPlayer ) ;
break ;
2020-05-05 18:50:14 +00:00
case 4 : // 68 (stop playing qav scene)
trPlayerCtrlStopScene ( pPlayer ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 5 : // 69 (set player look angle, TO-DO: if tx > 0, take a look on TX ID sprite)
2020-02-07 19:47:43 +00:00
//data4 is reserved
if ( pXSprite - > data4 ! = 0 ) break ;
2020-05-05 18:50:14 +00:00
else if ( valueIsBetween ( pXSprite - > data2 , - 128 , 128 ) )
2020-02-07 19:47:43 +00:00
trPlayerCtrlSetLookAngle ( pXSprite , pPlayer ) ;
break ;
2020-05-05 18:50:14 +00:00
case 6 : // 70 (erase player stuff...)
2020-02-07 19:47:43 +00:00
if ( pXSprite - > data2 < 0 ) break ;
else trPlayerCtrlEraseStuff ( pXSprite , pPlayer ) ;
break ;
2020-05-05 18:50:14 +00:00
case 7 : // 71 (give something to player...)
2020-02-07 19:47:43 +00:00
if ( pXSprite - > data2 < = 0 ) break ;
else trPlayerCtrlGiveStuff ( pXSprite , pPlayer , pCtrl ) ;
break ;
2020-05-05 18:50:14 +00:00
case 8 : // 72 (use inventory item)
2020-02-07 19:47:43 +00:00
if ( pXSprite - > data2 < 1 | | pXSprite - > data2 > 5 ) break ;
2020-05-05 18:50:14 +00:00
else trPlayerCtrlUsePackItem ( pXSprite , pPlayer , event . cmd ) ;
break ;
case 9 : // 73 (set player's sprite angle, TO-DO: if tx > 0, take a look on TX ID sprite)
//data4 is reserved
if ( pXSprite - > data4 ! = 0 ) break ;
else if ( pSprite - > flags & kModernTypeFlag1 ) pPlayer - > q16ang = fix16_from_int ( pSprite - > ang ) ;
else if ( valueIsBetween ( pXSprite - > data2 , - kAng360 , kAng360 ) ) pPlayer - > q16ang = fix16_from_int ( pXSprite - > data2 ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
return true ;
case kGenModernMissileUniversal :
case kGenModernSound :
switch ( event . cmd ) {
case kCmdOff :
if ( pXSprite - > state = = 1 ) SetSpriteState ( nSprite , pXSprite , 0 ) ;
break ;
case kCmdOn :
evKill ( nSprite , 3 ) ; // queue overflow protect
if ( pXSprite - > state = = 0 ) SetSpriteState ( nSprite , pXSprite , 1 ) ;
fallthrough__ ;
case kCmdRepeat :
switch ( pSprite - > type ) {
case kGenModernMissileUniversal :
useUniMissileGen ( 3 , pSprite - > extra ) ;
break ;
case kGenModernSound :
useSoundGen ( pSprite , pXSprite ) ;
break ;
}
if ( pXSprite - > txID ) evSend ( nSprite , 3 , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
if ( pXSprite - > busyTime > 0 ) evPost ( nSprite , 3 , ( 120 * pXSprite - > busyTime ) / 10 , kCmdRepeat ) ;
break ;
default :
if ( pXSprite - > state = = 0 ) evPost ( nSprite , 3 , 0 , kCmdOn ) ;
else evPost ( nSprite , 3 , 0 , kCmdOff ) ;
break ;
}
return true ;
}
}
bool modernTypeOperateWall ( int nWall , walltype * pWall , XWALL * pXWall , EVENT event ) {
switch ( pWall - > type ) {
case kSwitchOneWay :
switch ( event . cmd ) {
case kCmdOff :
SetWallState ( nWall , pXWall , 0 ) ;
break ;
case kCmdOn :
SetWallState ( nWall , pXWall , 1 ) ;
break ;
default :
SetWallState ( nWall , pXWall , pXWall - > restState ^ 1 ) ;
break ;
}
return true ;
default :
return false ; // no modern type found to work with, go normal OperateWall();
}
}
2020-03-01 20:36:28 +00:00
bool txIsRanged ( XSPRITE * pXSource ) {
if ( pXSource - > data1 > 0 & & pXSource - > data2 < = 0 & & pXSource - > data3 < = 0 & & pXSource - > data4 > 0 ) {
if ( pXSource - > data1 > pXSource - > data4 ) {
// data1 must be less than data4
int tmp = pXSource - > data1 ; pXSource - > data1 = pXSource - > data4 ;
pXSource - > data4 = tmp ;
}
return true ;
}
return false ;
}
void seqTxSendCmdAll ( XSPRITE * pXSource , int nIndex , COMMAND_ID cmd , bool modernSend ) {
bool ranged = txIsRanged ( pXSource ) ;
if ( ranged ) {
for ( pXSource - > txID = pXSource - > data1 ; pXSource - > txID < = pXSource - > data4 ; pXSource - > txID + + ) {
if ( pXSource - > txID < 0 | | pXSource - > txID > = kChannelUserMax ) continue ;
else if ( ! modernSend ) evSend ( nIndex , 3 , pXSource - > txID , cmd ) ;
else modernTypeSendCommand ( nIndex , pXSource - > txID , cmd ) ;
}
} else {
for ( int i = 0 ; i < = 3 ; i + + ) {
pXSource - > txID = GetDataVal ( & sprite [ pXSource - > reference ] , i ) ;
if ( pXSource - > txID < 0 | | pXSource - > txID > = kChannelUserMax ) continue ;
else if ( ! modernSend ) evSend ( nIndex , 3 , pXSource - > txID , cmd ) ;
else modernTypeSendCommand ( nIndex , pXSource - > txID , cmd ) ;
}
}
pXSource - > txID = pXSource - > sysData1 = 0 ;
return ;
}
void useRandomTx ( XSPRITE * pXSource , COMMAND_ID cmd , bool setState ) {
2020-03-13 20:59:13 +00:00
UNREFERENCED_PARAMETER ( cmd ) ;
2020-03-01 20:36:28 +00:00
spritetype * pSource = & sprite [ pXSource - > reference ] ;
int tx = 0 ; int maxRetries = kMaxRandomizeRetries ;
if ( txIsRanged ( pXSource ) ) {
while ( maxRetries - - > = 0 ) {
if ( ( tx = nnExtRandom ( pXSource - > data1 , pXSource - > data4 ) ) ! = pXSource - > txID )
break ;
}
} else {
while ( maxRetries - - > = 0 ) {
if ( ( tx = randomGetDataValue ( pXSource , kRandomizeTX ) ) > 0 & & tx ! = pXSource - > txID )
break ;
}
}
pXSource - > txID = ( tx > 0 & & tx < kChannelUserMax ) ? tx : 0 ;
if ( setState )
SetSpriteState ( pSource - > index , pXSource , pXSource - > state ^ 1 ) ;
//evSend(pSource->index, OBJ_SPRITE, pXSource->txID, (COMMAND_ID)pXSource->command);
}
void useSequentialTx ( XSPRITE * pXSource , COMMAND_ID cmd , bool setState ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
bool range = txIsRanged ( pXSource ) ; int cnt = 3 ; int tx = 0 ;
if ( range ) {
// make sure sysData is correct as we store current index of TX ID here.
if ( pXSource - > sysData1 < pXSource - > data1 ) pXSource - > sysData1 = pXSource - > data1 ;
else if ( pXSource - > sysData1 > pXSource - > data4 ) pXSource - > sysData1 = pXSource - > data4 ;
} else {
// make sure sysData is correct as we store current index of data field here.
if ( pXSource - > sysData1 > 3 ) pXSource - > sysData1 = 0 ;
else if ( pXSource - > sysData1 < 0 ) pXSource - > sysData1 = 3 ;
}
switch ( cmd ) {
case kCmdOff :
if ( ! range ) {
while ( cnt - - > = 0 ) { // skip empty data fields
if ( pXSource - > sysData1 - - < 0 ) pXSource - > sysData1 = 3 ;
if ( ( tx = GetDataVal ( pSource , pXSource - > sysData1 ) ) < = 0 ) continue ;
else break ;
}
} else {
if ( - - pXSource - > sysData1 < pXSource - > data1 ) pXSource - > sysData1 = pXSource - > data4 ;
tx = pXSource - > sysData1 ;
}
break ;
default :
if ( ! range ) {
while ( cnt - - > = 0 ) { // skip empty data fields
if ( pXSource - > sysData1 > 3 ) pXSource - > sysData1 = 0 ;
if ( ( tx = GetDataVal ( pSource , pXSource - > sysData1 + + ) ) < = 0 ) continue ;
else break ;
}
} else {
tx = pXSource - > sysData1 ;
if ( pXSource - > sysData1 > = pXSource - > data4 ) {
pXSource - > sysData1 = pXSource - > data1 ;
break ;
}
pXSource - > sysData1 + + ;
}
break ;
}
pXSource - > txID = ( tx > 0 & & tx < kChannelUserMax ) ? tx : 0 ;
if ( setState )
SetSpriteState ( pSource - > index , pXSource , pXSource - > state ^ 1 ) ;
//evSend(pSource->index, OBJ_SPRITE, pXSource->txID, (COMMAND_ID)pXSource->command);
}
2020-05-05 18:50:14 +00:00
int useCondition ( spritetype * pSource , XSPRITE * pXSource , EVENT event ) {
2020-03-13 20:59:13 +00:00
int objType = event . type ; int objIndex = event . index ;
2020-03-30 19:54:28 +00:00
bool srcIsCondition = ( objType = = OBJ_SPRITE & & sprite [ objIndex ] . type = = kModernCondition & & objIndex ! = pSource - > index ) ;
2020-03-13 20:59:13 +00:00
// if it's a tracking condition, it must ignore all the commands sent from objects
2020-05-05 18:50:14 +00:00
if ( pXSource - > busyTime > 0 & & event . funcID ! = kCallbackCondition ) return - 1 ;
else if ( ! srcIsCondition ) { // save object serials in the stack and make copy of initial object
2020-03-30 19:54:28 +00:00
pXSource - > targetX = pXSource - > targetY = condSerialize ( objType , objIndex ) ;
2020-03-13 20:59:13 +00:00
2020-05-05 18:50:14 +00:00
} else { // or grab serials of objects from previous conditions
pXSource - > targetX = xsprite [ sprite [ objIndex ] . extra ] . targetX ;
pXSource - > targetY = xsprite [ sprite [ objIndex ] . extra ] . targetY ;
2020-03-13 20:59:13 +00:00
}
2020-05-05 18:50:14 +00:00
int cond = pXSource - > data1 ; bool ok = false ; bool RVRS = ( pSource - > yvel = = kModernConditionFalse ) ;
bool RSET = ( pXSource - > command = = kCmdNumberic + 36 ) ;
2020-03-13 20:59:13 +00:00
2020-05-05 18:50:14 +00:00
if ( pXSource - > restState = = 0 ) {
2020-04-09 18:51:25 +00:00
2020-05-05 18:50:14 +00:00
bool PUSH = ( pXSource - > command = = kCmdNumberic ) ;
int comOp = pSource - > cstat ; // comparison operator
if ( cond = = 0 ) ok = true ; // dummy
else if ( cond > = kCondMixedBase & & cond < kCondMixedMax ) ok = condCheckMixed ( pXSource , event , comOp , PUSH ) ;
else if ( cond > = kCondWallBase & & cond < kCondWallMax ) ok = condCheckWall ( pXSource , comOp , PUSH ) ;
else if ( cond > = kCondSectorBase & & cond < kCondSectorMax ) ok = condCheckSector ( pXSource , comOp , PUSH ) ;
else if ( cond > = kCondPlayerBase & & cond < kCondPlayerMax ) ok = condCheckPlayer ( pXSource , comOp , PUSH ) ;
else if ( cond > = kCondDudeBase & & cond < kCondDudeMax ) ok = condCheckDude ( pXSource , comOp , PUSH ) ;
else if ( cond > = kCondSpriteBase & & cond < kCondSpriteMax ) ok = condCheckSprite ( pXSource , comOp , PUSH ) ;
else condError ( pXSource , " Unexpected condition id %d! " , cond ) ;
pXSource - > state = ( ok ^ RVRS ) ;
if ( pXSource - > waitTime > 0 & & pXSource - > state > 0 ) {
2020-04-09 18:51:25 +00:00
2020-05-05 18:50:14 +00:00
pXSource - > restState = 1 ;
evKill ( pSource - > index , OBJ_SPRITE ) ;
evPost ( pSource - > index , OBJ_SPRITE , ( pXSource - > waitTime * 120 ) / 10 , kCmdRepeat ) ;
return - 1 ;
2020-03-13 20:59:13 +00:00
}
2020-05-05 18:50:14 +00:00
} else if ( event . cmd = = kCmdRepeat ) {
2020-03-13 20:59:13 +00:00
2020-05-05 18:50:14 +00:00
pXSource - > restState = 0 ;
2020-03-13 20:59:13 +00:00
2020-05-05 18:50:14 +00:00
} else {
2020-03-19 19:35:31 +00:00
2020-05-05 18:50:14 +00:00
return - 1 ;
}
if ( pXSource - > state ) {
2020-03-30 19:54:28 +00:00
pXSource - > isTriggered = ( pXSource - > triggerOnce ) ? true : false ;
2020-05-05 18:50:14 +00:00
if ( RSET ) condRestore ( pXSource ) ; // reset focus to the initial object
2020-03-30 19:54:28 +00:00
// send command to rx bucket
if ( pXSource - > txID )
evSend ( pSource - > index , OBJ_SPRITE , pXSource - > txID , ( COMMAND_ID ) pXSource - > command ) ;
if ( pSource - > hitag ) {
2020-05-05 18:50:14 +00:00
// send it for object currently in the focus
2020-03-30 19:54:28 +00:00
if ( pSource - > hitag & kModernTypeFlag1 )
nnExtTriggerObject ( objType , objIndex , pXSource - > command ) ;
// send it for initial object
if ( ( pSource - > hitag & kModernTypeFlag2 ) & & ( pXSource - > targetX ! = pXSource - > targetY | | ! ( pSource - > hitag & kModernTypeFlag1 ) ) ) {
condUnserialize ( pXSource - > targetY , & objType , & objIndex ) ;
nnExtTriggerObject ( objType , objIndex , pXSource - > command ) ;
}
2020-03-13 20:59:13 +00:00
}
2020-05-05 18:50:14 +00:00
}
return pXSource - > state ;
2020-03-13 20:59:13 +00:00
}
2020-02-07 19:47:43 +00:00
void useRandomItemGen ( spritetype * pSource , XSPRITE * pXSource ) {
// let's first search for previously dropped items and remove it
if ( pXSource - > dropMsg > 0 ) {
for ( short nItem = headspritestat [ kStatItem ] ; nItem > = 0 ; nItem = nextspritestat [ nItem ] ) {
spritetype * pItem = & sprite [ nItem ] ;
if ( ( unsigned int ) pItem - > type = = pXSource - > dropMsg & & pItem - > x = = pSource - > x & & pItem - > y = = pSource - > y & & pItem - > z = = pSource - > z ) {
gFX . fxSpawn ( ( FX_ID ) 29 , pSource - > sectnum , pSource - > x , pSource - > y , pSource - > z , 0 ) ;
2020-03-01 20:36:28 +00:00
pItem - > type = kSpriteDecoration ;
actPostSprite ( nItem , kStatFree ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
}
// then drop item
spritetype * pDrop = randomDropPickupObject ( pSource , pXSource - > dropMsg ) ;
// check if generator affected by physics
if ( pDrop ! = NULL & & debrisGetIndex ( pSource - > index ) ! = - 1 & & ( pDrop - > extra > = 0 | | dbInsertXSprite ( pDrop - > index ) > 0 ) ) {
int nIndex = debrisGetFreeIndex ( ) ;
if ( nIndex > = 0 ) {
xsprite [ pDrop - > extra ] . physAttr | = kPhysMove | kPhysGravity | kPhysFalling ; // must fall always
pSource - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
gPhysSpritesList [ nIndex ] = pDrop - > index ;
if ( nIndex > = gPhysSpritesCount ) gPhysSpritesCount + + ;
getSpriteMassBySize ( pDrop ) ; // create mass cache
}
}
}
void useUniMissileGen ( int , int nXSprite ) {
XSPRITE * pXSprite = & xsprite [ nXSprite ] ; int dx = 0 , dy = 0 , dz = 0 ;
spritetype * pSprite = & sprite [ pXSprite - > reference ] ;
if ( pXSprite - > data1 < kMissileBase | | pXSprite - > data1 > = kMissileMax )
return ;
if ( pSprite - > cstat & 32 ) {
if ( pSprite - > cstat & 8 ) dz = 0x4000 ;
else dz = - 0x4000 ;
} else {
dx = Cos ( pSprite - > ang ) > > 16 ;
dy = Sin ( pSprite - > ang ) > > 16 ;
dz = pXSprite - > data3 < < 6 ; // add slope controlling
if ( dz > 0x10000 ) dz = 0x10000 ;
else if ( dz < - 0x10000 ) dz = - 0x10000 ;
}
spritetype * pMissile = NULL ;
pMissile = actFireMissile ( pSprite , 0 , 0 , dx , dy , dz , pXSprite - > data1 ) ;
if ( pMissile ! = NULL ) {
// inherit some properties of the generator
if ( pSprite - > flags & kModernTypeFlag1 ) {
pMissile - > xrepeat = pSprite - > xrepeat ;
pMissile - > yrepeat = pSprite - > yrepeat ;
pMissile - > pal = pSprite - > pal ;
pMissile - > shade = pSprite - > shade ;
}
// add velocity controlling
if ( pXSprite - > data2 > 0 ) {
int velocity = pXSprite - > data2 < < 12 ;
xvel [ pMissile - > index ] = mulscale ( velocity , dx , 14 ) ;
yvel [ pMissile - > index ] = mulscale ( velocity , dy , 14 ) ;
zvel [ pMissile - > index ] = mulscale ( velocity , dz , 14 ) ;
}
// add bursting for missiles
if ( pMissile - > type ! = kMissileFlareAlt & & pXSprite - > data4 > 0 )
2020-03-01 20:36:28 +00:00
evPost ( pMissile - > index , 3 , ClipHigh ( pXSprite - > data4 , 500 ) , kCallbackMissileBurst ) ;
2020-02-07 19:47:43 +00:00
}
}
void useSoundGen ( spritetype * pSource , XSPRITE * pXSource ) {
int pitch = pXSource - > data4 < < 1 ; if ( pitch < 2000 ) pitch = 0 ;
sfxPlay3DSoundCP ( pSource , pXSource - > data2 , - 1 , 0 , pitch , pXSource - > data3 ) ;
}
void useIncDecGen ( XSPRITE * pXSource , short objType , int objIndex ) {
2020-03-05 20:46:05 +00:00
char buffer [ 5 ] ; int data = - 65535 ; short tmp = 0 ; int dataIndex = 0 ;
sprintf ( buffer , " %d " , abs ( pXSource - > data1 ) ) ; int len = strlen ( buffer ) ;
2020-02-07 19:47:43 +00:00
2020-03-05 20:46:05 +00:00
for ( int i = 0 ; i < len ; i + + ) {
dataIndex = ( buffer [ i ] - 52 ) + 4 ;
if ( ( data = getDataFieldOfObject ( objType , objIndex , dataIndex ) ) = = - 65535 ) {
consoleSysMsg ( " \n Wrong index of data (%c) for IncDec Gen #%d! Only 1, 2, 3 and 4 indexes allowed! \n " , buffer [ i ] , objIndex ) ;
continue ;
}
spritetype * pSource = & sprite [ pXSource - > reference ] ;
2020-05-05 18:50:14 +00:00
2020-03-05 20:46:05 +00:00
if ( pXSource - > data2 < pXSource - > data3 ) {
2020-02-07 19:47:43 +00:00
2020-03-05 20:46:05 +00:00
data = ClipRange ( data , pXSource - > data2 , pXSource - > data3 ) ;
if ( ( data + = pXSource - > data4 ) > = pXSource - > data3 ) {
switch ( pSource - > flags ) {
2020-02-07 19:47:43 +00:00
case kModernTypeFlag0 :
case kModernTypeFlag1 :
if ( data > pXSource - > data3 ) data = pXSource - > data3 ;
break ;
2020-03-05 20:46:05 +00:00
case kModernTypeFlag2 :
2020-02-07 19:47:43 +00:00
if ( data > pXSource - > data3 ) data = pXSource - > data3 ;
if ( ! incDecGoalValueIsReached ( pXSource ) ) break ;
2020-03-05 20:46:05 +00:00
tmp = pXSource - > data3 ;
2020-02-07 19:47:43 +00:00
pXSource - > data3 = pXSource - > data2 ;
pXSource - > data2 = tmp ;
break ;
case kModernTypeFlag3 :
if ( data > pXSource - > data3 ) data = pXSource - > data2 ;
break ;
2020-03-05 20:46:05 +00:00
}
2020-02-07 19:47:43 +00:00
}
2020-03-05 20:46:05 +00:00
} else if ( pXSource - > data2 > pXSource - > data3 ) {
2020-02-07 19:47:43 +00:00
2020-03-05 20:46:05 +00:00
data = ClipRange ( data , pXSource - > data3 , pXSource - > data2 ) ;
if ( ( data - = pXSource - > data4 ) < = pXSource - > data3 ) {
switch ( pSource - > flags ) {
2020-02-07 19:47:43 +00:00
case kModernTypeFlag0 :
case kModernTypeFlag1 :
if ( data < pXSource - > data3 ) data = pXSource - > data3 ;
break ;
2020-03-05 20:46:05 +00:00
case kModernTypeFlag2 :
2020-02-07 19:47:43 +00:00
if ( data < pXSource - > data3 ) data = pXSource - > data3 ;
if ( ! incDecGoalValueIsReached ( pXSource ) ) break ;
2020-03-05 20:46:05 +00:00
tmp = pXSource - > data3 ;
2020-02-07 19:47:43 +00:00
pXSource - > data3 = pXSource - > data2 ;
pXSource - > data2 = tmp ;
break ;
case kModernTypeFlag3 :
if ( data < pXSource - > data3 ) data = pXSource - > data2 ;
break ;
2020-03-05 20:46:05 +00:00
}
2020-02-07 19:47:43 +00:00
}
}
2020-04-07 20:30:00 +00:00
pXSource - > sysData1 = data ;
2020-03-05 20:46:05 +00:00
setDataValueOfObject ( objType , objIndex , dataIndex , data ) ;
}
2020-02-07 19:47:43 +00:00
}
void useDataChanger ( XSPRITE * pXSource , int objType , int objIndex ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
switch ( objType ) {
case OBJ_SECTOR :
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data1 ! = - 1 & & pXSource - > data1 ! = 32767 ) )
setDataValueOfObject ( objType , objIndex , 1 , pXSource - > data1 ) ;
break ;
case OBJ_SPRITE :
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data1 ! = - 1 & & pXSource - > data1 ! = 32767 ) )
setDataValueOfObject ( objType , objIndex , 1 , pXSource - > data1 ) ;
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data2 ! = - 1 & & pXSource - > data2 ! = 32767 ) )
setDataValueOfObject ( objType , objIndex , 2 , pXSource - > data2 ) ;
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data3 ! = - 1 & & pXSource - > data3 ! = 32767 ) )
setDataValueOfObject ( objType , objIndex , 3 , pXSource - > data3 ) ;
if ( ( pSource - > flags & kModernTypeFlag1 ) | | pXSource - > data4 ! = 65535 )
setDataValueOfObject ( objType , objIndex , 4 , pXSource - > data4 ) ;
break ;
case OBJ_WALL :
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data1 ! = - 1 & & pXSource - > data1 ! = 32767 ) )
setDataValueOfObject ( objType , objIndex , 1 , pXSource - > data1 ) ;
break ;
}
}
void useSectorLigthChanger ( XSPRITE * pXSource , XSECTOR * pXSector ) {
spritetype * pSource = & sprite [ pXSource - > reference ] ;
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
2020-05-05 18:50:14 +00:00
pXSector - > wave = ClipHigh ( pXSource - > data1 , 11 ) ;
2020-02-07 19:47:43 +00:00
int oldAmplitude = pXSector - > amplitude ;
2020-05-05 18:50:14 +00:00
if ( valueIsBetween ( pXSource - > data2 , - 128 , 128 ) )
pXSector - > amplitude = pXSource - > data2 ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
2020-05-05 18:50:14 +00:00
pXSector - > freq = ClipHigh ( pXSource - > data3 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
2020-05-05 18:50:14 +00:00
pXSector - > phase = ClipHigh ( pXSource - > data4 , 255 ) ;
if ( pSource - > flags ) {
if ( pSource - > flags ! = kModernTypeFlag1 ) {
pXSector - > shadeAlways = ( pSource - > flags & 0x0001 ) ? true : false ;
pXSector - > shadeFloor = ( pSource - > flags & 0x0002 ) ? true : false ;
pXSector - > shadeCeiling = ( pSource - > flags & 0x0004 ) ? true : false ;
pXSector - > shadeWalls = ( pSource - > flags & 0x0008 ) ? true : false ;
} else {
2020-02-07 19:47:43 +00:00
pXSector - > shadeAlways = true ;
2020-05-05 18:50:14 +00:00
}
}
2020-02-07 19:47:43 +00:00
// add to shadeList if amplitude was set to 0 previously
2020-05-05 18:50:14 +00:00
if ( oldAmplitude ! = pXSector - > amplitude & & shadeCount < kMaxXSectors ) {
2020-02-07 19:47:43 +00:00
bool found = false ;
for ( int i = 0 ; i < shadeCount ; i + + ) {
if ( shadeList [ i ] ! = sector [ pXSector - > reference ] . extra ) continue ;
found = true ;
break ;
}
if ( ! found )
shadeList [ shadeCount + + ] = sector [ pXSector - > reference ] . extra ;
}
}
void useTargetChanger ( XSPRITE * pXSource , spritetype * pSprite ) {
2020-02-08 20:13:29 +00:00
if ( ! IsDudeSprite ( pSprite ) | | pSprite - > statnum ! = kStatDude ) {
2020-02-07 19:47:43 +00:00
switch ( pSprite - > type ) { // can be dead dude turned in gib
// make current target and all other dudes not attack this dude anymore
case kThingBloodBits :
case kThingBloodChunks :
aiFightFreeTargets ( pSprite - > index ) ;
return ;
default :
return ;
}
}
2020-02-08 20:13:29 +00:00
2020-02-07 19:47:43 +00:00
spritetype * pSource = & sprite [ pXSource - > reference ] ; XSPRITE * pXSprite = & xsprite [ pSprite - > extra ] ;
spritetype * pTarget = NULL ; XSPRITE * pXTarget = NULL ; int receiveHp = 33 + Random ( 33 ) ;
2020-02-08 20:13:29 +00:00
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ; int matesPerEnemy = 1 ;
2020-02-07 19:47:43 +00:00
// dude is burning?
if ( pXSprite - > burnTime > 0 & & spriRangeIsFine ( pXSprite - > burnSource ) ) {
if ( IsBurningDude ( pSprite ) ) return ;
else {
spritetype * pBurnSource = & sprite [ pXSprite - > burnSource ] ;
if ( pBurnSource - > extra > = 0 ) {
if ( pXSource - > data2 = = 1 & & aiFightIsMateOf ( pXSprite , & xsprite [ pBurnSource - > extra ] ) ) {
pXSprite - > burnTime = 0 ;
2020-04-07 20:30:00 +00:00
2020-02-07 19:47:43 +00:00
// heal dude a bit in case of friendly fire
2020-04-07 20:30:00 +00:00
int startHp = ( pXSprite - > sysData2 > 0 ) ? ClipRange ( pXSprite - > sysData2 < < 4 , 1 , 65535 ) : pDudeInfo - > startHealth < < 4 ;
2020-04-04 19:48:23 +00:00
if ( pXSprite - > health < startHp ) actHealDude ( pXSprite , receiveHp , startHp ) ;
2020-02-07 19:47:43 +00:00
} else if ( xsprite [ pBurnSource - > extra ] . health < = 0 ) {
pXSprite - > burnTime = 0 ;
}
}
}
}
spritetype * pPlayer = aiFightTargetIsPlayer ( pXSprite ) ;
// special handling for player(s) if target changer data4 > 2.
if ( pPlayer ! = NULL ) {
if ( pXSource - > data4 = = 3 ) {
aiSetTarget ( pXSprite , pSprite - > x , pSprite - > y , pSprite - > z ) ;
aiSetGenIdleState ( pSprite , pXSprite ) ;
if ( pSprite - > type = = kDudeModernCustom & & leechIsDropped ( pSprite ) )
removeLeech ( leechIsDropped ( pSprite ) ) ;
} else if ( pXSource - > data4 = = 4 ) {
aiSetTarget ( pXSprite , pPlayer - > x , pPlayer - > y , pPlayer - > z ) ;
if ( pSprite - > type = = kDudeModernCustom & & leechIsDropped ( pSprite ) )
removeLeech ( leechIsDropped ( pSprite ) ) ;
}
}
int maxAlarmDudes = 8 + Random ( 8 ) ;
if ( pXSprite - > target > - 1 & & sprite [ pXSprite - > target ] . extra > - 1 & & pPlayer = = NULL ) {
pTarget = & sprite [ pXSprite - > target ] ; pXTarget = & xsprite [ pTarget - > extra ] ;
if ( aiFightUnitCanFly ( pSprite ) & & aiFightIsMeleeUnit ( pTarget ) & & ! aiFightUnitCanFly ( pTarget ) )
pSprite - > flags | = 0x0002 ;
else if ( aiFightUnitCanFly ( pSprite ) )
pSprite - > flags & = ~ 0x0002 ;
if ( ! IsDudeSprite ( pTarget ) | | pXTarget - > health < 1 | | ! aiFightDudeCanSeeTarget ( pXSprite , pDudeInfo , pTarget ) ) {
aiSetTarget ( pXSprite , pSprite - > x , pSprite - > y , pSprite - > z ) ;
}
// dude attack or attacked by target that does not fit by data id?
else if ( pXSource - > data1 ! = 666 & & pXTarget - > data1 ! = pXSource - > data1 ) {
if ( aiFightDudeIsAffected ( pXTarget ) ) {
// force stop attack target
aiSetTarget ( pXSprite , pSprite - > x , pSprite - > y , pSprite - > z ) ;
if ( pXSprite - > burnSource = = pTarget - > index ) {
pXSprite - > burnTime = 0 ;
pXSprite - > burnSource = - 1 ;
}
// force stop attack dude
aiSetTarget ( pXTarget , pTarget - > x , pTarget - > y , pTarget - > z ) ;
if ( pXTarget - > burnSource = = pSprite - > index ) {
pXTarget - > burnTime = 0 ;
pXTarget - > burnSource = - 1 ;
}
}
}
// instantly kill annoying spiders, rats, hands etc if dude is big enough
else if ( aiFightIsAnnoyingUnit ( pTarget ) & & ! aiFightIsAnnoyingUnit ( pSprite ) & & tilesiz [ pSprite - > picnum ] . y > = 60 & &
aiFightGetTargetDist ( pSprite , pDudeInfo , pTarget ) < 2 ) {
actKillDude ( pSource - > index , pTarget , DAMAGE_TYPE_0 , 65535 ) ;
aiSetTarget ( pXSprite , pSprite - > x , pSprite - > y , pSprite - > z ) ;
} else if ( pXSource - > data2 = = 1 & & aiFightIsMateOf ( pXSprite , pXTarget ) ) {
spritetype * pMate = pTarget ; XSPRITE * pXMate = pXTarget ;
// heal dude
2020-04-07 20:30:00 +00:00
int startHp = ( pXSprite - > sysData2 > 0 ) ? ClipRange ( pXSprite - > sysData2 < < 4 , 1 , 65535 ) : pDudeInfo - > startHealth < < 4 ;
2020-04-04 19:48:23 +00:00
if ( pXSprite - > health < startHp ) actHealDude ( pXSprite , receiveHp , startHp ) ;
2020-02-07 19:47:43 +00:00
// heal mate
2020-04-07 20:30:00 +00:00
startHp = ( pXMate - > sysData2 > 0 ) ? ClipRange ( pXMate - > sysData2 < < 4 , 1 , 65535 ) : getDudeInfo ( pMate - > type ) - > startHealth < < 4 ;
2020-04-04 19:48:23 +00:00
if ( pXMate - > health < startHp ) actHealDude ( pXMate , receiveHp , startHp ) ;
2020-02-07 19:47:43 +00:00
if ( pXMate - > target > - 1 & & sprite [ pXMate - > target ] . extra > = 0 ) {
pTarget = & sprite [ pXMate - > target ] ;
// force mate stop attack dude, if he does
if ( pXMate - > target = = pSprite - > index ) {
aiSetTarget ( pXMate , pMate - > x , pMate - > y , pMate - > z ) ;
} else if ( ! aiFightIsMateOf ( pXSprite , & xsprite [ pTarget - > extra ] ) ) {
// force dude to attack same target that mate have
aiSetTarget ( pXSprite , pTarget - > index ) ;
return ;
} else {
// force mate to stop attack another mate
aiSetTarget ( pXMate , pMate - > x , pMate - > y , pMate - > z ) ;
}
}
// force dude stop attack mate, if target was not changed previously
if ( pXSprite - > target = = pMate - > index )
aiSetTarget ( pXSprite , pSprite - > x , pSprite - > y , pSprite - > z ) ;
}
// check if targets aims player then force this target to fight with dude
else if ( aiFightTargetIsPlayer ( pXTarget ) ! = NULL ) {
aiSetTarget ( pXTarget , pSprite - > index ) ;
}
int mDist = 3 ; if ( aiFightIsMeleeUnit ( pSprite ) ) mDist = 2 ;
if ( pXSprite - > target > = 0 & & aiFightGetTargetDist ( pSprite , pDudeInfo , & sprite [ pXSprite - > target ] ) < mDist ) {
if ( ! isActive ( pSprite - > index ) ) aiActivateDude ( pSprite , pXSprite ) ;
return ;
}
// lets try to look for target that fits better by distance
else if ( ( ( int ) gFrameClock & 256 ) ! = 0 & & ( pXSprite - > target < 0 | | aiFightGetTargetDist ( pSprite , pDudeInfo , pTarget ) > = mDist ) ) {
pTarget = aiFightGetTargetInRange ( pSprite , 0 , mDist , pXSource - > data1 , pXSource - > data2 ) ;
if ( pTarget ! = NULL ) {
pXTarget = & xsprite [ pTarget - > extra ] ;
// Make prev target not aim in dude
if ( pXSprite - > target > - 1 ) {
spritetype * prvTarget = & sprite [ pXSprite - > target ] ;
aiSetTarget ( & xsprite [ prvTarget - > extra ] , prvTarget - > x , prvTarget - > y , prvTarget - > z ) ;
if ( ! isActive ( pTarget - > index ) )
aiActivateDude ( pTarget , pXTarget ) ;
}
// Change target for dude
aiSetTarget ( pXSprite , pTarget - > index ) ;
if ( ! isActive ( pSprite - > index ) )
aiActivateDude ( pSprite , pXSprite ) ;
// ...and change target of target to dude to force it fight
if ( pXSource - > data3 > 0 & & pXTarget - > target ! = pSprite - > index ) {
aiSetTarget ( pXTarget , pSprite - > index ) ;
if ( ! isActive ( pTarget - > index ) )
aiActivateDude ( pTarget , pXTarget ) ;
}
return ;
}
}
}
if ( ( pXSprite - > target < 0 | | pPlayer ! = NULL ) & & ( ( int ) gFrameClock & 32 ) ! = 0 ) {
// try find first target that dude can see
for ( int nSprite = headspritestat [ kStatDude ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
2020-02-08 20:13:29 +00:00
2020-02-07 19:47:43 +00:00
pTarget = & sprite [ nSprite ] ; pXTarget = & xsprite [ pTarget - > extra ] ;
if ( pXTarget - > target = = pSprite - > index ) {
aiSetTarget ( pXSprite , pTarget - > index ) ;
return ;
}
// skip non-dudes and players
if ( ! IsDudeSprite ( pTarget ) | | ( IsPlayerSprite ( pTarget ) & & pXSource - > data4 > 0 ) | | pTarget - > owner = = pSprite - > index ) continue ;
// avoid self aiming, those who dude can't see, and those who dude own
else if ( ! aiFightDudeCanSeeTarget ( pXSprite , pDudeInfo , pTarget ) | | pSprite - > index = = pTarget - > index ) continue ;
// if Target Changer have data1 = 666, everyone can be target, except AI team mates.
else if ( pXSource - > data1 ! = 666 & & pXSource - > data1 ! = pXTarget - > data1 ) continue ;
// don't attack immortal, burning dudes and mates
if ( IsBurningDude ( pTarget ) | | ! IsKillableDude ( pTarget ) | | ( pXSource - > data2 = = 1 & & aiFightIsMateOf ( pXSprite , pXTarget ) ) )
continue ;
if ( pXSource - > data2 = = 0 | | ( pXSource - > data2 = = 1 & & ! aiFightMatesHaveSameTarget ( pXSprite , pTarget , matesPerEnemy ) ) ) {
// Change target for dude
aiSetTarget ( pXSprite , pTarget - > index ) ;
if ( ! isActive ( pSprite - > index ) )
aiActivateDude ( pSprite , pXSprite ) ;
// ...and change target of target to dude to force it fight
if ( pXSource - > data3 > 0 & & pXTarget - > target ! = pSprite - > index ) {
aiSetTarget ( pXTarget , pSprite - > index ) ;
if ( ! isActive ( pTarget - > index ) )
aiActivateDude ( pTarget , pXTarget ) ;
if ( pXSource - > data3 = = 2 )
aiFightAlarmDudesInSight ( pTarget , maxAlarmDudes ) ;
}
2020-02-08 20:13:29 +00:00
2020-02-07 19:47:43 +00:00
return ;
}
2020-02-08 20:13:29 +00:00
2020-02-07 19:47:43 +00:00
break ;
}
}
// got no target - let's ask mates if they have targets
if ( ( pXSprite - > target < 0 | | pPlayer ! = NULL ) & & pXSource - > data2 = = 1 & & ( ( int ) gFrameClock & 64 ) ! = 0 ) {
spritetype * pMateTarget = NULL ;
if ( ( pMateTarget = aiFightGetMateTargets ( pXSprite ) ) ! = NULL & & pMateTarget - > extra > 0 ) {
XSPRITE * pXMateTarget = & xsprite [ pMateTarget - > extra ] ;
if ( aiFightDudeCanSeeTarget ( pXSprite , pDudeInfo , pMateTarget ) ) {
if ( pXMateTarget - > target < 0 ) {
aiSetTarget ( pXMateTarget , pSprite - > index ) ;
if ( IsDudeSprite ( pMateTarget ) & & ! isActive ( pMateTarget - > index ) )
aiActivateDude ( pMateTarget , pXMateTarget ) ;
}
aiSetTarget ( pXSprite , pMateTarget - > index ) ;
if ( ! isActive ( pSprite - > index ) )
aiActivateDude ( pSprite , pXSprite ) ;
return ;
// try walk in mate direction in case if not see the target
} else if ( pXMateTarget - > target > = 0 & & aiFightDudeCanSeeTarget ( pXSprite , pDudeInfo , & sprite [ pXMateTarget - > target ] ) ) {
spritetype * pMate = & sprite [ pXMateTarget - > target ] ;
pXSprite - > target = pMateTarget - > index ;
pXSprite - > targetX = pMate - > x ;
pXSprite - > targetY = pMate - > y ;
pXSprite - > targetZ = pMate - > z ;
if ( ! isActive ( pSprite - > index ) )
aiActivateDude ( pSprite , pXSprite ) ;
return ;
}
}
}
}
void usePictureChanger ( XSPRITE * pXSource , int objType , int objIndex ) {
2020-05-05 18:50:14 +00:00
//spritetype* pSource = &sprite[pXSource->reference];
2020-02-07 19:47:43 +00:00
switch ( objType ) {
2020-05-05 18:50:14 +00:00
case OBJ_SECTOR :
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
sector [ objIndex ] . floorpicnum = pXSource - > data1 ;
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
sector [ objIndex ] . ceilingpicnum = pXSource - > data2 ;
2020-05-05 18:50:14 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
2020-02-07 19:47:43 +00:00
sector [ objIndex ] . floorpal = pXSource - > data3 ;
2020-05-05 18:50:14 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
2020-02-07 19:47:43 +00:00
sector [ objIndex ] . ceilingpal = pXSource - > data4 ;
break ;
case OBJ_SPRITE :
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
sprite [ objIndex ] . picnum = pXSource - > data1 ;
if ( pXSource - > data2 > = 0 ) sprite [ objIndex ] . shade = ( pXSource - > data2 > 127 ) ? 127 : pXSource - > data2 ;
else if ( pXSource - > data2 < - 1 ) sprite [ objIndex ] . shade = ( pXSource - > data2 < - 127 ) ? - 127 : pXSource - > data2 ;
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
sprite [ objIndex ] . pal = pXSource - > data3 ;
break ;
case OBJ_WALL :
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
wall [ objIndex ] . picnum = pXSource - > data1 ;
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
wall [ objIndex ] . overpicnum = pXSource - > data2 ;
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
wall [ objIndex ] . pal = pXSource - > data3 ;
break ;
}
}
//---------------------------------------
// player related
QAV * playerQavSceneLoad ( int qavId ) {
QAV * pQav = NULL ; DICTNODE * hQav = gSysRes . Lookup ( qavId , " QAV " ) ;
if ( hQav ) pQav = ( QAV * ) gSysRes . Lock ( hQav ) ;
else viewSetSystemMessage ( " Failed to load QAV animation #%d " , qavId ) ;
return pQav ;
}
void playerQavSceneProcess ( PLAYER * pPlayer , QAVSCENE * pQavScene ) {
int nIndex = pQavScene - > index ;
2020-05-05 18:50:14 +00:00
if ( xspriRangeIsFine ( sprite [ nIndex ] . extra ) ) {
2020-02-07 19:47:43 +00:00
XSPRITE * pXSprite = & xsprite [ sprite [ nIndex ] . extra ] ;
if ( pXSprite - > waitTime > 0 & & - - pXSprite - > sysData1 < = 0 ) {
2020-05-05 18:50:14 +00:00
if ( pXSprite - > txID > 0 ) evSend ( nIndex , 3 , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
else trPlayerCtrlStopScene ( pPlayer ) ;
2020-02-07 19:47:43 +00:00
} else {
playerQavScenePlay ( pPlayer ) ;
pPlayer - > weaponTimer = ClipLow ( pPlayer - > weaponTimer - = 4 , 0 ) ;
}
} else {
2020-05-05 18:50:14 +00:00
2020-02-07 19:47:43 +00:00
pQavScene - > index = pPlayer - > sceneQav = - 1 ;
pQavScene - > qavResrc = NULL ;
}
}
void playerQavSceneDraw ( PLAYER * pPlayer , int a2 , int a3 , int a4 , int a5 ) {
if ( pPlayer = = NULL | | pPlayer - > sceneQav = = - 1 ) return ;
QAVSCENE * pQavScene = & gPlayerCtrl [ pPlayer - > nPlayer ] . qavScene ;
spritetype * pSprite = & sprite [ pQavScene - > index ] ;
if ( pQavScene - > qavResrc ! = NULL ) {
QAV * pQAV = pQavScene - > qavResrc ;
int v4 = ( pPlayer - > weaponTimer = = 0 ) ? ( int ) totalclock % pQAV - > at10 : pQAV - > at10 - pPlayer - > weaponTimer ;
int flags = 2 ; int nInv = powerupCheck ( pPlayer , kPwUpShadowCloak ) ;
if ( nInv > = 120 * 8 | | ( nInv ! = 0 & & ( ( int ) totalclock & 32 ) ) ) {
a2 = - 128 ; flags | = 1 ;
}
// draw as weapon
if ( ! ( pSprite - > flags & kModernTypeFlag1 ) ) {
pQAV - > x = a3 ; pQAV - > y = a4 ;
pQAV - > Draw ( v4 , flags , a2 , a5 ) ;
// draw fullscreen (currently 4:3 only)
} else {
int wx1 = windowxy1 . x , wy1 = windowxy1 . y , wx2 = windowxy2 . x , wy2 = windowxy2 . y ;
windowxy2 . x = xdim - 1 ; windowxy2 . y = ydim - 1 ;
windowxy1 . x = windowxy1 . y = 0 ;
pQAV - > Draw ( v4 , flags , a2 , a5 ) ;
windowxy1 . x = wx1 ; windowxy1 . y = wy1 ;
windowxy2 . x = wx2 ; windowxy2 . y = wy2 ;
}
}
}
void playerQavScenePlay ( PLAYER * pPlayer ) {
2020-05-05 18:50:14 +00:00
if ( pPlayer = = NULL ) return ;
2020-02-07 19:47:43 +00:00
QAVSCENE * pQavScene = & gPlayerCtrl [ pPlayer - > nPlayer ] . qavScene ;
2020-05-05 18:50:14 +00:00
if ( pPlayer - > sceneQav = = - 1 & & pQavScene - > index > = 0 )
pPlayer - > sceneQav = xsprite [ sprite [ pQavScene - > index ] . extra ] . data2 ;
2020-02-07 19:47:43 +00:00
if ( pQavScene - > qavResrc ! = NULL ) {
QAV * pQAV = pQavScene - > qavResrc ;
pQAV - > nSprite = pPlayer - > pSprite - > index ;
int nTicks = pQAV - > at10 - pPlayer - > weaponTimer ;
pQAV - > Play ( nTicks - 4 , nTicks , pPlayer - > qavCallback , pPlayer ) ;
}
}
void playerQavSceneReset ( PLAYER * pPlayer ) {
QAVSCENE * pQavScene = & gPlayerCtrl [ pPlayer - > nPlayer ] . qavScene ;
pQavScene - > index = pQavScene - > dummy = pPlayer - > sceneQav = - 1 ;
pQavScene - > qavResrc = NULL ;
}
bool playerSizeShrink ( PLAYER * pPlayer , int divider ) {
pPlayer - > pXSprite - > scale = 256 / divider ;
playerSetRace ( pPlayer , kModeHumanShrink ) ;
return true ;
}
bool playerSizeGrow ( PLAYER * pPlayer , int multiplier ) {
pPlayer - > pXSprite - > scale = 256 * multiplier ;
playerSetRace ( pPlayer , kModeHumanGrown ) ;
return true ;
}
bool playerSizeReset ( PLAYER * pPlayer ) {
playerSetRace ( pPlayer , kModeHuman ) ;
pPlayer - > pXSprite - > scale = 0 ;
return true ;
}
void playerDeactivateShrooms ( PLAYER * pPlayer ) {
powerupDeactivate ( pPlayer , kPwUpGrowShroom ) ;
pPlayer - > pwUpTime [ kPwUpGrowShroom ] = 0 ;
powerupDeactivate ( pPlayer , kPwUpShrinkShroom ) ;
pPlayer - > pwUpTime [ kPwUpShrinkShroom ] = 0 ;
}
PLAYER * getPlayerById ( short id ) {
// relative to connected players
if ( id > = 1 & & id < = kMaxPlayers ) {
id = id - 1 ;
for ( int i = connecthead ; i > = 0 ; i = connectpoint2 [ i ] ) {
if ( id = = gPlayer [ i ] . nPlayer )
return & gPlayer [ i ] ;
}
// absolute sprite type
} else if ( id > = kDudePlayer1 & & id < = kDudePlayer8 ) {
for ( int i = connecthead ; i > = 0 ; i = connectpoint2 [ i ] ) {
if ( id = = gPlayer [ i ] . pSprite - > type )
return & gPlayer [ i ] ;
}
}
//viewSetSystemMessage("There is no player id #%d", id);
return NULL ;
}
// misc functions
bool IsBurningDude ( spritetype * pSprite ) {
if ( pSprite = = NULL ) return false ;
switch ( pSprite - > type ) {
case kDudeBurningInnocent :
case kDudeBurningCultist :
case kDudeBurningZombieAxe :
case kDudeBurningZombieButcher :
case kDudeBurningTinyCaleb :
case kDudeBurningBeast :
case kDudeModernCustomBurning :
return true ;
}
return false ;
}
bool IsKillableDude ( spritetype * pSprite ) {
switch ( pSprite - > type ) {
case kDudeGargoyleStatueFlesh :
case kDudeGargoyleStatueStone :
return false ;
default :
if ( ! IsDudeSprite ( pSprite ) | | xsprite [ pSprite - > extra ] . locked = = 1 ) return false ;
return true ;
}
}
bool isGrown ( spritetype * pSprite ) {
if ( powerupCheck ( & gPlayer [ pSprite - > type - kDudePlayer1 ] , kPwUpGrowShroom ) > 0 ) return true ;
else if ( pSprite - > extra > = 0 & & xsprite [ pSprite - > extra ] . scale > = 512 ) return true ;
else return false ;
}
bool isShrinked ( spritetype * pSprite ) {
if ( powerupCheck ( & gPlayer [ pSprite - > type - kDudePlayer1 ] , kPwUpShrinkShroom ) > 0 ) return true ;
else if ( pSprite - > extra > = 0 & & xsprite [ pSprite - > extra ] . scale > 0 & & xsprite [ pSprite - > extra ] . scale < = 128 ) return true ;
else return false ;
}
bool isActive ( int nSprite ) {
if ( sprite [ nSprite ] . extra < 0 | | sprite [ nSprite ] . extra > = kMaxXSprites )
return false ;
XSPRITE * pXDude = & xsprite [ sprite [ nSprite ] . extra ] ;
switch ( pXDude - > aiState - > stateType ) {
case kAiStateIdle :
case kAiStateGenIdle :
case kAiStateSearch :
case kAiStateMove :
case kAiStateOther :
return false ;
default :
return true ;
}
}
int getDataFieldOfObject ( int objType , int objIndex , int dataIndex ) {
int data = - 65535 ;
switch ( objType ) {
2020-03-01 20:36:28 +00:00
case OBJ_SPRITE :
switch ( dataIndex ) {
case 1 : return xsprite [ sprite [ objIndex ] . extra ] . data1 ;
case 2 : return xsprite [ sprite [ objIndex ] . extra ] . data2 ;
case 3 :
switch ( sprite [ objIndex ] . type ) {
case kDudeModernCustom : return xsprite [ sprite [ objIndex ] . extra ] . sysData1 ;
default : return xsprite [ sprite [ objIndex ] . extra ] . data3 ;
}
case 4 : return xsprite [ sprite [ objIndex ] . extra ] . data4 ;
default : return data ;
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
case OBJ_SECTOR : return xsector [ sector [ objIndex ] . extra ] . data ;
case OBJ_WALL : return xwall [ wall [ objIndex ] . extra ] . data ;
default : return data ;
2020-02-07 19:47:43 +00:00
}
}
bool setDataValueOfObject ( int objType , int objIndex , int dataIndex , int value ) {
switch ( objType ) {
case OBJ_SPRITE : {
XSPRITE * pXSprite = & xsprite [ sprite [ objIndex ] . extra ] ;
// exceptions
if ( IsDudeSprite ( & sprite [ objIndex ] ) & & pXSprite - > health < = 0 ) return true ;
2020-03-01 20:36:28 +00:00
switch ( sprite [ objIndex ] . type ) {
2020-02-07 19:47:43 +00:00
case kThingBloodBits :
case kThingBloodChunks :
case kThingZombieHead :
2020-03-01 20:36:28 +00:00
return true ;
2020-02-07 19:47:43 +00:00
break ;
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
switch ( dataIndex ) {
case 1 :
xsprite [ sprite [ objIndex ] . extra ] . data1 = value ;
switch ( sprite [ objIndex ] . type ) {
2020-03-01 20:36:28 +00:00
case kSwitchCombo :
if ( value = = xsprite [ sprite [ objIndex ] . extra ] . data2 ) SetSpriteState ( objIndex , & xsprite [ sprite [ objIndex ] . extra ] , 1 ) ;
else SetSpriteState ( objIndex , & xsprite [ sprite [ objIndex ] . extra ] , 0 ) ;
break ;
case kDudeModernCustom :
case kDudeModernCustomBurning :
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyWeapon ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyDmgScale ] = true ;
evPost ( objIndex , 3 , kGenDudeUpdTimeRate , kCallbackGenDudeUpdate ) ;
break ;
2020-02-07 19:47:43 +00:00
}
return true ;
case 2 :
xsprite [ sprite [ objIndex ] . extra ] . data2 = value ;
switch ( sprite [ objIndex ] . type ) {
2020-03-01 20:36:28 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertySpriteSize ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyMass ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyDmgScale ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyStates ] = true ;
gGenDudeExtra [ objIndex ] . updReq [ kGenDudePropertyAttack ] = true ;
evPost ( objIndex , 3 , kGenDudeUpdTimeRate , kCallbackGenDudeUpdate ) ;
break ;
2020-02-07 19:47:43 +00:00
}
return true ;
case 3 :
xsprite [ sprite [ objIndex ] . extra ] . data3 = value ;
switch ( sprite [ objIndex ] . type ) {
2020-03-01 20:36:28 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
xsprite [ sprite [ objIndex ] . extra ] . sysData1 = value ;
break ;
2020-02-07 19:47:43 +00:00
}
return true ;
case 4 :
xsprite [ sprite [ objIndex ] . extra ] . data4 = value ;
return true ;
default :
return false ;
}
}
case OBJ_SECTOR :
xsector [ sector [ objIndex ] . extra ] . data = value ;
return true ;
case OBJ_WALL :
xwall [ wall [ objIndex ] . extra ] . data = value ;
return true ;
default :
return false ;
}
}
2020-04-07 20:30:00 +00:00
int listTx ( XSPRITE * pXRedir , int tx ) {
if ( txIsRanged ( pXRedir ) ) {
if ( tx = = - 1 ) tx = pXRedir - > data1 ;
else if ( tx < pXRedir - > data4 ) tx + + ;
else tx = - 1 ;
} else {
if ( tx = = - 1 ) {
for ( int i = 0 ; i < = 3 ; i + + ) {
if ( ( tx = GetDataVal ( & sprite [ pXRedir - > reference ] , i ) ) < = 0 ) continue ;
else return tx ;
}
} else {
int saved = tx ; bool savedFound = false ;
for ( int i = 0 ; i < = 3 ; i + + ) {
tx = GetDataVal ( & sprite [ pXRedir - > reference ] , i ) ;
if ( savedFound & & tx > 0 ) return tx ;
else if ( tx ! = saved ) continue ;
else savedFound = true ;
}
}
tx = - 1 ;
}
return tx ;
}
XSPRITE * evrIsRedirector ( int nSprite ) {
if ( spriRangeIsFine ( nSprite ) ) {
switch ( sprite [ nSprite ] . type ) {
case kModernRandomTX :
case kModernSequentialTX :
2020-05-05 18:50:14 +00:00
if ( xspriRangeIsFine ( sprite [ nSprite ] . extra ) & & xsprite [ sprite [ nSprite ] . extra ] . command = = kCmdLink
& & ! xsprite [ sprite [ nSprite ] . extra ] . locked ) return & xsprite [ sprite [ nSprite ] . extra ] ;
2020-04-07 20:30:00 +00:00
break ;
}
}
return NULL ;
}
XSPRITE * evrListRedirectors ( int objType , int objXIndex , XSPRITE * pXRedir , int * tx ) {
if ( ! gEventRedirectsUsed ) return NULL ;
else if ( pXRedir & & ( * tx = listTx ( pXRedir , * tx ) ) ! = - 1 )
return pXRedir ;
int id = 0 ;
switch ( objType ) {
case OBJ_SECTOR :
if ( ! xsectRangeIsFine ( objXIndex ) ) return NULL ;
id = xsector [ objXIndex ] . txID ;
break ;
case OBJ_SPRITE :
if ( ! xspriRangeIsFine ( objXIndex ) ) return NULL ;
id = xsprite [ objXIndex ] . txID ;
break ;
case OBJ_WALL :
if ( ! xwallRangeIsFine ( objXIndex ) ) return NULL ;
id = xwall [ objXIndex ] . txID ;
break ;
default :
return NULL ;
}
int nIndex = ( pXRedir ) ? pXRedir - > reference : - 1 ; bool prevFound = false ;
for ( int i = bucketHead [ id ] ; i < bucketHead [ id + 1 ] ; i + + ) {
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
XSPRITE * pXSpr = evrIsRedirector ( rxBucket [ i ] . index ) ;
if ( ! pXSpr ) continue ;
else if ( prevFound | | nIndex = = - 1 ) { * tx = listTx ( pXSpr , * tx ) ; return pXSpr ; }
else if ( nIndex ! = pXSpr - > reference ) continue ;
else prevFound = true ;
}
* tx = - 1 ;
return NULL ;
}
2020-02-07 19:47:43 +00:00
// this function checks if all TX objects have the same value
bool incDecGoalValueIsReached ( XSPRITE * pXSprite ) {
2020-03-05 20:46:05 +00:00
2020-04-07 20:30:00 +00:00
if ( pXSprite - > data3 ! = pXSprite - > sysData1 ) return false ;
char buffer [ 5 ] ; sprintf ( buffer , " %d " , abs ( pXSprite - > data1 ) ) ; int len = strlen ( buffer ) ; int rx = - 1 ;
2020-02-07 19:47:43 +00:00
for ( int i = bucketHead [ pXSprite - > txID ] ; i < bucketHead [ pXSprite - > txID + 1 ] ; i + + ) {
2020-04-07 20:30:00 +00:00
if ( rxBucket [ i ] . type = = OBJ_SPRITE & & evrIsRedirector ( rxBucket [ i ] . index ) ) continue ;
2020-03-05 20:46:05 +00:00
for ( int a = 0 ; a < len ; a + + ) {
if ( getDataFieldOfObject ( rxBucket [ i ] . type , rxBucket [ i ] . index , ( buffer [ a ] - 52 ) + 4 ) ! = pXSprite - > data3 )
return false ;
}
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
2020-04-07 20:30:00 +00:00
XSPRITE * pXRedir = NULL ; // check redirected TX buckets
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , sprite [ pXSprite - > reference ] . extra , pXRedir , & rx ) ) ! = NULL ) {
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + ) {
for ( int a = 0 ; a < len ; a + + ) {
if ( getDataFieldOfObject ( rxBucket [ i ] . type , rxBucket [ i ] . index , ( buffer [ a ] - 52 ) + 4 ) ! = pXSprite - > data3 )
return false ;
2020-03-01 20:36:28 +00:00
}
}
}
2020-04-07 20:30:00 +00:00
2020-02-07 19:47:43 +00:00
return true ;
}
2020-05-05 18:50:14 +00:00
void seqSpawnerOffSameTx ( XSPRITE * pXSource ) {
for ( int nSprite = headspritestat [ kStatModernSeqSpawner ] ; nSprite > = 0 ; nSprite = nextspritestat [ nSprite ] ) {
if ( nSprite ! = pXSource - > reference ) {
XSPRITE * pXSprite = & xsprite [ sprite [ nSprite ] . extra ] ;
if ( pXSprite - > txID = = pXSource - > txID & & pXSprite - > state = = 1 ) {
evKill ( nSprite , OBJ_SPRITE ) ;
pXSprite - > state = 0 ;
}
}
}
}
2020-04-07 20:30:00 +00:00
2020-02-07 19:47:43 +00:00
// this function can be called via sending numbered command to TX kChannelModernEndLevelCustom
// it allows to set custom next level instead of taking it from INI file.
void levelEndLevelCustom ( int nLevel ) {
gGameOptions . uGameFlags | = 1 ;
if ( nLevel > = 16 | | nLevel < 0 ) {
gGameOptions . uGameFlags | = 2 ;
gGameOptions . nLevel = 0 ;
return ;
}
gNextLevel = nLevel ;
}
void callbackUniMissileBurst ( int nSprite ) // 22
{
dassert ( nSprite > = 0 & & nSprite < kMaxSprites ) ;
if ( sprite [ nSprite ] . statnum ! = kStatProjectile ) return ;
spritetype * pSprite = & sprite [ nSprite ] ;
int nAngle = getangle ( xvel [ nSprite ] , yvel [ nSprite ] ) ;
int nRadius = 0x55555 ;
for ( int i = 0 ; i < 8 ; i + + )
{
spritetype * pBurst = actSpawnSprite ( pSprite , 5 ) ;
pBurst - > type = pSprite - > type ;
pBurst - > shade = pSprite - > shade ;
pBurst - > picnum = pSprite - > picnum ;
pBurst - > cstat = pSprite - > cstat ;
if ( ( pBurst - > cstat & CSTAT_SPRITE_BLOCK ) ) {
pBurst - > cstat & = ~ CSTAT_SPRITE_BLOCK ; // we don't want missiles impact each other
evPost ( pBurst - > index , 3 , 100 , kCallbackMissileSpriteBlock ) ; // so set blocking flag a bit later
}
pBurst - > pal = pSprite - > pal ;
pBurst - > clipdist = pSprite - > clipdist / 4 ;
pBurst - > flags = pSprite - > flags ;
pBurst - > xrepeat = pSprite - > xrepeat / 2 ;
pBurst - > yrepeat = pSprite - > yrepeat / 2 ;
pBurst - > ang = ( ( pSprite - > ang + missileInfo [ pSprite - > type - kMissileBase ] . angleOfs ) & 2047 ) ;
pBurst - > owner = pSprite - > owner ;
actBuildMissile ( pBurst , pBurst - > extra , pSprite - > index ) ;
int nAngle2 = ( i < < 11 ) / 8 ;
int dx = 0 ;
int dy = mulscale30r ( nRadius , Sin ( nAngle2 ) ) ;
int dz = mulscale30r ( nRadius , - Cos ( nAngle2 ) ) ;
if ( i & 1 )
{
dy > > = 1 ;
dz > > = 1 ;
}
RotateVector ( & dx , & dy , nAngle ) ;
xvel [ pBurst - > index ] + = dx ;
yvel [ pBurst - > index ] + = dy ;
zvel [ pBurst - > index ] + = dz ;
evPost ( pBurst - > index , 3 , 960 , kCallbackRemove ) ;
}
evPost ( nSprite , 3 , 0 , kCallbackRemove ) ;
}
void callbackMakeMissileBlocking ( int nSprite ) // 23
{
dassert ( nSprite > = 0 & & nSprite < kMaxSprites ) ;
if ( sprite [ nSprite ] . statnum ! = kStatProjectile ) return ;
sprite [ nSprite ] . cstat | = CSTAT_SPRITE_BLOCK ;
}
void callbackGenDudeUpdate ( int nSprite ) // 24
{
if ( spriRangeIsFine ( nSprite ) )
genDudeUpdate ( & sprite [ nSprite ] ) ;
}
2020-02-11 22:15:25 +00:00
class NNLoadSave : public LoadSave
{
virtual void Load ( void ) ;
virtual void Save ( void ) ;
} ;
void NNLoadSave : : Load ( void )
{
Read ( gSpriteMass , sizeof ( gSpriteMass ) ) ;
Read ( & gProxySpritesCount , sizeof ( gProxySpritesCount ) ) ;
Read ( gProxySpritesList , sizeof ( gProxySpritesList ) ) ;
Read ( & gSightSpritesCount , sizeof ( gSightSpritesCount ) ) ;
Read ( gSightSpritesList , sizeof ( gSightSpritesList ) ) ;
Read ( & gPhysSpritesCount , sizeof ( gPhysSpritesCount ) ) ;
Read ( gPhysSpritesList , sizeof ( gPhysSpritesList ) ) ;
2020-03-01 20:36:28 +00:00
Read ( & gImpactSpritesCount , sizeof ( gImpactSpritesCount ) ) ;
Read ( gImpactSpritesList , sizeof ( gImpactSpritesList ) ) ;
Read ( & gEventRedirectsUsed , sizeof ( gEventRedirectsUsed ) ) ;
2020-02-11 22:15:25 +00:00
}
void NNLoadSave : : Save ( void )
{
Write ( gSpriteMass , sizeof ( gSpriteMass ) ) ;
Write ( & gProxySpritesCount , sizeof ( gProxySpritesCount ) ) ;
Write ( gProxySpritesList , sizeof ( gProxySpritesList ) ) ;
Write ( & gSightSpritesCount , sizeof ( gSightSpritesCount ) ) ;
Write ( gSightSpritesList , sizeof ( gSightSpritesList ) ) ;
Write ( & gPhysSpritesCount , sizeof ( gPhysSpritesCount ) ) ;
Write ( gPhysSpritesList , sizeof ( gPhysSpritesList ) ) ;
2020-03-01 20:36:28 +00:00
Write ( & gImpactSpritesCount , sizeof ( gImpactSpritesCount ) ) ;
Write ( gImpactSpritesList , sizeof ( gImpactSpritesList ) ) ;
Write ( & gEventRedirectsUsed , sizeof ( gEventRedirectsUsed ) ) ;
2020-02-11 22:15:25 +00:00
}
static NNLoadSave * myLoadSave ;
void NNLoadSaveConstruct ( void )
{
myLoadSave = new NNLoadSave ( ) ;
}
2020-02-07 19:47:43 +00:00
///////////////////////////////////////////////////////////////////
// This file provides modern features for mappers.
// For full documentation please visit http://cruo.bloodgame.ru/xxsystem
///////////////////////////////////////////////////////////////////
END_BLD_NS
2020-02-16 13:54:24 +00:00
# endif