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"
# ifdef NOONE_EXTENSIONS
# include <random>
2020-12-09 14:56:32 +00:00
# include "blood.h"
2020-11-22 15:47:08 +00:00
# include "savegamehelp.h"
2020-02-07 19:47:43 +00:00
BEGIN_BLD_NS
2021-05-03 22:03:01 +00:00
inline int mulscale8 ( int a , int b ) { return MulScale ( a , b , 8 ) ; }
2020-02-07 19:47:43 +00:00
bool gAllowTrueRandom = false ;
2020-03-01 20:36:28 +00:00
bool gEventRedirectsUsed = false ;
2021-09-01 19:52:00 +00:00
DBloodActor * gProxySpritesList [ ] ; // list of additional sprites which can be triggered by Proximity
2020-02-07 19:47:43 +00:00
short gProxySpritesCount ; // current count
2021-09-01 19:52:00 +00:00
DBloodActor * gSightSpritesList [ ] ; // list of additional sprites which can be triggered by Sight
2020-02-07 19:47:43 +00:00
short gSightSpritesCount ; // current count
2021-09-01 19:52:00 +00:00
DBloodActor * gPhysSpritesList [ ] ; // list of additional sprites which can be affected by physics
2020-02-07 19:47:43 +00:00
short gPhysSpritesCount ; // current count
2021-09-01 19:52:00 +00:00
DBloodActor * gImpactSpritesList [ ] ;
2020-03-01 20:36:28 +00:00
short gImpactSpritesCount ;
2020-03-13 20:59:13 +00:00
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
short gEffectGenCallbacks [ ] = {
kCallbackFXFlameLick ,
kCallbackFXFlareSpark ,
kCallbackFXFlareSparkLite ,
kCallbackFXZombieSpurt ,
kCallbackFXBloodSpurt ,
kCallbackFXArcSpark ,
kCallbackFXTeslaAlt ,
} ;
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 ;
2021-08-27 11:46:07 +00:00
const VECTORINFO_EXTRA gVectorInfoExtra [ ] = {
2020-02-07 19:47:43 +00:00
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
} ;
2021-08-27 11:46:07 +00:00
const 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
} ;
2021-08-27 11:46:07 +00:00
const THINGINFO_EXTRA gThingInfoExtra [ ] = {
2020-02-07 19:47:43 +00:00
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 ,
} ;
2021-08-27 11:46:07 +00:00
const DUDEINFO_EXTRA gDudeInfoExtra [ ] = {
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 200
2020-12-06 20:56:09 +00:00
{ false , false , 0 , 9 , 13 , 13 , 17 , 14 } , // 201
{ false , false , 0 , 9 , 13 , 13 , 17 , 14 } , // 202
{ false , true , 0 , 8 , 0 , 8 , - 1 , - 1 } , // 203
{ false , false , 0 , 8 , 0 , 8 , - 1 , - 1 } , // 204
{ false , true , 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 205
{ true , true , 0 , 0 , 0 , 0 , - 1 , - 1 } , // 206
{ true , false , 0 , 0 , 0 , 0 , - 1 , - 1 } , // 207
{ true , false , 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 208
{ true , false , 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 209
{ true , true , 0 , 0 , 0 , 0 , - 1 , - 1 } , // 210
{ false , true , 0 , 8 , 0 , 8 , - 1 , - 1 } , // 211
{ false , true , 0 , 6 , 0 , 6 , - 1 , - 1 } , // 212
{ false , true , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 213
{ false , true , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 214
{ false , true , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 215
{ false , true , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 216
{ false , true , 0 , 9 , 10 , 10 , - 1 , - 1 } , // 217
{ false , true , 0 , 0 , 0 , 0 , - 1 , - 1 } , // 218
2021-07-19 21:15:26 +00:00
{ true , false , 7 , 7 , 7 , 7 , - 1 , - 1 } , // 219
2020-12-06 20:56:09 +00:00
{ false , true , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 220
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 221
{ false , true , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 222
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 223
{ false , true , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 224
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 225
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 226
{ false , false , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 227
{ false , false , 0 , 7 , 0 , 7 , - 1 , - 1 } , // 228
{ false , false , 0 , 8 , 0 , 8 , - 1 , - 1 } , // 229
{ false , false , 0 , 9 , 13 , 13 , 17 , 14 } , // 230
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 231
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 232
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 233
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 234
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 235
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 236
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 237
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 238
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 239
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 240
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 241
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 242
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 243
{ false , true , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 244
{ false , true , 0 , 6 , 0 , 6 , - 1 , - 1 } , // 245
{ false , false , 0 , 9 , 13 , 13 , 17 , 14 } , // 246
{ false , false , 0 , 9 , 13 , 13 , 14 , 14 } , // 247
{ false , false , 0 , 9 , 13 , 13 , 14 , 14 } , // 248
{ false , false , 0 , 9 , 13 , 13 , 17 , 14 } , // 249
{ false , true , 0 , 6 , 8 , 8 , 10 , 9 } , // 250
{ false , true , 0 , 8 , 9 , 9 , 11 , 10 } , // 251
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 252
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 253
{ false , false , 0 , 9 , 17 , 13 , 17 , 14 } , // 254
{ false , false , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } , // 255
} ;
AISTATE genPatrolStates [ ] = {
//-------------------------------------------------------------------------------
{ kAiStatePatrolWaitL , 0 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitL , 7 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveL , 9 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveL , 8 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveL , 0 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveL , 6 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveL , 7 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
2021-07-19 21:15:26 +00:00
{ kAiStatePatrolTurnL , 9 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnL , 8 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnL , 0 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnL , 6 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnL , 7 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
2020-12-06 20:56:09 +00:00
//-------------------------------------------------------------------------------
{ kAiStatePatrolWaitW , 0 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitW , 10 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitW , 13 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitW , 17 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitW , 8 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitW , 9 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 0 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 10 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 13 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 8 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 9 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 7 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveW , 6 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
2021-07-19 21:15:26 +00:00
{ kAiStatePatrolTurnW , 0 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnW , 10 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnW , 13 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnW , 8 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnW , 9 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnW , 7 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnW , 6 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
2020-12-06 20:56:09 +00:00
//-------------------------------------------------------------------------------
{ kAiStatePatrolWaitC , 17 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitC , 11 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitC , 10 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolWaitC , 14 , - 1 , 0 , NULL , NULL , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveC , 14 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveC , 10 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
{ kAiStatePatrolMoveC , 9 , - 1 , 0 , NULL , aiPatrolMove , aiPatrolThink , NULL } ,
2021-07-19 21:15:26 +00:00
{ kAiStatePatrolTurnC , 14 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnC , 10 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
{ kAiStatePatrolTurnC , 9 , - 1 , 0 , aiPatrolRandGoalAng , aiPatrolTurn , aiPatrolThink , NULL } ,
2020-12-06 20:56:09 +00:00
//-------------------------------------------------------------------------------
2020-03-01 20:36:28 +00:00
} ;
2021-07-19 21:15:26 +00:00
CONDITION_TYPE_NAMES gCondTypeNames [ 7 ] = {
{ kCondGameBase , kCondGameMax , " Game " } ,
{ kCondMixedBase , kCondMixedMax , " Mixed " } ,
{ kCondWallBase , kCondWallMax , " Wall " } ,
{ kCondSectorBase , kCondSectorMax , " Sector " } ,
{ kCondPlayerBase , kCondPlayerMax , " Player " } ,
{ kCondDudeBase , kCondDudeMax , " Enemy " } ,
{ kCondSpriteBase , kCondSpriteMax , " Sprite " } ,
} ;
2020-02-07 19:47:43 +00:00
// for actor.cpp
//-------------------------------------------------------------------------
2021-08-27 11:53:49 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-08-27 11:53:49 +00:00
static DBloodActor * nnExtSpawnDude ( DBloodActor * sourceActor , DBloodActor * origin , short nType , int a3 , int a4 )
{
2020-12-05 17:32:49 +00:00
DBloodActor * pDudeActor = nullptr ;
2021-08-27 11:53:49 +00:00
auto pSource = & sourceActor - > s ( ) ;
auto pXSource = & sourceActor - > x ( ) ;
auto pOrigin = & origin - > s ( ) ;
if ( nType < kDudeBase | | nType > = kDudeMax | | ( pDudeActor = actSpawnSprite ( origin , kStatDude ) ) = = NULL )
2021-07-19 21:15:26 +00:00
return NULL ;
2020-12-05 17:32:49 +00:00
spritetype * pDude = & pDudeActor - > s ( ) ;
XSPRITE * pXDude = & pDudeActor - > x ( ) ;
2021-07-19 21:15:26 +00:00
2021-08-27 11:53:49 +00:00
int angle = pOrigin - > ang ;
int x , y , z = a4 + pOrigin - > z ;
2021-07-19 21:15:26 +00:00
if ( a3 < 0 )
{
2021-08-27 11:53:49 +00:00
x = pOrigin - > x ;
y = pOrigin - > y ;
2021-08-27 12:01:51 +00:00
}
else
2021-07-19 21:15:26 +00:00
{
2021-08-27 11:53:49 +00:00
x = pOrigin - > x + mulscale30r ( Cos ( angle ) , a3 ) ;
y = pOrigin - > y + mulscale30r ( Sin ( angle ) , a3 ) ;
2021-07-19 21:15:26 +00:00
}
vec3_t pos = { x , y , z } ;
2021-08-28 11:59:29 +00:00
setActorPos ( pDudeActor , & pos ) ;
2021-07-19 21:15:26 +00:00
pDude - > type = nType ;
pDude - > ang = angle ;
pDude - > cstat | = 0x1101 ;
pDude - > clipdist = getDudeInfo ( nType ) - > clipdist ;
pXDude - > respawn = 1 ;
pXDude - > health = getDudeInfo ( nType ) - > startHealth < < 4 ;
if ( fileSystem . FindResource ( getDudeInfo ( nType ) - > seqStartID , " SEQ " ) )
seqSpawn ( getDudeInfo ( nType ) - > seqStartID , 3 , pDude - > extra , - 1 ) ;
// add a way to inherit some values of spawner by dude.
if ( pSource - > flags & kModernTypeFlag1 ) {
//inherit pal?
if ( pDude - > pal < = 0 )
pDude - > pal = pSource - > pal ;
// inherit spawn sprite trigger settings, so designer can count monsters.
pXDude - > txID = pXSource - > txID ;
pXDude - > command = pXSource - > command ;
pXDude - > triggerOn = pXSource - > triggerOn ;
pXDude - > triggerOff = pXSource - > triggerOff ;
// inherit drop items
pXDude - > dropMsg = pXSource - > dropMsg ;
// inherit dude flags
pXDude - > dudeDeaf = pXSource - > dudeDeaf ;
pXDude - > dudeGuard = pXSource - > dudeGuard ;
pXDude - > dudeAmbush = pXSource - > dudeAmbush ;
pXDude - > dudeFlag4 = pXSource - > dudeFlag4 ;
pXDude - > unused1 = pXSource - > unused1 ;
}
2021-05-05 10:55:52 +00:00
aiInitSprite ( pDudeActor ) ;
2021-07-19 21:15:26 +00:00
gKillMgr . AddNewKill ( 1 ) ;
2021-08-29 11:44:04 +00:00
bool burning = IsBurningDude ( pDudeActor ) ;
2021-07-19 21:15:26 +00:00
if ( burning ) {
pXDude - > burnTime = 10 ;
2021-09-01 19:52:54 +00:00
pDudeActor - > SetTarget ( nullptr ) ;
2021-07-19 21:15:26 +00:00
}
if ( ( burning | | ( pSource - > flags & kModernTypeFlag3 ) ) & & ! pXDude - > dudeFlag4 )
2021-05-05 10:55:52 +00:00
aiActivateDude ( pDudeActor ) ;
2021-07-19 21:15:26 +00:00
2021-08-27 11:53:49 +00:00
return pDudeActor ;
2021-07-19 21:15:26 +00:00
}
2021-08-27 12:01:51 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-08-27 12:01:51 +00:00
bool nnExtIsImmune ( DBloodActor * actor , int dmgType , int minScale )
{
auto pSprite = & actor - > s ( ) ;
if ( dmgType > = kDmgFall & & dmgType < kDmgMax & & actor - > hasX ( ) & & actor - > x ( ) . locked ! = 1 )
{
2020-02-07 19:47:43 +00:00
if ( pSprite - > type > = kThingBase & & pSprite - > type < kThingMax )
2021-08-27 12:01:51 +00:00
{
2020-02-07 19:47:43 +00:00
return ( thingInfo [ pSprite - > type - kThingBase ] . dmgControl [ dmgType ] < = minScale ) ;
2021-08-27 12:01:51 +00:00
}
else if ( actor - > IsDudeActor ( ) )
{
if ( actor - > IsPlayerActor ( ) ) return ( gPlayer [ pSprite - > type - kDudePlayer1 ] . damageControl [ dmgType ] ) ;
2021-08-29 07:27:03 +00:00
else if ( pSprite - > type = = kDudeModernCustom ) return ( actor - > genDudeExtra . dmgControl [ dmgType ] < = minScale ) ;
2020-12-02 23:30:19 +00:00
else return ( getDudeInfo ( pSprite - > type ) - > damageVal [ dmgType ] < = minScale ) ;
2020-02-07 19:47:43 +00:00
}
}
return true ;
}
2021-08-27 12:11:59 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool nnExtEraseModernStuff ( DBloodActor * actor )
{
auto pSprite = & actor - > s ( ) ;
auto pXSprite = & actor - > x ( ) ;
2020-02-07 19:47:43 +00:00
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 :
2021-07-19 21:15:26 +00:00
case kModernSlopeChanger :
case kModernStealthRegion :
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 ;
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatDecoration ) ;
2020-02-07 19:47:43 +00:00
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 ;
}
2021-09-01 21:25:19 +00:00
if ( pXSprite - > Sight )
{
2020-02-07 19:47:43 +00:00
pXSprite - > Sight = false ; // it does not work in vanilla at all
erased = true ;
}
2021-09-01 21:25:19 +00:00
if ( pXSprite - > Proximity )
{
2020-02-07 19:47:43 +00:00
// proximity works only for things and dudes in vanilla
2021-09-01 21:25:19 +00:00
switch ( pSprite - > statnum )
{
2020-02-07 19:47:43 +00:00
case kStatThing :
case kStatDude :
break ;
default :
pXSprite - > Proximity = false ;
erased = true ;
break ;
}
}
return erased ;
}
2021-09-01 21:25:19 +00:00
//---------------------------------------------------------------------------
//
2021-08-29 07:44:08 +00:00
2021-09-01 21:25:19 +00:00
//
//---------------------------------------------------------------------------
2021-10-13 23:18:26 +00:00
void nnExtTriggerObject ( int objType , int objIndex , DBloodActor * objActor , int command )
2021-09-01 21:25:19 +00:00
{
switch ( objType )
{
2020-03-30 19:54:28 +00:00
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 :
2021-10-13 23:18:26 +00:00
if ( ! objActor | | ! objActor - > hasX ( ) ) break ;
trTriggerSprite ( objActor - > GetIndex ( ) , & objActor - > x ( ) , command ) ;
2020-03-30 19:54:28 +00:00
break ;
}
return ;
}
2021-09-01 21:25:19 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void nnExtResetGlobals ( )
{
2020-03-01 20:36:28 +00:00
gAllowTrueRandom = gEventRedirectsUsed = false ;
// reset counters
gProxySpritesCount = gSightSpritesCount = gPhysSpritesCount = gImpactSpritesCount = 0 ;
// fill arrays with negative values to avoid index 0 situation
2021-09-01 19:52:00 +00:00
memset ( gSightSpritesList , 0 , sizeof ( gSightSpritesList ) ) ;
memset ( gProxySpritesList , 0 , sizeof ( gProxySpritesList ) ) ;
memset ( gPhysSpritesList , 0 , sizeof ( gPhysSpritesList ) ) ;
memset ( gImpactSpritesList , 0 , sizeof ( gImpactSpritesList ) ) ;
2020-03-13 20:59:13 +00:00
// reset tracking conditions, if any
2021-09-01 21:25:19 +00:00
if ( gTrackingCondsCount > 0 )
{
for ( int i = 0 ; i < gTrackingCondsCount ; i + + )
{
2020-03-13 20:59:13 +00:00
TRCONDITION * pCond = & gCondition [ i ] ;
2021-11-13 12:49:26 +00:00
for ( unsigned k = 0 ; k < kMaxTracedObjects ; k + + )
2021-09-01 21:25:19 +00:00
{
2021-08-27 10:55:16 +00:00
pCond - > obj [ k ] . actor = nullptr ;
pCond - > obj [ k ] . index_ = pCond - > obj [ k ] . cmd = 0 ;
2020-03-13 20:59:13 +00:00
pCond - > obj [ k ] . type = - 1 ;
}
pCond - > length = 0 ;
}
gTrackingCondsCount = 0 ;
}
2021-07-19 21:15:26 +00:00
// clear sprite mass cache
2021-09-01 19:53:33 +00:00
for ( int i = 0 ; i < kMaxSprites ; i + + )
{
bloodActors [ i ] . spriteMass = { } ;
2021-07-19 21:15:26 +00:00
}
2020-03-01 20:36:28 +00:00
}
2021-09-01 21:25:19 +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.
2021-09-01 21:25:19 +00:00
if ( gGameOptions . nGameType = = 0 & & ! VanillaMode ( ) )
{
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.
2021-09-01 21:25:19 +00:00
for ( int i = kMaxRandomizeRetries ; i > = 0 ; i - - )
{
2020-02-07 19:47:43 +00:00
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-12-06 20:56:09 +00:00
}
2020-03-01 20:36:28 +00:00
2021-09-01 21:25:19 +00:00
for ( int i = 0 ; i < kMaxXSprites ; i + + )
{
2021-09-01 19:52:54 +00:00
auto actor = & bloodActors [ i ] ;
2021-08-28 12:14:18 +00:00
if ( actor - > s ( ) . statnum = = kStatFree | | ! actor - > hasX ( ) ) continue ;
2021-09-01 19:52:54 +00:00
XSPRITE * pXSprite = & actor - > x ( ) ;
spritetype * pSprite = & actor - > s ( ) ;
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-12-06 20:56:09 +00:00
if ( pXSprite - > command = = kCmdLink ) gEventRedirectsUsed = true ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
2021-08-27 17:12:22 +00:00
getSpriteMassBySize ( actor ) ; // create mass cache
2020-02-07 19:47:43 +00:00
break ;
2020-03-13 20:59:13 +00:00
case kModernCondition :
case kModernConditionFalse :
2021-10-13 21:58:04 +00:00
if ( ! pXSprite - > rxID & & pXSprite - > data1 > kCondGameMax ) condError ( actor , " \n The condition must have RX ID! \n SPRITE #%d " , actor - > GetIndex ( ) ) ;
2021-09-01 21:25:19 +00:00
else if ( ! pXSprite - > txID & & ! pSprite - > flags )
{
2020-10-11 10:38:17 +00:00
Printf ( PRINT_HIGH , " The condition must have TX ID or hitag to be set: RX ID %d, SPRITE #%d " , pXSprite - > rxID , pSprite - > index ) ;
2020-03-13 20:59:13 +00:00
}
break ;
2020-02-07 19:47:43 +00:00
}
// init after loading save file
2021-09-01 21:25:19 +00:00
if ( bSaveLoad )
{
2020-02-07 19:47:43 +00:00
// add in list of physics affected sprites
2021-09-01 21:25:19 +00:00
if ( pXSprite - > physAttr ! = 0 )
{
2021-08-28 12:14:18 +00:00
gPhysSpritesList [ gPhysSpritesCount + + ] = actor ; // add sprite index
2021-08-27 17:12:22 +00:00
getSpriteMassBySize ( actor ) ; // create mass cache
2020-02-07 19:47:43 +00:00
}
2021-09-01 21:25:19 +00:00
if ( pXSprite - > data3 ! = pXSprite - > sysData1 )
{
switch ( pSprite - > statnum )
{
2020-02-07 19:47:43 +00:00
case kStatDude :
2021-09-01 21:25:19 +00:00
switch ( pSprite - > type )
{
2020-02-07 19:47:43 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
pXSprite - > data3 = pXSprite - > sysData1 ; // move sndStartId back from sysData1 to data3
break ;
}
break ;
}
}
2021-09-01 21:25:19 +00:00
}
else
{
2020-05-22 16:28:03 +00:00
// auto set going On and going Off if both are empty
if ( pXSprite - > txID & & ! pXSprite - > triggerOn & & ! pXSprite - > triggerOff )
pXSprite - > triggerOn = pXSprite - > triggerOff = true ;
2020-04-09 18:51:25 +00:00
// copy custom start health to avoid overwrite by kThingBloodChunks
2021-09-01 21:29:54 +00:00
if ( actor - > IsDudeActor ( ) )
2020-04-04 19:48:23 +00:00
pXSprite - > sysData2 = pXSprite - > data4 ;
2020-04-09 18:51:25 +00:00
// check reserved statnums
2021-09-01 21:25:19 +00:00
if ( pSprite - > statnum > = kStatModernBase & & pSprite - > statnum < kStatModernMax )
{
2020-04-09 18:51:25 +00:00
bool sysStat = true ;
2021-09-01 21:25:19 +00:00
switch ( pSprite - > statnum )
{
2021-07-19 21:15:26 +00:00
case kStatModernStealthRegion :
sysStat = ( pSprite - > type ! = kModernStealthRegion ) ;
break ;
2020-04-09 18:51:25 +00:00
case kStatModernDudeTargetChanger :
2020-05-22 16:28:03 +00:00
sysStat = ( pSprite - > type ! = kModernDudeTargetChanger ) ;
2020-04-09 18:51:25 +00:00
break ;
case kStatModernCondition :
2020-05-22 16:28:03 +00:00
sysStat = ( pSprite - > type ! = kModernCondition & & pSprite - > type ! = kModernConditionFalse ) ;
2020-04-09 18:51:25 +00:00
break ;
case kStatModernEventRedirector :
2020-05-22 16:28:03 +00:00
sysStat = ( pSprite - > type ! = kModernRandomTX & & pSprite - > type ! = kModernSequentialTX ) ;
2020-04-09 18:51:25 +00:00
break ;
2021-07-19 21:15:26 +00:00
case kStatModernWindGen :
sysStat = ( pSprite - > type ! = kModernWindGenerator ) ;
break ;
2020-04-09 18:51:25 +00:00
case kStatModernPlayerLinker :
2020-05-05 18:50:14 +00:00
case kStatModernQavScene :
2020-05-22 16:28:03 +00:00
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 )
2021-09-01 21:29:54 +00:00
I_Error ( " Sprite statnum %d on sprite #%d is in a range of reserved (%d - %d)! " , pSprite - > statnum , actor - > GetIndex ( ) , kStatModernBase , kStatModernMax ) ;
2020-04-09 18:51:25 +00:00
}
2020-04-07 20:30:00 +00:00
2021-09-01 21:25:19 +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
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernEventRedirector ) ;
2020-03-13 20:59:13 +00:00
break ;
2020-02-07 19:47:43 +00:00
case kModernWindGenerator :
pSprite - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernWindGen ) ;
2020-02-07 19:47:43 +00:00
break ;
case kModernDudeTargetChanger :
case kModernObjDataAccumulator :
case kModernRandom :
case kModernRandom2 :
2021-07-19 21:15:26 +00:00
case kModernStealthRegion :
2020-02-07 19:47:43 +00:00
pSprite - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
pSprite - > cstat | = CSTAT_SPRITE_INVISIBLE ;
2021-09-01 21:25:19 +00:00
switch ( pSprite - > type )
{
2021-07-19 21:15:26 +00:00
// stealth regions for patrolling enemies
case kModernStealthRegion :
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernStealthRegion ) ;
2021-07-19 21:15:26 +00:00
break ;
2020-02-07 19:47:43 +00:00
// add statnum for faster dude searching
case kModernDudeTargetChanger :
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernDudeTargetChanger ) ;
2020-02-07 19:47:43 +00:00
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 ;
2021-07-19 21:15:26 +00:00
// remove kStatItem status from random item generators
2020-02-07 19:47:43 +00:00
case kModernRandom :
case kModernRandom2 :
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatDecoration ) ;
2021-07-19 21:15:26 +00:00
pXSprite - > sysData1 = pXSprite - > command ; // save the command so spawned item can inherit it
pXSprite - > command = kCmdLink ; // generator itself can't send commands
2020-02-07 19:47:43 +00:00
break ;
}
break ;
case kModernThingTNTProx :
pXSprite - > Proximity = true ;
break ;
2020-04-04 19:48:23 +00:00
case kDudeModernCustom :
2020-10-15 15:15:45 +00:00
{
2020-04-04 19:48:23 +00:00
if ( pXSprite - > txID < = 0 ) break ;
2021-09-01 21:29:54 +00:00
int found = 0 ;
BloodStatIterator it ( kStatDude ) ;
while ( DBloodActor * iactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-09-01 21:29:54 +00:00
XSPRITE * pXSpr = & iactor - > x ( ) ;
2020-04-04 19:48:23 +00:00
if ( pXSpr - > rxID ! = pXSprite - > txID ) continue ;
2020-10-11 10:22:36 +00:00
else if ( found ) I_Error ( " \n Custom dude (TX ID %d): \n Only one incarnation allowed per channel! " , pXSprite - > txID ) ;
2021-08-27 14:38:53 +00:00
ChangeActorStat ( iactor , kStatInactive ) ;
2020-04-04 19:48:23 +00:00
found + + ;
}
break ;
2020-10-15 15:15:45 +00:00
}
2020-05-22 16:28:03 +00:00
case kDudePodMother :
case kDudeTentacleMother :
pXSprite - > state = 1 ;
break ;
2020-04-04 19:48:23 +00:00
case kModernPlayerControl :
2021-09-01 21:25:19 +00:00
switch ( pXSprite - > command )
{
2020-05-05 18:50:14 +00:00
case kCmdLink :
2020-10-15 15:15:45 +00:00
{
2021-07-26 19:52:42 +00:00
if ( pXSprite - > data1 < 1 | | pXSprite - > data1 > kMaxPlayers )
2021-09-01 21:29:54 +00:00
I_Error ( " \n Player Control (SPRITE #%d): \n Player out of a range (data1 = %d) " , actor - > GetIndex ( ) , pXSprite - > data1 ) ;
2020-10-15 15:15:45 +00:00
2020-05-05 18:50:14 +00:00
//if (numplayers < pXSprite->data1)
2020-10-11 10:22:36 +00:00
//I_Error("\nPlayer Control (SPRITE #%d):\n There is no player #%d", pSprite->index, pXSprite->data1);
2020-05-05 18:50:14 +00:00
2020-05-22 16:28:03 +00:00
if ( pXSprite - > rxID & & pXSprite - > rxID ! = kChannelLevelStart )
2021-09-01 21:29:54 +00:00
I_Error ( " \n Player Control (SPRITE #%d) with Link command should have no RX ID! " , actor - > GetIndex ( ) ) ;
2020-05-05 18:50:14 +00:00
2020-05-22 16:28:03 +00:00
if ( pXSprite - > txID & & pXSprite - > txID < kChannelUser )
2021-09-01 21:29:54 +00:00
I_Error ( " \n Player Control (SPRITE #%d): \n TX ID should be in range of %d and %d! " , actor - > GetIndex ( ) , kChannelUser , kChannelMax ) ;
2020-04-04 19:48:23 +00:00
2020-05-22 16:28:03 +00:00
// only one linker per player allowed
2021-10-13 23:18:26 +00:00
BloodStatIterator it ( kStatModernPlayerLinker ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-13 23:18:26 +00:00
XSPRITE * pXCtrl = & iactor - > x ( ) ;
2020-05-22 16:28:03 +00:00
if ( pXSprite - > data1 = = pXCtrl - > data1 )
2021-10-13 23:18:26 +00:00
I_Error ( " \n Player Control (SPRITE #%d): \n Player %d already linked with different player control sprite #%d! " , actor - > GetIndex ( ) , pXSprite - > data1 , iactor - > GetIndex ( ) ) ;
2020-05-22 16:28:03 +00:00
}
2020-05-05 18:50:14 +00:00
pXSprite - > sysData1 = - 1 ;
pSprite - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernPlayerLinker ) ;
2020-05-22 16:28:03 +00:00
break ;
2020-10-15 15:15:45 +00:00
}
2020-05-05 18:50:14 +00:00
case 67 : // play qav animation
if ( pXSprite - > txID & & ! pXSprite - > waitTime ) pXSprite - > waitTime = 1 ;
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernQavScene ) ;
2020-05-05 18:50:14 +00:00
break ;
}
2020-04-04 19:48:23 +00:00
break ;
2020-03-01 20:36:28 +00:00
case kModernCondition :
2020-05-22 16:28:03 +00:00
case kModernConditionFalse :
2021-09-01 21:25:19 +00:00
if ( pXSprite - > busyTime > 0 )
{
if ( pXSprite - > waitTime > 0 )
{
2020-05-22 16:28:03 +00:00
pXSprite - > busyTime + = ClipHigh ( ( ( pXSprite - > waitTime * 120 ) / 10 ) , 4095 ) ; pXSprite - > waitTime = 0 ;
2020-10-11 10:38:17 +00:00
Printf ( PRINT_HIGH , " Summing busyTime and waitTime for tracking condition #%d, RX ID %d. Result = %d ticks " , pSprite - > index , pXSprite - > rxID , pXSprite - > busyTime ) ;
2020-05-22 16:28:03 +00:00
}
pXSprite - > busy = pXSprite - > busyTime ;
2020-03-30 19:54:28 +00:00
}
2020-05-22 16:28:03 +00:00
if ( pXSprite - > waitTime & & pXSprite - > command > = kCmdNumberic )
2021-10-13 21:58:04 +00:00
condError ( actor , " Delay is not available when using numberic commands (%d - %d) " , kCmdNumberic , 255 ) ;
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
2021-09-01 19:52:54 +00:00
pXSprite - > targetX = pXSprite - > targetY = pXSprite - > targetZ = pXSprite - > sysData2 = - 1 ;
actor - > SetTarget ( nullptr ) ;
2021-08-27 14:38:53 +00:00
ChangeActorStat ( actor , kStatModernCondition ) ;
2020-05-22 16:28:03 +00:00
int oldStat = pSprite - > cstat ; pSprite - > cstat = 0x30 ;
if ( oldStat & CSTAT_SPRITE_BLOCK )
pSprite - > cstat | = CSTAT_SPRITE_BLOCK ;
if ( oldStat & 0x2000 ) pSprite - > cstat | = 0x2000 ;
else if ( oldStat & 0x4000 ) pSprite - > cstat | = 0x4000 ;
2020-03-13 20:59:13 +00:00
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...
2021-09-01 21:25:19 +00:00
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 :
2021-09-01 21:29:54 +00:00
gProxySpritesList [ gProxySpritesCount + + ] = actor ;
2020-02-07 19:47:43 +00:00
if ( gProxySpritesCount = = kMaxSuperXSprites )
2020-10-11 10:22:36 +00:00
I_Error ( " Max (%d) *additional* Proximity sprites reached! " , kMaxSuperXSprites ) ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
}
2021-07-19 21:15:26 +00:00
// make Sight, Screen, Aim flags work not just for dudes and things...
2021-09-01 21:25:19 +00:00
if ( ( pXSprite - > Sight | | pXSprite - > unused3 ) & & 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 :
2021-09-01 21:29:54 +00:00
gSightSpritesList [ gSightSpritesCount + + ] = actor ;
2020-03-01 20:36:28 +00:00
if ( gSightSpritesCount = = kMaxSuperXSprites )
2020-10-11 10:22:36 +00:00
I_Error ( " Max (%d) Sight sprites reached! " , kMaxSuperXSprites ) ;
2020-03-01 20:36:28 +00:00
break ;
}
}
// make Impact flag work for sprites that affected by explosions...
2021-09-01 21:25:19 +00:00
if ( pXSprite - > Impact & & gImpactSpritesCount < 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 :
2021-09-01 21:29:54 +00:00
gImpactSpritesList [ gImpactSpritesCount + + ] = actor ;
2020-03-01 20:36:28 +00:00
if ( gImpactSpritesCount = = kMaxSuperXSprites )
2020-10-11 10:22:36 +00:00
I_Error ( " Max (%d) *additional* Impact sprites reached! " , kMaxSuperXSprites ) ;
2020-03-01 20:36:28 +00:00
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
// collect objects for tracking conditions
2021-09-01 21:29:54 +00:00
BloodStatIterator it ( kStatModernCondition ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-08-27 15:09:55 +00:00
spritetype * pSprite = & iactor - > s ( ) ;
XSPRITE * pXSprite = & iactor - > x ( ) ;
2020-05-19 19:42:29 +00:00
2021-01-07 12:33:20 +00:00
if ( pXSprite - > busyTime < = 0 | | pXSprite - > isTriggered ) continue ;
2020-05-19 19:42:29 +00:00
else if ( gTrackingCondsCount > = kMaxTrackingConditions )
2020-10-11 10:22:36 +00:00
I_Error ( " \n Max (%d) tracking conditions reached! " , kMaxTrackingConditions ) ;
2020-05-19 19:42:29 +00:00
int count = 0 ;
TRCONDITION * pCond = & gCondition [ gTrackingCondsCount ] ;
2021-09-01 21:25:19 +00:00
for ( int i = 0 ; i < kMaxXSprites ; i + + )
{
2021-08-28 12:14:18 +00:00
auto actor = & bloodActors [ i ] ;
2021-10-13 23:18:26 +00:00
if ( actor - > s ( ) . statnum = = kStatFree | | ! actor - > hasX ( ) | | actor - > x ( ) . txID ! = pXSprite - > rxID | | actor = = iactor )
2020-05-19 19:42:29 +00:00
continue ;
2021-08-28 12:14:18 +00:00
XSPRITE * pXSpr = & actor - > x ( ) ;
spritetype * pSpr = & actor - > s ( ) ;
2020-05-19 19:42:29 +00:00
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 )
2021-10-13 21:58:04 +00:00
condError ( iactor , " Tracking condition always must be first in condition sequence! " ) ;
2020-05-19 19:42:29 +00:00
if ( count > = kMaxTracedObjects )
2021-10-13 21:58:04 +00:00
condError ( iactor , " Max(%d) objects to track reached for condition #%d, RXID: %d! " ) ;
2020-05-19 19:42:29 +00:00
pCond - > obj [ count ] . type = OBJ_SPRITE ;
2021-08-27 10:55:16 +00:00
pCond - > obj [ count ] . index_ = 0 ;
pCond - > obj [ count ] . actor = & bloodActors [ index ] ;
2020-05-19 19:42:29 +00:00
pCond - > obj [ count + + ] . cmd = cmd ;
}
2021-09-01 21:29:54 +00:00
for ( int i = 0 ; i < kMaxXSectors ; i + + )
{
2020-05-19 19:42:29 +00:00
if ( ! sectRangeIsFine ( xsector [ i ] . reference ) | | xsector [ i ] . txID ! = pXSprite - > rxID ) continue ;
else if ( count > = kMaxTracedObjects )
2021-10-13 21:58:04 +00:00
condError ( iactor , " Max(%d) objects to track reached for condition #%d, RXID: %d! " ) ;
2020-05-19 19:42:29 +00:00
pCond - > obj [ count ] . type = OBJ_SECTOR ;
2021-08-27 10:55:16 +00:00
pCond - > obj [ count ] . actor = nullptr ;
pCond - > obj [ count ] . index_ = xsector [ i ] . reference ;
2020-05-19 19:42:29 +00:00
pCond - > obj [ count + + ] . cmd = xsector [ i ] . command ;
}
2021-09-01 21:29:54 +00:00
for ( int i = 0 ; i < kMaxXWalls ; i + + )
{
2020-05-19 19:42:29 +00:00
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 )
2021-10-13 21:58:04 +00:00
condError ( iactor , " Max(%d) objects to track reached for condition #%d, RXID: %d! " ) ;
2020-05-19 19:42:29 +00:00
pCond - > obj [ count ] . type = OBJ_WALL ;
2021-08-27 10:55:16 +00:00
pCond - > obj [ count ] . index_ = xwall [ i ] . reference ;
pCond - > obj [ count ] . actor = nullptr ;
2020-05-19 19:42:29 +00:00
pCond - > obj [ count + + ] . cmd = xwall [ i ] . command ;
}
2021-07-19 21:15:26 +00:00
if ( pXSprite - > data1 > kCondGameMax & & count = = 0 )
Printf ( PRINT_HIGH , " No objects to track found for condition #%d, RXID: %d! " , pSprite - > index , pXSprite - > rxID ) ;
2020-05-19 19:42:29 +00:00
pCond - > length = count ;
2021-10-12 21:52:54 +00:00
pCond - > actor = iactor ;
2020-05-19 19:42:29 +00:00
gTrackingCondsCount + + ;
}
2020-02-07 19:47:43 +00:00
}
// The following functions required for random event features
2021-08-27 15:09:55 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
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 ) ;
}
2021-08-27 15:14:55 +00:00
static int GetDataVal ( DBloodActor * actor , int data )
2021-08-27 15:09:55 +00:00
{
if ( ! actor - > hasX ( ) ) return - 1 ;
2020-05-22 16:28:03 +00:00
switch ( data ) {
2021-08-27 15:09:55 +00:00
case 0 : return actor - > x ( ) . data1 ;
case 1 : return actor - > x ( ) . data2 ;
case 2 : return actor - > x ( ) . data3 ;
case 3 : return actor - > x ( ) . data4 ;
2020-02-07 19:47:43 +00:00
}
return - 1 ;
}
2021-08-27 15:09:55 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// tries to get random data field of sprite
2021-08-27 15:09:55 +00:00
//
//---------------------------------------------------------------------------
2021-08-27 15:14:55 +00:00
static int randomGetDataValue ( DBloodActor * actor , int randType )
{
if ( actor = = NULL | | ! actor - > hasX ( ) ) return - 1 ;
auto pXSprite = & actor - > x ( ) ;
2020-02-07 19:47:43 +00:00
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.
2021-08-27 15:14:55 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
2020-02-07 19:47:43 +00:00
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 ;
}
}
2021-08-27 15:14:55 +00:00
if ( bad < 3 )
{
2020-02-07 19:47:43 +00:00
// try randomize few times
2021-08-27 15:14:55 +00:00
while ( maxRetries > 0 )
{
2020-02-07 19:47:43 +00:00
random = nnExtRandom ( 0 , 3 ) ;
if ( rData [ random ] > 0 ) return rData [ random ] ;
else maxRetries - - ;
}
}
return - 1 ;
}
2021-08-27 15:41:23 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function drops random item using random pickup generator(s)
2021-08-27 15:41:23 +00:00
//
//---------------------------------------------------------------------------
2020-12-05 22:49:51 +00:00
2021-08-27 15:41:23 +00:00
static DBloodActor * randomDropPickupObject ( DBloodActor * sourceactor , int prevItem )
{
DBloodActor * spawned = nullptr ;
int selected = - 1 ;
int maxRetries = 9 ;
2021-08-27 15:14:55 +00:00
if ( sourceactor - > hasX ( ) )
{
2021-08-27 15:41:23 +00:00
auto pSource = & sourceactor - > s ( ) ;
2021-08-27 15:14:55 +00:00
XSPRITE * pXSource = & sourceactor - > x ( ) ;
while ( ( selected = randomGetDataValue ( sourceactor , kRandomizeItem ) ) = = prevItem ) if ( maxRetries - - < = 0 ) break ;
2020-12-05 22:49:51 +00:00
if ( selected > 0 )
{
2021-08-27 15:41:23 +00:00
spawned = actDropObject ( sourceactor , selected ) ;
if ( spawned )
{
auto pSprite2 = & spawned - > s ( ) ;
2020-02-07 19:47:43 +00:00
2021-05-12 00:00:06 +00:00
pXSource - > dropMsg = uint8_t ( pSprite2 - > type ) ; // store dropped item type in dropMsg
2020-02-07 19:47:43 +00:00
pSprite2 - > x = pSource - > x ;
pSprite2 - > y = pSource - > y ;
pSprite2 - > z = pSource - > z ;
2021-08-27 20:13:17 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) & & ( pXSource - > txID > 0 | | ( pXSource - > txID ! = 3 & & pXSource - > lockMsg > 0 ) ) )
2021-08-27 15:41:23 +00:00
{
2021-08-27 20:13:17 +00:00
spawned - > addX ( ) ;
2021-08-27 15:41:23 +00:00
XSPRITE * pXSprite2 = & spawned - > x ( ) ;
2020-02-07 19:47:43 +00:00
// inherit spawn sprite trigger settings, so designer can send command when item picked up.
pXSprite2 - > txID = pXSource - > txID ;
2021-07-19 21:15:26 +00:00
pXSprite2 - > command = pXSource - > sysData1 ;
2020-02-07 19:47:43 +00:00
pXSprite2 - > triggerOn = pXSource - > triggerOn ;
pXSprite2 - > triggerOff = pXSource - > triggerOff ;
pXSprite2 - > Pickup = true ;
}
}
}
}
2021-08-27 15:41:23 +00:00
return spawned ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 15:41:23 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function spawns random dude using dudeSpawn
2021-08-27 15:41:23 +00:00
//
//---------------------------------------------------------------------------
DBloodActor * randomSpawnDude ( DBloodActor * sourceactor , DBloodActor * origin , int a3 , int a4 )
2020-12-05 17:32:49 +00:00
{
2021-08-27 15:41:23 +00:00
DBloodActor * spawned = NULL ; int selected = - 1 ;
2021-07-19 21:15:26 +00:00
2021-08-27 15:14:55 +00:00
if ( ( selected = randomGetDataValue ( sourceactor , kRandomizeDude ) ) > 0 )
2021-08-27 15:41:23 +00:00
spawned = nnExtSpawnDude ( sourceactor , origin , selected , a3 , 0 ) ;
2021-07-19 21:15:26 +00:00
2021-08-27 15:41:23 +00:00
return spawned ;
2020-02-07 19:47:43 +00:00
}
2021-07-19 21:15:26 +00:00
2021-09-01 21:54:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-12 21:42:18 +00:00
static void windGenDoVerticalWind ( int factor , int nSector )
{
int val , maxZ , zdiff ; bool maxZfound = false ;
2021-07-19 21:15:26 +00:00
// find maxz marker first
2021-10-12 21:42:18 +00:00
BloodSectIterator it ( nSector ) ;
while ( auto actor = it . Next ( ) )
{
auto sp = & actor - > s ( ) ;
if ( sp - > type = = kMarkerOn & & sp - > statnum ! = kStatMarker )
{
maxZ = sp - > z ;
2021-07-19 21:15:26 +00:00
maxZfound = true ;
break ;
}
}
2021-10-12 21:42:18 +00:00
it . Reset ( nSector ) ;
while ( auto actor = it . Next ( ) )
{
auto pSpr = & actor - > s ( ) ;
switch ( pSpr - > statnum )
{
2021-07-19 21:15:26 +00:00
case kStatFree :
continue ;
case kStatFX :
2021-10-12 21:42:18 +00:00
if ( actor - > zvel ( ) ) break ;
2021-07-19 21:15:26 +00:00
continue ;
case kStatThing :
case kStatDude :
if ( pSpr - > flags & kPhysGravity ) break ;
continue ;
default :
2021-10-12 21:42:18 +00:00
if ( actor - > hasX ( ) & & actor - > x ( ) . physAttr & kPhysGravity ) break ;
2021-07-19 21:15:26 +00:00
continue ;
}
2021-10-12 21:42:18 +00:00
if ( maxZfound & & pSpr - > z < = maxZ )
{
2021-07-19 21:15:26 +00:00
zdiff = pSpr - > z - maxZ ;
2021-10-12 21:42:18 +00:00
if ( actor - > zvel ( ) < 0 ) actor - > zvel ( ) + = MulScale ( actor - > zvel ( ) > > 4 , zdiff , 16 ) ;
2021-07-19 21:15:26 +00:00
continue ;
}
2021-10-12 21:42:18 +00:00
val = - MulScale ( factor * 64 , 0x10000 , 16 ) ;
2021-10-12 22:42:45 +00:00
if ( actor - > zvel ( ) > = 0 ) actor - > zvel ( ) + = val ;
else actor - > zvel ( ) = val ;
2021-07-19 21:15:26 +00:00
2021-10-12 22:42:45 +00:00
pSpr - > z + = actor - > zvel ( ) > > 12 ;
2021-07-19 21:15:26 +00:00
}
}
2021-09-01 21:54:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-02-07 19:47:43 +00:00
2021-09-01 21:54:40 +00:00
void nnExtProcessSuperSprites ( )
{
2020-05-22 16:28:03 +00:00
// process tracking conditions
2021-08-27 17:12:22 +00:00
if ( gTrackingCondsCount > 0 )
{
for ( int i = 0 ; i < gTrackingCondsCount ; i + + )
{
2021-11-13 12:49:26 +00:00
TRCONDITION const * pCond = & gCondition [ i ] ;
2021-10-12 21:52:54 +00:00
XSPRITE * pXCond = & pCond - > actor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXCond - > locked | | pXCond - > isTriggered | | + + pXCond - > busy < pXCond - > busyTime )
continue ;
2021-10-12 21:48:24 +00:00
if ( pXCond - > data1 > = kCondGameBase & & pXCond - > data1 < kCondGameMax )
{
2021-07-19 21:15:26 +00:00
EVENT evn ;
2021-08-27 10:03:11 +00:00
evn . index_ = 0 ;
2021-10-12 21:52:54 +00:00
evn . actor = pCond - > actor ;
2021-08-27 10:03:11 +00:00
evn . cmd = ( int8_t ) pXCond - > command ;
evn . type = OBJ_SPRITE ;
evn . funcID = kCallbackMax ;
2021-08-28 22:56:14 +00:00
useCondition ( pCond - > actor , evn ) ;
2021-09-01 21:54:40 +00:00
}
else if ( pCond - > length > 0 )
{
2020-05-22 16:28:03 +00:00
pXCond - > busy = 0 ;
2021-09-01 21:54:40 +00:00
for ( unsigned k = 0 ; k < pCond - > length ; k + + )
{
2020-05-22 16:28:03 +00:00
EVENT evn ;
2021-08-27 10:55:16 +00:00
evn . actor = pCond - > obj [ k ] . actor ;
evn . index_ = pCond - > obj [ k ] . index_ ;
2021-08-27 10:03:11 +00:00
evn . cmd = pCond - > obj [ k ] . cmd ;
2021-09-01 21:54:40 +00:00
evn . type = pCond - > obj [ k ] . type ;
2021-08-27 10:03:11 +00:00
evn . funcID = kCallbackMax ;
2021-08-28 22:56:14 +00:00
useCondition ( pCond - > actor , evn ) ;
2020-05-22 16:28:03 +00:00
}
}
2021-07-19 21:15:26 +00:00
2020-05-22 16:28:03 +00:00
}
}
2021-07-19 21:15:26 +00:00
// process floor oriented kModernWindGenerator to create a vertical wind in the sectors
2021-10-12 21:52:54 +00:00
BloodStatIterator it ( kStatModernWindGen ) ;
while ( auto windactor = it . Next ( ) )
{
spritetype * pWind = & windactor - > s ( ) ;
if ( ! ( pWind - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) | | pWind - > statnum > = kMaxStatus | | ! windactor - > hasX ( ) )
2021-07-19 21:15:26 +00:00
continue ;
2021-10-12 21:52:54 +00:00
XSPRITE * pXWind = & windactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( ! pXWind - > state | | pXWind - > locked )
continue ;
int j , rx ;
bool fWindAlways = ( pWind - > flags & kModernTypeFlag1 ) ;
if ( pXWind - > txID ) {
2021-09-01 21:54:40 +00:00
2021-07-19 21:15:26 +00:00
rx = pXWind - > txID ;
2021-09-01 21:54:40 +00:00
for ( j = bucketHead [ rx ] ; j < bucketHead [ rx + 1 ] ; j + + )
{
2021-07-19 21:15:26 +00:00
if ( rxBucket [ j ] . type ! = OBJ_SECTOR )
continue ;
2021-10-12 21:42:18 +00:00
int index = rxBucket [ j ] . rxindex ;
XSECTOR * pXSector = & xsector [ sector [ index ] . extra ] ;
2021-07-19 21:15:26 +00:00
if ( ( ! pXSector - > locked ) & & ( fWindAlways | | pXSector - > windAlways | | pXSector - > busy ) )
2021-10-12 21:42:18 +00:00
windGenDoVerticalWind ( pXWind - > sysData2 , index ) ;
2021-07-19 21:15:26 +00:00
}
2021-09-05 18:10:40 +00:00
DBloodActor * pXRedir = nullptr ; // check redirected TX buckets
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , 0 , windactor , pXRedir , & rx ) ) ! = nullptr )
2021-09-01 21:54:40 +00:00
{
for ( j = bucketHead [ rx ] ; j < bucketHead [ rx + 1 ] ; j + + )
{
2021-07-19 21:15:26 +00:00
if ( rxBucket [ j ] . type ! = OBJ_SECTOR )
continue ;
2021-10-12 21:42:18 +00:00
int index = rxBucket [ j ] . rxindex ;
XSECTOR * pXSector = & xsector [ sector [ index ] . extra ] ;
2021-07-19 21:15:26 +00:00
if ( ( ! pXSector - > locked ) & & ( fWindAlways | | pXSector - > windAlways | | pXSector - > busy ) )
2021-10-12 21:42:18 +00:00
windGenDoVerticalWind ( pXWind - > sysData2 , index ) ;
2021-07-19 21:15:26 +00:00
}
}
2021-09-01 21:54:40 +00:00
}
else if ( sectRangeIsFine ( pWind - > sectnum ) )
{
2021-07-19 21:15:26 +00:00
sectortype * pSect = & sector [ pWind - > sectnum ] ;
XSECTOR * pXSector = ( pSect - > extra > 0 ) ? & xsector [ pSect - > extra ] : NULL ;
if ( ( fWindAlways ) | | ( pXSector & & ! pXSector - > locked & & ( pXSector - > windAlways | | pXSector - > busy ) ) )
2021-10-12 21:42:18 +00:00
windGenDoVerticalWind ( pXWind - > sysData2 , pWind - > sectnum ) ;
2021-07-19 21:15:26 +00:00
}
}
2020-02-07 19:47:43 +00:00
// process additional proximity sprites
2021-09-01 21:54:40 +00:00
if ( gProxySpritesCount > 0 )
{
for ( int i = 0 ; i < gProxySpritesCount ; i + + )
{
2021-09-01 19:52:00 +00:00
if ( ! gProxySpritesList [ i ] | | ! gProxySpritesList [ i ] - > hasX ( ) ) continue ;
2020-02-07 19:47:43 +00:00
2021-09-01 19:52:00 +00:00
auto const pProxSpr = & gProxySpritesList [ i ] - > s ( ) ;
XSPRITE * pXProxSpr = & gProxySpritesList [ i ] - > x ( ) ;
if ( ! pXProxSpr - > Proximity | | ( ! pXProxSpr - > Interrutable & & pXProxSpr - > state ! = pXProxSpr - > restState ) | | pXProxSpr - > locked = = 1
| | pXProxSpr - > isTriggered ) continue ; // don't process locked or triggered sprites
2020-02-07 19:47:43 +00:00
2021-10-12 21:48:24 +00:00
int okDist = ( gProxySpritesList [ i ] - > IsDudeActor ( ) ) ? 96 : ClipLow ( pProxSpr - > clipdist * 3 , 32 ) ;
2021-09-01 19:51:39 +00:00
int x = pProxSpr - > x ;
int y = pProxSpr - > y ;
int z = pProxSpr - > z ;
int sectnum = pProxSpr - > sectnum ;
2020-02-07 19:47:43 +00:00
2021-09-01 21:54:40 +00:00
if ( ! pXProxSpr - > DudeLockout )
{
2021-10-12 21:52:54 +00:00
BloodStatIterator it ( kStatDude ) ;
while ( auto affected = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-12 21:52:54 +00:00
if ( ! affected - > hasX ( ) | | affected - > x ( ) . health < = 0 ) continue ;
else if ( CheckProximity ( & affected - > s ( ) , x , y , z , sectnum , okDist ) )
{
2021-09-01 19:52:00 +00:00
trTriggerSprite ( gProxySpritesList [ i ] , kCmdSpriteProximity ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
2021-09-01 21:54:40 +00:00
}
else
{
for ( int a = connecthead ; a > = 0 ; a = connectpoint2 [ a ] )
{
2021-07-19 21:15:26 +00:00
PLAYER * pPlayer = & gPlayer [ a ] ;
2021-10-12 21:48:24 +00:00
if ( ! pPlayer | | ! pPlayer - > actor ( ) - > hasX ( ) | | pPlayer - > pXSprite - > health < = 0 )
2021-07-19 21:15:26 +00:00
continue ;
2021-10-12 21:48:24 +00:00
if ( pPlayer - > pXSprite - > health > 0 & & CheckProximity ( gPlayer - > pSprite , x , y , z , sectnum , okDist ) )
{
2021-09-01 19:52:00 +00:00
trTriggerSprite ( gProxySpritesList [ i ] , kCmdSpriteProximity ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
}
}
}
// process sight sprites (for players only)
2021-09-01 21:54:40 +00:00
if ( gSightSpritesCount > 0 )
{
for ( int i = 0 ; i < gSightSpritesCount ; i + + )
{
2021-09-01 19:52:00 +00:00
if ( ! gSightSpritesList [ i ] | | ! gSightSpritesList [ i ] - > hasX ( ) ) continue ;
auto const pSightSpr = & gSightSpritesList [ i ] - > s ( ) ;
XSPRITE * pXSightSpr = & gSightSpritesList [ i ] - > x ( ) ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
if ( ( ! pXSightSpr - > Interrutable & & pXSightSpr - > state ! = pXSightSpr - > restState ) | | pXSightSpr - > locked = = 1 | |
2020-02-07 19:47:43 +00:00
pXSightSpr - > isTriggered ) continue ; // don't process locked or triggered sprites
2021-09-01 19:52:00 +00:00
int index = pSightSpr - > index ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
// sprite is drawn for one of players
2021-09-01 19:52:00 +00:00
if ( ( pXSightSpr - > unused3 & kTriggerSpriteScreen ) & & ( gSightSpritesList [ i ] - > s ( ) . cstat2 & CSTAT2_SPRITE_MAPPED ) )
2021-10-13 23:15:05 +00:00
{
2021-10-12 21:52:54 +00:00
trTriggerSprite ( gSightSpritesList [ i ] , kCmdSpriteSight ) ;
2021-09-01 19:52:00 +00:00
gSightSpritesList [ i ] - > s ( ) . cstat2 & = ~ CSTAT2_SPRITE_MAPPED ;
2021-07-19 21:15:26 +00:00
continue ;
}
2021-09-01 19:52:00 +00:00
int x = pSightSpr - > x ;
int y = pSightSpr - > y ;
int z = pSightSpr - > z ;
int sectnum = pSightSpr - > sectnum ;
2021-07-19 21:15:26 +00:00
int ztop2 , zbot2 ;
2021-10-12 21:48:24 +00:00
for ( int a = connecthead ; a > = 0 ; a = connectpoint2 [ a ] )
{
2021-07-19 21:15:26 +00:00
PLAYER * pPlayer = & gPlayer [ a ] ;
2021-10-12 21:48:24 +00:00
if ( ! pPlayer | | ! pPlayer - > actor ( ) - > hasX ( ) | | pPlayer - > pXSprite - > health < = 0 )
2021-07-19 21:15:26 +00:00
continue ;
spritetype * pPlaySprite = pPlayer - > pSprite ;
GetSpriteExtents ( pPlaySprite , & ztop2 , & zbot2 ) ;
2021-09-01 21:54:40 +00:00
if ( cansee ( x , y , z , sectnum , pPlaySprite - > x , pPlaySprite - > y , ztop2 , pPlaySprite - > sectnum ) )
{
if ( pXSightSpr - > Sight )
{
2021-09-01 19:52:00 +00:00
trTriggerSprite ( gSightSpritesList [ i ] , kCmdSpriteSight ) ;
2021-07-19 21:15:26 +00:00
break ;
}
2021-09-01 21:54:40 +00:00
if ( pXSightSpr - > unused3 & kTriggerSpriteAim )
{
2021-10-13 18:26:52 +00:00
bool vector = ( pSightSpr - > cstat & CSTAT_SPRITE_BLOCK_HITSCAN ) ;
2021-07-19 21:15:26 +00:00
if ( ! vector )
2021-10-13 18:26:52 +00:00
pSightSpr - > cstat | = CSTAT_SPRITE_BLOCK_HITSCAN ;
2021-07-19 21:15:26 +00:00
HitScan ( pPlaySprite , pPlayer - > zWeapon , pPlayer - > aim . dx , pPlayer - > aim . dy , pPlayer - > aim . dz , CLIPMASK0 | CLIPMASK1 , 0 ) ;
2021-09-01 21:54:40 +00:00
2021-07-19 21:15:26 +00:00
//VectorScan(pPlaySprite, 0, pPlayer->zWeapon, pPlayer->aim.dx, pPlayer->aim.dy, pPlayer->aim.dz, 0, 1);
if ( ! vector )
2021-10-13 18:26:52 +00:00
pSightSpr - > cstat & = ~ CSTAT_SPRITE_BLOCK_HITSCAN ;
2021-07-19 21:15:26 +00:00
2021-10-13 18:26:52 +00:00
if ( gHitInfo . hitactor = = gSightSpritesList [ i ] )
2021-09-01 21:54:40 +00:00
{
2021-10-13 18:26:52 +00:00
trTriggerSprite ( gHitInfo . hitactor , kCmdSpriteSight ) ;
2021-07-19 21:15:26 +00:00
break ;
}
}
2020-02-07 19:47:43 +00:00
}
}
}
}
// process Debris sprites for movement
2021-09-01 21:54:40 +00:00
if ( gPhysSpritesCount > 0 )
{
for ( int i = 0 ; i < gPhysSpritesCount ; i + + )
{
2021-09-01 19:52:00 +00:00
auto debrisactor = gPhysSpritesList [ i ] ;
if ( debrisactor = = nullptr | | ! debrisactor - > hasX ( ) ) continue ;
auto const pDebris = & debrisactor - > s ( ) ;
2021-09-01 21:54:40 +00:00
if ( pDebris - > statnum = = kStatFree | | ( pDebris - > flags & kHitagFree ) ! = 0 )
{
2021-09-01 19:52:00 +00:00
gPhysSpritesList [ i ] = nullptr ;
2020-02-07 19:47:43 +00:00
continue ;
}
2021-09-01 19:52:00 +00:00
XSPRITE * pXDebris = & debrisactor - > x ( ) ;
2021-09-01 21:54:40 +00:00
if ( ! ( pXDebris - > physAttr & kPhysMove ) & & ! ( pXDebris - > physAttr & kPhysGravity ) )
{
2021-09-01 19:52:00 +00:00
gPhysSpritesList [ i ] = nullptr ;
2020-02-07 19:47:43 +00:00
continue ;
}
2021-08-27 15:04:34 +00:00
int nDebris = pDebris - > index ;
2021-01-07 12:33:20 +00:00
2021-09-01 21:54:40 +00:00
XSECTOR * pXSector = ( sector [ pDebris - > sectnum ] . extra > = 0 ) ? & xsector [ sector [ pDebris - > sectnum ] . extra ] : nullptr ;
2021-08-27 15:04:34 +00:00
viewBackupSpriteLoc ( nDebris , pDebris ) ;
2021-09-01 21:54:40 +00:00
2021-01-07 12:33:20 +00:00
bool uwater = false ;
2021-09-01 19:53:33 +00:00
int mass = debrisactor - > spriteMass . mass ;
int airVel = debrisactor - > spriteMass . airVel ;
2021-01-07 12:33:20 +00:00
2021-08-27 15:04:34 +00:00
int top , bottom ;
2021-10-12 21:48:24 +00:00
GetActorExtents ( debrisactor , & top , & bottom ) ;
2021-01-07 12:33:20 +00:00
2021-09-01 21:54:40 +00:00
if ( pXSector ! = nullptr )
{
2021-01-07 12:33:20 +00:00
if ( ( uwater = pXSector - > Underwater ) ! = 0 ) airVel < < = 6 ;
2021-09-01 21:54:40 +00:00
if ( pXSector - > panVel ! = 0 & & getflorzofslope ( pDebris - > sectnum , pDebris - > x , pDebris - > y ) < = bottom )
{
2021-01-07 12:33:20 +00:00
int angle = pXSector - > panAngle ; int speed = 0 ;
2021-09-01 21:54:40 +00:00
if ( pXSector - > panAlways | | pXSector - > state | | pXSector - > busy )
{
2021-08-27 15:04:34 +00:00
speed = pXSector - > panVel < < 9 ;
if ( ! pXSector - > panAlways & & pXSector - > busy )
speed = MulScale ( speed , pXSector - > busy , 16 ) ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 15:04:34 +00:00
if ( sector [ pDebris - > sectnum ] . floorstat & 64 )
angle = ( angle + GetWallAngle ( sector [ pDebris - > sectnum ] . wallptr ) + 512 ) & 2047 ;
int dx = MulScale ( speed , Cos ( angle ) , 30 ) ;
int dy = MulScale ( speed , Sin ( angle ) , 30 ) ;
debrisactor - > xvel ( ) + = dx ;
debrisactor - > yvel ( ) + = dy ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 15:04:34 +00:00
}
2021-10-03 10:31:19 +00:00
actAirDrag ( debrisactor , airVel ) ;
2020-02-07 19:47:43 +00:00
2021-09-01 21:54:40 +00:00
if ( pXDebris - > physAttr & kPhysDebrisTouch )
{
2021-01-07 12:33:20 +00:00
PLAYER * pPlayer = NULL ;
2021-09-01 21:54:40 +00:00
for ( int a = connecthead ; a ! = - 1 ; a = connectpoint2 [ a ] )
{
2021-01-07 12:33:20 +00:00
pPlayer = & gPlayer [ a ] ;
2021-10-12 21:48:24 +00:00
auto pact = pPlayer - > actor ( ) ;
2021-09-02 18:23:51 +00:00
if ( pact - > hit ( ) . hit . type = = kHitSprite & & pact - > hit ( ) . hit . index = = nDebris )
2021-10-12 21:48:24 +00:00
{
int nSpeed = approxDist ( pact - > xvel ( ) , pact - > yvel ( ) ) ;
2021-05-03 22:22:35 +00:00
nSpeed = ClipLow ( nSpeed - MulScale ( nSpeed , mass , 6 ) , 0x9000 - ( mass < < 3 ) ) ;
2020-02-07 19:47:43 +00:00
2021-08-27 15:04:34 +00:00
debrisactor - > xvel ( ) + = MulScale ( nSpeed , Cos ( pPlayer - > pSprite - > ang ) , 30 ) ;
debrisactor - > yvel ( ) + = MulScale ( nSpeed , Sin ( pPlayer - > pSprite - > ang ) , 30 ) ;
2020-02-07 19:47:43 +00:00
2021-10-12 21:48:24 +00:00
debrisactor - > hit ( ) . hit = pPlayer - > pSprite - > index | 0xc000 ;
2021-01-07 12:33:20 +00:00
}
}
}
if ( pXDebris - > physAttr & kPhysGravity ) pXDebris - > physAttr | = kPhysFalling ;
2021-08-27 15:04:34 +00:00
if ( ( pXDebris - > physAttr & kPhysFalling ) | | debrisactor - > xvel ( ) | | debrisactor - > yvel ( ) | | debrisactor - > zvel ( ) | | velFloor [ pDebris - > sectnum ] | | velCeil [ pDebris - > sectnum ] )
2020-02-07 19:47:43 +00:00
debrisMove ( i ) ;
2021-08-27 15:04:34 +00:00
if ( debrisactor - > xvel ( ) | | debrisactor - > yvel ( ) )
pXDebris - > goalAng = getangle ( debrisactor - > xvel ( ) , debrisactor - > yvel ( ) ) & 2047 ;
2021-01-07 12:33:20 +00:00
int ang = pDebris - > ang & 2047 ;
2021-08-27 08:18:33 +00:00
if ( ( uwater = spriteIsUnderwater ( debrisactor ) ) = = false ) evKillActor ( debrisactor , kCallbackEnemeyBubble ) ;
2021-09-01 21:54:40 +00:00
else if ( Chance ( 0x1000 - mass ) )
{
2021-10-12 22:46:21 +00:00
if ( debrisactor - > zvel ( ) > 0x100 ) debrisBubble ( debrisactor ) ;
2021-09-01 21:54:40 +00:00
if ( ang = = pXDebris - > goalAng )
{
2021-01-07 12:33:20 +00:00
pXDebris - > goalAng = ( pDebris - > ang + Random3 ( kAng60 ) ) & 2047 ;
2021-10-12 22:46:21 +00:00
debrisBubble ( debrisactor ) ;
2021-01-07 12:33:20 +00:00
}
}
2021-08-27 15:04:34 +00:00
int angStep = ClipLow ( mulscale8 ( 1 , ( ( abs ( debrisactor - > xvel ( ) ) + abs ( debrisactor - > yvel ( ) ) ) > > 5 ) ) , ( uwater ) ? 1 : 0 ) ;
2021-01-07 12:33:20 +00:00
if ( ang < pXDebris - > goalAng ) pDebris - > ang = ClipHigh ( ang + angStep , pXDebris - > goalAng ) ;
else if ( ang > pXDebris - > goalAng ) pDebris - > ang = ClipLow ( ang - angStep , pXDebris - > goalAng ) ;
int nSector = pDebris - > sectnum ;
int cz = getceilzofslope ( nSector , pDebris - > x , pDebris - > y ) ;
int fz = getflorzofslope ( nSector , pDebris - > x , pDebris - > y ) ;
2021-09-01 21:54:40 +00:00
2021-10-12 21:48:24 +00:00
GetActorExtents ( debrisactor , & top , & bottom ) ;
2021-01-07 12:33:20 +00:00
if ( fz > = bottom & & gLowerLink [ nSector ] < 0 & & ! ( sector [ nSector ] . ceilingstat & 0x1 ) ) pDebris - > z + = ClipLow ( cz - top , 0 ) ;
if ( cz < = top & & gUpperLink [ nSector ] < 0 & & ! ( sector [ nSector ] . floorstat & 0x1 ) ) pDebris - > z + = ClipHigh ( fz - bottom , 0 ) ;
2020-02-07 19:47:43 +00:00
}
}
}
2021-08-27 17:12:22 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function plays sound predefined in missile info
2021-08-27 17:12:22 +00:00
//
//---------------------------------------------------------------------------
void sfxPlayMissileSound ( DBloodActor * actor , int missileId )
{
2021-08-27 11:46:07 +00:00
const MISSILEINFO_EXTRA * pMissType = & gMissileInfoExtra [ missileId - kMissileBase ] ;
2021-08-27 17:12:22 +00:00
sfxPlay3DSound ( actor , Chance ( 0x5000 ) ? pMissType - > fireSound [ 0 ] : pMissType - > fireSound [ 1 ] , - 1 , 0 ) ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 17:12:22 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function plays sound predefined in vector info
2021-08-27 17:12:22 +00:00
//
//---------------------------------------------------------------------------
void sfxPlayVectorSound ( DBloodActor * actor , int vectorId )
{
2021-08-27 11:46:07 +00:00
const VECTORINFO_EXTRA * pVectorData = & gVectorInfoExtra [ vectorId ] ;
2021-08-27 17:12:22 +00:00
sfxPlay3DSound ( actor , Chance ( 0x5000 ) ? pVectorData - > fireSound [ 0 ] : pVectorData - > fireSound [ 1 ] , - 1 , 0 ) ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 17:12:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int getSpriteMassBySize ( DBloodActor * actor )
{
auto pSprite = & actor - > s ( ) ;
2020-02-07 19:47:43 +00:00
int mass = 0 ; int seqId = - 1 ; int clipDist = pSprite - > clipdist ; Seq * pSeq = NULL ;
2021-08-27 17:12:22 +00:00
if ( ! actor - > hasX ( ) )
{
2020-10-11 10:22:36 +00:00
I_Error ( " getSpriteMassBySize: pSprite->extra < 0 " ) ;
2021-08-27 17:12:22 +00:00
}
else if ( actor - > IsDudeActor ( ) )
{
2021-09-02 18:08:31 +00:00
switch ( pSprite - > type )
{
2020-02-07 19:47:43 +00:00
case kDudePodMother : // fake dude, no seq
break ;
case kDudeModernCustom :
case kDudeModernCustomBurning :
2021-09-02 18:08:31 +00:00
seqId = actor - > x ( ) . data2 ;
2021-08-29 07:27:03 +00:00
clipDist = actor - > genDudeExtra . initVals [ 2 ] ;
2020-02-07 19:47:43 +00:00
break ;
default :
2020-02-08 20:13:29 +00:00
seqId = getDudeInfo ( pSprite - > type ) - > seqStartID ;
2020-02-07 19:47:43 +00:00
break ;
}
2021-09-02 18:08:31 +00:00
}
else
{
2020-02-07 19:47:43 +00:00
seqId = seqGetID ( 3 , pSprite - > extra ) ;
}
2021-09-01 19:53:33 +00:00
SPRITEMASS * cached = & actor - > spriteMass ;
2020-02-07 19:47:43 +00:00
if ( ( ( seqId > = 0 & & seqId = = cached - > seqId ) | | pSprite - > picnum = = cached - > picnum ) & & pSprite - > xrepeat = = cached - > xrepeat & &
2021-09-02 18:08:31 +00:00
pSprite - > yrepeat = = cached - > yrepeat & & clipDist = = cached - > clipdist )
{
2020-02-07 19:47:43 +00:00
return cached - > mass ;
}
2021-09-02 18:08:31 +00:00
int picnum = pSprite - > picnum ;
int massDiv = 30 ;
int addMul = 2 ;
int subMul = 2 ;
2020-02-07 19:47:43 +00:00
2021-09-02 18:08:31 +00:00
if ( seqId > = 0 )
{
2020-07-25 20:47:46 +00:00
auto pSeq = getSequence ( seqId ) ;
if ( pSeq )
2020-02-07 19:47:43 +00:00
{
picnum = seqGetTile ( & pSeq - > frames [ 0 ] ) ;
2021-09-02 18:08:31 +00:00
}
else
2020-02-07 19:47:43 +00:00
picnum = pSprite - > picnum ;
}
clipDist = ClipLow ( pSprite - > clipdist , 1 ) ;
2021-09-02 18:08:31 +00:00
int x = tileWidth ( picnum ) ;
int y = tileHeight ( picnum ) ;
int xrepeat = pSprite - > xrepeat ;
int yrepeat = pSprite - > yrepeat ;
2020-02-07 19:47:43 +00:00
// take surface type into account
2021-08-27 19:49:18 +00:00
switch ( tileGetSurfType ( pSprite - > picnum ) )
2021-09-02 18:08:31 +00:00
{
2020-02-07 19:47:43 +00:00
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 ) ;
2021-09-02 18:08:31 +00:00
else if ( xrepeat < 64 & & mass > 0 )
{
for ( int i = 64 - xrepeat ; i > 0 ; i - - )
{
if ( ( mass - = subMul ) < = 100 & & subMul - - < = 1 )
{
2020-02-07 19:47:43 +00:00
mass - = i ;
break ;
}
}
}
if ( yrepeat > 64 ) mass + = ( ( yrepeat - 64 ) * addMul ) ;
2021-09-02 18:08:31 +00:00
else if ( yrepeat < 64 & & mass > 0 )
{
for ( int i = 64 - yrepeat ; i > 0 ; i - - )
{
if ( ( mass - = subMul ) < = 100 & & subMul - - < = 1 )
{
2020-02-07 19:47:43 +00:00
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 ) ;
2021-09-02 18:08:31 +00:00
cached - > xrepeat = pSprite - > xrepeat ;
cached - > yrepeat = pSprite - > yrepeat ;
cached - > picnum = pSprite - > picnum ;
cached - > seqId = seqId ;
2020-02-07 19:47:43 +00:00
cached - > clipdist = pSprite - > clipdist ;
return cached - > mass ;
}
2021-08-27 20:13:17 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static int debrisGetIndex ( DBloodActor * actor )
{
if ( ! actor - > hasX ( ) | | actor - > x ( ) . physAttr = = 0 )
2020-02-07 19:47:43 +00:00
return - 1 ;
2021-08-27 20:13:17 +00:00
for ( int i = 0 ; i < gPhysSpritesCount ; i + + )
{
if ( gPhysSpritesList [ i ] ! = actor ) continue ;
2020-02-07 19:47:43 +00:00
return i ;
}
return - 1 ;
}
2021-09-02 19:37:28 +00:00
int debrisGetFreeIndex ( void )
{
for ( int i = 0 ; i < kMaxSuperXSprites ; i + + )
{
2021-09-01 19:52:00 +00:00
if ( gPhysSpritesList [ i ] = = nullptr ) return i ;
auto spr = & gPhysSpritesList [ i ] - > s ( ) ;
if ( spr - > statnum = = kStatFree ) return i ;
2020-02-07 19:47:43 +00:00
2021-09-01 19:52:00 +00:00
else if ( ( spr - > flags & kHitagFree ) | | ! gPhysSpritesList [ i ] - > hasX ( ) ) return i ;
else if ( gPhysSpritesList [ i ] - > x ( ) . physAttr = = 0 ) return i ;
2020-02-07 19:47:43 +00:00
}
return - 1 ;
}
2021-09-02 19:37:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-12 22:46:21 +00:00
void debrisConcuss ( DBloodActor * owner , int listIndex , int x , int y , int z , int dmg )
2021-09-16 19:36:34 +00:00
{
2021-09-01 19:52:00 +00:00
auto actor = gPhysSpritesList [ listIndex ] ;
2021-09-01 19:53:33 +00:00
if ( actor ! = nullptr & & actor - > hasX ( ) )
2021-09-16 19:36:34 +00:00
{
2021-09-01 19:53:33 +00:00
spritetype * pSprite = & actor - > s ( ) ;
2020-02-07 19:47:43 +00:00
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 ) ;
2021-07-19 21:15:26 +00:00
bool thing = ( pSprite - > type > = kThingBase & & pSprite - > type < kThingMax ) ;
2020-11-22 23:18:07 +00:00
int size = ( tileWidth ( pSprite - > picnum ) * pSprite - > xrepeat * tileHeight ( pSprite - > picnum ) * pSprite - > yrepeat ) > > 1 ;
2021-10-12 22:46:21 +00:00
if ( xsprite [ pSprite - > extra ] . physAttr & kPhysDebrisExplode )
{
2021-09-01 19:53:33 +00:00
if ( actor - > spriteMass . mass > 0 )
{
int t = scale ( dmg , size , actor - > spriteMass . mass ) ;
2020-02-07 19:47:43 +00:00
2021-10-12 22:46:21 +00:00
actor - > xvel ( ) + = MulScale ( t , dx , 16 ) ;
actor - > yvel ( ) + = MulScale ( t , dy , 16 ) ;
actor - > zvel ( ) + = MulScale ( t , dz , 16 ) ;
2020-02-07 19:47:43 +00:00
}
2021-07-19 21:15:26 +00:00
if ( thing )
pSprite - > statnum = kStatThing ; // temporary change statnum property
2020-02-07 19:47:43 +00:00
}
2021-09-16 19:36:34 +00:00
actDamageSprite ( owner , actor , kDamageExplode , dmg ) ;
2021-07-19 21:15:26 +00:00
if ( thing )
pSprite - > statnum = kStatDecoration ; // return statnum property back
2020-02-07 19:47:43 +00:00
}
}
2021-09-02 19:37:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-12 22:46:21 +00:00
void debrisBubble ( DBloodActor * actor )
2021-09-01 19:53:33 +00:00
{
2021-10-12 22:46:21 +00:00
spritetype * pSprite = & actor - > s ( ) ;
2021-09-01 19:53:33 +00:00
2021-01-07 12:33:20 +00:00
int top , bottom ;
GetSpriteExtents ( pSprite , & top , & bottom ) ;
2021-07-19 21:15:26 +00:00
for ( unsigned int i = 0 ; i < 1 + Random ( 5 ) ; i + + ) {
2021-01-07 12:33:20 +00:00
2021-05-03 22:22:35 +00:00
int nDist = ( pSprite - > xrepeat * ( tileWidth ( pSprite - > picnum ) > > 1 ) ) > > 2 ;
2021-01-07 12:33:20 +00:00
int nAngle = Random ( 2048 ) ;
2021-05-03 22:22:35 +00:00
int x = pSprite - > x + MulScale ( nDist , Cos ( nAngle ) , 30 ) ;
int y = pSprite - > y + MulScale ( nDist , Sin ( nAngle ) , 30 ) ;
2021-01-07 12:33:20 +00:00
int z = bottom - Random ( bottom - top ) ;
2021-08-31 23:46:42 +00:00
auto pFX = gFX . fxSpawnActor ( ( FX_ID ) ( FX_23 + Random ( 3 ) ) , pSprite - > sectnum , x , y , z , 0 ) ;
2021-01-07 12:33:20 +00:00
if ( pFX ) {
2021-10-12 22:46:21 +00:00
pFX - > xvel ( ) = actor - > xvel ( ) + Random2 ( 0x1aaaa ) ;
pFX - > yvel ( ) = actor - > yvel ( ) + Random2 ( 0x1aaaa ) ;
pFX - > zvel ( ) = actor - > zvel ( ) + Random2 ( 0x1aaaa ) ;
2021-01-07 12:33:20 +00:00
}
}
if ( Chance ( 0x2000 ) )
2021-10-12 22:46:21 +00:00
evPostActor ( actor , 0 , kCallbackEnemeyBubble ) ;
2021-01-07 12:33:20 +00:00
}
2021-09-02 19:37:28 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-12 22:48:50 +00:00
void debrisMove ( int listIndex )
2021-09-01 19:52:00 +00:00
{
auto actor = gPhysSpritesList [ listIndex ] ;
2021-09-01 19:53:51 +00:00
XSPRITE * pXSprite = & actor - > x ( ) ;
2021-09-01 19:52:00 +00:00
spritetype * pSprite = & actor - > s ( ) ;
int nSector = pSprite - > sectnum ;
2021-01-07 12:33:20 +00:00
2021-09-01 19:52:00 +00:00
if ( ! actor - > hasX ( ) )
{
gPhysSpritesList [ listIndex ] = nullptr ;
2020-02-07 19:47:43 +00:00
return ;
2021-09-01 19:52:00 +00:00
}
else if ( pSprite - > sectnum < 0 | | pSprite - > sectnum > = kMaxSectors )
{
gPhysSpritesList [ listIndex ] = nullptr ;
2020-02-07 19:47:43 +00:00
return ;
}
2021-01-07 12:33:20 +00:00
int top , bottom , i ;
2021-09-01 19:52:00 +00:00
GetActorExtents ( actor , & top , & bottom ) ;
2020-02-07 19:47:43 +00:00
2021-09-02 18:23:51 +00:00
Collision moveHit ;
moveHit . setNone ( ) ;
2021-01-07 12:33:20 +00:00
int floorDist = ( bottom - pSprite - > z ) > > 2 ;
int ceilDist = ( pSprite - > z - top ) > > 2 ;
int clipDist = pSprite - > clipdist < < 2 ;
2021-09-01 19:53:33 +00:00
int mass = actor - > spriteMass . mass ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
bool uwater = false ;
2021-09-01 19:53:33 +00:00
int tmpFraction = actor - > spriteMass . fraction ;
2021-09-02 19:37:28 +00:00
if ( sector [ nSector ] . extra > = 0 & & xsector [ sector [ nSector ] . extra ] . Underwater )
{
2020-02-07 19:47:43 +00:00
tmpFraction > > = 1 ;
2021-01-07 12:33:20 +00:00
uwater = true ;
}
2020-02-07 19:47:43 +00:00
2021-10-12 22:48:50 +00:00
if ( actor - > xvel ( ) | | actor - > yvel ( ) )
{
2020-02-07 19:47:43 +00:00
short oldcstat = pSprite - > cstat ;
pSprite - > cstat & = ~ ( CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN ) ;
2021-10-12 22:48:50 +00:00
moveHit = actor - > hit ( ) . hit = ClipMove ( & pSprite - > pos , & nSector , actor - > xvel ( ) > > 12 ,
actor - > yvel ( ) > > 12 , clipDist , ceilDist , floorDist , CLIPMASK0 ) ;
2020-02-07 19:47:43 +00:00
pSprite - > cstat = oldcstat ;
2021-09-02 19:37:28 +00:00
if ( pSprite - > sectnum ! = nSector )
{
2021-01-07 12:33:20 +00:00
if ( ! sectRangeIsFine ( nSector ) ) return ;
2021-10-12 22:48:50 +00:00
else ChangeActorSect ( actor , nSector ) ;
2021-01-07 12:33:20 +00:00
}
2020-02-07 19:47:43 +00:00
2021-09-02 19:37:28 +00:00
if ( sector [ nSector ] . type > = kSectorPath & & sector [ nSector ] . type < = kSectorRotate )
{
2021-11-07 18:35:55 +00:00
int nSector2 = nSector ;
2021-10-30 11:00:18 +00:00
if ( pushmove ( & pSprite - > pos , & nSector2 , clipDist , ceilDist , floorDist , CLIPMASK0 ) ! = - 1 )
2021-01-07 12:33:20 +00:00
nSector = nSector2 ;
}
2021-10-12 22:48:50 +00:00
if ( actor - > hit ( ) . hit . type = = kHitWall )
2021-09-02 18:23:51 +00:00
{
2021-10-12 22:48:50 +00:00
moveHit = actor - > hit ( ) . hit ;
2021-09-02 18:23:51 +00:00
i = moveHit . index ;
2021-10-12 22:48:50 +00:00
actWallBounceVector ( & actor - > xvel ( ) , & actor - > yvel ( ) , i , tmpFraction ) ;
2021-01-07 12:33:20 +00:00
}
2021-09-02 19:37:28 +00:00
}
else if ( ! FindSector ( pSprite - > x , pSprite - > y , pSprite - > z , & nSector ) )
{
2021-01-07 12:33:20 +00:00
return ;
}
2020-02-07 19:47:43 +00:00
2021-09-02 19:37:28 +00:00
if ( pSprite - > sectnum ! = nSector )
{
2020-10-11 10:38:17 +00:00
assert ( nSector > = 0 & & nSector < kMaxSectors ) ;
2021-10-12 22:48:50 +00:00
ChangeActorSect ( actor , nSector ) ;
2021-01-07 12:33:20 +00:00
nSector = pSprite - > sectnum ;
2020-02-07 19:47:43 +00:00
}
2021-07-19 21:15:26 +00:00
if ( sector [ nSector ] . extra > 0 )
2021-01-07 12:33:20 +00:00
uwater = xsector [ sector [ nSector ] . extra ] . Underwater ;
2020-02-07 19:47:43 +00:00
2021-10-12 22:48:50 +00:00
if ( actor - > zvel ( ) )
pSprite - > z + = actor - > zvel ( ) > > 8 ;
2020-02-07 19:47:43 +00:00
2021-09-02 18:23:51 +00:00
int ceilZ , floorZ ;
Collision ceilColl , floorColl ;
GetZRange ( pSprite , & ceilZ , & ceilColl , & floorZ , & floorColl , clipDist , CLIPMASK0 , PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR ) ;
2020-02-07 19:47:43 +00:00
GetSpriteExtents ( pSprite , & top , & bottom ) ;
2021-09-02 19:37:28 +00:00
if ( ( pXSprite - > physAttr & kPhysDebrisSwim ) & & uwater )
{
2021-01-07 12:33:20 +00:00
int vc = 0 ;
int cz = getceilzofslope ( nSector , pSprite - > x , pSprite - > y ) ;
int fz = getflorzofslope ( nSector , pSprite - > x , pSprite - > y ) ;
int div = ClipLow ( bottom - top , 1 ) ;
if ( gLowerLink [ nSector ] > = 0 ) cz + = ( cz < 0 ) ? 0x500 : - 0x500 ;
2021-09-01 19:53:51 +00:00
if ( top > cz & & ( ! ( pXSprite - > physAttr & kPhysDebrisFloat ) | | fz < = bottom < < 2 ) )
2021-10-12 22:48:50 +00:00
actor - > zvel ( ) - = DivScale ( ( bottom - ceilZ ) > > 6 , mass , 8 ) ;
2021-01-07 12:33:20 +00:00
if ( fz < bottom )
vc = 58254 + ( ( bottom - fz ) * - 80099 ) / div ;
2021-09-02 19:37:28 +00:00
if ( vc )
{
2021-01-07 12:33:20 +00:00
pSprite - > z + = ( ( vc < < 2 ) > > 1 ) > > 8 ;
2021-10-12 22:48:50 +00:00
actor - > zvel ( ) + = vc ;
2021-01-07 12:33:20 +00:00
}
2021-09-02 19:37:28 +00:00
}
else if ( ( pXSprite - > physAttr & kPhysGravity ) & & bottom < floorZ )
{
2020-02-07 19:47:43 +00:00
pSprite - > z + = 455 ;
2021-10-12 22:48:50 +00:00
actor - > zvel ( ) + = 58254 ;
2021-01-07 12:33:20 +00:00
2020-02-07 19:47:43 +00:00
}
2021-01-07 12:33:20 +00:00
2021-09-04 07:37:17 +00:00
if ( ( i = CheckLink ( actor ) ) ! = 0 )
2021-09-02 19:37:28 +00:00
{
2021-09-02 18:23:51 +00:00
GetZRange ( pSprite , & ceilZ , & ceilColl , & floorZ , & floorColl , clipDist , CLIPMASK0 , PARALLAXCLIP_CEILING | PARALLAXCLIP_FLOOR ) ;
2021-09-02 19:37:28 +00:00
if ( ! ( pSprite - > cstat & CSTAT_SPRITE_INVISIBLE ) )
{
switch ( i )
{
2020-02-07 19:47:43 +00:00
case kMarkerUpWater :
case kMarkerUpGoo :
2021-09-01 19:53:33 +00:00
int pitch = ( 150000 - ( actor - > spriteMass . mass < < 9 ) ) + Random3 ( 8192 ) ;
2020-02-07 19:47:43 +00:00
sfxPlay3DSoundCP ( pSprite , 720 , - 1 , 0 , pitch , 75 - Random ( 40 ) ) ;
2021-09-02 19:37:28 +00:00
if ( ! spriteIsUnderwater ( actor ) )
{
2021-08-27 08:18:33 +00:00
evKillActor ( actor , kCallbackEnemeyBubble ) ;
2021-09-02 19:37:28 +00:00
}
else
{
2021-08-27 08:18:33 +00:00
evPostActor ( actor , 0 , kCallbackEnemeyBubble ) ;
2021-09-02 19:37:28 +00:00
for ( int i = 2 ; i < = 5 ; i + + )
{
2021-01-07 12:33:20 +00:00
if ( Chance ( 0x5000 * i ) )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , Random ( 5 ) , kCallbackEnemeyBubble ) ;
2020-02-07 19:47:43 +00:00
}
}
break ;
}
}
}
2021-10-12 22:48:50 +00:00
GetActorExtents ( actor , & top , & bottom ) ;
2020-02-07 19:47:43 +00:00
2021-01-07 12:33:20 +00:00
if ( floorZ < = bottom ) {
2020-02-07 19:47:43 +00:00
2021-10-12 22:48:50 +00:00
actor - > hit ( ) . florhit = floorColl ;
int v30 = actor - > zvel ( ) - velFloor [ pSprite - > sectnum ] ;
2021-01-07 12:33:20 +00:00
2021-09-02 19:37:28 +00:00
if ( v30 > 0 )
{
2021-09-01 19:53:51 +00:00
pXSprite - > physAttr | = kPhysFalling ;
2021-10-12 22:48:50 +00:00
actFloorBounceVector ( & actor - > xvel ( ) , & actor - > yvel ( ) , & v30 , pSprite - > sectnum , tmpFraction ) ;
actor - > zvel ( ) = v30 ;
2020-02-07 19:47:43 +00:00
2021-10-12 22:48:50 +00:00
if ( abs ( actor - > zvel ( ) ) < 0x10000 )
{
actor - > zvel ( ) = velFloor [ pSprite - > sectnum ] ;
2021-09-01 19:53:51 +00:00
pXSprite - > physAttr & = ~ kPhysFalling ;
2020-02-07 19:47:43 +00:00
}
2021-09-02 18:23:51 +00:00
moveHit = floorColl ;
2021-08-31 23:46:42 +00:00
DBloodActor * pFX = NULL , * pFX2 = NULL ;
2021-09-02 19:37:28 +00:00
switch ( tileGetSurfType ( floorColl ) )
{
2021-01-07 12:33:20 +00:00
case kSurfLava :
2021-08-31 23:46:42 +00:00
if ( ( pFX = gFX . fxSpawnActor ( FX_10 , pSprite - > sectnum , pSprite - > x , pSprite - > y , floorZ , 0 ) ) = = NULL ) break ;
2021-09-02 19:37:28 +00:00
for ( i = 0 ; i < 7 ; i + + )
{
2021-08-31 23:46:42 +00:00
if ( ( pFX2 = gFX . fxSpawnActor ( FX_14 , pFX - > s ( ) . sectnum , pFX - > s ( ) . x , pFX - > s ( ) . y , pFX - > s ( ) . z , 0 ) ) = = NULL ) continue ;
pFX2 - > xvel ( ) = Random2 ( 0x6aaaa ) ;
pFX2 - > yvel ( ) = Random2 ( 0x6aaaa ) ;
pFX2 - > zvel ( ) = - ( int ) Random ( 0xd5555 ) ;
2021-01-07 12:33:20 +00:00
}
break ;
case kSurfWater :
2021-08-31 23:46:42 +00:00
gFX . fxSpawnActor ( FX_9 , pSprite - > sectnum , pSprite - > x , pSprite - > y , floorZ , 0 ) ;
2021-01-07 12:33:20 +00:00
break ;
}
2021-09-02 19:37:28 +00:00
}
2021-10-12 22:48:50 +00:00
else if ( actor - > zvel ( ) = = 0 )
2021-09-02 19:37:28 +00:00
{
2021-09-01 19:53:51 +00:00
pXSprite - > physAttr & = ~ kPhysFalling ;
2021-01-07 12:33:20 +00:00
}
2021-09-02 19:37:28 +00:00
}
else
{
2021-10-12 22:48:50 +00:00
actor - > hit ( ) . florhit . setNone ( ) ;
2021-09-01 19:53:51 +00:00
if ( pXSprite - > physAttr & kPhysGravity )
pXSprite - > physAttr | = kPhysFalling ;
2020-02-07 19:47:43 +00:00
}
2021-09-02 19:37:28 +00:00
if ( top < = ceilZ )
{
2021-10-12 22:48:50 +00:00
actor - > hit ( ) . ceilhit = moveHit = ceilColl ;
2020-02-07 19:47:43 +00:00
pSprite - > z + = ClipLow ( ceilZ - top , 0 ) ;
2021-10-12 22:48:50 +00:00
if ( actor - > zvel ( ) < = 0 & & ( pXSprite - > physAttr & kPhysFalling ) )
actor - > zvel ( ) = MulScale ( - actor - > zvel ( ) , 0x2000 , 16 ) ;
2020-02-07 19:47:43 +00:00
2021-09-02 19:37:28 +00:00
}
else
{
2021-10-12 22:48:50 +00:00
actor - > hit ( ) . ceilhit . setNone ( ) ;
GetActorExtents ( actor , & top , & bottom ) ;
2020-02-07 19:47:43 +00:00
}
2021-09-02 18:23:51 +00:00
if ( moveHit . type ! = kHitNone & & pXSprite - > Impact & & ! pXSprite - > locked & & ! pXSprite - > isTriggered & & ( pXSprite - > state = = pXSprite - > restState | | pXSprite - > Interrutable ) ) {
2021-01-07 12:33:20 +00:00
if ( pSprite - > type > = kThingBase & & pSprite - > type < kThingMax )
2021-10-12 22:48:50 +00:00
ChangeActorStat ( actor , kStatThing ) ;
2021-01-07 12:33:20 +00:00
2021-10-12 22:48:50 +00:00
trTriggerSprite ( actor , kCmdToggle ) ;
2021-01-07 12:33:20 +00:00
}
2021-10-12 22:48:50 +00:00
if ( ! actor - > xvel ( ) & & ! actor - > yvel ( ) ) return ;
2021-09-02 18:23:51 +00:00
else if ( floorColl . type = = kHitSprite )
{
2020-02-07 19:47:43 +00:00
2021-09-02 18:23:51 +00:00
if ( ( floorColl . actor - > s ( ) . cstat & 0x30 ) = = 0 )
{
2021-10-12 22:48:50 +00:00
actor - > xvel ( ) + = MulScale ( 4 , pSprite - > x - floorColl . actor - > s ( ) . x , 2 ) ;
actor - > yvel ( ) + = MulScale ( 4 , pSprite - > y - floorColl . actor - > s ( ) . y , 2 ) ;
2021-01-07 12:33:20 +00:00
return ;
2020-02-07 19:47:43 +00:00
}
2021-09-02 18:08:31 +00:00
}
2020-02-07 19:47:43 +00:00
2021-09-01 19:53:51 +00:00
pXSprite - > height = ClipLow ( floorZ - bottom , 0 ) > > 8 ;
if ( uwater | | pXSprite - > height > = 0x100 )
2021-01-07 12:33:20 +00:00
return ;
2020-02-07 19:47:43 +00:00
2021-01-07 12:33:20 +00:00
int nDrag = 0x2a00 ;
2021-09-01 19:53:51 +00:00
if ( pXSprite - > height > 0 )
nDrag - = scale ( nDrag , pXSprite - > height , 0x100 ) ;
2020-02-07 19:47:43 +00:00
2021-10-12 22:48:50 +00:00
actor - > xvel ( ) - = mulscale16r ( actor - > xvel ( ) , nDrag ) ;
actor - > yvel ( ) - = mulscale16r ( actor - > yvel ( ) , nDrag ) ;
if ( approxDist ( actor - > xvel ( ) , actor - > yvel ( ) ) < 0x1000 )
actor - > xvel ( ) = actor - > yvel ( ) = 0 ;
2020-02-07 19:47:43 +00:00
}
2021-08-28 07:50:01 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-01-07 12:33:20 +00:00
2021-08-28 07:50:01 +00:00
bool ceilIsTooLow ( DBloodActor * actor )
{
if ( actor ! = nullptr )
{
sectortype * pSector = & sector [ actor - > s ( ) . sectnum ] ;
2020-02-07 19:47:43 +00:00
int a = pSector - > ceilingz - pSector - > floorz ;
int top , bottom ;
2021-08-28 07:50:01 +00:00
GetActorExtents ( actor , & top , & bottom ) ;
2020-02-07 19:47:43 +00:00
int b = top - bottom ;
if ( a > b ) return true ;
}
return false ;
}
2021-08-28 07:50:01 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void aiSetGenIdleState ( DBloodActor * actor )
2020-11-07 12:46:03 +00:00
{
2021-08-28 07:50:01 +00:00
switch ( actor - > s ( ) . type )
{
2020-02-07 19:47:43 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
2021-08-31 19:52:26 +00:00
aiGenDudeNewState ( actor , & genIdle ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
2020-11-07 12:46:03 +00:00
aiNewState ( actor , & genIdle ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
2021-08-28 07:50:01 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function stops wind on all TX sectors affected by WindGen after it goes off state.
2021-08-28 07:50:01 +00:00
//
//---------------------------------------------------------------------------
void windGenStopWindOnSectors ( DBloodActor * sourceactor )
{
spritetype * pSource = & sourceactor - > s ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
if ( pXSource - > txID < = 0 & & xsectRangeIsFine ( sector [ pSource - > sectnum ] . extra ) )
{
2020-03-01 20:36:28 +00:00
xsector [ sector [ pSource - > sectnum ] . extra ] . windVel = 0 ;
2020-02-07 19:47:43 +00:00
return ;
}
2021-08-28 07:50:01 +00:00
for ( int i = bucketHead [ pXSource - > txID ] ; i < bucketHead [ pXSource - > txID + 1 ] ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SECTOR ) continue ;
2021-09-01 17:29:48 +00:00
XSECTOR * pXSector = & xsector [ sector [ rxBucket [ i ] . rxindex ] . extra ] ;
2020-03-01 20:36:28 +00:00
if ( ( pXSector - > state = = 1 & & ! pXSector - > windAlways )
2021-08-28 07:50:01 +00:00
| | ( ( pSource - > flags & kModernTypeFlag1 ) & & ! ( pSource - > flags & kModernTypeFlag2 ) ) )
{
2020-03-01 20:36:28 +00:00
pXSector - > windVel = 0 ;
}
}
2020-04-07 20:30:00 +00:00
// check redirected TX buckets
2021-09-05 18:10:40 +00:00
int rx = - 1 ;
DBloodActor * pXRedir = nullptr ;
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , 0 , sourceactor , pXRedir , & rx ) ) ! = nullptr )
2021-08-28 07:50:01 +00:00
{
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + )
{
2020-04-07 20:30:00 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SECTOR ) continue ;
2021-09-01 17:29:48 +00:00
XSECTOR * pXSector = & xsector [ sector [ rxBucket [ i ] . rxindex ] . extra ] ;
2020-04-07 20:30:00 +00:00
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
}
}
2021-08-28 08:38:36 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-05-05 18:50:14 +00:00
2021-08-28 08:38:36 +00:00
void trPlayerCtrlStartScene ( DBloodActor * sourceactor , PLAYER * pPlayer , bool force )
{
auto pXSource = & sourceactor - > x ( ) ;
TRPLAYERCTRL * pCtrl = & gPlayerCtrl [ pPlayer - > nPlayer ] ;
2020-02-07 19:47:43 +00:00
2021-08-28 08:38:36 +00:00
if ( pCtrl - > qavScene . initiator ! = nullptr & & ! force ) return ;
2020-05-22 16:28:03 +00:00
2020-02-07 19:47:43 +00:00
QAV * pQav = playerQavSceneLoad ( pXSource - > data2 ) ;
2021-08-28 08:38:36 +00:00
if ( pQav ! = nullptr )
{
2020-02-07 19:47:43 +00:00
// save current weapon
pXSource - > dropMsg = pPlayer - > curWeapon ;
2021-08-28 08:38:36 +00:00
auto initiator = pCtrl - > qavScene . initiator ;
if ( initiator ! = nullptr & & initiator ! = sourceactor & & initiator - > hasX ( ) )
pXSource - > dropMsg = initiator - > x ( ) . dropMsg ;
2020-02-07 19:47:43 +00:00
2021-08-28 08:38:36 +00:00
if ( initiator = = nullptr )
2020-02-07 19:47:43 +00:00
WeaponLower ( pPlayer ) ;
2020-11-21 14:45:37 +00:00
pXSource - > sysData1 = ClipLow ( ( pQav - > duration * pXSource - > waitTime ) / 4 , 0 ) ; // how many times animation should be played
2020-02-07 19:47:43 +00:00
2021-08-28 08:38:36 +00:00
pCtrl - > qavScene . initiator = sourceactor ;
2020-02-07 19:47:43 +00:00
pCtrl - > qavScene . qavResrc = pQav ;
pCtrl - > qavScene . dummy = - 1 ;
2021-04-11 10:10:52 +00:00
//pCtrl->qavScene.qavResrc->Preload();
2020-02-07 19:47:43 +00:00
pPlayer - > sceneQav = pXSource - > data2 ;
2020-11-21 14:45:37 +00:00
pPlayer - > weaponTimer = pCtrl - > qavScene . qavResrc - > duration ;
2020-02-07 19:47:43 +00:00
pPlayer - > qavCallback = ( pXSource - > data3 > 0 ) ? ClipRange ( pXSource - > data3 - 1 , 0 , 32 ) : - 1 ;
pPlayer - > qavLoop = false ;
2021-08-05 02:38:26 +00:00
pPlayer - > qavLastTick = I_GetTime ( pCtrl - > qavScene . qavResrc - > ticrate ) ;
pPlayer - > qavTimer = pCtrl - > qavScene . qavResrc - > duration ;
2020-02-07 19:47:43 +00:00
}
}
2021-08-28 08:38:36 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-02-07 19:47:43 +00:00
2021-08-28 08:38:36 +00:00
void trPlayerCtrlStopScene ( PLAYER * pPlayer )
{
2020-02-07 19:47:43 +00:00
TRPLAYERCTRL * pCtrl = & gPlayerCtrl [ pPlayer - > nPlayer ] ;
2021-08-28 08:38:36 +00:00
auto initiator = pCtrl - > qavScene . initiator ;
XSPRITE * pXSource = nullptr ;
if ( initiator - > hasX ( ) )
{
pXSource = & initiator - > x ( ) ;
2020-05-05 18:50:14 +00:00
pXSource - > sysData1 = 0 ;
}
2020-02-07 19:47:43 +00:00
2021-08-28 08:38:36 +00:00
if ( pCtrl - > qavScene . initiator ! = nullptr )
{
pCtrl - > qavScene . initiator = nullptr ;
pCtrl - > qavScene . qavResrc = nullptr ;
2020-05-22 16:28:03 +00:00
pPlayer - > sceneQav = - 1 ;
2020-02-07 19:47:43 +00:00
2020-05-22 16:28:03 +00:00
// restore weapon
2021-08-28 08:38:36 +00:00
if ( pPlayer - > pXSprite - > health > 0 )
{
2020-05-05 18:50:14 +00:00
int oldWeapon = ( pXSource & & pXSource - > dropMsg ! = 0 ) ? pXSource - > dropMsg : 1 ;
2020-08-26 14:48:33 +00:00
pPlayer - > newWeapon = pPlayer - > curWeapon = oldWeapon ;
2020-05-22 16:28:03 +00:00
WeaponRaise ( pPlayer ) ;
}
2020-05-05 18:50:14 +00:00
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlLink ( DBloodActor * sourceactor , PLAYER * pPlayer , bool checkCondition )
{
auto pXSource = & sourceactor - > x ( ) ;
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
2021-09-02 20:13:31 +00:00
if ( checkCondition & & pXSource - > txID > = kChannelUser )
{
for ( int i = 0 ; i < gTrackingCondsCount ; i + + )
{
2020-05-22 16:28:03 +00:00
TRCONDITION * pCond = & gCondition [ i ] ;
2021-10-12 21:52:54 +00:00
if ( pCond - > actor - > x ( ) . rxID ! = pXSource - > txID )
2020-05-22 16:28:03 +00:00
continue ;
2020-04-04 19:48:23 +00:00
// search for player control sprite and replace it with actual player sprite
2021-09-02 20:13:31 +00:00
for ( unsigned k = 0 ; k < pCond - > length ; k + + )
{
2021-08-27 10:55:16 +00:00
if ( pCond - > obj [ k ] . type ! = OBJ_SPRITE | | pCond - > obj [ k ] . actor ! = sourceactor ) continue ;
pCond - > obj [ k ] . actor = pPlayer - > actor ( ) ;
pCond - > obj [ k ] . index_ = 0 ;
2021-08-27 16:21:01 +00:00
pCond - > obj [ k ] . cmd = ( uint8_t ) pPlayer - > pXSprite - > command ;
2020-03-13 20:59:13 +00:00
break ;
}
}
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlSetRace ( int value , PLAYER * pPlayer )
{
playerSetRace ( pPlayer , value ) ;
2021-09-02 20:13:31 +00:00
switch ( pPlayer - > lifeMode )
{
2020-02-07 19:47:43 +00:00
case kModeHuman :
case kModeBeast :
playerSizeReset ( pPlayer ) ;
break ;
case kModeHumanShrink :
playerSizeShrink ( pPlayer , 2 ) ;
break ;
case kModeHumanGrown :
playerSizeGrow ( pPlayer , 2 ) ;
break ;
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlSetMoveSpeed ( int value , PLAYER * pPlayer )
{
int speed = ClipRange ( value , 0 , 500 ) ;
for ( int i = 0 ; i < kModeMax ; i + + )
2021-09-02 20:13:31 +00:00
{
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 ] ;
2020-05-22 16:28:03 +00:00
curPosture - > frontAccel = ( defPosture - > frontAccel * speed ) / kPercFull ;
curPosture - > sideAccel = ( defPosture - > sideAccel * speed ) / kPercFull ;
curPosture - > backAccel = ( defPosture - > backAccel * speed ) / kPercFull ;
2020-02-07 19:47:43 +00:00
}
2020-05-22 16:28:03 +00:00
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlSetJumpHeight ( int value , PLAYER * pPlayer )
{
int jump = ClipRange ( value , 0 , 500 ) ;
2021-09-02 20:13:31 +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 ] ;
2020-05-22 16:28:03 +00:00
curPosture - > normalJumpZ = ( defPosture - > normalJumpZ * jump ) / kPercFull ;
curPosture - > pwupJumpZ = ( defPosture - > pwupJumpZ * jump ) / kPercFull ;
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlSetScreenEffect ( int value , int timeval , PLAYER * pPlayer )
{
int eff = ClipLow ( value , 0 ) ;
int time = ( eff > 0 ) ? timeval : 0 ;
2020-05-05 18:50:14 +00:00
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 ;
2021-09-02 20:13:31 +00:00
[[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 ;
2021-09-02 20:13:31 +00:00
[[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 ;
2021-09-02 20:13:31 +00:00
[[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 ;
2021-09-02 20:13:31 +00:00
[[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 ;
2021-09-02 20:13:31 +00:00
[[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 ;
2021-09-02 20:13:31 +00:00
[[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 ;
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlSetLookAngle ( int value , PLAYER * pPlayer )
2020-09-17 07:20:33 +00:00
{
2021-09-02 20:16:18 +00:00
double const upAngle = 289 ;
double const downAngle = - 347 ;
2021-07-18 08:52:07 +00:00
double const lookStepUp = 4.0 * upAngle / 60.0 ;
double const lookStepDown = - 4.0 * downAngle / 60.0 ;
2021-09-02 20:16:18 +00:00
double const look = value < < 5 ;
2021-07-18 08:52:07 +00:00
double adjustment ;
2021-09-02 20:13:31 +00:00
if ( look > 0 )
{
2021-07-18 08:52:07 +00:00
adjustment = min ( MulScaleF ( lookStepUp , look , 8 ) , upAngle ) ;
2020-09-17 07:20:33 +00:00
}
2021-09-02 20:13:31 +00:00
else if ( look < 0 )
{
2021-07-18 08:52:07 +00:00
adjustment = - max ( MulScaleF ( lookStepDown , abs ( look ) , 8 ) , downAngle ) ;
}
2021-09-02 20:13:31 +00:00
else
{
2021-07-18 08:52:07 +00:00
adjustment = 0 ;
2020-09-17 07:20:33 +00:00
}
2020-02-07 19:47:43 +00:00
2021-07-18 08:52:07 +00:00
pPlayer - > horizon . settarget ( 100. * tan ( adjustment * pi : : pi ( ) / 1024. ) ) ;
2021-07-18 09:26:24 +00:00
pPlayer - > horizon . lockinput ( ) ;
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlEraseStuff ( int value , PLAYER * pPlayer )
{
2020-02-07 19:47:43 +00:00
2021-09-02 20:16:18 +00:00
switch ( value )
2021-09-02 20:13:31 +00:00
{
2020-02-07 19:47:43 +00:00
case 0 : // erase all
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
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 ;
2021-08-18 09:56:06 +00:00
pPlayer - > curWeapon = kWeapNone ;
2021-08-03 10:23:48 +00:00
pPlayer - > nextWeapon = kWeapPitchFork ;
2020-02-07 19:47:43 +00:00
WeaponRaise ( pPlayer ) ;
2021-09-02 20:16:18 +00:00
if ( value ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case 2 : // erase all armor
for ( int i = 0 ; i < 3 ; i + + ) pPlayer - > armor [ i ] = 0 ;
2021-09-02 20:16:18 +00:00
if ( value ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
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-22 16:28:03 +00:00
pPlayer - > packItemId = - 1 ;
2021-09-02 20:16:18 +00:00
if ( value ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case 4 : // erase all keys
for ( int i = 0 ; i < 8 ; i + + ) pPlayer - > hasKey [ i ] = false ;
2021-09-02 20:16:18 +00:00
if ( value ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-05-22 16:28:03 +00:00
case 5 : // erase powerups
for ( int i = 0 ; i < kMaxPowerUps ; i + + ) pPlayer - > pwUpTime [ i ] = 0 ;
2020-12-06 20:56:09 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlGiveStuff ( int data2 , int weapon , int data4 , PLAYER * pPlayer , TRPLAYERCTRL * pCtrl )
{
switch ( data2 )
{
2020-02-07 19:47:43 +00:00
case 1 : // give N weapon and default ammo for it
case 2 : // give just N ammo for selected weapon
2021-09-02 20:13:31 +00:00
if ( weapon < = 0 | | weapon > 13 )
{
2020-10-16 05:12:35 +00:00
Printf ( PRINT_HIGH , " Weapon #%d is out of a weapons range! " , weapon ) ;
2020-05-05 18:50:14 +00:00
break ;
2021-09-02 20:16:18 +00:00
} else if ( data2 = = 2 & & data4 = = 0 )
{
2020-10-16 05:12:35 +00:00
Printf ( PRINT_HIGH , " Zero ammo for weapon #%d is specified! " , weapon ) ;
2020-05-05 18:50:14 +00:00
break ;
}
2021-09-02 20:13:31 +00:00
switch ( weapon )
{
2021-08-03 10:23:39 +00:00
case kWeapProximity : // remote bomb
case kWeapRemote : // prox bomb
2020-05-05 18:50:14 +00:00
pPlayer - > hasWeapon [ weapon ] = true ;
weapon - - ;
2021-09-02 20:16:18 +00:00
pPlayer - > ammoCount [ weapon ] = ClipHigh ( pPlayer - > ammoCount [ weapon ] + ( ( data2 = = 2 ) ? data4 : 1 ) , gAmmoInfo [ weapon ] . max ) ;
2020-05-05 18:50:14 +00:00
weapon + + ;
break ;
default :
2021-09-02 20:13:31 +00:00
for ( int i = 0 ; i < 11 ; i + + )
{
2020-05-05 18:50:14 +00:00
if ( gWeaponItemData [ i ] . type ! = weapon ) continue ;
2020-02-07 19:47:43 +00:00
2020-05-22 16:35:25 +00:00
const WEAPONITEMDATA * pWeaponData = & gWeaponItemData [ i ] ;
int nAmmoType = pWeaponData - > ammoType ;
2021-09-02 20:16:18 +00:00
switch ( data2 ) {
2020-05-05 18:50:14 +00:00
case 1 :
pPlayer - > hasWeapon [ weapon ] = true ;
if ( pPlayer - > ammoCount [ nAmmoType ] > = pWeaponData - > count ) break ;
2020-05-22 16:28:03 +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 :
2021-09-02 20:16:18 +00:00
pPlayer - > ammoCount [ nAmmoType ] = ClipHigh ( pPlayer - > ammoCount [ nAmmoType ] + data4 , gAmmoInfo [ nAmmoType ] . max ) ;
2020-05-22 16:28:03 +00:00
break ;
}
2020-05-05 18:50:14 +00:00
break ;
}
break ;
}
2021-09-02 20:16:18 +00:00
if ( pPlayer - > hasWeapon [ weapon ] & & data4 = = 0 ) // switch on it
2021-08-28 08:38:36 +00:00
{
2021-08-03 10:23:48 +00:00
pPlayer - > nextWeapon = kWeapNone ;
2020-02-07 19:47:43 +00:00
2021-08-28 08:38:36 +00:00
if ( pPlayer - > sceneQav > = 0 & & pCtrl - > qavScene . initiator & & pCtrl - > qavScene . initiator - > hasX ( ) )
{
pCtrl - > qavScene . initiator - > x ( ) . dropMsg = weapon ;
}
else if ( pPlayer - > curWeapon ! = weapon )
{
2020-08-26 14:48:33 +00:00
pPlayer - > newWeapon = weapon ;
2020-05-22 16:28:03 +00:00
WeaponRaise ( pPlayer ) ;
2020-02-07 19:47:43 +00:00
}
}
2020-05-22 16:28:03 +00:00
break ;
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlUsePackItem ( int data2 , int data3 , int data4 , PLAYER * pPlayer , int evCmd )
{
unsigned int invItem = data2 - 1 ;
2021-09-02 20:13:31 +00:00
switch ( evCmd )
{
2020-05-05 18:50:14 +00:00
case kCmdOn :
if ( ! pPlayer - > packSlots [ invItem ] . isActive ) packUseItem ( pPlayer , invItem ) ;
break ;
case kCmdOff :
if ( pPlayer - > packSlots [ invItem ] . isActive ) packUseItem ( pPlayer , invItem ) ;
break ;
default :
2020-05-22 16:28:03 +00:00
packUseItem ( pPlayer , invItem ) ;
2020-05-05 18:50:14 +00:00
break ;
}
2020-02-07 19:47:43 +00:00
2021-09-02 20:16:18 +00:00
switch ( data4 )
2021-09-02 20:13:31 +00:00
{
2020-05-05 18:50:14 +00:00
case 2 : // both
case 0 : // switch on it
if ( pPlayer - > packSlots [ invItem ] . curAmount > 0 ) pPlayer - > packItemId = invItem ;
2021-09-02 20:16:18 +00:00
if ( ! data4 ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-05-05 18:50:14 +00:00
case 1 : // force remove after use
2020-05-22 16:28:03 +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
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 20:16:18 +00:00
void trPlayerCtrlUsePowerup ( DBloodActor * sourceactor , PLAYER * pPlayer , int evCmd )
{
2021-07-26 19:52:42 +00:00
2021-09-02 20:16:18 +00:00
bool relative = ( sourceactor - > s ( ) . flags & kModernTypeFlag1 ) ;
2021-07-26 19:52:42 +00:00
2021-09-02 20:16:18 +00:00
int nPower = ( kMinAllowedPowerup + sourceactor - > x ( ) . data2 ) - 1 ;
int nTime = ClipRange ( abs ( sourceactor - > x ( ) . data3 ) * 100 , - gPowerUpInfo [ nPower ] . maxTime , gPowerUpInfo [ nPower ] . maxTime ) ;
if ( sourceactor - > x ( ) . data3 < 0 )
2021-07-26 19:52:42 +00:00
nTime = - nTime ;
2021-09-02 20:13:31 +00:00
if ( pPlayer - > pwUpTime [ nPower ] )
{
2021-07-26 19:52:42 +00:00
if ( ! relative & & nTime < = 0 )
powerupDeactivate ( pPlayer , nPower ) ;
}
2021-09-02 20:13:31 +00:00
if ( nTime ! = 0 )
{
2021-07-26 19:52:42 +00:00
if ( pPlayer - > pwUpTime [ nPower ] < = 0 )
powerupActivate ( pPlayer , nPower ) ; // MUST activate first for powerups like kPwUpDeathMask
// ...so we able to change time amount
if ( relative ) pPlayer - > pwUpTime [ nPower ] + = nTime ;
else pPlayer - > pwUpTime [ nPower ] = nTime ;
}
if ( pPlayer - > pwUpTime [ nPower ] < = 0 )
powerupDeactivate ( pPlayer , nPower ) ;
return ;
}
2021-08-28 10:11:03 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void useObjResizer ( DBloodActor * sourceactor , int targType , int targIndex , DBloodActor * targetactor )
{
auto pXSource = & sourceactor - > x ( ) ;
switch ( targType )
{
2020-02-07 19:47:43 +00:00
// for sectors
2021-08-28 10:11:03 +00:00
case OBJ_SECTOR :
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
sector [ targIndex ] . floorxpan_ = ( float ) ClipRange ( pXSource - > data1 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
sector [ targIndex ] . floorypan_ = ( float ) ClipRange ( pXSource - > data2 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
sector [ targIndex ] . ceilingxpan_ = ( float ) ClipRange ( pXSource - > data3 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
2021-08-28 10:11:03 +00:00
sector [ targIndex ] . ceilingypan_ = ( float ) ClipRange ( pXSource - > data4 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
break ;
// for sprites
2021-08-28 10:11:03 +00:00
case OBJ_SPRITE :
{
2020-12-06 20:56:09 +00:00
bool fit = false ;
2021-08-28 10:11:03 +00:00
auto pTarget = & targetactor - > s ( ) ;
2020-02-07 19:47:43 +00:00
// resize by seq scaling
2021-08-28 10:11:03 +00:00
if ( sourceactor - > s ( ) . flags & kModernTypeFlag1 )
{
if ( valueIsBetween ( pXSource - > data1 , - 255 , 32767 ) )
{
2020-02-07 19:47:43 +00:00
int mulDiv = ( valueIsBetween ( pXSource - > data2 , 0 , 257 ) ) ? pXSource - > data2 : 256 ;
2021-08-28 10:11:03 +00:00
if ( pXSource - > data1 > 0 ) targetactor - > x ( ) . scale = mulDiv * ClipHigh ( pXSource - > data1 , 25 ) ;
else if ( pXSource - > data1 < 0 ) targetactor - > x ( ) . scale = mulDiv / ClipHigh ( abs ( pXSource - > data1 ) , 25 ) ;
else targetactor - > x ( ) . scale = 0 ;
2020-12-06 20:56:09 +00:00
fit = true ;
2020-02-07 19:47:43 +00:00
}
// resize by repeats
2021-08-28 10:11:03 +00:00
}
else
{
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
{
pTarget - > xrepeat = ClipRange ( pXSource - > data1 , 0 , 255 ) ;
2020-12-06 20:56:09 +00:00
fit = true ;
}
2021-08-28 10:11:03 +00:00
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
{
pTarget - > yrepeat = ClipRange ( pXSource - > data2 , 0 , 255 ) ;
2020-12-06 20:56:09 +00:00
fit = true ;
}
}
2021-08-28 10:11:03 +00:00
if ( fit & & ( pTarget - > type = = kDudeModernCustom | | pTarget - > type = = kDudeModernCustomBurning ) )
{
2020-12-06 20:56:09 +00:00
// request properties update for custom dude
2021-08-28 10:11:03 +00:00
2021-08-29 07:27:03 +00:00
targetactor - > genDudeExtra . updReq [ kGenDudePropertySpriteSize ] = true ;
targetactor - > genDudeExtra . updReq [ kGenDudePropertyAttack ] = true ;
targetactor - > genDudeExtra . updReq [ kGenDudePropertyMass ] = true ;
targetactor - > genDudeExtra . updReq [ kGenDudePropertyDmgScale ] = true ;
2021-08-28 10:11:03 +00:00
evPostActor ( targetactor , kGenDudeUpdTimeRate , kCallbackGenDudeUpdate ) ;
2020-02-07 19:47:43 +00:00
}
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
pTarget - > xoffset = ClipRange ( pXSource - > data3 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
2021-08-28 10:11:03 +00:00
pTarget - > yoffset = ClipRange ( pXSource - > data4 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-12-06 20:56:09 +00:00
}
2020-02-07 19:47:43 +00:00
case OBJ_WALL :
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
wall [ targIndex ] . xrepeat = ClipRange ( pXSource - > data1 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
wall [ targIndex ] . yrepeat = ClipRange ( pXSource - > data2 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
2021-08-28 10:11:03 +00:00
wall [ targIndex ] . xpan_ = ( float ) ClipRange ( pXSource - > data3 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
2021-08-28 10:11:03 +00:00
wall [ targIndex ] . ypan_ = ( float ) ClipRange ( pXSource - > data4 , 0 , 255 ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 21:26:18 +00:00
void usePropertiesChanger ( DBloodActor * sourceactor , int objType , int objIndex , DBloodActor * targetactor )
{
auto pXSource = & sourceactor - > x ( ) ;
spritetype * pSource = & sourceactor - > s ( ) ;
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
switch ( objType )
{
case OBJ_WALL :
{
2020-02-07 19:47:43 +00:00
walltype * pWall = & wall [ objIndex ] ; int old = - 1 ;
// data3 = set wall hitag
2021-09-02 20:13:31 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
{
2020-02-07 19:47:43 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) ) pWall - > hitag = pWall - > hitag | = pXSource - > data3 ;
else pWall - > hitag = pXSource - > data3 ;
}
// data4 = set wall cstat
2021-09-02 20:13:31 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
{
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 21:26:18 +00:00
case OBJ_SPRITE :
{
spritetype * pSprite = & targetactor - > s ( ) ;
XSPRITE * pXSprite = & targetactor - > x ( ) ;
bool thing2debris = false ;
int old = - 1 ;
2020-02-07 19:47:43 +00:00
// data3 = set sprite hitag
2021-09-02 20:13:31 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
{
2020-02-07 19:47:43 +00:00
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
2021-09-02 20:13:31 +00:00
if ( valueIsBetween ( pXSource - > data2 , - 1 , 32767 ) | | thing2debris )
{
switch ( pSprite - > statnum )
{
2020-02-07 19:47:43 +00:00
case kStatDude : // dudes already treating in game
case kStatFree :
case kStatMarker :
2021-01-07 12:33:20 +00:00
case kStatPathMarker :
2020-02-07 19:47:43 +00:00
break ;
default :
// store physics attributes in xsprite to avoid setting hitag for modern types!
int flags = ( pXSprite - > physAttr ! = 0 ) ? pXSprite - > physAttr : 0 ;
2020-12-06 20:56:09 +00:00
int oldFlags = flags ;
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
if ( thing2debris )
{
2020-02-07 19:47:43 +00:00
// 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 ) ;
2021-09-05 18:10:40 +00:00
targetactor - > xvel ( ) = targetactor - > yvel ( ) = targetactor - > zvel ( ) = 0 ;
2020-12-06 20:56:09 +00:00
pXSprite - > restState = pXSprite - > state ;
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
}
else
{
// WTF is this?!?
char digits [ 6 ] = { } ;
snprintf ( digits , 6 , " %d " , pXSource - > data2 ) ;
2021-07-19 21:15:26 +00:00
for ( unsigned int i = 0 ; i < sizeof ( digits ) ; i + + )
2021-01-07 12:33:20 +00:00
digits [ i ] = ( digits [ i ] > = 48 & & digits [ i ] < = 57 ) ? ( digits [ i ] - 57 ) + 9 : 0 ;
2020-12-06 20:56:09 +00:00
2020-02-07 19:47:43 +00:00
// first digit of data2: set main physics attributes
2021-09-02 20:13:31 +00:00
switch ( digits [ 0 ] )
{
2020-02-07 19:47:43 +00:00
case 0 :
flags & = ~ kPhysMove ;
flags & = ~ ( kPhysGravity | kPhysFalling ) ;
break ;
2020-12-06 20:56:09 +00:00
case 1 :
2020-02-07 19:47:43 +00:00
flags | = kPhysMove ;
flags & = ~ ( kPhysGravity | kPhysFalling ) ;
break ;
2020-12-06 20:56:09 +00:00
case 2 :
2020-02-07 19:47:43 +00:00
flags & = ~ kPhysMove ;
flags | = ( kPhysGravity | kPhysFalling ) ;
break ;
2020-12-06 20:56:09 +00:00
case 3 :
2020-02-07 19:47:43 +00:00
flags | = kPhysMove ;
flags | = ( kPhysGravity | kPhysFalling ) ;
break ;
}
2021-01-07 12:33:20 +00:00
// second digit of data2: touch physics flags
2021-09-02 20:13:31 +00:00
switch ( digits [ 1 ] )
{
2021-01-07 12:33:20 +00:00
case 0 :
flags & = ~ kPhysDebrisTouch ;
break ;
case 1 :
flags | = kPhysDebrisTouch ;
break ;
}
// third digit of data2: weapon physics flags
2021-09-02 20:13:31 +00:00
switch ( digits [ 2 ] )
{
2020-12-06 20:56:09 +00:00
case 0 :
2020-02-07 19:47:43 +00:00
flags & = ~ kPhysDebrisVector ;
flags & = ~ kPhysDebrisExplode ;
break ;
2020-12-06 20:56:09 +00:00
case 1 :
2020-02-07 19:47:43 +00:00
flags | = kPhysDebrisVector ;
flags & = ~ kPhysDebrisExplode ;
break ;
2020-12-06 20:56:09 +00:00
case 2 :
2020-02-07 19:47:43 +00:00
flags & = ~ kPhysDebrisVector ;
flags | = kPhysDebrisExplode ;
break ;
2020-12-06 20:56:09 +00:00
case 3 :
2020-02-07 19:47:43 +00:00
flags | = kPhysDebrisVector ;
flags | = kPhysDebrisExplode ;
break ;
}
2021-01-07 12:33:20 +00:00
// fourth digit of data2: swimming / flying physics flags
2021-09-02 20:13:31 +00:00
switch ( digits [ 3 ] )
{
2021-01-07 12:33:20 +00:00
case 0 :
flags & = ~ kPhysDebrisSwim ;
flags & = ~ kPhysDebrisFly ;
flags & = ~ kPhysDebrisFloat ;
break ;
case 1 :
flags | = kPhysDebrisSwim ;
flags & = ~ kPhysDebrisFly ;
flags & = ~ kPhysDebrisFloat ;
break ;
case 2 :
flags | = kPhysDebrisSwim ;
flags | = kPhysDebrisFloat ;
flags & = ~ kPhysDebrisFly ;
break ;
case 3 :
flags | = kPhysDebrisFly ;
flags & = ~ kPhysDebrisSwim ;
flags & = ~ kPhysDebrisFloat ;
break ;
case 4 :
flags | = kPhysDebrisFly ;
flags | = kPhysDebrisFloat ;
flags & = ~ kPhysDebrisSwim ;
break ;
case 5 :
flags | = kPhysDebrisSwim ;
flags | = kPhysDebrisFly ;
flags & = ~ kPhysDebrisFloat ;
break ;
case 6 :
flags | = kPhysDebrisSwim ;
flags | = kPhysDebrisFly ;
flags | = kPhysDebrisFloat ;
break ;
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 21:26:18 +00:00
int nIndex = debrisGetIndex ( targetactor ) ; // check if there is no sprite in list
2020-02-07 19:47:43 +00:00
// adding physics sprite in list
2021-09-02 20:13:31 +00:00
if ( ( flags & kPhysGravity ) ! = 0 | | ( flags & kPhysMove ) ! = 0 )
{
2020-02-07 19:47:43 +00:00
2020-12-06 20:56:09 +00:00
if ( oldFlags = = 0 )
2021-09-02 21:26:18 +00:00
targetactor - > xvel ( ) = targetactor - > yvel ( ) = targetactor - > zvel ( ) = 0 ;
2020-12-06 20:56:09 +00:00
2021-09-02 20:13:31 +00:00
if ( nIndex ! = - 1 )
{
2021-01-07 12:33:20 +00:00
pXSprite - > physAttr = flags ; // just update physics attributes
2021-09-02 20:13:31 +00:00
}
else if ( ( nIndex = debrisGetFreeIndex ( ) ) < 0 )
{
2020-02-07 19:47:43 +00:00
viewSetSystemMessage ( " Max (%d) Physics affected sprites reached! " , kMaxSuperXSprites ) ;
2021-09-02 20:13:31 +00:00
}
else
{
2020-02-07 19:47:43 +00:00
pXSprite - > physAttr = flags ; // update physics attributes
// allow things to became debris, so they use different physics...
2021-09-02 21:26:18 +00:00
if ( pSprite - > statnum = = kStatThing ) ChangeActorStat ( targetactor , 0 ) ;
2021-01-07 12:33:20 +00:00
// set random goal ang for swimming so they start turning
2021-09-02 21:26:18 +00:00
if ( ( flags & kPhysDebrisSwim ) & & ! targetactor - > xvel ( ) & & ! targetactor - > yvel ( ) & & ! targetactor - > zvel ( ) )
2021-01-07 12:33:20 +00:00
pXSprite - > goalAng = ( pSprite - > ang + Random3 ( kAng45 ) ) & 2047 ;
if ( pXSprite - > physAttr & kPhysDebrisVector )
pSprite - > cstat | = CSTAT_SPRITE_BLOCK_HITSCAN ;
2020-02-07 19:47:43 +00:00
2021-09-02 21:26:18 +00:00
gPhysSpritesList [ nIndex ] = targetactor ;
2020-02-07 19:47:43 +00:00
if ( nIndex > = gPhysSpritesCount ) gPhysSpritesCount + + ;
2021-09-02 21:26:18 +00:00
getSpriteMassBySize ( targetactor ) ; // create physics cache
2020-02-07 19:47:43 +00:00
}
// removing physics from sprite in list (don't remove sprite from list)
2021-09-02 21:26:18 +00:00
}
else if ( nIndex ! = - 1 )
{
2020-02-07 19:47:43 +00:00
pXSprite - > physAttr = flags ;
2021-09-02 21:26:18 +00:00
targetactor - > xvel ( ) = targetactor - > yvel ( ) = targetactor - > zvel ( ) = 0 ;
2020-02-07 19:47:43 +00:00
if ( pSprite - > lotag > = kThingBase & & pSprite - > lotag < kThingMax )
2021-09-02 21:26:18 +00:00
ChangeActorStat ( targetactor , kStatThing ) ; // if it was a thing - restore statnum
2020-02-07 19:47:43 +00:00
}
break ;
}
}
// data4 = sprite cstat
2021-09-01 21:25:19 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
{
2020-02-07 19:47:43 +00:00
old = pSprite - > cstat ;
// set new cstat
2020-05-05 18:50:14 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) ) pSprite - > cstat | = pXSource - > data4 ; // relative
2021-09-05 10:25:52 +00:00
else pSprite - > cstat = pXSource - > data4 & 0xffff ; // absolute
2020-02-07 19:47:43 +00:00
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
2021-09-01 21:25:19 +00:00
if ( old & 0x6000 )
{
2020-02-07 19:47:43 +00:00
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 ;
2021-09-01 21:25:19 +00:00
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
2021-08-29 07:44:08 +00:00
BloodSectIterator it ( objIndex ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 14:48:32 +00:00
{
2021-08-29 07:44:08 +00:00
spritetype * pSpr = & iactor - > s ( ) ;
if ( pSpr - > statnum ! = kStatDude | | ! iactor - > IsDudeActor ( ) | | ! iactor - > hasX ( ) )
2020-05-05 18:50:14 +00:00
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 ) {
2020-08-28 20:51:05 +00:00
pPlayer - > posture = ( ! ( pPlayer - > input . actions & SB_CROUCH ) ) ? kPostureStand : kPostureCrouch ;
2020-05-05 18:50:14 +00:00
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 ;
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 21:36:57 +00:00
void useTeleportTarget ( DBloodActor * sourceactor , DBloodActor * actor )
{
auto pSprite = & actor - > s ( ) ;
auto pSource = & sourceactor - > s ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
PLAYER * pPlayer = getPlayerById ( pSprite - > type ) ;
2020-02-07 19:47:43 +00:00
XSECTOR * pXSector = ( sector [ pSource - > sectnum ] . extra > = 0 ) ? & xsector [ sector [ pSource - > sectnum ] . extra ] : NULL ;
2021-09-02 21:36:57 +00:00
bool isDude = ( ! pPlayer & & actor - > IsDudeActor ( ) ) ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
if ( pSprite - > sectnum ! = pSource - > sectnum )
2021-09-02 21:36:57 +00:00
ChangeActorSect ( actor , pSource - > sectnum ) ;
2020-03-01 20:36:28 +00:00
2021-07-19 21:15:26 +00:00
pSprite - > x = pSource - > x ; pSprite - > y = pSource - > y ;
2021-09-02 21:36:57 +00:00
int zTop , zBot ;
GetActorExtents ( sourceactor , & zTop , & zBot ) ;
2021-07-19 21:15:26 +00:00
pSprite - > z = zBot ;
clampSprite ( pSprite , 0x01 ) ;
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
2021-09-02 20:13:31 +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
2021-09-02 20:13:31 +00:00
if ( pXSector - > Underwater )
{
2020-05-05 18:50:14 +00:00
spritetype * pLink = ( gLowerLink [ pSource - > sectnum ] > = 0 ) ? & sprite [ gLowerLink [ pSource - > sectnum ] ] : NULL ;
2021-09-02 20:13:31 +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
2021-09-02 20:13:31 +00:00
if ( pPlayer )
{
2020-03-01 20:36:28 +00:00
int waterPal = kMediumWater ;
2021-09-02 20:13:31 +00:00
if ( pLink )
{
2020-03-01 20:36:28 +00:00
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 ;
}
2021-09-02 20:13:31 +00:00
}
else
{
2021-09-02 21:36:57 +00:00
actor - > x ( ) . medium = kMediumNormal ;
2021-09-02 20:13:31 +00:00
if ( pPlayer )
{
2020-08-28 20:51:05 +00:00
pPlayer - > posture = ( ! ( pPlayer - > input . actions & SB_CROUCH ) ) ? kPostureStand : kPostureCrouch ;
2020-03-01 20:36:28 +00:00
pPlayer - > nWaterPal = 0 ;
}
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
}
2021-09-02 21:36:57 +00:00
if ( pSprite - > statnum = = kStatDude & & actor - > IsDudeActor ( ) & & ! actor - > IsPlayerActor ( ) )
{
XSPRITE * pXDude = & actor - > x ( ) ;
int x = pXDude - > targetX ;
int y = pXDude - > targetY ;
int z = pXDude - > targetZ ;
2021-09-01 19:52:54 +00:00
auto target = actor - > GetTarget ( ) ;
2020-03-01 20:36:28 +00:00
2021-05-05 10:55:52 +00:00
aiInitSprite ( actor ) ;
2020-02-07 19:47:43 +00:00
2021-09-01 19:52:54 +00:00
if ( target ! = nullptr )
{
2021-09-02 21:36:57 +00:00
pXDude - > targetX = x ;
pXDude - > targetY = y ;
pXDude - > targetZ = z ;
2021-09-01 19:52:54 +00:00
actor - > SetTarget ( target ) ;
2021-09-02 21:36:57 +00:00
aiActivateDude ( actor ) ;
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
if ( pXSource - > data2 = = 1 )
{
if ( pPlayer )
{
2021-07-18 07:35:11 +00:00
pPlayer - > angle . settarget ( pSource - > ang ) ;
2021-07-18 09:26:24 +00:00
pPlayer - > angle . lockinput ( ) ;
2021-07-18 07:35:11 +00:00
}
2021-09-02 21:36:57 +00:00
else if ( isDude ) pXSource - > goalAng = pSprite - > ang = pSource - > ang ;
2020-02-07 19:47:43 +00:00
else pSprite - > ang = pSource - > ang ;
}
if ( pXSource - > data3 = = 1 )
2021-09-02 21:36:57 +00:00
actor - > xvel ( ) = actor - > yvel ( ) = actor - > zvel ( ) = 0 ;
2020-02-07 19:47:43 +00:00
viewBackupSpriteLoc ( pSprite - > index , pSprite ) ;
if ( pXSource - > data4 > 0 )
sfxPlay3DSound ( pSource , pXSource - > data4 , - 1 , 0 ) ;
2021-09-02 20:13:31 +00:00
if ( pPlayer )
{
2020-02-07 19:47:43 +00:00
playerResetInertia ( pPlayer ) ;
if ( pXSource - > data2 = = 1 )
pPlayer - > zViewVel = pPlayer - > zWeaponVel = 0 ;
}
}
2021-09-02 21:42:25 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void useEffectGen ( DBloodActor * sourceactor , DBloodActor * actor )
{
if ( ! actor ) actor = sourceactor ;
auto pSprite = & actor - > s ( ) ;
auto pSource = & sourceactor - > s ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
2020-03-01 20:36:28 +00:00
int fxId = ( pXSource - > data3 < = 0 ) ? pXSource - > data2 : pXSource - > data2 + Random ( pXSource - > data3 + 1 ) ;
2021-07-19 21:15:26 +00:00
2021-09-02 21:42:25 +00:00
if ( ! actor - > hasX ( ) ) return ;
2021-09-02 20:13:31 +00:00
else if ( fxId > = kEffectGenCallbackBase )
{
2021-07-19 21:15:26 +00:00
int length = sizeof ( gEffectGenCallbacks ) / sizeof ( gEffectGenCallbacks [ 0 ] ) ;
2021-09-02 20:13:31 +00:00
if ( fxId < kEffectGenCallbackBase + length )
{
2021-07-19 21:15:26 +00:00
fxId = gEffectGenCallbacks [ fxId - kEffectGenCallbackBase ] ;
2021-08-27 08:18:33 +00:00
evKillActor ( actor , ( CALLBACK_ID ) fxId ) ;
evPostActor ( actor , 0 , ( CALLBACK_ID ) fxId ) ;
2021-07-19 21:15:26 +00:00
}
2021-09-02 20:13:31 +00:00
}
else if ( valueIsBetween ( fxId , 0 , kFXMax ) )
{
2021-09-02 21:42:25 +00:00
int pos , top , bottom ;
GetActorExtents ( actor , & top , & bottom ) ;
2021-08-31 23:46:42 +00:00
DBloodActor * pEffect = nullptr ;
2020-03-01 20:36:28 +00:00
// select where exactly effect should be spawned
2021-09-02 20:13:31 +00:00
switch ( pXSource - > data4 )
{
2020-03-01 20:36:28 +00:00
case 1 :
pos = bottom ;
break ;
2020-05-19 19:42:29 +00:00
case 2 : // middle
2020-11-22 23:18:07 +00:00
pos = pSprite - > z + ( tileHeight ( pSprite - > picnum ) / 2 + tileTopOffset ( pSprite - > picnum ) ) ;
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
2021-09-02 20:13:31 +00:00
if ( ( pEffect = gFX . fxSpawnActor ( ( FX_ID ) fxId , pSprite - > sectnum , pSprite - > x , pSprite - > y , pos , 0 ) ) ! = NULL )
{
2021-08-31 23:46:42 +00:00
auto pEffectSpr = & pEffect - > s ( ) ;
pEffectSpr - > owner = pSource - > index ;
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
if ( pSource - > flags & kModernTypeFlag1 )
{
2021-08-31 23:46:42 +00:00
pEffectSpr - > pal = pSource - > pal ;
pEffectSpr - > xoffset = pSource - > xoffset ;
pEffectSpr - > yoffset = pSource - > yoffset ;
pEffectSpr - > xrepeat = pSource - > xrepeat ;
pEffectSpr - > yrepeat = pSource - > yrepeat ;
pEffectSpr - > shade = pSource - > shade ;
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
if ( pSource - > flags & kModernTypeFlag2 )
{
2021-08-31 23:46:42 +00:00
pEffectSpr - > cstat = pSource - > cstat ;
if ( pEffectSpr - > cstat & CSTAT_SPRITE_INVISIBLE )
pEffectSpr - > cstat & = ~ CSTAT_SPRITE_INVISIBLE ;
2020-05-05 18:50:14 +00:00
}
2020-03-01 20:36:28 +00:00
2021-08-31 23:46:42 +00:00
if ( pEffectSpr - > cstat & CSTAT_SPRITE_ONE_SIDED )
pEffectSpr - > cstat & = ~ CSTAT_SPRITE_ONE_SIDED ;
2020-02-07 19:47:43 +00:00
}
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-02-07 19:47:43 +00:00
2021-09-02 21:42:25 +00:00
void useSectorWindGen ( DBloodActor * sourceactor , sectortype * pSector )
{
auto pSource = & sourceactor - > s ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
2021-09-02 21:42:25 +00:00
XSECTOR * pXSector = nullptr ;
int nXSector = 0 ;
2020-03-01 20:36:28 +00:00
2021-09-02 20:13:31 +00:00
if ( pSector ! = nullptr )
{
2020-02-07 19:47:43 +00:00
pXSector = & xsector [ pSector - > extra ] ;
nXSector = sector [ pXSector - > reference ] . extra ;
2021-09-02 20:13:31 +00:00
}
else if ( xsectRangeIsFine ( sector [ pSource - > sectnum ] . extra ) )
{
2020-03-01 20:36:28 +00:00
pXSector = & xsector [ sector [ pSource - > sectnum ] . extra ] ;
nXSector = sector [ pXSector - > reference ] . extra ;
2021-09-02 20:13:31 +00:00
}
else
{
2021-07-19 21:15:26 +00:00
nXSector = dbInsertXSector ( pSource - > sectnum ) ;
2020-03-01 20:36:28 +00:00
pXSector = & xsector [ nXSector ] ; pXSector - > windAlways = 1 ;
2020-02-07 19:47:43 +00:00
}
2021-07-19 21:15:26 +00:00
int windVel = ClipRange ( pXSource - > data2 , 0 , 32767 ) ;
if ( ( pXSource - > data1 & 0x0001 ) )
windVel = nnExtRandom ( 0 , windVel ) ;
// process vertical wind in nnExtProcessSuperSprites();
2021-09-02 20:13:31 +00:00
if ( ( pSource - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) )
{
2021-07-19 21:15:26 +00:00
pXSource - > sysData2 = windVel < < 1 ;
return ;
}
pXSector - > windVel = windVel ;
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
int ang = pSource - > ang ;
2021-09-02 20:13:31 +00:00
if ( pXSource - > data4 < = 0 )
{
if ( ( pXSource - > data1 & 0x0002 ) )
{
2020-03-01 20:36:28 +00:00
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
}
}
else if ( pSource - > cstat & 0x2000 ) pSource - > ang + = pXSource - > data4 ;
else if ( pSource - > cstat & 0x4000 ) pSource - > ang - = pXSource - > data4 ;
2021-09-02 20:13:31 +00:00
else if ( pXSource - > sysData1 = = 0 )
{
2020-03-01 20:36:28 +00:00
if ( ( ang + = pXSource - > data4 ) > = kAng180 ) pXSource - > sysData1 = 1 ;
pSource - > ang = ClipHigh ( ang , kAng180 ) ;
2021-09-02 20:13:31 +00:00
}
else
{
2020-03-01 20:36:28 +00:00
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 ;
2021-09-02 20:13:31 +00:00
if ( pXSource - > data3 > 0 & & pXSource - > data3 < 4 )
{
switch ( pXSource - > data3 )
{
2021-07-19 21:15:26 +00:00
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 ;
2020-02-07 19:47:43 +00:00
}
2020-11-26 16:19:42 +00:00
if ( pXSector - > panCeiling )
{
StartInterpolation ( pXSector - > reference , Interp_Sect_CeilingPanX ) ;
StartInterpolation ( pXSector - > reference , Interp_Sect_CeilingPanY ) ;
}
if ( pXSector - > panFloor )
{
StartInterpolation ( pXSector - > reference , Interp_Sect_FloorPanX ) ;
StartInterpolation ( pXSector - > reference , Interp_Sect_FloorPanY ) ;
}
2020-02-07 19:47:43 +00:00
short oldPan = pXSector - > panVel ;
pXSector - > panAngle = pXSector - > windAng ;
pXSector - > panVel = pXSector - > windVel ;
// add to panList if panVel was set to 0 previously
2021-09-02 20:13:31 +00:00
if ( oldPan = = 0 & & pXSector - > panVel ! = 0 & & panCount < kMaxXSectors )
{
2020-02-07 19:47:43 +00:00
int i ;
2021-09-02 20:13:31 +00:00
for ( i = 0 ; i < panCount ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( panList [ i ] ! = nXSector ) continue ;
break ;
}
if ( i = = panCount )
2020-11-26 16:19:42 +00:00
{
2020-02-07 19:47:43 +00:00
panList [ panCount + + ] = nXSector ;
2020-11-26 16:19:42 +00:00
}
2020-02-07 19:47:43 +00:00
}
}
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 21:52:24 +00:00
void useSpriteDamager ( DBloodActor * sourceactor , int objType , int objIndex , DBloodActor * targetactor )
{
auto pSource = & sourceactor - > s ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
2020-12-06 20:56:09 +00:00
sectortype * pSector = & sector [ pSource - > sectnum ] ;
2021-11-09 23:05:42 +00:00
int top , bottom ;
2020-12-06 20:56:09 +00:00
bool floor , ceil , wall , enter ;
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
switch ( objType )
{
2020-12-06 20:56:09 +00:00
case OBJ_SPRITE :
2021-09-02 21:52:24 +00:00
damageSprites ( sourceactor , targetactor ) ;
2020-12-06 20:56:09 +00:00
break ;
case OBJ_SECTOR :
2021-09-02 18:23:51 +00:00
{
2021-09-02 21:52:24 +00:00
GetActorExtents ( sourceactor , & top , & bottom ) ;
2021-09-02 18:23:51 +00:00
floor = ( bottom > = pSector - > floorz ) ;
ceil = ( top < = pSector - > ceilingz ) ;
wall = ( pSource - > cstat & 0x10 ) ;
enter = ( ! floor & & ! ceil & & ! wall ) ;
2021-09-02 21:52:24 +00:00
BloodSectIterator it ( objIndex ) ;
while ( auto iactor = it . Next ( ) )
2021-09-02 18:23:51 +00:00
{
2021-09-02 21:52:24 +00:00
auto & hit = iactor - > hit ( ) ;
2021-09-02 18:23:51 +00:00
2021-09-02 21:52:24 +00:00
if ( ! iactor - > IsDudeActor ( ) | | ! iactor - > hasX ( ) )
2020-12-06 20:56:09 +00:00
continue ;
else if ( enter )
2021-09-02 21:52:24 +00:00
damageSprites ( sourceactor , iactor ) ;
2021-09-02 18:23:51 +00:00
else if ( floor & & hit . florhit . type = = kHitSector & & hit . florhit . index = = objIndex )
2021-09-02 21:52:24 +00:00
damageSprites ( sourceactor , iactor ) ;
2021-09-02 18:23:51 +00:00
else if ( ceil & & hit . ceilhit . type = = kHitSector & & hit . ceilhit . index = = objIndex )
2021-09-02 21:52:24 +00:00
damageSprites ( sourceactor , iactor ) ;
2021-09-02 18:23:51 +00:00
else if ( wall & & hit . hit . type = = kHitWall & & sectorofwall ( hit . hit . index ) = = objIndex )
2021-09-02 21:52:24 +00:00
damageSprites ( sourceactor , iactor ) ;
2020-12-06 20:56:09 +00:00
}
break ;
2021-09-02 18:23:51 +00:00
}
2020-12-06 20:56:09 +00:00
case - 1 :
2021-09-02 21:52:24 +00:00
{
BloodStatIterator it ( kStatDude ) ;
while ( auto iactor = it . Next ( ) )
{
if ( iactor - > s ( ) . statnum ! = kStatDude ) continue ;
switch ( pXSource - > data1 )
{
2020-12-06 20:56:09 +00:00
case 667 :
2021-09-02 21:52:24 +00:00
if ( iactor - > IsPlayerActor ( ) ) continue ;
damageSprites ( sourceactor , iactor ) ;
2020-12-06 20:56:09 +00:00
break ;
case 668 :
2021-09-02 21:52:24 +00:00
if ( iactor - > IsPlayerActor ( ) ) continue ;
damageSprites ( sourceactor , iactor ) ;
2020-12-06 20:56:09 +00:00
break ;
default :
2021-09-02 21:52:24 +00:00
damageSprites ( sourceactor , iactor ) ;
2020-12-06 20:56:09 +00:00
break ;
}
}
break ;
2021-11-09 23:05:42 +00:00
}
2021-09-02 21:52:24 +00:00
}
2020-12-06 20:56:09 +00:00
}
2020-02-07 19:47:43 +00:00
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 21:52:24 +00:00
void damageSprites ( DBloodActor * sourceactor , DBloodActor * actor )
2021-09-16 19:36:34 +00:00
{
spritetype * pSource = & sourceactor - > s ( ) ;
2021-09-02 21:52:24 +00:00
if ( ! actor - > IsDudeActor ( ) | | ! actor - > hasX ( ) | | actor - > x ( ) . health < = 0 | | sourceactor - > x ( ) . data3 < 0 )
2020-05-05 18:50:14 +00:00
return ;
2020-05-22 16:28:03 +00:00
2021-07-19 21:15:26 +00:00
int health = 0 ;
2021-09-02 21:52:24 +00:00
auto pSprite = & actor - > s ( ) ;
auto pXSprite = & actor - > x ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
2021-09-16 19:36:34 +00:00
PLAYER * pPlayer = getPlayerById ( pSprite - > type ) ;
2020-05-05 18:50:14 +00:00
int dmgType = ( pXSource - > data2 > = kDmgFall ) ? ClipHigh ( pXSource - > data2 , kDmgElectric ) : - 1 ;
2021-09-02 21:52:24 +00:00
int dmg = pXSprite - > health < < 4 ;
int armor [ 3 ] ;
2020-05-05 18:50:14 +00:00
2020-12-06 07:36:54 +00:00
bool godMode = ( pPlayer & & ( ( dmgType > = 0 & & pPlayer - > damageControl [ dmgType ] ) | | powerupCheck ( pPlayer , kPwUpDeathMask ) | | pPlayer - > godMode ) ) ; // kneeling
2020-05-05 18:50:14 +00:00
if ( godMode | | pXSprite - > locked ) return ;
2021-09-02 20:13:31 +00:00
else if ( pXSource - > data3 )
{
2020-05-05 18:50:14 +00:00
if ( pSource - > flags & kModernTypeFlag1 ) dmg = ClipHigh ( pXSource - > data3 < < 1 , 65535 ) ;
2020-05-22 16:28:03 +00:00
else if ( pXSprite - > sysData2 > 0 ) dmg = ( ClipHigh ( pXSprite - > sysData2 < < 4 , 65535 ) * pXSource - > data3 ) / kPercFull ;
else dmg = ( ( getDudeInfo ( pSprite - > type ) - > startHealth < < 4 ) * pXSource - > data3 ) / kPercFull ;
2021-07-19 21:15:26 +00:00
health = pXSprite - > health - dmg ;
2020-05-22 16:28:03 +00:00
}
2020-05-05 18:50:14 +00:00
2021-09-02 20:13:31 +00:00
if ( dmgType > = kDmgFall )
{
if ( dmg < ( int ) pXSprite - > health < < 4 )
{
if ( ! nnExtIsImmune ( actor , dmgType , 0 ) )
2021-08-27 12:01:51 +00:00
{
2021-09-02 20:13:31 +00:00
if ( pPlayer )
2021-08-27 12:01:51 +00:00
{
2021-07-19 21:15:26 +00:00
playerDamageArmor ( pPlayer , ( DAMAGE_TYPE ) dmgType , dmg ) ;
for ( int i = 0 ; i < 3 ; armor [ i ] = pPlayer - > armor [ i ] , pPlayer - > armor [ i ] = 0 , i + + ) ;
2021-08-27 12:01:51 +00:00
actDamageSprite ( sourceactor , actor , ( DAMAGE_TYPE ) dmgType , dmg ) ;
2021-07-19 21:15:26 +00:00
for ( int i = 0 ; i < 3 ; pPlayer - > armor [ i ] = armor [ i ] , i + + ) ;
2020-12-05 22:49:51 +00:00
}
2021-09-02 20:13:31 +00:00
else
{
2021-09-16 19:36:34 +00:00
actDamageSprite ( sourceactor , actor , ( DAMAGE_TYPE ) dmgType , dmg ) ;
2021-07-19 21:15:26 +00:00
}
2020-05-22 16:28:03 +00:00
}
2021-09-02 20:13:31 +00:00
else
{
//Printf(PRINT_HIGH, "Dude type %d is immune to damage type %d!", pSprite->type, dmgType);
2020-12-05 22:49:51 +00:00
}
2020-05-22 16:28:03 +00:00
}
2021-09-02 21:52:24 +00:00
else if ( ! pPlayer ) actKillDude ( sourceactor , actor , ( DAMAGE_TYPE ) dmgType , dmg ) ;
else playerDamageSprite ( sourceactor , pPlayer , ( DAMAGE_TYPE ) dmgType , dmg ) ;
2020-05-05 18:50:14 +00:00
}
2021-07-19 21:15:26 +00:00
else if ( ( pXSprite - > health = ClipLow ( health , 1 ) ) > 16 ) ;
2021-09-02 21:52:24 +00:00
else if ( ! pPlayer ) actKillDude ( sourceactor , actor , kDamageBullet , dmg ) ;
else playerDamageSprite ( sourceactor , pPlayer , kDamageBullet , dmg ) ;
2021-07-19 21:15:26 +00:00
2021-09-02 20:13:31 +00:00
if ( pXSprite - > health > 0 )
{
2021-07-19 21:15:26 +00:00
if ( ! ( pSource - > flags & kModernTypeFlag8 ) )
pXSprite - > health = health ;
bool showEffects = ! ( pSource - > flags & kModernTypeFlag2 ) ; // show it by default
bool forceRecoil = ( pSource - > flags & kModernTypeFlag4 ) ;
2021-09-02 20:13:31 +00:00
if ( showEffects )
{
switch ( dmgType )
{
2021-07-19 21:15:26 +00:00
case kDmgBurn :
if ( pXSprite - > burnTime > 0 ) break ;
2021-09-02 21:52:24 +00:00
actBurnSprite ( sourceactor , actor , ClipLow ( dmg > > 1 , 128 ) ) ;
2021-08-27 07:44:47 +00:00
evKillActor ( actor , kCallbackFXFlameLick ) ;
evPostActor ( actor , 0 , kCallbackFXFlameLick ) ; // show flames
2021-07-19 21:15:26 +00:00
break ;
case kDmgElectric :
forceRecoil = true ; // show tesla recoil animation
break ;
case kDmgBullet :
2021-08-27 07:44:47 +00:00
evKillActor ( actor , kCallbackFXBloodSpurt ) ;
2021-09-02 20:13:31 +00:00
for ( int i = 1 ; i < 6 ; i + + )
{
2021-07-19 21:15:26 +00:00
if ( Chance ( 0x16000 > > i ) )
2021-08-26 22:07:33 +00:00
fxSpawnBlood ( actor , dmg < < 4 ) ;
2021-07-19 21:15:26 +00:00
}
break ;
case kDmgChoke :
if ( ! pPlayer | | ! Chance ( 0x2000 ) ) break ;
else pPlayer - > blindEffect + = dmg < < 2 ;
}
}
2021-09-02 20:13:31 +00:00
if ( forceRecoil & & ! pPlayer )
{
2021-07-19 21:15:26 +00:00
pXSprite - > data3 = 32767 ;
2021-10-06 20:20:57 +00:00
actor - > dudeExtra . teslaHit = ( dmgType = = kDmgElectric ) ? 1 : 0 ;
2021-07-19 21:15:26 +00:00
if ( pXSprite - > aiState - > stateType ! = kAiStateRecoil )
2021-09-02 21:52:24 +00:00
RecoilDude ( actor ) ;
2021-07-19 21:15:26 +00:00
}
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 22:04:17 +00:00
void useSeqSpawnerGen ( DBloodActor * sourceactor , int objType , int index , DBloodActor * iactor )
{
auto pXSource = & sourceactor - > x ( ) ;
if ( pXSource - > data2 > 0 & & ! getSequence ( pXSource - > data2 ) )
{
2020-10-11 10:38:17 +00:00
Printf ( PRINT_HIGH , " Missing sequence #%d " , pXSource - > data2 ) ;
2020-02-07 19:47:43 +00:00
return ;
}
2021-08-27 20:13:17 +00:00
spritetype * pSource = & sourceactor - > s ( ) ;
2021-09-02 20:13:31 +00:00
switch ( objType )
{
2020-02-07 19:47:43 +00:00
case OBJ_SECTOR :
2021-09-02 20:13:31 +00:00
if ( pXSource - > data2 < = 0 )
{
2020-02-07 19:47:43 +00:00
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 1 )
seqKill ( 2 , sector [ index ] . extra ) ;
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 2 )
seqKill ( 1 , sector [ index ] . extra ) ;
2021-09-02 20:13:31 +00:00
}
else
{
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 20:13:31 +00:00
2020-02-07 19:47:43 +00:00
case OBJ_WALL :
2021-09-02 20:13:31 +00:00
if ( pXSource - > data2 < = 0 )
{
2020-02-07 19:47:43 +00:00
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 ) ;
2021-09-02 20:13:31 +00:00
}
else
{
2020-02-07 19:47:43 +00:00
if ( pXSource - > data3 = = 3 | | pXSource - > data3 = = 1 )
seqSpawn ( pXSource - > data2 , 0 , wall [ index ] . extra , - 1 ) ;
2021-09-02 20:13:31 +00:00
2020-02-07 19:47:43 +00:00
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 ) ;
}
}
2021-09-02 20:13:31 +00:00
if ( pXSource - > data4 > 0 )
{
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 20:13:31 +00:00
2020-02-07 19:47:43 +00:00
case OBJ_SPRITE :
2021-09-02 22:04:17 +00:00
{
auto pSprite = & iactor - > s ( ) ;
if ( pXSource - > data2 < = 0 ) seqKill ( iactor ) ;
else if ( sectRangeIsFine ( pSprite - > sectnum ) )
{
if ( pXSource - > data3 > 0 )
{
int nSpawned = InsertSprite ( pSprite - > sectnum , kStatDecoration ) ;
auto spawned = & bloodActors [ nSpawned ] ;
2021-08-27 20:13:17 +00:00
auto pSpawned = & spawned - > s ( ) ;
int top , bottom ; GetSpriteExtents ( & sprite [ index ] , & top , & bottom ) ;
2021-09-02 22:04:17 +00:00
pSpawned - > x = pSprite - > x ;
pSpawned - > y = pSprite - > y ;
switch ( pXSource - > data3 )
{
default :
pSpawned - > z = pSprite - > z ;
2021-08-27 20:13:17 +00:00
break ;
case 2 :
pSpawned - > z = bottom ;
break ;
case 3 :
pSpawned - > z = top ;
break ;
case 4 :
2021-09-02 22:04:17 +00:00
pSpawned - > z = pSprite - > z + tileHeight ( pSprite - > picnum ) / 2 + tileTopOffset ( pSprite - > picnum ) ;
2021-08-27 20:13:17 +00:00
break ;
case 5 :
case 6 :
2021-09-02 22:04:17 +00:00
if ( ! sectRangeIsFine ( pSprite - > sectnum ) ) pSpawned - > z = top ;
2021-08-27 20:13:17 +00:00
else pSpawned - > z = ( pXSource - > data3 = = 5 ) ? sector [ pSpawned - > sectnum ] . floorz : sector [ pSpawned - > sectnum ] . ceilingz ;
break ;
}
2020-12-06 20:56:09 +00:00
2021-09-02 22:04:17 +00:00
if ( spawned ! = nullptr )
{
2020-12-06 20:56:09 +00:00
2021-08-27 20:13:17 +00:00
spawned - > addX ( ) ;
seqSpawn ( pXSource - > data2 , spawned , - 1 ) ;
2021-09-02 20:13:31 +00:00
if ( pSource - > flags & kModernTypeFlag1 )
{
2021-08-27 20:13:17 +00:00
pSpawned - > pal = pSource - > pal ;
pSpawned - > shade = pSource - > shade ;
pSpawned - > xrepeat = pSource - > xrepeat ;
pSpawned - > yrepeat = pSource - > yrepeat ;
pSpawned - > xoffset = pSource - > xoffset ;
pSpawned - > yoffset = pSource - > yoffset ;
2020-12-06 20:56:09 +00:00
}
2021-09-02 20:13:31 +00:00
if ( pSource - > flags & kModernTypeFlag2 )
{
2021-08-27 20:13:17 +00:00
pSpawned - > cstat | = pSource - > cstat ;
}
2020-12-06 20:56:09 +00:00
2021-08-27 20:13:17 +00:00
// should be: the more is seqs, the shorter is timer
evPostActor ( spawned , 1000 , kCallbackRemove ) ;
2020-12-06 20:56:09 +00:00
}
2021-08-27 20:13:17 +00:00
}
2021-09-02 20:13:31 +00:00
else
{
2021-09-02 22:04:17 +00:00
seqSpawn ( pXSource - > data2 , iactor , - 1 ) ;
2021-08-27 20:13:17 +00:00
}
2021-09-02 20:13:31 +00:00
2020-05-05 18:50:14 +00:00
if ( pXSource - > data4 > 0 )
2021-09-02 22:04:17 +00:00
sfxPlay3DSound ( iactor , pXSource - > data4 , - 1 , 0 ) ;
2020-02-07 19:47:43 +00:00
}
return ;
}
2021-09-02 22:04:17 +00:00
}
2020-02-07 19:47:43 +00:00
}
2021-09-02 20:13:31 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 22:32:45 +00:00
int condSerialize ( DBloodActor * objActor )
{
// this one only gets used for players
return kCondSerialSprite + objActor - > GetIndex ( ) ;
}
void condPush ( DBloodActor * actor , int objType , int objIndex , DBloodActor * objActor ) {
int r = - 1 ;
2020-03-30 19:54:28 +00:00
switch ( objType ) {
2021-10-13 22:32:45 +00:00
case OBJ_SECTOR : r = kCondSerialSector + objIndex ; break ;
case OBJ_WALL : r = kCondSerialWall + objIndex ; break ;
case OBJ_SPRITE : r = kCondSerialSprite + objActor - > GetIndex ( ) ; break ;
2020-03-30 19:54:28 +00:00
}
2021-10-13 22:32:45 +00:00
if ( r = = - 1 ) I_Error ( " Unknown object type %d, index %d " , objType , objIndex ) ;
2021-10-13 23:40:46 +00:00
// condition-target
2021-10-13 22:32:45 +00:00
else actor - > x ( ) . targetX = r ;
2020-03-30 19:54:28 +00:00
}
2021-10-13 22:32:45 +00:00
void condUnserialize ( DBloodActor * serialActor , int * objType , int * objIndex , DBloodActor * * objActor ) {
2021-10-13 23:40:46 +00:00
// condition-target
2021-10-13 22:32:45 +00:00
int serial = serialActor - > x ( ) . targetX ;
2020-03-30 19:54:28 +00:00
if ( serial > = kCondSerialSector & & serial < kCondSerialWall ) {
* objIndex = serial - kCondSerialSector ;
* objType = OBJ_SECTOR ;
2021-10-13 22:32:45 +00:00
* objActor = nullptr ;
2020-03-30 19:54:28 +00:00
} else if ( serial > = kCondSerialWall & & serial < kCondSerialSprite ) {
* objIndex = serial - kCondSerialWall ;
* objType = OBJ_WALL ;
2021-10-13 22:32:45 +00:00
* objActor = nullptr ;
2020-03-30 19:54:28 +00:00
} else if ( serial > = kCondSerialSprite & & serial < kCondSerialMax ) {
* objIndex = serial - kCondSerialSprite ;
* objType = OBJ_SPRITE ;
2021-10-13 22:32:45 +00:00
* objActor = & bloodActors [ * objIndex ] ;
2020-03-30 19:54:28 +00:00
} else {
2020-10-11 10:22:36 +00:00
I_Error ( " %d is not condition serial! " , serial ) ;
2020-03-30 19:54:28 +00:00
}
}
2021-10-13 22:32:45 +00:00
void condBackup ( DBloodActor * actor )
{
2021-10-13 23:40:46 +00:00
// condition-target
2021-10-13 22:32:45 +00:00
actor - > x ( ) . targetY = actor - > x ( ) . targetX ;
}
void condRestore ( DBloodActor * actor )
{
2021-10-13 23:40:46 +00:00
// condition-target
2021-10-13 22:32:45 +00:00
actor - > x ( ) . targetX = actor - > x ( ) . targetY ;
2020-03-19 19:35:31 +00:00
}
2020-03-30 19:54:28 +00:00
// normal comparison
2021-10-13 22:03:52 +00:00
bool condCmp ( int val , int arg1 , int arg2 , int comOp )
{
2020-03-30 19:54:28 +00:00
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
2021-10-13 22:03:52 +00:00
else if ( comOp & CSTAT_SPRITE_BLOCK )
{
2020-10-11 10:22:36 +00:00
if ( arg1 > arg2 ) I_Error ( " Value of argument #1 (%d) must be less than value of argument #2 (%d) " , arg1 , arg2 ) ;
2020-05-22 16:28:03 +00:00
return ( val > = arg1 & & val < = arg2 ) ;
}
else return ( val = = arg1 ) ;
2020-03-19 19:35:31 +00:00
}
2021-10-13 21:58:04 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void condError ( DBloodActor * aCond , const char * pzFormat , . . . )
{
char buffer [ 256 ] ;
char buffer2 [ 512 ] ;
FString condType = " Unknown " ;
auto pXCond = & aCond - > x ( ) ;
for ( int i = 0 ; i < 7 ; i + + )
{
2021-07-19 21:15:26 +00:00
if ( pXCond - > data1 < gCondTypeNames [ i ] . rng1 | | pXCond - > data1 > = gCondTypeNames [ i ] . rng2 ) continue ;
2021-07-25 09:44:07 +00:00
condType = gCondTypeNames [ i ] . name ;
condType . ToUpper ( ) ;
2021-07-19 21:15:26 +00:00
break ;
}
2021-10-08 17:09:26 +00:00
snprintf ( buffer , 256 , " \n \n %s CONDITION RX: %d, TX: %d, SPRITE: #%d RETURNS: \n " , condType . GetChars ( ) , pXCond - > rxID , pXCond - > txID , pXCond - > reference ) ;
2020-05-05 18:50:14 +00:00
va_list args ;
va_start ( args , pzFormat ) ;
2021-07-25 09:50:23 +00:00
vsnprintf ( buffer2 , 512 , pzFormat , args ) ;
2020-10-11 10:22:36 +00:00
I_Error ( " %s%s " , buffer , buffer2 ) ;
2020-05-05 18:50:14 +00:00
}
2021-10-13 22:03:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 22:46:15 +00:00
bool condCheckGame ( DBloodActor * aCond , const EVENT & event , int cmpOp , bool PUSH )
{
auto pXCond = & aCond - > x ( ) ;
2020-05-22 16:28:03 +00:00
2020-03-13 20:59:13 +00:00
//int var = -1;
2021-10-13 22:03:52 +00:00
int cond = pXCond - > data1 - kCondGameBase ;
int arg1 = pXCond - > data2 ;
int arg2 = pXCond - > data3 ;
int arg3 = pXCond - > data4 ;
2021-07-19 21:15:26 +00:00
switch ( cond ) {
case 1 : return condCmp ( gFrameCount / ( kTicsPerSec * 60 ) , arg1 , arg2 , cmpOp ) ; // compare level minutes
case 2 : return condCmp ( ( gFrameCount / kTicsPerSec ) % 60 , arg1 , arg2 , cmpOp ) ; // compare level seconds
case 3 : return condCmp ( ( ( gFrameCount % kTicsPerSec ) * 33 ) / 10 , arg1 , arg2 , cmpOp ) ; // compare level mseconds
case 4 : return condCmp ( gFrameCount , arg1 , arg2 , cmpOp ) ; // compare level time (unsafe)
case 5 : return condCmp ( gKillMgr . Kills , arg1 , arg2 , cmpOp ) ; // compare current global kills counter
case 6 : return condCmp ( gKillMgr . TotalKills , arg1 , arg2 , cmpOp ) ; // compare total global kills counter
case 7 : return condCmp ( gSecretMgr . Founds , arg1 , arg2 , cmpOp ) ; // compare how many secrets found
case 8 : return condCmp ( gSecretMgr . Total , arg1 , arg2 , cmpOp ) ; // compare total secrets
/*----------------------------------------------------------------------------------------------------------------------------------*/
case 20 : return condCmp ( gVisibility , arg1 , arg2 , cmpOp ) ; // compare global visibility value
/*----------------------------------------------------------------------------------------------------------------------------------*/
case 30 : return Chance ( ( 0x10000 * arg3 ) / kPercFull ) ; // check chance
case 31 : return condCmp ( nnExtRandom ( arg1 , arg2 ) , arg1 , arg2 , cmpOp ) ;
/*----------------------------------------------------------------------------------------------------------------------------------*/
case 47 : return condCmp ( gStatCount [ ClipRange ( arg3 , 0 , kMaxStatus ) ] , arg1 , arg2 , cmpOp ) ; // compare counter of specific statnum sprites
case 48 : return condCmp ( Numsprites , arg1 , arg2 , cmpOp ) ; // compare counter of total sprites
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition id (%d)! " , cond ) ;
2021-07-19 21:15:26 +00:00
return false ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 22:46:15 +00:00
bool condCheckMixed ( DBloodActor * aCond , const EVENT & event , int cmpOp , bool PUSH )
2021-09-03 19:23:22 +00:00
{
2021-10-13 22:46:15 +00:00
auto pXCond = & aCond - > x ( ) ;
2021-07-19 21:15:26 +00:00
//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 ;
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( aCond , & objType , & objIndex , & objActor ) ;
2020-03-30 19:54:28 +00:00
2021-10-13 22:03:52 +00:00
switch ( cond )
{
2020-05-22 16:28:03 +00:00
case 0 : return ( objType = = OBJ_SECTOR & & sectRangeIsFine ( objIndex ) ) ; // is a sector?
case 5 : return ( objType = = OBJ_WALL & & wallRangeIsFine ( objIndex ) ) ; // is a wall?
2021-10-13 22:46:15 +00:00
case 10 : return ( objType = = OBJ_SPRITE & & objActor ! = nullptr ) ; // is a sprite?
2020-04-01 20:19:50 +00:00
case 15 : // x-index is fine?
2021-10-13 22:46:15 +00:00
switch ( objType )
{
2020-04-01 20:19:50 +00:00
case OBJ_WALL : return xwallRangeIsFine ( wall [ objIndex ] . extra ) ;
2021-10-13 22:46:15 +00:00
case OBJ_SPRITE : return objActor & & objActor - > hasX ( ) ;
2020-04-01 20:19:50 +00:00
case OBJ_SECTOR : return xsectRangeIsFine ( sector [ objIndex ] . extra ) ;
}
break ;
2020-03-30 19:54:28 +00:00
case 20 : // type in a range?
2021-10-13 22:46:15 +00:00
switch ( objType )
{
2020-03-13 20:59:13 +00:00
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 :
2021-10-13 22:46:15 +00:00
return condCmp ( objActor - > s ( ) . type , 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 ;
2021-01-07 12:33:20 +00:00
case 24 :
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 :
2021-10-13 22:03:52 +00:00
switch ( objType )
{
case OBJ_WALL :
{
2020-04-01 20:19:50 +00:00
walltype * pObj = & wall [ objIndex ] ;
2021-10-13 22:03:52 +00:00
switch ( cond )
{
2021-01-07 12:33:20 +00:00
case 24 : return condCmp ( surfType [ wall [ objIndex ] . picnum ] , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
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 ) ;
2020-11-26 07:38:59 +00:00
case 31 : return condCmp ( pObj - > xpan ( ) , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
case 32 : return condCmp ( pObj - > yrepeat , arg1 , arg2 , cmpOp ) ;
2020-11-26 07:38:59 +00:00
case 33 : return condCmp ( pObj - > ypan ( ) , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
}
break ;
}
2021-10-13 22:03:52 +00:00
case OBJ_SPRITE :
{
2021-10-13 22:46:15 +00:00
spritetype * pObj = & objActor - > s ( ) ;
2021-10-13 22:03:52 +00:00
switch ( cond )
{
2021-10-13 22:46:15 +00:00
case 24 : return condCmp ( surfType [ pObj - > picnum ] , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
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 ;
}
2021-10-13 22:03:52 +00:00
case OBJ_SECTOR :
{
2020-04-01 20:19:50 +00:00
sectortype * pObj = & sector [ objIndex ] ;
2021-10-13 22:03:52 +00:00
switch ( cond )
{
2021-01-07 12:33:20 +00:00
case 24 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2021-10-13 22:46:15 +00:00
default : return ( condCmp ( surfType [ pObj - > floorpicnum ] , arg1 , arg2 , cmpOp ) | | condCmp ( surfType [ pObj - > ceilingpicnum ] , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( surfType [ pObj - > floorpicnum ] , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( surfType [ pObj - > ceilingpicnum ] , arg1 , arg2 , cmpOp ) ;
2021-01-07 12:33:20 +00:00
}
break ;
2020-05-05 18:50:14 +00:00
case 25 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2020-05-05 18:50:14 +00:00
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 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2020-05-05 18:50:14 +00:00
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 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2020-05-05 18:50:14 +00:00
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 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2020-05-05 18:50:14 +00:00
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 ) ;
2020-11-25 19:52:06 +00:00
case 30 : return condCmp ( pObj - > floorxpan ( ) , arg1 , arg2 , cmpOp ) ;
case 31 : return condCmp ( pObj - > ceilingxpan ( ) , arg1 , arg2 , cmpOp ) ;
case 32 : return condCmp ( pObj - > floorypan ( ) , arg1 , arg2 , cmpOp ) ;
case 33 : return condCmp ( pObj - > ceilingypan ( ) , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
}
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 :
2021-10-13 22:03:52 +00:00
switch ( objType )
{
case OBJ_WALL :
{
2021-07-19 21:15:26 +00:00
if ( ! xwallRangeIsFine ( wall [ objIndex ] . extra ) )
return condCmp ( 0 , arg1 , arg2 , cmpOp ) ;
2021-10-13 22:46:15 +00:00
auto pObj = & wall [ objIndex ] ;
XWALL * pXObj = & xwall [ pObj - > extra ] ;
2021-10-13 22:03:52 +00:00
switch ( cond )
{
2020-05-05 18:50:14 +00:00
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 ) ;
2021-07-19 21:15:26 +00:00
case 52 : return pXObj - > locked ;
case 53 : return pXObj - > triggerOn ;
case 54 : return pXObj - > triggerOff ;
case 55 : return pXObj - > triggerOnce ;
case 56 : return pXObj - > isTriggered ;
case 57 : return pXObj - > state ;
2020-05-22 16:28:03 +00:00
case 58 : return condCmp ( ( kPercFull * pXObj - > busy ) / 65536 , arg1 , arg2 , cmpOp ) ;
2021-07-19 21:15:26 +00:00
case 59 : return pXObj - > dudeLockout ;
2020-05-05 18:50:14 +00:00
case 70 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2021-10-13 22:46:15 +00:00
default : return ( condCmp ( seqGetID ( 0 , pObj - > extra ) , arg1 , arg2 , cmpOp ) | | condCmp ( seqGetID ( 4 , pObj - > extra ) , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( seqGetID ( 0 , pObj - > extra ) , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( seqGetID ( 4 , pObj - > extra ) , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
}
2020-05-22 16:28:03 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 71 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2021-10-13 22:46:15 +00:00
default : return ( condCmp ( seqGetStatus ( 0 , pObj - > extra ) , arg1 , arg2 , cmpOp ) | | condCmp ( seqGetStatus ( 4 , pObj - > extra ) , arg1 , arg2 , cmpOp ) ) ;
case 1 : return condCmp ( seqGetStatus ( 0 , pObj - > extra ) , arg1 , arg2 , cmpOp ) ;
case 2 : return condCmp ( seqGetStatus ( 4 , pObj - > extra ) , arg1 , arg2 , cmpOp ) ;
2020-05-22 16:28:03 +00:00
}
2020-05-05 18:50:14 +00:00
break ;
}
2020-03-13 20:59:13 +00:00
break ;
}
case OBJ_SPRITE : {
2021-10-13 22:46:15 +00:00
if ( ! objActor - > hasX ( ) )
2021-07-19 21:15:26 +00:00
return condCmp ( 0 , arg1 , arg2 , cmpOp ) ;
2021-10-13 22:46:15 +00:00
XSPRITE * pXObj = & objActor - > x ( ) ;
2021-10-13 22:03:52 +00:00
switch ( cond )
{
2020-05-05 18:50:14 +00:00
case 41 : case 42 :
case 43 : case 44 :
2021-10-13 22:46:15 +00:00
return condCmp ( getDataFieldOfObject ( OBJ_SPRITE , objIndex , objActor , 1 + cond - 41 ) , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
case 50 : return condCmp ( pXObj - > rxID , arg1 , arg2 , cmpOp ) ;
case 51 : return condCmp ( pXObj - > txID , arg1 , arg2 , cmpOp ) ;
2021-07-19 21:15:26 +00:00
case 52 : return pXObj - > locked ;
case 53 : return pXObj - > triggerOn ;
case 54 : return pXObj - > triggerOff ;
case 55 : return pXObj - > triggerOnce ;
case 56 : return pXObj - > isTriggered ;
case 57 : return pXObj - > state ;
2020-05-22 16:28:03 +00:00
case 58 : return condCmp ( ( kPercFull * pXObj - > busy ) / 65536 , arg1 , arg2 , cmpOp ) ;
2021-07-19 21:15:26 +00:00
case 59 : return pXObj - > DudeLockout ;
2021-10-13 22:46:15 +00:00
case 70 : return condCmp ( seqGetID ( objActor ) , arg1 , arg2 , cmpOp ) ;
case 71 : return condCmp ( seqGetStatus ( objActor ) , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
}
2020-03-13 20:59:13 +00:00
break ;
}
2021-10-13 22:03:52 +00:00
case OBJ_SECTOR :
{
2021-07-19 21:15:26 +00:00
if ( xsectRangeIsFine ( sector [ objIndex ] . extra ) )
return condCmp ( 0 , arg1 , arg2 , cmpOp ) ;
XSECTOR * pXObj = & xsector [ sector [ objIndex ] . extra ] ;
2020-05-05 18:50:14 +00:00
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 ) ;
2021-07-19 21:15:26 +00:00
case 52 : return pXObj - > locked ;
case 53 : return pXObj - > triggerOn ;
case 54 : return pXObj - > triggerOff ;
case 55 : return pXObj - > triggerOnce ;
case 56 : return pXObj - > isTriggered ;
case 57 : return pXObj - > state ;
2020-05-22 16:28:03 +00:00
case 58 : return condCmp ( ( kPercFull * pXObj - > busy ) / 65536 , arg1 , arg2 , cmpOp ) ;
2021-07-19 21:15:26 +00:00
case 59 : return pXObj - > dudeLockout ;
2020-05-05 18:50:14 +00:00
case 70 :
2021-10-13 22:03:52 +00:00
// wall???
switch ( arg3 )
{
2020-05-05 18:50:14 +00:00
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-05-22 16:28:03 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 71 :
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2020-05-05 18:50:14 +00:00
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 ) ;
2020-05-22 16:28:03 +00:00
}
2020-05-05 18:50:14 +00:00
break ;
2020-05-22 16:28:03 +00:00
}
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
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition id (%d)! " , cond ) ;
2020-03-13 20:59:13 +00:00
return false ;
}
2021-10-13 22:49:45 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool condCheckSector ( DBloodActor * aCond , int cmpOp , bool PUSH )
{
auto pXCond = & aCond - > x ( ) ;
2020-03-13 20:59:13 +00:00
int var = - 1 ;
2021-10-13 22:49:45 +00:00
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
2021-10-13 22:49:45 +00:00
int objType = - 1 , objIndex = - 1 ;
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( aCond , & objType , & objIndex , & objActor ) ;
2020-03-13 20:59:13 +00:00
if ( objType ! = OBJ_SECTOR | | ! sectRangeIsFine ( objIndex ) )
2021-10-13 21:58:04 +00:00
condError ( aCond , " 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 ;
2021-10-13 22:03:52 +00:00
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?
2021-10-13 22:49:45 +00:00
BloodSectIterator it ( objIndex ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 14:48:32 +00:00
{
2021-10-13 22:49:45 +00:00
if ( ! condCmp ( iactor - > s ( ) . type , arg1 , arg2 , cmpOp ) ) continue ;
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , iactor ) ;
2020-04-01 20:19:50 +00:00
return true ;
}
return false ;
2020-03-13 20:59:13 +00:00
}
2021-10-13 22:03:52 +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-12-06 20:56:09 +00:00
case 51 : return condCmp ( pXSect - > Depth , arg1 , arg2 , cmpOp ) ;
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 ;
2021-10-13 22:03:52 +00:00
switch ( pSect - > type )
{
2020-03-30 19:54:28 +00:00
case kSectorZMotion :
case kSectorRotate :
case kSectorSlide :
2021-10-13 22:03:52 +00:00
if ( cond = = 60 )
{
2020-03-30 19:54:28 +00:00
h = ClipLow ( abs ( pXSect - > onFloorZ - pXSect - > offFloorZ ) , 1 ) ;
curH = abs ( pSect - > floorz - pXSect - > offFloorZ ) ;
2021-10-13 22:03:52 +00:00
}
else
{
2020-03-30 19:54:28 +00:00
h = ClipLow ( abs ( pXSect - > onCeilZ - pXSect - > offCeilZ ) , 1 ) ;
curH = abs ( pSect - > ceilingz - pXSect - > offCeilZ ) ;
}
2020-05-22 16:28:03 +00:00
return condCmp ( ( kPercFull * curH ) / h , arg1 , arg2 , cmpOp ) ;
2020-03-30 19:54:28 +00:00
default :
2021-10-13 21:58:04 +00:00
condError ( aCond , " Usupported sector type %d " , pSect - > type ) ;
2020-03-30 19:54:28 +00:00
return false ;
}
}
2020-12-06 20:56:09 +00:00
case 57 : // this sector in movement?
2021-01-07 12:33:20 +00:00
return ! pXSect - > unused1 ;
2020-03-13 20:59:13 +00:00
}
2021-10-13 22:03:52 +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 :
2021-07-19 21:15:26 +00:00
return condCmp ( 0 , arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
}
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition id (%d)! " , cond ) ;
2020-03-05 20:46:05 +00:00
return false ;
}
2021-10-13 22:03:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 22:49:45 +00:00
bool condCheckWall ( DBloodActor * aCond , int cmpOp , bool PUSH )
{
auto pXCond = & aCond - > x ( ) ;
2020-05-05 18:50:14 +00:00
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
2021-10-13 22:49:45 +00:00
int objType = - 1 , objIndex = - 1 ;
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( aCond , & objType , & objIndex , & objActor ) ;
2020-03-13 20:59:13 +00:00
if ( objType ! = OBJ_WALL | | ! wallRangeIsFine ( objIndex ) )
2021-10-13 21:58:04 +00:00
condError ( aCond , " 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
2021-10-13 22:03:52 +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 ;
2021-10-13 22:49:45 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , var , nullptr ) ;
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 ;
2021-10-13 22:49:45 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , pWall - > nextsector , nullptr ) ;
2020-03-13 20:59:13 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 20 :
if ( ! wallRangeIsFine ( pWall - > nextwall ) ) return false ;
2021-10-13 22:49:45 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_WALL , pWall - > nextwall , nullptr ) ;
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 ;
2021-10-13 22:49:45 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , var , nullptr ) ;
2020-03-19 19:35:31 +00:00
return true ;
2020-03-13 20:59:13 +00:00
}
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition id (%d)! " , cond ) ;
2020-03-13 20:59:13 +00:00
return false ;
}
2021-10-13 22:03:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 23:00:19 +00:00
bool condCheckPlayer ( DBloodActor * aCond , int cmpOp , bool PUSH )
{
auto pXCond = & aCond - > x ( ) ;
2020-05-05 18:50:14 +00:00
2021-10-13 23:00:19 +00:00
int var = - 1 ;
PLAYER * pPlayer = NULL ;
int cond = pXCond - > data1 - kCondPlayerBase ;
int arg1 = pXCond - > data2 ;
int arg2 = pXCond - > data3 ;
int arg3 = pXCond - > data4 ;
2020-05-05 18:50:14 +00:00
2021-10-13 23:00:19 +00:00
int objType = - 1 , objIndex = - 1 ;
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( aCond , & objType , & objIndex , & objActor ) ;
2020-03-30 19:54:28 +00:00
2021-10-13 23:00:19 +00:00
if ( objType ! = OBJ_SPRITE | | ! objActor )
condError ( aCond , " Object #%d (objType: %d) is not a sprite! " , objActor - > GetIndex ( ) , objType ) ;
2021-07-19 21:15:26 +00:00
2021-10-13 23:00:19 +00:00
for ( int i = 0 ; i < kMaxPlayers ; i + + )
{
if ( objActor ! = gPlayer [ i ] . actor ( ) ) continue ;
2021-07-19 21:15:26 +00:00
pPlayer = & gPlayer [ i ] ;
break ;
}
2021-10-13 22:03:52 +00:00
if ( ! pPlayer )
{
2021-10-13 23:00:19 +00:00
condError ( aCond , " Object #%d (objType: %d) is not a player! " , objActor - > GetIndex ( ) , objType ) ;
2021-07-19 21:15:26 +00:00
return false ;
2020-05-05 18:50:14 +00:00
}
2020-04-04 19:48:23 +00:00
2020-05-22 16:28:03 +00:00
switch ( cond ) {
2020-05-05 18:50:14 +00:00
case 0 : // check if this player is connected
2021-08-29 11:44:04 +00:00
if ( ! condCmp ( pPlayer - > nPlayer + 1 , arg1 , arg2 , cmpOp ) | | pPlayer - > actor ( ) = = nullptr ) return false ;
2021-10-13 23:00:19 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , pPlayer - > actor ( ) ) ;
2020-05-05 18:50:14 +00:00
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-05-22 16:28:03 +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 ) ;
2021-07-26 19:52:42 +00:00
case 8 : // check for powerup amount in seconds
if ( arg3 > 0 & & arg3 < = ( kMaxAllowedPowerup - ( kMinAllowedPowerup < < 1 ) + 1 ) ) {
var = ( kMinAllowedPowerup + arg3 ) - 1 ; // allowable powerups
return condCmp ( pPlayer - > pwUpTime [ var ] / 100 , arg1 , arg2 , cmpOp ) ;
2021-07-19 21:15:26 +00:00
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected powerup #%d " , arg3 ) ;
2021-07-19 21:15:26 +00:00
return false ;
2020-05-05 18:50:14 +00:00
case 9 :
2021-10-13 23:00:19 +00:00
if ( ! pPlayer - > fragger ( ) ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , pPlayer - > fragger ( ) ) ;
2020-05-05 18:50:14 +00:00
return true ;
2020-05-22 16:28:03 +00:00
case 10 : // check keys pressed
switch ( arg1 ) {
2020-08-26 14:54:13 +00:00
case 1 : return ( pPlayer - > input . fvel > 0 ) ; // forward
case 2 : return ( pPlayer - > input . fvel < 0 ) ; // backward
case 3 : return ( pPlayer - > input . svel > 0 ) ; // left
case 4 : return ( pPlayer - > input . svel < 0 ) ; // right
2020-08-28 20:51:05 +00:00
case 5 : return ! ! ( pPlayer - > input . actions & SB_JUMP ) ; // jump
case 6 : return ! ! ( pPlayer - > input . actions & SB_CROUCH ) ; // crouch
case 7 : return ! ! ( pPlayer - > input . actions & SB_FIRE ) ; // normal fire weapon
case 8 : return ! ! ( pPlayer - > input . actions & SB_ALTFIRE ) ; // alt fire weapon
2021-07-19 21:15:26 +00:00
case 9 : return ! ! ( pPlayer - > input . actions & SB_OPEN ) ; // use
2020-05-22 16:28:03 +00:00
default :
2021-10-13 21:58:04 +00:00
condError ( aCond , " Specify a correct key! " ) ;
2020-05-22 16:28:03 +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-05-22 16:28:03 +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 ) ) ;
2021-08-29 11:44:04 +00:00
case 48 : return isShrinked ( pPlayer - > actor ( ) ) ;
case 49 : return isGrown ( pPlayer - > actor ( ) ) ;
2020-05-05 18:50:14 +00:00
}
2020-03-19 19:35:31 +00:00
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition #%d! " , cond ) ;
2020-05-05 18:50:14 +00:00
return false ;
}
2021-10-13 22:03:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 23:00:19 +00:00
bool condCheckDude ( DBloodActor * aCond , int cmpOp , bool PUSH )
{
auto pXCond = & aCond - > x ( ) ;
2020-05-05 18:50:14 +00:00
2021-07-19 21:15:26 +00:00
int var = - 1 ;
2020-05-22 16:28:03 +00:00
int cond = pXCond - > data1 - kCondDudeBase ; int arg1 = pXCond - > data2 ;
2021-07-19 21:15:26 +00:00
int arg2 = pXCond - > data3 ; int arg3 = pXCond - > data4 ;
2020-05-05 18:50:14 +00:00
2021-10-13 23:00:19 +00:00
int objType = - 1 , objIndex = - 1 ;
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( aCond , & objType , & objIndex , & objActor ) ;
2021-10-13 23:00:19 +00:00
if ( objType ! = OBJ_SPRITE | | ! objActor )
condError ( aCond , " Object #%d (objType: %d) is not a sprite! " , objActor - > GetIndex ( ) , objType ) ;
2021-07-19 21:15:26 +00:00
2021-10-13 23:00:19 +00:00
spritetype * pSpr = & objActor - > s ( ) ;
if ( ! objActor - > hasX ( ) | | pSpr - > type = = kThingBloodChunks )
condError ( aCond , " Object #%d (objType: %d) is dead! " , objActor - > GetIndex ( ) , objType ) ;
2020-05-05 18:50:14 +00:00
2021-10-13 23:00:19 +00:00
if ( ! objActor - > IsDudeActor ( ) | | objActor - > IsPlayerActor ( ) )
condError ( aCond , " Object #%d (objType: %d) is not an enemy! " , objActor - > GetIndex ( ) , objType ) ;
2020-05-05 18:50:14 +00:00
2021-10-13 23:00:19 +00:00
XSPRITE * pXSpr = & objActor - > x ( ) ;
auto targ = objActor - > GetTarget ( ) ;
switch ( cond )
{
2021-07-19 21:15:26 +00:00
default : break ;
case 0 : // dude have any targets?
2021-10-13 23:00:19 +00:00
if ( ! targ ) return false ;
else if ( ! targ - > IsDudeActor ( ) & & targ - > s ( ) . type ! = kMarkerPath ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , targ ) ;
2021-07-19 21:15:26 +00:00
return true ;
2021-10-13 23:00:19 +00:00
case 1 : return aiFightDudeIsAffected ( objActor ) ; // dude affected by ai fight?
2021-07-19 21:15:26 +00:00
case 2 : // distance to the target in a range?
case 3 : // is the target visible?
case 4 : // is the target visible with periphery?
{
2020-05-22 16:28:03 +00:00
2021-10-13 23:00:19 +00:00
if ( ! targ )
condError ( aCond , " Dude #%d has no target! " , objActor - > GetIndex ( ) ) ;
2020-05-22 16:28:03 +00:00
2021-10-13 23:00:19 +00:00
spritetype * pTrgt = & targ - > s ( ) ;
2021-07-19 21:15:26 +00:00
DUDEINFO * pInfo = getDudeInfo ( pSpr - > type ) ;
int eyeAboveZ = pInfo - > eyeHeight * pSpr - > yrepeat < < 2 ;
2021-10-13 23:00:19 +00:00
int dx = pTrgt - > x - pSpr - > x ;
int dy = pTrgt - > y - pSpr - > y ;
2020-05-22 16:28:03 +00:00
2021-10-13 22:03:52 +00:00
switch ( cond )
{
case 2 :
2021-07-19 21:15:26 +00:00
var = condCmp ( approxDist ( dx , dy ) , arg1 * 512 , arg2 * 512 , cmpOp ) ;
break ;
case 3 :
case 4 :
var = cansee ( pSpr - > x , pSpr - > y , pSpr - > z , pSpr - > sectnum , pTrgt - > x , pTrgt - > y , pTrgt - > z - eyeAboveZ , pTrgt - > sectnum ) ;
2021-10-13 22:03:52 +00:00
if ( cond = = 4 & & var > 0 )
{
2021-07-19 21:15:26 +00:00
var = ( ( 1024 + getangle ( dx , dy ) - pSpr - > ang ) & 2047 ) - 1024 ;
var = ( abs ( var ) < ( ( arg1 < = 0 ) ? pInfo - > periphery : ClipHigh ( arg1 , 2048 ) ) ) ;
}
break ;
}
if ( var < = 0 ) return false ;
2021-10-13 23:00:19 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , targ ) ;
2021-07-19 21:15:26 +00:00
return true ;
2020-05-22 16:28:03 +00:00
2021-07-19 21:15:26 +00:00
}
case 5 : return pXSpr - > dudeFlag4 ;
case 6 : return pXSpr - > dudeDeaf ;
case 7 : return pXSpr - > dudeGuard ;
case 8 : return pXSpr - > dudeAmbush ;
case 9 : return ( pXSpr - > unused1 & kDudeFlagStealth ) ;
case 10 : // check if the marker is busy with another dude
case 11 : // check if the marker is reached
2021-10-13 23:00:19 +00:00
if ( ! pXSpr - > dudeFlag4 | | ! targ | | targ - > s ( ) . type ! = kMarkerPath ) return false ;
2021-07-19 21:15:26 +00:00
switch ( cond ) {
case 10 :
2021-08-29 12:36:40 +00:00
{
2021-10-13 23:00:19 +00:00
auto var = aiPatrolMarkerBusy ( objActor , targ ) ;
2021-08-29 12:36:40 +00:00
if ( ! var ) return false ;
2021-10-13 23:42:08 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , var ) ;
2021-07-19 21:15:26 +00:00
break ;
2021-08-29 12:36:40 +00:00
}
2021-07-19 21:15:26 +00:00
case 11 :
2021-10-13 23:00:19 +00:00
if ( ! aiPatrolMarkerReached ( objActor ) ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , targ ) ;
2021-07-19 21:15:26 +00:00
break ;
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
return true ;
case 12 : // compare spot progress value in %
2021-10-13 23:00:19 +00:00
if ( ! pXSpr - > dudeFlag4 | | ! targ | | targ - > s ( ) . type ! = kMarkerPath ) var = 0 ;
2021-07-19 21:15:26 +00:00
else if ( ! ( pXSpr - > unused1 & kDudeFlagStealth ) | | pXSpr - > data3 < 0 | | pXSpr - > data3 > kMaxPatrolSpotValue ) var = 0 ;
else var = ( kPercFull * pXSpr - > data3 ) / kMaxPatrolSpotValue ;
return condCmp ( var , arg1 , arg2 , cmpOp ) ;
case 15 : return getDudeInfo ( pSpr - > type ) - > lockOut ; // dude allowed to interact with objects?
case 16 : return condCmp ( pXSpr - > aiState - > stateType , arg1 , arg2 , cmpOp ) ;
case 17 : return condCmp ( pXSpr - > stateTimer , arg1 , arg2 , cmpOp ) ;
case 20 : // kDudeModernCustom conditions
case 21 :
case 22 :
case 23 :
case 24 :
2021-10-13 22:03:52 +00:00
switch ( pSpr - > type )
{
2021-07-19 21:15:26 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
2020-12-06 20:56:09 +00:00
switch ( cond ) {
2021-07-19 21:15:26 +00:00
case 20 : // life leech is thrown?
2021-05-05 19:06:38 +00:00
{
2021-10-13 23:00:19 +00:00
auto act = objActor - > genDudeExtra . pLifeLeech ;
2021-05-05 19:06:38 +00:00
if ( ! act ) return false ;
2021-10-13 23:00:19 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , act ) ;
2021-07-19 21:15:26 +00:00
return true ;
2021-05-05 19:06:38 +00:00
}
2021-10-13 23:00:19 +00:00
2021-07-19 21:15:26 +00:00
case 21 : // life leech is destroyed?
2021-05-05 19:06:38 +00:00
{
2021-10-13 23:00:19 +00:00
auto act = objActor - > genDudeExtra . pLifeLeech ;
2021-05-05 19:06:38 +00:00
if ( ! act ) return false ;
if ( pSpr - > owner = = kMaxSprites - 1 ) return true ;
2021-10-13 23:00:19 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , act ) ;
2021-07-19 21:15:26 +00:00
return false ;
2021-05-05 19:06:38 +00:00
}
2021-10-13 22:03:52 +00:00
2021-07-19 21:15:26 +00:00
case 22 : // are required amount of dudes is summoned?
2021-10-13 23:00:19 +00:00
return condCmp ( objActor - > genDudeExtra . slaveCount , arg1 , arg2 , cmpOp ) ;
2021-08-29 07:27:03 +00:00
2021-07-19 21:15:26 +00:00
case 23 : // check if dude can...
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2021-10-13 23:00:19 +00:00
case 1 : return objActor - > genDudeExtra . canAttack ;
case 2 : return objActor - > genDudeExtra . canBurn ;
case 3 : return objActor - > genDudeExtra . canDuck ;
case 4 : return objActor - > genDudeExtra . canElectrocute ;
case 5 : return objActor - > genDudeExtra . canFly ;
case 6 : return objActor - > genDudeExtra . canRecoil ;
case 7 : return objActor - > genDudeExtra . canSwim ;
case 8 : return objActor - > genDudeExtra . canWalk ;
2021-10-13 21:58:04 +00:00
default : condError ( aCond , " Invalid argument %d " , arg3 ) ; break ;
2021-07-19 21:15:26 +00:00
}
2020-12-06 20:56:09 +00:00
break ;
2021-07-19 21:15:26 +00:00
case 24 : // compare weapon dispersion
2021-10-13 23:00:19 +00:00
return condCmp ( objActor - > genDudeExtra . baseDispersion , arg1 , arg2 , cmpOp ) ;
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
break ;
default :
2021-10-13 23:00:19 +00:00
condError ( aCond , " Dude #%d is not a Custom Dude! " , objActor - > GetIndex ( ) ) ;
2021-07-19 21:15:26 +00:00
return false ;
}
2020-03-19 19:35:31 +00:00
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition #%d! " , cond ) ;
2020-03-19 19:35:31 +00:00
return false ;
}
2021-10-13 22:03:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 23:06:50 +00:00
bool condCheckSprite ( DBloodActor * aCond , int cmpOp , bool PUSH )
{
auto pXCond = & aCond - > x ( ) ;
2020-05-05 18:50:14 +00:00
2021-01-07 12:33:20 +00:00
int var = - 1 , var2 = - 1 , var3 = - 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
2021-10-13 23:06:50 +00:00
int objType = - 1 , objIndex = - 1 ;
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( aCond , & objType , & objIndex , & objActor ) ;
2020-03-13 20:59:13 +00:00
2021-10-13 23:06:50 +00:00
if ( objType ! = OBJ_SPRITE | | ! objActor )
condError ( aCond , " Object #%d (objType: %d) is not a sprite! " , cond , objActor - > GetIndex ( ) , objType ) ;
2020-03-13 20:59:13 +00:00
2021-10-13 23:06:50 +00:00
spritetype * pSpr = & objActor - > s ( ) ;
XSPRITE * pXSpr = objActor - > hasX ( ) ? & objActor - > x ( ) : nullptr ;
2020-03-13 20:59:13 +00:00
2021-10-13 22:03:52 +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 ( ( pSpr - > ang & 2047 ) , arg1 , arg2 , cmpOp ) ;
case 5 : return condCmp ( pSpr - > statnum , arg1 , arg2 , cmpOp ) ;
2020-12-06 20:56:09 +00:00
case 6 : return ( ( pSpr - > flags & kHitagRespawn ) | | pSpr - > statnum = = kStatRespawn ) ;
2021-10-13 17:11:34 +00:00
case 7 : return condCmp ( spriteGetSlope ( pSpr ) , arg1 , arg2 , cmpOp ) ;
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 ;
2021-10-13 23:06:50 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , objActor - > GetOwner ( ) ) ;
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 ;
2021-10-13 23:06:50 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , pSpr - > sectnum , nullptr ) ;
2020-03-13 20:59:13 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 25 :
2021-10-13 22:03:52 +00:00
switch ( arg1 )
{
2021-10-13 23:06:50 +00:00
case 0 : return ( objActor - > xvel ( ) | | objActor - > yvel ( ) | | objActor - > zvel ( ) ) ;
case 1 : return ( objActor - > xvel ( ) ) ;
case 2 : return ( objActor - > yvel ( ) ) ;
case 3 : return ( objActor - > zvel ( ) ) ;
2020-03-30 19:54:28 +00:00
}
break ;
2020-04-01 20:19:50 +00:00
case 30 :
2021-10-13 23:06:50 +00:00
if ( ! spriteIsUnderwater ( objActor ) & & ! spriteIsUnderwater ( objActor , true ) ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , pSpr - > sectnum , nullptr ) ;
2020-03-13 20:59:13 +00:00
return true ;
2021-10-13 22:03:52 +00:00
case 31 :
if ( arg1 = = - 1 )
{
for ( var = 0 ; var < kDmgMax ; var + + )
{
2021-10-13 23:06:50 +00:00
if ( ! nnExtIsImmune ( objActor , arg1 , 0 ) )
2021-07-19 21:15:26 +00:00
return false ;
}
return true ;
}
2021-10-13 23:06:50 +00:00
return nnExtIsImmune ( objActor , arg1 , 0 ) ;
2021-10-13 22:03:52 +00:00
2020-04-01 20:19:50 +00:00
case 35 : // hitscan: ceil?
case 36 : // hitscan: floor?
case 37 : // hitscan: wall?
case 38 : // hitscan: sprite?
2021-10-13 22:03:52 +00:00
switch ( arg1 )
{
2020-03-13 20:59:13 +00:00
case 0 : arg1 = CLIPMASK0 | CLIPMASK1 ; break ;
case 1 : arg1 = CLIPMASK0 ; break ;
case 2 : arg1 = CLIPMASK1 ; break ;
}
if ( ( pPlayer = getPlayerById ( pSpr - > type ) ) ! = NULL )
2021-07-19 21:15:26 +00:00
var = HitScan ( pSpr , pPlayer - > zWeapon , pPlayer - > aim . dx , pPlayer - > aim . dy , pPlayer - > aim . dz , arg1 , arg3 < < 1 ) ;
2021-10-13 23:06:50 +00:00
else if ( objActor - > IsDudeActor ( ) )
var = HitScan ( pSpr , pSpr - > z , bcos ( pSpr - > ang ) , bsin ( pSpr - > ang ) , ( ! objActor - > hasX ( ) ) ? 0 : objActor - > dudeSlope , arg1 , arg3 < < 1 ) ;
2021-09-19 14:34:01 +00:00
else if ( ( var2 & CSTAT_SPRITE_ALIGNMENT_MASK ) = = CSTAT_SPRITE_ALIGNMENT_FLOOR ) {
2021-01-07 12:33:20 +00:00
2021-07-19 21:15:26 +00:00
var3 = ( var2 & 0x0008 ) ? 0x10000 < < 1 : - ( 0x10000 < < 1 ) ;
2021-01-07 12:33:20 +00:00
var = HitScan ( pSpr , pSpr - > z , Cos ( pSpr - > ang ) > > 16 , Sin ( pSpr - > ang ) > > 16 , var3 , arg1 , arg3 < < 1 ) ;
2021-10-13 22:03:52 +00:00
}
else
{
2021-10-30 06:22:27 +00:00
var = HitScan ( pSpr , pSpr - > z , bcos ( pSpr - > ang ) , bsin ( pSpr - > ang ) , 0 , arg1 , arg3 < < 1 ) ;
2021-01-07 12:33:20 +00:00
}
2021-10-13 22:03:52 +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
2021-07-19 21:15:26 +00:00
if ( ! PUSH ) return retn ;
2021-10-13 22:03:52 +00:00
switch ( var )
{
2021-10-13 23:06:50 +00:00
case 0 : case 4 : condPush ( aCond , OBJ_WALL , gHitInfo . hitwall , nullptr ) ; break ;
case 1 : case 2 : condPush ( aCond , OBJ_SECTOR , gHitInfo . hitsect , nullptr ) ; break ;
case 3 : condPush ( aCond , OBJ_SPRITE , 0 , gHitInfo . hitactor ) ; break ;
2021-07-19 21:15:26 +00:00
}
}
2020-03-30 19:54:28 +00:00
return retn ;
2021-10-13 23:06:50 +00:00
2020-04-01 20:19:50 +00:00
case 45 : // this sprite is a target of some dude?
2021-10-13 23:06:50 +00:00
BloodStatIterator it ( kStatDude ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-13 23:06:50 +00:00
if ( objActor = = iactor ) continue ;
2020-04-01 20:19:50 +00:00
2021-10-13 23:06:50 +00:00
if ( iactor - > IsDudeActor ( ) & & iactor - > hasX ( ) )
{
XSPRITE * pXDude = & iactor - > x ( ) ;
if ( pXDude - > health < = 0 | | iactor - > GetTarget ( ) ! = objActor ) continue ;
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , iactor ) ;
2020-04-01 20:19:50 +00:00
return true ;
}
}
return false ;
2020-03-13 20:59:13 +00:00
}
2021-10-13 22:03:52 +00:00
}
else if ( pXSpr )
{
switch ( cond )
{
2020-03-30 19:54:28 +00:00
default : break ;
case 50 : // compare hp (in %)
2021-10-13 23:06:50 +00:00
if ( objActor - > IsDudeActor ( ) ) var = ( pXSpr - > sysData2 > 0 ) ? ClipRange ( pXSpr - > sysData2 < < 4 , 1 , 65535 ) : getDudeInfo ( pSpr - > type ) - > startHealth < < 4 ;
2021-07-19 21:15:26 +00:00
else if ( pSpr - > type = = kThingBloodChunks ) return condCmp ( 0 , arg1 , arg2 , cmpOp ) ;
2020-05-05 18:50:14 +00:00
else if ( pSpr - > type > = kThingBase & & pSpr - > type < kThingMax ) var = thingInfo [ pSpr - > type - kThingBase ] . startHealth < < 4 ;
2020-05-22 16:28:03 +00:00
return condCmp ( ( kPercFull * pXSpr - > health ) / ClipLow ( var , 1 ) , arg1 , arg2 , cmpOp ) ;
2020-04-01 20:19:50 +00:00
case 55 : // touching ceil of sector?
2021-10-13 23:06:50 +00:00
if ( objActor - > hit ( ) . ceilhit . type ! = kHitSector ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , objActor - > hit ( ) . ceilhit . index , nullptr ) ;
2020-03-13 20:59:13 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 56 : // touching floor of sector?
2021-10-13 23:06:50 +00:00
if ( objActor - > hit ( ) . florhit . type ! = kHitSector ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_SECTOR , objActor - > hit ( ) . florhit . index , nullptr ) ;
2020-03-30 19:54:28 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 57 : // touching walls of sector?
2021-10-13 23:06:50 +00:00
if ( objActor - > hit ( ) . hit . type ! = kHitWall ) return false ;
else if ( PUSH ) condPush ( aCond , OBJ_WALL , objActor - > hit ( ) . hit . index , nullptr ) ;
2020-03-13 20:59:13 +00:00
return true ;
2020-04-01 20:19:50 +00:00
case 58 : // touching another sprite?
2021-09-02 18:23:51 +00:00
{
DBloodActor * actorvar = nullptr ;
2021-10-13 22:03:52 +00:00
switch ( arg3 )
{
2020-03-13 20:59:13 +00:00
case 0 :
case 1 :
2021-10-13 23:06:50 +00:00
if ( objActor - > hit ( ) . florhit . type = = kHitSprite ) actorvar = objActor - > hit ( ) . florhit . actor ;
2020-03-13 20:59:13 +00:00
if ( arg3 | | var > = 0 ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-03-13 20:59:13 +00:00
case 2 :
2021-10-13 23:06:50 +00:00
if ( objActor - > hit ( ) . hit . type = = kHitSprite ) actorvar = objActor - > hit ( ) . hit . actor ;
2020-03-13 20:59:13 +00:00
if ( arg3 | | var > = 0 ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-03-13 20:59:13 +00:00
case 3 :
2021-10-13 23:06:50 +00:00
if ( objActor - > hit ( ) . ceilhit . type = = kHitSprite ) actorvar = objActor - > hit ( ) . ceilhit . actor ;
2020-03-13 20:59:13 +00:00
break ;
}
2021-09-02 18:23:51 +00:00
if ( actorvar = = nullptr )
{
2021-10-13 23:06:50 +00:00
// check if something is touching this sprite
2021-09-02 18:23:51 +00:00
for ( int i = kMaxXSprites - 1 , idx = i ; i > 0 ; idx = xsprite [ - - i ] . reference )
{
2021-10-13 23:06:50 +00:00
if ( idx < 0 ) continue ;
2021-09-02 18:23:51 +00:00
auto iactor = & bloodActors [ idx ] ;
2021-10-13 23:06:50 +00:00
if ( iactor - > s ( ) . flags & kHitagRespawn ) continue ;
auto & hit = iactor - > hit ( ) ;
switch ( arg3 )
{
2021-01-07 12:33:20 +00:00
case 0 :
case 1 :
2021-10-13 23:06:50 +00:00
if ( hit . ceilhit . type = = kHitSprite & & hit . ceilhit . actor = = objActor ) actorvar = iactor ;
2021-09-02 18:23:51 +00:00
if ( arg3 | | actorvar ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2021-01-07 12:33:20 +00:00
case 2 :
2021-10-13 23:06:50 +00:00
if ( hit . hit . type = = kHitSprite & & hit . hit . actor = = objActor ) actorvar = iactor ;
2021-09-02 18:23:51 +00:00
if ( arg3 | | actorvar ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2021-01-07 12:33:20 +00:00
case 3 :
2021-10-13 23:06:50 +00:00
if ( hit . florhit . type = = kHitSprite & & hit . florhit . actor = = objActor ) actorvar = iactor ;
2021-01-07 12:33:20 +00:00
break ;
}
}
}
2021-09-02 18:23:51 +00:00
if ( actorvar = = nullptr ) return false ;
2021-10-13 23:06:50 +00:00
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , actorvar ) ;
2020-03-30 19:54:28 +00:00
return true ;
2021-09-02 18:23:51 +00:00
}
2021-10-13 23:06:50 +00:00
2020-04-01 20:19:50 +00:00
case 65 : // compare burn time (in %)
2021-10-13 23:06:50 +00:00
var = ( objActor - > IsDudeActor ( ) ) ? 2400 : 1200 ;
2020-05-22 16:28:03 +00:00
if ( ! condCmp ( ( kPercFull * pXSpr - > burnTime ) / var , arg1 , arg2 , cmpOp ) ) return false ;
2021-10-13 23:06:50 +00:00
else if ( PUSH & & spriRangeIsFine ( pXSpr - > burnSource ) ) condPush ( aCond , OBJ_SPRITE , 0 , objActor - > GetBurnSource ( ) ) ;
2020-03-13 20:59:13 +00:00
return true ;
2021-10-13 23:06:50 +00:00
2020-05-22 16:28:03 +00:00
case 66 : // any flares stuck in this sprite?
2020-10-15 15:15:45 +00:00
{
2021-10-13 23:06:50 +00:00
BloodStatIterator it ( kStatFlare ) ;
while ( auto flareactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-13 23:06:50 +00:00
if ( ! flareactor - > hasX ( ) | | ( flareactor - > s ( ) . flags & kHitagFree ) )
2020-05-22 16:28:03 +00:00
continue ;
2020-10-15 15:15:45 +00:00
2021-10-13 23:06:50 +00:00
if ( flareactor - > GetTarget ( ) ! = objActor ) continue ;
else if ( PUSH ) condPush ( aCond , OBJ_SPRITE , 0 , flareactor ) ;
2020-05-22 16:28:03 +00:00
return true ;
}
return false ;
2020-10-15 15:15:45 +00:00
}
2020-04-01 20:19:50 +00:00
case 70 :
2021-10-13 23:06:50 +00:00
return condCmp ( getSpriteMassBySize ( objActor ) , arg1 , arg2 , cmpOp ) ; // mass of the sprite in a range?
2020-03-13 20:59:13 +00:00
}
2021-10-13 22:03:52 +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 :
2021-07-19 21:15:26 +00:00
return condCmp ( 0 , arg1 , arg2 , cmpOp ) ;
2020-03-13 20:59:13 +00:00
}
}
2021-10-13 21:58:04 +00:00
condError ( aCond , " Unexpected condition id (%d)! " , cond ) ;
2020-03-13 20:59:13 +00:00
return false ;
2020-03-05 20:46:05 +00:00
}
2021-10-13 23:18:26 +00:00
//---------------------------------------------------------------------------
//
2020-05-05 18:50:14 +00:00
// this updates index of object in all conditions
2021-10-13 23:18:26 +00:00
// only used when spawning players
//
//---------------------------------------------------------------------------
2020-05-22 16:28:03 +00:00
2021-10-13 23:18:26 +00:00
void condUpdateObjectIndex ( DBloodActor * oldActor , DBloodActor * newActor )
{
2020-05-22 16:28:03 +00:00
// update index in tracking conditions first
2021-08-27 10:55:16 +00:00
for ( int i = 0 ; i < gTrackingCondsCount ; i + + )
{
2020-05-22 16:28:03 +00:00
TRCONDITION * pCond = & gCondition [ i ] ;
2021-08-27 10:55:16 +00:00
for ( unsigned k = 0 ; k < pCond - > length ; k + + )
{
2021-10-13 23:18:26 +00:00
if ( pCond - > obj [ k ] . type ! = OBJ_SPRITE | | pCond - > obj [ k ] . actor ! = oldActor ) continue ;
2021-08-27 10:55:16 +00:00
pCond - > obj [ k ] . actor = newActor ;
2020-04-04 19:48:23 +00:00
break ;
}
2020-05-05 18:50:14 +00:00
}
2020-05-22 16:28:03 +00:00
2021-08-27 10:55:16 +00:00
// puke...
2021-10-13 23:18:26 +00:00
int oldSerial = condSerialize ( oldActor ) ;
int newSerial = condSerialize ( newActor ) ;
2020-05-22 16:28:03 +00:00
// then update serials
2021-10-13 23:18:26 +00:00
BloodStatIterator it ( kStatModernCondition ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-13 23:18:26 +00:00
XSPRITE * pXCond = & iactor - > x ( ) ;
2021-10-13 23:40:46 +00:00
// condition-target
// condition-target
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
}
}
2021-08-28 16:26:24 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool modernTypeSetSpriteState ( DBloodActor * actor , int nState )
{
auto pXSprite = & actor - > x ( ) ;
2020-02-07 19:47:43 +00:00
if ( ( pXSprite - > busy & 0xffff ) = = 0 & & pXSprite - > state = = nState )
2021-08-28 16:26:24 +00:00
return false ;
2020-02-07 19:47:43 +00:00
2020-09-01 13:00:35 +00:00
pXSprite - > busy = IntToFixed ( nState ) ;
pXSprite - > state = nState ;
2020-02-07 19:47:43 +00:00
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ;
2020-02-07 19:47:43 +00:00
if ( pXSprite - > restState ! = nState & & pXSprite - > waitTime > 0 )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , ( pXSprite - > waitTime * 120 ) / 10 , pXSprite - > restState ? kCmdOn : kCmdOff ) ;
2020-02-07 19:47:43 +00:00
if ( pXSprite - > txID ! = 0 & & ( ( pXSprite - > triggerOn & & pXSprite - > state ) | | ( pXSprite - > triggerOff & & ! pXSprite - > state ) ) )
2021-08-28 16:26:24 +00:00
modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2020-02-07 19:47:43 +00:00
2021-08-28 16:26:24 +00:00
return true ;
2020-02-07 19:47:43 +00:00
}
2021-08-28 16:26:24 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void modernTypeSendCommand ( DBloodActor * actor , int destChannel , COMMAND_ID command )
{
switch ( command )
{
2020-02-07 19:47:43 +00:00
case kCmdLink :
2021-08-27 08:46:57 +00:00
evSendActor ( actor , destChannel , kCmdModernUse ) ; // just send command to change properties
2020-02-07 19:47:43 +00:00
return ;
case kCmdUnlock :
2021-08-27 08:46:57 +00:00
evSendActor ( actor , destChannel , command ) ; // send normal command first
evSendActor ( actor , destChannel , kCmdModernUse ) ; // then send command to change properties
2020-02-07 19:47:43 +00:00
return ;
default :
2021-08-27 08:46:57 +00:00
evSendActor ( actor , destChannel , kCmdModernUse ) ; // send first command to change properties
evSendActor ( actor , destChannel , command ) ; // then send normal command
2020-02-07 19:47:43 +00:00
return ;
}
}
2021-10-03 10:29:05 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function used by various new modern types.
2021-10-03 10:29:05 +00:00
//
//---------------------------------------------------------------------------
2020-02-07 19:47:43 +00:00
2021-10-03 10:29:05 +00:00
void modernTypeTrigger ( int destObjType , int destObjIndex , DBloodActor * destactor , const EVENT & event )
{
2021-08-27 10:03:11 +00:00
if ( event . type ! = OBJ_SPRITE | | ! event . actor | | ! event . actor - > hasX ( ) ) return ;
spritetype * pSource = & event . actor - > s ( ) ;
XSPRITE * pXSource = & event . actor - > x ( ) ;
2020-02-07 19:47:43 +00:00
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 :
2021-10-03 10:29:05 +00:00
{
if ( ! destactor ) return ;
auto pSpr = & destactor - > s ( ) ;
if ( pSpr - > flags & kHitagFree ) return ;
2021-01-07 12:33:20 +00:00
2020-03-01 20:36:28 +00:00
// 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.
2021-10-03 10:29:05 +00:00
switch ( pSpr - > type )
{
2020-03-01 20:36:28 +00:00
case kModernRandomTX :
case kModernSequentialTX :
2021-10-03 10:29:05 +00:00
XSPRITE * pXSpr = & destactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXSpr - > command ! = kCmdLink | | pXSpr - > locked ) break ; // no redirect mode detected
2021-10-03 10:26:29 +00:00
switch ( pSpr - > type )
{
2021-07-19 21:15:26 +00:00
case kModernRandomTX :
2021-10-03 12:50:55 +00:00
useRandomTx ( destactor , ( COMMAND_ID ) pXSource - > command , false ) ; // set random TX id
2021-07-19 21:15:26 +00:00
break ;
case kModernSequentialTX :
2021-10-03 10:26:29 +00:00
if ( pSpr - > flags & kModernTypeFlag1 )
{
2021-10-03 12:50:55 +00:00
seqTxSendCmdAll ( destactor , event . actor , ( COMMAND_ID ) pXSource - > command , true ) ;
2021-07-19 21:15:26 +00:00
return ;
}
2021-10-03 12:50:55 +00:00
useSequentialTx ( destactor , ( COMMAND_ID ) pXSource - > command , false ) ; // set next TX id
2021-07-19 21:15:26 +00:00
break ;
2020-03-01 20:36:28 +00:00
}
2021-07-19 21:15:26 +00:00
if ( pXSpr - > txID < = 0 | | pXSpr - > txID > = kChannelUserMax ) return ;
2021-08-28 16:26:24 +00:00
modernTypeSendCommand ( event . actor , pXSpr - > txID , ( COMMAND_ID ) pXSource - > command ) ;
2021-07-19 21:15:26 +00:00
return ;
2020-03-01 20:36:28 +00:00
}
2020-02-07 19:47:43 +00:00
break ;
2021-10-03 10:26:29 +00:00
}
2020-02-07 19:47:43 +00:00
default :
return ;
}
2021-10-03 10:26:29 +00:00
switch ( pSource - > type )
{
2020-02-07 19:47:43 +00:00
// allows teleport any sprite from any location to the source destination
case kMarkerWarpDest :
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-09-02 21:36:57 +00:00
useTeleportTarget ( event . actor , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-12-06 20:56:09 +00:00
// changes slope of sprite or sector
case kModernSlopeChanger :
2021-10-03 10:26:29 +00:00
switch ( destObjType )
{
2020-12-06 20:56:09 +00:00
case OBJ_SPRITE :
case OBJ_SECTOR :
2021-10-13 17:13:15 +00:00
useSlopeChanger ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-12-06 20:56:09 +00:00
break ;
}
break ;
2020-02-07 19:47:43 +00:00
case kModernSpriteDamager :
2020-12-06 20:56:09 +00:00
// damages xsprite via TX ID or xsprites in a sector
2021-10-03 10:26:29 +00:00
switch ( destObjType )
{
2020-12-06 20:56:09 +00:00
case OBJ_SPRITE :
case OBJ_SECTOR :
2021-09-02 21:52:24 +00:00
useSpriteDamager ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-12-06 20:56:09 +00:00
break ;
}
2020-02-07 19:47:43 +00:00
break ;
// can spawn any effect passed in data2 on it's or txID sprite
case kModernEffectSpawner :
2021-07-19 21:15:26 +00:00
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-09-02 21:42:25 +00:00
useEffectGen ( event . actor , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// takes data2 as SEQ ID and spawns it on it's or TX ID object
case kModernSeqSpawner :
2021-09-02 22:04:17 +00:00
useSeqSpawnerGen ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// creates wind on TX ID sector
case kModernWindGenerator :
if ( destObjType ! = OBJ_SECTOR | | pXSource - > data2 < 0 ) break ;
2021-09-02 21:42:25 +00:00
useSectorWindGen ( event . actor , & sector [ destObjIndex ] ) ;
2020-02-07 19:47:43 +00:00
break ;
// size and pan changer of sprite/wall/sector via TX ID
case kModernObjSizeChanger :
2021-08-28 10:11:03 +00:00
useObjResizer ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// iterate data filed value of destination object
case kModernObjDataAccumulator :
2021-10-03 15:47:59 +00:00
useIncDecGen ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// change data field value of destination object
case kModernObjDataChanger :
2021-10-13 17:17:45 +00:00
useDataChanger ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// change sector lighting dynamically
case kModernSectorFXChanger :
if ( destObjType ! = OBJ_SECTOR ) break ;
2021-10-13 17:17:45 +00:00
useSectorLigthChanger ( event . actor , & xsector [ sector [ destObjIndex ] . extra ] ) ;
2020-02-07 19:47:43 +00:00
break ;
// change target of dudes and make it fight
case kModernDudeTargetChanger :
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-10-13 17:17:45 +00:00
useTargetChanger ( event . actor , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// change picture and palette of TX ID object
case kModernObjPicnumChanger :
2021-08-29 11:28:13 +00:00
usePictureChanger ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
// change various properties
case kModernObjPropertiesChanger :
2021-09-02 21:26:18 +00:00
usePropertiesChanger ( event . actor , destObjType , destObjIndex , destactor ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-12-06 20:56:09 +00:00
// updated vanilla sound gen that now allows to play sounds on TX ID sprites
case kGenModernSound :
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-08-29 06:57:23 +00:00
useSoundGen ( event . actor , destactor ) ;
2020-12-06 20:56:09 +00:00
break ;
2021-07-19 21:15:26 +00:00
// updated ecto skull gen that allows to fire missile from TX ID sprites
case kGenModernMissileUniversal :
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-08-29 06:45:14 +00:00
useUniMissileGen ( event . actor , destactor ) ;
2021-07-19 21:15:26 +00:00
break ;
// spawn enemies on TX ID sprites
case kMarkerDudeSpawn :
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-10-03 12:18:47 +00:00
useDudeSpawn ( event . actor , destactor ) ;
2021-07-19 21:15:26 +00:00
break ;
// spawn custom dude on TX ID sprites
case kModernCustomDudeSpawn :
if ( destObjType ! = OBJ_SPRITE ) break ;
2021-10-03 10:29:05 +00:00
useCustomDudeSpawn ( event . actor , destactor ) ;
2021-07-19 21:15:26 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
}
2021-10-03 10:31:19 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// the following functions required for kModernDudeTargetChanger
2021-10-03 10:31:19 +00:00
//
//---------------------------------------------------------------------------
DBloodActor * aiFightGetTargetInRange ( DBloodActor * actor , int minDist , int maxDist , int data , int teamMode )
{
auto pSprite = & actor - > s ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ;
BloodStatIterator it ( kStatDude ) ;
while ( auto targactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-03 10:34:47 +00:00
if ( ! aiFightDudeCanSeeTarget ( actor , pDudeInfo , targactor ) ) continue ;
2021-10-03 10:34:47 +00:00
auto pTarget = & targactor - > s ( ) ;
2021-10-03 10:31:19 +00:00
auto pXTarget = & targactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
2021-10-03 12:11:07 +00:00
int dist = aiFightGetTargetDist ( actor , pDudeInfo , targactor ) ;
2020-02-07 19:47:43 +00:00
if ( dist < minDist | | dist > maxDist ) continue ;
2021-10-03 10:31:19 +00:00
else if ( actor - > GetTarget ( ) = = targactor ) return targactor ;
else if ( ! targactor - > IsDudeActor ( ) | | targactor = = actor | | targactor - > IsPlayerActor ( ) ) continue ;
2021-08-29 11:44:04 +00:00
else if ( IsBurningDude ( targactor ) | | ! IsKillableDude ( targactor ) | | targactor - > GetOwner ( ) = = actor ) continue ;
2021-10-03 12:11:07 +00:00
else if ( ( teamMode = = 1 & & pXSprite - > rxID = = pXTarget - > rxID ) | | aiFightMatesHaveSameTarget ( actor , targactor , 1 ) ) continue ;
2021-10-03 10:31:19 +00:00
else if ( data = = 666 | | pXTarget - > data1 = = data )
{
if ( actor - > GetTarget ( ) )
{
2021-10-03 12:11:07 +00:00
int fineDist1 = aiFightGetFineTargetDist ( actor , actor - > GetTarget ( ) ) ;
int fineDist2 = aiFightGetFineTargetDist ( actor , targactor ) ;
2020-02-07 19:47:43 +00:00
if ( fineDist1 < fineDist2 )
continue ;
}
2021-10-03 10:31:19 +00:00
return targactor ;
2020-02-07 19:47:43 +00:00
}
}
2021-10-03 10:26:29 +00:00
return nullptr ;
2020-02-07 19:47:43 +00:00
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 10:34:47 +00:00
DBloodActor * aiFightTargetIsPlayer ( DBloodActor * actor )
2021-10-03 10:34:47 +00:00
{
2021-10-03 10:34:47 +00:00
auto targ = actor - > GetTarget ( ) ;
if ( targ & & targ - > IsPlayerActor ( ) ) return targ ;
return nullptr ;
2020-02-07 19:47:43 +00:00
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 10:34:47 +00:00
DBloodActor * aiFightGetMateTargets ( DBloodActor * actor )
2021-09-01 17:57:38 +00:00
{
2021-10-03 10:34:47 +00:00
auto pXSprite = & actor - > x ( ) ;
2021-09-01 17:57:38 +00:00
int rx = pXSprite - > rxID ;
2020-02-07 19:47:43 +00:00
2021-09-01 17:57:38 +00:00
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + )
{
if ( rxBucket [ i ] . type = = OBJ_SPRITE )
{
auto mate = rxBucket [ i ] . GetActor ( ) ;
if ( ! mate | | ! mate - > hasX ( ) | | mate = = actor | | ! mate - > IsDudeActor ( ) )
2020-02-07 19:47:43 +00:00
continue ;
2021-09-01 17:57:38 +00:00
if ( mate - > GetTarget ( ) )
{
if ( ! mate - > GetTarget ( ) - > IsPlayerActor ( ) )
2021-10-03 10:34:47 +00:00
return mate - > GetTarget ( ) ;
2020-02-07 19:47:43 +00:00
}
}
}
2021-10-03 10:34:47 +00:00
return nullptr ;
2020-02-07 19:47:43 +00:00
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 10:34:47 +00:00
bool aiFightMatesHaveSameTarget ( DBloodActor * leaderactor , DBloodActor * targetactor , int allow )
{
auto pXLeader = & leaderactor - > x ( ) ;
int rx = pXLeader - > rxID ;
2020-02-07 19:47:43 +00:00
2021-10-03 10:34:47 +00:00
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SPRITE )
continue ;
2021-10-03 10:34:47 +00:00
auto mate = rxBucket [ i ] . actor ;
if ( ! mate | | ! mate - > hasX ( ) | | mate = = leaderactor | | ! mate - > IsDudeActor ( ) )
2021-09-01 17:57:38 +00:00
continue ;
2020-02-07 19:47:43 +00:00
2021-10-03 10:34:47 +00:00
if ( mate - > GetTarget ( ) = = targetactor & & allow - - < = 0 )
2020-02-07 19:47:43 +00:00
return true ;
}
return false ;
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 10:34:47 +00:00
bool aiFightDudeCanSeeTarget ( DBloodActor * dudeactor , DUDEINFO * pDudeInfo , DBloodActor * targetactor )
{
auto pDude = & dudeactor - > s ( ) ;
auto pXDude = & dudeactor - > x ( ) ;
auto pTarget = & targetactor - > s ( ) ;
2020-02-07 19:47:43 +00:00
int dx = pTarget - > x - pDude - > x ; int dy = pTarget - > y - pDude - > y ;
// check target
2021-10-03 10:34:47 +00:00
if ( approxDist ( dx , dy ) < pDudeInfo - > seeDist )
{
2020-02-07 19:47:43 +00:00
int eyeAboveZ = pDudeInfo - > eyeHeight * pDude - > yrepeat < < 2 ;
// is there a line of sight to the target?
2021-10-03 10:34:47 +00:00
if ( cansee ( pDude - > x , pDude - > y , pDude - > z , pDude - > sectnum , pTarget - > x , pTarget - > y , pTarget - > z - eyeAboveZ , pTarget - > sectnum ) )
{
2020-02-07 19:47:43 +00:00
/*int nAngle = getangle(dx, dy);
int losAngle = ( ( 1024 + nAngle - pDude - > ang ) & 2047 ) - 1024 ;
// is the target visible?
2021-01-04 12:02:00 +00:00
if ( abs ( losAngle ) < 2048 ) // 360 deg periphery here*/
2020-02-07 19:47:43 +00:00
return true ;
}
2020-02-08 20:13:29 +00:00
2020-02-07 19:47:43 +00:00
}
return false ;
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// 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.
2021-10-03 10:34:47 +00:00
//
//---------------------------------------------------------------------------
void aiFightActivateDudes ( int rx )
{
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
2021-09-01 18:06:00 +00:00
auto dudeactor = rxBucket [ i ] . GetActor ( ) ;
if ( ! dudeactor | | ! dudeactor - > hasX ( ) | | ! dudeactor - > IsDudeActor ( ) | | dudeactor - > x ( ) . aiState - > stateType ! = kAiStateGenIdle ) continue ;
aiInitSprite ( dudeactor ) ;
2020-02-07 19:47:43 +00:00
}
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function sets target to -1 for all dudes that hunting for nSprite
2021-10-03 10:34:47 +00:00
//
//---------------------------------------------------------------------------
2021-10-03 10:34:47 +00:00
void aiFightFreeTargets ( DBloodActor * actor )
{
BloodStatIterator it ( kStatDude ) ;
while ( auto targetactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-03 10:34:47 +00:00
if ( ! targetactor - > IsDudeActor ( ) | | ! targetactor - > hasX ( ) ) continue ;
else if ( targetactor - > GetTarget ( ) = = actor )
aiSetTarget ( targetactor , targetactor - > s ( ) . x , targetactor - > s ( ) . y , targetactor - > s ( ) . z ) ;
2020-02-07 19:47:43 +00:00
}
}
2021-10-03 10:34:47 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function sets target to -1 for all targets that hunting for dudes affected by selected kModernDudeTargetChanger
2021-10-03 10:34:47 +00:00
//
//---------------------------------------------------------------------------
2021-10-03 10:34:47 +00:00
void aiFightFreeAllTargets ( DBloodActor * sourceactor )
{
auto txID = sourceactor - > x ( ) . txID ;
if ( txID < = 0 ) return ;
for ( int i = bucketHead [ txID ] ; i < bucketHead [ txID + 1 ] ; i + + )
{
if ( rxBucket [ i ] . type = = OBJ_SPRITE & & rxBucket [ i ] . actor & & rxBucket [ i ] . actor - > hasX ( ) )
aiFightFreeTargets ( rxBucket [ i ] . actor ) ;
2020-02-07 19:47:43 +00:00
}
}
2021-10-03 12:16:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-03-30 19:54:28 +00:00
2021-10-03 12:16:22 +00:00
bool aiFightDudeIsAffected ( DBloodActor * dudeactor )
{
auto pXDude = & dudeactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
if ( pXDude - > rxID < = 0 | | pXDude - > locked = = 1 ) return false ;
2021-10-03 12:16:22 +00:00
BloodStatIterator it ( kStatModernDudeTargetChanger ) ;
while ( auto actor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-03 12:16:22 +00:00
if ( ! actor - > hasX ( ) ) continue ;
XSPRITE * pXSprite = & actor - > x ( ) ;
if ( pXSprite - > txID < = 0 | | pXSprite - > state ! = 1 ) continue ;
for ( int i = bucketHead [ pXSprite - > txID ] ; i < bucketHead [ pXSprite - > txID + 1 ] ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
2021-10-03 12:16:22 +00:00
auto rxactor = rxBucket [ i ] . actor ;
if ( ! rxactor | | ! rxactor - > hasX ( ) | | ! rxactor - > IsDudeActor ( ) ) continue ;
else if ( rxactor = = dudeactor ) return true ;
2020-02-07 19:47:43 +00:00
}
}
return false ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function tells if there any dude found for kModernDudeTargetChanger
2021-09-03 19:23:22 +00:00
//
//---------------------------------------------------------------------------
2021-10-03 12:16:22 +00:00
bool aiFightGetDudesForBattle ( DBloodActor * actor )
{
auto txID = actor - > x ( ) . txID ;
for ( int i = bucketHead [ txID ] ; i < bucketHead [ txID + 1 ] ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
2021-09-01 18:06:00 +00:00
auto actor = rxBucket [ i ] . GetActor ( ) ;
if ( ! actor | | ! actor - > hasX ( ) | | ! actor - > IsDudeActor ( ) ) continue ;
if ( actor - > x ( ) . health > 0 ) return true ;
2020-02-07 19:47:43 +00:00
}
2020-04-07 20:30:00 +00:00
// check redirected TX buckets
2021-10-03 12:16:22 +00:00
int rx = - 1 ;
2021-09-05 18:10:40 +00:00
DBloodActor * pXRedir = nullptr ;
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , 0 , actor , pXRedir , & rx ) ) ! = nullptr )
2021-10-03 12:16:22 +00:00
{
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + )
{
2021-09-01 18:06:00 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
auto actor = rxBucket [ i ] . GetActor ( ) ;
if ( ! actor | | ! actor - > hasX ( ) | | ! actor - > IsDudeActor ( ) ) continue ;
if ( actor - > x ( ) . health > 0 ) return true ;
2020-03-01 20:36:28 +00:00
}
}
2020-02-07 19:47:43 +00:00
return false ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:17:24 +00:00
void aiFightAlarmDudesInSight ( DBloodActor * actor , int max )
{
auto pSprite = & actor - > s ( ) ;
auto pSXprite = & actor - > x ( ) ;
2020-02-08 20:13:29 +00:00
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ;
2021-10-03 12:17:24 +00:00
BloodStatIterator it ( kStatDude ) ;
while ( auto dudeactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-03 12:17:24 +00:00
if ( dudeactor = = actor | | ! dudeactor - > IsDudeActor ( ) | | ! dudeactor - > hasX ( ) )
2020-02-07 19:47:43 +00:00
continue ;
2021-10-03 12:17:24 +00:00
if ( aiFightDudeCanSeeTarget ( actor , pDudeInfo , dudeactor ) )
{
if ( dudeactor - > GetTarget ( ) ! = nullptr | | dudeactor - > x ( ) . rxID > 0 )
2020-02-07 19:47:43 +00:00
continue ;
2021-10-03 12:17:24 +00:00
auto pDude = & dudeactor - > s ( ) ;
aiSetTarget ( dudeactor , pDude - > x , pDude - > y , pDude - > z ) ;
aiActivateDude ( dudeactor ) ;
2020-02-07 19:47:43 +00:00
if ( max - - < 1 )
break ;
}
}
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:17:24 +00:00
bool aiFightUnitCanFly ( DBloodActor * dude )
{
return ( dude - > IsDudeActor ( ) & & gDudeInfoExtra [ dude - > s ( ) . type - kDudeBase ] . flying ) ;
2020-02-07 19:47:43 +00:00
}
2021-10-03 12:17:24 +00:00
bool aiFightIsMeleeUnit ( DBloodActor * dude )
{
if ( dude - > s ( ) . type = = kDudeModernCustom ) return ( dude - > hasX ( ) & & dudeIsMelee ( dude ) ) ;
else return ( dude - > IsDudeActor ( ) & & gDudeInfoExtra [ dude - > s ( ) . type - kDudeBase ] . melee ) ;
2020-02-07 19:47:43 +00:00
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:11:07 +00:00
int aiFightGetTargetDist ( DBloodActor * actor , DUDEINFO * pDudeInfo , DBloodActor * target )
{
int dx = target - > s ( ) . x - actor - > s ( ) . x ;
int dy = target - > s ( ) . y - actor - > s ( ) . y ;
2020-02-07 19:47:43 +00:00
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 ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:11:07 +00:00
int aiFightGetFineTargetDist ( DBloodActor * actor , DBloodActor * target )
{
int dx = target - > s ( ) . x - actor - > s ( ) . x ;
int dy = target - > s ( ) . y - actor - > s ( ) . y ;
2020-02-07 19:47:43 +00:00
int dist = approxDist ( dx , dy ) ;
return dist ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int sectorInMotion ( int nSector )
{
2020-12-06 20:56:09 +00:00
2021-09-03 19:23:22 +00:00
for ( int i = 0 ; i < kMaxBusyCount ; i + + )
{
2021-05-03 22:03:01 +00:00
if ( gBusy - > index = = nSector ) return i ;
2020-12-06 20:56:09 +00:00
}
return - 1 ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:18:47 +00:00
void sectorKillSounds ( int nSector )
{
BloodSectIterator it ( nSector ) ;
while ( auto actor = it . Next ( ) )
{
if ( actor - > s ( ) . type ! = kSoundSector ) continue ;
sfxKill3DSound ( actor ) ;
2021-01-07 12:33:20 +00:00
}
}
2020-12-06 20:56:09 +00:00
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-09-03 19:23:22 +00:00
void sectorPauseMotion ( int nSector )
{
2021-01-07 12:33:20 +00:00
if ( ! xsectRangeIsFine ( sector [ nSector ] . extra ) ) return ;
XSECTOR * pXSector = & xsector [ sector [ nSector ] . extra ] ;
pXSector - > unused1 = 1 ;
2021-08-27 08:18:33 +00:00
evKillSector ( nSector ) ;
2020-12-06 20:56:09 +00:00
2021-01-07 12:33:20 +00:00
sectorKillSounds ( nSector ) ;
if ( ( pXSector - > busy = = 0 & & ! pXSector - > state ) | | ( pXSector - > busy = = 65536 & & pXSector - > state ) )
SectorEndSound ( nSector , xsector [ sector [ nSector ] . extra ] . state ) ;
2020-12-06 20:56:09 +00:00
}
2021-10-03 12:11:07 +00:00
//---------------------------------------------------------------------------
//
//
//
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
void sectorContinueMotion ( int nSector , EVENT event )
{
2021-01-07 12:33:20 +00:00
if ( ! xsectRangeIsFine ( sector [ nSector ] . extra ) ) return ;
2021-09-03 19:23:22 +00:00
else if ( gBusyCount > = kMaxBusyCount )
{
2021-05-03 22:22:35 +00:00
Printf ( PRINT_HIGH , " Failed to continue motion for sector #%d. Max (%d) busy objects count reached! " , nSector , kMaxBusyCount ) ;
2020-12-06 20:56:09 +00:00
return ;
2021-01-07 12:33:20 +00:00
}
2020-12-06 20:56:09 +00:00
XSECTOR * pXSector = & xsector [ sector [ nSector ] . extra ] ;
pXSector - > unused1 = 0 ;
2021-09-03 19:23:22 +00:00
int busyTimeA = pXSector - > busyTimeA ;
int waitTimeA = pXSector - > waitTimeA ;
int busyTimeB = pXSector - > busyTimeB ;
int waitTimeB = pXSector - > waitTimeB ;
if ( sector [ nSector ] . type = = kSectorPath )
{
2021-01-07 12:33:20 +00:00
if ( ! spriRangeIsFine ( pXSector - > marker0 ) ) return ;
busyTimeA = busyTimeB = xsprite [ sprite [ pXSector - > marker0 ] . extra ] . busyTime ;
waitTimeA = waitTimeB = xsprite [ sprite [ pXSector - > marker0 ] . extra ] . waitTime ;
}
if ( ! pXSector - > interruptable & & event . cmd ! = kCmdSectorMotionContinue
2021-09-03 19:23:22 +00:00
& & ( ( ! pXSector - > state & & pXSector - > busy ) | | ( pXSector - > state & & pXSector - > busy ! = 65536 ) ) )
{
2021-01-07 12:33:20 +00:00
event . cmd = kCmdSectorMotionContinue ;
2021-09-03 19:23:22 +00:00
}
else if ( event . cmd = = kCmdToggle )
{
2021-01-07 12:33:20 +00:00
event . cmd = ( pXSector - > state ) ? kCmdOn : kCmdOff ;
}
//viewSetSystemMessage("%d / %d", pXSector->busy, pXSector->state);
2020-12-06 20:56:09 +00:00
int nDelta = 1 ;
2021-09-03 19:23:22 +00:00
switch ( event . cmd )
{
2020-12-06 20:56:09 +00:00
case kCmdOff :
2021-09-03 19:23:22 +00:00
if ( pXSector - > busy = = 0 )
{
2021-08-27 08:18:33 +00:00
if ( pXSector - > reTriggerB & & waitTimeB ) evPostSector ( nSector , ( waitTimeB * 120 ) / 10 , kCmdOff ) ;
2021-01-07 12:33:20 +00:00
return ;
}
2020-12-06 20:56:09 +00:00
pXSector - > state = 1 ;
2021-01-07 12:33:20 +00:00
nDelta = 65536 / ClipLow ( ( busyTimeB * 120 ) / 10 , 1 ) ;
2020-12-06 20:56:09 +00:00
break ;
2021-09-03 19:23:22 +00:00
2020-12-06 20:56:09 +00:00
case kCmdOn :
2021-09-03 19:23:22 +00:00
if ( pXSector - > busy = = 65536 )
{
2021-08-27 08:18:33 +00:00
if ( pXSector - > reTriggerA & & waitTimeA ) evPostSector ( nSector , ( waitTimeA * 120 ) / 10 , kCmdOn ) ;
2021-01-07 12:33:20 +00:00
return ;
}
2020-12-06 20:56:09 +00:00
pXSector - > state = 0 ;
2021-01-07 12:33:20 +00:00
nDelta = 65536 / ClipLow ( ( busyTimeA * 120 ) / 10 , 1 ) ;
2020-12-06 20:56:09 +00:00
break ;
2021-09-03 19:23:22 +00:00
2020-12-06 20:56:09 +00:00
case kCmdSectorMotionContinue :
2021-01-07 12:33:20 +00:00
nDelta = 65536 / ClipLow ( ( ( ( pXSector - > state ) ? busyTimeB : busyTimeA ) * 120 ) / 10 , 1 ) ;
2020-12-06 20:56:09 +00:00
break ;
}
2021-01-07 12:33:20 +00:00
//bool crush = pXSector->Crush;
2020-12-06 20:56:09 +00:00
int busyFunc = BUSYID_0 ;
2021-09-03 19:23:22 +00:00
switch ( sector [ nSector ] . type )
{
2020-12-06 20:56:09 +00:00
case kSectorZMotion :
busyFunc = BUSYID_2 ;
break ;
case kSectorZMotionSprite :
busyFunc = BUSYID_1 ;
break ;
case kSectorSlideMarked :
case kSectorSlide :
busyFunc = BUSYID_3 ;
break ;
case kSectorRotateMarked :
case kSectorRotate :
busyFunc = BUSYID_4 ;
break ;
case kSectorRotateStep :
busyFunc = BUSYID_5 ;
break ;
case kSectorPath :
busyFunc = BUSYID_7 ;
break ;
default :
2021-05-03 22:03:01 +00:00
I_Error ( " Unsupported sector type %d " , sector [ nSector ] . type ) ;
2020-12-06 20:56:09 +00:00
break ;
}
2021-01-07 12:33:20 +00:00
SectorStartSound ( nSector , pXSector - > state ) ;
2020-12-06 20:56:09 +00:00
nDelta = ( pXSector - > state ) ? - nDelta : nDelta ;
2021-05-03 22:03:01 +00:00
gBusy [ gBusyCount ] . index = nSector ;
gBusy [ gBusyCount ] . delta = nDelta ;
gBusy [ gBusyCount ] . busy = pXSector - > busy ;
gBusy [ gBusyCount ] . type = ( BUSYID ) busyFunc ;
2020-12-06 20:56:09 +00:00
gBusyCount + + ;
}
2021-10-03 12:11:07 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-03 19:23:22 +00:00
bool modernTypeOperateSector ( int nSector , sectortype * pSector , XSECTOR * pXSector , const EVENT & event )
{
if ( event . cmd > = kCmdLock & & event . cmd < = kCmdToggleLock )
{
switch ( event . cmd )
{
2020-12-06 20:56:09 +00:00
case kCmdLock :
pXSector - > locked = 1 ;
break ;
case kCmdUnlock :
pXSector - > locked = 0 ;
break ;
case kCmdToggleLock :
pXSector - > locked = pXSector - > locked ^ 1 ;
break ;
}
2021-09-03 19:23:22 +00:00
switch ( pSector - > type )
{
2020-12-06 20:56:09 +00:00
case kSectorCounter :
if ( pXSector - > locked ! = 1 ) break ;
SetSectorState ( nSector , pXSector , 0 ) ;
2021-08-27 08:18:33 +00:00
evPostSector ( nSector , 0 , kCallbackCounterCheck ) ;
2020-12-06 20:56:09 +00:00
break ;
}
return true ;
// continue motion of the paused sector
2021-09-03 19:23:22 +00:00
}
else if ( pXSector - > unused1 )
{
switch ( event . cmd )
{
2020-12-06 20:56:09 +00:00
case kCmdOff :
case kCmdOn :
case kCmdToggle :
case kCmdSectorMotionContinue :
sectorContinueMotion ( nSector , event ) ;
return true ;
}
// pause motion of the sector
2021-09-03 19:23:22 +00:00
}
else if ( event . cmd = = kCmdSectorMotionPause )
{
2020-12-06 20:56:09 +00:00
sectorPauseMotion ( nSector ) ;
return true ;
}
return false ;
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
2021-10-03 12:11:07 +00:00
//
//
//
//---------------------------------------------------------------------------
void useCustomDudeSpawn ( DBloodActor * pSource , DBloodActor * pSprite )
2021-05-06 07:55:56 +00:00
{
genDudeSpawn ( pSource , pSprite , pSprite - > s ( ) . clipdist < < 1 ) ;
2021-07-19 21:15:26 +00:00
}
2021-10-03 12:18:47 +00:00
void useDudeSpawn ( DBloodActor * pSource , DBloodActor * pSprite )
{
if ( randomSpawnDude ( pSource , pSprite , pSprite - > s ( ) . clipdist < < 1 , 0 ) = = nullptr )
nnExtSpawnDude ( pSource , pSprite , pSource - > x ( ) . data1 , pSprite - > s ( ) . clipdist < < 1 , 0 ) ;
2021-07-19 21:15:26 +00:00
}
2021-09-03 19:23:22 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-02 23:33:57 +00:00
bool modernTypeOperateSprite ( DBloodActor * actor , EVENT event )
{
auto pSprite = & actor - > s ( ) ;
auto pXSprite = & actor - > x ( ) ;
2021-01-07 12:33:20 +00:00
2021-09-02 23:31:47 +00:00
if ( event . cmd > = kCmdLock & & event . cmd < = kCmdToggleLock )
{
switch ( event . cmd )
{
2020-05-22 16:28:03 +00:00
case kCmdLock :
pXSprite - > locked = 1 ;
break ;
case kCmdUnlock :
pXSprite - > locked = 0 ;
break ;
case kCmdToggleLock :
pXSprite - > locked = pXSprite - > locked ^ 1 ;
break ;
2020-03-30 19:54:28 +00:00
}
2020-04-01 20:19:50 +00:00
2021-09-02 23:31:47 +00:00
switch ( pSprite - > type )
{
2020-05-22 16:28:03 +00:00
case kModernCondition :
case kModernConditionFalse :
pXSprite - > restState = 0 ;
if ( pXSprite - > busyTime < = 0 ) break ;
else if ( ! pXSprite - > locked ) pXSprite - > busy = 0 ;
2020-03-30 19:54:28 +00:00
break ;
}
return true ;
2021-09-02 23:31:47 +00:00
}
else if ( event . cmd = = kCmdDudeFlagsSet )
{
if ( event . type ! = OBJ_SPRITE )
{
2021-08-27 10:03:11 +00:00
viewSetSystemMessage ( " Only sprites can use command #%d " , event . cmd ) ;
2021-01-07 12:33:20 +00:00
return true ;
2021-09-02 23:31:47 +00:00
}
else if ( event . actor & & event . actor - > hasX ( ) )
{
2021-01-07 12:33:20 +00:00
2021-07-19 21:15:26 +00:00
// copy dude flags from the source to destination sprite
2021-09-04 08:53:56 +00:00
aiPatrolFlagsMgr ( event . actor , actor , true , false ) ;
2021-01-07 12:33:20 +00:00
}
2020-03-30 19:54:28 +00:00
}
2021-08-29 07:44:08 +00:00
if ( pSprite - > statnum = = kStatDude & & actor - > IsDudeActor ( ) )
2021-09-02 23:31:47 +00:00
{
switch ( event . cmd )
{
2020-03-01 20:36:28 +00:00
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state ) SetSpriteState ( actor , 0 ) ;
2020-03-01 20:36:28 +00:00
break ;
case kCmdOn :
2021-09-02 23:33:57 +00:00
if ( ! pXSprite - > state ) SetSpriteState ( actor , 1 ) ;
if ( ! actor - > IsDudeActor ( ) | | actor - > IsPlayerActor ( ) | | pXSprite - > health < = 0 ) break ;
2020-12-06 20:56:09 +00:00
else if ( pXSprite - > aiState - > stateType > = kAiStatePatrolBase & & pXSprite - > aiState - > stateType < kAiStatePatrolMax )
break ;
2021-09-02 23:31:47 +00:00
switch ( pXSprite - > aiState - > stateType )
{
2020-03-01 20:36:28 +00:00
case kAiStateIdle :
case kAiStateGenIdle :
2021-09-02 20:13:31 +00:00
aiActivateDude ( actor ) ;
2020-03-01 20:36:28 +00:00
break ;
}
break ;
2021-09-02 23:31:47 +00:00
2021-01-07 12:33:20 +00:00
case kCmdDudeFlagsSet :
2021-08-27 10:03:11 +00:00
if ( ! event . actor | | ! event . actor - > hasX ( ) ) break ;
2021-09-04 08:53:56 +00:00
else aiPatrolFlagsMgr ( event . actor , actor , false , true ) ; // initialize patrol dude with possible new flags
2020-12-06 20:56:09 +00:00
break ;
2021-09-02 23:31:47 +00:00
2020-03-01 20:36:28 +00:00
default :
2021-08-27 08:18:33 +00:00
if ( ! pXSprite - > state ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-03-01 20:36:28 +00:00
break ;
}
return true ;
}
2021-07-19 21:15:26 +00:00
2021-09-02 23:31:47 +00:00
switch ( pSprite - > type )
{
2020-02-07 19:47:43 +00:00
default :
return false ; // no modern type found to work with, go normal OperateSprite();
2021-07-19 21:15:26 +00:00
case kThingBloodBits :
case kThingBloodChunks :
// dude to thing morphing causing a lot of problems since it continues receiving commands after dude is dead.
// this leads to weird stuff like exploding with gargoyle gib or corpse disappearing immediately.
// let's allow only specific commands here to avoid this.
if ( pSprite - > inittype < kDudeBase | | pSprite - > inittype > = kDudeMax ) return false ;
else if ( event . cmd ! = kCmdToggle & & event . cmd ! = kCmdOff & & event . cmd ! = kCmdSpriteImpact ) return true ;
2021-09-02 23:33:57 +00:00
DudeToGibCallback1 ( 0 , actor ) ; // set proper gib type just in case DATAs was changed from the outside.
2021-07-19 21:15:26 +00:00
return false ;
2021-09-02 23:31:47 +00:00
2020-05-22 16:28:03 +00:00
case kModernCondition :
case kModernConditionFalse :
2021-08-28 22:56:14 +00:00
if ( ! pXSprite - > isTriggered ) useCondition ( actor , event ) ;
2020-02-07 19:47:43 +00:00
return true ;
2021-09-02 23:31:47 +00:00
2020-02-07 19:47:43 +00:00
// add spawn random dude feature - works only if at least 2 data fields are not empty.
case kMarkerDudeSpawn :
2021-07-19 21:15:26 +00:00
if ( ! gGameOptions . nMonsterSettings ) return true ;
2021-10-03 12:18:47 +00:00
else if ( ! ( pSprite - > flags & kModernTypeFlag4 ) ) useDudeSpawn ( actor , actor ) ;
2021-08-27 08:46:57 +00:00
else if ( pXSprite - > txID ) evSendActor ( actor , pXSprite - > txID , kCmdModernUse ) ;
2021-07-19 21:15:26 +00:00
return true ;
2021-09-02 23:31:47 +00:00
2021-07-19 21:15:26 +00:00
case kModernCustomDudeSpawn :
if ( ! gGameOptions . nMonsterSettings ) return true ;
2021-05-06 07:55:56 +00:00
else if ( ! ( pSprite - > flags & kModernTypeFlag4 ) ) useCustomDudeSpawn ( actor , actor ) ;
2021-08-27 08:46:57 +00:00
else if ( pXSprite - > txID ) evSendActor ( actor , pXSprite - > txID , kCmdModernUse ) ;
2020-02-07 19:47:43 +00:00
return true ;
2021-09-02 23:31:47 +00:00
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
2021-09-02 23:31:47 +00:00
switch ( pSprite - > type )
{
2020-03-01 20:36:28 +00:00
case kModernRandomTX :
2021-10-03 12:50:55 +00:00
useRandomTx ( actor , ( COMMAND_ID ) pXSprite - > command , true ) ;
2020-03-01 20:36:28 +00:00
break ;
2021-09-02 23:31:47 +00:00
2020-03-01 20:36:28 +00:00
case kModernSequentialTX :
2021-10-03 12:50:55 +00:00
if ( ! ( pSprite - > flags & kModernTypeFlag1 ) ) useSequentialTx ( actor , ( COMMAND_ID ) pXSprite - > command , true ) ;
else seqTxSendCmdAll ( actor , actor , ( COMMAND_ID ) pXSprite - > command , 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 ;
2021-09-02 23:31:47 +00:00
2020-02-07 19:47:43 +00:00
case kModernSpriteDamager :
2021-09-02 23:31:47 +00:00
switch ( event . cmd )
{
2020-12-06 20:56:09 +00:00
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-12-06 20:56:09 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-12-06 20:56:09 +00:00
case kCmdRepeat :
2021-08-28 16:26:24 +00:00
if ( pXSprite - > txID > 0 ) modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2021-09-02 21:52:24 +00:00
else if ( pXSprite - > data1 = = 0 & & sectRangeIsFine ( pSprite - > sectnum ) ) useSpriteDamager ( actor , OBJ_SECTOR , pSprite - > sectnum , nullptr ) ;
else if ( pXSprite - > data1 > = 666 & & pXSprite - > data1 < 669 ) useSpriteDamager ( actor , - 1 , - 1 , nullptr ) ;
2021-09-02 23:31:47 +00:00
else
{
2020-02-07 19:47:43 +00:00
PLAYER * pPlayer = getPlayerById ( pXSprite - > data1 ) ;
2020-12-06 20:56:09 +00:00
if ( pPlayer ! = NULL )
2021-09-02 21:52:24 +00:00
useSpriteDamager ( actor , OBJ_SPRITE , 0 , pPlayer - > actor ( ) ) ;
2020-12-06 20:56:09 +00:00
}
if ( pXSprite - > busyTime > 0 )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , pXSprite - > busyTime , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
2021-09-02 23:33:57 +00:00
2020-12-06 20:56:09 +00:00
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
2020-12-06 20:56:09 +00:00
return true ;
2021-09-02 23:33:57 +00:00
2020-12-06 20:56:09 +00:00
case kMarkerWarpDest :
if ( pXSprite - > txID < = 0 ) {
PLAYER * pPlayer = getPlayerById ( pXSprite - > data1 ) ;
2021-09-02 23:33:57 +00:00
if ( pPlayer ! = NULL & & SetSpriteState ( actor , pXSprite - > state ^ 1 ) = = 1 )
useTeleportTarget ( actor , pPlayer - > actor ( ) ) ;
2020-02-07 19:47:43 +00:00
return true ;
}
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kModernObjPropertiesChanger :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > txID < = 0 )
{
if ( SetSpriteState ( actor , pXSprite - > state ^ 1 ) = = 1 )
usePropertiesChanger ( actor , - 1 , - 1 , nullptr ) ;
2020-02-07 19:47:43 +00:00
return true ;
}
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-12-06 20:56:09 +00:00
case kModernSlopeChanger :
2020-02-07 19:47:43 +00:00
case kModernObjSizeChanger :
case kModernObjPicnumChanger :
case kModernSectorFXChanger :
case kModernObjDataChanger :
2021-08-28 16:26:24 +00:00
modernTypeSetSpriteState ( actor , pXSprite - > state ^ 1 ) ;
2020-02-07 19:47:43 +00:00
return true ;
2021-09-02 23:31:47 +00:00
2020-02-07 19:47:43 +00:00
case kModernSeqSpawner :
case kModernEffectSpawner :
2021-09-02 23:31:47 +00:00
switch ( event . cmd )
{
2020-02-07 19:47:43 +00:00
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-02-07 19:47:43 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-05 18:10:40 +00:00
if ( pSprite - > type = = kModernSeqSpawner ) seqSpawnerOffSameTx ( actor ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdRepeat :
2021-08-28 16:26:24 +00:00
if ( pXSprite - > txID > 0 ) modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2021-09-02 22:04:17 +00:00
else if ( pSprite - > type = = kModernSeqSpawner ) useSeqSpawnerGen ( actor , 3 , 0 , actor ) ;
2021-09-02 21:42:25 +00:00
else useEffectGen ( actor , nullptr ) ;
2020-02-07 19:47:43 +00:00
if ( pXSprite - > busyTime > 0 )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , ClipLow ( ( int ( pXSprite - > busyTime ) + Random2 ( pXSprite - > data1 ) ) * 120 / 10 , 0 ) , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
return true ;
2021-09-02 23:31:47 +00:00
2020-02-07 19:47:43 +00:00
case kModernWindGenerator :
2021-09-02 23:31:47 +00:00
switch ( event . cmd )
{
2020-02-07 19:47:43 +00:00
case kCmdOff :
2021-08-28 07:50:01 +00:00
windGenStopWindOnSectors ( actor ) ;
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-02-07 19:47:43 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdRepeat :
2021-08-28 16:26:24 +00:00
if ( pXSprite - > txID > 0 ) modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2021-09-02 21:42:25 +00:00
else useSectorWindGen ( actor , nullptr ) ;
2020-02-07 19:47:43 +00:00
2021-08-27 08:18:33 +00:00
if ( pXSprite - > busyTime > 0 ) evPostActor ( actor , pXSprite - > busyTime , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
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 ) ;
2021-09-02 23:31:47 +00:00
switch ( event . cmd )
{
2020-02-07 19:47:43 +00:00
case kCmdOff :
if ( pXSprite - > data4 = = 3 ) aiFightActivateDudes ( pXSprite - > txID ) ;
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-02-07 19:47:43 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdRepeat :
2021-09-02 23:31:47 +00:00
if ( pXSprite - > txID < = 0 | | ! aiFightGetDudesForBattle ( actor ) )
{
2021-10-03 10:34:47 +00:00
aiFightFreeAllTargets ( actor ) ;
2021-08-27 08:18:33 +00:00
evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
2021-09-02 23:31:47 +00:00
}
else
{
2021-08-28 16:26:24 +00:00
modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 08:18:33 +00:00
if ( pXSprite - > busyTime > 0 ) evPostActor ( actor , pXSprite - > busyTime , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
2021-05-12 00:00:06 +00:00
pXSprite - > dropMsg = uint8_t ( pXSprite - > data4 ) ;
2020-02-07 19:47:43 +00:00
return true ;
2021-09-02 23:33:57 +00:00
2020-02-07 19:47:43 +00:00
case kModernObjDataAccumulator :
switch ( event . cmd ) {
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-02-07 19:47:43 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdRepeat :
// force OFF after *all* TX objects reach the goal value
2021-09-05 18:10:40 +00:00
if ( pSprite - > flags = = kModernTypeFlag0 & & incDecGoalValueIsReached ( actor ) )
2021-09-02 23:33:57 +00:00
{
2021-08-27 08:18:33 +00:00
evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
2020-04-07 20:30:00 +00:00
2021-08-28 16:26:24 +00:00
modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2021-08-27 08:18:33 +00:00
if ( pXSprite - > busyTime > 0 ) evPostActor ( actor , pXSprite - > busyTime , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
return true ;
case kModernRandom :
case kModernRandom2 :
2021-09-02 23:31:47 +00:00
switch ( event . cmd )
{
2020-02-07 19:47:43 +00:00
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-02-07 19:47:43 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdRepeat :
2021-08-28 22:56:14 +00:00
useRandomItemGen ( actor ) ;
2020-02-07 19:47:43 +00:00
if ( pXSprite - > busyTime > 0 )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , ( 120 * pXSprite - > busyTime ) / 10 , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
return true ;
2021-09-02 23:31:47 +00:00
2020-02-07 19:47:43 +00:00
case kModernThingTNTProx :
2021-09-02 23:31:47 +00:00
if ( pSprite - > statnum ! = kStatRespawn )
{
switch ( event . cmd )
{
2020-02-07 19:47:43 +00:00
case kCmdSpriteProximity :
if ( pXSprite - > state ) break ;
sfxPlay3DSound ( pSprite , 452 , 0 , 0 ) ;
2021-08-27 08:18:33 +00:00
evPostActor ( actor , 30 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
pXSprite - > state = 1 ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdOn :
sfxPlay3DSound ( pSprite , 451 , 0 , 0 ) ;
pXSprite - > Proximity = 1 ;
break ;
default :
2021-09-02 23:33:57 +00:00
actExplodeSprite ( actor ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
return true ;
case kModernThingEnemyLifeLeech :
2021-05-06 07:20:34 +00:00
dudeLeechOperate ( actor , event ) ;
2020-02-07 19:47:43 +00:00
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
2021-08-28 16:26:24 +00:00
| | ( ( cmd < 67 | | cmd > 68 ) & & ! modernTypeSetSpriteState ( actor , pXSprite - > state ^ 1 ) ) )
2020-05-05 18:50:14 +00:00
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 ;
2021-09-02 23:31:47 +00:00
else if ( pPlayer - > pXSprite - > health < = 0 )
{
2020-05-05 18:50:14 +00:00
switch ( cmd ) {
case 36 :
2020-12-05 22:49:51 +00:00
actHealDude ( pPlayer - > actor ( ) , ( ( pXSprite - > data2 > 0 ) ? ClipHigh ( pXSprite - > data2 , 200 ) : getDudeInfo ( pPlayer - > pSprite - > type ) - > startHealth ) , 200 ) ;
2021-08-18 09:56:06 +00:00
pPlayer - > curWeapon = kWeapPitchFork ;
2020-05-05 18:50:14 +00:00
break ;
2020-05-22 16:28:03 +00:00
}
return true ;
}
2020-02-07 19:47:43 +00:00
2021-09-02 23:31:47 +00:00
switch ( cmd )
{
2020-05-05 18:50:14 +00:00
case 0 : // 64 (player life form)
if ( pXSprite - > data2 < kModeHuman | | pXSprite - > data2 > kModeHumanGrown ) break ;
2021-09-02 20:16:18 +00:00
else trPlayerCtrlSetRace ( pXSprite - > data2 , pPlayer ) ;
2020-02-07 19:47:43 +00:00
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 ) )
2021-09-02 20:16:18 +00:00
trPlayerCtrlSetMoveSpeed ( pXSprite - > data2 , pPlayer ) ;
2020-02-07 19:47:43 +00:00
// player jump height (for all races and stand posture only)
if ( valueIsBetween ( pXSprite - > data3 , - 1 , 32767 ) )
2021-09-02 20:16:18 +00:00
trPlayerCtrlSetJumpHeight ( pXSprite - > data3 , pPlayer ) ;
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 20:16:18 +00:00
else trPlayerCtrlSetScreenEffect ( pXSprite - > data2 , pXSprite - > data3 , pPlayer ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 3 : // 67 (start playing qav scene)
2021-08-28 08:38:36 +00:00
trPlayerCtrlStartScene ( actor , pPlayer , ( pXSprite - > data4 = = 1 ) ? true : false ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-05-05 18:50:14 +00:00
case 4 : // 68 (stop playing qav scene)
2020-05-22 16:28:03 +00:00
if ( pXSprite - > data2 > 0 & & pXSprite - > data2 ! = pPlayer - > sceneQav ) break ;
else 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 ) )
2021-09-02 20:16:18 +00:00
trPlayerCtrlSetLookAngle ( pXSprite - > data2 , pPlayer ) ;
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 20:16:18 +00:00
else trPlayerCtrlEraseStuff ( pXSprite - > data2 , pPlayer ) ;
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 20:16:18 +00:00
else trPlayerCtrlGiveStuff ( pXSprite - > data2 , pXSprite - > data3 , pXSprite - > data4 , pPlayer , pCtrl ) ;
2020-02-07 19:47:43 +00:00
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 ;
2021-09-02 20:16:18 +00:00
else trPlayerCtrlUsePackItem ( pXSprite - > data2 , pXSprite - > data3 , pXSprite - > data4 , pPlayer , event . cmd ) ;
2020-05-05 18:50:14 +00:00
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 ;
2021-09-02 20:13:31 +00:00
else if ( pSprite - > flags & kModernTypeFlag1 )
{
2021-07-18 07:35:11 +00:00
pPlayer - > angle . settarget ( pSprite - > ang ) ;
2021-07-18 09:26:24 +00:00
pPlayer - > angle . lockinput ( ) ;
2021-07-18 07:35:11 +00:00
}
2021-09-02 23:31:47 +00:00
else if ( valueIsBetween ( pXSprite - > data2 , - kAng360 , kAng360 ) )
{
2021-07-18 07:35:11 +00:00
pPlayer - > angle . settarget ( pXSprite - > data2 ) ;
2021-07-18 09:26:24 +00:00
pPlayer - > angle . lockinput ( ) ;
2021-07-18 07:35:11 +00:00
}
2020-02-07 19:47:43 +00:00
break ;
2021-07-26 19:52:42 +00:00
case 10 : // 74 (de)activate powerup
if ( pXSprite - > data2 < = 0 | | pXSprite - > data2 > ( kMaxAllowedPowerup - ( kMinAllowedPowerup < < 1 ) + 1 ) ) break ;
2021-09-02 20:16:18 +00:00
trPlayerCtrlUsePowerup ( actor , pPlayer , event . cmd ) ;
2021-07-26 19:52:42 +00:00
break ;
// case 11: // 75 (print the book)
2021-01-07 12:33:20 +00:00
// data2: RFF TXT id
// data3: background tile
// data4: font base tile
// pal: font / background palette
// hitag:
// d1: 0: print whole text at a time, 1: print line by line, 2: word by word, 3: letter by letter
// d2: 1: force pause the game (sp only)
// d3: 1: inherit palette for font, 2: inherit palette for background, 3: both
// busyTime: speed of word/letter/line printing
// waitTime: if TX ID > 0 and TX ID object is book reader, trigger it?
2021-07-26 19:52:42 +00:00
//break;
2021-01-07 12:33:20 +00:00
2020-02-07 19:47:43 +00:00
}
}
return true ;
2021-09-02 23:33:57 +00:00
2020-02-07 19:47:43 +00:00
case kGenModernSound :
switch ( event . cmd ) {
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-02-07 19:47:43 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-02-07 19:47:43 +00:00
case kCmdRepeat :
2021-08-28 16:26:24 +00:00
if ( pXSprite - > txID ) modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2021-08-29 06:57:23 +00:00
else useSoundGen ( actor , actor ) ;
2020-12-06 20:56:09 +00:00
if ( pXSprite - > busyTime > 0 )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , ( 120 * pXSprite - > busyTime ) / 10 , kCmdRepeat ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-12-06 20:56:09 +00:00
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
2020-12-06 20:56:09 +00:00
return true ;
case kGenModernMissileUniversal :
2021-09-02 23:31:47 +00:00
switch ( event . cmd )
{
2020-12-06 20:56:09 +00:00
case kCmdOff :
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 1 ) SetSpriteState ( actor , 0 ) ;
2020-12-06 20:56:09 +00:00
break ;
case kCmdOn :
2021-08-27 08:18:33 +00:00
evKillActor ( actor ) ; // queue overflow protect
2021-09-02 23:33:57 +00:00
if ( pXSprite - > state = = 0 ) SetSpriteState ( actor , 1 ) ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-12-06 20:56:09 +00:00
case kCmdRepeat :
2021-08-28 16:26:24 +00:00
if ( pXSprite - > txID ) modernTypeSendCommand ( actor , pXSprite - > txID , ( COMMAND_ID ) pXSprite - > command ) ;
2021-08-29 06:45:14 +00:00
else useUniMissileGen ( actor , actor ) ;
2021-07-19 21:15:26 +00:00
if ( pXSprite - > busyTime > 0 )
2021-08-27 08:18:33 +00:00
evPostActor ( actor , ( 120 * pXSprite - > busyTime ) / 10 , kCmdRepeat ) ;
2021-07-19 21:15:26 +00:00
2020-02-07 19:47:43 +00:00
break ;
default :
2021-08-27 08:18:33 +00:00
if ( pXSprite - > state = = 0 ) evPostActor ( actor , 0 , kCmdOn ) ;
else evPostActor ( actor , 0 , kCmdOff ) ;
2020-02-07 19:47:43 +00:00
break ;
}
2020-05-22 16:28:03 +00:00
return true ;
2020-02-07 19:47:43 +00:00
}
}
2021-10-03 12:47:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool modernTypeOperateWall ( int nWall , walltype * pWall , XWALL * pXWall , EVENT event )
{
2020-02-07 19:47:43 +00:00
2021-10-03 12:47:40 +00:00
switch ( pWall - > type )
{
2020-02-07 19:47:43 +00:00
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();
}
}
2021-10-03 12:47:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:50:55 +00:00
bool txIsRanged ( DBloodActor * source )
{
if ( ! source - > hasX ( ) ) return false ;
auto pXSource = & source - > x ( ) ;
2021-10-03 12:47:40 +00:00
if ( pXSource - > data1 > 0 & & pXSource - > data2 < = 0 & & pXSource - > data3 < = 0 & & pXSource - > data4 > 0 )
{
if ( pXSource - > data1 > pXSource - > data4 )
{
2020-03-01 20:36:28 +00:00
// data1 must be less than data4
int tmp = pXSource - > data1 ; pXSource - > data1 = pXSource - > data4 ;
pXSource - > data4 = tmp ;
}
return true ;
}
return false ;
}
2021-10-03 12:47:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:50:55 +00:00
void seqTxSendCmdAll ( DBloodActor * source , DBloodActor * actor , COMMAND_ID cmd , bool modernSend )
{
bool ranged = txIsRanged ( source ) ;
auto pXSource = & source - > x ( ) ;
2021-10-03 12:47:40 +00:00
if ( ranged )
{
for ( pXSource - > txID = pXSource - > data1 ; pXSource - > txID < = pXSource - > data4 ; pXSource - > txID + + )
{
2020-05-22 16:28:03 +00:00
if ( pXSource - > txID < = 0 | | pXSource - > txID > = kChannelUserMax ) continue ;
2021-08-27 08:46:57 +00:00
else if ( ! modernSend ) evSendActor ( actor , pXSource - > txID , cmd ) ;
2021-08-28 16:26:24 +00:00
else modernTypeSendCommand ( actor , pXSource - > txID , cmd ) ;
2020-03-01 20:36:28 +00:00
}
2021-10-03 12:47:40 +00:00
}
else
{
for ( int i = 0 ; i < = 3 ; i + + )
{
2021-08-27 15:09:55 +00:00
pXSource - > txID = GetDataVal ( & bloodActors [ pXSource - > reference ] , i ) ;
2020-05-22 16:28:03 +00:00
if ( pXSource - > txID < = 0 | | pXSource - > txID > = kChannelUserMax ) continue ;
2021-08-27 08:46:57 +00:00
else if ( ! modernSend ) evSendActor ( actor , pXSource - > txID , cmd ) ;
2021-08-28 16:26:24 +00:00
else modernTypeSendCommand ( actor , pXSource - > txID , cmd ) ;
2020-03-01 20:36:28 +00:00
}
}
pXSource - > txID = pXSource - > sysData1 = 0 ;
}
2021-10-03 12:47:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:50:55 +00:00
void useRandomTx ( DBloodActor * sourceactor , COMMAND_ID cmd , bool setState )
{
2021-08-27 15:14:55 +00:00
spritetype * pSource = & sourceactor - > s ( ) ;
2021-10-03 12:50:55 +00:00
auto pXSource = & sourceactor - > x ( ) ;
2020-03-01 20:36:28 +00:00
int tx = 0 ; int maxRetries = kMaxRandomizeRetries ;
2021-10-03 12:50:55 +00:00
if ( txIsRanged ( sourceactor ) )
{
2021-10-03 12:47:40 +00:00
while ( maxRetries - - > = 0 )
{
2020-03-01 20:36:28 +00:00
if ( ( tx = nnExtRandom ( pXSource - > data1 , pXSource - > data4 ) ) ! = pXSource - > txID )
break ;
}
2021-10-03 12:47:40 +00:00
}
else
{
while ( maxRetries - - > = 0 )
{
2021-08-27 15:14:55 +00:00
if ( ( tx = randomGetDataValue ( sourceactor , kRandomizeTX ) ) > 0 & & tx ! = pXSource - > txID )
2020-03-01 20:36:28 +00:00
break ;
}
}
pXSource - > txID = ( tx > 0 & & tx < kChannelUserMax ) ? tx : 0 ;
if ( setState )
2021-10-03 12:50:55 +00:00
SetSpriteState ( sourceactor , pXSource - > state ^ 1 ) ;
2021-08-27 08:46:57 +00:00
//evSendActor(pSource->index, pXSource->txID, (COMMAND_ID)pXSource->command);
2020-03-01 20:36:28 +00:00
}
2021-10-03 12:47:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 12:50:55 +00:00
void useSequentialTx ( DBloodActor * sourceactor , COMMAND_ID cmd , bool setState )
{
2021-08-27 15:09:55 +00:00
spritetype * pSource = & sourceactor - > s ( ) ;
2021-10-03 12:50:55 +00:00
auto pXSource = & sourceactor - > x ( ) ;
bool range = txIsRanged ( sourceactor ) ; int cnt = 3 ; int tx = 0 ;
2020-03-01 20:36:28 +00:00
2021-10-03 12:47:40 +00:00
if ( range )
{
2020-03-01 20:36:28 +00:00
// 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 ;
2021-10-03 12:47:40 +00:00
}
else
{
2020-03-01 20:36:28 +00:00
// 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 ;
}
2021-10-03 12:47:40 +00:00
switch ( cmd )
{
2020-03-01 20:36:28 +00:00
case kCmdOff :
2021-10-03 12:47:40 +00:00
if ( ! range )
{
while ( cnt - - > = 0 ) // skip empty data fields
{
2020-03-01 20:36:28 +00:00
if ( pXSource - > sysData1 - - < 0 ) pXSource - > sysData1 = 3 ;
2021-08-27 15:09:55 +00:00
if ( ( tx = GetDataVal ( sourceactor , pXSource - > sysData1 ) ) < = 0 ) continue ;
2020-03-01 20:36:28 +00:00
else break ;
}
2021-10-03 12:47:40 +00:00
}
else
{
2020-03-01 20:36:28 +00:00
if ( - - pXSource - > sysData1 < pXSource - > data1 ) pXSource - > sysData1 = pXSource - > data4 ;
tx = pXSource - > sysData1 ;
}
break ;
2021-10-03 12:47:40 +00:00
2020-03-01 20:36:28 +00:00
default :
2021-10-03 12:47:40 +00:00
if ( ! range )
{
while ( cnt - - > = 0 ) // skip empty data fields
{
2020-03-01 20:36:28 +00:00
if ( pXSource - > sysData1 > 3 ) pXSource - > sysData1 = 0 ;
2021-08-27 15:09:55 +00:00
if ( ( tx = GetDataVal ( sourceactor , pXSource - > sysData1 + + ) ) < = 0 ) continue ;
2020-03-01 20:36:28 +00:00
else break ;
}
2021-10-03 12:47:40 +00:00
}
else
{
2020-03-01 20:36:28 +00:00
tx = pXSource - > sysData1 ;
2021-10-03 12:47:40 +00:00
if ( pXSource - > sysData1 > = pXSource - > data4 )
{
2020-03-01 20:36:28 +00:00
pXSource - > sysData1 = pXSource - > data1 ;
break ;
}
pXSource - > sysData1 + + ;
}
break ;
}
pXSource - > txID = ( tx > 0 & & tx < kChannelUserMax ) ? tx : 0 ;
if ( setState )
2021-10-03 12:50:55 +00:00
SetSpriteState ( sourceactor , pXSource - > state ^ 1 ) ;
2021-08-27 08:46:57 +00:00
//evSendActor(pSource->index, pXSource->txID, (COMMAND_ID)pXSource->command);
2021-07-19 21:15:26 +00:00
2020-03-01 20:36:28 +00:00
}
2021-10-03 12:47:40 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-05-05 18:50:14 +00:00
2021-08-28 22:56:14 +00:00
int useCondition ( DBloodActor * sourceactor , const EVENT & event )
2021-10-03 12:47:40 +00:00
{
2021-08-28 22:56:14 +00:00
spritetype * pSource = & sourceactor - > s ( ) ;
auto pXSource = & sourceactor - > x ( ) ;
int objType = event . type ;
2021-08-27 10:03:11 +00:00
int objIndex = event . index_ ;
2020-05-22 16:28:03 +00:00
bool srcIsCondition = false ;
2021-08-28 22:56:14 +00:00
if ( objType = = OBJ_SPRITE & & event . actor = = nullptr ) return - 1 ;
if ( objType = = OBJ_SPRITE ) objIndex = event . actor - > s ( ) . index ; // need this below for calling nnExtTriggerObject
2021-08-27 10:03:11 +00:00
if ( objType = = OBJ_SPRITE & & event . actor ! = sourceactor )
2020-05-22 16:28:03 +00:00
srcIsCondition = ( sprite [ objIndex ] . type = = kModernCondition | | sprite [ objIndex ] . type = = kModernConditionFalse ) ;
2020-03-13 20:59:13 +00:00
2020-05-22 16:28:03 +00:00
// if it's a tracking condition, it must ignore all the commands sent from objects
if ( pXSource - > busyTime > 0 & & event . funcID ! = kCallbackMax ) return - 1 ;
2021-10-13 22:03:52 +00:00
else if ( ! srcIsCondition ) // save object serials in the stack and make copy of initial object
{
2021-10-13 22:32:45 +00:00
condPush ( sourceactor , objType , objIndex , event . actor ) ;
condBackup ( sourceactor ) ;
2021-10-13 22:03:52 +00:00
}
else // or grab serials of objects from previous conditions
{
2021-10-13 23:40:46 +00:00
// condition-target
2021-08-27 10:03:11 +00:00
pXSource - > targetX = event . actor - > x ( ) . targetX ;
pXSource - > targetY = event . actor - > x ( ) . targetY ;
2020-05-05 18:50:14 +00:00
2020-05-22 16:28:03 +00:00
}
2021-10-13 22:46:15 +00:00
int cond = pXSource - > data1 ;
bool ok = false ;
bool RVRS = ( pSource - > type = = kModernConditionFalse ) ;
bool RSET = ( pXSource - > command = = kCmdNumberic + 36 ) ;
bool PUSH = ( pXSource - > command = = kCmdNumberic ) ;
2020-05-22 16:28:03 +00:00
int comOp = pSource - > cstat ; // comparison operator
2021-07-19 21:15:26 +00:00
2021-10-13 22:46:15 +00:00
if ( pXSource - > restState = = 0 )
{
2020-05-05 18:50:14 +00:00
if ( cond = = 0 ) ok = true ; // dummy
2021-10-13 22:46:15 +00:00
else if ( cond > = kCondGameBase & & cond < kCondGameMax ) ok = condCheckGame ( sourceactor , event , comOp , PUSH ) ;
else if ( cond > = kCondMixedBase & & cond < kCondMixedMax ) ok = condCheckMixed ( sourceactor , event , comOp , PUSH ) ;
2021-10-13 22:49:45 +00:00
else if ( cond > = kCondWallBase & & cond < kCondWallMax ) ok = condCheckWall ( sourceactor , comOp , PUSH ) ;
else if ( cond > = kCondSectorBase & & cond < kCondSectorMax ) ok = condCheckSector ( sourceactor , comOp , PUSH ) ;
2021-10-13 23:00:19 +00:00
else if ( cond > = kCondPlayerBase & & cond < kCondPlayerMax ) ok = condCheckPlayer ( sourceactor , comOp , PUSH ) ;
else if ( cond > = kCondDudeBase & & cond < kCondDudeMax ) ok = condCheckDude ( sourceactor , comOp , PUSH ) ;
2021-10-13 23:06:50 +00:00
else if ( cond > = kCondSpriteBase & & cond < kCondSpriteMax ) ok = condCheckSprite ( sourceactor , comOp , PUSH ) ;
else condError ( sourceactor , " Unexpected condition id %d! " , cond ) ;
2020-05-05 18:50:14 +00:00
pXSource - > state = ( ok ^ RVRS ) ;
2021-10-13 22:03:52 +00:00
if ( pXSource - > waitTime > 0 & & pXSource - > state > 0 )
{
2020-05-05 18:50:14 +00:00
pXSource - > restState = 1 ;
2021-08-27 08:18:33 +00:00
evKillActor ( sourceactor ) ;
evPostActor ( sourceactor , ( pXSource - > waitTime * 120 ) / 10 , kCmdRepeat ) ;
2020-05-05 18:50:14 +00:00
return - 1 ;
2020-05-22 16:28:03 +00:00
}
2021-10-13 22:03:52 +00:00
}
else if ( event . cmd = = kCmdRepeat )
{
2020-05-05 18:50:14 +00:00
pXSource - > restState = 0 ;
2021-10-13 22:03:52 +00:00
}
else
{
2020-05-05 18:50:14 +00:00
return - 1 ;
}
2021-10-13 22:03:52 +00:00
if ( pXSource - > state )
{
2021-07-19 21:15:26 +00:00
pXSource - > isTriggered = pXSource - > triggerOnce ;
if ( RSET )
2021-10-13 22:32:45 +00:00
condRestore ( sourceactor ) ; // reset focus to the initial object
2020-05-22 16:28:03 +00:00
2020-03-30 19:54:28 +00:00
// send command to rx bucket
if ( pXSource - > txID )
2021-08-28 22:56:14 +00:00
evSendActor ( sourceactor , pXSource - > txID , ( COMMAND_ID ) pXSource - > command ) ;
2021-01-07 12:33:20 +00:00
if ( pSource - > flags ) {
2020-03-30 19:54:28 +00:00
2020-05-05 18:50:14 +00:00
// send it for object currently in the focus
2021-10-13 22:03:52 +00:00
if ( pSource - > flags & kModernTypeFlag1 )
{
2021-10-13 23:18:26 +00:00
nnExtTriggerObject ( objType , objIndex , event . actor , pXSource - > command ) ;
2020-05-22 16:28:03 +00:00
}
2020-03-30 19:54:28 +00:00
// send it for initial object
2021-10-13 23:40:46 +00:00
// condition-target
2021-01-07 12:33:20 +00:00
if ( ( pSource - > flags & kModernTypeFlag2 ) & & ( pXSource - > targetX ! = pXSource - > targetY | | ! ( pSource - > hitag & kModernTypeFlag1 ) ) ) {
2021-10-13 22:32:45 +00:00
DBloodActor * objActor = nullptr ;
condUnserialize ( sourceactor , & objType , & objIndex , & objActor ) ;
2021-10-13 23:18:26 +00:00
nnExtTriggerObject ( objType , objIndex , objActor , pXSource - > command ) ;
2020-03-30 19:54:28 +00:00
}
2020-05-22 16:28:03 +00:00
}
2020-05-05 18:50:14 +00:00
}
return pXSource - > state ;
2020-03-13 20:59:13 +00:00
}
2021-08-28 22:56:14 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void useRandomItemGen ( DBloodActor * actor )
{
spritetype * pSource = & actor - > s ( ) ;
XSPRITE * pXSource = & actor - > x ( ) ;
2020-02-07 19:47:43 +00:00
// let's first search for previously dropped items and remove it
2021-08-28 22:56:14 +00:00
if ( pXSource - > dropMsg > 0 )
{
2021-08-28 12:04:50 +00:00
BloodStatIterator it ( kStatItem ) ;
while ( auto iactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-08-28 12:04:50 +00:00
spritetype * pItem = & iactor - > s ( ) ;
2021-08-28 22:56:14 +00:00
if ( ( unsigned int ) pItem - > type = = pXSource - > dropMsg & & pItem - > x = = pSource - > x & & pItem - > y = = pSource - > y & & pItem - > z = = pSource - > z )
{
2021-08-31 23:46:42 +00:00
gFX . fxSpawnActor ( ( FX_ID ) 29 , pSource - > sectnum , pSource - > x , pSource - > y , pSource - > z , 0 ) ;
2020-03-01 20:36:28 +00:00
pItem - > type = kSpriteDecoration ;
2021-08-28 12:04:50 +00:00
actPostSprite ( iactor , kStatFree ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
}
// then drop item
2021-08-28 22:56:14 +00:00
auto dropactor = randomDropPickupObject ( actor , pXSource - > dropMsg ) ;
2020-02-07 19:47:43 +00:00
2021-08-28 22:56:14 +00:00
if ( dropactor ! = nullptr )
2021-08-27 15:41:23 +00:00
{
auto pDrop = & dropactor - > s ( ) ;
2021-07-19 21:15:26 +00:00
clampSprite ( pDrop ) ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
// check if generator affected by physics
2021-08-28 22:56:14 +00:00
if ( debrisGetIndex ( actor ) ! = - 1 )
2021-08-27 20:13:17 +00:00
{
dropactor - > addX ( ) ;
2021-07-19 21:15:26 +00:00
int nIndex = debrisGetFreeIndex ( ) ;
2021-08-27 20:13:17 +00:00
if ( nIndex > = 0 )
{
dropactor - > x ( ) . physAttr | = kPhysMove | kPhysGravity | kPhysFalling ; // must fall always
2021-07-19 21:15:26 +00:00
pSource - > cstat & = ~ CSTAT_SPRITE_BLOCK ;
2021-08-27 20:13:17 +00:00
gPhysSpritesList [ nIndex ] = dropactor ;
2021-07-19 21:15:26 +00:00
if ( nIndex > = gPhysSpritesCount ) gPhysSpritesCount + + ;
2021-08-27 17:12:22 +00:00
getSpriteMassBySize ( dropactor ) ; // create mass cache
2021-07-19 21:15:26 +00:00
}
2020-02-07 19:47:43 +00:00
}
}
}
2021-08-29 06:45:14 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void useUniMissileGen ( DBloodActor * sourceactor , DBloodActor * actor )
{
if ( actor = = nullptr ) actor = sourceactor ;
XSPRITE * pXSource = & sourceactor - > x ( ) ;
spritetype * pSource = & sourceactor - > s ( ) ;
spritetype * pSprite = & actor - > s ( ) ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
int dx = 0 , dy = 0 , dz = 0 ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
if ( pXSource - > data1 < kMissileBase | | pXSource - > data1 > = kMissileMax )
2020-02-07 19:47:43 +00:00
return ;
2021-08-29 06:45:14 +00:00
if ( pSprite - > cstat & 32 )
{
2020-02-07 19:47:43 +00:00
if ( pSprite - > cstat & 8 ) dz = 0x4000 ;
else dz = - 0x4000 ;
2021-08-29 06:45:14 +00:00
}
else
{
2021-10-30 06:22:27 +00:00
dx = bcos ( pSprite - > ang ) ;
2021-10-30 06:22:17 +00:00
dy = bsin ( pSprite - > ang ) ;
2021-07-19 21:15:26 +00:00
dz = pXSource - > data3 < < 6 ; // add slope controlling
2020-02-07 19:47:43 +00:00
if ( dz > 0x10000 ) dz = 0x10000 ;
else if ( dz < - 0x10000 ) dz = - 0x10000 ;
}
2021-08-29 06:45:14 +00:00
auto missileactor = actFireMissile ( actor , 0 , 0 , dx , dy , dz , actor - > x ( ) . data1 ) ;
if ( missileactor )
{
auto pMissile = & missileactor - > s ( ) ;
2021-07-19 21:15:26 +00:00
int from ; // inherit some properties of the generator
2021-08-29 06:45:14 +00:00
if ( ( from = ( pSource - > flags & kModernTypeFlag3 ) ) > 0 )
{
2021-07-19 21:15:26 +00:00
int canInherit = 0xF ;
2021-08-29 06:45:14 +00:00
if ( missileactor - > hasX ( ) & & seqGetStatus ( missileactor ) > = 0 )
{
2021-07-19 21:15:26 +00:00
canInherit & = ~ 0x8 ;
2021-08-29 06:45:14 +00:00
SEQINST * pInst = GetInstance ( missileactor ) ;
Seq * pSeq = pInst - > pSequence ;
for ( int i = 0 ; i < pSeq - > nFrames ; i + + )
{
2021-07-19 21:15:26 +00:00
if ( ( canInherit & 0x4 ) & & pSeq - > frames [ i ] . palette ! = 0 ) canInherit & = ~ 0x4 ;
if ( ( canInherit & 0x2 ) & & pSeq - > frames [ i ] . xrepeat ! = 0 ) canInherit & = ~ 0x2 ;
if ( ( canInherit & 0x1 ) & & pSeq - > frames [ i ] . yrepeat ! = 0 ) canInherit & = ~ 0x1 ;
}
}
2020-02-07 19:47:43 +00:00
2021-08-29 06:45:14 +00:00
if ( canInherit ! = 0 )
{
2021-07-19 21:15:26 +00:00
if ( canInherit & 0x2 )
pMissile - > xrepeat = ( from = = kModernTypeFlag1 ) ? pSource - > xrepeat : pSprite - > xrepeat ;
if ( canInherit & 0x1 )
pMissile - > yrepeat = ( from = = kModernTypeFlag1 ) ? pSource - > yrepeat : pSprite - > yrepeat ;
2020-02-07 19:47:43 +00:00
2021-07-19 21:15:26 +00:00
if ( canInherit & 0x4 )
pMissile - > pal = ( from = = kModernTypeFlag1 ) ? pSource - > pal : pSprite - > pal ;
if ( canInherit & 0x8 )
pMissile - > shade = ( from = = kModernTypeFlag1 ) ? pSource - > shade : pSprite - > shade ;
}
2020-02-07 19:47:43 +00:00
}
// add velocity controlling
2021-08-29 06:45:14 +00:00
if ( pXSource - > data2 > 0 )
{
2021-07-19 21:15:26 +00:00
int velocity = pXSource - > data2 < < 12 ;
2021-08-29 06:45:14 +00:00
missileactor - > xvel ( ) = MulScale ( velocity , dx , 14 ) ;
missileactor - > yvel ( ) = MulScale ( velocity , dy , 14 ) ;
missileactor - > zvel ( ) = MulScale ( velocity , dz , 14 ) ;
2020-02-07 19:47:43 +00:00
}
// add bursting for missiles
2021-07-19 21:15:26 +00:00
if ( pMissile - > type ! = kMissileFlareAlt & & pXSource - > data4 > 0 )
2021-08-29 06:45:14 +00:00
evPostActor ( missileactor , ClipHigh ( pXSource - > data4 , 500 ) , kCallbackMissileBurst ) ;
2020-02-07 19:47:43 +00:00
}
}
2021-08-29 06:57:23 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void useSoundGen ( DBloodActor * sourceactor , DBloodActor * actor )
{
int pitch = sourceactor - > x ( ) . data4 < < 1 ;
if ( pitch < 2000 ) pitch = 0 ;
sfxPlay3DSoundCP ( actor , sourceactor - > x ( ) . data2 , - 1 , 0 , pitch , sourceactor - > x ( ) . data3 ) ;
2020-02-07 19:47:43 +00:00
}
2021-08-29 06:57:23 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 15:47:59 +00:00
void useIncDecGen ( DBloodActor * sourceactor , short objType , int objIndex , DBloodActor * objactor )
{
auto pXSource = & sourceactor - > x ( ) ;
spritetype * pSource = & sourceactor - > s ( ) ;
char buffer [ 7 ] ;
int data = - 65535 ;
short tmp = 0 ;
int dataIndex = 0 ;
snprintf ( buffer , 7 , " %d " , abs ( pXSource - > data1 ) ) ;
int len = int ( strlen ( buffer ) ) ;
2020-02-07 19:47:43 +00:00
2021-10-03 15:47:59 +00:00
for ( int i = 0 ; i < len ; i + + )
{
2020-03-05 20:46:05 +00:00
dataIndex = ( buffer [ i ] - 52 ) + 4 ;
2021-10-03 15:47:59 +00:00
if ( ( data = getDataFieldOfObject ( objType , objIndex , objactor , dataIndex ) ) = = - 65535 )
{
2020-10-11 10:38:17 +00:00
Printf ( PRINT_HIGH , " \n Wrong index of data (%c) for IncDec Gen #%d! Only 1, 2, 3 and 4 indexes allowed! \n " , buffer [ i ] , objIndex ) ;
2020-03-05 20:46:05 +00:00
continue ;
}
2020-05-05 18:50:14 +00:00
2021-10-03 15:42:40 +00:00
if ( pXSource - > data2 < pXSource - > data3 )
{
2020-03-05 20:46:05 +00:00
data = ClipRange ( data , pXSource - > data2 , pXSource - > data3 ) ;
2021-10-03 15:42:40 +00:00
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 ;
2021-09-05 18:10:40 +00:00
if ( ! incDecGoalValueIsReached ( sourceactor ) ) 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
}
2021-10-03 15:42:40 +00:00
}
else if ( pXSource - > data2 > pXSource - > data3 )
{
2020-03-05 20:46:05 +00:00
data = ClipRange ( data , pXSource - > data3 , pXSource - > data2 ) ;
2021-10-03 15:42:40 +00:00
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 ;
2021-09-05 18:10:40 +00:00
if ( ! incDecGoalValueIsReached ( sourceactor ) ) 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 ;
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , objactor , dataIndex , data ) ;
2020-03-05 20:46:05 +00:00
}
2020-02-07 19:47:43 +00:00
}
2021-10-13 17:13:15 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-10-13 17:13:15 +00:00
void sprite2sectorSlope ( DBloodActor * actor , sectortype * pSector , char rel , bool forcez )
{
auto pSprite = & actor - > s ( ) ;
2021-01-07 12:33:20 +00:00
int slope = 0 , z = 0 ;
switch ( rel ) {
default :
z = getflorzofslope ( pSprite - > sectnum , pSprite - > x , pSprite - > y ) ;
2021-10-13 17:13:15 +00:00
if ( ( pSprite - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) & & actor - > hasX ( ) & & actor - > x ( ) . Touch ) z - - ;
2021-01-07 12:33:20 +00:00
slope = pSector - > floorheinum ;
break ;
case 1 :
z = getceilzofslope ( pSprite - > sectnum , pSprite - > x , pSprite - > y ) ;
2021-10-13 17:13:15 +00:00
if ( ( pSprite - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) & & actor - > hasX ( ) & & actor - > x ( ) . Touch ) z + + ;
2021-01-07 12:33:20 +00:00
slope = pSector - > ceilingheinum ;
break ;
}
2021-10-13 17:11:34 +00:00
spriteSetSlope ( pSprite , slope ) ;
2021-01-07 12:33:20 +00:00
if ( forcez ) pSprite - > z = z ;
}
2021-10-13 17:09:20 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 17:13:15 +00:00
void useSlopeChanger ( DBloodActor * sourceactor , int objType , int objIndex , DBloodActor * objActor )
{
auto pXSource = & sourceactor - > x ( ) ;
spritetype * pSource = & sourceactor - > s ( ) ;
2021-01-07 12:33:20 +00:00
2021-10-13 17:17:45 +00:00
int slope , oslope ;
2021-01-07 12:33:20 +00:00
bool flag2 = ( pSource - > flags & kModernTypeFlag2 ) ;
if ( pSource - > flags & kModernTypeFlag1 ) slope = ClipRange ( pXSource - > data2 , - 32767 , 32767 ) ;
else slope = ( 32767 / kPercFull ) * ClipRange ( pXSource - > data2 , - kPercFull , kPercFull ) ;
2021-10-13 17:09:20 +00:00
if ( objType = = OBJ_SECTOR )
{
2020-12-06 20:56:09 +00:00
sectortype * pSect = & sector [ objIndex ] ;
2021-01-07 12:33:20 +00:00
2021-10-13 17:09:20 +00:00
switch ( pXSource - > data1 )
{
2020-12-06 20:56:09 +00:00
case 2 :
case 0 :
2021-01-07 12:33:20 +00:00
if ( slope = = 0 ) pSect - > floorstat & = ~ 0x0002 ;
else if ( ! ( pSect - > floorstat & 0x0002 ) )
2020-12-06 20:56:09 +00:00
pSect - > floorstat | = 0x0002 ;
2021-01-07 12:33:20 +00:00
// just set floor slope
2021-10-13 17:09:20 +00:00
if ( flag2 )
{
2021-01-07 12:33:20 +00:00
pSect - > floorheinum = slope ;
2021-10-13 17:09:20 +00:00
}
else
{
2021-01-07 12:33:20 +00:00
// force closest floor aligned sprites to inherit slope of the sector's floor
2021-10-13 17:13:15 +00:00
oslope = pSect - > floorheinum ;
BloodSectIterator it ( objIndex ) ;
while ( auto iactor = it . Next ( ) )
{
auto spr = & iactor - > s ( ) ;
if ( ! ( spr - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) ) continue ;
else if ( getflorzofslope ( objIndex , spr - > x , spr - > y ) - kSlopeDist < = spr - > z )
{
sprite2sectorSlope ( iactor , & sector [ objIndex ] , 0 , true ) ;
2021-01-07 12:33:20 +00:00
// set new slope of floor
pSect - > floorheinum = slope ;
// force sloped sprites to be on floor slope z
2021-10-13 17:13:15 +00:00
sprite2sectorSlope ( iactor , & sector [ objIndex ] , 0 , true ) ;
2021-01-07 12:33:20 +00:00
// restore old slope for next sprite
pSect - > floorheinum = oslope ;
}
2020-12-06 20:56:09 +00:00
}
2021-01-07 12:33:20 +00:00
// finally set new slope of floor
2020-12-06 20:56:09 +00:00
pSect - > floorheinum = slope ;
2021-01-07 12:33:20 +00:00
}
2020-12-06 20:56:09 +00:00
if ( pXSource - > data1 = = 0 ) break ;
2021-09-02 20:13:31 +00:00
[[fallthrough]] ;
2020-12-06 20:56:09 +00:00
case 1 :
2021-01-07 12:33:20 +00:00
if ( slope = = 0 ) pSect - > ceilingstat & = ~ 0x0002 ;
else if ( ! ( pSect - > ceilingstat & 0x0002 ) )
2020-12-06 20:56:09 +00:00
pSect - > ceilingstat | = 0x0002 ;
2021-01-07 12:33:20 +00:00
// just set ceiling slope
2021-10-13 17:09:20 +00:00
if ( flag2 )
{
2021-01-07 12:33:20 +00:00
pSect - > ceilingheinum = slope ;
2021-10-13 17:09:20 +00:00
}
else
{
2021-10-13 17:13:15 +00:00
oslope = pSect - > ceilingheinum ;
BloodSectIterator it ( objIndex ) ;
while ( auto iactor = it . Next ( ) )
{
auto spr = & iactor - > s ( ) ;
if ( ! ( spr - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) ) continue ;
else if ( getceilzofslope ( objIndex , spr - > x , spr - > y ) + kSlopeDist > = spr - > z )
{
sprite2sectorSlope ( iactor , & sector [ objIndex ] , 1 , true ) ;
2021-01-07 12:33:20 +00:00
// set new slope of ceiling
pSect - > ceilingheinum = slope ;
// force sloped sprites to be on ceiling slope z
2021-10-13 17:13:15 +00:00
sprite2sectorSlope ( iactor , & sector [ objIndex ] , 1 , true ) ;
2021-01-07 12:33:20 +00:00
// restore old slope for next sprite
pSect - > ceilingheinum = oslope ;
2020-12-06 20:56:09 +00:00
}
2021-01-07 12:33:20 +00:00
}
// finally set new slope of ceiling
2020-12-06 20:56:09 +00:00
pSect - > ceilingheinum = slope ;
2021-01-07 12:33:20 +00:00
}
2020-12-06 20:56:09 +00:00
break ;
}
2021-01-07 12:33:20 +00:00
// let's give a little impulse to the physics sprites...
2021-10-13 17:13:15 +00:00
BloodSectIterator it ( objIndex ) ;
while ( auto iactor = it . Next ( ) )
{
auto spr = & iactor - > s ( ) ;
auto xspr = & iactor - > x ( ) ;
if ( spr - > extra > 0 & & xspr - > physAttr > 0 )
{
xspr - > physAttr | = kPhysFalling ;
iactor - > zvel ( ) + + ;
}
else if ( ( spr - > statnum = = kStatThing | | spr - > statnum = = kStatDude ) & & ( spr - > flags & kPhysGravity ) )
{
spr - > flags | = kPhysFalling ;
iactor - > zvel ( ) + + ;
2020-12-06 20:56:09 +00:00
}
2021-01-07 12:33:20 +00:00
}
2021-10-13 17:09:20 +00:00
}
else if ( objType = = OBJ_SPRITE )
{
2021-10-13 17:13:15 +00:00
spritetype * pSpr = & objActor - > s ( ) ;
2020-12-06 20:56:09 +00:00
if ( ! ( pSpr - > cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR ) ) pSpr - > cstat | = CSTAT_SPRITE_ALIGNMENT_FLOOR ;
2021-01-07 12:33:20 +00:00
if ( ( pSpr - > cstat & CSTAT_SPRITE_ALIGNMENT_SLOPE ) ! = CSTAT_SPRITE_ALIGNMENT_SLOPE )
pSpr - > cstat | = CSTAT_SPRITE_ALIGNMENT_SLOPE ;
2021-10-13 17:09:20 +00:00
switch ( pXSource - > data4 )
{
2021-01-07 12:33:20 +00:00
case 1 :
case 2 :
case 3 :
if ( ! sectRangeIsFine ( pSpr - > sectnum ) ) break ;
2021-10-13 17:09:20 +00:00
switch ( pXSource - > data4 )
{
2021-10-13 17:13:15 +00:00
case 1 : sprite2sectorSlope ( objActor , & sector [ pSpr - > sectnum ] , 0 , flag2 ) ; break ;
case 2 : sprite2sectorSlope ( objActor , & sector [ pSpr - > sectnum ] , 1 , flag2 ) ; break ;
2021-01-07 12:33:20 +00:00
case 3 :
2021-10-13 17:13:15 +00:00
if ( getflorzofslope ( pSpr - > sectnum , pSpr - > x , pSpr - > y ) - kSlopeDist < = pSpr - > z ) sprite2sectorSlope ( objActor , & sector [ pSpr - > sectnum ] , 0 , flag2 ) ;
if ( getceilzofslope ( pSpr - > sectnum , pSpr - > x , pSpr - > y ) + kSlopeDist > = pSpr - > z ) sprite2sectorSlope ( objActor , & sector [ pSpr - > sectnum ] , 1 , flag2 ) ;
2021-01-07 12:33:20 +00:00
break ;
}
break ;
default :
2021-10-13 17:11:34 +00:00
spriteSetSlope ( pSpr , slope ) ;
2021-01-07 12:33:20 +00:00
break ;
}
2020-12-06 20:56:09 +00:00
}
}
2021-10-13 17:15:44 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 17:17:45 +00:00
void useDataChanger ( DBloodActor * sourceactor , int objType , int objIndex , DBloodActor * objActor )
{
auto pXSource = & sourceactor - > x ( ) ;
spritetype * pSource = & sourceactor - > s ( ) ;
2021-10-13 17:15:44 +00:00
switch ( objType )
{
2020-12-06 20:56:09 +00:00
case OBJ_SECTOR :
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data1 ! = - 1 & & pXSource - > data1 ! = 32767 ) )
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , nullptr , 1 , pXSource - > data1 ) ;
2020-02-07 19:47:43 +00:00
break ;
case OBJ_SPRITE :
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data1 ! = - 1 & & pXSource - > data1 ! = 32767 ) )
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , objActor , 1 , pXSource - > data1 ) ;
2020-02-07 19:47:43 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data2 ! = - 1 & & pXSource - > data2 ! = 32767 ) )
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , objActor , 2 , pXSource - > data2 ) ;
2020-02-07 19:47:43 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data3 ! = - 1 & & pXSource - > data3 ! = 32767 ) )
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , objActor , 3 , pXSource - > data3 ) ;
2020-02-07 19:47:43 +00:00
if ( ( pSource - > flags & kModernTypeFlag1 ) | | pXSource - > data4 ! = 65535 )
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , objActor , 4 , pXSource - > data4 ) ;
2020-02-07 19:47:43 +00:00
break ;
case OBJ_WALL :
if ( ( pSource - > flags & kModernTypeFlag1 ) | | ( pXSource - > data1 ! = - 1 & & pXSource - > data1 ! = 32767 ) )
2021-10-03 15:47:59 +00:00
setDataValueOfObject ( objType , objIndex , nullptr , 1 , pXSource - > data1 ) ;
2020-02-07 19:47:43 +00:00
break ;
}
}
2021-10-13 17:15:44 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 17:17:45 +00:00
void useSectorLigthChanger ( DBloodActor * sourceactor , XSECTOR * pXSector )
{
auto pXSource = & sourceactor - > x ( ) ;
spritetype * pSource = & sourceactor - > s ( ) ;
2020-02-07 19:47:43 +00:00
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 ) )
2021-05-12 00:00:06 +00:00
pXSector - > amplitude = uint8_t ( 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 ) ;
2021-10-13 17:15:44 +00:00
if ( pSource - > flags )
{
if ( pSource - > flags ! = kModernTypeFlag1 )
{
2020-05-05 18:50:14 +00:00
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 ;
2021-10-13 17:15:44 +00:00
}
else
{
2020-05-22 16:28:03 +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
2021-10-13 17:15:44 +00:00
if ( oldAmplitude ! = pXSector - > amplitude & & shadeCount < kMaxXSectors )
{
2020-02-07 19:47:43 +00:00
bool found = false ;
2021-10-13 17:15:44 +00:00
for ( int i = 0 ; i < shadeCount ; i + + )
{
2020-02-07 19:47:43 +00:00
if ( shadeList [ i ] ! = sector [ pXSector - > reference ] . extra ) continue ;
found = true ;
break ;
}
if ( ! found )
shadeList [ shadeCount + + ] = sector [ pXSector - > reference ] . extra ;
}
}
2021-10-13 17:15:44 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-13 17:17:45 +00:00
void useTargetChanger ( DBloodActor * sourceactor , DBloodActor * actor )
{
spritetype * pSprite = & actor - > s ( ) ;
2020-02-08 20:13:29 +00:00
2021-10-13 17:17:45 +00:00
if ( ! actor - > IsDudeActor ( ) | | pSprite - > statnum ! = kStatDude )
{
2021-10-13 17:15:44 +00:00
switch ( pSprite - > type ) // can be dead dude turned in gib
{
2020-02-07 19:47:43 +00:00
// make current target and all other dudes not attack this dude anymore
case kThingBloodBits :
case kThingBloodChunks :
2021-10-13 17:17:45 +00:00
aiFightFreeTargets ( actor ) ;
2020-02-07 19:47:43 +00:00
return ;
default :
return ;
}
}
2020-02-08 20:13:29 +00:00
2021-10-13 17:17:45 +00:00
auto pXSource = & sourceactor - > x ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
int receiveHp = 33 + Random ( 33 ) ;
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ;
int matesPerEnemy = 1 ;
2020-02-07 19:47:43 +00:00
// dude is burning?
2021-10-13 17:15:44 +00:00
if ( pXSprite - > burnTime > 0 & & spriRangeIsFine ( pXSprite - > burnSource ) )
{
2021-08-29 11:44:04 +00:00
if ( IsBurningDude ( actor ) ) return ;
2021-10-13 17:15:44 +00:00
else
{
2021-10-13 17:17:45 +00:00
auto burnactor = actor - > GetBurnSource ( ) ;
spritetype * pBurnSource = & burnactor - > s ( ) ;
if ( burnactor - > hasX ( ) )
{
if ( pXSource - > data2 = = 1 & & pXSprite - > rxID = = burnactor - > x ( ) . rxID )
{
2020-02-07 19:47:43 +00:00
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 ;
2021-10-13 17:17:45 +00:00
if ( pXSprite - > health < ( unsigned ) startHp ) actHealDude ( actor , receiveHp , startHp ) ;
}
else if ( burnactor - > x ( ) . health < = 0 )
{
2020-02-07 19:47:43 +00:00
pXSprite - > burnTime = 0 ;
}
}
}
}
2021-10-03 10:34:47 +00:00
auto playeractor = aiFightTargetIsPlayer ( actor ) ;
2020-02-07 19:47:43 +00:00
// special handling for player(s) if target changer data4 > 2.
2021-10-03 10:34:47 +00:00
if ( playeractor ! = nullptr )
{
auto pPlayer = & playeractor - > s ( ) ;
2021-05-05 19:06:38 +00:00
auto actLeech = leechIsDropped ( actor ) ;
2021-10-13 17:15:44 +00:00
if ( pXSource - > data4 = = 3 )
{
2021-10-13 17:17:45 +00:00
aiSetTarget ( actor , pSprite - > x , pSprite - > y , pSprite - > z ) ;
aiSetGenIdleState ( actor ) ;
2021-05-05 19:06:38 +00:00
if ( pSprite - > type = = kDudeModernCustom & & actLeech )
2021-05-05 19:18:09 +00:00
removeLeech ( actLeech ) ;
2021-10-13 17:15:44 +00:00
}
else if ( pXSource - > data4 = = 4 )
{
2021-10-13 17:17:45 +00:00
aiSetTarget ( actor , pPlayer - > x , pPlayer - > y , pPlayer - > z ) ;
2021-05-05 19:06:38 +00:00
if ( pSprite - > type = = kDudeModernCustom & & actLeech )
2021-05-05 19:18:09 +00:00
removeLeech ( actLeech ) ;
2020-02-07 19:47:43 +00:00
}
}
int maxAlarmDudes = 8 + Random ( 8 ) ;
2021-10-13 17:49:16 +00:00
auto targetactor = actor - > GetTarget ( ) ;
if ( targetactor & & targetactor - > hasX ( ) & & playeractor = = nullptr )
{
2021-10-13 18:00:01 +00:00
auto pTarget = & targetactor - > s ( ) ;
auto pXTarget = & targetactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
2021-10-13 17:49:16 +00:00
if ( aiFightUnitCanFly ( actor ) & & aiFightIsMeleeUnit ( targetactor ) & & ! aiFightUnitCanFly ( targetactor ) )
2020-02-07 19:47:43 +00:00
pSprite - > flags | = 0x0002 ;
2021-10-03 12:17:24 +00:00
else if ( aiFightUnitCanFly ( actor ) )
2020-02-07 19:47:43 +00:00
pSprite - > flags & = ~ 0x0002 ;
2021-10-13 17:49:16 +00:00
if ( ! targetactor - > IsDudeActor ( ) | | pXTarget - > health < 1 | | ! aiFightDudeCanSeeTarget ( actor , pDudeInfo , targetactor ) )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:45:48 +00:00
aiSetTarget ( actor , pSprite - > x , pSprite - > y , pSprite - > z ) ;
2020-02-07 19:47:43 +00:00
}
// dude attack or attacked by target that does not fit by data id?
2021-10-13 17:35:00 +00:00
else if ( pXSource - > data1 ! = 666 & & pXTarget - > data1 ! = pXSource - > data1 )
{
2021-10-13 17:49:16 +00:00
if ( aiFightDudeIsAffected ( targetactor ) )
{
2020-02-07 19:47:43 +00:00
// force stop attack target
2021-10-13 17:45:48 +00:00
aiSetTarget ( actor , pSprite - > x , pSprite - > y , pSprite - > z ) ;
2021-10-13 17:49:16 +00:00
if ( actor - > GetBurnSource ( ) = = targetactor )
2021-10-13 17:35:00 +00:00
{
2020-02-07 19:47:43 +00:00
pXSprite - > burnTime = 0 ;
pXSprite - > burnSource = - 1 ;
}
// force stop attack dude
2021-10-13 17:49:16 +00:00
aiSetTarget ( targetactor , pTarget - > x , pTarget - > y , pTarget - > z ) ;
if ( targetactor - > GetBurnSource ( ) = = actor )
2021-10-13 17:35:00 +00:00
{
2020-02-07 19:47:43 +00:00
pXTarget - > burnTime = 0 ;
pXTarget - > burnSource = - 1 ;
}
}
}
2021-10-13 17:35:00 +00:00
else if ( pXSource - > data2 = = 1 & & pXSprite - > rxID = = pXTarget - > rxID )
{
2021-10-13 17:52:06 +00:00
auto mateactor = targetactor ;
spritetype * pMate = pTarget ;
XSPRITE * pXMate = pXTarget ;
2020-02-07 19:47:43 +00:00
// heal dude
2020-04-07 20:30:00 +00:00
int startHp = ( pXSprite - > sysData2 > 0 ) ? ClipRange ( pXSprite - > sysData2 < < 4 , 1 , 65535 ) : pDudeInfo - > startHealth < < 4 ;
2021-10-13 17:45:48 +00:00
if ( pXSprite - > health < ( unsigned ) startHp ) actHealDude ( actor , 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 ;
2021-10-13 17:52:06 +00:00
if ( pXMate - > health < ( unsigned ) startHp ) actHealDude ( mateactor , receiveHp , startHp ) ;
2020-02-07 19:47:43 +00:00
2021-10-13 17:52:06 +00:00
auto matetarget = mateactor - > GetTarget ( ) ;
if ( matetarget ! = nullptr & & matetarget - > hasX ( ) )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:52:06 +00:00
auto pMateTarget = & matetarget - > s ( ) ;
2020-02-07 19:47:43 +00:00
// force mate stop attack dude, if he does
2021-10-13 17:52:06 +00:00
if ( matetarget = = actor )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:52:06 +00:00
aiSetTarget ( mateactor , pMate - > x , pMate - > y , pMate - > z ) ;
2021-10-13 17:35:00 +00:00
}
2021-10-13 17:52:06 +00:00
else if ( pXSprite - > rxID ! = matetarget - > x ( ) . rxID )
2021-10-13 17:35:00 +00:00
{
2020-02-07 19:47:43 +00:00
// force dude to attack same target that mate have
2021-10-13 17:52:06 +00:00
aiSetTarget ( actor , matetarget ) ;
2020-02-07 19:47:43 +00:00
return ;
2021-10-13 17:35:00 +00:00
}
else
{
2020-02-07 19:47:43 +00:00
// force mate to stop attack another mate
2021-10-13 17:52:06 +00:00
aiSetTarget ( mateactor , pMate - > x , pMate - > y , pMate - > z ) ;
2020-02-07 19:47:43 +00:00
}
}
// force dude stop attack mate, if target was not changed previously
2021-10-13 17:52:06 +00:00
if ( actor = = mateactor )
aiSetTarget ( actor , pSprite - > x , pSprite - > y , pSprite - > z ) ;
2020-02-07 19:47:43 +00:00
}
// check if targets aims player then force this target to fight with dude
2021-10-03 10:34:47 +00:00
else if ( aiFightTargetIsPlayer ( actor ) ! = nullptr )
{
2021-10-13 17:52:06 +00:00
aiSetTarget ( targetactor , actor ) ;
2020-02-07 19:47:43 +00:00
}
2021-10-13 17:52:06 +00:00
int mDist = 3 ;
if ( aiFightIsMeleeUnit ( actor ) ) mDist = 2 ;
2021-10-13 17:35:00 +00:00
2021-10-13 17:55:39 +00:00
if ( targetactor ! = nullptr & & aiFightGetTargetDist ( actor , pDudeInfo , targetactor ) < mDist )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:45:48 +00:00
if ( ! isActive ( actor ) ) aiActivateDude ( actor ) ;
2020-02-07 19:47:43 +00:00
return ;
}
// lets try to look for target that fits better by distance
2021-10-13 17:55:39 +00:00
else if ( ( PlayClock & 256 ) ! = 0 & & ( targetactor = = nullptr | | aiFightGetTargetDist ( actor , pDudeInfo , targetactor ) > = mDist ) )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:55:39 +00:00
auto newtargactor = aiFightGetTargetInRange ( actor , 0 , mDist , pXSource - > data1 , pXSource - > data2 ) ;
if ( newtargactor ! = nullptr )
2021-10-03 10:31:19 +00:00
{
2021-10-13 17:55:39 +00:00
auto pNewTarg = & newtargactor - > s ( ) ;
auto pXNewTarg = & newtargactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
// Make prev target not aim in dude
2021-10-13 17:55:39 +00:00
if ( targetactor )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:55:39 +00:00
aiSetTarget ( targetactor , targetactor - > s ( ) . x , targetactor - > s ( ) . y , targetactor - > s ( ) . z ) ;
if ( ! isActive ( newtargactor ) )
aiActivateDude ( newtargactor ) ;
2020-02-07 19:47:43 +00:00
}
// Change target for dude
2021-10-13 17:55:39 +00:00
aiSetTarget ( actor , newtargactor ) ;
2021-10-13 17:45:48 +00:00
if ( ! isActive ( actor ) )
aiActivateDude ( actor ) ;
2020-02-07 19:47:43 +00:00
// ...and change target of target to dude to force it fight
2021-10-13 17:55:39 +00:00
if ( pXSource - > data3 > 0 & & newtargactor - > GetTarget ( ) ! = actor )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:55:39 +00:00
aiSetTarget ( newtargactor , actor ) ;
if ( ! isActive ( newtargactor ) )
aiActivateDude ( newtargactor ) ;
2020-02-07 19:47:43 +00:00
}
2020-05-22 16:28:03 +00:00
2020-02-07 19:47:43 +00:00
return ;
}
}
}
2020-05-22 16:28:03 +00:00
2021-10-13 17:59:48 +00:00
if ( ( targetactor = = nullptr | | playeractor ! = nullptr ) & & ( PlayClock & 32 ) ! = 0 )
2021-10-13 17:35:00 +00:00
{
2020-02-07 19:47:43 +00:00
// try find first target that dude can see
2021-10-13 17:59:48 +00:00
BloodStatIterator it ( kStatDude ) ;
while ( auto newtargactor = it . Next ( ) )
2020-10-15 15:15:45 +00:00
{
2021-10-13 17:59:48 +00:00
auto pXNewTarg = & newtargactor - > x ( ) ;
2020-02-07 19:47:43 +00:00
2021-10-13 17:59:48 +00:00
if ( newtargactor - > GetTarget ( ) = = actor )
2021-10-13 17:35:00 +00:00
{
2021-10-13 17:59:48 +00:00
aiSetTarget ( actor , newtargactor ) ;
2020-02-07 19:47:43 +00:00
return ;
}
// skip non-dudes and players
2021-10-13 17:59:48 +00:00
if ( ! newtargactor - > IsDudeActor ( ) | | ( newtargactor - > IsPlayerActor ( ) & & pXSource - > data4 > 0 ) | | newtargactor - > GetOwner ( ) = = actor ) continue ;
2020-02-07 19:47:43 +00:00
// avoid self aiming, those who dude can't see, and those who dude own
2021-10-13 17:59:48 +00:00
else if ( ! aiFightDudeCanSeeTarget ( actor , pDudeInfo , newtargactor ) | | actor = = newtargactor ) continue ;
2020-02-07 19:47:43 +00:00
// if Target Changer have data1 = 666, everyone can be target, except AI team mates.
2021-10-13 17:59:48 +00:00
else if ( pXSource - > data1 ! = 666 & & pXSource - > data1 ! = pXNewTarg - > data1 ) continue ;
2020-02-07 19:47:43 +00:00
// don't attack immortal, burning dudes and mates
2021-08-29 11:44:04 +00:00
if ( IsBurningDude ( newtargactor ) | | ! IsKillableDude ( newtargactor ) | | ( pXSource - > data2 = = 1 & & pXSprite - > rxID = = pXNewTarg - > rxID ) )
2020-02-07 19:47:43 +00:00
continue ;
2021-10-13 17:59:48 +00:00
if ( pXSource - > data2 = = 0 | | ( pXSource - > data2 = = 1 & & ! aiFightMatesHaveSameTarget ( actor , newtargactor , matesPerEnemy ) ) )
2021-10-03 10:34:47 +00:00
{
2020-02-07 19:47:43 +00:00
// Change target for dude
2021-10-13 17:59:48 +00:00
aiSetTarget ( actor , newtargactor ) ;
2021-10-13 17:45:48 +00:00
if ( ! isActive ( actor ) )
aiActivateDude ( actor ) ;
2020-02-07 19:47:43 +00:00
// ...and change target of target to dude to force it fight
2021-10-13 17:59:48 +00:00
if ( pXSource - > data3 > 0 & & newtargactor - > GetTarget ( ) ! = actor )
{
aiSetTarget ( newtargactor , actor ) ;
if ( playeractor = = nullptr & & ! isActive ( newtargactor ) )
aiActivateDude ( newtargactor ) ;
2020-02-07 19:47:43 +00:00
if ( pXSource - > data3 = = 2 )
2021-10-13 17:59:48 +00:00
aiFightAlarmDudesInSight ( newtargactor , maxAlarmDudes ) ;
2020-02-07 19:47:43 +00:00
}
return ;
}
break ;
}
}
// got no target - let's ask mates if they have targets
2021-10-13 17:45:48 +00:00
if ( ( actor - > GetTarget ( ) = = nullptr | | playeractor ! = nullptr ) & & pXSource - > data2 = = 1 & & ( PlayClock & 64 ) ! = 0 )
{
2021-10-03 10:34:47 +00:00
DBloodActor * pMateTargetActor = aiFightGetMateTargets ( actor ) ;
if ( pMateTargetActor ! = nullptr & & pMateTargetActor - > hasX ( ) )
{
auto pMateTarget = & pMateTargetActor - > s ( ) ;
XSPRITE * pXMateTarget = & pMateTargetActor - > x ( ) ;
2021-10-13 17:35:00 +00:00
if ( aiFightDudeCanSeeTarget ( actor , pDudeInfo , pMateTargetActor ) )
{
2021-08-29 12:42:05 +00:00
if ( pMateTargetActor - > GetTarget ( ) = = nullptr )
2021-10-13 17:35:00 +00:00
{
2021-10-13 18:00:01 +00:00
aiSetTarget ( pMateTargetActor , actor ) ;
if ( pMateTargetActor - > IsDudeActor ( ) & & ! isActive ( pMateTargetActor ) )
aiActivateDude ( pMateTargetActor ) ;
2020-02-07 19:47:43 +00:00
}
2021-10-13 18:00:01 +00:00
aiSetTarget ( actor , pMateTargetActor ) ;
2021-10-13 17:45:48 +00:00
if ( ! isActive ( actor ) )
aiActivateDude ( actor ) ;
2020-02-07 19:47:43 +00:00
return ;
// try walk in mate direction in case if not see the target
2021-10-13 17:35:00 +00:00
}
2021-10-13 18:00:01 +00:00
else if ( pMateTargetActor - > GetTarget ( ) & & aiFightDudeCanSeeTarget ( actor , pDudeInfo , pMateTargetActor - > GetTarget ( ) ) )
2021-10-13 17:35:00 +00:00
{
2021-10-13 18:00:01 +00:00
actor - > SetTarget ( pMateTargetActor ) ;
spritetype * pMate = & pMateTargetActor - > GetTarget ( ) - > s ( ) ;
2020-02-07 19:47:43 +00:00
pXSprite - > targetX = pMate - > x ;
pXSprite - > targetY = pMate - > y ;
pXSprite - > targetZ = pMate - > z ;
2021-10-13 17:45:48 +00:00
if ( ! isActive ( actor ) )
aiActivateDude ( actor ) ;
2020-02-07 19:47:43 +00:00
return ;
}
}
}
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-08-29 11:28:13 +00:00
void usePictureChanger ( DBloodActor * sourceactor , int objType , int objIndex , DBloodActor * objActor )
{
auto pXSource = & sourceactor - > x ( ) ;
2020-05-05 18:50:14 +00:00
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 ) )
2021-05-12 00:00:06 +00:00
sector [ objIndex ] . floorpal = uint8_t ( pXSource - > data3 ) ;
2020-02-07 19:47:43 +00:00
2020-05-05 18:50:14 +00:00
if ( valueIsBetween ( pXSource - > data4 , - 1 , 65535 ) )
2021-05-12 00:00:06 +00:00
sector [ objIndex ] . ceilingpal = uint8_t ( pXSource - > data4 ) ;
2020-02-07 19:47:43 +00:00
break ;
case OBJ_SPRITE :
if ( valueIsBetween ( pXSource - > data1 , - 1 , 32767 ) )
2021-08-29 11:28:13 +00:00
objActor - > s ( ) . picnum = pXSource - > data1 ;
2020-02-07 19:47:43 +00:00
2021-08-29 11:28:13 +00:00
if ( pXSource - > data2 > = 0 ) objActor - > s ( ) . shade = ( pXSource - > data2 > 127 ) ? 127 : pXSource - > data2 ;
else if ( pXSource - > data2 < - 1 ) objActor - > s ( ) . shade = ( pXSource - > data2 < - 127 ) ? - 127 : pXSource - > data2 ;
2020-02-07 19:47:43 +00:00
if ( valueIsBetween ( pXSource - > data3 , - 1 , 32767 ) )
2021-08-29 11:28:13 +00:00
objActor - > s ( ) . pal = uint8_t ( pXSource - > data3 ) ;
2020-02-07 19:47:43 +00:00
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 ) )
2021-05-12 00:00:06 +00:00
wall [ objIndex ] . pal = uint8_t ( pXSource - > data3 ) ;
2020-02-07 19:47:43 +00:00
break ;
2020-05-22 16:28:03 +00:00
}
2020-02-07 19:47:43 +00:00
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-02-07 19:47:43 +00:00
2021-10-13 18:11:37 +00:00
QAV * playerQavSceneLoad ( int qavId )
{
2020-07-25 21:35:39 +00:00
QAV * pQav = getQAV ( qavId ) ;
if ( ! pQav ) viewSetSystemMessage ( " Failed to load QAV animation #%d " , qavId ) ;
2020-02-07 19:47:43 +00:00
return pQav ;
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void playerQavSceneProcess ( PLAYER * pPlayer , QAVSCENE * pQavScene )
2021-08-28 08:38:36 +00:00
{
auto initiator = pQavScene - > initiator ;
if ( initiator - > hasX ( ) )
{
XSPRITE * pXSprite = & initiator - > x ( ) ;
2021-10-13 18:11:37 +00:00
if ( pXSprite - > waitTime > 0 & & - - pXSprite - > sysData1 < = 0 )
{
if ( pXSprite - > txID > = kChannelUser )
{
2021-08-28 08:38:36 +00:00
for ( int i = bucketHead [ pXSprite - > txID ] ; i < bucketHead [ pXSprite - > txID + 1 ] ; i + + )
{
if ( rxBucket [ i ] . type = = OBJ_SPRITE )
{
2021-09-01 18:12:27 +00:00
auto rxactor = rxBucket [ i ] . GetActor ( ) ;
2021-08-28 08:38:36 +00:00
if ( ! rxactor | | ! rxactor - > hasX ( ) | | rxactor = = initiator ) continue ;
2020-05-22 16:28:03 +00:00
2021-08-28 08:38:36 +00:00
spritetype * pSpr = & rxactor - > s ( ) ;
auto pXSpr = & rxactor - > x ( ) ;
if ( pSpr - > type = = kModernPlayerControl & & pXSpr - > command = = 67 )
{
2020-05-22 16:28:03 +00:00
if ( pXSpr - > data2 = = pXSprite - > data2 | | pXSpr - > locked ) continue ;
2021-08-28 08:38:36 +00:00
else trPlayerCtrlStartScene ( rxactor , pPlayer , true ) ;
2020-05-22 16:28:03 +00:00
return ;
}
}
2021-10-13 23:18:26 +00:00
nnExtTriggerObject ( rxBucket [ i ] . type , rxBucket [ i ] . rxindex , rxBucket [ i ] . actor , pXSprite - > command ) ;
2020-05-22 16:28:03 +00:00
}
2021-10-13 18:11:37 +00:00
}
trPlayerCtrlStopScene ( pPlayer ) ;
}
else
{
2020-02-07 19:47:43 +00:00
playerQavScenePlay ( pPlayer ) ;
pPlayer - > weaponTimer = ClipLow ( pPlayer - > weaponTimer - = 4 , 0 ) ;
}
2021-10-13 18:11:37 +00:00
}
else
{
2020-05-05 18:50:14 +00:00
2021-08-28 08:38:36 +00:00
pQavScene - > initiator = nullptr ;
pPlayer - > sceneQav = - 1 ;
2020-02-07 19:47:43 +00:00
pQavScene - > qavResrc = NULL ;
}
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void playerQavSceneDraw ( PLAYER * pPlayer , int a2 , double a3 , double a4 , int a5 )
{
2020-02-07 19:47:43 +00:00
if ( pPlayer = = NULL | | pPlayer - > sceneQav = = - 1 ) return ;
QAVSCENE * pQavScene = & gPlayerCtrl [ pPlayer - > nPlayer ] . qavScene ;
2021-08-28 08:38:36 +00:00
spritetype * pSprite = & pQavScene - > initiator - > s ( ) ;
2020-02-07 19:47:43 +00:00
2021-10-13 18:11:37 +00:00
if ( pQavScene - > qavResrc ! = NULL )
{
2020-02-07 19:47:43 +00:00
QAV * pQAV = pQavScene - > qavResrc ;
2021-08-05 02:38:26 +00:00
int v4 ;
double smoothratio ;
qavProcessTimer ( pPlayer , pQAV , & v4 , & smoothratio ) ;
2020-02-07 19:47:43 +00:00
int flags = 2 ; int nInv = powerupCheck ( pPlayer , kPwUpShadowCloak ) ;
2021-10-13 18:11:37 +00:00
if ( nInv > = 120 * 8 | | ( nInv ! = 0 & & ( PlayClock & 32 ) ) )
{
2020-02-07 19:47:43 +00:00
a2 = - 128 ; flags | = 1 ;
}
// draw as weapon
2021-10-13 18:11:37 +00:00
if ( ! ( pSprite - > flags & kModernTypeFlag1 ) )
{
2020-08-02 22:25:40 +00:00
pQAV - > x = int ( a3 ) ; pQAV - > y = int ( a4 ) ;
2021-07-29 05:14:21 +00:00
pQAV - > Draw ( a3 , a4 , v4 , flags , a2 , a5 , true , smoothratio ) ;
2020-02-07 19:47:43 +00:00
// draw fullscreen (currently 4:3 only)
2021-10-13 18:11:37 +00:00
}
else
{
2020-08-14 19:08:28 +00:00
// What an awful hack. This throws proper ordering out of the window, but there is no way to reproduce this better with strict layering of elements.
// From the above commit it seems to be incomplete anyway...
2021-07-29 05:14:21 +00:00
pQAV - > Draw ( v4 , flags , a2 , a5 , false , smoothratio ) ;
2020-02-07 19:47:43 +00:00
}
}
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
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 ;
2021-08-28 08:38:36 +00:00
if ( pPlayer - > sceneQav = = - 1 & & pQavScene - > initiator ! = nullptr )
pPlayer - > sceneQav = pQavScene - > initiator - > x ( ) . data2 ;
2020-05-05 18:50:14 +00:00
2021-10-13 18:11:37 +00:00
if ( pQavScene - > qavResrc ! = NULL )
{
2020-02-07 19:47:43 +00:00
QAV * pQAV = pQavScene - > qavResrc ;
pQAV - > nSprite = pPlayer - > pSprite - > index ;
2020-11-21 14:45:37 +00:00
int nTicks = pQAV - > duration - pPlayer - > weaponTimer ;
2020-02-07 19:47:43 +00:00
pQAV - > Play ( nTicks - 4 , nTicks , pPlayer - > qavCallback , pPlayer ) ;
}
}
2021-10-13 18:11:37 +00:00
void playerQavSceneReset ( PLAYER * pPlayer )
{
2020-02-07 19:47:43 +00:00
QAVSCENE * pQavScene = & gPlayerCtrl [ pPlayer - > nPlayer ] . qavScene ;
2021-08-28 08:38:36 +00:00
pQavScene - > initiator = nullptr ;
pQavScene - > dummy = pPlayer - > sceneQav = - 1 ;
2020-02-07 19:47:43 +00:00
pQavScene - > qavResrc = NULL ;
}
2021-10-13 18:11:37 +00:00
bool playerSizeShrink ( PLAYER * pPlayer , int divider )
{
2020-02-07 19:47:43 +00:00
pPlayer - > pXSprite - > scale = 256 / divider ;
playerSetRace ( pPlayer , kModeHumanShrink ) ;
return true ;
}
2021-10-13 18:11:37 +00:00
bool playerSizeGrow ( PLAYER * pPlayer , int multiplier )
{
2020-02-07 19:47:43 +00:00
pPlayer - > pXSprite - > scale = 256 * multiplier ;
playerSetRace ( pPlayer , kModeHumanGrown ) ;
return true ;
}
2021-10-13 18:11:37 +00:00
bool playerSizeReset ( PLAYER * pPlayer )
{
2020-02-07 19:47:43 +00:00
playerSetRace ( pPlayer , kModeHuman ) ;
pPlayer - > pXSprite - > scale = 0 ;
return true ;
}
2021-10-13 18:11:37 +00:00
void playerDeactivateShrooms ( PLAYER * pPlayer )
{
2020-02-07 19:47:43 +00:00
powerupDeactivate ( pPlayer , kPwUpGrowShroom ) ;
pPlayer - > pwUpTime [ kPwUpGrowShroom ] = 0 ;
powerupDeactivate ( pPlayer , kPwUpShrinkShroom ) ;
pPlayer - > pwUpTime [ kPwUpShrinkShroom ] = 0 ;
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-02-07 19:47:43 +00:00
2021-10-13 18:11:37 +00:00
PLAYER * getPlayerById ( short id )
{
2020-02-07 19:47:43 +00:00
// relative to connected players
2021-10-13 18:11:37 +00:00
if ( id > = 1 & & id < = kMaxPlayers )
{
2020-02-07 19:47:43 +00:00
id = id - 1 ;
2021-10-13 18:11:37 +00:00
for ( int i = connecthead ; i > = 0 ; i = connectpoint2 [ i ] )
{
2020-02-07 19:47:43 +00:00
if ( id = = gPlayer [ i ] . nPlayer )
return & gPlayer [ i ] ;
}
// absolute sprite type
2021-10-13 18:11:37 +00:00
}
else if ( id > = kDudePlayer1 & & id < = kDudePlayer8 )
{
for ( int i = connecthead ; i > = 0 ; i = connectpoint2 [ i ] )
{
2020-02-07 19:47:43 +00:00
if ( id = = gPlayer [ i ] . pSprite - > type )
return & gPlayer [ i ] ;
}
}
//viewSetSystemMessage("There is no player id #%d", id);
return NULL ;
}
2021-10-13 18:11:37 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-08-29 11:44:04 +00:00
bool IsBurningDude ( DBloodActor * actor )
{
if ( actor = = NULL ) return false ;
switch ( actor - > s ( ) . type )
{
2020-02-07 19:47:43 +00:00
case kDudeBurningInnocent :
case kDudeBurningCultist :
case kDudeBurningZombieAxe :
case kDudeBurningZombieButcher :
case kDudeBurningTinyCaleb :
case kDudeBurningBeast :
case kDudeModernCustomBurning :
return true ;
}
return false ;
}
2021-08-29 11:44:04 +00:00
bool IsKillableDude ( DBloodActor * actor )
{
switch ( actor - > s ( ) . type )
{
2020-02-07 19:47:43 +00:00
case kDudeGargoyleStatueFlesh :
case kDudeGargoyleStatueStone :
return false ;
default :
2021-08-29 11:44:04 +00:00
if ( ! actor - > IsDudeActor ( ) | | actor - > x ( ) . locked = = 1 ) return false ;
2020-02-07 19:47:43 +00:00
return true ;
}
}
2021-08-29 11:44:04 +00:00
bool isGrown ( DBloodActor * actor )
{
if ( powerupCheck ( & gPlayer [ actor - > s ( ) . type - kDudePlayer1 ] , kPwUpGrowShroom ) > 0 ) return true ;
else if ( actor - > hasX ( ) & & actor - > x ( ) . scale > = 512 ) return true ;
2020-02-07 19:47:43 +00:00
else return false ;
}
2021-08-29 11:44:04 +00:00
bool isShrinked ( DBloodActor * actor )
{
if ( powerupCheck ( & gPlayer [ actor - > s ( ) . type - kDudePlayer1 ] , kPwUpShrinkShroom ) > 0 ) return true ;
else if ( actor - > hasX ( ) & & actor - > x ( ) . scale > 0 & & actor - > x ( ) . scale < = 128 ) return true ;
2020-02-07 19:47:43 +00:00
else return false ;
}
2021-10-13 17:45:48 +00:00
bool isActive ( DBloodActor * actor )
{
if ( ! actor - > hasX ( ) )
2020-02-07 19:47:43 +00:00
return false ;
2021-10-13 17:45:48 +00:00
switch ( actor - > x ( ) . aiState - > stateType )
{
2020-02-07 19:47:43 +00:00
case kAiStateIdle :
case kAiStateGenIdle :
case kAiStateSearch :
case kAiStateMove :
case kAiStateOther :
return false ;
default :
return true ;
}
}
2021-10-13 18:26:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int getDataFieldOfObject ( int objType , int objIndex , DBloodActor * actor , int dataIndex )
2021-08-30 19:43:21 +00:00
{
2020-02-07 19:47:43 +00:00
int data = - 65535 ;
2021-08-30 19:43:21 +00:00
switch ( objType )
{
2020-03-01 20:36:28 +00:00
case OBJ_SPRITE :
2021-08-30 19:43:21 +00:00
switch ( dataIndex )
{
case 1 : return actor - > x ( ) . data1 ;
case 2 : return actor - > x ( ) . data2 ;
2020-03-01 20:36:28 +00:00
case 3 :
2021-08-30 19:43:21 +00:00
switch ( actor - > s ( ) . type )
{
case kDudeModernCustom : return actor - > x ( ) . sysData1 ;
default : return actor - > x ( ) . data3 ;
2020-03-01 20:36:28 +00:00
}
2021-08-30 19:43:21 +00:00
case 4 : return actor - > x ( ) . data4 ;
2020-03-01 20:36:28 +00:00
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
}
}
2021-10-13 18:26:52 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-10-03 15:47:59 +00:00
bool setDataValueOfObject ( int objType , int objIndex , DBloodActor * objActor , int dataIndex , int value )
{
2021-10-03 15:42:40 +00:00
switch ( objType )
{
case OBJ_SPRITE :
{
2021-10-03 15:47:59 +00:00
XSPRITE * pXSprite = & objActor - > x ( ) ;
int type = objActor - > s ( ) . type ;
2020-02-07 19:47:43 +00:00
// exceptions
2021-10-03 15:47:59 +00:00
if ( objActor - > IsDudeActor ( ) & & pXSprite - > health < = 0 ) return true ;
switch ( type )
2021-10-03 15:42:40 +00:00
{
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
2021-10-03 15:42:40 +00:00
switch ( dataIndex )
{
2020-02-07 19:47:43 +00:00
case 1 :
2021-10-03 15:47:59 +00:00
pXSprite - > data1 = value ;
switch ( type )
{
2020-03-01 20:36:28 +00:00
case kSwitchCombo :
2021-10-03 15:47:59 +00:00
if ( value = = pXSprite - > data2 ) SetSpriteState ( objActor , 1 ) ;
else SetSpriteState ( objActor , 0 ) ;
2020-03-01 20:36:28 +00:00
break ;
case kDudeModernCustom :
case kDudeModernCustomBurning :
2021-08-29 07:27:03 +00:00
objActor - > genDudeExtra . updReq [ kGenDudePropertyWeapon ] = true ;
objActor - > genDudeExtra . updReq [ kGenDudePropertyDmgScale ] = true ;
2021-10-03 15:47:59 +00:00
evPostActor ( objActor , kGenDudeUpdTimeRate , kCallbackGenDudeUpdate ) ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
return true ;
case 2 :
2021-10-03 15:47:59 +00:00
pXSprite - > data2 = value ;
switch ( type )
{
2020-03-01 20:36:28 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
2021-08-29 07:27:03 +00:00
objActor - > genDudeExtra . updReq [ kGenDudePropertySpriteSize ] = true ;
objActor - > genDudeExtra . updReq [ kGenDudePropertyMass ] = true ;
objActor - > genDudeExtra . updReq [ kGenDudePropertyDmgScale ] = true ;
objActor - > genDudeExtra . updReq [ kGenDudePropertyStates ] = true ;
objActor - > genDudeExtra . updReq [ kGenDudePropertyAttack ] = true ;
2021-10-03 15:47:59 +00:00
evPostActor ( objActor , kGenDudeUpdTimeRate , kCallbackGenDudeUpdate ) ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
return true ;
case 3 :
2021-10-03 15:47:59 +00:00
pXSprite - > data3 = value ;
switch ( type )
{
2020-03-01 20:36:28 +00:00
case kDudeModernCustom :
case kDudeModernCustomBurning :
2021-10-03 15:47:59 +00:00
pXSprite - > sysData1 = value ;
2020-03-01 20:36:28 +00:00
break ;
2020-02-07 19:47:43 +00:00
}
return true ;
case 4 :
2021-10-03 15:47:59 +00:00
pXSprite - > data4 = value ;
2020-02-07 19:47:43 +00:00
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 ;
}
}
2021-10-13 18:26:52 +00:00
//---------------------------------------------------------------------------
//
2021-07-19 21:15:26 +00:00
// a replacement of vanilla CanMove for patrol dudes
2021-10-13 18:26:52 +00:00
//
//---------------------------------------------------------------------------
2021-08-29 11:59:06 +00:00
bool nnExtCanMove ( DBloodActor * actor , DBloodActor * target , int nAngle , int nRange )
{
auto pSprite = & actor - > s ( ) ;
2021-07-19 21:15:26 +00:00
int x = pSprite - > x , y = pSprite - > y , z = pSprite - > z , nSector = pSprite - > sectnum ;
HitScan ( pSprite , z , Cos ( nAngle ) > > 16 , Sin ( nAngle ) > > 16 , 0 , CLIPMASK0 , nRange ) ;
int nDist = approxDist ( x - gHitInfo . hitx , y - gHitInfo . hity ) ;
2021-08-29 11:59:06 +00:00
if ( target ! = nullptr & & nDist - ( pSprite - > clipdist < < 2 ) < nRange )
return ( target = = gHitInfo . hitactor ) ;
2021-07-19 21:15:26 +00:00
x + = MulScale ( nRange , Cos ( nAngle ) , 30 ) ;
y + = MulScale ( nRange , Sin ( nAngle ) , 30 ) ;
if ( ! FindSector ( x , y , z , & nSector ) )
return false ;
if ( sector [ nSector ] . extra > 0 ) {
XSECTOR * pXSector = & xsector [ sector [ nSector ] . extra ] ;
2021-08-27 12:01:51 +00:00
return ! ( ( sector [ nSector ] . type = = kSectorDamage | | pXSector - > damageType > 0 ) & & pXSector - > state & & ! nnExtIsImmune ( actor , pXSector - > damageType , 16 ) ) ;
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
return true ;
2020-12-06 20:56:09 +00:00
}
2021-10-13 18:26:52 +00:00
//---------------------------------------------------------------------------
//
2021-07-19 21:15:26 +00:00
// a replacement of vanilla aiChooseDirection for patrol dudes
2021-10-13 18:26:52 +00:00
//
//---------------------------------------------------------------------------
2021-08-29 11:59:06 +00:00
void nnExtAiSetDirection ( DBloodActor * actor , int a3 )
{
spritetype * pSprite = & actor - > s ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
2021-07-19 21:15:26 +00:00
assert ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeMax ) ;
int nSprite = pSprite - > index ;
int vc = ( ( a3 + 1024 - pSprite - > ang ) & 2047 ) - 1024 ;
2021-08-29 11:59:06 +00:00
int t1 = DMulScale ( actor - > xvel ( ) , Cos ( pSprite - > ang ) , actor - > yvel ( ) , Sin ( pSprite - > ang ) , 30 ) ;
2021-07-19 21:15:26 +00:00
int vsi = ( ( t1 * 15 ) > > 12 ) / 2 ;
int v8 = 341 ;
if ( vc < 0 )
v8 = - 341 ;
2021-08-29 11:59:06 +00:00
if ( nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang + vc , vsi ) )
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = pSprite - > ang + vc ;
2021-08-29 11:59:06 +00:00
else if ( nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang + vc / 2 , vsi ) )
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = pSprite - > ang + vc / 2 ;
2021-08-29 11:59:06 +00:00
else if ( nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang - vc / 2 , vsi ) )
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = pSprite - > ang - vc / 2 ;
2021-08-29 11:59:06 +00:00
else if ( nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang + v8 , vsi ) )
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = pSprite - > ang + v8 ;
2021-08-29 11:59:06 +00:00
else if ( nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang , vsi ) )
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = pSprite - > ang ;
2021-08-29 11:59:06 +00:00
else if ( nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang - v8 , vsi ) )
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = pSprite - > ang - v8 ;
else
pXSprite - > goalAng = pSprite - > ang + 341 ;
2021-08-29 11:59:06 +00:00
if ( pXSprite - > dodgeDir )
{
if ( ! nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang + pXSprite - > dodgeDir * 512 , 512 ) )
2021-07-19 21:15:26 +00:00
{
pXSprite - > dodgeDir = - pXSprite - > dodgeDir ;
2021-08-29 11:59:06 +00:00
if ( ! nnExtCanMove ( actor , actor - > GetTarget ( ) , pSprite - > ang + pXSprite - > dodgeDir * 512 , 512 ) )
2021-07-19 21:15:26 +00:00
pXSprite - > dodgeDir = 0 ;
}
}
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
2021-08-29 12:36:40 +00:00
//---------------------------------------------------------------------------
//
2021-07-19 21:15:26 +00:00
/// patrol functions
2021-08-29 12:36:40 +00:00
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-08-29 12:36:40 +00:00
void aiPatrolState ( DBloodActor * actor , int state )
{
spritetype * pSprite = & actor - > s ( ) ;
assert ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeMax & & actor - > hasX ( ) ) ;
assert ( actor - > GetTarget ( ) ) ;
2021-07-19 21:15:26 +00:00
2021-08-29 12:36:40 +00:00
XSPRITE * pXSprite = & actor - > x ( ) ;
2021-07-19 21:15:26 +00:00
2021-08-29 12:36:40 +00:00
auto markeractor = actor - > GetTarget ( ) ;
spritetype * pMarker = & markeractor - > s ( ) ;
XSPRITE * pXMarker = & markeractor - > x ( ) ;
2021-07-19 21:15:26 +00:00
assert ( pMarker - > type = = kMarkerPath ) ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
bool nSeqOverride = false , crouch = false ;
int i , seq = - 1 , start = 0 , end = kPatrolStateSize ;
2021-08-27 11:46:07 +00:00
const DUDEINFO_EXTRA * pExtra = & gDudeInfoExtra [ pSprite - > type - kDudeBase ] ;
2021-07-19 21:15:26 +00:00
2020-12-06 20:56:09 +00:00
switch ( state ) {
case kAiStatePatrolWaitL :
seq = pExtra - > idlgseqofs ;
start = 0 ; end = 2 ;
break ;
case kAiStatePatrolMoveL :
seq = pExtra - > mvegseqofs ;
2021-07-19 21:15:26 +00:00
start = 2 , end = 7 ;
break ;
case kAiStatePatrolTurnL :
seq = pExtra - > mvegseqofs ;
start = 7 , end = 12 ;
2020-12-06 20:56:09 +00:00
break ;
case kAiStatePatrolWaitW :
seq = pExtra - > idlwseqofs ;
2021-07-19 21:15:26 +00:00
start = 12 ; end = 18 ;
2020-12-06 20:56:09 +00:00
break ;
case kAiStatePatrolMoveW :
seq = pExtra - > mvewseqofs ;
2021-07-19 21:15:26 +00:00
start = 18 ; end = 25 ;
break ;
case kAiStatePatrolTurnW :
seq = pExtra - > mvewseqofs ;
start = 25 ; end = 32 ;
2020-12-06 20:56:09 +00:00
break ;
case kAiStatePatrolWaitC :
seq = pExtra - > idlcseqofs ;
2021-07-19 21:15:26 +00:00
start = 32 ; end = 36 ;
2020-12-06 20:56:09 +00:00
crouch = true ;
break ;
case kAiStatePatrolMoveC :
seq = pExtra - > mvecseqofs ;
2021-07-19 21:15:26 +00:00
start = 36 ; end = 39 ;
crouch = true ;
break ;
case kAiStatePatrolTurnC :
seq = pExtra - > mvecseqofs ;
start = 39 ; end = kPatrolStateSize ;
2020-12-06 20:56:09 +00:00
crouch = true ;
break ;
}
2021-07-19 21:15:26 +00:00
if ( pXMarker - > data4 > 0 ) seq = pXMarker - > data4 , nSeqOverride = true ;
else if ( ! nSeqOverride & & state = = kAiStatePatrolWaitC & & ( pSprite - > type = = kDudeCultistTesla | | pSprite - > type = = kDudeCultistTNT ) )
seq = 11537 , nSeqOverride = true ; // these don't have idle crouch seq for some reason...
2020-12-06 20:56:09 +00:00
if ( seq < 0 )
2021-09-05 17:21:18 +00:00
return aiPatrolStop ( actor , nullptr ) ;
2020-12-06 20:56:09 +00:00
2021-08-29 12:36:40 +00:00
for ( i = start ; i < end ; i + + )
{
2021-07-19 21:15:26 +00:00
AISTATE * newState = & genPatrolStates [ i ] ;
if ( newState - > stateType ! = state | | ( ! nSeqOverride & & seq ! = newState - > seqId ) )
continue ;
2021-08-31 19:52:26 +00:00
if ( pSprite - > type = = kDudeModernCustom ) aiGenDudeNewState ( actor , newState ) ;
2021-08-29 12:36:40 +00:00
else aiNewState ( actor , newState ) ;
2020-12-06 20:56:09 +00:00
2021-01-07 12:33:20 +00:00
if ( crouch ) pXSprite - > unused1 | = kDudeFlagCrouch ;
else pXSprite - > unused1 & = ~ kDudeFlagCrouch ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
if ( nSeqOverride )
2021-08-29 12:36:40 +00:00
seqSpawn ( seq , actor ) ;
2020-12-06 20:56:09 +00:00
return ;
}
2021-08-29 12:36:40 +00:00
if ( i = = end )
{
viewSetSystemMessage ( " No patrol state #%d found for dude #%d (type = %d) " , state , actor - > GetIndex ( ) , pSprite - > type ) ;
2021-09-05 17:21:18 +00:00
aiPatrolStop ( actor , nullptr ) ;
2020-12-06 20:56:09 +00:00
}
}
2021-08-29 12:36:40 +00:00
//---------------------------------------------------------------------------
//
2020-12-06 20:56:09 +00:00
// check if some dude already follows the given marker
2021-08-29 12:36:40 +00:00
//
//---------------------------------------------------------------------------
DBloodActor * aiPatrolMarkerBusy ( DBloodActor * except , DBloodActor * marker )
{
BloodStatIterator it ( kStatDude ) ;
while ( auto actor = it . Next ( ) )
{
if ( ! actor - > IsDudeActor ( ) | | actor = = except | | ! actor - > hasX ( ) )
2020-12-06 20:56:09 +00:00
continue ;
2021-08-29 12:36:40 +00:00
auto targ = actor - > GetTarget ( ) ;
if ( actor - > x ( ) . health > 0 & & targ ! = nullptr & & targ - > s ( ) . type = = kMarkerPath & & targ = = marker )
return actor ;
2020-12-06 20:56:09 +00:00
}
2021-08-29 12:36:40 +00:00
return nullptr ;
2020-12-06 20:56:09 +00:00
}
2021-08-29 12:36:40 +00:00
//---------------------------------------------------------------------------
//
// check if some dude already follows the given marker
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-08-29 12:36:40 +00:00
bool aiPatrolMarkerReached ( DBloodActor * actor )
{
spritetype * pSprite = & actor - > s ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
2021-07-19 21:15:26 +00:00
assert ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeMax ) ;
2021-08-27 11:46:07 +00:00
const DUDEINFO_EXTRA * pExtra = & gDudeInfoExtra [ pSprite - > type - kDudeBase ] ;
2021-08-29 12:36:40 +00:00
auto markeractor = actor - > GetTarget ( ) ;
if ( markeractor & & markeractor - > s ( ) . type = = kMarkerPath )
{
spritetype * pMarker = & markeractor - > s ( ) ;
int okDist = ClipLow ( pMarker - > clipdist < < 1 , 4 ) ;
2021-07-19 21:15:26 +00:00
int oX = abs ( pMarker - > x - pSprite - > x ) > > 4 ;
int oY = abs ( pMarker - > y - pSprite - > y ) > > 4 ;
2021-08-29 12:36:40 +00:00
if ( approxDist ( oX , oY ) < = okDist )
{
if ( spriteIsUnderwater ( actor ) | | pExtra - > flying )
{
2021-07-19 21:15:26 +00:00
okDist = pMarker - > clipdist < < 4 ;
int ztop , zbot , ztop2 , zbot2 ;
2021-08-29 12:36:40 +00:00
GetActorExtents ( actor , & ztop , & zbot ) ;
GetActorExtents ( markeractor , & ztop2 , & zbot2 ) ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
int oZ1 = abs ( zbot - ztop2 ) > > 6 ;
int oZ2 = abs ( ztop - zbot2 ) > > 6 ;
if ( oZ1 > okDist & & oZ2 > okDist )
return false ;
}
return true ;
2020-12-06 20:56:09 +00:00
}
}
return false ;
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-05 17:25:08 +00:00
DBloodActor * findNextMarker ( DBloodActor * mark , bool back )
{
auto pXMark = & mark - > x ( ) ;
2020-12-06 20:56:09 +00:00
2021-09-05 17:25:08 +00:00
BloodStatIterator it ( kStatPathMarker ) ;
while ( auto next = it . Next ( ) )
{
if ( ! next - > hasX ( ) | | next = = mark ) continue ;
XSPRITE * pXNext = & next - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( ( pXNext - > locked | | pXNext - > isTriggered | | pXNext - > DudeLockout ) | | ( back & & pXNext - > data2 ! = pXMark - > data1 ) | | ( ! back & & pXNext - > data1 ! = pXMark - > data2 ) )
continue ;
2020-12-06 20:56:09 +00:00
2021-09-05 17:25:08 +00:00
return next ;
2021-07-19 21:15:26 +00:00
}
2021-09-05 17:25:08 +00:00
return nullptr ;
2021-07-19 21:15:26 +00:00
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-05 17:25:08 +00:00
bool markerIsNode ( DBloodActor * mark , bool back )
{
auto pXMark = & mark - > x ( ) ;
int cnt = 0 ;
BloodStatIterator it ( kStatPathMarker ) ;
while ( auto next = it . Next ( ) )
{
if ( ! next - > hasX ( ) | | next = = mark ) continue ;
2021-07-19 21:15:26 +00:00
2021-09-05 17:25:08 +00:00
XSPRITE * pXNext = & next - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( ( pXNext - > locked | | pXNext - > isTriggered | | pXNext - > DudeLockout ) | | ( back & & pXNext - > data2 ! = pXMark - > data1 ) | | ( ! back & & pXNext - > data1 ! = pXMark - > data2 ) )
continue ;
if ( + + cnt > 1 )
return true ;
}
return false ;
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-08-29 13:57:42 +00:00
void aiPatrolSetMarker ( DBloodActor * actor )
2021-08-29 13:00:32 +00:00
{
2021-08-29 13:57:42 +00:00
auto pSprite = & actor - > s ( ) ;
auto pXSprite = & actor - > x ( ) ;
2021-08-29 13:00:32 +00:00
auto targetactor = actor - > GetTarget ( ) ;
2021-07-19 21:15:26 +00:00
2021-08-29 13:57:42 +00:00
DBloodActor * selected = nullptr ;
int closest = 200000 ;
2021-07-19 21:15:26 +00:00
// select closest marker that dude can see
2021-08-29 13:57:42 +00:00
if ( targetactor = = nullptr )
{
2021-08-29 13:00:32 +00:00
int zt1 , zb1 , zt2 , zb2 , dist ;
GetActorExtents ( actor , & zt2 , & zb2 ) ;
2021-07-19 21:15:26 +00:00
2021-08-29 13:57:42 +00:00
BloodStatIterator it ( kStatPathMarker ) ;
while ( auto nextactor = it . Next ( ) )
{
if ( ! nextactor - > hasX ( ) ) continue ;
auto pNext = & nextactor - > s ( ) ;
auto pXNext = & nextactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXNext - > locked | | pXNext - > isTriggered | | pXNext - > DudeLockout | | ( dist = approxDist ( pNext - > x - pSprite - > x , pNext - > y - pSprite - > y ) ) > closest )
continue ;
2020-12-06 20:56:09 +00:00
2021-08-29 13:57:42 +00:00
GetActorExtents ( nextactor , & zt1 , & zb1 ) ;
2021-08-29 13:00:32 +00:00
if ( cansee ( pNext - > x , pNext - > y , zt1 , pNext - > sectnum , pSprite - > x , pSprite - > y , zt2 , pSprite - > sectnum ) )
{
2021-07-19 21:15:26 +00:00
closest = dist ;
2021-08-29 13:57:42 +00:00
selected = nextactor ;
2020-12-06 20:56:09 +00:00
}
}
2021-08-29 13:00:32 +00:00
}
2020-12-06 20:56:09 +00:00
// set next marker
2021-08-29 13:00:32 +00:00
else if ( targetactor - > s ( ) . type = = kMarkerPath & & targetactor - > hasX ( ) )
{
2021-07-19 21:15:26 +00:00
// idea: which one of next (allowed) markers are closer to the potential target?
// idea: -3 select random next marker that dude can see in radius of reached marker
// if reached marker is in radius of another marker with -3, but greater radius, use that marker
// idea: for nodes only flag32 = specify if enemy must return back to node or allowed to select
// another marker which belongs that node?
2021-08-29 13:57:42 +00:00
spritetype * pPrev = NULL ; XSPRITE * pXPrev = NULL ;
DBloodActor * prevactor = nullptr ;
DBloodActor * firstFinePath = nullptr ;
int next ;
2021-07-19 21:15:26 +00:00
int breakChance = 0 ;
2021-08-29 13:00:32 +00:00
auto pCur = & targetactor - > s ( ) ;
auto pXCur = & targetactor - > x ( ) ;
2021-08-29 13:57:42 +00:00
if ( actor - > prevmarker )
2021-07-26 05:31:36 +00:00
{
2021-08-29 13:57:42 +00:00
prevactor = actor - > prevmarker ;
pPrev = & prevactor - > s ( ) ;
pXPrev = & prevactor - > x ( ) ;
2021-07-26 05:31:36 +00:00
}
2021-07-19 21:15:26 +00:00
2021-08-29 13:00:32 +00:00
bool node = markerIsNode ( targetactor , false ) ;
2021-09-04 08:53:56 +00:00
pXSprite - > unused2 = aiPatrolGetPathDir ( actor , targetactor ) ; // decide if it should go back or forward
2021-07-19 21:15:26 +00:00
if ( pXSprite - > unused2 = = kPatrolMoveBackward & & Chance ( 0x8000 ) & & node )
pXSprite - > unused2 = kPatrolMoveForward ;
2021-08-29 13:00:32 +00:00
bool back = ( pXSprite - > unused2 = = kPatrolMoveBackward ) ; next = ( back ) ? pXCur - > data1 : pXCur - > data2 ;
2021-08-29 13:57:42 +00:00
BloodStatIterator it ( kStatPathMarker ) ;
while ( auto nextactor = it . Next ( ) )
{
if ( nextactor = = targetactor | | ! nextactor - > hasX ( ) ) continue ;
else if ( pXSprite - > targetX > = 0 & & nextactor = = prevactor & & node )
{
2021-07-19 21:15:26 +00:00
if ( pXCur - > data2 = = pXPrev - > data1 )
continue ;
}
2021-08-29 13:57:42 +00:00
auto pXNext = & nextactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( ( pXNext - > locked | | pXNext - > isTriggered | | pXNext - > DudeLockout ) | | ( back & & pXNext - > data2 ! = next ) | | ( ! back & & pXNext - > data1 ! = next ) )
continue ;
2021-08-29 13:57:42 +00:00
if ( firstFinePath = = nullptr ) firstFinePath = nextactor ;
if ( aiPatrolMarkerBusy ( actor , nextactor ) & & ! Chance ( 0x0010 ) ) continue ;
else selected = nextactor ;
2021-07-19 21:15:26 +00:00
breakChance + = nnExtRandom ( 1 , 5 ) ;
if ( breakChance > = 5 )
break ;
2020-12-06 20:56:09 +00:00
}
2021-08-29 13:57:42 +00:00
if ( firstFinePath = = nullptr )
{
2021-07-19 21:15:26 +00:00
viewSetSystemMessage ( " No markers with id #%d found for dude #%d! (back = %d) " , next , pSprite - > index , back ) ;
2020-12-06 20:56:09 +00:00
return ;
}
2021-07-19 21:15:26 +00:00
2021-08-29 13:57:42 +00:00
if ( selected = = nullptr )
selected = firstFinePath ;
2020-12-06 20:56:09 +00:00
}
2021-08-29 13:57:42 +00:00
if ( ! selected )
2020-12-06 20:56:09 +00:00
return ;
2021-08-29 13:57:42 +00:00
actor - > SetTarget ( selected ) ;
selected - > SetOwner ( actor ) ;
actor - > prevmarker = targetactor ; // keep previous marker index here, use actual sprite coords when selecting direction
2020-12-06 20:56:09 +00:00
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-05 17:21:18 +00:00
void aiPatrolStop ( DBloodActor * actor , DBloodActor * targetactor , bool alarm )
2021-05-05 07:47:08 +00:00
{
2021-09-05 17:21:18 +00:00
auto pSprite = & actor - > s ( ) ;
2021-09-05 17:18:29 +00:00
if ( actor - > hasX ( ) )
2021-05-05 07:47:08 +00:00
{
2021-09-05 17:18:29 +00:00
XSPRITE * pXSprite = & actor - > x ( ) ;
2020-12-06 20:56:09 +00:00
pXSprite - > data3 = 0 ; // reset spot progress
2021-07-19 21:15:26 +00:00
pXSprite - > unused1 & = ~ kDudeFlagCrouch ; // reset the crouch status
pXSprite - > unused2 = kPatrolMoveForward ; // reset path direction
2021-08-29 13:57:42 +00:00
actor - > prevmarker = nullptr ;
2021-07-19 21:15:26 +00:00
pXSprite - > targetX = - 1 ; // reset the previous marker index
2020-12-06 20:56:09 +00:00
if ( pXSprite - > health < = 0 )
return ;
2021-09-05 17:18:29 +00:00
auto mytarget = actor - > GetTarget ( ) ;
if ( mytarget & & mytarget - > s ( ) . type = = kMarkerPath )
{
if ( targetactor = = nullptr ) pSprite - > ang = mytarget - > s ( ) . ang & 2047 ;
2021-05-05 07:47:08 +00:00
actor - > SetTarget ( nullptr ) ;
2020-12-06 20:56:09 +00:00
}
2021-09-05 17:18:29 +00:00
bool patrol = pXSprite - > dudeFlag4 ;
pXSprite - > dudeFlag4 = 0 ;
if ( targetactor & & targetactor - > hasX ( ) & & targetactor - > IsDudeActor ( ) )
{
aiSetTarget ( actor , targetactor ) ;
aiActivateDude ( actor ) ;
2021-07-19 21:15:26 +00:00
// alarm only when in non-recoil state?
//if (((pXSprite->unused1 & kDudeFlagStealth) && stype != kAiStateRecoil) || !(pXSprite->unused1 & kDudeFlagStealth)) {
2021-08-29 15:07:09 +00:00
if ( alarm ) aiPatrolAlarmFull ( actor , targetactor , Chance ( 0x0100 ) ) ;
else aiPatrolAlarmLite ( actor , targetactor ) ;
2021-07-19 21:15:26 +00:00
//}
2020-12-06 20:56:09 +00:00
2021-09-05 17:10:50 +00:00
}
else
{
2021-05-05 10:55:52 +00:00
aiInitSprite ( actor ) ;
2021-09-05 17:18:29 +00:00
aiSetTarget ( actor , pXSprite - > targetX , pXSprite - > targetY , pXSprite - > targetZ ) ;
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
pXSprite - > dudeFlag4 = patrol ; // this must be kept so enemy can patrol after respawn again
2020-12-06 20:56:09 +00:00
}
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-09-05 17:10:50 +00:00
void aiPatrolRandGoalAng ( DBloodActor * actor )
{
2021-07-19 21:15:26 +00:00
int goal = kAng90 ;
if ( Chance ( 0x4000 ) )
goal = kAng120 ;
if ( Chance ( 0x4000 ) )
goal = kAng180 ;
if ( Chance ( 0x8000 ) )
goal = - goal ;
2021-09-05 17:14:31 +00:00
actor - > x ( ) . goalAng = ( actor - > s ( ) . ang + goal ) & 2047 ;
2021-07-19 21:15:26 +00:00
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-09-05 17:10:50 +00:00
void aiPatrolTurn ( DBloodActor * actor )
{
2021-07-19 21:15:26 +00:00
auto pXSprite = & actor - > x ( ) ;
auto pSprite = & actor - > s ( ) ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
int nTurnRange = ( getDudeInfo ( pSprite - > type ) - > angSpeed < < 1 ) > > 4 ;
2020-12-06 20:56:09 +00:00
int nAng = ( ( pXSprite - > goalAng + 1024 - pSprite - > ang ) & 2047 ) - 1024 ;
pSprite - > ang = ( pSprite - > ang + ClipRange ( nAng , - nTurnRange , nTurnRange ) ) & 2047 ;
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-05-03 22:03:01 +00:00
2021-09-05 17:10:50 +00:00
void aiPatrolMove ( DBloodActor * actor )
{
2021-05-03 22:03:01 +00:00
auto pXSprite = & actor - > x ( ) ;
auto pSprite = & actor - > s ( ) ;
2021-09-05 17:14:31 +00:00
auto targetactor = actor - > GetTarget ( ) ;
2020-12-06 20:56:09 +00:00
2021-09-05 17:14:31 +00:00
if ( ! ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeMax ) | | ! targetactor )
2020-12-06 20:56:09 +00:00
return ;
2021-07-19 21:15:26 +00:00
int dudeIdx = pSprite - > type - kDudeBase ;
2021-09-05 17:10:50 +00:00
switch ( pSprite - > type )
{
2020-12-06 20:56:09 +00:00
case kDudeCultistShotgunProne : dudeIdx = kDudeCultistShotgun - kDudeBase ; break ;
case kDudeCultistTommyProne : dudeIdx = kDudeCultistTommy - kDudeBase ; break ;
}
2021-09-05 17:14:31 +00:00
spritetype * pTarget = & targetactor - > s ( ) ;
XSPRITE * pXTarget = & targetactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
DUDEINFO * pDudeInfo = & dudeInfo [ dudeIdx ] ;
2021-08-27 11:46:07 +00:00
const DUDEINFO_EXTRA * pExtra = & gDudeInfoExtra [ dudeIdx ] ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
int dx = ( pTarget - > x - pSprite - > x ) ;
int dy = ( pTarget - > y - pSprite - > y ) ;
int dz = ( pTarget - > z - ( pSprite - > z - pDudeInfo - > eyeHeight ) ) * 6 ;
int vel = ( pXSprite - > unused1 & kDudeFlagCrouch ) ? kMaxPatrolCrouchVelocity : kMaxPatrolVelocity ;
int goalAng = 341 ;
2021-09-05 17:10:50 +00:00
if ( pExtra - > flying | | spriteIsUnderwater ( actor ) )
{
2021-07-19 21:15:26 +00:00
goalAng > > = 1 ;
zvel [ pSprite - > index ] = dz ;
if ( pSprite - > flags & kPhysGravity )
pSprite - > flags & = ~ kPhysGravity ;
2021-09-05 17:10:50 +00:00
}
else if ( ! pExtra - > flying )
{
2020-12-06 20:56:09 +00:00
pSprite - > flags | = kPhysGravity | kPhysFalling ;
}
2021-07-19 21:15:26 +00:00
int nTurnRange = ( pDudeInfo - > angSpeed < < 2 ) > > 4 ;
int nAng = ( ( pXSprite - > goalAng + 1024 - pSprite - > ang ) & 2047 ) - 1024 ;
pSprite - > ang = ( pSprite - > ang + ClipRange ( nAng , - nTurnRange , nTurnRange ) ) & 2047 ;
2021-08-29 12:36:40 +00:00
if ( abs ( nAng ) > goalAng | | ( ( pXTarget - > waitTime > 0 | | pXTarget - > data1 = = pXTarget - > data2 ) & & aiPatrolMarkerReached ( actor ) ) )
{
2021-09-02 18:23:51 +00:00
actor - > xvel ( ) = 0 ;
actor - > yvel ( ) = 0 ;
2021-07-19 21:15:26 +00:00
return ;
}
2021-09-05 17:14:31 +00:00
if ( actor - > hit ( ) . hit . type = = kHitSprite )
2021-09-02 18:23:51 +00:00
{
2021-09-05 17:14:31 +00:00
auto hitactor = actor - > hit ( ) . hit . actor ;
2021-09-02 18:23:51 +00:00
hitactor - > x ( ) . dodgeDir = - 1 ;
2021-07-19 21:15:26 +00:00
pXSprite - > dodgeDir = 1 ;
2021-09-02 18:23:51 +00:00
aiMoveDodge ( hitactor ) ;
2021-09-05 17:10:50 +00:00
}
else
{
2021-07-19 21:15:26 +00:00
int frontSpeed = aiPatrolGetVelocity ( pDudeInfo - > frontSpeed , pXTarget - > busyTime ) ;
2021-09-05 17:14:31 +00:00
actor - > xvel ( ) + = MulScale ( frontSpeed , Cos ( pSprite - > ang ) , 30 ) ;
actor - > yvel ( ) + = MulScale ( frontSpeed , Sin ( pSprite - > ang ) , 30 ) ;
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
vel = MulScale ( vel , approxDist ( dx , dy ) < < 6 , 16 ) ;
2021-09-05 17:14:31 +00:00
actor - > xvel ( ) = ClipRange ( actor - > xvel ( ) , - vel , vel ) ;
actor - > yvel ( ) = ClipRange ( actor - > yvel ( ) , - vel , vel ) ;
2020-12-06 20:56:09 +00:00
return ;
}
2021-09-05 17:10:50 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-08-29 15:07:09 +00:00
void aiPatrolAlarmLite ( DBloodActor * actor , DBloodActor * targetactor )
{
if ( ! actor - > hasX ( ) | | ! actor - > IsDudeActor ( ) )
2021-07-19 21:15:26 +00:00
return ;
2021-08-29 15:07:09 +00:00
spritetype * pSprite = & actor - > s ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
spritetype * pTarget = & targetactor - > s ( ) ;
XSPRITE * pXTarget = & targetactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXSprite - > health < = 0 )
return ;
int zt1 , zb1 , zt2 , zb2 ; //int eaz1 = (getDudeInfo(pSprite->type)->eyeHeight * pSprite->yrepeat) << 2;
2021-08-29 15:07:09 +00:00
GetActorExtents ( actor , & zt1 , & zb1 ) ;
GetActorExtents ( targetactor , & zt2 , & zb2 ) ;
2021-07-19 21:15:26 +00:00
2021-08-29 15:07:09 +00:00
BloodStatIterator it ( kStatDude ) ;
while ( auto dudeactor = it . Next ( ) )
{
auto pDude = & dudeactor - > s ( ) ;
if ( dudeactor = = actor | | ! dudeactor - > IsDudeActor ( ) | | dudeactor - > IsPlayerActor ( ) | | ! dudeactor - > hasX ( ) )
2021-07-19 21:15:26 +00:00
continue ;
2020-12-06 20:56:09 +00:00
2021-08-29 15:07:09 +00:00
auto pXDude = & dudeactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXDude - > health < = 0 )
continue ;
int eaz2 = ( getDudeInfo ( pTarget - > type ) - > eyeHeight * pTarget - > yrepeat ) < < 2 ;
int nDist = approxDist ( pDude - > x - pSprite - > x , pDude - > y - pSprite - > y ) ;
2021-08-29 15:07:09 +00:00
if ( nDist > = kPatrolAlarmSeeDist | | ! cansee ( pSprite - > x , pSprite - > y , zt1 , pSprite - > sectnum , pDude - > x , pDude - > y , pDude - > z - eaz2 , pDude - > sectnum ) )
{
2021-07-19 21:15:26 +00:00
nDist = approxDist ( pDude - > x - pTarget - > x , pDude - > y - pTarget - > y ) ;
if ( nDist > = kPatrolAlarmSeeDist | | ! cansee ( pTarget - > x , pTarget - > y , zt2 , pTarget - > sectnum , pDude - > x , pDude - > y , pDude - > z - eaz2 , pDude - > sectnum ) )
continue ;
}
2021-09-05 17:21:18 +00:00
if ( aiInPatrolState ( pXDude - > aiState ) ) aiPatrolStop ( dudeactor , dudeactor - > GetTarget ( ) ) ;
2021-08-29 15:07:09 +00:00
if ( dudeactor - > GetTarget ( ) & & dudeactor - > GetTarget ( ) = = actor - > GetTarget ( ) )
2021-07-19 21:15:26 +00:00
continue ;
2021-09-16 17:32:46 +00:00
aiSetTarget ( dudeactor , targetactor ) ;
aiActivateDude ( dudeactor ) ;
2021-07-19 21:15:26 +00:00
}
}
2021-08-29 15:07:09 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-08-29 15:07:09 +00:00
void aiPatrolAlarmFull ( DBloodActor * actor , DBloodActor * targetactor , bool chain )
{
if ( ! actor - > hasX ( ) | | ! actor - > IsDudeActor ( ) )
2021-07-19 21:15:26 +00:00
return ;
2021-08-29 15:07:09 +00:00
spritetype * pSprite = & actor - > s ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
spritetype * pTarget = & targetactor - > s ( ) ;
XSPRITE * pXTarget = & targetactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXSprite - > health < = 0 )
return ;
int eaz2 = ( getDudeInfo ( pSprite - > type ) - > eyeHeight * pSprite - > yrepeat ) < < 2 ;
int x2 = pSprite - > x , y2 = pSprite - > y , z2 = pSprite - > z - eaz2 , sect2 = pSprite - > sectnum ;
2021-08-29 15:07:09 +00:00
int tzt , tzb ;
GetActorExtents ( targetactor , & tzt , & tzb ) ;
2021-07-19 21:15:26 +00:00
int x3 = pTarget - > x , y3 = pTarget - > y , z3 = tzt , sect3 = pTarget - > sectnum ;
2021-08-29 15:07:09 +00:00
BloodStatIterator it ( kStatDude ) ;
while ( auto dudeactor = it . Next ( ) )
{
auto pDude = & dudeactor - > s ( ) ;
if ( dudeactor = = actor | | ! dudeactor - > IsDudeActor ( ) | | dudeactor - > IsPlayerActor ( ) | | ! dudeactor - > hasX ( ) )
2021-07-19 21:15:26 +00:00
continue ;
2021-08-29 15:07:09 +00:00
auto pXDude = & dudeactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXDude - > health < = 0 )
continue ;
int eaz1 = ( getDudeInfo ( pDude - > type ) - > eyeHeight * pDude - > yrepeat ) < < 2 ;
int x1 = pDude - > x , y1 = pDude - > y , z1 = pDude - > z - eaz1 , sect1 = pDude - > sectnum ;
int nDist1 = approxDist ( x1 - x2 , y1 - y2 ) ;
int nDist2 = approxDist ( x1 - x3 , y1 - y3 ) ;
//int hdist = (pXDude->dudeDeaf) ? 0 : getDudeInfo(pDude->type)->hearDist / 4;
int sdist = ( pXDude - > dudeGuard ) ? 0 : getDudeInfo ( pDude - > type ) - > seeDist / 2 ;
if ( //(nDist1 < hdist || nDist2 < hdist) ||
( ( nDist1 < sdist & & cansee ( x1 , y1 , z1 , sect1 , x2 , y2 , z2 , sect2 ) ) | | ( nDist2 < sdist & & cansee ( x1 , y1 , z1 , sect1 , x3 , y3 , z3 , sect3 ) ) ) ) {
2021-09-05 17:21:18 +00:00
if ( aiInPatrolState ( pXDude - > aiState ) ) aiPatrolStop ( dudeactor , dudeactor - > GetTarget ( ) ) ;
2021-08-29 15:07:09 +00:00
if ( dudeactor - > GetTarget ( ) & & dudeactor - > GetTarget ( ) = = actor - > GetTarget ( ) )
2020-12-06 20:56:09 +00:00
continue ;
2021-08-29 15:07:09 +00:00
if ( actor - > GetTarget ( ) ) aiSetTarget ( dudeactor , actor - > GetTarget ( ) ) ;
2021-09-16 17:32:46 +00:00
else aiSetTarget ( dudeactor , pSprite - > x , pSprite - > y , pSprite - > z ) ;
aiActivateDude ( dudeactor ) ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
if ( chain )
2021-08-29 15:07:09 +00:00
aiPatrolAlarmFull ( dudeactor , targetactor , Chance ( 0x0010 ) ) ;
2021-07-19 21:15:26 +00:00
//Printf("Dude #%d alarms dude #%d", pSprite->index, pDude->index);
2020-12-06 20:56:09 +00:00
}
}
}
2021-08-29 15:16:38 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-08-29 15:16:38 +00:00
bool spritesTouching ( DBloodActor * actor1 , DBloodActor * actor2 )
{
if ( ! actor1 - > hasX ( ) | | ! actor2 - > hasX ( ) )
2020-12-06 20:56:09 +00:00
return false ;
2021-08-29 15:16:38 +00:00
auto hit = & actor1 - > hit ( ) ;
2021-09-02 18:23:51 +00:00
DBloodActor * hitactor = nullptr ;
2021-08-29 15:16:38 +00:00
if ( hit - > hit . type = = kHitSprite ) hitactor = hit - > hit . actor ;
else if ( hit - > florhit . type = = kHitSprite ) hitactor = hit - > florhit . actor ;
else if ( hit - > ceilhit . type = = kHitSprite ) hitactor = hit - > ceilhit . actor ;
2021-09-02 18:23:51 +00:00
else return false ;
2021-08-29 15:16:38 +00:00
return hitactor - > hasX ( ) & & hitactor = = actor2 ;
2020-12-06 20:56:09 +00:00
}
2021-08-29 15:16:38 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
bool aiCanCrouch ( DBloodActor * actor )
2021-08-29 07:27:03 +00:00
{
auto pSprite = & actor - > s ( ) ;
2020-12-06 20:56:09 +00:00
if ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeVanillaMax )
return ( gDudeInfoExtra [ pSprite - > type - kDudeBase ] . idlcseqofs > = 0 & & gDudeInfoExtra [ pSprite - > type - kDudeBase ] . mvecseqofs > = 0 ) ;
else if ( pSprite - > type = = kDudeModernCustom | | pSprite - > type = = kDudeModernCustomBurning )
2021-08-29 07:27:03 +00:00
return actor - > genDudeExtra . canDuck ;
2020-12-06 20:56:09 +00:00
return false ;
}
2021-08-29 15:16:38 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-12-06 20:56:09 +00:00
2021-08-29 15:16:38 +00:00
bool readyForCrit ( DBloodActor * hunter , DBloodActor * victim )
{
auto pHunter = & hunter - > s ( ) ;
auto pVictim = & victim - > s ( ) ;
2020-12-06 20:56:09 +00:00
if ( ! ( pHunter - > type > = kDudeBase & & pHunter - > type < kDudeMax ) | | ! ( pVictim - > type > = kDudeBase & & pVictim - > type < kDudeMax ) )
return false ;
2021-07-19 21:15:26 +00:00
int dx , dy ;
dx = pVictim - > x - pHunter - > x ;
dy = pVictim - > y - pHunter - > y ;
if ( approxDist ( dx , dy ) > = ( 7000 / ClipLow ( gGameOptions . nDifficulty > > 1 , 1 ) ) )
2020-12-06 20:56:09 +00:00
return false ;
2021-07-19 21:15:26 +00:00
return ( abs ( ( ( getangle ( dx , dy ) + 1024 - pVictim - > ang ) & 2047 ) - 1024 ) < = kAng45 ) ;
2020-12-06 20:56:09 +00:00
}
2021-08-29 15:16:38 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-09-04 08:44:08 +00:00
DBloodActor * aiPatrolSearchTargets ( DBloodActor * actor )
{
spritetype * pSprite = & actor - > s ( ) ;
XSPRITE * pXSprite = & actor - > x ( ) ;
2021-08-27 11:41:58 +00:00
enum { kMaxPatrolFoundSounds = 256 } ; // should be the maximum amount of sound channels the engine can play at the same time.
PATROL_FOUND_SOUNDS patrolBonkles [ kMaxPatrolFoundSounds ] ;
2021-05-03 22:03:01 +00:00
assert ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeMax ) ;
2021-07-19 21:15:26 +00:00
DUDEINFO * pDudeInfo = getDudeInfo ( pSprite - > type ) ; PLAYER * pPlayer = NULL ;
2021-09-04 08:43:02 +00:00
for ( int i = 0 ; i < kMaxPatrolFoundSounds ; i + + )
{
2021-08-27 11:41:58 +00:00
patrolBonkles [ i ] . snd = patrolBonkles [ i ] . cur = 0 ;
patrolBonkles [ i ] . max = ClipLow ( ( gGameOptions . nDifficulty + 1 ) > > 1 , 1 ) ;
}
2021-09-04 08:44:08 +00:00
int i , j , f , mod , x , y , z , dx , dy , nDist , eyeAboveZ , sndCnt = 0 , seeDist , hearDist , feelDist , seeChance , hearChance ;
bool stealth = ( pXSprite - > unused1 & kDudeFlagStealth ) ;
bool blind = ( pXSprite - > dudeGuard ) ;
bool deaf = ( pXSprite - > dudeDeaf ) ;
2021-01-07 12:33:20 +00:00
2021-09-04 08:44:08 +00:00
DBloodActor * newtarget = nullptr ;
2021-07-19 21:15:26 +00:00
// search for player targets
2021-09-04 08:44:08 +00:00
for ( i = connecthead ; i ! = - 1 ; i = connectpoint2 [ i ] )
{
2021-07-19 21:15:26 +00:00
pPlayer = & gPlayer [ i ] ;
2021-09-04 08:44:08 +00:00
if ( ! pPlayer - > actor ( ) - > hasX ( ) ) continue ;
2021-07-19 21:15:26 +00:00
spritetype * pSpr = pPlayer - > pSprite ;
2021-09-04 08:44:08 +00:00
XSPRITE * pXSpr = & pPlayer - > actor ( ) - > x ( ) ;
2021-07-19 21:15:26 +00:00
if ( pXSpr - > health < = 0 )
2020-12-06 20:56:09 +00:00
continue ;
2021-09-04 08:44:08 +00:00
newtarget = nullptr ;
seeChance = hearChance = 0x0000 ;
2021-07-19 21:15:26 +00:00
x = pSpr - > x , y = pSpr - > y , z = pSpr - > z , dx = x - pSprite - > x , dy = y - pSprite - > y ; nDist = approxDist ( dx , dy ) ;
2021-01-07 12:33:20 +00:00
seeDist = ( stealth ) ? pDudeInfo - > seeDist / 3 : pDudeInfo - > seeDist > > 1 ;
2021-07-19 21:15:26 +00:00
hearDist = pDudeInfo - > hearDist ; feelDist = hearDist > > 1 ;
// TO-DO: is there any dudes that sees this patrol dude and sees target?
2020-12-06 20:56:09 +00:00
2021-09-04 08:43:02 +00:00
if ( nDist < = seeDist )
{
2021-07-19 21:15:26 +00:00
eyeAboveZ = ( pDudeInfo - > eyeHeight * pSprite - > yrepeat ) < < 2 ;
2021-09-04 08:44:08 +00:00
if ( nDist < seeDist > > 3 ) GetActorExtents ( pPlayer - > actor ( ) , & z , & j ) ; //use ztop of the target sprite
2021-07-19 21:15:26 +00:00
if ( ! cansee ( x , y , z , pSpr - > sectnum , pSprite - > x , pSprite - > y , pSprite - > z - eyeAboveZ , pSprite - > sectnum ) )
continue ;
2020-12-06 20:56:09 +00:00
}
2021-09-04 08:43:02 +00:00
else
continue ;
2020-12-06 20:56:09 +00:00
bool invisible = ( powerupCheck ( pPlayer , kPwUpShadowCloak ) > 0 ) ;
2021-08-29 15:16:38 +00:00
if ( spritesTouching ( actor , pPlayer - > actor ( ) ) | | spritesTouching ( pPlayer - > actor ( ) , actor ) )
{
2021-09-04 08:44:08 +00:00
DPrintf ( DMSG_SPAMMY , " Patrol dude #%d spot the Player #%d via touch. " , actor - > GetIndex ( ) , pPlayer - > nPlayer + 1 ) ;
2021-07-19 21:15:26 +00:00
if ( invisible ) pPlayer - > pwUpTime [ kPwUpShadowCloak ] = 0 ;
2021-09-04 08:44:08 +00:00
newtarget = pPlayer - > actor ( ) ;
2021-07-19 21:15:26 +00:00
break ;
}
2021-09-04 08:43:02 +00:00
if ( ! deaf )
{
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
soundEngine - > EnumerateChannels ( [ & ] ( FSoundChan * chan )
{
int sndx = 0 , sndy = 0 ;
int searchsect = - 1 ;
if ( chan - > SourceType = = SOURCE_Actor )
{
auto emitter = ( spritetype * ) chan - > Source ;
if ( emitter < sprite | | emitter > = sprite + MAXSPRITES | | emitter - > statnum = = kStatFree ) return false ; // not a valid source.
sndx = emitter - > x ;
sndy = emitter - > y ;
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
// sound attached to the sprite
2021-09-04 08:43:02 +00:00
if ( pSpr - > index ! = emitter - > index & & emitter - > owner ! = pSpr - > index )
{
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
if ( ! sectRangeIsFine ( emitter - > sectnum ) ) return false ;
searchsect = emitter - > sectnum ;
2021-07-19 21:15:26 +00:00
}
2020-12-06 20:56:09 +00:00
}
2021-07-25 08:42:15 +00:00
else if ( chan - > SourceType = = SOURCE_Unattached )
{
if ( chan - > UserData < 0 | | chan - > UserData > = numsectors ) return false ; // not a vaild sector sound.
sndx = int ( chan - > Point [ 0 ] * 16 ) ;
sndy = int ( chan - > Point [ 1 ] * - 16 ) ;
searchsect = chan - > UserData ;
2020-12-06 20:56:09 +00:00
}
2021-07-25 08:42:15 +00:00
if ( searchsect = = - 1 ) return false ;
int nDist = approxDist ( sndx - pSprite - > x , sndy - pSprite - > y ) ;
if ( nDist > hearDist ) return false ;
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
int sndnum = chan - > OrgID ;
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
// N same sounds per single enemy
for ( int f = 0 ; f < sndCnt ; f + + )
{
if ( patrolBonkles [ f ] . snd ! = sndnum ) continue ;
else if ( + + patrolBonkles [ f ] . cur > = patrolBonkles [ f ] . max )
return false ;
}
if ( sndCnt < kMaxPatrolFoundSounds - 1 )
patrolBonkles [ sndCnt + + ] . snd = sndnum ;
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
bool found = false ;
BloodSectIterator it ( searchsect ) ;
while ( auto act = it . Next ( ) )
{
2021-09-04 08:44:08 +00:00
if ( act - > GetOwner ( ) = = pPlayer - > actor ( ) )
2021-07-25 08:42:15 +00:00
{
found = true ;
break ;
}
}
if ( ! found ) return false ;
2021-07-19 21:15:26 +00:00
2021-07-25 08:42:15 +00:00
f = ClipLow ( ( hearDist - nDist ) / 8 , 0 ) ;
int sndvol = int ( chan - > Volume * ( 80.f / 0.8f ) ) ;
hearChance + = mulscale8 ( sndvol , f ) + Random ( gGameOptions . nDifficulty ) ;
return ( hearChance > = kMaxPatrolSpotValue ) ;
} ) ;
2020-12-06 20:56:09 +00:00
2021-07-25 08:42:15 +00:00
if ( invisible & & hearChance > = kMaxPatrolSpotValue > > 2 )
{
2021-09-04 08:44:08 +00:00
newtarget = pPlayer - > actor ( ) ;
2021-07-19 21:15:26 +00:00
pPlayer - > pwUpTime [ kPwUpShadowCloak ] = 0 ;
invisible = false ;
break ;
2020-12-06 20:56:09 +00:00
}
}
2021-09-04 08:43:02 +00:00
if ( ! invisible & & ( ! deaf | | ! blind ) )
{
if ( stealth )
{
switch ( pPlayer - > lifeMode )
{
2021-07-19 21:15:26 +00:00
case kModeHuman :
case kModeHumanShrink :
2021-09-04 08:43:02 +00:00
if ( pPlayer - > lifeMode = = kModeHumanShrink )
{
2021-07-19 21:15:26 +00:00
seeDist - = mulscale8 ( 164 , seeDist ) ;
feelDist - = mulscale8 ( 164 , feelDist ) ;
}
2021-09-04 08:43:02 +00:00
if ( pPlayer - > posture = = kPostureCrouch )
{
2021-07-19 21:15:26 +00:00
seeDist - = mulscale8 ( 64 , seeDist ) ;
feelDist - = mulscale8 ( 128 , feelDist ) ;
}
break ;
case kModeHumanGrown :
2021-09-04 08:43:02 +00:00
if ( pPlayer - > posture ! = kPostureCrouch )
{
2021-07-19 21:15:26 +00:00
seeDist + = mulscale8 ( 72 , seeDist ) ;
feelDist + = mulscale8 ( 64 , feelDist ) ;
2021-09-04 08:43:02 +00:00
}
else
{
2021-07-19 21:15:26 +00:00
seeDist + = mulscale8 ( 48 , seeDist ) ;
}
break ;
}
}
bool itCanHear = false ; bool itCanSee = false ;
2021-09-04 08:44:08 +00:00
feelDist = ClipLow ( feelDist , 0 ) ;
seeDist = ClipLow ( seeDist , 0 ) ;
2021-07-19 21:15:26 +00:00
2021-09-04 08:43:02 +00:00
if ( hearDist )
{
2021-09-04 08:44:08 +00:00
auto act = pPlayer - > actor ( ) ;
2021-07-19 21:15:26 +00:00
itCanHear = ( ! deaf & & ( nDist < hearDist | | hearChance > 0 ) ) ;
2021-09-04 08:44:08 +00:00
if ( itCanHear & & nDist < feelDist & & ( act - > xvel ( ) | | act - > yvel ( ) | | act - > zvel ( ) ) )
hearChance + = ClipLow ( mulscale8 ( 1 , ClipLow ( ( ( feelDist - nDist ) + ( abs ( act - > xvel ( ) ) + abs ( act - > yvel ( ) ) + abs ( act - > zvel ( ) ) ) ) > > 6 , 0 ) ) , 0 ) ;
2021-07-19 21:15:26 +00:00
}
2021-09-04 08:43:02 +00:00
if ( seeDist )
{
2021-07-19 21:15:26 +00:00
int periphery = ClipLow ( pDudeInfo - > periphery , kAng60 ) ;
int nDeltaAngle = abs ( ( ( getangle ( dx , dy ) + 1024 - pSprite - > ang ) & 2047 ) - 1024 ) ;
2021-09-04 08:43:02 +00:00
if ( ( itCanSee = ( ! blind & & nDist < seeDist & & nDeltaAngle < periphery ) ) = = true )
{
2021-07-19 21:15:26 +00:00
int base = 100 + ( ( 20 * gGameOptions . nDifficulty ) - ( nDeltaAngle / 5 ) ) ;
//seeChance = base - MulScale(ClipRange(5 - gGameOptions.nDifficulty, 1, 4), nDist >> 1, 16);
//scale(0x40000, a6, dist2);
int d = nDist > > 2 ;
int m = DivScale ( d , 0x2000 , 8 ) ;
int t = MulScale ( d , m , 8 ) ;
//int n = mulscale8(nDeltaAngle >> 2, 64);
seeChance = ClipRange ( DivScale ( base , t , 8 ) , 0 , kMaxPatrolSpotValue > > 1 ) ;
//seeChance = scale(0x1000, base, t);
//viewSetSystemMessage("SEE CHANCE: %d, BASE %d, DIST %d, T %d", seeChance, base, nDist, t);
//itCanSee = false;
}
}
if ( ! itCanSee & & ! itCanHear )
continue ;
2021-09-04 08:43:02 +00:00
if ( stealth )
{
2021-07-19 21:15:26 +00:00
// search in stealth regions to modify spot chances
2021-09-04 08:44:08 +00:00
BloodStatIterator it ( kStatModernStealthRegion ) ;
while ( auto iactor = it . Next ( ) )
{
if ( ! iactor - > hasX ( ) )
2021-07-19 21:15:26 +00:00
continue ;
2021-09-04 08:44:08 +00:00
spritetype * pSteal = & iactor - > s ( ) ;
2021-07-19 21:15:26 +00:00
XSPRITE * pXSteal = & xsprite [ pSteal - > extra ] ;
if ( pXSteal - > locked ) // ignore locked regions
continue ;
bool fixd = ( pSteal - > flags & kModernTypeFlag1 ) ; // fixed percent value
bool both = ( pSteal - > flags & kModernTypeFlag4 ) ; // target AND dude must be in this region
bool dude = ( both | | ( pSteal - > flags & kModernTypeFlag2 ) ) ; // dude must be in this region
bool trgt = ( both | | ! dude ) ; // target must be in this region
bool crouch = ( pSteal - > flags & kModernTypeFlag8 ) ; // target must crouch
//bool floor = (pSteal->cstat & CSTAT_SPRITE_BLOCK); // target (or dude?) must touch floor of the sector
2021-09-04 08:43:02 +00:00
if ( trgt )
{
2021-07-19 21:15:26 +00:00
if ( pXSteal - > data1 > 0 )
{
if ( approxDist ( abs ( pSteal - > x - pSpr - > x ) > > 4 , abs ( pSteal - > y - pSpr - > y ) > > 4 ) > = pXSteal - > data1 )
continue ;
2021-09-04 08:43:02 +00:00
}
else if ( pSpr - > sectnum ! = pSteal - > sectnum )
2021-07-19 21:15:26 +00:00
continue ;
if ( crouch & & pPlayer - > posture = = kPostureStand )
continue ;
}
2021-09-04 08:43:02 +00:00
if ( dude )
{
2021-07-19 21:15:26 +00:00
if ( pXSteal - > data1 > 0 )
{
if ( approxDist ( abs ( pSteal - > x - pSprite - > x ) > > 4 , abs ( pSteal - > y - pSprite - > y ) > > 4 ) > = pXSteal - > data1 )
continue ;
2021-09-04 08:43:02 +00:00
}
else if ( pSprite - > sectnum ! = pSteal - > sectnum )
2021-07-19 21:15:26 +00:00
continue ;
}
2021-09-04 08:43:02 +00:00
if ( itCanHear )
{
2021-07-19 21:15:26 +00:00
if ( fixd )
hearChance = ClipLow ( hearChance , pXSteal - > data2 ) ;
mod = ( hearChance * pXSteal - > data2 ) / kPercFull ;
if ( fixd ) hearChance = mod ; else hearChance + = mod ;
hearChance = ClipRange ( hearChance , - kMaxPatrolSpotValue , kMaxPatrolSpotValue ) ;
}
2021-09-04 08:43:02 +00:00
if ( itCanSee )
{
2021-07-19 21:15:26 +00:00
if ( fixd )
seeChance = ClipLow ( seeChance , pXSteal - > data3 ) ;
mod = ( seeChance * pXSteal - > data3 ) / kPercFull ;
if ( fixd ) seeChance = mod ; else seeChance + = mod ;
seeChance = ClipRange ( seeChance , - kMaxPatrolSpotValue , kMaxPatrolSpotValue ) ;
}
// trigger this region if target gonna be spot
if ( pXSteal - > txID & & pXSprite - > data3 + hearChance + seeChance > = kMaxPatrolSpotValue )
2021-09-04 08:44:08 +00:00
trTriggerSprite ( iactor , kCmdToggle ) ;
2021-07-19 21:15:26 +00:00
// continue search another stealth regions to affect chances
}
}
2021-09-04 08:43:02 +00:00
if ( itCanHear & & hearChance > 0 )
{
2021-09-04 08:44:08 +00:00
DPrintf ( DMSG_SPAMMY , " Patrol dude #%d hearing the Player #%d. " , actor - > GetIndex ( ) , pPlayer - > nPlayer + 1 ) ;
2021-07-19 21:15:26 +00:00
pXSprite - > data3 = ClipRange ( pXSprite - > data3 + hearChance , - kMaxPatrolSpotValue , kMaxPatrolSpotValue ) ;
2021-09-04 08:43:02 +00:00
if ( ! stealth )
{
2021-09-04 08:44:08 +00:00
newtarget = pPlayer - > actor ( ) ;
2021-01-07 12:33:20 +00:00
break ;
2021-09-04 08:43:02 +00:00
}
2020-12-06 20:56:09 +00:00
}
2021-09-04 08:43:02 +00:00
if ( itCanSee & & seeChance > 0 )
{
2021-07-19 21:15:26 +00:00
//DPrintf(DMSG_SPAMMY, "Patrol dude #%d seeing the Player #%d.", pSprite->index, pPlayer->nPlayer + 1);
//pXSprite->data3 += seeChance;
pXSprite - > data3 = ClipRange ( pXSprite - > data3 + seeChance , - kMaxPatrolSpotValue , kMaxPatrolSpotValue ) ;
2021-09-04 08:43:02 +00:00
if ( ! stealth )
{
2021-09-04 08:44:08 +00:00
newtarget = pPlayer - > actor ( ) ;
2021-01-07 12:33:20 +00:00
break ;
2021-07-19 21:15:26 +00:00
}
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
}
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
// add check for corpses?
2020-12-06 20:56:09 +00:00
2021-09-04 08:43:02 +00:00
if ( ( pXSprite - > data3 = ClipRange ( pXSprite - > data3 , 0 , kMaxPatrolSpotValue ) ) = = kMaxPatrolSpotValue )
{
2021-09-04 08:44:08 +00:00
newtarget = pPlayer - > actor ( ) ;
2020-12-06 20:56:09 +00:00
break ;
}
2021-07-19 21:15:26 +00:00
2020-12-06 20:56:09 +00:00
//int perc = (100 * ClipHigh(pXSprite->data3, kMaxPatrolSpotValue)) / kMaxPatrolSpotValue;
//viewSetSystemMessage("%d / %d / %d / %d", hearChance, seeDist, seeChance, perc);
}
2021-09-04 08:44:08 +00:00
if ( newtarget ) return newtarget ;
2021-07-19 21:15:26 +00:00
pXSprite - > data3 - = ClipLow ( ( ( kPercFull * pXSprite - > data3 ) / kMaxPatrolSpotValue ) > > 2 , 3 ) ;
2021-09-04 08:44:08 +00:00
return nullptr ;
2020-12-06 20:56:09 +00:00
}
2021-09-04 08:43:02 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-04 08:53:56 +00:00
void aiPatrolFlagsMgr ( DBloodActor * sourceactor , DBloodActor * destactor , bool copy , bool init )
2021-08-29 16:13:15 +00:00
{
2021-09-04 08:53:56 +00:00
XSPRITE * pXSource = & sourceactor - > x ( ) ;
XSPRITE * pXDest = & destactor - > x ( ) ;
2021-07-19 21:15:26 +00:00
// copy flags
2021-09-04 08:52:12 +00:00
if ( copy )
{
2021-07-19 21:15:26 +00:00
pXDest - > dudeFlag4 = pXSource - > dudeFlag4 ;
pXDest - > dudeAmbush = pXSource - > dudeAmbush ;
pXDest - > dudeGuard = pXSource - > dudeGuard ;
pXDest - > dudeDeaf = pXSource - > dudeDeaf ;
pXDest - > unused1 = pXSource - > unused1 ;
if ( pXSource - > unused1 & kDudeFlagStealth ) pXDest - > unused1 | = kDudeFlagStealth ;
else pXDest - > unused1 & = ~ kDudeFlagStealth ;
}
// do init
2021-09-04 08:52:12 +00:00
if ( init )
{
if ( ! pXDest - > dudeFlag4 )
{
2021-07-19 21:15:26 +00:00
if ( aiInPatrolState ( pXDest - > aiState ) )
2021-09-05 17:21:18 +00:00
aiPatrolStop ( destactor , nullptr ) ;
2021-09-04 08:52:12 +00:00
}
else
{
2021-07-19 21:15:26 +00:00
if ( aiInPatrolState ( pXDest - > aiState ) )
return ;
2021-09-04 08:53:56 +00:00
destactor - > SetTarget ( nullptr ) ;
2021-07-19 21:15:26 +00:00
pXDest - > stateTimer = 0 ;
2021-08-29 13:57:42 +00:00
aiPatrolSetMarker ( destactor ) ;
2021-08-29 12:36:40 +00:00
if ( spriteIsUnderwater ( destactor ) ) aiPatrolState ( destactor , kAiStatePatrolWaitW ) ;
else aiPatrolState ( destactor , kAiStatePatrolWaitL ) ;
2021-07-19 21:15:26 +00:00
pXDest - > data3 = 0 ; // reset the spot progress
}
}
}
2021-09-04 08:52:12 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-04 08:53:56 +00:00
bool aiPatrolGetPathDir ( DBloodActor * actor , DBloodActor * marker )
{
if ( actor - > x ( ) . unused2 = = kPatrolMoveForward ) return ( marker - > x ( ) . data2 = = - 2 ) ? ( bool ) kPatrolMoveBackward : ( bool ) kPatrolMoveForward ;
else return ( findNextMarker ( marker , kPatrolMoveBackward ) ! = nullptr ) ? ( bool ) kPatrolMoveBackward : ( bool ) kPatrolMoveForward ;
2021-07-19 21:15:26 +00:00
}
2021-09-04 08:52:12 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-07-19 21:15:26 +00:00
2021-09-04 08:52:12 +00:00
void aiPatrolThink ( DBloodActor * actor )
{
2021-05-03 22:03:01 +00:00
auto pXSprite = & actor - > x ( ) ;
auto pSprite = & actor - > s ( ) ;
2020-12-06 20:56:09 +00:00
2021-07-19 21:15:26 +00:00
assert ( pSprite - > type > = kDudeBase & & pSprite - > type < kDudeMax ) ;
2020-12-06 20:56:09 +00:00
2021-09-04 08:44:08 +00:00
DBloodActor * targetactor ;
2021-09-04 08:53:56 +00:00
int stateTimer ;
auto markeractor = actor - > GetTarget ( ) ;
2021-09-04 08:44:08 +00:00
if ( ( targetactor = aiPatrolSearchTargets ( actor ) ) ! = nullptr )
{
aiPatrolStop ( actor , targetactor , pXSprite - > dudeAmbush ) ;
2020-12-06 20:56:09 +00:00
return ;
}
2021-07-19 21:15:26 +00:00
2020-12-06 20:56:09 +00:00
2021-05-05 18:40:31 +00:00
bool crouch = ( pXSprite - > unused1 & kDudeFlagCrouch ) , uwater = spriteIsUnderwater ( actor ) ;
2021-09-04 08:53:56 +00:00
if ( markeractor = = nullptr | | ( pSprite - > type = = kDudeModernCustom & & ( ( uwater & & ! canSwim ( actor ) ) | | ! canWalk ( actor ) ) ) )
{
2021-09-05 17:21:18 +00:00
aiPatrolStop ( actor , nullptr ) ;
2021-07-19 21:15:26 +00:00
return ;
2020-12-06 20:56:09 +00:00
}
2021-07-19 21:15:26 +00:00
2021-08-27 08:46:57 +00:00
spritetype * pMarker = & markeractor - > s ( ) ;
XSPRITE * pXMarker = & markeractor - > x ( ) ;
2021-08-27 11:46:07 +00:00
const DUDEINFO_EXTRA * pExtra = & gDudeInfoExtra [ pSprite - > type - kDudeBase ] ;
2021-07-19 21:15:26 +00:00
bool isFinal = ( ( ! pXSprite - > unused2 & & pXMarker - > data2 = = - 1 ) | | ( pXSprite - > unused2 & & pXMarker - > data1 = = - 1 ) ) ;
bool reached = false ;
2021-09-04 08:52:12 +00:00
if ( aiPatrolWaiting ( pXSprite - > aiState ) )
{
2021-07-19 21:15:26 +00:00
//viewSetSystemMessage("WAIT %d / %d", pXSprite->targetY, pXSprite->stateTimer);
2021-09-04 08:53:56 +00:00
if ( pXSprite - > stateTimer > 0 | | pXMarker - > data1 = = pXMarker - > data2 )
{
2021-07-19 21:15:26 +00:00
if ( pExtra - > flying )
2021-09-04 08:53:56 +00:00
actor - > zvel ( ) = Random2 ( 0x8000 ) ;
2021-07-19 21:15:26 +00:00
// turn while waiting
2021-09-04 08:52:12 +00:00
if ( pMarker - > flags & kModernTypeFlag16 )
{
2021-07-19 21:15:26 +00:00
stateTimer = pXSprite - > stateTimer ;
2021-09-04 08:52:12 +00:00
if ( - - pXSprite - > unused4 < = 0 )
{
2021-08-29 12:36:40 +00:00
if ( uwater ) aiPatrolState ( actor , kAiStatePatrolTurnW ) ;
else if ( crouch ) aiPatrolState ( actor , kAiStatePatrolTurnC ) ;
else aiPatrolState ( actor , kAiStatePatrolTurnL ) ;
2021-07-19 21:15:26 +00:00
pXSprite - > unused4 = kMinPatrolTurnDelay + Random ( kPatrolTurnDelayRange ) ;
}
// must restore stateTimer for waiting
pXSprite - > stateTimer = stateTimer ;
}
2020-12-06 20:56:09 +00:00
return ;
2021-07-19 21:15:26 +00:00
}
// trigger at departure
2021-09-04 08:52:12 +00:00
if ( pXMarker - > triggerOff )
{
2021-07-19 21:15:26 +00:00
// send command
2021-09-04 08:52:12 +00:00
if ( pXMarker - > txID )
{
2021-08-27 08:46:57 +00:00
evSendActor ( markeractor , pXMarker - > txID , ( COMMAND_ID ) pXMarker - > command ) ;
2021-07-19 21:15:26 +00:00
// copy dude flags for current dude
2021-09-04 08:52:12 +00:00
}
else if ( pXMarker - > command = = kCmdDudeFlagsSet )
{
2021-09-04 08:53:56 +00:00
aiPatrolFlagsMgr ( markeractor , actor , true , true ) ;
2021-07-19 21:15:26 +00:00
if ( ! pXSprite - > dudeFlag4 ) // this dude is not in patrol anymore
return ;
}
}
// release the enemy
2021-09-04 08:52:12 +00:00
if ( isFinal )
{
2021-09-05 17:21:18 +00:00
aiPatrolStop ( actor , nullptr ) ;
2020-12-06 20:56:09 +00:00
return ;
}
2021-07-19 21:15:26 +00:00
// move next marker
2021-08-29 13:57:42 +00:00
aiPatrolSetMarker ( actor ) ;
2020-12-06 20:56:09 +00:00
2021-09-04 08:52:12 +00:00
} else if ( aiPatrolTurning ( pXSprite - > aiState ) )
{
2021-07-19 21:15:26 +00:00
//viewSetSystemMessage("TURN");
2021-09-04 08:52:12 +00:00
if ( ( int ) pSprite - > ang = = ( int ) pXSprite - > goalAng )
{
2021-07-19 21:15:26 +00:00
// save imer for waiting
stateTimer = pXSprite - > stateTimer ;
2021-08-29 12:36:40 +00:00
if ( uwater ) aiPatrolState ( actor , kAiStatePatrolWaitW ) ;
else if ( crouch ) aiPatrolState ( actor , kAiStatePatrolWaitC ) ;
else aiPatrolState ( actor , kAiStatePatrolWaitL ) ;
2021-07-19 21:15:26 +00:00
// must restore it
pXSprite - > stateTimer = stateTimer ;
}
return ;
2021-09-04 08:52:12 +00:00
}
else if ( ( reached = aiPatrolMarkerReached ( actor ) ) = = true )
{
2021-07-19 21:15:26 +00:00
pXMarker - > isTriggered = pXMarker - > triggerOnce ; // can't select this marker for path anymore if true
2021-09-04 08:52:12 +00:00
if ( pMarker - > flags > 0 )
{
2021-07-19 21:15:26 +00:00
if ( ( pMarker - > flags & kModernTypeFlag2 ) & & ( pMarker - > flags & kModernTypeFlag1 ) ) crouch = ! crouch ;
else if ( pMarker - > flags & kModernTypeFlag2 ) crouch = false ;
2021-08-29 07:27:03 +00:00
else if ( ( pMarker - > flags & kModernTypeFlag1 ) & & aiCanCrouch ( actor ) ) crouch = true ;
2021-07-19 21:15:26 +00:00
}
2020-12-06 20:56:09 +00:00
2021-09-04 08:52:12 +00:00
if ( pXMarker - > waitTime > 0 | | pXMarker - > data1 = = pXMarker - > data2 )
{
2021-07-19 21:15:26 +00:00
// take marker's angle
2021-09-04 08:52:12 +00:00
if ( ! ( pMarker - > flags & kModernTypeFlag4 ) )
{
2021-07-19 21:15:26 +00:00
pXSprite - > goalAng = ( ( ! ( pMarker - > flags & kModernTypeFlag8 ) & & pXSprite - > unused2 ) ? pMarker - > ang + kAng180 : pMarker - > ang ) & 2047 ;
if ( ( int ) pSprite - > ang ! = ( int ) pXSprite - > goalAng ) // let the enemy play move animation while turning
return ;
}
2021-08-29 12:36:40 +00:00
if ( markeractor - > GetOwner ( ) = = actor )
markeractor - > SetOwner ( aiPatrolMarkerBusy ( actor , markeractor ) ) ;
2021-07-19 21:15:26 +00:00
// trigger at arrival
2021-09-04 08:52:12 +00:00
if ( pXMarker - > triggerOn )
{
2021-07-19 21:15:26 +00:00
// send command
2021-09-04 08:52:12 +00:00
if ( pXMarker - > txID )
{
2021-08-27 08:46:57 +00:00
evSendActor ( markeractor , pXMarker - > txID , ( COMMAND_ID ) pXMarker - > command ) ;
2021-09-04 08:52:12 +00:00
}
else if ( pXMarker - > command = = kCmdDudeFlagsSet )
{
2021-07-19 21:15:26 +00:00
// copy dude flags for current dude
2021-09-04 08:53:56 +00:00
aiPatrolFlagsMgr ( markeractor , actor , true , true ) ;
2021-07-19 21:15:26 +00:00
if ( ! pXSprite - > dudeFlag4 ) // this dude is not in patrol anymore
return ;
}
}
2021-08-29 12:36:40 +00:00
if ( uwater ) aiPatrolState ( actor , kAiStatePatrolWaitW ) ;
else if ( crouch ) aiPatrolState ( actor , kAiStatePatrolWaitC ) ;
else aiPatrolState ( actor , kAiStatePatrolWaitL ) ;
2021-07-19 21:15:26 +00:00
2020-12-06 20:56:09 +00:00
if ( pXMarker - > waitTime )
pXSprite - > stateTimer = ( pXMarker - > waitTime * 120 ) / 10 ;
2021-07-19 21:15:26 +00:00
if ( pMarker - > flags & kModernTypeFlag16 )
pXSprite - > unused4 = kMinPatrolTurnDelay + Random ( kPatrolTurnDelayRange ) ;
2020-12-06 20:56:09 +00:00
return ;
2021-09-04 08:52:12 +00:00
}
else
{
2021-08-29 12:36:40 +00:00
if ( markeractor - > GetOwner ( ) = = actor )
markeractor - > SetOwner ( aiPatrolMarkerBusy ( actor , markeractor ) ) ;
2021-09-04 08:52:12 +00:00
if ( pXMarker - > triggerOn | | pXMarker - > triggerOff )
{
if ( pXMarker - > txID )
{
2021-07-19 21:15:26 +00:00
// send command at arrival
if ( pXMarker - > triggerOn )
2021-08-27 08:46:57 +00:00
evSendActor ( markeractor , pXMarker - > txID , ( COMMAND_ID ) pXMarker - > command ) ;
2021-07-19 21:15:26 +00:00
// send command at departure
if ( pXMarker - > triggerOff )
2021-08-27 08:46:57 +00:00
evSendActor ( markeractor , pXMarker - > txID , ( COMMAND_ID ) pXMarker - > command ) ;
2021-07-19 21:15:26 +00:00
// copy dude flags for current dude
2021-09-04 08:52:12 +00:00
}
else if ( pXMarker - > command = = kCmdDudeFlagsSet )
{
2021-09-04 08:53:56 +00:00
aiPatrolFlagsMgr ( markeractor , actor , true , true ) ;
2021-07-19 21:15:26 +00:00
if ( ! pXSprite - > dudeFlag4 ) // this dude is not in patrol anymore
return ;
}
}
// release the enemy
2021-09-04 08:52:12 +00:00
if ( isFinal )
{
2021-09-05 17:21:18 +00:00
aiPatrolStop ( actor , nullptr ) ;
2021-07-19 21:15:26 +00:00
return ;
}
// move the next marker
2021-08-29 13:57:42 +00:00
aiPatrolSetMarker ( actor ) ;
2020-12-06 20:56:09 +00:00
}
}
2021-08-29 11:59:06 +00:00
nnExtAiSetDirection ( actor , getangle ( pMarker - > x - pSprite - > x , pMarker - > y - pSprite - > y ) ) ;
2021-07-19 21:15:26 +00:00
if ( aiPatrolMoving ( pXSprite - > aiState ) & & ! reached ) return ;
2021-08-29 12:36:40 +00:00
else if ( uwater ) aiPatrolState ( actor , kAiStatePatrolMoveW ) ;
else if ( crouch ) aiPatrolState ( actor , kAiStatePatrolMoveC ) ;
else aiPatrolState ( actor , kAiStatePatrolMoveL ) ;
2020-12-06 20:56:09 +00:00
return ;
}
// ------------------------------------------------
2021-09-05 18:10:40 +00:00
int listTx ( DBloodActor * actor , int tx )
{
if ( txIsRanged ( actor ) )
{
XSPRITE * pXRedir = & actor - > x ( ) ;
2020-04-07 20:30:00 +00:00
if ( tx = = - 1 ) tx = pXRedir - > data1 ;
else if ( tx < pXRedir - > data4 ) tx + + ;
else tx = - 1 ;
2021-09-05 18:05:15 +00:00
}
else
{
if ( tx = = - 1 )
{
for ( int i = 0 ; i < = 3 ; i + + )
{
2021-09-05 18:10:40 +00:00
if ( ( tx = GetDataVal ( actor , i ) ) < = 0 ) continue ;
2020-04-07 20:30:00 +00:00
else return tx ;
}
2021-09-05 18:05:15 +00:00
}
else
{
2020-04-07 20:30:00 +00:00
int saved = tx ; bool savedFound = false ;
2021-09-05 18:05:15 +00:00
for ( int i = 0 ; i < = 3 ; i + + )
{
2021-09-05 18:10:40 +00:00
tx = GetDataVal ( actor , i ) ;
2020-04-07 20:30:00 +00:00
if ( savedFound & & tx > 0 ) return tx ;
else if ( tx ! = saved ) continue ;
else savedFound = true ;
}
}
tx = - 1 ;
}
return tx ;
}
2021-09-05 18:05:15 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-01 18:20:10 +00:00
DBloodActor * evrIsRedirector ( DBloodActor * actor )
{
2021-09-05 18:05:15 +00:00
if ( actor )
2021-09-01 18:20:10 +00:00
{
switch ( actor - > s ( ) . type )
{
2020-04-07 20:30:00 +00:00
case kModernRandomTX :
case kModernSequentialTX :
2021-09-01 18:20:10 +00:00
if ( actor - > hasX ( ) & & actor - > x ( ) . command = = kCmdLink & & ! actor - > x ( ) . locked ) return actor ;
2020-04-07 20:30:00 +00:00
break ;
}
}
2021-09-05 18:05:15 +00:00
return nullptr ;
2020-04-07 20:30:00 +00:00
}
2021-09-05 18:05:15 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-05 18:10:40 +00:00
DBloodActor * evrListRedirectors ( int objType , int objXIndex , DBloodActor * objActor , DBloodActor * pXRedir , int * tx )
{
2020-04-07 20:30:00 +00:00
if ( ! gEventRedirectsUsed ) return NULL ;
else if ( pXRedir & & ( * tx = listTx ( pXRedir , * tx ) ) ! = - 1 )
return pXRedir ;
int id = 0 ;
2021-09-05 18:05:15 +00:00
switch ( objType )
{
2020-04-07 20:30:00 +00:00
case OBJ_SECTOR :
if ( ! xsectRangeIsFine ( objXIndex ) ) return NULL ;
id = xsector [ objXIndex ] . txID ;
break ;
case OBJ_SPRITE :
2021-09-05 18:10:40 +00:00
if ( ! objActor ) return NULL ;
id = objActor - > x ( ) . txID ;
2020-04-07 20:30:00 +00:00
break ;
case OBJ_WALL :
if ( ! xwallRangeIsFine ( objXIndex ) ) return NULL ;
id = xwall [ objXIndex ] . txID ;
break ;
default :
return NULL ;
}
2021-09-05 18:10:40 +00:00
bool prevFound = false ;
2021-09-05 18:05:15 +00:00
for ( int i = bucketHead [ id ] ; i < bucketHead [ id + 1 ] ; i + + )
{
2020-04-07 20:30:00 +00:00
if ( rxBucket [ i ] . type ! = OBJ_SPRITE ) continue ;
2021-09-05 18:10:40 +00:00
auto pXSpr = evrIsRedirector ( rxBucket [ i ] . actor ) ;
if ( ! pXSpr ) continue ;
else if ( prevFound | | pXRedir = = nullptr )
{
* tx = listTx ( pXSpr , * tx ) ;
return pXSpr ;
}
else if ( pXRedir ! = pXSpr ) continue ;
2020-04-07 20:30:00 +00:00
else prevFound = true ;
}
* tx = - 1 ;
return NULL ;
}
2021-09-05 18:05:15 +00:00
//---------------------------------------------------------------------------
//
2020-02-07 19:47:43 +00:00
// this function checks if all TX objects have the same value
2021-09-05 18:05:15 +00:00
//
//---------------------------------------------------------------------------
2021-09-05 18:10:40 +00:00
bool incDecGoalValueIsReached ( DBloodActor * actor )
{
XSPRITE * pXSprite = & actor - > x ( ) ;
2020-04-07 20:30:00 +00:00
if ( pXSprite - > data3 ! = pXSprite - > sysData1 ) return false ;
2021-09-05 18:10:40 +00:00
char buffer [ 5 ] ;
snprintf ( buffer , 5 , " %d " , abs ( pXSprite - > data1 ) ) ;
int len = int ( strlen ( buffer ) ) ;
int rx = - 1 ;
for ( int i = bucketHead [ pXSprite - > txID ] ; i < bucketHead [ pXSprite - > txID + 1 ] ; i + + )
{
if ( rxBucket [ i ] . type = = OBJ_SPRITE & & evrIsRedirector ( rxBucket [ i ] . actor ) ) continue ;
2021-09-05 18:05:15 +00:00
for ( int a = 0 ; a < len ; a + + )
{
2021-08-30 19:43:21 +00:00
if ( getDataFieldOfObject ( rxBucket [ i ] . type , rxBucket [ i ] . rxindex , rxBucket [ i ] . actor , ( buffer [ a ] - 52 ) + 4 ) ! = pXSprite - > data3 )
2020-03-05 20:46:05 +00:00
return false ;
}
2020-02-07 19:47:43 +00:00
}
2020-03-01 20:36:28 +00:00
2021-09-05 18:10:40 +00:00
DBloodActor * pXRedir = nullptr ; // check redirected TX buckets
while ( ( pXRedir = evrListRedirectors ( OBJ_SPRITE , 0 , actor , pXRedir , & rx ) ) ! = nullptr )
{
2021-09-05 18:05:15 +00:00
for ( int i = bucketHead [ rx ] ; i < bucketHead [ rx + 1 ] ; i + + )
{
for ( int a = 0 ; a < len ; a + + )
{
2021-08-30 19:43:21 +00:00
if ( getDataFieldOfObject ( rxBucket [ i ] . type , rxBucket [ i ] . rxindex , rxBucket [ i ] . actor , ( buffer [ a ] - 52 ) + 4 ) ! = pXSprite - > data3 )
2020-04-07 20:30:00 +00:00
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 ;
}
2021-09-05 18:05:15 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2021-09-05 18:10:40 +00:00
void seqSpawnerOffSameTx ( DBloodActor * actor )
{
auto pXSource = & actor - > x ( ) ;
2021-09-05 18:05:15 +00:00
for ( int i = 0 ; i < kMaxXSprites ; i + + )
{
2021-09-05 18:10:40 +00:00
auto iactor = & bloodActors [ i ] ;
if ( iactor - > s ( ) . statnum = = kStatFree | | iactor - > s ( ) . type ! = kModernSeqSpawner | | ! iactor - > hasX ( ) | | iactor = = actor ) continue ;
XSPRITE * pXSprite = & iactor - > x ( ) ;
if ( pXSprite - > txID = = pXSource - > txID & & pXSprite - > state = = 1 )
{
evKillActor ( iactor ) ;
pXSprite - > state = 0 ;
2020-05-05 18:50:14 +00:00
}
}
}
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 ) {
2020-09-04 18:46:44 +00:00
gGameOptions . uGameFlags | = GF_AdvanceLevel ;
2021-05-02 07:08:57 +00:00
gNextLevel = FindMapByIndex ( currentLevel - > cluster , nLevel + 1 ) ;
2020-02-07 19:47:43 +00:00
}
2021-09-01 19:54:23 +00:00
void callbackUniMissileBurst ( DBloodActor * actor , int ) // 22
2020-02-07 19:47:43 +00:00
{
2021-09-01 19:55:12 +00:00
if ( ! actor ) return ;
2021-09-01 19:54:23 +00:00
spritetype * pSprite = & actor - > s ( ) ;
if ( pSprite - > statnum ! = kStatProjectile ) return ;
int nAngle = getangle ( actor - > xvel ( ) , actor - > yvel ( ) ) ;
2020-02-07 19:47:43 +00:00
int nRadius = 0x55555 ;
for ( int i = 0 ; i < 8 ; i + + )
{
2021-09-05 18:10:40 +00:00
auto burstactor = actSpawnSprite ( actor , 5 ) ;
auto pBurst = & burstactor - > s ( ) ;
2020-02-07 19:47:43 +00:00
pBurst - > type = pSprite - > type ;
pBurst - > shade = pSprite - > shade ;
pBurst - > picnum = pSprite - > picnum ;
2021-09-05 10:25:52 +00:00
2020-02-07 19:47:43 +00:00
pBurst - > cstat = pSprite - > cstat ;
if ( ( pBurst - > cstat & CSTAT_SPRITE_BLOCK ) ) {
pBurst - > cstat & = ~ CSTAT_SPRITE_BLOCK ; // we don't want missiles impact each other
2021-08-27 08:18:33 +00:00
evPostActor ( & bloodActors [ pBurst - > index ] , 100 , kCallbackMissileSpriteBlock ) ; // so set blocking flag a bit later
2020-02-07 19:47:43 +00:00
}
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 ;
2020-12-05 20:23:53 +00:00
actBuildMissile ( & bloodActors [ pBurst - > index ] , & bloodActors [ pSprite - > index ] ) ;
2020-02-07 19:47:43 +00:00
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 ) ;
2021-09-05 18:05:15 +00:00
burstactor - > xvel ( ) + = dx ;
burstactor - > yvel ( ) + = dy ;
burstactor - > zvel ( ) + = dz ;
2021-08-27 08:18:33 +00:00
evPostActor ( & bloodActors [ pBurst - > index ] , 960 , kCallbackRemove ) ;
2020-02-07 19:47:43 +00:00
}
2021-08-27 07:44:47 +00:00
evPostActor ( actor , 0 , kCallbackRemove ) ;
2020-02-07 19:47:43 +00:00
}
2021-09-01 19:54:23 +00:00
void callbackMakeMissileBlocking ( DBloodActor * actor , int ) // 23
2020-02-07 19:47:43 +00:00
{
2021-09-01 19:54:23 +00:00
if ( ! actor | | actor - > s ( ) . statnum ! = kStatProjectile ) return ;
actor - > s ( ) . cstat | = CSTAT_SPRITE_BLOCK ;
2020-02-07 19:47:43 +00:00
}
2021-09-01 19:54:23 +00:00
void callbackGenDudeUpdate ( DBloodActor * actor , int ) // 24
2020-02-07 19:47:43 +00:00
{
2021-09-01 19:54:23 +00:00
if ( actor )
genDudeUpdate ( actor ) ;
2020-02-07 19:47:43 +00:00
}
2021-07-19 21:15:26 +00:00
void clampSprite ( spritetype * pSprite , int which ) {
int zTop , zBot ;
if ( pSprite - > sectnum > = 0 & & pSprite - > sectnum < kMaxSectors ) {
GetSpriteExtents ( pSprite , & zTop , & zBot ) ;
if ( which & 0x01 )
pSprite - > z + = ClipHigh ( getflorzofslope ( pSprite - > sectnum , pSprite - > x , pSprite - > y ) - zBot , 0 ) ;
if ( which & 0x02 )
pSprite - > z + = ClipLow ( getceilzofslope ( pSprite - > sectnum , pSprite - > x , pSprite - > y ) - zTop , 0 ) ;
2020-02-11 22:15:25 +00:00
2021-07-19 21:15:26 +00:00
}
}
2020-02-11 22:15:25 +00:00
2020-11-22 15:47:08 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
FSerializer & Serialize ( FSerializer & arc , const char * keyname , GENDUDEEXTRA & w , GENDUDEEXTRA * def )
2020-02-11 22:15:25 +00:00
{
2020-11-22 15:47:08 +00:00
if ( arc . BeginObject ( keyname ) )
{
arc . Array ( " initvals " , w . initVals , 3 )
. Array ( " availdeaths " , w . availDeaths , kDamageMax )
( " movespeed " , w . moveSpeed )
( " firedist " , w . fireDist )
( " throwdist " , w . throwDist )
( " curweapon " , w . curWeapon )
( " weapontype " , w . weaponType )
( " basedispersion " , w . baseDispersion )
( " slavecount " , w . slaveCount )
2021-05-05 19:06:38 +00:00
( " lifeleech " , w . pLifeLeech )
2020-11-22 15:47:08 +00:00
. Array ( " slaves " , w . slave , w . slaveCount )
. Array ( " dmgcontrol " , w . dmgControl , kDamageMax )
. Array ( " updreq " , w . updReq , kGenDudePropertyMax )
( " flags " , w . flags )
. EndObject ( ) ;
}
return arc ;
2020-02-11 22:15:25 +00:00
}
2020-11-22 15:47:08 +00:00
FSerializer & Serialize ( FSerializer & arc , const char * keyname , SPRITEMASS & w , SPRITEMASS * def )
2020-02-11 22:15:25 +00:00
{
2020-11-22 15:47:08 +00:00
static SPRITEMASS nul ;
if ( arc . isReading ( ) ) w = { } ;
if ( arc . BeginObject ( keyname ) )
{
arc ( " seq " , w . seqId , & nul . seqId )
( " picnum " , w . picnum , & nul . picnum )
( " xrepeat " , w . xrepeat , & nul . xrepeat )
( " yrepeat " , w . yrepeat , & nul . yrepeat )
( " clipdist " , w . clipdist )
( " mass " , w . mass )
( " airvel " , w . airVel )
( " fraction " , w . fraction )
. EndObject ( ) ;
}
return arc ;
2020-02-11 22:15:25 +00:00
}
2021-08-27 16:21:01 +00:00
FSerializer & Serialize ( FSerializer & arc , const char * keyname , OBJECTS_TO_TRACK & w , OBJECTS_TO_TRACK * def )
{
static OBJECTS_TO_TRACK nul ;
if ( arc . isReading ( ) ) w = { } ;
if ( arc . BeginObject ( keyname ) )
{
arc ( " type " , w . type , & nul . type )
2021-08-27 10:55:16 +00:00
( " index " , w . index_ , & nul . index_ )
2021-08-27 16:21:01 +00:00
( " xrepeat " , w . cmd , & nul . cmd )
. EndObject ( ) ;
}
return arc ;
}
FSerializer & Serialize ( FSerializer & arc , const char * keyname , TRCONDITION & w , TRCONDITION * def )
{
static TRCONDITION nul ;
if ( arc . isReading ( ) ) w = { } ;
if ( arc . BeginObject ( keyname ) )
{
arc ( " length " , w . length , & nul . length )
2021-10-12 21:52:54 +00:00
( " xindex " , w . actor , & nul . actor )
2021-08-27 16:21:01 +00:00
. Array ( " obj " , w . obj , w . length )
. EndObject ( ) ;
}
return arc ;
}
2020-11-22 15:47:08 +00:00
void SerializeNNExts ( FSerializer & arc )
2020-02-11 22:15:25 +00:00
{
2020-11-22 15:47:08 +00:00
if ( arc . BeginObject ( " nnexts " ) )
{
2021-08-29 07:27:03 +00:00
# ifdef OLD_SAVEGAME
2020-11-22 15:47:08 +00:00
// the GenDudeArray only contains valid info for kDudeModernCustom and kDudeModernCustomBurning so only save the relevant entries as these are not small.
bool foundsome = false ;
for ( int i = 0 ; i < kMaxSprites ; i + + )
{
2021-09-01 19:53:33 +00:00
if ( activeSprites [ i ] & & ( sprite [ i ] . type = = kDudeModernCustom | | sprite [ i ] . type = = kDudeModernCustomBurning ) )
2020-11-22 15:47:08 +00:00
{
if ( ! foundsome ) arc . BeginArray ( " gendudeextra " ) ;
foundsome = true ;
2021-08-29 07:27:03 +00:00
arc ( nullptr , bloodActors [ i ] . genDudeExtra ) ;
2020-11-22 15:47:08 +00:00
}
}
if ( foundsome ) arc . EndArray ( ) ;
2021-09-01 19:53:33 +00:00
// In compatibility mode write this out as a sparse array sorted by xsprite index.
SPRITEMASS gSpriteMass [ kMaxSprites ] ;
for ( int i = 0 ; i < kMaxSprites ; i + + )
{
int x = sprite [ i ] . extra ;
if ( x < = 0 ) continue ;
gSpriteMass [ x ] = bloodActors [ i ] . spriteMass ;
}
arc . SparseArray ( " spritemass " , gSpriteMass , kMaxSprites , activeXSprites ) ;
for ( int i = 0 ; i < kMaxSprites ; i + + )
{
int x = sprite [ i ] . extra ;
if ( x < = 0 ) continue ;
if ( activeXSprites [ x ] ) bloodActors [ i ] . spriteMass = gSpriteMass [ x ] ;
}
# endif
arc ( " proxyspritescount " , gProxySpritesCount )
2020-11-22 15:47:08 +00:00
. Array ( " proxyspriteslist " , gProxySpritesList , gProxySpritesCount )
( " sightspritescount " , gSightSpritesCount )
. Array ( " sightspriteslist " , gSightSpritesList , gSightSpritesCount )
( " physspritescount " , gPhysSpritesCount )
. Array ( " physspriteslist " , gPhysSpritesList , gPhysSpritesCount )
( " impactspritescount " , gImpactSpritesCount )
. Array ( " impactspriteslist " , gImpactSpritesList , gImpactSpritesCount )
( " eventredirects " , gEventRedirectsUsed )
2021-08-27 16:21:01 +00:00
( " trconditioncount " , gTrackingCondsCount )
. Array ( " trcondition " , gCondition , gTrackingCondsCount )
2020-11-22 15:47:08 +00:00
. EndObject ( ) ;
}
2020-02-11 22:15:25 +00:00
}
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