13638 lines
312 KiB
C
Executable File
13638 lines
312 KiB
C
Executable File
/*
|
|
Copyright (C) 1994-1995 Apogee Software, Ltd.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "rt_def.h"
|
|
#include "rt_sound.h"
|
|
#include "rt_door.h"
|
|
#include "rt_ted.h"
|
|
#include "rt_draw.h"
|
|
#include "watcom.h"
|
|
#include "z_zone.h"
|
|
#include "w_wad.h"
|
|
#include "lumpy.h"
|
|
#include "gmove.h"
|
|
#include "states.h"
|
|
#include "rt_sqrt.h"
|
|
#include "rt_stat.h"
|
|
#include "sprites.h"
|
|
#include "rt_actor.h"
|
|
#include "rt_game.h"
|
|
#include "rt_main.h"
|
|
#include "rt_playr.h"
|
|
#include "rt_util.h"
|
|
#include "rt_rand.h"
|
|
#include "rt_menu.h"
|
|
#include "rt_swift.h"
|
|
#include "_rt_acto.h"
|
|
#include "rt_cfg.h"
|
|
#include "rt_floor.h"
|
|
#include "engine.h"
|
|
#include "develop.h"
|
|
#include "rt_view.h"
|
|
#include "isr.h"
|
|
#include "rt_com.h"
|
|
#include "rt_scale.h"
|
|
#include "modexlib.h"
|
|
#include "rt_net.h"
|
|
#include "rt_msg.h"
|
|
#include "fx_man.h"
|
|
//MED
|
|
#include "memcheck.h"
|
|
|
|
|
|
|
|
|
|
#define SGN(x) (((x) > 0)?(1):(-1))
|
|
|
|
|
|
|
|
#define WILEYBLITZCHANCE 20
|
|
#define GIBSOUND SD_GIBSPLASHSND
|
|
#define ACTORTHUDSND SD_BODYLANDSND
|
|
#define ACTORLANDSND SD_PLAYERLANDSND
|
|
|
|
//========================== Global Variables ===================================================
|
|
|
|
#define SHP(difficulty,ob) (starthitpoints[difficulty][ob->obclass])
|
|
|
|
#define CAP_OSCUROS_HITPOINTS(ob) \
|
|
{ \
|
|
if (ob->hitpoints > (SHP(gamestate.difficulty,ob)<<1)) \
|
|
ob->hitpoints = (SHP(gamestate.difficulty,ob)<<1); \
|
|
}
|
|
|
|
|
|
boolean ludicrousgibs=false;
|
|
|
|
short colheight[15];
|
|
|
|
byte deathshapeoffset[8] = {0,7,7,8,8,9,8,7};
|
|
|
|
unsigned long MAXFUNCTION,MINFUNCTION,MAXSTATE,MINSTATE;
|
|
|
|
objtype *PLAYER0MISSILE;
|
|
objtype *SCREENEYE;
|
|
objtype *FIRSTACTOR,*LASTACTOR;
|
|
|
|
objtype *FIRSTFREE,*LASTFREE;
|
|
objtype *lastactive,*firstactive,**objlist;
|
|
objtype *firstareaactor[NUMAREAS+1],*lastareaactor[NUMAREAS+1];
|
|
int objcount;
|
|
|
|
byte RANDOMACTORTYPE[10];
|
|
|
|
#if (SHAREWARE == 0)
|
|
_2Dpoint SNAKEPATH[512];
|
|
#endif
|
|
misc_stuff mstruct,*MISCVARS = &mstruct;
|
|
int angletodir[ANGLES];
|
|
objtype *new;
|
|
|
|
void *actorat[MAPSIZE][MAPSIZE];
|
|
#if (DEVELOPMENT == 1)
|
|
FILE * williamdebug;
|
|
#endif
|
|
exit_t playstate;
|
|
|
|
void T_SlideDownScreen(objtype*);
|
|
|
|
basic_actor_sounds BAS[NUMCLASSES+3] =
|
|
{{0,0,0,0,0},
|
|
{0,0,0,0,0},
|
|
{0,SD_LOWGUARD1SEESND,SD_LOWGUARDFIRESND,SD_LOWGUARDOUCHSND,SD_LOWGUARD1DIESND},
|
|
{0,SD_HIGHGUARD1SEESND,SD_HIGHGUARDFIRESND,SD_HIGHGUARDOUCHSND,SD_HIGHGUARDDIESND},
|
|
{0,SD_OVERP1SEESND,SD_OVERPFIRESND,SD_OVERPOUCHSND,SD_OVERPDIESND},
|
|
{0,SD_STRIKE1SEESND,SD_STRIKEFIRESND,SD_STRIKEOUCHSND,SD_STRIKEDIESND},
|
|
{0,SD_BLITZ1SEESND,SD_BLITZFIRESND,SD_BLITZOUCHSND,SD_BLITZDIESND},
|
|
{0,SD_ENFORCERSEESND,SD_ENFORCERFIRESND,SD_ENFORCEROUCHSND,SD_ENFORCERDIESND} ,
|
|
{0,SD_MONKSEESND,SD_MONKGRABSND,SD_MONKOUCHSND,SD_MONKDIESND},
|
|
{0,SD_FIREMONKSEESND,SD_FIREMONKFIRESND,SD_FIREMONKOUCHSND,SD_FIREMONKDIESND},
|
|
{0,SD_ROBOTSEESND,SD_ROBOTFIRESND,0,SD_ROBOTDIESND},
|
|
|
|
//bosses
|
|
{SD_DARIANSAY1,SD_DARIANSEESND,SD_DARIANFIRESND,0,SD_DARIANDIESND},
|
|
{SD_KRISTSAY1,SD_KRISTSEESND,SD_KRISTFIRESND,0,SD_KRISTDIESND},
|
|
{0,SD_NMESEESND,SD_NMEFIRE1SND,0,SD_NMEDIESND},
|
|
{SD_DARKMONKSAY1,SD_DARKMONKSEESND,SD_DARKMONKFIRE1SND,0,SD_DARKMONKDIESND},
|
|
{SD_SNAKESAY1,SD_SNAKESEESND,SD_SNAKESPITSND,0,SD_SNAKEDIESND},
|
|
|
|
//specials
|
|
{0,SD_EMPLACEMENTSEESND,SD_EMPLACEMENTFIRESND,0,0},
|
|
{0,SD_ROBOTSEESND,SD_ROBOTFIRESND,0,SD_ROBOTDIESND}, //wallop
|
|
{0,0,0,0,0}, //pillar
|
|
{SD_FIREJETSND,0,0,0,0}, //firejet
|
|
{SD_BLADESPINSND,0,0,0,0}, //blade
|
|
{SD_CYLINDERMOVESND,0,0,0,0}, //crushcol
|
|
{SD_BOULDERROLLSND,0,0,SD_BOULDERHITSND,0}, //boulder
|
|
{SD_SPEARSTABSND,0,0,0,0}, //spear
|
|
{0,0,0,0,0}, //gasgrate
|
|
{SD_SPRINGBOARDSND,0,0,0,0}, //spring
|
|
{0,0,0,0,0}, //shuriken
|
|
{SD_FIREBALLSND,0,0,SD_FIREBALLHITSND,0}, //wallfire
|
|
{0,0,0,0,0}, //net
|
|
{SD_KRISTMINEBEEPSND,0,0,0,0}, //h_mine
|
|
{0,0,0,0,0}, //grenade
|
|
{0,0,0,0,0}, //fireball
|
|
{0,0,0,0,0}, //dmfball
|
|
{0,0,0,0,0}, //bigshuriken
|
|
{0,0,0,0,0}, //missile
|
|
{0,0,0,0,0}, //NMEsaucer
|
|
{0,0,0,0,0}, //dm_weapon
|
|
{0,0,0,0,0}, //dm_heatseek
|
|
{0,0,0,0,0}, //dm_spit
|
|
{SD_MISSILEFLYSND,0,SD_BAZOOKAFIRESND,SD_MISSILEHITSND,0},
|
|
{SD_MISSILEFLYSND,0,SD_FIREBOMBFIRESND,SD_MISSILEHITSND,0},
|
|
{SD_MISSILEFLYSND,0,SD_HEATSEEKFIRESND,SD_MISSILEHITSND,0},
|
|
{SD_MISSILEFLYSND,0,SD_DRUNKFIRESND,SD_MISSILEHITSND,0},
|
|
{SD_FLAMEWALLSND,0,SD_FLAMEWALLFIRESND,SD_FIREHITSND,0},
|
|
{SD_MISSILEFLYSND,0,SD_SPLITFIRESND,SD_MISSILEHITSND,0},
|
|
{SD_GRAVSND,0,SD_GRAVFIRESND,SD_GRAVHITSND,0},
|
|
{SD_GRAVSND,0,SD_GODMODEFIRESND,SD_GRAVHITSND,0}
|
|
|
|
};
|
|
|
|
|
|
//========================== Local Variables ==================================================
|
|
|
|
extern boolean dopefish;
|
|
|
|
|
|
boolean Masterdisk;
|
|
|
|
static objtype *SNAKEHEAD,*SNAKEEND,*PARTICLE_GENERATOR,*EXPLOSIONS;
|
|
#if (SHAREWARE == 0)
|
|
static int OLDTILEX,OLDTILEY;
|
|
#endif
|
|
|
|
|
|
static char *debugstr[] = {
|
|
|
|
"inerttype",
|
|
"player",
|
|
"lowguard",
|
|
"highguard",
|
|
"overpatrol",
|
|
"strikeguard",
|
|
"blitzguard",
|
|
"triadenforcer",
|
|
"deathmonk",
|
|
"dfiremonk",
|
|
"roboguard",
|
|
"b_darian",
|
|
"b_heinrich",
|
|
"b_darkmonk",
|
|
"b_roboboss",
|
|
"b_darksnake",
|
|
"patrolgun",
|
|
"wallop",
|
|
"pillar",
|
|
"firejet",
|
|
"blade",
|
|
"crushcol",
|
|
"boulder",
|
|
"spear",
|
|
"gasgrate",
|
|
"spring",
|
|
"shuriken",
|
|
"wallfire",
|
|
"net",
|
|
"h_mine",
|
|
"grenade",
|
|
"fireball",
|
|
"dmfball",
|
|
"bigshuriken",
|
|
"missile",
|
|
"NMEsaucer",
|
|
"dm_weapon",
|
|
"dm_heatseek",
|
|
"dm_spit",
|
|
"p_bazooka",
|
|
"p_firebomb",
|
|
"p_heatseek",
|
|
"p_drunkmissile",
|
|
"p_firewall",
|
|
"p_splitmissile",
|
|
"p_kes",
|
|
"p_godball",
|
|
"collectorobj"
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int starthitpoints[4][NUMENEMIES+2] =
|
|
|
|
{{0,0,30,35,50,40,45,425,200,200,100,1500,2500,3000,3000,-1,200,2},
|
|
{0,0,40,50,55,50,50,475,250,250,125,2300,3400,4500,3600,-1,250,2},
|
|
{0,0,50,65,60,60,60,525,275,300,150,2400,3600,5000,4500,-1,300,2},
|
|
{0,0,60,80,70,70,75,525,300,350,175,2800,3800,5900,4800,-1,350,2}};
|
|
|
|
|
|
static statobj_t *touchsprite;
|
|
|
|
|
|
static const byte dirdiff[8][8] = {{0,1,2,3,4,3,2,1},{1,0,1,2,3,4,3,2},
|
|
{2,1,0,1,2,3,4,3},{3,2,1,0,1,2,3,4},
|
|
{4,3,2,1,0,1,2,3},{3,4,3,2,1,0,1,2},
|
|
{2,3,4,3,2,1,0,1},{1,2,3,4,3,2,1,0}};
|
|
|
|
static const byte dirorder[8][2] = {{southeast,northeast},{east,north},
|
|
{northeast,northwest},{north,west},
|
|
{northwest,southwest},{west,south},
|
|
{southwest,southeast},{south,east}};
|
|
|
|
#if (SHAREWARE == 0)
|
|
|
|
static const byte dirdiff16[16][16] = {
|
|
{0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1},
|
|
{1,0,1,2,3,4,5,6,7,8,7,6,5,4,3,2},
|
|
{2,1,0,1,2,3,4,5,6,7,8,7,6,5,4,3},
|
|
{3,2,1,0,1,2,3,4,5,6,7,8,7,6,5,4},
|
|
{4,3,2,1,0,1,2,3,4,5,6,7,8,7,6,5},
|
|
{5,4,3,2,1,0,1,2,3,4,5,6,7,8,7,6},
|
|
{6,5,4,3,2,1,0,1,2,3,4,5,6,7,8,7},
|
|
{7,6,5,4,3,2,1,0,1,2,3,4,5,6,7,8},
|
|
{8,7,6,5,4,3,2,1,0,1,2,3,4,5,6,7},
|
|
{7,8,7,6,5,4,3,2,1,0,1,2,3,4,5,6},
|
|
{6,7,8,7,6,5,4,3,2,1,0,1,2,3,4,5},
|
|
{5,6,7,8,7,6,5,4,3,2,1,0,1,2,3,4},
|
|
{4,5,6,7,8,7,6,5,4,3,2,1,0,1,2,3},
|
|
{3,4,5,6,7,8,7,6,5,4,3,2,1,0,1,2},
|
|
{2,3,4,5,6,7,8,7,6,5,4,3,2,1,0,1},
|
|
{1,2,3,4,5,6,7,8,7,6,5,4,3,2,1,0}};
|
|
#endif
|
|
|
|
static const byte dirorder16[16][2] = {
|
|
{15,1} , {0,2}, {1,3}, {2,4},
|
|
{3,5} , {4,6}, {5,7}, {6,8},
|
|
{7,9} , {8,10}, {9,11}, {10,12},
|
|
{11,13}, {12,14}, {13,15}, {14,0}};
|
|
|
|
//static byte opposite16[16] = {8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7};
|
|
|
|
#if (SHAREWARE == 0)
|
|
|
|
static statetype * UPDATE_STATES[NUMSTATES][NUMENEMIES] =
|
|
|
|
{ {&s_lowgrdstand,&s_highgrdstand,&s_opstand,&s_strikestand,
|
|
&s_blitzstand,&s_enforcerstand,&s_dmonkstand,&s_firemonkstand,
|
|
&s_robogrdstand,&s_darianstand,&s_heinrichstand,NULL,
|
|
&s_darkmonkstand,NULL,&s_gunstand,&s_wallstand},
|
|
|
|
{&s_lowgrdpath1,&s_highgrdpath1,&s_oppath1,&s_strikepath1,
|
|
&s_blitzpath1,&s_enforcerpath1,&s_dmonkpath1,&s_firemonkpath1,
|
|
&s_robogrdpath1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,&s_wallpath},
|
|
|
|
{&s_lowgrdcollide,&s_highgrdcollide,&s_opcollide,&s_strikecollide,
|
|
&s_blitzcollide,&s_enforcercollide,&s_dmonkcollide,&s_firemonkcollide,
|
|
&s_robogrdcollide,&s_dariancollide,NULL,NULL,
|
|
NULL,NULL,NULL,&s_wallcollide},
|
|
|
|
{&s_lowgrdcollide2,&s_highgrdcollide2,&s_opcollide2,&s_strikecollide2,
|
|
&s_blitzcollide2,&s_enforcercollide2,&s_dmonkcollide2,&s_firemonkcollide2,
|
|
&s_robogrdcollide2,&s_dariancollide2,NULL,NULL,
|
|
NULL,NULL,NULL,&s_wallcollide},
|
|
|
|
{&s_lowgrdchase1,&s_highgrdchase1,&s_opchase1,&s_strikechase1,
|
|
&s_blitzchase1,&s_enforcerchase1,&s_dmonkchase1,&s_firemonkchase1,
|
|
NULL/*se1*/,&s_darianchase1,&s_heinrichchase,&s_NMEchase,
|
|
&s_darkmonkchase1,NULL,&s_gunstand,&s_wallpath},
|
|
|
|
/*
|
|
{&s_lowgrduse1,&s_highgrduse1,&s_opuse1,&s_strikeuse1,
|
|
&s_blitzuse,&s_enforceruse1,NULL,NULL,
|
|
NULL,&s_darianuse1,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},*/
|
|
{0},
|
|
|
|
{&s_lowgrdshoot1,&s_highgrdshoot1,&s_opshoot1,&s_strikeshoot1,
|
|
&s_blitzshoot1,&s_enforcershoot1,NULL,&s_firemonkcast1,
|
|
&s_robogrdshoot1,&s_darianshoot1,&s_heinrichshoot1,NULL,
|
|
NULL,NULL,&s_gunfire1,&s_wallshoot},
|
|
|
|
{&s_lowgrddie1,&s_highgrddie1,&s_opdie1,&s_strikedie1,
|
|
&s_blitzdie1,&s_enforcerdie1,&s_dmonkdie1,&s_firemonkdie1,
|
|
&s_robogrddie1,&s_dariandie1,&s_heinrichdie1,&s_NMEdie,
|
|
&s_darkmonkdie1,NULL,&s_gundie1,NULL},
|
|
|
|
{0},
|
|
|
|
{NULL,NULL,NULL,&s_strikewait,
|
|
&s_blitzstand,&s_enforcerdie1,&s_dmonkdie1,&s_firemonkdie1,
|
|
&s_robogrddie1,&s_dariandie1,&s_heinrichdie1,NULL,
|
|
&s_darkmonkdie1,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrdcrushed1,&s_highgrdcrushed1,&s_opcrushed1,&s_strikecrushed1,
|
|
&s_blitzcrushed1,&s_enforcercrushed1,&s_dmonkcrushed1,&s_firemonkcrushed1,
|
|
&s_robogrddie1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL}
|
|
|
|
};
|
|
|
|
#else
|
|
|
|
static statetype * UPDATE_STATES[NUMSTATES][NUMENEMIES] =
|
|
|
|
{ {&s_lowgrdstand,&s_highgrdstand,NULL,&s_strikestand,
|
|
&s_blitzstand,&s_enforcerstand,NULL,NULL,
|
|
&s_robogrdstand,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrdpath1,&s_highgrdpath1,NULL,&s_strikepath1,
|
|
&s_blitzpath1,&s_enforcerpath1,NULL,NULL,
|
|
&s_robogrdpath1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrdcollide,&s_highgrdcollide,NULL,&s_strikecollide,
|
|
&s_blitzcollide,&s_enforcercollide,NULL,NULL,
|
|
NULL,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrdcollide2,&s_highgrdcollide2,NULL,&s_strikecollide2,
|
|
&s_blitzcollide2,&s_enforcercollide2,NULL,NULL,
|
|
&s_robogrdcollide2,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrdchase1,&s_highgrdchase1,NULL,&s_strikechase1,
|
|
&s_blitzchase1,&s_enforcerchase1,NULL,NULL,
|
|
NULL,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
/*
|
|
{&s_lowgrduse1,&s_highgrduse1,&s_opuse1,&s_strikeuse1,
|
|
&s_blitzuse,&s_enforceruse1,NULL,NULL,
|
|
NULL,&s_darianuse1,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},*/
|
|
{0},
|
|
|
|
{&s_lowgrdshoot1,&s_highgrdshoot1,NULL,&s_strikeshoot1,
|
|
&s_blitzshoot1,&s_enforcershoot1,NULL,NULL,
|
|
&s_robogrdshoot1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrddie1,&s_highgrddie1,NULL,&s_strikedie1,
|
|
&s_blitzdie1,&s_enforcerdie1,NULL,NULL,
|
|
&s_robogrddie1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{0},
|
|
|
|
{NULL,NULL,NULL,&s_strikewait,
|
|
&s_blitzstand,&s_enforcerdie1,NULL,NULL,
|
|
&s_robogrddie1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL},
|
|
|
|
{&s_lowgrdcrushed1,&s_highgrdcrushed1,NULL,&s_strikecrushed1,
|
|
&s_blitzcrushed1,&s_enforcercrushed1,NULL,NULL,
|
|
&s_robogrddie1,NULL,NULL,NULL,
|
|
NULL,NULL,NULL,NULL}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define TABLE_ACTOR(ob) ((ob->obclass >= lowguardobj) && (ob->obclass <= wallopobj))
|
|
|
|
|
|
void T_Reset(objtype*ob);
|
|
void ApplyGravity(objtype *ob);
|
|
void BeginEnemyHurt(objtype *ob);
|
|
void T_PlayDead(objtype *ob);
|
|
void SpawnFirewall(objtype*ob,int which,int newz);
|
|
void SelectKristChaseDir(objtype*ob);
|
|
void ExplodeStatic(statobj_t*tempstat);
|
|
void AvoidPlayerMissile(objtype*ob);
|
|
int EnvironmentDamage(objtype *ob);
|
|
|
|
static int STOPSPEED = 0x200;
|
|
static int PLAYERFRICTION = 0xe000;
|
|
static int ACTORFRICTION = 0xf000;
|
|
static int DIAGADJUST = 0xb504;
|
|
static boolean MissileSound = true;
|
|
|
|
|
|
|
|
|
|
boolean FirstExplosionState(statetype *state)
|
|
{
|
|
if (DoPanicMapping())
|
|
{
|
|
if (state == &s_altexplosion1)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if ((state == &s_explosion1) ||
|
|
(state == &s_grexplosion1) ||
|
|
(state == &s_staticexplosion1)
|
|
)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SetGibSpeed(int speed)
|
|
{
|
|
MISCVARS->gibspeed = speed;
|
|
}
|
|
|
|
void ResetGibSpeed(void)
|
|
{
|
|
MISCVARS->gibspeed = NORMALGIBSPEED;
|
|
}
|
|
|
|
int ValidAreanumber (int areanumber)
|
|
{ if ((areanumber >=0) && (areanumber <= NUMAREAS))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int GetIndexForState (statetype * state)
|
|
{
|
|
int i;
|
|
|
|
if (state == NULL)
|
|
return -1;
|
|
|
|
for (i=0;i<MAXSTATES;i++)
|
|
{
|
|
if (statetable[i]==state)
|
|
return i;
|
|
}
|
|
Error("Cannot find the state in 'GetIndexForState', state->shapenum = %d\n",state->shapenum);
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
statetype * GetStateForIndex (int index)
|
|
{
|
|
if (index == -1)
|
|
return NULL;
|
|
|
|
return statetable[index];
|
|
}
|
|
|
|
|
|
statobj_t* GetStaticForIndex(int index)
|
|
{statobj_t* temp;
|
|
|
|
for(temp=FIRSTSTAT;temp;temp=temp->statnext)
|
|
if (index == temp->whichstat)
|
|
return temp;
|
|
|
|
Error("Cannot find the static in 'GetStaticForIndex', statindex %d\n",index);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SaveActors(byte **buffer,int*size)
|
|
{objtype*temp,*tact;
|
|
saved_actor_type dummy;
|
|
byte*tptr;
|
|
int actorcount;
|
|
|
|
|
|
for(actorcount=0,temp=FIRSTACTOR;temp;temp=temp->next)
|
|
temp->whichactor = actorcount++;
|
|
|
|
|
|
|
|
*size = sizeof(int) + sizeof(numplayers) + sizeof(misc_stuff) + objcount*sizeof(saved_actor_type);
|
|
*buffer = (byte*)SafeMalloc(*size);
|
|
tptr = *buffer;
|
|
|
|
memcpy(tptr,MISCVARS,sizeof(misc_stuff));
|
|
tptr += sizeof(misc_stuff);
|
|
|
|
memcpy(tptr,&numplayers,sizeof(numplayers));
|
|
tptr += sizeof(numplayers);
|
|
|
|
memcpy(tptr,&consoleplayer,sizeof(consoleplayer));
|
|
tptr += sizeof(consoleplayer);
|
|
|
|
for(temp=FIRSTACTOR;temp;temp=temp->next)
|
|
{dummy.x = temp->x;
|
|
dummy.y = temp->y;
|
|
dummy.z = temp->z;
|
|
dummy.flags = temp->flags;
|
|
dummy.areanumber = temp->areanumber;
|
|
//dummy.whichactor = temp->whichactor;
|
|
dummy.hitpoints = temp->hitpoints;
|
|
dummy.ticcount = temp->ticcount;
|
|
dummy.obclass = (byte)(temp->obclass);
|
|
dummy.stateindex = GetIndexForState(temp->state);
|
|
dummy.shapeoffset = temp->shapeoffset;
|
|
dummy.dirchoosetime = temp->dirchoosetime;
|
|
dummy.door_to_open = temp->door_to_open;
|
|
dummy.targetx = temp->targettilex;
|
|
dummy.targety = temp->targettiley;
|
|
dummy.dir = (signed char)temp->dir;
|
|
dummy.angle = temp->angle;
|
|
dummy.yzangle = temp->yzangle;
|
|
dummy.speed = temp->speed;
|
|
dummy.momentumx = temp->momentumx;
|
|
dummy.momentumy = temp->momentumy;
|
|
dummy.momentumz = temp->momentumz;
|
|
|
|
dummy.temp1 = temp->temp1;
|
|
dummy.temp2 = temp->temp2;
|
|
dummy.temp3 = temp->temp3;
|
|
if (temp->whatever)
|
|
{/*if ((temp->flags & FL_USE) && (temp!=player))
|
|
{dummy.whateverindex = (GetIndexForState((statetype*)(temp->whatever))|SG_PSTATE);
|
|
if ((dummy.whateverindex < 0) && (dummy.whateverindex != -1))
|
|
Error("Bad actor whatever save value of %d\n",dummy.whateverindex);
|
|
}
|
|
else*/
|
|
{tact = (objtype*)(temp->whatever);
|
|
if (tact->which == ACTOR)
|
|
dummy.whateverindex = tact->whichactor;
|
|
else
|
|
{statobj_t *tstat;
|
|
|
|
tstat = (statobj_t*)(temp->whatever);
|
|
dummy.whateverindex = (tstat->whichstat|SG_PSTAT);
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
dummy.whateverindex = -1;
|
|
|
|
|
|
if (temp->target)
|
|
{tact = (objtype*)(temp->target);
|
|
if (tact->which == ACTOR)
|
|
{dummy.targetindex = tact->whichactor;
|
|
Debug("\nsave actor %d, type %d has target %d",temp->whichactor,temp->obclass,tact->whichactor);
|
|
}
|
|
else if (tact->which == SPRITE)
|
|
{statobj_t *tstat;
|
|
|
|
tstat = (statobj_t*)(temp->target);
|
|
dummy.targetindex = (tstat->whichstat|SG_PSTAT);
|
|
}
|
|
else // It must be a push wall, and we don't save that
|
|
dummy.targetindex=-1;
|
|
}
|
|
else
|
|
dummy.targetindex = -1;
|
|
|
|
|
|
memcpy(tptr,&(dummy.x),sizeof(saved_actor_type));
|
|
tptr += sizeof(saved_actor_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadActors(byte *buffer,int size)
|
|
{
|
|
int numactors,i,playerindex;
|
|
saved_actor_type dummy;
|
|
objtype *temp;
|
|
short *targetindices,*whateverindices;
|
|
|
|
InitActorList();
|
|
|
|
memcpy(MISCVARS,buffer,sizeof(misc_stuff));
|
|
buffer += sizeof(misc_stuff);
|
|
|
|
memcpy(&numplayers,buffer,sizeof(numplayers));
|
|
buffer += sizeof(numplayers);
|
|
|
|
memcpy(&playerindex,buffer,sizeof(playerindex));
|
|
buffer += sizeof(playerindex);
|
|
|
|
size -= (sizeof(misc_stuff)+sizeof(numplayers)+sizeof(playerindex));
|
|
numactors = size/sizeof(saved_actor_type);
|
|
|
|
|
|
objlist = (objtype**)SafeMalloc(numactors*sizeof(objtype*));
|
|
targetindices = (short*)SafeMalloc(numactors*sizeof(short));
|
|
whateverindices = (short*)SafeMalloc(numactors*sizeof(short));
|
|
|
|
for(i=0;i<numactors;i++)
|
|
{
|
|
targetindices[i] = 0;
|
|
whateverindices[i] = 0;
|
|
objlist[i] = NULL;
|
|
}
|
|
|
|
|
|
for(i=0;i<numactors;i++)
|
|
{
|
|
GetNewActor();
|
|
objlist[i] = new;
|
|
if (i < numplayers)
|
|
{
|
|
PLAYER[i]=new;
|
|
if (i==playerindex)
|
|
player=new;
|
|
}
|
|
|
|
memcpy(&(dummy.x),buffer,sizeof(saved_actor_type));
|
|
|
|
//new->x = dummy.x;
|
|
//new->y = dummy.y;
|
|
SetFinePosition(new,dummy.x,dummy.y);
|
|
SetVisiblePosition(new,dummy.x,dummy.y);
|
|
new->z = dummy.z;
|
|
new->flags = dummy.flags;
|
|
new->hitpoints = dummy.hitpoints;
|
|
new->ticcount = dummy.ticcount;
|
|
new->shapeoffset = dummy.shapeoffset;
|
|
new->obclass = (classtype)(dummy.obclass);
|
|
|
|
|
|
new->state = GetStateForIndex(dummy.stateindex);
|
|
if (new->state == &s_superparticles)
|
|
PARTICLE_GENERATOR = new;
|
|
else if
|
|
(new->state->think == T_SlideDownScreen)
|
|
SCREENEYE = new;
|
|
new->dirchoosetime = dummy.dirchoosetime;
|
|
new->door_to_open = dummy.door_to_open;
|
|
new->targettilex = dummy.targetx;
|
|
new->targettiley = dummy.targety;
|
|
new->dir = (dirtype)(dummy.dir);
|
|
new->angle = dummy.angle;
|
|
new->yzangle = dummy.yzangle;
|
|
new->speed = dummy.speed;
|
|
new->momentumx = dummy.momentumx;
|
|
new->momentumy = dummy.momentumy;
|
|
new->momentumz = dummy.momentumz;
|
|
new->temp1 = dummy.temp1;
|
|
new->temp2 = dummy.temp2;
|
|
new->temp3 = dummy.temp3;
|
|
if (dummy.whateverindex == -1)
|
|
new->whatever = NULL;
|
|
|
|
else if (dummy.whateverindex & SG_PSTAT)
|
|
new->whatever = GetStaticForIndex(dummy.whateverindex & ~SG_PSTAT);
|
|
else
|
|
whateverindices[i] = dummy.whateverindex+1;
|
|
|
|
|
|
if (dummy.targetindex == -1)
|
|
new->target = NULL;
|
|
else if (dummy.targetindex & SG_PSTAT)
|
|
new->target = GetStaticForIndex(dummy.targetindex & ~SG_PSTAT);
|
|
else
|
|
{
|
|
targetindices[i] = dummy.targetindex+1;
|
|
Debug("\nload actor %d, type %d has target %d",i,new->obclass,dummy.targetindex);
|
|
}
|
|
|
|
|
|
new->areanumber = dummy.areanumber;
|
|
new->shapenum = new->state->shapenum + new->shapeoffset;
|
|
new->which = ACTOR;
|
|
if (new->flags & FL_ABP)
|
|
MakeActive(new);
|
|
if (new->obclass != inertobj)
|
|
MakeLastInArea(new);
|
|
|
|
if (!(new->flags & (FL_NEVERMARK|FL_NONMARK)))
|
|
actorat[new->tilex][new->tiley] = new;
|
|
|
|
PreCacheActor(new->obclass,-1);
|
|
buffer += sizeof(saved_actor_type);
|
|
}
|
|
|
|
|
|
// find unique links between actors,
|
|
// searching list AFTER all have been spawned
|
|
|
|
for(i=0;i<numactors;i++)
|
|
{temp=objlist[i];
|
|
if (whateverindices[i])
|
|
temp->whatever = objlist[whateverindices[i]-1];
|
|
if (targetindices[i])
|
|
temp->target = objlist[targetindices[i]-1];
|
|
}
|
|
|
|
|
|
for(temp=FIRSTACTOR;temp;temp=temp->next)
|
|
{if (temp->obclass == b_darksnakeobj)
|
|
{if (!SNAKEHEAD)
|
|
SNAKEHEAD = temp;
|
|
else if (!temp->whatever)
|
|
SNAKEEND = temp;
|
|
}
|
|
|
|
}
|
|
|
|
if (SNAKEHEAD)
|
|
for(temp=FIRSTACTOR;temp;temp=temp->next)
|
|
{if (temp->state == &s_megaexplosions)
|
|
EXPLOSIONS = temp;
|
|
}
|
|
|
|
//SafeFree(objlist);
|
|
SafeFree(targetindices);
|
|
SafeFree(whateverindices);
|
|
|
|
}
|
|
|
|
|
|
|
|
int RandomSign(void)
|
|
{
|
|
if (GameRandomNumber("random sign",0) < 128)
|
|
return -1;
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
void AddToFreeList(objtype*ob)
|
|
{ if (!FIRSTFREE)
|
|
FIRSTFREE = ob;
|
|
else
|
|
{ob->prev = LASTFREE;
|
|
LASTFREE->next = ob;
|
|
}
|
|
LASTFREE = ob;
|
|
|
|
}
|
|
|
|
void RemoveFromFreeList(objtype*ob)
|
|
{
|
|
if (ob == LASTFREE)
|
|
LASTFREE = ob->prev;
|
|
else
|
|
ob->next->prev = ob->prev;
|
|
|
|
if (ob == FIRSTFREE)
|
|
FIRSTFREE = ob->next;
|
|
else
|
|
ob->prev->next = ob->next;
|
|
|
|
ob->prev = NULL;
|
|
ob->next = NULL;
|
|
|
|
}
|
|
|
|
|
|
void MakeActive(objtype *ob)
|
|
{if ((ob == firstactive) || (ob->prevactive) || (ob->nextactive))
|
|
{
|
|
SoftError("\ndouble make active try");
|
|
//AddEndGameCommand ();
|
|
return;
|
|
}
|
|
|
|
if (!firstactive)
|
|
firstactive = ob;
|
|
else
|
|
{ob->prevactive = lastactive;
|
|
lastactive->nextactive = ob;
|
|
}
|
|
lastactive = ob;
|
|
|
|
#if ((DEVELOPMENT == 1))
|
|
#if ((LOADSAVETEST == 1))
|
|
if (!lastactive)
|
|
Debug("\nlastactive = NULL !");
|
|
else
|
|
Debug("\nlastactive = %8x",lastactive);
|
|
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void MakeLastInArea(objtype *ob)
|
|
{
|
|
if (!ValidAreanumber(ob->areanumber))
|
|
Error("\n ob type %s at %d,%d has illegal areanumber of %d",
|
|
debugstr[ob->obclass],ob->tilex,ob->tiley,ob->areanumber);
|
|
|
|
|
|
if ((ob == firstareaactor[ob->areanumber]) || (ob->previnarea) || (ob->nextinarea))
|
|
{
|
|
SoftError("\ndouble make last in area try");
|
|
//AddEndGameCommand ();
|
|
return;
|
|
}
|
|
if (!firstareaactor[ob->areanumber])
|
|
firstareaactor[ob->areanumber] = ob;
|
|
else
|
|
{ob->previnarea = lastareaactor[ob->areanumber];
|
|
lastareaactor[ob->areanumber]->nextinarea = ob;
|
|
}
|
|
lastareaactor[ob->areanumber] = ob;
|
|
}
|
|
|
|
|
|
|
|
void RemoveFromArea(objtype*ob)
|
|
{
|
|
if (!((ob == firstareaactor[ob->areanumber]) || (ob->previnarea) || (ob->nextinarea)))
|
|
{
|
|
SoftError("\ndouble remove from area try");
|
|
//AddEndGameCommand ();
|
|
return;
|
|
}
|
|
|
|
if (ob == lastareaactor[ob->areanumber]) // remove from master list
|
|
lastareaactor[ob->areanumber] = ob->previnarea;
|
|
else
|
|
ob->nextinarea->previnarea = ob->previnarea;
|
|
|
|
if (ob == firstareaactor[ob->areanumber])
|
|
firstareaactor[ob->areanumber] = ob->nextinarea;
|
|
else
|
|
ob->previnarea->nextinarea = ob->nextinarea;
|
|
|
|
ob->previnarea = NULL;
|
|
ob->nextinarea = NULL;
|
|
}
|
|
|
|
|
|
void MakeInactive(objtype*ob)
|
|
{
|
|
if (!ACTIVE(ob))
|
|
// if (!((ob == firstactive) || (ob->prevactive) || (ob->nextactive)))
|
|
{
|
|
SoftError("\n trying to remove inactive object");
|
|
//AddEndGameCommand ();
|
|
return;
|
|
}
|
|
|
|
//if (ob->flags & FL_ABP)
|
|
{
|
|
|
|
if (ob == lastactive) // remove from master list
|
|
lastactive = ob->prevactive;
|
|
else
|
|
ob->nextactive->prevactive = ob->prevactive;
|
|
|
|
if (ob == firstactive)
|
|
firstactive = ob->nextactive;
|
|
else
|
|
ob->prevactive->nextactive = ob->nextactive;
|
|
|
|
|
|
ob->prevactive = NULL;
|
|
ob->nextactive = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void A_Steal(objtype*ob)
|
|
{
|
|
int dx,dy,dz;
|
|
|
|
ActorMovement(ob);
|
|
|
|
dx = abs(ob->x - PLAYER[0]->x);
|
|
dy = abs(ob->y - PLAYER[0]->y);
|
|
dz = abs(ob->z - PLAYER[0]->z);
|
|
|
|
if ((dx > TOUCHDIST) || (dy > TOUCHDIST) || (dz > (TOUCHDIST >> 10)))
|
|
{
|
|
NewState(ob,&s_blitzchase1);
|
|
return;
|
|
}
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
SD_PlaySoundRTP(SD_BLITZSTEALSND,ob->x,ob->y);
|
|
if (PLAYER[0]->flags & FL_GASMASK)
|
|
{
|
|
PLAYER[0]->flags &= ~FL_GASMASK;
|
|
PLAYERSTATE[0].protectiontime = 1;
|
|
ob->temp3 = stat_gasmask;
|
|
GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true);
|
|
|
|
}
|
|
else if(PLAYER[0]->flags & FL_BPV)
|
|
{
|
|
PLAYER[0]->flags &= ~FL_BPV;
|
|
PLAYERSTATE[0].protectiontime = 1;
|
|
ob->temp3 = stat_bulletproof;
|
|
GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true);
|
|
|
|
}
|
|
else if(PLAYER[0]->flags & FL_AV)
|
|
{
|
|
PLAYER[0]->flags &= ~FL_AV;
|
|
PLAYERSTATE[0].protectiontime = 1;
|
|
ob->temp3 = stat_asbesto;
|
|
GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true);
|
|
|
|
}
|
|
else if (PLAYERSTATE[0].missileweapon != -1)
|
|
{
|
|
NewState(PLAYER[0],&s_player);
|
|
PLAYERSTATE[0].attackframe = PLAYERSTATE[0].weaponframe = 0;
|
|
PLAYERSTATE[0].new_weapon = PLAYERSTATE[0].bulletweapon;
|
|
ob->temp3 = GetItemForWeapon(PLAYERSTATE[0].missileweapon);
|
|
ob->temp2 = PLAYERSTATE[0].ammo;
|
|
//ob->temp1 = oldpolltime;
|
|
PLAYERSTATE[0].ammo = -1;
|
|
|
|
if (PLAYERSTATE[0].weapon == PLAYERSTATE[0].missileweapon)
|
|
PLAYERSTATE[0].weapondowntics = WEAPONS[PLAYERSTATE[0].weapon].screenheight/GMOVE;
|
|
PLAYERSTATE[0].missileweapon = -1;
|
|
|
|
if ( SHOW_BOTTOM_STATUS_BAR() )
|
|
DrawBarAmmo (false);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void FindAddresses(void)
|
|
{
|
|
int i;
|
|
unsigned long tstate,tfunct;
|
|
|
|
MINFUNCTION = -1l;
|
|
MAXFUNCTION = 0x00000000;
|
|
MINSTATE = -1l;
|
|
MAXSTATE = 0x00000000;
|
|
|
|
for(i=0;i<MAXSTATES;i++)
|
|
{
|
|
tstate = (unsigned long)(statetable[i]);
|
|
if (tstate < MINSTATE)
|
|
MINSTATE = tstate;
|
|
|
|
if (tstate > MAXSTATE)
|
|
MAXSTATE = tstate;
|
|
if (statetable[i]!=NULL)
|
|
{
|
|
tfunct = (unsigned long)(statetable[i]->think);
|
|
if (tfunct < MINFUNCTION)
|
|
MINFUNCTION = tfunct;
|
|
|
|
if (tfunct > MAXFUNCTION)
|
|
MAXFUNCTION = tfunct;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckBounds(objtype*ob)
|
|
{
|
|
unsigned long tstate,tfunct;
|
|
|
|
tstate = (unsigned long)(ob->state);
|
|
tfunct = (unsigned long)(ob->state->think);
|
|
|
|
if ((tfunct < MINFUNCTION) || (tfunct > MAXFUNCTION) ||
|
|
(tstate < MINSTATE) || (tstate > MAXSTATE))
|
|
{
|
|
if (tfunct < MINFUNCTION)
|
|
Error("%s has thinking function less than MINFUNCTION",debugstr[ob->obclass]);
|
|
|
|
else if (tfunct > MAXFUNCTION)
|
|
Error("%s has thinking function greater than MAXFUNCTION",debugstr[ob->obclass]);
|
|
|
|
if (tstate < MINSTATE)
|
|
Error("%s has state less than MINSTATE",debugstr[ob->obclass]);
|
|
|
|
else if (tstate > MAXSTATE)
|
|
Error("%s has state greater than MAXSTATE",debugstr[ob->obclass]);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= DoActor
|
|
=
|
|
=====================
|
|
*/
|
|
|
|
void DoActor (objtype *ob)
|
|
{
|
|
void (*think)(objtype *);
|
|
int door;
|
|
|
|
|
|
// for(i=0;i<tics;i++)
|
|
// {
|
|
|
|
#if (BNACRASHPREVENT == 1)//
|
|
if (ob->state == 0){return;}
|
|
#endif
|
|
ApplyGravity(ob);
|
|
M_CheckDoor(ob);
|
|
M_CheckBossSounds(ob);
|
|
if ((ob->obclass >= b_darianobj) &&
|
|
(ob->obclass < b_darksnakeobj) &&
|
|
MISCVARS->REDTIME
|
|
)
|
|
{
|
|
MISCVARS->REDTIME --;
|
|
MISCVARS->redindex = (MISCVARS->REDTIME & 15);
|
|
}
|
|
|
|
if (ob->obclass == playerobj)
|
|
ControlPlayerObj(ob);
|
|
|
|
think = ob->state->think;
|
|
if (think)
|
|
{
|
|
//CheckBounds(ob);
|
|
think (ob);
|
|
|
|
if (!ob->state)
|
|
{
|
|
RemoveObj (ob);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ob->ticcount)
|
|
ob->ticcount --;
|
|
|
|
else
|
|
{
|
|
if (!(ob->state->next))
|
|
{
|
|
RemoveObj (ob);
|
|
return;
|
|
}
|
|
else
|
|
NewState(ob,ob->state->next);
|
|
}
|
|
|
|
|
|
if (ob->flags&FL_NEVERMARK)
|
|
return;
|
|
|
|
if ((ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley])
|
|
return;
|
|
|
|
actorat[ob->tilex][ob->tiley] = ob;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ApplyGravity(objtype *ob)
|
|
{
|
|
int oldmomentumz;
|
|
|
|
if (((ob->momentumz) || (ob->z != nominalheight)) &&
|
|
(ob->obclass > playerobj) &&
|
|
((ob->obclass <= roboguardobj) || (ob->obclass == collectorobj) ||
|
|
(ob->obclass == b_heinrichobj)) &&
|
|
(ob->state->think != T_Stand)
|
|
)
|
|
{
|
|
ob->z += (ob->momentumz>>16);
|
|
ob->momentumz += GRAVITY;
|
|
if (ob->z >= nominalheight)
|
|
{
|
|
ob->z = nominalheight;
|
|
oldmomentumz = ob->momentumz;
|
|
ob->momentumz = 0;
|
|
if (oldmomentumz > 2*GRAVITY)
|
|
{
|
|
if (ob->flags & FL_DYING)
|
|
SD_PlaySoundRTP(ACTORTHUDSND,ob->x,ob->y);
|
|
else
|
|
{
|
|
int oldviolence = gamestate.violence;
|
|
|
|
SD_PlaySoundRTP(ACTORLANDSND,ob->x,ob->y);
|
|
gamestate.violence = vl_low;
|
|
BeginEnemyHurt(ob);
|
|
gamestate.violence = oldviolence;
|
|
}
|
|
}
|
|
if (ob->flags&FL_FALLINGOBJECT)
|
|
{
|
|
RemoveObj(ob);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===================
|
|
=
|
|
= NewState
|
|
=
|
|
= Changes ob to a new state, setting ticcount to the max for that state
|
|
=
|
|
===================
|
|
*/
|
|
|
|
void NewState (objtype *ob, statetype *newstate)
|
|
{
|
|
if (DoPanicMapping() &&
|
|
((newstate == &s_explosion1) ||
|
|
(newstate == &s_grexplosion1) ||
|
|
(newstate == &s_staticexplosion1)
|
|
)
|
|
)
|
|
ob->state = &s_altexplosion1;
|
|
else{
|
|
#if (BNACRASHPREVENT == 1)//crashed here when oscuro and larves were all killed
|
|
if (ob == 0){return;}
|
|
#endif
|
|
ob->state = newstate;
|
|
}
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
#if (BNACRASHPREVENT == 1)
|
|
if (ob->state == 0){return;}
|
|
#endif
|
|
|
|
ob->ticcount = (ob->state->tictime>>1);
|
|
ob->shapenum = ob->state->shapenum + ob->shapeoffset;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=========================
|
|
=
|
|
= InitActorList
|
|
=
|
|
= Call to clear out the actor object lists returning them all to the free
|
|
= list. Allocates a special spot for the player.
|
|
=
|
|
=========================
|
|
*/
|
|
|
|
|
|
|
|
void InitActorList (void)
|
|
{
|
|
//====== NETWORK STUFF =======================================
|
|
memset(&DEADPLAYER[0],0,sizeof(DEADPLAYER));
|
|
NUMDEAD = 0;
|
|
|
|
|
|
|
|
//======= NULLIFY GLOBAL POINTERS ============================
|
|
|
|
LASTACTOR=FIRSTACTOR=NULL;
|
|
FIRSTFREE = LASTFREE = NULL;
|
|
firstactive = lastactive = NULL;
|
|
memset(firstareaactor,0,sizeof(firstareaactor));
|
|
memset(lastareaactor,0,sizeof(lastareaactor));
|
|
NUMSPAWNLOCATIONS = 0;
|
|
|
|
|
|
PARTICLE_GENERATOR = NULL;
|
|
EXPLOSIONS = NULL;
|
|
SNAKEEND=SNAKEHEAD=NULL;
|
|
SCREENEYE = NULL;
|
|
PLAYER0MISSILE = NULL;
|
|
|
|
//============================================================
|
|
|
|
objcount = 0;
|
|
memset(MISCVARS,0,sizeof(misc_stuff));
|
|
MISCVARS->gibgravity = -1;
|
|
MISCVARS->gibspeed = NORMALGIBSPEED;
|
|
|
|
memset(&RANDOMACTORTYPE[0],0,sizeof(RANDOMACTORTYPE));
|
|
FindAddresses();
|
|
MissileSound = true;
|
|
Masterdisk = false;
|
|
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
/*
|
|
=========================
|
|
=
|
|
= GetNewActor
|
|
=
|
|
= Sets the global variable new to point to a free spot in objlist.
|
|
= The free spot is inserted at the end of the liked list
|
|
=
|
|
= When the object list is full, the caller can either have it bomb out ot
|
|
= return a dummy object pointer that will never get used
|
|
=
|
|
=========================
|
|
*/
|
|
|
|
void GetNewActor (void)
|
|
{
|
|
objtype *temp;
|
|
|
|
if (!FIRSTFREE)
|
|
{
|
|
temp = (objtype*)Z_LevelMalloc(sizeof(objtype),PU_LEVELSTRUCT,NULL);
|
|
//SoftError("\nMalloc-ing actor");
|
|
//if (insetupgame)
|
|
// SoftError("in setup");
|
|
}
|
|
|
|
else
|
|
{
|
|
temp = LASTFREE;
|
|
//SoftError("\nfree actor available");
|
|
RemoveFromFreeList(LASTFREE);
|
|
}
|
|
|
|
if (temp)
|
|
{
|
|
new = temp;
|
|
memset(new,0,sizeof(*new));
|
|
|
|
if (FIRSTACTOR)
|
|
{
|
|
new->prev = LASTACTOR;
|
|
LASTACTOR->next = new;
|
|
}
|
|
else
|
|
FIRSTACTOR = new;
|
|
LASTACTOR = new;
|
|
|
|
new->door_to_open = -1;
|
|
new->soundhandle = -1;
|
|
objcount ++;
|
|
}
|
|
else
|
|
Error("Z_LevelMalloc failed in GetNewActor");
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
/*
|
|
=========================
|
|
=
|
|
= RemoveObj
|
|
=
|
|
= Add the given object back into the free list, and unlink it from it's
|
|
= neighbors
|
|
=
|
|
=========================
|
|
*/
|
|
|
|
|
|
void RemoveObj (objtype *gone)
|
|
{
|
|
if (gone == PLAYER[0])
|
|
Error ("RemoveObj: Tried to remove the player!");
|
|
|
|
gone->state=NULL;
|
|
|
|
MakeInactive(gone);
|
|
|
|
if (gone->obclass!=inertobj) {
|
|
if (ValidAreanumber(gone->areanumber))
|
|
RemoveFromArea(gone);
|
|
else
|
|
Error("tried to remove an instance of %s with invalid areanumber %d",debugstr[gone->obclass],gone->areanumber);
|
|
}
|
|
|
|
if (gone == LASTACTOR)
|
|
LASTACTOR = gone->prev;
|
|
else
|
|
gone->next->prev = gone->prev;
|
|
|
|
if (gone == FIRSTACTOR)
|
|
FIRSTACTOR = gone->next;
|
|
else
|
|
gone->prev->next = gone->next;
|
|
|
|
if (gone == EXPLOSIONS)
|
|
EXPLOSIONS = NULL;
|
|
gone->next = NULL;
|
|
gone->prev = NULL;
|
|
// SoftError("\nremoving instance of %s",debugstr[gone->obclass]);
|
|
if (actorat[gone->tilex][gone->tiley] == (void*)gone)
|
|
actorat[gone->tilex][gone->tiley] = NULL;
|
|
|
|
gone->flags |= FL_NEVERMARK;
|
|
|
|
if (gone->flags & FL_TARGET)
|
|
UnTargetActor(gone);
|
|
|
|
//Add_To_Delete_Array(gone);
|
|
//Z_Free(gone);
|
|
AddToFreeList(gone);
|
|
|
|
objcount--;
|
|
}
|
|
|
|
|
|
//============== World Physics Model Functions =========================
|
|
|
|
|
|
void ParseMomentum(objtype *ob,int angle)
|
|
{
|
|
ob->momentumx += FixedMul(ob->speed,costable[angle]);
|
|
ob->momentumy -= FixedMul(ob->speed,sintable[angle]);
|
|
}
|
|
|
|
void Set_3D_Momenta(objtype *ob, int speed, int theta, int phi)
|
|
{
|
|
int _2Ddiag;
|
|
|
|
ob->momentumz = -FixedMul(speed,sintable[phi]);
|
|
_2Ddiag = FixedMul(speed,costable[phi]);
|
|
ob->momentumx = FixedMul(_2Ddiag,costable[theta]);
|
|
ob->momentumy = -FixedMul(_2Ddiag,sintable[theta]);
|
|
|
|
|
|
}
|
|
|
|
|
|
int AngleBetween(objtype *source,objtype*target)
|
|
{
|
|
int dx,dy;
|
|
|
|
dx = target->x - source->x;
|
|
dy = source->y - target->y;
|
|
return (atan2_appx(dx,dy));
|
|
}
|
|
|
|
|
|
void GetMomenta(objtype *target, objtype *source, int *newmomx,
|
|
int *newmomy, int *newmomz, int magnitude
|
|
)
|
|
{
|
|
int angle,dx,dy,dz,yzangle,xydist,_2Ddiag;
|
|
|
|
dx = target->x - source->x;
|
|
dy = source->y - target->y;
|
|
dz = source->z - target->z;
|
|
xydist = FindDistance(dx,dy);
|
|
angle = atan2_appx(dx,dy);
|
|
yzangle = atan2_appx(xydist,(dz<<10));
|
|
_2Ddiag = FixedMul(magnitude,costable[yzangle]);
|
|
|
|
*newmomz = -FixedMul(magnitude,sintable[yzangle]);
|
|
*newmomx = FixedMul(_2Ddiag,costable[angle]);
|
|
*newmomy = -FixedMul(_2Ddiag,sintable[angle]);
|
|
}
|
|
|
|
//=======================================================================
|
|
|
|
|
|
|
|
|
|
|
|
void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state, classtype which)
|
|
{
|
|
int newarea;
|
|
|
|
GetNewActor ();
|
|
new->obclass = which;
|
|
SetTilePosition(new,tilex,tiley);
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
new->dir = nodir;
|
|
new->which = ACTOR;
|
|
if (FirstExplosionState(state))
|
|
new->flags |= (FL_NEVERMARK|FL_NOFRICTION);
|
|
|
|
if ((which != inertobj) && (which != diskobj))
|
|
actorat[tilex][tiley] = new;
|
|
|
|
newarea = AREANUMBER(tilex,tiley);
|
|
if ((which <= springobj) && (which != inertobj))
|
|
{
|
|
if (ValidAreanumber(newarea))
|
|
new->areanumber = newarea;
|
|
else
|
|
Error("illegal initial areanumber of %d for actor type %s"
|
|
"trying to spawn at %d, %d",newarea,debugstr[which],tilex,tiley);
|
|
}
|
|
else
|
|
new->areanumber = newarea;
|
|
|
|
if ((which != inertobj) && (!Masterdisk))
|
|
MakeLastInArea(new);
|
|
NewState(new,state);
|
|
new->z = nominalheight;
|
|
if (which==springobj)
|
|
new->z+=2;
|
|
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
|
|
void ConsiderAlternateActor(objtype *ob,classtype which)
|
|
{
|
|
if (((which >= lowguardobj) && (which <= blitzguardobj)) ||
|
|
(which == dfiremonkobj))
|
|
{if (GameRandomNumber("SpawnStand",which) < 128)
|
|
{switch(which)
|
|
{case lowguardobj:
|
|
ob->shapeoffset = W_GetNumForName("MARSHOO1") -
|
|
W_GetNumForName("LWGSHOO1");
|
|
break;
|
|
case highguardobj:
|
|
ob->shapeoffset = W_GetNumForName("HIGSHOO1") -
|
|
W_GetNumForName("HG2SHOO1");
|
|
break;
|
|
case overpatrolobj:
|
|
ob->shapeoffset = W_GetNumForName("PATSHOO1") -
|
|
W_GetNumForName("OBPSHOO1");
|
|
break;
|
|
case strikeguardobj:
|
|
ob->shapeoffset = W_GetNumForName("XYGSHOO1") -
|
|
W_GetNumForName("ANGSHOO1");
|
|
break;
|
|
/*case blitzguardobj:
|
|
altstartlabel = "WIGSHOO1";
|
|
new->shapeoffset = 80;
|
|
break;*/
|
|
case dfiremonkobj:
|
|
ob->shapeoffset = W_GetNumForName("MRKKSH1") -
|
|
W_GetNumForName("ALLKSH1");
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//if (new->shapeoffset)
|
|
// {if (W_CheckNumForName(altstartlabel) == -1)
|
|
// new->shapeoffset = 0;
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
===================
|
|
=
|
|
= StandardEnemyInit
|
|
=
|
|
===================
|
|
*/
|
|
|
|
void StandardEnemyInit(objtype *ob,int dir)
|
|
{
|
|
int zoffset;
|
|
|
|
if ((ob->obclass == deathmonkobj) || (ob->obclass == dfiremonkobj))
|
|
ob->temp2 = DRAINTIME;
|
|
|
|
else if ((ob->obclass == highguardobj) || (ob->obclass == triadenforcerobj))
|
|
ob->flags |= FL_HASAUTO;
|
|
|
|
ob->hitpoints = starthitpoints[gamestate.difficulty][ob->obclass];
|
|
ob->dir = dir*2;
|
|
ob->flags |= (FL_SHOOTABLE|FL_BLOCK);
|
|
ob->speed = ENEMYRUNSPEED;
|
|
ob->dirchoosetime = 0;
|
|
ob->door_to_open = -1;
|
|
|
|
zoffset = MAPSPOT(ob->tilex,ob->tiley,2);
|
|
if ((zoffset&0xff00)==0xb000)
|
|
Set_NewZ_to_MapValue(&(ob->z),zoffset,"standard enemy",ob->tilex,ob->tiley);
|
|
else
|
|
ob->z = PlatformHeight(ob->tilex,ob->tiley);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConsiderOutfittingBlitzguard(objtype *ob)
|
|
{
|
|
if ((GameRandomNumber("wiley blitzguard",0) < WILEYBLITZCHANCE) &&
|
|
(gamestate.difficulty >= gd_medium)
|
|
)
|
|
{
|
|
ob->temp3 = stat_bazooka;
|
|
ob->temp2 = 3;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= SpawnStand
|
|
=
|
|
===============
|
|
*/
|
|
|
|
|
|
void SpawnStand (classtype which, int tilex, int tiley, int dir, int ambush)
|
|
{statetype *temp;
|
|
|
|
#if (SHAREWARE == 1)
|
|
switch(which)
|
|
{
|
|
case overpatrolobj:
|
|
case wallopobj:
|
|
case deathmonkobj:
|
|
case dfiremonkobj:
|
|
case b_darianobj:
|
|
case b_heinrichobj:
|
|
case b_darkmonkobj:
|
|
Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
if ((which == lowguardobj) && (GameRandomNumber("SpawnStand",which) < 128))
|
|
which = blitzguardobj;
|
|
|
|
|
|
if ((temp = UPDATE_STATES[STAND][which-lowguardobj]) != NULL)
|
|
{
|
|
SpawnNewObj(tilex,tiley,temp,which);
|
|
if (!loadedgame)
|
|
gamestate.killtotal++;
|
|
|
|
|
|
if (ambush)
|
|
new->flags |= FL_AMBUSH;
|
|
|
|
|
|
#if 0
|
|
#if (SUPERROTT == 1)
|
|
|
|
ConsiderAlternateActor(new,which);
|
|
|
|
#endif
|
|
#endif
|
|
|
|
StandardEnemyInit(new,dir);
|
|
|
|
if (which == b_darkmonkobj)
|
|
{
|
|
new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE);
|
|
new->speed = ENEMYRUNSPEED*2;
|
|
}
|
|
|
|
if (which == blitzguardobj)
|
|
ConsiderOutfittingBlitzguard(new);
|
|
|
|
|
|
if ((new->obclass >= lowguardobj) && (new->obclass <= dfiremonkobj))
|
|
RANDOMACTORTYPE[new->obclass]++;
|
|
|
|
if (MAPSPOT(tilex,tiley,2) == 0xdead)
|
|
{
|
|
new->flags |= FL_KEYACTOR;
|
|
MISCVARS->KEYACTORSLEFT++;
|
|
}
|
|
|
|
PreCacheActor(which,0);
|
|
}
|
|
//else
|
|
//Error("NULL initialization error");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= SpawnPatrol
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void SpawnPatrol (classtype which, int tilex, int tiley, int dir)
|
|
{statetype *temp;
|
|
int path=PATH;
|
|
#if 0
|
|
#if (SUPERROTT == 1)
|
|
char *altstartlabel;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
#if (SHAREWARE==1)
|
|
switch(which)
|
|
{
|
|
case overpatrolobj:
|
|
case wallopobj:
|
|
case deathmonkobj:
|
|
case dfiremonkobj:
|
|
case b_darianobj:
|
|
case b_heinrichobj:
|
|
case b_darkmonkobj:
|
|
Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
#endif
|
|
|
|
if ((which == lowguardobj) && (GameRandomNumber("SpawnStand",which) < 128))
|
|
which = blitzguardobj;
|
|
|
|
|
|
|
|
|
|
if ((temp= UPDATE_STATES[path][(int)(which-lowguardobj)]) != NULL)
|
|
{
|
|
SpawnNewObj(tilex,tiley,temp,which);
|
|
|
|
if (!loadedgame)
|
|
gamestate.killtotal++;
|
|
|
|
|
|
#if 0
|
|
#if (SUPERROTT == 1)
|
|
ConsiderAlternateActor(new,which);
|
|
#endif
|
|
#endif
|
|
|
|
StandardEnemyInit(new,dir);
|
|
|
|
if ((which == wallopobj) || (which == roboguardobj))
|
|
{new->flags |= FL_NOFRICTION;
|
|
//new->flags &= ~FL_SHOOTABLE;
|
|
new->dir <<= 1;
|
|
ParseMomentum(new,dirangle16[new->dir]);
|
|
}
|
|
else
|
|
ParseMomentum(new,dirangle8[new->dir]);
|
|
|
|
|
|
if (which == blitzguardobj)
|
|
ConsiderOutfittingBlitzguard(new);
|
|
|
|
|
|
if (MAPSPOT(tilex,tiley,2) == 0xdead)
|
|
{new->flags |= FL_KEYACTOR;
|
|
MISCVARS->KEYACTORSLEFT++;
|
|
}
|
|
|
|
PreCacheActor(which,0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void SpawnDisk(int tilex, int tiley, int type, boolean master)
|
|
{int zoffset;
|
|
|
|
|
|
|
|
if (master == true)
|
|
{
|
|
Masterdisk = true;
|
|
SpawnNewObj(tilex,tiley,&s_diskmaster,diskobj);
|
|
Masterdisk = false;
|
|
new->flags |= FL_MASTER;
|
|
new->momentumz = -(DISKMOMZ << 16);
|
|
new->flags |= FL_SYNCED;
|
|
new->flags |= FL_NEVERMARK;
|
|
new->temp1 = 1;
|
|
//RemoveFromArea(new);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
if (!type)
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_elevdisk,diskobj);
|
|
new->momentumz = -(DISKMOMZ << 16);
|
|
//new->flags |= FL_SYNCED;
|
|
zoffset = MAPSPOT(tilex,tiley,2);
|
|
if ((zoffset&0xff00)==0xb000)
|
|
Set_NewZ_to_MapValue((fixed*)(&(new->temp2)),zoffset,"elev disk",tilex,tiley);
|
|
else
|
|
new->temp2 = 32;
|
|
new->temp1 = 1;
|
|
}
|
|
else
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_pathdisk,diskobj);
|
|
zoffset = MAPSPOT(tilex,tiley,2);
|
|
if ((zoffset&0xff00)==0xb000)
|
|
Set_NewZ_to_MapValue((fixed*)(&(new->z)),zoffset,"path disk",tilex,tiley);
|
|
|
|
new->dir = (type-1) << 1;
|
|
new->speed = 0x1000;
|
|
|
|
//ParseMomentum(new,dirangle8[new->dir]);
|
|
}
|
|
actorat[tilex][tiley] = NULL;
|
|
new->flags |= FL_BLOCK;
|
|
new->flags |= (FL_NOFRICTION|FL_ACTIVE|FL_NEVERMARK);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
objtype* DiskAt(int tilex,int tiley)
|
|
{int area;
|
|
objtype *temp;
|
|
statobj_t *tstat;
|
|
|
|
area = AREANUMBER(tilex,tiley);
|
|
for(temp = firstareaactor[area];temp;temp = temp->nextinarea)
|
|
{if ((temp->tilex != tilex) || (temp->tiley != tiley) ||
|
|
(temp->obclass != diskobj))
|
|
continue;
|
|
return temp;
|
|
|
|
}
|
|
|
|
for(tstat = firstactivestat;tstat;tstat = tstat->nextactive)
|
|
{
|
|
if ((tstat->tilex != tilex) || (tstat->tiley != tiley) ||
|
|
(tstat->itemnumber != stat_disk))
|
|
continue;
|
|
return (objtype*)tstat;
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
void SetElevatorDiskVariables(objtype *ob,int newz, int newmomentumz,
|
|
int newtemp1,int newtemp3,int newdirchoose)
|
|
{
|
|
ob->z = newz;
|
|
ob->momentumz = newmomentumz;
|
|
ob->temp1 = newtemp1;
|
|
ob->temp3 = newtemp3;
|
|
ob->dirchoosetime = newdirchoose;
|
|
}
|
|
|
|
|
|
void T_ElevDisk(objtype*ob)
|
|
{
|
|
objtype *temp = (objtype*)(actorat[ob->tilex][ob->tiley]);
|
|
objtype *master;
|
|
|
|
if (ob->flags & FL_MASTER)
|
|
goto masterlabel;
|
|
|
|
master = (objtype*)(ob->target);
|
|
if (!master)
|
|
Error("disk without master !");
|
|
|
|
|
|
//SoftError("\n ob->z:%d %s, master z:%d",ob->z,
|
|
// (ob->flags & FL_SYNCED)?("SYNCED"):("UNSYNCED"),master->z);
|
|
|
|
|
|
if (M_ISACTOR(temp) && (temp != ob) && (!(temp->flags & FL_DYING)))
|
|
{
|
|
int dz = abs(ob->z - temp->z),
|
|
dx = abs(ob->x - temp->x),
|
|
dy = abs(ob->y - temp->y);
|
|
|
|
if ((dx < 0x7000) && (dy < 0x7000) && (dz < 68) && (temp->z > ob->z))
|
|
{
|
|
ob->flags &= ~FL_SYNCED;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (master && (!(ob->flags & FL_SYNCED)))
|
|
{
|
|
int dz;
|
|
|
|
dz = abs(master->z - ob->z);
|
|
if ((dz > 0) && (dz < 8))
|
|
{
|
|
SetElevatorDiskVariables(ob,master->z,master->momentumz,master->temp1,
|
|
master->temp3,master->dirchoosetime);
|
|
ob->flags |= FL_SYNCED;
|
|
//return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
masterlabel:
|
|
|
|
if (ob->dirchoosetime)
|
|
{
|
|
ob->dirchoosetime --;
|
|
return;
|
|
}
|
|
|
|
|
|
if (ob->temp1) // moving
|
|
{
|
|
ob->z += (ob->momentumz >> 16);
|
|
if (ob->momentumz > 0) // down
|
|
{
|
|
if (ob->z >= nominalheight + 40 + DISKMOMZ)
|
|
SetElevatorDiskVariables(ob,ob->z - (ob->momentumz>>16),0,0,0,35);
|
|
}
|
|
else
|
|
{
|
|
if (ob->z < ob->temp2) // temp2 has max height
|
|
SetElevatorDiskVariables(ob,ob->z - (ob->momentumz>>16),0,0,1,35);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ob->temp3)
|
|
ob->momentumz = (DISKMOMZ << 16);
|
|
else
|
|
ob->momentumz = -(DISKMOMZ << 16);
|
|
ob->temp1 = 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpawnInertActor(int newx,int newy, int newz)
|
|
{
|
|
GetNewActor ();
|
|
MakeActive(new);
|
|
|
|
new->obclass = inertobj;
|
|
new->which = ACTOR;
|
|
SetFinePosition(new,newx,newy);
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
new->z = newz;
|
|
new->dir = 0;
|
|
new->speed = 0;
|
|
new->flags = (FL_NEVERMARK|FL_ABP);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if (SHAREWARE == 0)
|
|
void SpawnGroundExplosion(int x, int y, int z)
|
|
{
|
|
SpawnInertActor(x,y,z);
|
|
NewState(new,&s_grexplosion1);
|
|
new->temp2 = GameRandomNumber("SpawnGroundExplosion",0)>>2;
|
|
|
|
}
|
|
#endif
|
|
|
|
void SpawnSlowParticles(int which, int numgibs, int x,int y,int z)
|
|
{objtype *prevlast,*temp;
|
|
int tilex,tiley;
|
|
|
|
tilex = x>>16;
|
|
tiley = y>>16;
|
|
|
|
SpawnNewObj(tilex,tiley,&s_gibs1,inertobj);
|
|
SetFinePosition(new,x,y);
|
|
SetVisiblePosition(new,x,y);
|
|
prevlast = new;
|
|
prevlast->flags |= FL_ABP;
|
|
MakeActive(prevlast);
|
|
SpawnParticles(new,which,numgibs);
|
|
|
|
for(temp = prevlast->next;temp;temp=temp->next)
|
|
{temp->z = z;
|
|
temp->momentumx >>= 1;
|
|
temp->momentumy >>= 1;
|
|
temp->momentumz >>= 1;
|
|
}
|
|
RemoveObj(prevlast);
|
|
|
|
}
|
|
|
|
|
|
void ResolveDoorSpace(int tilex,int tiley)
|
|
{
|
|
statobj_t* tstat,*temp;
|
|
|
|
for(tstat = firstactivestat;tstat;)
|
|
{
|
|
temp = tstat->nextactive;
|
|
|
|
if (tstat->flags & FL_DEADBODY)
|
|
{
|
|
if ((tstat->tilex == tilex) && (tstat->tiley == tiley))
|
|
{
|
|
if ((tstat->flags & FL_DEADBODY) && (tstat->linked_to != -1))
|
|
DEADPLAYER[tstat->linked_to] = NULL;
|
|
RemoveStatic(tstat);
|
|
if (tstat->flags & FL_DEADBODY)
|
|
SpawnSlowParticles(GUTS,8,tstat->x,tstat->y,tstat->z);
|
|
else
|
|
SpawnSlowParticles(gt_sparks,8,tstat->x,tstat->y,tstat->z);
|
|
SD_PlaySoundRTP(SD_ACTORSQUISHSND,tstat->x,tstat->y);
|
|
}
|
|
}
|
|
tstat = temp;
|
|
}
|
|
}
|
|
|
|
|
|
void SpawnSpear(int tilex,int tiley,int up)
|
|
{
|
|
int count,i;
|
|
statetype *tstate;
|
|
|
|
|
|
if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers))
|
|
return;
|
|
|
|
if (!up)
|
|
{
|
|
#if (SHAREWARE == 1)
|
|
Error("\ndownspear at %d,%d in shareware!",tilex,tiley);
|
|
#else
|
|
SpawnNewObj(tilex,tiley,&s_speardown1,spearobj);
|
|
new->z = 0;
|
|
#endif
|
|
}
|
|
else
|
|
|
|
SpawnNewObj(tilex,tiley,&s_spearup1,spearobj);
|
|
|
|
count = (int)(GameRandomNumber("Spawn Spear",0) % 16);
|
|
for(i=0,tstate = new->state;i<count;i++,tstate=tstate->next);
|
|
NewState(new,tstate);
|
|
|
|
PreCacheActor(spearobj,up);
|
|
new->flags |= (FL_ABP);//|FL_INVULNERABLE);
|
|
MakeActive(new);
|
|
}
|
|
|
|
|
|
|
|
void SpawnSpring(int tilex,int tiley)
|
|
{
|
|
int iconvalue;
|
|
|
|
iconvalue = MAPSPOT(tilex,tiley,2);
|
|
if (iconvalue == 3)
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_autospring1,springobj);
|
|
new->ticcount = (GameRandomNumber("Spawn Spring",0) % new->ticcount)+1;
|
|
new->temp1 = iconvalue;
|
|
}
|
|
else
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_spring1,springobj);
|
|
if (iconvalue == 2)
|
|
new->temp1 = iconvalue;
|
|
}
|
|
|
|
PreCacheActor(springobj,0);
|
|
new->flags &= ~(FL_SHOOTABLE|FL_BLOCK);
|
|
}
|
|
|
|
|
|
|
|
|
|
void T_Spring(objtype*ob)
|
|
{
|
|
objtype *temp;
|
|
int op,dx,dy,dz;
|
|
|
|
|
|
if ((ob->state->condition & SF_DOWN) && (ob->temp1))
|
|
{
|
|
if (ob->ticcount)
|
|
return;
|
|
ob->shapenum++;
|
|
TurnActorIntoSprite(ob);
|
|
return;
|
|
}
|
|
|
|
for(temp=firstareaactor[ob->areanumber];temp;temp=temp->nextinarea)
|
|
{
|
|
if (temp == ob)
|
|
continue;
|
|
|
|
if (temp->obclass >= roboguardobj)
|
|
continue;
|
|
|
|
dx = abs(ob->x-temp->x);
|
|
dy = abs(ob->y-temp->y);
|
|
dz = abs(ob->z-temp->z);
|
|
if ((dx > ACTORSIZE+0x2800) || (dy > ACTORSIZE+0x2800) || (dz > 40))
|
|
continue;
|
|
if (!temp->momentumz)
|
|
{
|
|
op = FixedMul(GRAVITY,(temp->z-5)<<16) << 1;
|
|
temp->momentumz = -FixedSqrtHP(op);
|
|
SD_PlaySoundRTP(SD_SPRINGBOARDSND,ob->x,ob->y);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_Count(objtype*ob)
|
|
{
|
|
int index;
|
|
wall_t* tswitch;
|
|
touchplatetype *temp;
|
|
objtype* tempactor;
|
|
|
|
if (ob->dirchoosetime)
|
|
{
|
|
ob->dirchoosetime --;
|
|
if (ob->dirchoosetime>980)
|
|
MISCVARS->gasindex=((1050-ob->dirchoosetime)<<4)/70;
|
|
else if (ob->dirchoosetime<35)
|
|
MISCVARS->gasindex=(ob->dirchoosetime<<4)/35;
|
|
if (ob->temp3)
|
|
{
|
|
ob->temp3 --;
|
|
if (ob->temp3 & 1)
|
|
SD_PlaySoundRTP(SD_GASHISSSND,ob->x,ob->y);
|
|
}
|
|
else
|
|
{
|
|
ob->temp3 = 105;
|
|
for(tempactor=firstareaactor[ob->areanumber];tempactor;tempactor=tempactor->nextinarea)
|
|
{
|
|
if (tempactor == ob)
|
|
continue;
|
|
if (!(tempactor->flags & FL_SHOOTABLE))
|
|
continue;
|
|
if (tempactor->obclass != playerobj)
|
|
{
|
|
if ((tempactor->obclass >= lowguardobj) &&
|
|
(tempactor->obclass <= dfiremonkobj))
|
|
{
|
|
int oldviolence = gamestate.violence;
|
|
|
|
gamestate.violence = vl_low;
|
|
DamageThing(tempactor,EnvironmentDamage(ob));
|
|
Collision(tempactor,ob,-(tempactor->momentumx),-(tempactor->momentumy));
|
|
gamestate.violence = oldviolence;
|
|
|
|
}
|
|
}
|
|
else if (!(tempactor->flags & FL_GASMASK))
|
|
{
|
|
DamageThing(tempactor,EnvironmentDamage(ob));
|
|
Collision(tempactor,ob,0,0);
|
|
M_CheckPlayerKilled(tempactor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
int i;
|
|
playertype *pstate;
|
|
|
|
for(i=0;i<numplayers;i++)
|
|
{
|
|
M_LINKSTATE(PLAYER[i],pstate);
|
|
PLAYER[i]->flags &= ~FL_GASMASK;
|
|
pstate->protectiontime = 1;
|
|
}
|
|
|
|
NewState(ob,&s_gas1);
|
|
SD_PlaySoundRTP(SD_GASENDSND,ob->x,ob->y);
|
|
ob->flags &= ~FL_ACTIVE;
|
|
MISCVARS->gasindex=0;
|
|
MU_StartSong(song_level);
|
|
MU_RestoreSongPosition();
|
|
MISCVARS->GASON = 0;
|
|
|
|
index = touchindices[ob->temp1][ob->temp2]-1;
|
|
TRIGGER[index] = 0;
|
|
for(temp = touchplate[index];temp;temp = temp->nextaction)
|
|
if (temp->action == EnableObject)
|
|
{
|
|
tempactor = (objtype*)(temp->whichobj);
|
|
tempactor->flags &= ~FL_ACTIVE;
|
|
}
|
|
|
|
tswitch = (wall_t*)actorat[ob->temp1][ob->temp2];
|
|
/*
|
|
if (tswitch && (tswitch->which != ACTOR))
|
|
{
|
|
tilemap[ob->temp1][ob->temp2]--;
|
|
tswitch->flags &= ~FL_ON;
|
|
}
|
|
*/
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpawnBlade(int tilex, int tiley,int dir,int upordown,int moving)
|
|
{int count,i;
|
|
statetype *nstate;
|
|
|
|
#if (SHAREWARE == 1)
|
|
if (!upordown)
|
|
Error("\ndown spinblade at %d,%d not allowed in shareware !",tilex,tiley);
|
|
if (moving)
|
|
Error("\nupdown spinblade at %d,%d not allowed in shareware !",tilex,tiley);
|
|
|
|
#endif
|
|
|
|
|
|
if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers))
|
|
return;
|
|
|
|
if (moving)
|
|
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
|
|
if (upordown)
|
|
SpawnNewObj(tilex,tiley,&s_spinupblade1,bladeobj);
|
|
else
|
|
{SpawnNewObj(tilex,tiley,&s_spindownblade1,bladeobj);
|
|
new->z = 0;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{if (upordown)
|
|
SpawnNewObj(tilex,tiley,&s_upblade1,bladeobj);
|
|
|
|
#if (SHAREWARE == 0)
|
|
|
|
else
|
|
{SpawnNewObj(tilex,tiley,&s_downblade1,bladeobj);
|
|
new->z = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
count = (int)(GameRandomNumber("SpawnBlade",0) % 16);
|
|
for(nstate=new->state,i=0;i<count;nstate = nstate->next,i++);
|
|
NewState(new,nstate);
|
|
|
|
new->flags |= (FL_BLOCK);
|
|
new->flags &= ~FL_SHOOTABLE;
|
|
new->dir = dir;
|
|
if (dir != nodir)
|
|
{new->flags |= FL_NOFRICTION;
|
|
new->speed = ENEMYRUNSPEED;
|
|
|
|
}
|
|
if (!MAPSPOT(tilex,tiley,2))
|
|
{new->flags |= FL_ACTIVE;
|
|
ParseMomentum(new,dirangle8[new->dir]);
|
|
}
|
|
PreCacheActor(bladeobj,(moving<<1)+upordown);
|
|
}
|
|
|
|
|
|
void SpawnCrushingColumn(int tilex, int tiley, int upordown)
|
|
{int i,count;
|
|
statetype * nstate;
|
|
|
|
|
|
#if (SHAREWARE == 1)
|
|
if (!upordown)
|
|
Error("\ncrush-up column at %d,%d not allowed in shareware!",tilex,tiley);
|
|
#endif
|
|
|
|
|
|
if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers))
|
|
return;
|
|
#if (SHAREWARE == 0)
|
|
if (!upordown)
|
|
SpawnNewObj(tilex,tiley,&s_columnupup1,crushcolobj);
|
|
else
|
|
#endif
|
|
{SpawnNewObj(tilex,tiley,&s_columndowndown1,crushcolobj);
|
|
new->z = 0;
|
|
}
|
|
|
|
count = (int)(GameRandomNumber("SpawnCrushingColumn",0) % 8);
|
|
for(nstate=new->state,i=0;i<count;nstate = nstate->next,i++)
|
|
{if ((!upordown) && (nstate->condition & SF_UP))
|
|
new->temp1 += (((nstate->tictime>>1) + 1)<<2);
|
|
|
|
}
|
|
NewState(new,nstate);
|
|
new->flags |= (FL_BLOCK);
|
|
|
|
new->flags &= ~FL_SHOOTABLE;
|
|
PreCacheActor(crushcolobj,upordown);
|
|
}
|
|
|
|
|
|
|
|
void SpawnFirejet(int tilex, int tiley, int dir, int upordown)
|
|
{
|
|
int statecount,i;
|
|
statetype *tstate;
|
|
|
|
|
|
statecount = (int)(GameRandomNumber("SpawnFirejet",0) % 22);
|
|
|
|
if (upordown)
|
|
{
|
|
for(i=0,tstate=&s_firejetup1;i<statecount;i++,tstate=tstate->next);
|
|
SpawnNewObj(tilex,tiley,tstate,firejetobj);
|
|
}
|
|
else
|
|
{
|
|
#if (SHAREWARE == 1)
|
|
Error("\ndown firejet at %d,%d not allowed in shareware",tilex,tiley);
|
|
#else
|
|
for(i=0,tstate=&s_firejetdown1;i<statecount;i++,tstate=tstate->next);
|
|
SpawnNewObj(tilex,tiley,tstate,firejetobj);
|
|
new->z = 0;
|
|
#endif
|
|
}
|
|
|
|
PreCacheActor(firejetobj,upordown);
|
|
|
|
new->flags &= ~FL_SHOOTABLE;
|
|
|
|
if (dir != nodir)
|
|
{
|
|
new->dir = dir*2;
|
|
new->flags |= FL_NOFRICTION;
|
|
new->speed = ENEMYRUNSPEED;
|
|
ParseMomentum(new,dirangle8[new->dir]);
|
|
}
|
|
else
|
|
new->dir = dir;
|
|
}
|
|
|
|
|
|
void SpawnFirebomb(objtype*ob,int damage,int which)
|
|
{
|
|
int i,low,high,doorat;
|
|
wall_t *tempwall;
|
|
doorobj_t*tempdoor;
|
|
|
|
if (which == 0)
|
|
{
|
|
low = (ob->dir>>1);
|
|
high = low;
|
|
}
|
|
else
|
|
{
|
|
low = 0;
|
|
high = which-1;
|
|
|
|
if ((FindDistance((ob->x-player->x), (ob->y-player->y))<0x120000) &&
|
|
(player->z==nominalheight)
|
|
)
|
|
SHAKETICS = 35;
|
|
}
|
|
|
|
for (i=low;i<=high;i++)
|
|
{
|
|
MissileSound = false;
|
|
/*
|
|
if (((which == 0) && ((low == 5) || (low == 6))) ||
|
|
((which == 6) && ((i==4) || (i==5)))
|
|
)
|
|
{
|
|
|
|
if (((which == 0) && (low == 5)) ||
|
|
((which == 6) && (i == 4))
|
|
)
|
|
{
|
|
newz = ob->z + 64;
|
|
if (newz > maxheight)
|
|
continue;
|
|
SpawnMissile(ob,p_firebombobj,0,0,&s_grexplosion1,0);
|
|
new->z = newz;
|
|
new->dir = 10;
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
newz = ob->z - 64;
|
|
if ((sky == 0) && (newz < 0))
|
|
continue;
|
|
SpawnMissile(ob,p_firebombobj,0,0,&s_grexplosion1,0);
|
|
new->z = newz;
|
|
new->dir = 12;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
else */
|
|
{
|
|
SpawnMissile(ob,p_firebombobj,0,dirangle8[2*i],&s_grexplosion1,0x10000);
|
|
new->z = ob->z;
|
|
new->dir = (i<<1);
|
|
|
|
}
|
|
|
|
MissileSound = true;
|
|
|
|
|
|
SD_PlaySoundRTP(SD_EXPLODEFLOORSND,ob->x,ob->y);
|
|
new->temp2 = FixedMul(damage,DIAGADJUST);
|
|
|
|
|
|
tempwall = (wall_t*)actorat[new->tilex][new->tiley];
|
|
doorat= 0;
|
|
if (M_ISDOOR(new->tilex,new->tiley))
|
|
{
|
|
tempdoor = doorobjlist[tilemap[new->tilex][new->tiley]&0x3ff];
|
|
if (tempdoor->position<0x8000)
|
|
doorat = 1;
|
|
}
|
|
|
|
if ((tempwall && M_ISWALL(tempwall)) || doorat ||
|
|
(new->tilex <=0) || (new->tilex > MAPSIZE-1) ||
|
|
(new->tiley <=0) || (new->tiley > MAPSIZE-1)
|
|
)
|
|
{
|
|
new->z = ob->z;
|
|
SetFinePosition(new,ob->x,ob->y);
|
|
SetVisiblePosition(new,ob->x,ob->y);
|
|
}
|
|
new->whatever = ob->whatever;
|
|
new->temp3 = ob->temp3 - 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MissileHitActor(objtype *owner, objtype *missile, objtype *victim,
|
|
int damage, int hitmomx, int hitmomy
|
|
)
|
|
{
|
|
int tcl = victim->obclass;
|
|
int ocl = missile->obclass;
|
|
|
|
if (
|
|
(victim->flags & FL_DYING) || // hey, they're dying already;
|
|
(victim->flags & FL_HEAD) || // don't hurt overrobot's head, wheels
|
|
(tcl == wallopobj) || // bcraft is invulnerable
|
|
(tcl == b_darkmonkobj) || // darkmonk is invulnerable
|
|
(!(victim->flags & FL_SHOOTABLE)) || // don't hurt environment dangers, dead guys
|
|
((tcl == b_darksnakeobj) &&
|
|
((SNAKELEVEL != 3) || (!victim->temp3))// return for non-red snake
|
|
)
|
|
)
|
|
return;
|
|
|
|
|
|
if ((tcl == playerobj) || (tcl == b_heinrichobj))
|
|
victim->target = owner;
|
|
|
|
|
|
if (tcl == NMEsaucerobj) // can shoot over's saucer
|
|
{
|
|
NewState(victim,&s_explosion1);
|
|
victim->flags &= ~FL_SHOOTABLE;
|
|
victim->temp2 = damage;
|
|
return;
|
|
}
|
|
|
|
else if (tcl == roboguardobj) // check roboguard
|
|
{
|
|
DamageThing(victim,damage);
|
|
Collision(victim,owner,0,0);
|
|
}
|
|
|
|
else if (tcl == collectorobj)
|
|
{
|
|
if (gamestate.SpawnEluder)
|
|
return;
|
|
|
|
DamageThing(victim,damage);
|
|
Collision(victim,owner,0,0);
|
|
}
|
|
|
|
else if (tcl == patrolgunobj)
|
|
{
|
|
DamageThing(victim,damage);
|
|
if (victim->hitpoints <= 0)
|
|
{
|
|
victim->momentumx = victim->momentumy = victim->momentumz = 0;
|
|
victim->flags |= FL_DYING;
|
|
if (victim->temp1 == -1) // this is 4-way gun
|
|
NewState(victim,&s_robogrddie1);
|
|
#if (SHAREWARE == 0)
|
|
else // this is normal
|
|
NewState(victim,&s_gundie1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// bosses are "special" ==========================
|
|
|
|
else if ((tcl >= b_darianobj) && (tcl < b_darkmonkobj))
|
|
|
|
{
|
|
DamageThing(victim,damage);
|
|
if (!(victim->flags & FL_ATTACKMODE))
|
|
FirstSighting (victim); // put into combat mode
|
|
if (victim->hitpoints <= 0)
|
|
{
|
|
victim->momentumx = victim->momentumy = victim->momentumz = 0;
|
|
victim->flags |= FL_DYING;
|
|
NewState(victim,UPDATE_STATES[DIE][victim->obclass-lowguardobj]);
|
|
switch (victim->obclass)
|
|
{
|
|
case b_darianobj:
|
|
AddMessage("Darian defeated!",MSG_CHEAT);
|
|
break;
|
|
|
|
case b_heinrichobj:
|
|
AddMessage("Krist defeated!",MSG_CHEAT);
|
|
break;
|
|
|
|
case b_robobossobj:
|
|
AddMessage("NME defeated!",MSG_CHEAT);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
MU_StartSong(song_bossdie);
|
|
}
|
|
#if (SHAREWARE == 0)
|
|
else
|
|
{
|
|
MISCVARS->REDTIME = (damage >> 1);
|
|
if (victim->obclass == b_heinrichobj)
|
|
{
|
|
NewState(victim,&s_heinrichdefend);
|
|
if (Near(victim,PLAYER[0],3))
|
|
{
|
|
MISCVARS->HRAMMING = 1;
|
|
MISCVARS->HMINING = 0;
|
|
victim->dirchoosetime = 0;
|
|
}
|
|
else
|
|
{
|
|
MISCVARS->HMINING = 1;
|
|
MISCVARS->HRAMMING = 0;
|
|
victim->dirchoosetime = 5;//10;
|
|
}
|
|
victim->targettilex = victim->targettiley = 0;
|
|
victim->target = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if (SHAREWARE == 0)
|
|
else if ((tcl == b_darksnakeobj) && (victim->temp3)) // red snake
|
|
{
|
|
DamageThing(SNAKEEND,damage);
|
|
if (victim->state->think == T_DarkSnakeChase)
|
|
NewState(victim,&s_redheadhit);
|
|
else
|
|
NewState(victim,&s_redlinkhit);
|
|
victim->temp3 = 0;
|
|
}
|
|
#endif
|
|
|
|
//===============================================
|
|
else // all other actors
|
|
{
|
|
|
|
if ((tcl == playerobj) &&
|
|
(victim->flags & FL_AV) &&
|
|
(ocl != p_godballobj)
|
|
)
|
|
{
|
|
playertype *pstate;
|
|
|
|
M_LINKSTATE(victim,pstate);
|
|
pstate->protectiontime -= ((damage<<1) + damage);
|
|
if (pstate->protectiontime < 1)
|
|
pstate->protectiontime = 1;
|
|
if (victim==player)
|
|
GM_UpdateBonus (pstate->protectiontime, false);
|
|
|
|
return; // asbestos vest prevents victim damage
|
|
}
|
|
|
|
DamageThing(victim,damage);
|
|
|
|
|
|
|
|
if ((tcl < roboguardobj) && (victim->hitpoints <= 0))
|
|
{
|
|
if (ocl != p_godballobj)
|
|
victim->flags |= FL_HBM;
|
|
else
|
|
victim->flags |= (FL_GODSTRUCK | FL_FULLLIGHT);
|
|
}
|
|
|
|
if (tcl == playerobj)
|
|
{
|
|
playertype *pstate;
|
|
|
|
M_LINKSTATE(victim,pstate);
|
|
if (pstate->health <= 0)
|
|
{
|
|
if (ocl != p_godballobj)
|
|
victim->flags |= FL_HBM;
|
|
else
|
|
victim->flags |= (FL_GODSTRUCK | FL_FULLLIGHT);
|
|
|
|
if (M_ISACTOR(owner))
|
|
{
|
|
if (owner->obclass == playerobj)
|
|
{
|
|
if (!victim->momentumz)
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,victim->dirchoosetime);
|
|
else
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_missile_in_air,owner->dirchoosetime,victim->dirchoosetime);
|
|
}
|
|
else
|
|
BATTLE_CheckGameStatus(battle_player_killed,missile->dirchoosetime);
|
|
}
|
|
else
|
|
BATTLE_CheckGameStatus(battle_player_killed,missile->dirchoosetime);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if ((owner->obclass == playerobj) && (victim->flags & FL_HBM))
|
|
{
|
|
MISCVARS->supergibflag = true;
|
|
//GivePoints(starthitpoints[gamestate.difficulty][victim->obclass]*5);
|
|
}
|
|
|
|
Collision(victim,owner,hitmomx,hitmomy);
|
|
MISCVARS->supergibflag = false;
|
|
if ((tcl == blitzguardobj) && (owner->obclass == playerobj))
|
|
victim->flags |= FL_TARGET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MissileHit (objtype *ob,void *hitwhat)
|
|
{
|
|
int damage=0, random,tcl=0,ocl,fireweapon=0,sound,hitmomx,hitmomy;
|
|
objtype* tempactor=NULL,*owner;
|
|
|
|
|
|
|
|
if (ob==missobj)
|
|
missobj=NULL;
|
|
|
|
if (ob == PLAYER0MISSILE)
|
|
PLAYER0MISSILE = NULL;
|
|
|
|
|
|
ob->momentumz = 0;
|
|
hitmomx = ob->momentumx;
|
|
hitmomy = ob->momentumy;
|
|
if (ob->soundhandle != -1)
|
|
SD_StopSound(ob->soundhandle);
|
|
|
|
ob->flags &= ~FL_SHOOTABLE;
|
|
if (FirstExplosionState(ob->state))
|
|
return;
|
|
|
|
/*
|
|
if ((ob->z < -28) || (IsWindow(ob->tilex,ob->tiley)))
|
|
{
|
|
NewState(ob,&s_megaremove);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
tempactor = (objtype*)hitwhat;
|
|
owner = (objtype*)(ob->whatever);
|
|
|
|
random = GameRandomNumber("MissileHit",0);
|
|
ocl = ob->obclass;
|
|
if (tempactor)
|
|
{
|
|
if (tempactor->which == ACTOR)
|
|
tcl = tempactor->obclass;
|
|
else if (tempactor->which == SPRITE)
|
|
tcl = -1;
|
|
}
|
|
|
|
if ((!tcl) && (ob->z < -30))
|
|
{
|
|
if (ob->soundhandle != -1)
|
|
SD_StopSound(ob->soundhandle);
|
|
|
|
NewState(ob,&s_megaremove);
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
if (((ocl != p_kesobj) && (ocl != p_godballobj)) || (!tcl))
|
|
ZEROMOM;
|
|
|
|
if (tcl == b_darianobj)
|
|
MISCVARS->ESAU_SHOOTING = false;
|
|
|
|
switch(ocl)
|
|
{
|
|
|
|
|
|
case p_bazookaobj:
|
|
NewState(ob,&s_explosion1);
|
|
if (M_ISACTOR(owner) && (owner->obclass == blitzguardobj))
|
|
damage = 30 + (random >> 4);
|
|
else
|
|
damage = 2*((random>>3)+80);
|
|
break;
|
|
|
|
case p_heatseekobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = 2*((random>>3)+50);
|
|
break;
|
|
|
|
|
|
case p_drunkmissileobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = ((random>>3)+25);
|
|
break;
|
|
|
|
case p_firebombobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = 2*((random>>3)+90);
|
|
ob->temp3 = 4;
|
|
SpawnFirebomb(ob,damage,4);
|
|
break;
|
|
|
|
case p_firewallobj:
|
|
|
|
if (tcl == playerobj)
|
|
gamestate.DOGROUNDZEROBONUS = true;
|
|
NewState(ob,&s_explosion1);
|
|
damage = 2*((random>>3)+50);
|
|
break;
|
|
|
|
|
|
case p_godballobj:
|
|
if ((tcl >= pillarobj) || (!tcl) || ((tcl == -1) && (!(tempactor->flags & FL_SHOOTABLE))))
|
|
NewState(ob,&s_explosion1);
|
|
ob->target = NULL;
|
|
damage = 500;
|
|
break;
|
|
|
|
|
|
case shurikenobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = ((random >>3) + 30)>>2;
|
|
break;
|
|
|
|
|
|
case grenadeobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = (random >>3) + 20;
|
|
break;
|
|
|
|
case fireballobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = (random >> 3) + 10;
|
|
fireweapon = 1;
|
|
break;
|
|
|
|
case missileobj:
|
|
NewState(ob,&s_explosion1);
|
|
if (M_ISACTOR(owner) && (owner->obclass == wallopobj))
|
|
damage = (random >> 5);
|
|
else
|
|
damage = (random >>3) + 30;
|
|
if (tcl && (tcl != b_heinrichobj))
|
|
damage = 3*damage>>3;
|
|
break;
|
|
|
|
case wallfireobj:
|
|
if ((!tempactor) ||
|
|
(tempactor->which == ACTOR) ||
|
|
(tempactor->which == SPRITE)
|
|
)
|
|
NewState(ob,&s_explosion1);
|
|
else if (M_ISWALL(tempactor) || (tempactor->which == DOOR))
|
|
NewState(ob,&s_crossdone1);
|
|
damage = EnvironmentDamage(ob);
|
|
fireweapon = 1;
|
|
break;
|
|
|
|
|
|
|
|
case inertobj:
|
|
ob->state = NULL;
|
|
return;
|
|
break;
|
|
|
|
|
|
#if (SHAREWARE == 0)
|
|
|
|
|
|
case p_splitmissileobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = 2*((random>>3)+50);
|
|
break;
|
|
|
|
|
|
case p_kesobj:
|
|
if ((tcl >= pillarobj) ||
|
|
(!tcl) ||
|
|
((tcl == -1) && (!(tempactor->flags & FL_SHOOTABLE)))
|
|
)
|
|
NewState(ob,&s_explosion1);
|
|
damage = 2*((random>>3)+140);
|
|
break;
|
|
|
|
|
|
case netobj:
|
|
ob->state=NULL;
|
|
MISCVARS->NET_IN_FLIGHT = false;
|
|
if (tempactor == PLAYER[0])
|
|
{
|
|
if ((tempactor->flags & FL_GODMODE) ||
|
|
(tempactor->flags & FL_DOGMODE) ||
|
|
godmode
|
|
)
|
|
damage = 0;
|
|
else
|
|
{
|
|
damage = (random >>4) + 5;
|
|
PLAYERSTATE[0].NETCAPTURED = -1;
|
|
PLAYERSTATE[0].weapondowntics = WEAPONS[PLAYERSTATE[0].weapon].screenheight/GMOVE;
|
|
NewState(PLAYER[0],&s_player);
|
|
PLAYERSTATE[0].attackframe = PLAYERSTATE[0].weaponframe = 0;
|
|
PLAYERSTATE[0].batblast = 0;
|
|
if (PLAYERSTATE[0].HASKNIFE == false)
|
|
AddMessage("Wiggle left and right to get out of net!",
|
|
MSG_GAME);
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case bigshurikenobj:
|
|
NewState(ob,&s_oshurikenhit1);
|
|
if (owner->obclass == wallopobj)
|
|
damage = (random >> 5);
|
|
else
|
|
damage = 4*((random >>3) + 30)/10;
|
|
|
|
break;
|
|
|
|
|
|
case dm_spitobj:
|
|
NewState(ob,&s_spithit1);
|
|
damage = 30;
|
|
if (gamestate.difficulty == gd_hard)
|
|
damage += 15;
|
|
break;
|
|
|
|
case dm_weaponobj:
|
|
damage = 20;
|
|
NewState(ob,&s_explosion1);
|
|
break;
|
|
|
|
case dm_heatseekobj:
|
|
damage = 20;
|
|
NewState(ob,&s_explosion1);
|
|
break;
|
|
|
|
case dmfballobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = (random >>3) + 20;
|
|
fireweapon = 1;
|
|
break;
|
|
|
|
case h_mineobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = (random >>3) + 20;
|
|
break;
|
|
|
|
case NMEsaucerobj:
|
|
NewState(ob,&s_explosion1);
|
|
damage = 2*((random>>3)+30);
|
|
break;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
//default:
|
|
//Error("Unknown ob %d called MissileHit",ob->obclass);
|
|
}
|
|
|
|
//if (!ob->state)
|
|
//return;
|
|
if ((sound = BAS[ob->obclass].hit)!=0)
|
|
SD_PlaySoundRTP(sound,ob->x,ob->y);
|
|
|
|
|
|
if (FirstExplosionState(ob->state))
|
|
SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y);
|
|
|
|
|
|
if (tcl>0) // actors
|
|
{
|
|
MissileHitActor(owner,ob,tempactor,damage,hitmomx,hitmomy);
|
|
if ((ocl == p_kesobj) && (tcl < roboguardobj))
|
|
{
|
|
tempactor->momentumx = hitmomx; // kes gives wus targets its momentum
|
|
tempactor->momentumy = hitmomy;
|
|
//missile->flags |= FL_NOFRICTION;
|
|
}
|
|
}
|
|
|
|
else if (tcl < 0) // static
|
|
{
|
|
DamageThing(hitwhat,damage);
|
|
if (FirstExplosionState(new->state))
|
|
new->whatever = ob->whatever;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void T_Spears(objtype*ob)
|
|
{int dx,dy,dz,i;
|
|
|
|
|
|
for(i=0;i<numplayers;i++)
|
|
{if (PLAYER[i]->flags & FL_DYING)
|
|
continue;
|
|
|
|
dx = abs(PLAYER[i]->x - ob->x);
|
|
dy = abs(PLAYER[i]->y - ob->y);
|
|
dz = abs(PLAYER[i]->z - ob->z);
|
|
|
|
|
|
if ((!ob->ticcount)&&(ob->state->condition&SF_SOUND) &&
|
|
areabyplayer[ob->areanumber])
|
|
SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
|
|
|
|
if ((dx < STANDDIST) && (dy < STANDDIST) && (dz < 20))
|
|
{ob->flags &= ~FL_BLOCK;
|
|
if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH))
|
|
{DamageThing(PLAYER[i],EnvironmentDamage(ob));
|
|
Collision(PLAYER[i],ob,0,0);
|
|
M_CheckPlayerKilled(PLAYER[i]);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{if (ob->state->condition & SF_DOWN)
|
|
ob->flags &= ~FL_BLOCK;
|
|
else
|
|
ob->flags |= FL_BLOCK;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_CrushUp(objtype*ob)
|
|
{int dx, dy,dist,dz,i,playeron;
|
|
|
|
|
|
if ((!ob->ticcount) && (ob->state->condition & SF_SOUND) &&
|
|
areabyplayer[ob->areanumber])
|
|
SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
|
|
dist = ACTORSIZE+0x2000;
|
|
if (ob->state->condition & SF_UP)
|
|
{ob->temp1 += 4;
|
|
// Debug("\ncol momz = 4");
|
|
}
|
|
else if (ob->state->condition & SF_DOWN)
|
|
{ob->temp1 -= 4;
|
|
// Debug("\ncol mom z = -4");
|
|
}
|
|
else
|
|
{//ob->momentumz = 0;
|
|
// Debug("\ncol mom z = 0");
|
|
}
|
|
|
|
ob->temp2 = maxheight - ob->temp1 + 32;
|
|
|
|
|
|
playeron = 0;
|
|
for(i=0;i<numplayers;i++)
|
|
{dx = abs(PLAYER[i]->x - ob->x);
|
|
dy = abs(PLAYER[i]->y - ob->y);
|
|
dz = abs(ob->temp2-PLAYER[i]->z);
|
|
|
|
if ((dx < dist) && (dy < dist) && (dz < 65))
|
|
{ob->flags &= ~FL_BLOCK;
|
|
//player->temp2 = 0;
|
|
playeron = 1;
|
|
if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH) &&
|
|
(levelheight<2) && (!(ob->flags & FL_DYING)))
|
|
{DamageThing(PLAYER[i],EnvironmentDamage(ob));
|
|
if (PLAYER[i]->hitpoints <= 0)
|
|
PLAYER[i]->flags |= FL_HBM;
|
|
Collision(PLAYER[i],ob,0,0);
|
|
M_CheckPlayerKilled(PLAYER[i]);
|
|
//NewState(ob,ob->state); //reset ticcount
|
|
return;
|
|
}
|
|
if (ob->state->condition & SF_UP)
|
|
{
|
|
PLAYER[i]->momentumz = -(4<<16);
|
|
if (PLAYER[i]->z < -30)
|
|
PLAYER[i]->z = -30;
|
|
}
|
|
else if (ob->state->condition & SF_DOWN)
|
|
{PLAYER[i]->momentumz = (4<<16);
|
|
if (PLAYER[i]->z >= nominalheight)
|
|
PLAYER[i]->z = nominalheight;
|
|
}
|
|
else
|
|
PLAYER[i]->momentumz = 0;
|
|
PLAYER[i]->whatever = ob;
|
|
ob->whatever = PLAYER[i];
|
|
//PLAYER[i]->flags |= FL_RIDING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//if (!playeron)
|
|
{if (ob->state->condition & SF_BLOCK)
|
|
ob->flags |= FL_BLOCK;
|
|
else
|
|
ob->flags &= ~FL_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void T_CrushDown(objtype*ob)
|
|
{int dx,dy,dz,i,playeron;
|
|
|
|
|
|
if ((!ob->ticcount) && (ob->state->condition & SF_SOUND)&&
|
|
areabyplayer[ob->areanumber])
|
|
SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
|
|
|
|
ob->temp2 = ob->z;
|
|
playeron = 0;
|
|
for(i=0;i<numplayers;i++)
|
|
{dx = abs(PLAYER[i]->x - ob->x);
|
|
dy = abs(PLAYER[i]->y - ob->y);
|
|
dz = abs(PLAYER[i]->z - ob->z);
|
|
|
|
if ((dx < STANDDIST) && (dy < STANDDIST) && (dz < 20))
|
|
{//PLAYER[i]->temp2 = 0;
|
|
playeron = 1;
|
|
ob->flags &= ~FL_BLOCK;
|
|
if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH) &&
|
|
(!(ob->flags & FL_DYING)))
|
|
{DamageThing(PLAYER[i],EnvironmentDamage(ob));
|
|
if (PLAYER[i]->hitpoints <= 0)
|
|
PLAYER[i]->flags |= FL_HBM;
|
|
Collision(PLAYER[i],ob,0,0);
|
|
M_CheckPlayerKilled(PLAYER[i]);
|
|
//NewState(ob,ob->state); //reset ticcount
|
|
return;
|
|
}
|
|
if ((ob->state->condition & SF_DOWN) &&
|
|
((ob->state != &s_columndowndown1) &&
|
|
(ob->state != s_columndowndown1.next)))
|
|
{PLAYER[i]->temp2 = COLUMNCRUSH;
|
|
PLAYER[i]->whatever = ob;
|
|
}
|
|
}
|
|
|
|
}
|
|
//if (!playeron)
|
|
{if (ob->state->condition & SF_BLOCK)
|
|
ob->flags |= FL_BLOCK;
|
|
else
|
|
ob->flags &= ~FL_BLOCK;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void T_Explosion(objtype* ob)
|
|
{
|
|
int momx,momy,momz;
|
|
int dx,dy,dz;
|
|
int pdamage,dist,blastradius=0x20000,
|
|
fatalradius=0x9000,impulse,damage,
|
|
scalefactor;
|
|
|
|
|
|
|
|
statobj_t* checkstat;
|
|
objtype* check,*owner;
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
damage = EXPLOSION_DAMAGE;
|
|
owner = (objtype*)(ob->whatever);
|
|
if ((ob->temp3) && (ob->obclass == p_firebombobj))
|
|
{
|
|
SpawnFirebomb(ob,damage,0);
|
|
ob->temp3 = 0;
|
|
}
|
|
//================== check surrounding actors ============================//
|
|
|
|
|
|
for(check = firstactive;check;check=check->nextactive)
|
|
{
|
|
if (check == ob)
|
|
continue;
|
|
|
|
// if (check == owner)
|
|
// continue;
|
|
|
|
dx = abs(check->x - ob->x);
|
|
if (dx > blastradius)
|
|
continue;
|
|
|
|
dy = abs(ob->y - check->y);
|
|
if (dy > blastradius)
|
|
continue;
|
|
|
|
dz = ((abs(ob->z - check->z))<<10);
|
|
if (dz > blastradius)
|
|
continue;
|
|
|
|
|
|
if (check->flags & FL_HEAD)
|
|
continue;
|
|
|
|
if (check->flags & FL_DYING)
|
|
continue;
|
|
|
|
if (!(check->flags & FL_SHOOTABLE))
|
|
continue;
|
|
|
|
|
|
|
|
if ((check->obclass >= roboguardobj) && (check->obclass != b_darkmonkobj))
|
|
//(check->obclass <= wallopobj))
|
|
continue;
|
|
|
|
|
|
if (!CheckLine(ob,check,SIGHT))
|
|
continue;
|
|
|
|
|
|
if (check->obclass == NMEsaucerobj)
|
|
{
|
|
NewState(check,&s_explosion1);
|
|
check->flags &= ~FL_SHOOTABLE;
|
|
return;
|
|
}
|
|
#if 0
|
|
dist = FindDistance(dx,dy);
|
|
scalefactor = (blastradius-dist)>>4;
|
|
if (scalefactor > 0xffff)
|
|
scalefactor = 0xffff;
|
|
pdamage = FixedMul(damage,scalefactor);
|
|
#endif
|
|
//#if 0
|
|
//magdx = abs(dx);
|
|
//magdy = abs(dy);
|
|
|
|
dist = Find_3D_Distance(dx,dy,dz);
|
|
SoftError("\ndist: %x\n",dist);
|
|
|
|
|
|
//if (dist < 0x10000)
|
|
// dist = 0x10000;
|
|
|
|
scalefactor = FixedDiv2(1<<16,FixedMul(dist,dist));
|
|
//scalefactor = FixedDiv2(1<<16,dist);
|
|
|
|
if (scalefactor > 0x12000)
|
|
scalefactor = 0x12000;
|
|
pdamage = FixedMul(damage,scalefactor);
|
|
SoftError("\ndamage: %d, scalefactor: %x\n",pdamage,scalefactor);
|
|
//#endif
|
|
impulse = FixedMul(EXPLOSION_IMPULSE,scalefactor);
|
|
if (check->obclass == playerobj)
|
|
{
|
|
check->target = owner;
|
|
if (check->flags & FL_AV)
|
|
pdamage = 0;
|
|
}
|
|
|
|
if (check->obclass < roboguardobj)
|
|
{
|
|
SoftError("\nhitpoints before: %d",check->hitpoints);
|
|
DamageThing(check,pdamage);
|
|
SoftError("\nhitpoints after: %d",check->hitpoints);
|
|
if ((check->hitpoints <= 0) && (gamestate.violence == vl_excessive) &&
|
|
((ob->obclass == p_firebombobj) ||
|
|
((dx < fatalradius) && (dy < fatalradius) && (dz < 20)))
|
|
)
|
|
check->flags |= FL_HBM;
|
|
|
|
GetMomenta(check,ob,&momx,&momy,&momz,impulse);
|
|
//Debug("\nhitmomx = %d, hitmomy = %d",momx,momy);
|
|
|
|
/*if (M_ISACTOR(owner) &&
|
|
(owner->obclass == playerobj) &&
|
|
(check->hitpoints <=0)
|
|
)
|
|
GivePoints(starthitpoints[gamestate.difficulty][check->obclass]*5);
|
|
*/
|
|
Collision(check,owner,momx,momy);
|
|
check->momentumz += (momz<<6);
|
|
if ((check->obclass == playerobj) && (check->flags & FL_DYING) &&
|
|
M_ISACTOR(owner))
|
|
{
|
|
if (owner->obclass == playerobj)
|
|
{
|
|
if (check->z != nominalheight)
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_missile_in_air,owner->dirchoosetime,check->dirchoosetime);
|
|
else
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,check->dirchoosetime);
|
|
}
|
|
else
|
|
BATTLE_CheckGameStatus(battle_player_killed,check->dirchoosetime);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (check->obclass != b_darkmonkobj) {
|
|
SoftError("non-darkmonk actor %d being helped by explosion",check->obclass);
|
|
}
|
|
check->hitpoints += pdamage;
|
|
}
|
|
}
|
|
|
|
//======================== check surrounding statics ================
|
|
|
|
|
|
for(checkstat = firstactivestat;checkstat;checkstat=checkstat->nextactive)
|
|
{
|
|
|
|
if ((!(checkstat->flags & FL_SHOOTABLE)) && (checkstat->itemnumber != stat_priestporridge))
|
|
continue;
|
|
|
|
if ((checkstat->itemnumber >= stat_lifeitem1) &&
|
|
(checkstat->itemnumber <= stat_lifeitem4))
|
|
continue;
|
|
|
|
dx = abs(checkstat->x - ob->x);
|
|
dy = abs(checkstat->y - ob->y);
|
|
dz = ((abs(checkstat->z - ob->z))<<10);
|
|
|
|
if ((dx < blastradius) && (dy < blastradius) && (dz < blastradius))
|
|
{
|
|
dist = Find_3D_Distance(dx,dy,dz)+0xc00;
|
|
|
|
if (dist < 0x10000)
|
|
dist = 0x10000;
|
|
|
|
scalefactor = FixedDiv2(1<<16,FixedMul(dist,dist));
|
|
pdamage = FixedMul(damage,scalefactor);
|
|
|
|
if (checkstat->itemnumber != stat_priestporridge)
|
|
DamageThing(checkstat,pdamage);
|
|
else if (!(checkstat->flags & FL_ACTIVE))
|
|
{
|
|
checkstat->flags |= FL_ACTIVE;
|
|
checkstat->count = 1;
|
|
//checkstat->numanims = 6;
|
|
SD_PlaySoundRTP(SD_COOKHEALTHSND,ob->x,ob->y);
|
|
}
|
|
}
|
|
}
|
|
|
|
//======================== check surrounding walls ================
|
|
{
|
|
int tilexlow,tilexhigh;
|
|
int tileylow,tileyhigh;
|
|
int radius =0x10000;
|
|
int x,y;
|
|
|
|
tilexlow = (int)((ob->x-radius) >>TILESHIFT);
|
|
tileylow = (int)((ob->y-radius) >>TILESHIFT);
|
|
|
|
tilexhigh = (int)((ob->x+radius) >>TILESHIFT);
|
|
tileyhigh = (int)((ob->y+radius) >>TILESHIFT);
|
|
|
|
for (y=tileylow;y<=tileyhigh;y++)
|
|
{
|
|
for (x=tilexlow;x<=tilexhigh;x++)
|
|
{
|
|
if ((tilemap[x][y]&0x8000) && (tilemap[x][y]&0x4000) && (abs(ob->z - nominalheight) < 32))
|
|
{
|
|
maskedwallobj_t * mw;
|
|
|
|
mw=maskobjlist[tilemap[x][y]&0x3ff];
|
|
if (mw->flags&MW_SHOOTABLE)
|
|
UpdateMaskedWall(tilemap[x][y]&0x3ff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpawnScreenEye(objtype *ob)
|
|
{
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_eye1,inertobj);
|
|
new->targettiley = 0;
|
|
new->targettilex = GameRandomNumber("eye position",0) + 20;
|
|
SCREENEYE = new;
|
|
//RemoveFromArea(new);
|
|
new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SpawnSuperFatalityGibs(objtype *ob,objtype *attacker)
|
|
{
|
|
int crazygibs = (GameRandomNumber("crazy gibs",0) % 6) + 4;
|
|
int i;
|
|
|
|
|
|
if ((MISCVARS->supergibflag == true) &&
|
|
((crazygibs == 9) || (ludicrousgibs == true))
|
|
)
|
|
|
|
{
|
|
int olddirect = MISCVARS->directgibs;
|
|
|
|
MISCVARS->directgibs = false;
|
|
if (ludicrousgibs == false)
|
|
{
|
|
if (attacker == player)
|
|
{
|
|
AddMessage("Ludicrous Gibs!",MSG_GAME);
|
|
if (!(attacker->flags&FL_DOGMODE))
|
|
SD_Play(PlayerSnds[locplayerstate->player]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MISCVARS->randgibspeed = true;
|
|
|
|
#ifdef MEDGIBS
|
|
SpawnParticles(ob,GUTS,150);
|
|
#else
|
|
SpawnParticles(ob,GUTS,75);
|
|
#endif
|
|
|
|
MISCVARS->randgibspeed = false;
|
|
}
|
|
SpawnParticles(ob,GUTS,40);
|
|
MISCVARS->directgibs = olddirect;
|
|
|
|
}
|
|
|
|
for (i=gt_head;i<=crazygibs;i++)
|
|
{
|
|
|
|
if (((ob->obclass == dfiremonkobj) || (ob->obclass == deathmonkobj)) &&
|
|
(i == gt_leg)
|
|
)
|
|
SpawnParticles(ob,gt_arm,1);
|
|
else
|
|
SpawnParticles(ob,i,1);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
boolean Vicious_Annihilation(objtype *ob, objtype *attacker)
|
|
{
|
|
if ((ob->flags & FL_HBM) && (gamestate.violence >= vl_high))
|
|
{
|
|
ob->shapeoffset = 0;
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
NewState(ob,(ob->obclass == playerobj)?(&s_remoteguts1):(&s_guts1));
|
|
SD_PlaySoundRTP(SD_ACTORSQUISHSND,ob->x,ob->y);
|
|
if (gamestate.violence == vl_excessive)
|
|
{
|
|
int numgibs;
|
|
objtype *prevlast;
|
|
|
|
numgibs = (GameRandomNumber("excessive guts",0) & 7) + 4;
|
|
//SoftError("\nnumgibs = %d,gamestate.difficulty = %d",numgibs,gamestate.difficulty);
|
|
prevlast = LASTACTOR;
|
|
MISCVARS->fulllightgibs = true;
|
|
SpawnParticles(ob,GUTS,numgibs);
|
|
MISCVARS->fulllightgibs = false;
|
|
for(prevlast = prevlast->next;prevlast;prevlast = prevlast->next)
|
|
prevlast->momentumz += (prevlast->momentumz >> 1);
|
|
|
|
if ((GameRandomNumber("super gib chance",0) < 100) ||
|
|
(ludicrousgibs == true)
|
|
)
|
|
{
|
|
MISCVARS->directgibs = true;
|
|
//MED
|
|
MISCVARS->gibgravity = GRAVITY/2;
|
|
// MISCVARS->gibgravity = GRAVITY*2;
|
|
MISCVARS->fulllightgibs = true;
|
|
SpawnSuperFatalityGibs(ob,attacker);
|
|
MISCVARS->fulllightgibs = false;
|
|
MISCVARS->gibgravity = -1;
|
|
MISCVARS->directgibs = false;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
if (ob->flags & FL_GODSTRUCK)
|
|
{
|
|
ob->shapeoffset = 0;
|
|
ob->flags |= (FL_FULLLIGHT);
|
|
ob->flags &= ~FL_COLORED;
|
|
ob->momentumx = ob->momentumy = ob->momentumz = 0;
|
|
KillActor(ob);
|
|
NewState(ob,&s_vaporized1);
|
|
return true;
|
|
}
|
|
|
|
if (ob->flags & FL_SKELETON)
|
|
{
|
|
KillActor(ob);
|
|
ob->shapeoffset = 0;
|
|
ob->flags &= ~FL_COLORED;
|
|
ob->momentumx = ob->momentumy = ob->momentumz = 0;
|
|
NewState(ob,&s_skeleton1);
|
|
SD_PlaySoundRTP(SD_ACTORBURNEDSND,ob->x,ob->y);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
=
|
|
= BeginEnemyFatality
|
|
=
|
|
========================
|
|
*/
|
|
|
|
|
|
|
|
void BeginEnemyFatality(objtype *ob,objtype *attacker)
|
|
{
|
|
|
|
|
|
if ((attacker == player) && (ob->obclass < (NUMENEMIES + 2)))
|
|
{
|
|
GivePoints(starthitpoints[gamestate.difficulty][ob->obclass]*5);
|
|
if (timelimitenabled)
|
|
timelimit+=VBLCOUNTER;
|
|
}
|
|
|
|
ob->flags |= FL_DYING;
|
|
ob->soundhandle = -1;
|
|
|
|
#if 0
|
|
if ((ob->obclass == blitzguardobj) &&
|
|
(ob->state->condition & SF_DOWN)
|
|
)
|
|
|
|
SD_Play(PlayerSnds[locplayerstate->player]);
|
|
#endif
|
|
|
|
if (Vicious_Annihilation(ob,attacker))
|
|
return;
|
|
|
|
if ((ob->obclass == patrolgunobj) && (ob->temp1 == -1))
|
|
NewState(ob,&s_robogrddie1);
|
|
else if (ob->obclass == collectorobj)
|
|
{
|
|
if ((!M_ISACTOR(attacker)) || (attacker->obclass != playerobj))
|
|
RespawnEluder();
|
|
else
|
|
BATTLE_CheckGameStatus(battle_shot_deluder,attacker->dirchoosetime);
|
|
|
|
NewState(ob,&s_explosion1);
|
|
MISCVARS->gibgravity = GRAVITY/2;
|
|
MISCVARS->fulllightgibs = true;
|
|
SpawnParticles(ob,gt_sparks,100);
|
|
MISCVARS->fulllightgibs = false;
|
|
MISCVARS->gibgravity = -1;
|
|
}
|
|
else
|
|
{
|
|
statetype *temp;
|
|
|
|
if ((ob->obclass == blitzguardobj) &&
|
|
(ob->state->condition & SF_FAKING)
|
|
)
|
|
{
|
|
NewState(ob,&s_blitzstruggledie1);
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
}
|
|
else if
|
|
((ob->obclass == blitzguardobj) &&
|
|
(ob->state->condition & SF_DOWN)
|
|
)
|
|
{
|
|
NewState(ob,&s_blitzplead7);
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
}
|
|
|
|
else if
|
|
((temp= M_S(DIE)) != NULL)
|
|
{
|
|
if (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob))
|
|
SET_DEATH_SHAPEOFFSET(ob);
|
|
NewState(ob,temp);
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
}
|
|
else
|
|
Error("Null dead state called in Collision, obclass %d",ob->obclass);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
========================
|
|
=
|
|
= BeginPlayerFatality
|
|
=
|
|
========================
|
|
*/
|
|
|
|
|
|
|
|
|
|
void BeginPlayerFatality(objtype *ob,objtype *attacker)
|
|
{
|
|
playertype *pstate;
|
|
M_LINKSTATE(ob,pstate);
|
|
|
|
|
|
ob->flags &= ~(FL_ELASTO|FL_GODMODE|FL_DOGMODE|FL_NOFRICTION|FL_RIDING);
|
|
|
|
ob->flags |= FL_DYING;
|
|
pstate->weapon = -1;
|
|
|
|
|
|
if (BATTLEMODE)
|
|
SD_PlaySoundRTP (SD_PLAYERTCDEATHSND+(pstate->player),ob->x,ob->y);
|
|
|
|
if (Vicious_Annihilation(ob,attacker) == false)
|
|
{
|
|
if (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob))
|
|
SET_DEATH_SHAPEOFFSET(ob);
|
|
|
|
NewState(ob,&s_remotedie1);
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
========================
|
|
=
|
|
= BeginEnemyHurt
|
|
=
|
|
========================
|
|
*/
|
|
|
|
|
|
void BeginEnemyHurt(objtype *ob)
|
|
{
|
|
statetype *temp;
|
|
|
|
if ((temp= M_S(COLLIDE1)) != NULL)
|
|
{
|
|
|
|
|
|
if ((ob->obclass == blitzguardobj) &&
|
|
(ob->state->condition & SF_FAKING)
|
|
)
|
|
{
|
|
ob->temp1 = 1;
|
|
ob->dirchoosetime = 0;
|
|
T_PlayDead(ob);
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
if ((ob->obclass == triadenforcerobj) &&
|
|
(GameRandomNumber("george pain chance",0) <
|
|
(50 + (gamestate.difficulty<<6))
|
|
)
|
|
)
|
|
{
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
if (LOW_VIOLENCE_PAIN_SHOULD_BE_SET(ob))
|
|
SET_PAIN_SHAPEOFFSET(ob);
|
|
|
|
if (GameRandomNumber("Collision",0) < 128)
|
|
NewState(ob,temp);
|
|
else
|
|
NewState(ob,M_S(COLLIDE2));
|
|
}
|
|
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
ob->ticcount = PAINTIME;
|
|
if (ob->obclass == strikeguardobj)
|
|
ob->ticcount >>= 1;
|
|
}
|
|
}
|
|
|
|
|
|
void Collision(objtype*ob,objtype *attacker,int hitmomentumx,int hitmomentumy)
|
|
{
|
|
int ocl;
|
|
|
|
if ((!(ob->flags & FL_SHOOTABLE)) || (ob->flags & FL_DYING))
|
|
return;
|
|
|
|
ocl = ob->obclass;
|
|
|
|
ob->momentumx += hitmomentumx;
|
|
ob->momentumy += hitmomentumy;
|
|
|
|
if ((ocl == playerobj) && (gamestate.battlemode == battle_Eluder))
|
|
return;
|
|
|
|
//insertion 5
|
|
|
|
if (ocl != playerobj)
|
|
{
|
|
if ((!(ob->flags & FL_ATTACKMODE)) && (TABLE_ACTOR(ob)))
|
|
ActivateEnemy(ob);
|
|
|
|
|
|
if (ob->hitpoints <= 0)
|
|
BeginEnemyFatality(ob,attacker);
|
|
|
|
else if (ocl != roboguardobj)// && (ob->state->think != T_Collide))
|
|
BeginEnemyHurt(ob);
|
|
}
|
|
else
|
|
{
|
|
playertype *pstate;
|
|
|
|
if ((ob->flags & FL_GODMODE) || (ob->flags & FL_DOGMODE) || godmode)
|
|
return;
|
|
|
|
M_LINKSTATE(ob,pstate);
|
|
if (pstate->health<=0)
|
|
BeginPlayerFatality(ob,attacker);
|
|
else
|
|
ob->flags |= FL_PAIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void T_BossExplosions(objtype*ob)
|
|
{
|
|
|
|
if (ob->temp1)
|
|
{if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
else
|
|
{int randtime,randangle,randdist,sound;
|
|
statetype *nstate;
|
|
|
|
ob->temp1 --;
|
|
randtime = GameRandomNumber("Boss Explosion Time",0);
|
|
ob->dirchoosetime = 10;
|
|
if (randtime < 128)
|
|
ob->dirchoosetime >>= 1;
|
|
randangle = (GameRandomNumber("Boss Explosion Angle",0) << 3);
|
|
randdist = (GameRandomNumber("Boss Explosion Distance",0) << 7)+0x4000;
|
|
sound = SD_EXPLODEFLOORSND;
|
|
if (randtime < 128)
|
|
{nstate = &s_explosion1;
|
|
sound++;
|
|
}
|
|
#if (SHAREWARE == 0)
|
|
else
|
|
nstate = &s_grexplosion1;
|
|
#endif
|
|
|
|
|
|
SpawnMissile(ob,inertobj,0,randangle,nstate,randdist);
|
|
SD_PlaySoundRTP(sound,new->x,new->y);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
gib_t RandomGutsType(void)
|
|
{
|
|
int rand = GameRandomNumber("gut random",0);
|
|
|
|
|
|
if (rand < 128)
|
|
return gt_organ;
|
|
|
|
//if (rand < 160)
|
|
return gt_rib;
|
|
|
|
//return gt_pinkorgan;
|
|
|
|
|
|
}
|
|
|
|
|
|
//MED
|
|
void SpawnParticles(objtype*ob,int which,int numparticles)
|
|
{
|
|
int randphi,randtheta,i,nspeed;
|
|
boolean eyespawned = false;
|
|
int gibtype;
|
|
int randadj;
|
|
|
|
|
|
if ((ob->z <= -64) && (sky == 0)) //shouldn't happen
|
|
return;
|
|
|
|
|
|
if (((which == GUTS) || (which == RANDOM)) && (gamestate.violence < vl_high))
|
|
which = gt_sparks;
|
|
|
|
gibtype = which;
|
|
|
|
|
|
for(i=0;i<numparticles;i++)
|
|
{
|
|
int ordertemp; /* DDOI - Watcom evaluates the mult order diff */
|
|
randphi = (GameRandomNumber("particle generate phi",0) << 3);
|
|
// randadj = RandomSign() * (GameRandomNumber("rand gib adjust",0) >> 4);
|
|
ordertemp = (GameRandomNumber("rand gib adjust",0) >> 4);
|
|
randadj = RandomSign() * ordertemp;
|
|
|
|
|
|
|
|
if (ob->z > (nominalheight - 32))
|
|
randphi &= ((ANGLES/2) - 1);
|
|
|
|
randtheta = (GameRandomNumber("particle generate theta",0) << 3);
|
|
nspeed = MISCVARS->gibspeed;
|
|
|
|
|
|
if (which == RANDOM)
|
|
{
|
|
if (GameRandomNumber("random gib",0) < 128)
|
|
gibtype = RandomGutsType();
|
|
else
|
|
gibtype = gt_sparks;
|
|
|
|
}
|
|
|
|
if ((which == GUTS) || (which == DISEMBOWEL))
|
|
{
|
|
|
|
gibtype = RandomGutsType();
|
|
if (which == DISEMBOWEL)
|
|
{
|
|
randphi>>=2;
|
|
randtheta=ob->temp1+(randtheta>>3)-(randtheta>>4);
|
|
}
|
|
|
|
}
|
|
|
|
if (lowmemory && (gibtype >= gt_rib) && (gibtype <= gt_limb))
|
|
gibtype = gt_organ;
|
|
|
|
if
|
|
(
|
|
// (gibtype >= gt_organ) && (gibtype <= gt_limb) &&
|
|
(MISCVARS->numgibs >= MAXGIBS)
|
|
)
|
|
return;
|
|
|
|
|
|
|
|
if (gibtype == gt_lsoul)
|
|
{
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_littlesoul,inertobj);
|
|
randphi = 0;
|
|
}
|
|
|
|
|
|
#if (SHAREWARE == 0)
|
|
else if (gibtype == gt_spit)
|
|
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_slop1,inertobj);
|
|
#endif
|
|
else
|
|
{
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_gibs1,inertobj);
|
|
new->shapeoffset = gibtype*12;
|
|
NewState(new,new->state);
|
|
}
|
|
|
|
if (MISCVARS->directgibs == true)
|
|
{
|
|
int dx,dy,dz;
|
|
|
|
dx = PLAYER[0]->x - ob->x;
|
|
dy = ob->y - PLAYER[0]->y;
|
|
randtheta = AngleBetween(ob,PLAYER[0]) +
|
|
(randadj<<4);
|
|
dz = 100 + (randadj<<3);
|
|
|
|
#ifdef MEDGIBS
|
|
nspeed = 0x2800;
|
|
#else
|
|
nspeed = 0x2800 + (randadj<<7);
|
|
#endif
|
|
|
|
randphi = atan2_appx(FindDistance(dx,dy),dz<<10);
|
|
}
|
|
|
|
|
|
if ((eyespawned == false) && (which == GUTS) &&
|
|
(ob->obclass != playerobj)
|
|
)
|
|
{
|
|
eyespawned = true;
|
|
new->flags |= FL_EYEBALL;
|
|
}
|
|
|
|
if ((gibtype >= gt_organ) && (gibtype <= gt_limb))
|
|
{
|
|
new->dirchoosetime = GIBVALUE;
|
|
MISCVARS->numgibs ++;
|
|
}
|
|
new->temp2 = gibtype;
|
|
new->temp3 = (MISCVARS->gibgravity == -1)?(GRAVITY):(MISCVARS->gibgravity);
|
|
|
|
new->speed = nspeed>>1;
|
|
|
|
#ifndef MEDGIBS
|
|
if (MISCVARS->randgibspeed == true)
|
|
new->speed += (randadj << 11);
|
|
#endif
|
|
|
|
// if (ob->state == &s_snakefireworks)
|
|
new->z = ob->z;
|
|
|
|
Fix(randphi);
|
|
Fix(randtheta);
|
|
|
|
Set_3D_Momenta(new,new->speed,randtheta,randphi);
|
|
new->momentumz <<= 6;
|
|
new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP|FL_NEVERMARK);
|
|
if (MISCVARS->fulllightgibs == true)
|
|
new->flags |= FL_FULLLIGHT;
|
|
new->dir = west;
|
|
new->whatever = ob;
|
|
MakeActive(new);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_SlideDownScreen(objtype *ob)
|
|
{
|
|
|
|
ob->targettiley += 12;
|
|
if (ob->targettiley > 300)
|
|
{
|
|
NewState(ob,&s_megaremove);
|
|
SCREENEYE = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
void T_SpawnSoul(objtype*ob)
|
|
{
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_bigsoul,inertobj);
|
|
new->momentumz = -4000;
|
|
new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP|FL_NEVERMARK);
|
|
new->z = ob->z;
|
|
new->dir = west;
|
|
MakeActive(new);
|
|
|
|
SpawnParticles(ob,gt_lsoul,6);
|
|
|
|
|
|
}
|
|
|
|
|
|
void BloodDrip(objtype *ob,int tilex,int tiley)
|
|
{int dx,dy,x,y,scale;
|
|
|
|
dx = ob->tilex - tilex;
|
|
dy = ob->tiley - tiley;
|
|
|
|
if (!dy)
|
|
{
|
|
scale = (ob->momentumx)?(FixedDiv2(ob->momentumy,ob->momentumx)):(0);
|
|
x = (dx < 0)?(tilex << 16):((tilex+1) << 16);
|
|
y = FixedMul(x - ob->x,scale) + ob->y;
|
|
}
|
|
|
|
else if (!dx)
|
|
{
|
|
scale = (ob->momentumy)?(FixedDiv2(ob->momentumx,ob->momentumy)):(0);
|
|
y = (dy < 0)?(tiley << 16):((tiley+1) << 16);
|
|
x = FixedMul(y - ob->y,scale) + ob->x;
|
|
}
|
|
|
|
ob->temp2 = (GameRandomNumber("BloodDrip",0) << 9) + 0xc000;
|
|
ob->temp1 = (ob->z<<16);
|
|
SetFinePosition(ob,x,y);
|
|
ob->shapeoffset = 0;
|
|
NewState(ob,&s_blooddrip1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_BloodFall(objtype*ob)
|
|
{
|
|
ob->temp1 += ob->temp2;
|
|
ob->z = (ob->temp1 >> 16);
|
|
if (ob->z >= maxheight)
|
|
{
|
|
ob->shapeoffset = 12;
|
|
MISCVARS->numgibs--;
|
|
NewState(ob,&s_gibsdone1);
|
|
ob->z = nominalheight;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void T_Xylophone(objtype*ob)
|
|
{
|
|
if (!ob->ticcount)
|
|
SD_PlaySoundRTP(SD_ACTORSKELETONSND,ob->x,ob->y);
|
|
}
|
|
|
|
|
|
|
|
void T_ParticleGenerate(objtype*ob)
|
|
{
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
else
|
|
{
|
|
SetGibSpeed(0x3000);
|
|
SpawnParticles(ob,gt_sparks,(GameRandomNumber("particle count",0) % 10) + 7);
|
|
ResetGibSpeed();
|
|
ob->dirchoosetime = 10;
|
|
if (GameRandomNumber("particle generator choose time",0) < 128)
|
|
ob->dirchoosetime >>= 1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_Particle(objtype*ob)
|
|
{int dx,dy,dz;
|
|
|
|
ob->z += (ob->momentumz>>16);
|
|
|
|
if ((ob->z >= nominalheight) || (!ob->momentumz))
|
|
|
|
{
|
|
if (ob->z >= nominalheight)
|
|
ob->z = nominalheight;
|
|
//done:
|
|
if (ob->temp2 == gt_spit)
|
|
ob->state = NULL;
|
|
else
|
|
{
|
|
if (ob->dirchoosetime == GIBVALUE)
|
|
{
|
|
MISCVARS->numgibs--;
|
|
SD_PlaySoundRTP(GIBSOUND,ob->x,ob->y);
|
|
}
|
|
NewState(ob,&s_gibsdone1);
|
|
}
|
|
return;
|
|
}
|
|
else if ((ob->z < -64) && (sky == 0))
|
|
{
|
|
ob->momentumz = 1; //any positive value will do
|
|
ob->z = -64;
|
|
|
|
}
|
|
|
|
|
|
ob->momentumz += ob->temp3;
|
|
ActorMovement(ob);
|
|
|
|
|
|
if (!BATTLEMODE)
|
|
{
|
|
dx = abs(ob->x - PLAYER[0]->x);
|
|
dy = abs(ob->y - PLAYER[0]->y);
|
|
dz = abs(ob->z - PLAYER[0]->z);
|
|
|
|
#if (SHAREWARE==0)
|
|
if ((ob->flags & FL_EYEBALL) && (dx < 0x20000) && (dy < 0x20000) &&
|
|
(dz < 64) && (GameRandomNumber("eye chance",0) < 15) &&
|
|
(SCREENEYE == NULL) && (locplayerstate->weapon != wp_dog)
|
|
)
|
|
#else
|
|
if ((ob->flags & FL_EYEBALL) && (dx < 0x20000) && (dy < 0x20000) &&
|
|
(dz < 64) && (GameRandomNumber("eye chance",0) < 15) &&
|
|
(SCREENEYE == NULL)
|
|
)
|
|
#endif
|
|
SpawnScreenEye(ob);
|
|
}
|
|
|
|
//MoveActor(ob);
|
|
//if ((!ob->momentumx) && (!ob->momentumy))
|
|
//goto done;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void DropItemInEmptyTile(int item,int tilex,int tiley)
|
|
{
|
|
int stilex = tilex;
|
|
int stiley = tiley;
|
|
|
|
FindEmptyTile(&stilex,&stiley);
|
|
SpawnStatic(stilex,stiley,item,9);
|
|
LASTSTAT->flags |= FL_ABP;
|
|
MakeStatActive(LASTSTAT);
|
|
|
|
}
|
|
|
|
|
|
void KillActor(objtype*ob)
|
|
{int ocl;
|
|
|
|
ocl = ob->obclass;
|
|
|
|
|
|
//GivePoints(starthitpoints[gamestate.difficulty][ob->obclass]*5);
|
|
if ((ocl == highguardobj) &&
|
|
(GameRandomNumber("Drop mp40 chance",0) < 25))
|
|
{
|
|
DropItemInEmptyTile(stat_mp40,ob->tilex,ob->tiley);
|
|
}
|
|
|
|
else if ((ocl == blitzguardobj) && (ob->temp3))
|
|
{
|
|
DropItemInEmptyTile(ob->temp3,ob->tilex,ob->tiley);
|
|
LASTSTAT->ammo = ob->temp2;
|
|
}
|
|
|
|
if (actorat[ob->tilex][ob->tiley] == (void*)ob)
|
|
actorat[ob->tilex][ob->tiley] = NULL;
|
|
|
|
gamestate.killcount++;
|
|
ob->flags &= ~FL_SHOOTABLE;
|
|
ob->flags &= ~FL_BLOCK;
|
|
ob->flags |= FL_NEVERMARK;
|
|
#if (SHAREWARE == 0)
|
|
if (ocl == b_darksnakeobj)
|
|
{if (ob == SNAKEHEAD)
|
|
{SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
|
|
new->temp1 = 7000;
|
|
new->flags |= FL_ABP;
|
|
EXPLOSIONS = new;
|
|
NewState(ob,&s_darkmonkheaddie1);
|
|
MakeActive(new);
|
|
ob->dirchoosetime = 0;
|
|
|
|
}
|
|
else
|
|
{objtype *temp;
|
|
|
|
SNAKEEND = (objtype*)(ob->target);
|
|
SNAKEEND->whatever = NULL;
|
|
NewState(ob,&s_explosion1);
|
|
for(temp=SNAKEHEAD;temp;temp = (objtype*)(temp->whatever))
|
|
temp->speed += 0x500;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{ob->whatever = NULL;
|
|
if (ob->obclass!=playerobj)
|
|
ob->target = NULL;
|
|
}
|
|
|
|
if ((ob->flags & FL_KEYACTOR) && (ocl!=playerobj) && (ocl != blitzguardobj))
|
|
{MISCVARS->KEYACTORSLEFT --;
|
|
if (!MISCVARS->KEYACTORSLEFT)
|
|
{SpawnNewObj(ob->tilex,ob->tiley,&s_timekeeper,inertobj);
|
|
new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void T_End(objtype *ob)
|
|
{
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
if (MAPSPOT(0,5,2) == LASTLEVELVALUE)
|
|
playstate = ex_gameover;
|
|
else
|
|
playstate = ex_bossdied;
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_Convert(objtype*ob)
|
|
{
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
if (ob->obclass == playerobj)
|
|
{
|
|
if (ob->state == &s_vaporized8)
|
|
{
|
|
T_SpawnSoul(ob);
|
|
NewState(ob,&s_voidwait);
|
|
}
|
|
else if (ob->state == &s_skeleton48)
|
|
{
|
|
playertype *pstate;
|
|
|
|
M_LINKSTATE(ob,pstate);
|
|
if ((pstate->falling == true) ||
|
|
(!ob->momentumz)
|
|
)
|
|
NewState(ob,&s_ashwait);
|
|
else
|
|
CheckPlayerSpecials(ob);
|
|
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ob->state == &s_vaporized8)
|
|
T_SpawnSoul(ob);
|
|
else if (ob->state == &s_skeleton48)
|
|
TurnActorIntoSprite(ob);
|
|
}
|
|
}
|
|
|
|
void TurnActorIntoSprite(objtype *ob)
|
|
{statobj_t*temp;
|
|
objtype *tactor;
|
|
|
|
|
|
if (!firstemptystat)
|
|
temp = (statobj_t*)Z_LevelMalloc(sizeof(statobj_t),PU_LEVELSTRUCT,NULL);
|
|
|
|
else
|
|
{temp = lastemptystat;
|
|
//SoftError("\nfree actor available");
|
|
RemoveFromFreeStaticList(lastemptystat);
|
|
}
|
|
|
|
|
|
if (temp)
|
|
{
|
|
if ((ob->obclass == blitzguardobj) &&
|
|
((ob->flags & FL_PLEADING) || (ob->flags & FL_UNDEAD))
|
|
)
|
|
MISCVARS->NUMBEGGINGKEVINS = 0;
|
|
|
|
|
|
if (ob->obclass == roboguardobj)
|
|
{for(tactor=firstareaactor[ob->areanumber];tactor;tactor=tactor->nextinarea)
|
|
{if (tactor == ob)
|
|
continue;
|
|
if (tactor->obclass != ob->obclass)
|
|
continue;
|
|
|
|
if (tactor->flags & FL_DYING)
|
|
continue;
|
|
if (!tactor->state->think)
|
|
NewState(tactor,UPDATE_STATES[PATH][tactor->obclass-lowguardobj]);
|
|
|
|
}
|
|
}
|
|
|
|
memset(temp,0,sizeof(*temp));
|
|
temp->shapenum = ob->shapenum;
|
|
temp->linked_to = -1;
|
|
temp->whichstat = statcount ++;
|
|
SetFinePosition(temp,ob->x,ob->y);
|
|
temp->areanumber = MAPSPOT(temp->tilex,temp->tiley,0)-AREATILE;
|
|
// if ((temp->areanumbers<=0) || (temp->areanumber>NUMAREAS))
|
|
// Error ("Sprite at x=%ld y=%ld type=%ld has an illegal areanumber\n",tilex,tiley,mtype);
|
|
|
|
temp->visspot = &spotvis[temp->tilex][temp->tiley];
|
|
temp->which = SPRITE;
|
|
temp->itemnumber = -1;
|
|
temp->flags = FL_DEADBODY;
|
|
if (ob->flags & FL_COLORED)
|
|
{playertype *pstate;
|
|
|
|
M_LINKSTATE(ob,pstate);
|
|
temp->flags |= FL_COLORED;
|
|
temp->hitpoints = pstate->uniformcolor;
|
|
}
|
|
temp->z = ob->z;
|
|
AddStatic(temp);
|
|
// sprites[temp->tilex][temp->tiley] = temp;
|
|
|
|
if (areabyplayer[temp->areanumber])
|
|
{temp->flags |= FL_ABP;
|
|
MakeStatActive(temp);
|
|
}
|
|
if (ob->state != &s_guts12)
|
|
actorat[ob->tilex][ob->tiley] = temp;
|
|
ob->state = NULL; // say goodbye actor
|
|
}
|
|
else
|
|
Error("Z_LevelMalloc failed in TurnActorIntoSprite!");
|
|
|
|
}
|
|
|
|
|
|
void T_Blood(objtype*ob)
|
|
{
|
|
if (ob->dirchoosetime)
|
|
{ob->dirchoosetime --;
|
|
return;
|
|
}
|
|
|
|
ob->dirchoosetime = 35 + (GameRandomNumber("blood time",0) % 20);
|
|
NewState(ob,&s_deadblood1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ActorDeath(objtype*ob)
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if (ob->obclass == b_heinrichobj)
|
|
{
|
|
KillActor(ob);
|
|
ob->temp1 = ob->dirchoosetime = 5;//10; // init. spin counter for heinrich
|
|
ob->temp3 = 7; //number of times to stay at fast spin
|
|
ob->temp2 = ob->dir; //temp2 holds orig. dir.
|
|
}
|
|
|
|
else if (ob->obclass == b_robobossobj)
|
|
{
|
|
objtype *wheels,*head;
|
|
|
|
|
|
head = (objtype*)(ob->whatever);
|
|
wheels = (objtype*)(ob->target);
|
|
head->flags &= ~(FL_HEAD|FL_SHOOTABLE|FL_BLOCK);
|
|
head->temp2 = 5;
|
|
head->flags |= (FL_NOFRICTION|FL_CRAZY);
|
|
// head->obclass = inertobj;
|
|
//RemoveObj(wheels); // remove wheels
|
|
KillActor(ob);
|
|
ob->whatever = head;
|
|
ob->target = wheels;
|
|
//ob->temp1 = 25;
|
|
//ob->shapeoffset = 0;
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
|
|
new->temp1 = 18;
|
|
new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
//ob->state = NULL;
|
|
NewState(ob,&s_NMEdeathbuildup);
|
|
}
|
|
else
|
|
#endif
|
|
if ((ob->state == ob->state->next) &&
|
|
(ob->flags & FL_DYING)
|
|
)
|
|
{
|
|
KillActor(ob);
|
|
TurnActorIntoSprite(ob);
|
|
if (LASTSTAT->z < nominalheight)
|
|
{
|
|
if ((!IsPlatform(LASTSTAT->tilex,LASTSTAT->tiley)) &&
|
|
(DiskAt(LASTSTAT->tilex,LASTSTAT->tiley) == NULL)
|
|
)
|
|
{
|
|
SpawnParticles(ob,GUTS,10 + gamestate.difficulty);
|
|
RemoveStatic(LASTSTAT);
|
|
}
|
|
}
|
|
/*
|
|
else if ((GameRandomNumber("blood spray",0) < 300) && areabyplayer[ob->areanumber])
|
|
{ob->shapeoffset = 0;
|
|
ob->temp2 = ob->temp3 = 0;
|
|
ob->temp1 = 10;
|
|
NewState(ob,&s_deadblood1);
|
|
return;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void BeginPostPainAction(objtype *ob)
|
|
{
|
|
|
|
if ((ob->obclass == strikeguardobj) &&
|
|
(ob->target == (void*)PLAYER[0])
|
|
)
|
|
{//ob->target = NULL;
|
|
if (LOW_VIOLENCE_DEATH_IS_SET(ob))
|
|
RESET_DEATH_SHAPEOFFSET(ob);
|
|
|
|
if (GameRandomNumber("T_Collide",0) < 128)
|
|
NewState(ob,&s_strikerollright1);
|
|
else
|
|
NewState(ob,&s_strikerollleft1);
|
|
|
|
SelectRollDir(ob);
|
|
|
|
if (ob->dirchoosetime)
|
|
{
|
|
SD_PlaySoundRTP(SD_STRIKEROLLSND,ob->x,ob->y);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
if (LOW_VIOLENCE_PAIN_IS_SET(ob))
|
|
RESET_PAIN_SHAPEOFFSET(ob);
|
|
|
|
if (ob->obclass < roboguardobj)
|
|
ob->flags &= ~FL_NOFRICTION;
|
|
|
|
if (
|
|
(ob->obclass == blitzguardobj) &&
|
|
(gamestate.violence == vl_excessive) &&
|
|
(GameRandomNumber("blitzplead",0) < 128) &&
|
|
(MISCVARS->NUMBEGGINGKEVINS == 0) &&
|
|
(ob->flags & FL_TARGET) &&
|
|
|
|
(ob->hitpoints < (starthitpoints[gamestate.difficulty][ob->obclass] >> 1)) &&
|
|
(ob->momentumz == 0) &&
|
|
(!(ob->flags & FL_UNDEAD))
|
|
)
|
|
|
|
{
|
|
NewState(ob,&s_blitzplead1);
|
|
MISCVARS->NUMBEGGINGKEVINS = 1;
|
|
ob->momentumx = ob->momentumy = 0;
|
|
ob->flags |= FL_PLEADING;
|
|
ob->flags &= ~FL_TARGET;
|
|
ob->dirchoosetime = 165;
|
|
ob->hitpoints = 1;
|
|
}
|
|
|
|
else
|
|
{
|
|
NewState(ob,M_S(CHASE));
|
|
ob->targettilex = ob->targettiley = 0;
|
|
ob->dirchoosetime = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_Collide(objtype*ob)
|
|
{
|
|
if (!(ob->flags & FL_SHOOTABLE))
|
|
return;
|
|
|
|
ActorMovement(ob);
|
|
|
|
if (ob->state == NULL)
|
|
return;
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
if (ob->hitpoints <= 0)
|
|
{
|
|
|
|
if ((ob->soundhandle == -1) &&
|
|
(!ob->ticcount) &&
|
|
(ob->state->next->tictime == 0)
|
|
)
|
|
{
|
|
ob->soundhandle = SD_PlaySoundRTP(ACTORTHUDSND,ob->x,ob->y);
|
|
|
|
}
|
|
|
|
if (ob->momentumx || ob->momentumy || ob->momentumz)
|
|
return;
|
|
|
|
ActorDeath(ob);
|
|
return;
|
|
}
|
|
|
|
BeginPostPainAction(ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=========================================================================
|
|
=
|
|
= Special Blitzguard Functions
|
|
=
|
|
=========================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
=================
|
|
=
|
|
= T_Plead
|
|
=
|
|
=================
|
|
*/
|
|
|
|
|
|
|
|
void T_Plead(objtype*ob)
|
|
{
|
|
int handle;
|
|
|
|
ActorMovement(ob);
|
|
if (ob->dirchoosetime)
|
|
{
|
|
if (!(ob->dirchoosetime & 31))
|
|
{
|
|
int random = GameRandomNumber("blitz plead sound",0);
|
|
if (random < 80)
|
|
SD_PlaySoundRTP(SD_BLITZPLEADSND,ob->x,ob->y);
|
|
else if (random < 160)
|
|
SD_PlaySoundRTP(SD_BLITZPLEAD1SND,ob->x,ob->y);
|
|
else
|
|
SD_PlaySoundRTP(SD_BLITZPLEAD2SND,ob->x,ob->y);
|
|
}
|
|
ob->dirchoosetime --;
|
|
return;
|
|
}
|
|
ob->hitpoints = (starthitpoints[gamestate.difficulty][blitzguardobj]>>1);
|
|
//ob->flags |= FL_DYING;
|
|
ob->flags |= FL_UNDEAD;
|
|
SET_DEATH_SHAPEOFFSET(ob);
|
|
NewState(ob,&s_blitzfakedie1);
|
|
ob->flags &= ~FL_PLEADING;
|
|
ob->dirchoosetime = (GameRandomNumber("blitz fake time",0) >> 2) + 70;
|
|
handle = SD_PlaySoundRTP(SD_BLITZOUCHSND,ob->x,ob->y);
|
|
SD_SetSoundPitch (handle,-500);
|
|
ob->temp1 = 0;
|
|
ob->temp1 = (GameRandomNumber("blitz visible rise",0) < 60);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
=
|
|
= T_ReallyDead
|
|
=
|
|
=================
|
|
*/
|
|
|
|
|
|
void T_ReallyDead(objtype *ob)
|
|
{
|
|
ActorMovement(ob);
|
|
if ((!ob->ticcount) && (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob)))
|
|
SET_DEATH_SHAPEOFFSET(ob);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
=
|
|
= T_PlayDead
|
|
=
|
|
=================
|
|
*/
|
|
|
|
|
|
|
|
void T_PlayDead(objtype *ob)
|
|
{
|
|
int dangle;
|
|
|
|
ob->flags &= ~FL_DYING;
|
|
|
|
ActorMovement(ob);
|
|
if (ob->dirchoosetime)
|
|
{
|
|
ob->dirchoosetime--;
|
|
return;
|
|
}
|
|
|
|
dangle = abs(player->angle - AngleBetween(player,ob));
|
|
if (dangle > ANGLES/2)
|
|
dangle = ANGLES - dangle;
|
|
|
|
if (ob->temp1 || (dangle > ANGLES/4))
|
|
{
|
|
if (LOW_VIOLENCE_DEATH_IS_SET(ob))
|
|
RESET_DEATH_SHAPEOFFSET(ob);
|
|
ob->temp1 = 0;
|
|
|
|
NewState(ob,&s_blitzrise2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void AdjustAngle(int maxadjust, short int *currangle,int targetangle)
|
|
{
|
|
int dangle,i,magangle;
|
|
|
|
for(i=0;i<maxadjust;i++)
|
|
{
|
|
dangle = *currangle - targetangle;
|
|
|
|
if (dangle)
|
|
{
|
|
magangle = abs(dangle);
|
|
if (magangle > (ANGLES/2))
|
|
{
|
|
if (dangle > 0)
|
|
(*currangle) ++;
|
|
else
|
|
(*currangle) --;
|
|
}
|
|
else
|
|
{
|
|
if (dangle > 0)
|
|
(*currangle) --;
|
|
else
|
|
(*currangle) ++;
|
|
}
|
|
Fix(*currangle);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ResolveMinimumDistance(objtype *heatseeker, objtype *potential_target,
|
|
int *currmin)
|
|
{
|
|
int currdist,angle,magangle;
|
|
|
|
|
|
currdist = FindDistance((heatseeker->x-potential_target->x),
|
|
(heatseeker->y-potential_target->y));
|
|
angle = AngleBetween(heatseeker,potential_target);
|
|
|
|
|
|
if (heatseeker->obclass != p_godballobj)
|
|
{
|
|
magangle = abs(heatseeker->angle - angle);
|
|
if (magangle > VANG180)
|
|
magangle = ANGLES - magangle;
|
|
if (magangle > ANGLESDIV8)
|
|
return;
|
|
}
|
|
|
|
if (currdist > LOOKAHEAD)
|
|
return;
|
|
|
|
if (currdist < (*currmin))
|
|
{
|
|
*currmin = currdist;
|
|
heatseeker->target = potential_target;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void HeatSeek(objtype*ob)
|
|
{int xydist;
|
|
|
|
int mindist;
|
|
objtype* tactor;
|
|
objtype* owner;
|
|
statobj_t* tstat;
|
|
int angle,dz,yzangle,adjust;
|
|
int dx,dy;
|
|
|
|
|
|
owner=(objtype *)ob->whatever;
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
|
|
else
|
|
{
|
|
if (!ob->target)
|
|
{mindist = 0x7fffffff;
|
|
|
|
for (tactor=firstactive;tactor;tactor=tactor->nextactive)
|
|
{
|
|
if (tactor == owner)
|
|
continue;
|
|
|
|
if (tactor->flags & FL_HEAD)
|
|
continue;
|
|
|
|
if ((tactor == ob) ||
|
|
(!(tactor->flags & FL_SHOOTABLE)) ||
|
|
(tactor->flags & FL_DYING))
|
|
continue;
|
|
|
|
if (!CheckLine(ob,tactor,SIGHT))
|
|
continue;
|
|
|
|
if ((tactor->obclass == bladeobj) || (tactor->obclass == NMEsaucerobj))
|
|
continue;
|
|
|
|
|
|
ResolveMinimumDistance(ob,tactor,&mindist);
|
|
|
|
}
|
|
|
|
if (ob->obclass != p_godballobj)
|
|
{
|
|
for(tstat=firstactivestat;tstat;tstat=tstat->nextactive)
|
|
{
|
|
if (!(tstat->flags & FL_HEAT))
|
|
continue;
|
|
|
|
if (!CheckLine(ob,tstat,SHOOT))
|
|
continue;
|
|
|
|
ResolveMinimumDistance(ob,(objtype*)tstat,&mindist);
|
|
}
|
|
}
|
|
|
|
|
|
if (!ob->target)
|
|
ob->dirchoosetime = 5;
|
|
}
|
|
|
|
else //if (ob->target != owner)
|
|
{
|
|
tactor = (objtype*)ob->target;
|
|
|
|
if ((!tactor->nextactive) && (!tactor->prevactive))
|
|
{
|
|
ob->target = NULL;
|
|
return;
|
|
}
|
|
dx = tactor->x - ob->x;
|
|
dy = ob->y - tactor->y;
|
|
dz = ob->z - tactor->z;
|
|
//xydist = FixedSqrtHP((FixedMul(dx,dx) + FixedMul(dy,dy))>>16);
|
|
xydist = FindDistance(dx,dy);
|
|
angle = atan2_appx(dx,dy);
|
|
adjust = (ob->obclass == p_godballobj)?(GODHAPT):(HAAPT);
|
|
AdjustAngle(adjust,&(ob->angle),angle);
|
|
ob->dir = angletodir[ob->angle];
|
|
ob->momentumx = FixedMul(ob->speed,costable[ob->angle]);
|
|
ob->momentumy = -FixedMul(ob->speed,sintable[ob->angle]);
|
|
|
|
yzangle = atan2_appx(xydist,(dz<<10));
|
|
adjust = (ob->obclass == p_godballobj)?(GODVAPT):(VAAPT);
|
|
AdjustAngle(adjust,&(ob->yzangle),yzangle);
|
|
ob->momentumz = -(FixedMul(ob->speed,sintable[ob->yzangle]));
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void Stagger(objtype*ob)
|
|
{
|
|
int randadj;
|
|
|
|
|
|
randadj = (int)(GameRandomNumber("Stagger",1) >> 3);
|
|
|
|
if (!ob->dirchoosetime)
|
|
{
|
|
ob->momentumz = ob->temp1 + (RandomSign() << 12);
|
|
ob->dirchoosetime = 6;
|
|
}
|
|
else
|
|
ob->dirchoosetime --;
|
|
|
|
if ((ob->z + (ob->momentumz >> 10)) > (maxheight-12))
|
|
ob->momentumz = -ob->momentumz;
|
|
else if ((ob->z < 5) && (!sky))
|
|
ob->z = 5;
|
|
|
|
ob->angle += (RandomSign()*randadj);
|
|
Fix(ob->angle);
|
|
ob->momentumx = FixedMul(ob->speed,costable[ob->angle]);
|
|
ob->momentumy = -FixedMul(ob->speed,sintable[ob->angle]);
|
|
ob->dir = angletodir[ob->angle];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpawnSplit(objtype *ob,int angle)
|
|
{
|
|
Fix(angle);
|
|
SpawnMissile(ob,p_heatseekobj,ob->speed,angle,&s_p_bazooka1,0x4000);
|
|
new->momentumz = ob->momentumz;
|
|
new->whatever = ob->whatever;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SplitMissile(objtype*ob)
|
|
{
|
|
|
|
SD_PlaySoundRTP(SD_SPLITSND,ob->x,ob->y);
|
|
if (ob->soundhandle != -1)
|
|
SD_StopSound(ob->soundhandle);
|
|
|
|
SpawnSplit(ob,ob->angle + ANGLES/12);
|
|
SpawnSplit(ob,ob->angle - ANGLES/12);
|
|
|
|
if (missobj == ob)
|
|
{
|
|
if (GameRandomNumber("split misscam",0)<128)
|
|
missobj = LASTACTOR;
|
|
else
|
|
missobj = LASTACTOR->prev;
|
|
}
|
|
|
|
ob->state=NULL; // get rid of current missile
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpawnMissileSmoke(objtype *ob)
|
|
{
|
|
if (!ValidAreanumber(AREANUMBER(ob->tilex,ob->tiley)))
|
|
return;
|
|
|
|
SpawnStatic(ob->tilex,ob->tiley,stat_missmoke,-1);
|
|
LASTSTAT->flags |= FL_ABP;
|
|
MakeStatActive(LASTSTAT);
|
|
SetFinePosition(LASTSTAT,ob->x,ob->y);
|
|
LASTSTAT->z = ob->z+3;
|
|
|
|
}
|
|
|
|
|
|
void T_Projectile (objtype *ob)
|
|
{
|
|
objtype *owner;
|
|
playertype * pstate;
|
|
|
|
owner = (objtype*)(ob->whatever);
|
|
|
|
if (owner->obclass == playerobj)
|
|
M_LINKSTATE(owner,pstate);
|
|
|
|
if ((ob->soundhandle != -1) &&
|
|
(!(oldpolltime & 7))
|
|
)
|
|
SD_PanRTP(ob->soundhandle,ob->x,ob->y);
|
|
#if (SHAREWARE == 0)
|
|
if (ob->obclass == h_mineobj)
|
|
{
|
|
if (!ob->dirchoosetime)
|
|
{
|
|
NewState(ob,&s_grexplosion1);
|
|
SD_PlaySoundRTP(SD_KRISTMINEHITSND,ob->x,ob->y);
|
|
}
|
|
else
|
|
ob->dirchoosetime --;
|
|
if (
|
|
(ob->state == &s_mine1) &&
|
|
(!ob->ticcount)
|
|
)
|
|
SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
if (ob->state == &s_p_grenade)
|
|
ob->momentumz += (GRAVITY>>6);
|
|
else if
|
|
(ob->state == &s_grenade_fall6)
|
|
{
|
|
NewState(ob,&s_explosion1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ob->obclass == p_drunkmissileobj)
|
|
{
|
|
if (ob->temp3 > 0)
|
|
{
|
|
ob->temp3 --;
|
|
Stagger(ob);
|
|
}
|
|
else
|
|
{
|
|
if (ob->target == NULL)
|
|
Stagger(ob);
|
|
HeatSeek(ob);
|
|
}
|
|
|
|
}
|
|
|
|
else if (ob->temp1 == NME_DRUNKTYPE)
|
|
|
|
Stagger(ob);
|
|
|
|
|
|
else if ((ob->obclass == p_heatseekobj) ||
|
|
(ob->obclass == dm_heatseekobj) ||
|
|
(ob->temp1 == NME_HEATSEEKINGTYPE) ||
|
|
(ob->obclass == p_godballobj)
|
|
)
|
|
HeatSeek(ob);
|
|
|
|
|
|
else if
|
|
((ob->obclass == p_splitmissileobj) &&
|
|
(!pstate->buttonstate[bt_attack])
|
|
)
|
|
{
|
|
SplitMissile(ob);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((!BATTLEMODE) &&
|
|
(!(ob->ticcount & 7)) &&
|
|
(ob->obclass != p_firewallobj) &&
|
|
(ob->obclass != p_kesobj) &&
|
|
(ob->obclass != p_godballobj) &&
|
|
((ob->obclass >= p_bazookaobj) || (ob->obclass == missileobj))
|
|
)// &&
|
|
|
|
SpawnMissileSmoke(ob);
|
|
|
|
MissileMovement(ob);
|
|
|
|
if (ob->obclass == grenadeobj)
|
|
|
|
{
|
|
if (ob->temp1 > 0)
|
|
ob->temp1 -= ob->speed;
|
|
else if (!(ob->flags & FL_DONE))
|
|
{
|
|
NewState(ob,&s_grenade_fall1);
|
|
ob->flags |= FL_DONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void StartFirewall(objtype*ob, int newz)
|
|
{
|
|
objtype *owner = (objtype*)(ob->whatever);
|
|
|
|
MISCVARS->firespawned = 0;
|
|
owner->temp1 = 0;
|
|
SpawnFirewall(ob,2,newz);
|
|
if (missobj == ob)
|
|
missobj = LASTACTOR;
|
|
NewState(ob,&s_megaremove);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MissileMovement(objtype*ob)
|
|
{int tryx, tryy,tryz;
|
|
|
|
tryx = ob->x + ob->momentumx;
|
|
tryy = ob->y + ob->momentumy;
|
|
tryz = ob->z + (ob->momentumz >> 10);
|
|
if (!MissileTryMove (ob, tryx, tryy, tryz))
|
|
return;
|
|
ob->z += (ob->momentumz >> 10);
|
|
MoveActor(ob);
|
|
}
|
|
|
|
|
|
|
|
#define DetonateMissile(x,y) \
|
|
{MissileHit(x,y); \
|
|
return false; \
|
|
} \
|
|
|
|
#define QuietDetonate(ob) \
|
|
{ \
|
|
if (ob->soundhandle != -1) \
|
|
SD_StopSound(ob->soundhandle); \
|
|
if (ob == missobj) \
|
|
missobj = NULL; \
|
|
NewState(ob,&s_megaremove); \
|
|
return false; \
|
|
}
|
|
|
|
boolean MissileTryMove(objtype*ob,int tryx,int tryy,int tryz)
|
|
{
|
|
int tilexlow,tileylow,tilexhigh,tileyhigh,x,y,
|
|
trytilex,trytiley,dx,dy,dzt,dztp1,radius,
|
|
sprrad,actrad,tcl,ocl,oldsrad,area,zdist,
|
|
wall;
|
|
|
|
objtype *temp;
|
|
wall_t *tempwall;
|
|
doorobj_t *tempdoor;
|
|
int doorn;
|
|
statobj_t *tempstat;
|
|
boolean areatried[NUMAREAS] = {0};
|
|
|
|
sprrad = 0x4500;
|
|
actrad = ACTORSIZE+0x2800;
|
|
ocl = ob->obclass;
|
|
radius = PROJSIZE-0x2200;
|
|
if (ocl==wallfireobj)
|
|
radius-=0x3000;
|
|
trytilex = (tryx >> TILESHIFT);
|
|
trytiley = (tryy >> TILESHIFT);
|
|
|
|
|
|
if (IsWindow(trytilex,trytiley) || (!InMapBounds((tryx>>16),(tryy>>16))))
|
|
{
|
|
QuietDetonate(ob);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*/
|
|
//==== ceiling/floor clipping =================//
|
|
|
|
if (tryz < -30)
|
|
{if ((sky==0) || (ocl == inertobj))
|
|
{
|
|
DetonateMissile(ob,NULL);
|
|
}
|
|
/*
|
|
else
|
|
return true;
|
|
*/
|
|
/*
|
|
else
|
|
{
|
|
NewState(ob,&s_megaremove);
|
|
if (missobj == ob)
|
|
missobj = NULL;
|
|
|
|
return false;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
if (tryz > (maxheight-10))
|
|
{
|
|
if ((ocl == p_firewallobj) && (!(ob->flags & FL_ISFIRE)))
|
|
StartFirewall(ob,nominalheight);
|
|
else
|
|
MissileHit(ob,NULL);
|
|
return false;
|
|
}
|
|
|
|
//=============================================//
|
|
|
|
sprrad = PROJSIZE+0x1000;
|
|
|
|
|
|
tilexlow = (int)((tryx-radius) >>TILESHIFT);
|
|
tileylow = (int)((tryy-radius) >>TILESHIFT);
|
|
|
|
tilexhigh = (int)((tryx+radius) >>TILESHIFT);
|
|
tileyhigh = (int)((tryy+radius) >>TILESHIFT);
|
|
|
|
oldsrad = sprrad;
|
|
|
|
|
|
if (ocl == inertobj)
|
|
goto walls;
|
|
|
|
area = ob->areanumber;
|
|
areatried[area] = true;
|
|
actors:
|
|
for(temp=firstareaactor[area];temp;temp=temp->nextinarea)
|
|
{
|
|
if (temp == ob)
|
|
continue;
|
|
|
|
dx = abs(tryx - temp->x);
|
|
dy = abs(tryy - temp->y);
|
|
if ((dx > actrad) || (dy > actrad))
|
|
continue;
|
|
|
|
if (temp->flags & FL_HEAD)
|
|
continue;
|
|
|
|
if ((!(temp->flags & FL_BLOCK)) || (temp->flags & FL_DYING))
|
|
continue;
|
|
|
|
tcl = temp->obclass;
|
|
|
|
|
|
zdist = 32;
|
|
dzt = abs(tryz - temp->z);
|
|
|
|
if ((tcl == playerobj) && (temp->flags & FL_DOGMODE))
|
|
{
|
|
dzt = abs(tryz - (temp->z + 30));
|
|
zdist = 10;
|
|
}
|
|
else if (tcl == diskobj)
|
|
{
|
|
zdist = 50;
|
|
}
|
|
|
|
if (dzt > zdist)
|
|
continue;
|
|
|
|
|
|
|
|
//if ((ocl==wallfireobj) && (tcl==playerobj) && (temp->flags&FL_DOGMODE) && (dz>15))
|
|
// continue;
|
|
|
|
//if ((ocl==playerobj) &&
|
|
// (ob->whatever == (void*)temp))
|
|
//continue;
|
|
|
|
if (ob->whatever && (ob->whatever == temp->whatever))// &&
|
|
// (ocl == tcl))// missiles with same owner
|
|
// go through each other
|
|
continue;
|
|
|
|
if (!(ob->flags & FL_ISFIRE))
|
|
{
|
|
|
|
int random;
|
|
|
|
if (tcl != b_darkmonkobj)
|
|
{
|
|
MissileHit(ob,temp);
|
|
ob->target = NULL;
|
|
if (tcl == wallfireobj)
|
|
MissileHit(temp,NULL);
|
|
if (((ocl == p_kesobj) || (ocl == p_godballobj)) && (tcl < pillarobj))
|
|
continue;
|
|
else
|
|
return false;
|
|
}
|
|
random = GameRandomNumber("empower darkmonk",0);
|
|
#if (SHAREWARE == 0)
|
|
|
|
if (ocl == p_kesobj)
|
|
{
|
|
NewState(ob,&s_megaremove);
|
|
//ob->state = NULL;
|
|
temp->hitpoints += (((random>>3)+140)<<1);
|
|
CAP_OSCUROS_HITPOINTS(temp);
|
|
}
|
|
else if (ocl == p_firebombobj)
|
|
{
|
|
NewState(ob,&s_explosion1);
|
|
temp->hitpoints += (((random>>3)+70)<<1);
|
|
CAP_OSCUROS_HITPOINTS(temp);
|
|
|
|
ob->target = NULL;
|
|
}
|
|
else
|
|
{
|
|
NewState(ob,&s_explosion1);
|
|
temp->hitpoints += (((random>>3)+50)<<1);
|
|
CAP_OSCUROS_HITPOINTS(temp);
|
|
|
|
ob->target = NULL;
|
|
}
|
|
temp->temp3 = ocl;
|
|
temp->speed = 5*SPDPATROL;
|
|
NewState(temp,&s_darkmonkreact);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
else if (tcl < roboguardobj)
|
|
{if ((temp->z == nominalheight) &&
|
|
(!((tcl == playerobj) && ((temp->flags & FL_GODMODE) || (temp->flags & FL_DOGMODE) || godmode))))
|
|
{
|
|
if (tcl == playerobj)
|
|
{
|
|
playertype *pstate;
|
|
objtype *owner = (objtype*)(ob->whatever);
|
|
|
|
M_LINKSTATE(temp,pstate);
|
|
if (temp->flags & FL_AV)
|
|
{pstate->protectiontime = 1;
|
|
if (temp==player)
|
|
GM_UpdateBonus (pstate->protectiontime, false);
|
|
continue;
|
|
}
|
|
|
|
|
|
//temp->flags &= ~FL_COLORED;
|
|
pstate->health = 0;
|
|
pstate->weapon = -1;
|
|
if (owner->obclass == playerobj)
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,temp->dirchoosetime);
|
|
|
|
|
|
}
|
|
|
|
|
|
temp->flags |= FL_SKELETON;
|
|
temp->hitpoints = 0;
|
|
Collision(temp,ob->whatever,-temp->momentumx,-temp->momentumy);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
|
|
|
|
else
|
|
{
|
|
NewState(ob,&s_megaremove);
|
|
ob->target = NULL;
|
|
#if (SHAREWARE == 0)
|
|
if (tcl == b_darkmonkobj)
|
|
NewState(temp,&s_darkmonkfspark1);
|
|
#endif
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
for (y=tileylow;y<=tileyhigh;y++)
|
|
for (x=tilexlow;x<=tilexhigh;x++)
|
|
{
|
|
area = AREANUMBER(x,y);
|
|
if (ValidAreanumber(area) && (areatried[area]==false))
|
|
{
|
|
areatried[area] = true;
|
|
goto actors;
|
|
}
|
|
}
|
|
|
|
|
|
/******************* WALLS/PWALLS *****************************************/
|
|
|
|
walls:
|
|
|
|
for (y=tileylow;y<=tileyhigh;y++)
|
|
for (x=tilexlow;x<=tilexhigh;x++)
|
|
{
|
|
|
|
|
|
tempwall = (wall_t*)actorat[x][y];
|
|
wall=tilemap[x][y];
|
|
|
|
if (tempwall && M_ISWALL(tempwall) && (tempwall->which!=MWALL))
|
|
{if (ocl == h_mineobj)
|
|
{
|
|
if (WallCheck(ob->x-ob->momentumx, tryy))
|
|
{ob->momentumx = -ob->momentumx;
|
|
continue;
|
|
}
|
|
else if (WallCheck(tryx, ob->y-ob->momentumy))
|
|
{ob->momentumy = -ob->momentumy;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
DetonateMissile(ob,tempwall);
|
|
//MissileHit(ob,tempwall);
|
|
//return false;
|
|
}
|
|
|
|
|
|
tempstat = sprites[x][y];
|
|
sprrad = oldsrad;
|
|
|
|
if (tempstat &&
|
|
((tempstat->flags & FL_SHOOTABLE) || (tempstat->flags & FL_BLOCK)))
|
|
{
|
|
|
|
if ((tempstat->itemnumber >= stat_bcolumn) &&
|
|
(tempstat->itemnumber <= stat_icolumn))
|
|
sprrad += 0x5000;
|
|
|
|
dx = tryx - tempstat->x;
|
|
if ((dx < -sprrad) || (dx > sprrad))
|
|
continue;
|
|
|
|
dy = tryy - tempstat->y;
|
|
if ((dy < -sprrad) || (dy > sprrad))
|
|
continue;
|
|
|
|
//#define MINSTATZDIFF 60
|
|
|
|
dzt = abs(ob->z - tempstat->z);
|
|
dztp1 = abs(tryz - tempstat->z);
|
|
/*
|
|
if (ocl == p_firewallobj)// && (dztp1 <= MINSTATZDIFF))
|
|
{
|
|
if (ob->flags & FL_ISFIRE)
|
|
{
|
|
int cz = (ob->z - tempstat->z + MINSTATZDIFF);
|
|
|
|
if ((cz >= -MAXSTEPHEIGHT) && (cz <= 0))
|
|
{
|
|
ob->z = tempstat->z - MINSTATZDIFF;
|
|
tryz = ob->z + (ob->momentumz >> 16);
|
|
dzt = MINSTATZDIFF;
|
|
}
|
|
}
|
|
|
|
if ((dztp1 >= MINSTATZDIFF) || (dzt >= MINSTATZDIFF))
|
|
continue;
|
|
|
|
if (!(ob->flags & FL_ISFIRE))
|
|
{
|
|
StartFirewall(ob,tempstat->z - MINSTATZDIFF);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
else*/
|
|
{
|
|
if (dztp1 > 50)
|
|
continue;
|
|
|
|
DetonateMissile(ob,tempstat);
|
|
}
|
|
//MissileHit(ob,tempstat);
|
|
//return false;
|
|
}
|
|
}
|
|
|
|
|
|
//mwalls:
|
|
|
|
|
|
|
|
if (M_ISMWALL(trytilex,trytiley))
|
|
{
|
|
maskedwallobj_t * mw;
|
|
|
|
wall=tilemap[trytilex][trytiley];
|
|
//tempwall = (wall_t*)actorat[trytilex][trytiley];
|
|
|
|
|
|
mw=maskobjlist[wall&0x3ff];
|
|
|
|
if (!(mw->flags&MW_BLOCKING))
|
|
{
|
|
|
|
if ((levelheight > 1) &&
|
|
(((!(mw->flags & MW_ABOVEPASSABLE)) && (tryz <= 32)) ||
|
|
((!(mw->flags & MW_MIDDLEPASSABLE)) && (tryz > 25) && (tryz < nominalheight-32)) ||
|
|
((!(mw->flags & MW_BOTTOMPASSABLE)) && (tryz > maxheight - 74))
|
|
)
|
|
)
|
|
DetonateMissile(ob,NULL);
|
|
|
|
}
|
|
|
|
else if (mw->flags&MW_SHOOTABLE)
|
|
{
|
|
if (ob->z >= maxheight-64)
|
|
{
|
|
UpdateMaskedWall(tilemap[trytilex][trytiley]&0x3ff);
|
|
}
|
|
else
|
|
DetonateMissile(ob,NULL);
|
|
|
|
}
|
|
|
|
else
|
|
DetonateMissile(ob,NULL);
|
|
//MissileHit(ob,tempwall);
|
|
//return false;
|
|
}
|
|
|
|
|
|
|
|
/******************* DOOR STUFF ******************************************/
|
|
|
|
|
|
else if (M_ISDOOR(trytilex,trytiley))
|
|
{
|
|
doorn = tilemap[trytilex][trytiley];
|
|
tempdoor = doorobjlist[doorn&0x3ff];
|
|
if (tempdoor->position>=0x8000)
|
|
{
|
|
if (ob->z>maxheight-64)
|
|
return true;
|
|
}
|
|
DetonateMissile(ob,tempdoor);
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpawnFirewall(objtype*ob,int which,int newz)
|
|
{int i,j,count,newx,newy;
|
|
objtype* owner;
|
|
wall_t*tempwall;
|
|
statetype* frame;
|
|
int offset;
|
|
|
|
owner = (objtype*)(ob->whatever);
|
|
|
|
if ((owner->temp1 < 2) && (MISCVARS->firespawned < 14))
|
|
{for(i=0;i<=which;i++)
|
|
{
|
|
GetNewActor ();
|
|
MakeActive(new);
|
|
MISCVARS->firespawned ++;
|
|
new->obclass = p_firewallobj;
|
|
new->which = ACTOR;
|
|
new->areanumber = ob->areanumber;
|
|
MakeLastInArea(new);
|
|
offset = 0x6000;
|
|
if (!which)
|
|
new->temp1 = ob->temp1;
|
|
else if (i==1)
|
|
new->temp1 = ob->angle + ANGLES/4;
|
|
else if (i==2)
|
|
new->temp1 = ob->angle - ANGLES/4;
|
|
else
|
|
{
|
|
new->temp1 = 0;
|
|
offset = 0;
|
|
new->flags |= FL_DONE;
|
|
}
|
|
|
|
Fix(new->temp1);
|
|
|
|
new->speed = 0x8000;
|
|
new->angle = ob->angle;
|
|
ParseMomentum(new,new->angle);
|
|
newx = ob->x + FixedMul(offset,costable[new->temp1]);
|
|
newy = ob->y - FixedMul(offset,sintable[new->temp1]);
|
|
SetFinePosition(new,newx,newy);
|
|
SetVisiblePosition(new,newx,newy);
|
|
new->whatever = ob->whatever;
|
|
new->dirchoosetime = 2;
|
|
new->flags |= (FL_NEVERMARK|FL_ABP|FL_NOFRICTION);
|
|
count = (int)(GameRandomNumber("SpawFireWall",0) & 15);
|
|
|
|
for(frame = &s_fireunit1,j=0;j<count;frame = frame->next,j++);
|
|
|
|
NewState(new,frame);
|
|
new->flags |= FL_ISFIRE;
|
|
//SD_Play(SD_EXPL);
|
|
tempwall = (wall_t*)actorat[new->tilex][new->tiley];
|
|
new->z = newz;
|
|
if (tempwall && M_ISWALL(tempwall))
|
|
{
|
|
SetFinePosition(new,ob->x,ob->y);
|
|
SetVisiblePosition(new,ob->x,ob->y);
|
|
owner->temp1++;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void T_Firethink(objtype*ob)
|
|
{
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
else if (!(ob->flags & FL_DONE))
|
|
{
|
|
SpawnFirewall(ob,0,ob->z);
|
|
ob->flags |= FL_DONE;
|
|
}
|
|
|
|
MissileMovement(ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResolveRide(objtype *ob)
|
|
{
|
|
objtype *ride = (objtype*)(ob->whatever);
|
|
|
|
if (M_ISACTOR(ride) && (ob->obclass != playerobj))
|
|
{
|
|
if (ob->flags & FL_RIDING)
|
|
{
|
|
int dx,dy;
|
|
|
|
dx = ob->x - ride->x;
|
|
dy = ob->y - ride->y;
|
|
if ((dx < -MINACTORDIST) || (dx > MINACTORDIST) ||
|
|
(dy < -MINACTORDIST) || (dy > MINACTORDIST) )
|
|
{
|
|
ride->whatever = NULL;
|
|
ob->whatever = NULL;
|
|
ob->flags &= ~FL_RIDING;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MoveActor(objtype*ob)
|
|
{
|
|
int linked,oldarea,newarea,
|
|
tilex,tiley,oldtilex,oldtiley;
|
|
|
|
linked = 0;
|
|
|
|
ResolveRide(ob);
|
|
|
|
oldtilex = ob->tilex;
|
|
oldtiley = ob->tiley;
|
|
|
|
SetFinePosition(ob,ob->x+ob->momentumx,ob->y+ob->momentumy);
|
|
/*
|
|
if (ob->state == &s_explosion1)
|
|
Error("moving explosion"); */
|
|
|
|
if ((ob->obclass == playerobj) || (ob->flags & FL_NOFRICTION) || (ob->state->think == T_Collide) ||
|
|
(ob->obclass == b_heinrichobj) || (ob->obclass == h_mineobj))
|
|
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
|
|
|
|
|
|
if (ob->obclass == inertobj)
|
|
return;
|
|
|
|
if ((ob->obclass == b_darksnakeobj) && (ob != SNAKEHEAD))
|
|
{
|
|
oldarea = ob->areanumber;
|
|
newarea = SNAKEHEAD->areanumber;
|
|
if (oldarea != newarea)
|
|
{
|
|
RemoveFromArea(ob);
|
|
ob->areanumber = newarea;
|
|
MakeLastInArea(ob);
|
|
}
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
oldarea = ob->areanumber;
|
|
newarea = AREANUMBER(ob->tilex,ob->tiley);
|
|
if (!(ob->flags & (FL_NONMARK|FL_NEVERMARK)))
|
|
{
|
|
if ((oldtilex != ob->tilex) || (oldtiley != ob->tiley))
|
|
{
|
|
if (actorat[oldtilex][oldtiley] == (void*)ob)
|
|
actorat[oldtilex][oldtiley] = NULL;
|
|
if (actorat[ob->tilex][ob->tiley])
|
|
{
|
|
objtype* temp;
|
|
|
|
temp = (objtype*)actorat[ob->tilex][ob->tiley];
|
|
if (temp->which != SPRITE)
|
|
actorat[ob->tilex][ob->tiley] = ob;
|
|
}
|
|
else
|
|
actorat[ob->tilex][ob->tiley] = ob;
|
|
}
|
|
}
|
|
|
|
|
|
#define CheckAdjacentArea(x,y) \
|
|
{ \
|
|
if (InMapBounds(x,y)) \
|
|
{ \
|
|
temparea = AREANUMBER(x,y); \
|
|
if (ValidAreanumber(temparea)) \
|
|
newarea = temparea; \
|
|
} \
|
|
}
|
|
|
|
|
|
if (!ValidAreanumber(newarea)) //find empty tile
|
|
{
|
|
int temparea;
|
|
tilex = ob->tilex;
|
|
tiley = ob->tiley;
|
|
|
|
CheckAdjacentArea(tilex+1,tiley);
|
|
CheckAdjacentArea(tilex-1,tiley);
|
|
CheckAdjacentArea(tilex,tiley+1);
|
|
CheckAdjacentArea(tilex,tiley-1);
|
|
|
|
}
|
|
//Error("new area invalid for actor %d, class %d",
|
|
// ob-&objlist[0],ob->obclass);
|
|
|
|
|
|
//====================== swap in linked lists =====================
|
|
if (oldarea != newarea)
|
|
{
|
|
RemoveFromArea(ob);
|
|
ob->areanumber = newarea;
|
|
MakeLastInArea(ob);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SpawnPushColumn(int tilex,int tiley,int which,int dir, int linked)
|
|
{
|
|
if (which==0)
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_pushcolumn1,pillarobj);
|
|
// for (i=0;i<(levelheight-1);i++)
|
|
// SpawnStatic(tilex,tiley,stat_bcolumn,-(i<<6));
|
|
}
|
|
else if (which==1)
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_pushcolumn2,pillarobj);
|
|
// for (i=0;i<(levelheight-1);i++)
|
|
// SpawnStatic(tilex,tiley,stat_gcolumn,-(i<<6));
|
|
}
|
|
else
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_pushcolumn3,pillarobj);
|
|
// for (i=0;i<(levelheight-1);i++)
|
|
// SpawnStatic(tilex,tiley,stat_icolumn,-(i<<6));
|
|
}
|
|
PreCacheActor(pillarobj,which);
|
|
|
|
gamestate.secrettotal++;
|
|
new->speed = PILLARMOM;
|
|
new->temp1 = 0x20000;
|
|
new->temp2 = linked;
|
|
new->flags |= (FL_BLOCK|FL_NOFRICTION);
|
|
new->flags &= ~FL_SHOOTABLE;
|
|
new->flags |= FL_HEIGHTFLIPPABLE;
|
|
new->dir = dir;
|
|
if (dir != nodir)
|
|
ParseMomentum(new,dirangle8[new->dir]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpawnWallfire(int tilex, int tiley, int dir)
|
|
{int offx,offy;
|
|
|
|
|
|
GetNewActor();
|
|
new->speed = 0x2000;
|
|
SetTilePosition(new,tilex,tiley);
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
new->obclass = wallfireobj;
|
|
new->dir = dir*2;
|
|
new->flags |= (FL_BLOCK|FL_NOFRICTION|FL_NEVERMARK);
|
|
new->flags &= ~FL_SHOOTABLE;
|
|
new->which = ACTOR;
|
|
new->angle = dirangle8[new->dir];
|
|
offx = FixedMul(0x10000,costable[new->angle])>>TILESHIFT;
|
|
offy = -(FixedMul(0x10000,sintable[new->angle])>>TILESHIFT);
|
|
|
|
new->areanumber = MAPSPOT(new->tilex+offx,new->tiley+offy,0)-AREATILE;
|
|
MakeLastInArea(new);
|
|
|
|
NewState(new,&s_wallfireball);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpawnSneaky(int tilex,int tiley)
|
|
{
|
|
|
|
SpawnNewObj(tilex,tiley,&s_sneakydown,lowguardobj);
|
|
new->temp3 = SNEAKY;
|
|
if (!loadedgame)
|
|
gamestate.killtotal++;
|
|
StandardEnemyInit(new,north>>1);
|
|
|
|
PreCacheActor(lowguardobj,0);
|
|
}
|
|
|
|
|
|
void RespawnEluder(void)
|
|
{
|
|
int rand,count;
|
|
int nx,ny;
|
|
|
|
rand = (GameRandomNumber("eluder respawn",0) % NUMSPAWNLOCATIONS);
|
|
|
|
for(count=0;count < NUMSPAWNLOCATIONS;count++)
|
|
{
|
|
if (!actorat[SPAWNLOC[rand].x][SPAWNLOC[rand].y])
|
|
{
|
|
SpawnCollector(SPAWNLOC[rand].x,SPAWNLOC[rand].y);
|
|
return;
|
|
}
|
|
rand= ((rand + 1) % NUMSPAWNLOCATIONS);
|
|
|
|
}
|
|
|
|
//MED
|
|
nx = SPAWNLOC[rand].x;
|
|
ny = SPAWNLOC[rand].y;
|
|
FindEmptyTile(&nx,&ny);
|
|
SpawnCollector(nx,ny);
|
|
}
|
|
|
|
//****************************************************************************
|
|
//
|
|
//
|
|
//
|
|
//****************************************************************************
|
|
|
|
void SpawnCollector(int tilex, int tiley)
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if ( dopefish==true )
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_scottwander1,collectorobj);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SpawnNewObj(tilex,tiley,&s_collectorwander1,collectorobj);
|
|
}
|
|
|
|
new->flags |= (FL_SHOOTABLE|FL_BLOCK|FL_NOFRICTION|FL_FULLLIGHT);
|
|
new->hitpoints = 500;
|
|
new->speed = 0x3000;
|
|
new->dir = north;
|
|
new->dirchoosetime = 175;
|
|
new->z = PlatformHeight(tilex,tiley);
|
|
if (new->z == -10)
|
|
new->z = 0;
|
|
if (areabyplayer[new->areanumber])
|
|
{new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SelectDoorDir(objtype*ob)
|
|
{int dx,dy,actrad;
|
|
dirtype dtry1,dtry2,tdir,olddir;
|
|
|
|
dx= ob->targettilex - ob->x;
|
|
dy= ob->y - ob->targettiley;
|
|
olddir = ob->dir;
|
|
if ((abs(dx) < 0x4000) && (abs(dy) < 0x4000))
|
|
{ZEROMOM;
|
|
SetFinePosition(ob,ob->targettilex,ob->targettiley);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
ParseMomentum(ob,dirangle8[ob->temp2]);
|
|
ActorMovement(ob);
|
|
ob->temp2 = 0;
|
|
ob->temp1 = 20;
|
|
#if (SHAREWARE == 0)
|
|
if ( dopefish==true )
|
|
{
|
|
NewState(ob,&s_scottwander1);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
NewState(ob,&s_collectorwander1);
|
|
}
|
|
ob->targettilex = ob->targettiley = 0;
|
|
ob->dirchoosetime = 165;
|
|
return;
|
|
}
|
|
|
|
|
|
ZEROMOM;
|
|
|
|
ParseMomentum(ob,atan2_appx(dx,dy));
|
|
ActorMovement(ob);
|
|
if (ob->momentumx || ob->momentumy)
|
|
return;
|
|
actrad = ACTORSIZE;
|
|
dtry1=nodir;
|
|
dtry2=nodir;
|
|
|
|
if (dx> actrad)
|
|
dtry1= east;
|
|
else if (dx< -actrad)
|
|
dtry1= west;
|
|
if (dy> actrad)
|
|
dtry2= north;
|
|
else if (dy < -actrad)
|
|
dtry2= south;
|
|
|
|
|
|
if (abs(dy)>abs(dx))
|
|
{tdir=dtry1;
|
|
dtry1=dtry2;
|
|
dtry2=tdir;
|
|
}
|
|
|
|
|
|
if (dtry1 != nodir)
|
|
M_CHECKDIR(ob,dtry1);
|
|
|
|
if (dtry2 != nodir)
|
|
M_CHECKDIR(ob,dtry2);
|
|
|
|
if (dtry1 != nodir)
|
|
M_CHECKDIR(ob,dirorder[dtry1][NEXT]);
|
|
|
|
if (dtry2 != nodir)
|
|
M_CHECKDIR(ob,dirorder[dtry2][NEXT]);
|
|
|
|
for(tdir = dirorder[olddir][NEXT];tdir != olddir;tdir = dirorder[tdir][NEXT])
|
|
M_CHECKDIR(ob,tdir);
|
|
ob->dir = olddir;
|
|
|
|
}
|
|
|
|
|
|
boolean EluderCaught(objtype*ob)
|
|
{
|
|
objtype *temp;
|
|
int dx,dy,dz;
|
|
playertype *pstate;
|
|
int dist = 0xc000;
|
|
|
|
for(temp = PLAYER[0];temp != PLAYER[numplayers-1]->next;temp=temp->next)
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if (temp->state != &s_doguse)
|
|
continue;
|
|
#endif
|
|
|
|
dx = M_ABS(temp->x - ob->x);
|
|
if (dx > dist)
|
|
continue;
|
|
|
|
dy = M_ABS(temp->y - ob->y);
|
|
if (dy > dist)
|
|
continue;
|
|
|
|
dz = M_ABS(temp->z - ob->z);
|
|
if (dz > (dist>>10))
|
|
continue;
|
|
|
|
M_LINKSTATE(temp,pstate);
|
|
//if (DOGSCRATCH.attackinfo[pstate->attackframe].attack == at_pulltrigger)
|
|
{BATTLE_CheckGameStatus(battle_caught_eluder,temp->dirchoosetime);
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_itemspawn1,inertobj);
|
|
new->flags |= FL_ABP;
|
|
SetFinePosition(new,ob->x,ob->y);
|
|
SetVisiblePosition(new,ob->x,ob->y);
|
|
new->z = ob->z;
|
|
SD_PlaySoundRTP(SD_GETBONUSSND,ob->x,ob->y);
|
|
MakeActive(new);
|
|
NewState(ob,&s_megaremove);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void T_CollectorFindDoor(objtype*ob)
|
|
{
|
|
|
|
if (EluderCaught(ob))
|
|
return;
|
|
|
|
if (!(gamestate.TimeCount % 17))
|
|
SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y);
|
|
|
|
|
|
if ((ob->z != nominalheight) && (!IsPlatform(ob->tilex,ob->tiley)))
|
|
ZEROMOM;
|
|
|
|
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
else
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if ( dopefish==true )
|
|
{
|
|
NewState(ob,&s_scottwander1);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
NewState(ob,&s_collectorwander1);
|
|
}
|
|
ob->dirchoosetime = 165;
|
|
ob->targettilex = ob->targettiley = 0;
|
|
return;
|
|
}
|
|
|
|
if (ob->temp1)
|
|
{int dx,dy;
|
|
|
|
ob->temp1 --;
|
|
ActorMovement(ob);
|
|
dx = ob->targettilex-ob->x;
|
|
dy = ob->targettiley-ob->y;
|
|
if ((abs(dx) < 0x4000) && (abs(dy) < 0x4000))
|
|
{ZEROMOM;
|
|
SetFinePosition(ob,ob->targettilex,ob->targettiley);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
|
|
ParseMomentum(ob,dirangle8[ob->temp2]);
|
|
ActorMovement(ob);
|
|
ob->temp2 = 0;
|
|
ob->temp1 = 35;
|
|
#if (SHAREWARE == 0)
|
|
if ( dopefish==true )
|
|
{
|
|
NewState(ob,&s_scottwander1);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
NewState(ob,&s_collectorwander1);
|
|
}
|
|
ob->targettilex = ob->targettiley = 0;
|
|
ob->dirchoosetime = 165;
|
|
return;
|
|
}
|
|
if (NOMOM)
|
|
ob->temp1 = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
ob->temp1 = 5;
|
|
|
|
if (ob->targettilex || ob->targettiley)
|
|
SelectDoorDir(ob);
|
|
|
|
else
|
|
{int i;
|
|
doorobj_t* dptr;
|
|
|
|
//==========================================================================
|
|
#define SetCollectorTarget(xoffset,yoffset,newdir) \
|
|
{ \
|
|
ob->targettilex = ((dptr->tilex + (xoffset)) << TILESHIFT) + HALFGLOBAL1; \
|
|
ob->targettiley = ((dptr->tiley + (yoffset)) << TILESHIFT) + HALFGLOBAL1; \
|
|
ob->temp2 = newdir; \
|
|
if (GameRandomNumber("collector door search",0) < 100) \
|
|
return; \
|
|
}
|
|
//==========================================================================
|
|
|
|
for(i=0;i<doornum;i++)
|
|
{dptr = doorobjlist[i];
|
|
if (dptr->vertical)
|
|
{
|
|
int area1 = AREANUMBER(dptr->tilex-1,dptr->tiley),
|
|
area2 = AREANUMBER(dptr->tilex+1,dptr->tiley);
|
|
|
|
if (area1 == ob->areanumber)
|
|
SetCollectorTarget(-1,0,east)
|
|
|
|
else if (area2 == ob->areanumber)
|
|
SetCollectorTarget(1,0,west);
|
|
|
|
}
|
|
else
|
|
{
|
|
int area1 = AREANUMBER(dptr->tilex,dptr->tiley-1),
|
|
area2 = AREANUMBER(dptr->tilex,dptr->tiley+1);
|
|
|
|
if (area1 == ob->areanumber)
|
|
SetCollectorTarget(0,-1,south)
|
|
|
|
else if (area2 == ob->areanumber)
|
|
SetCollectorTarget(0,1,north);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void T_CollectorWander(objtype*ob)
|
|
{
|
|
|
|
int newtilex,newtiley;
|
|
|
|
if (EluderCaught(ob))
|
|
return;
|
|
|
|
if ((ob->z != nominalheight) && (!IsPlatform(ob->tilex,ob->tiley)))
|
|
ZEROMOM;
|
|
|
|
if (!(gamestate.TimeCount & 15))//%17
|
|
SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y);
|
|
|
|
if (ob->dirchoosetime)
|
|
{
|
|
if (doornum > 0)
|
|
ob->dirchoosetime --;
|
|
}
|
|
|
|
else
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if ( dopefish==true )
|
|
{
|
|
NewState(ob,&s_scottwanderdoor1);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
NewState(ob,&s_collectorfdoor1);
|
|
}
|
|
ob->temp1 = 0;
|
|
ob->dirchoosetime = 165;
|
|
ob->targettilex = ob->targettiley = 0;
|
|
return;
|
|
}
|
|
|
|
if (ob->temp1) // temp1 holds direction time
|
|
ob->temp1 --;
|
|
else
|
|
{
|
|
dirtype bestdir,tempdir;
|
|
|
|
bestdir = angletodir[GameRandomNumber("collector theta",0) << 3];
|
|
|
|
for(tempdir = bestdir;tempdir != dirorder[bestdir][PREV];tempdir = dirorder[tempdir][NEXT])
|
|
{
|
|
ParseMomentum(ob,dirangle8[tempdir]);
|
|
newtilex = ((ob->x + ob->momentumx)>>16);
|
|
newtiley = ((ob->y + ob->momentumy)>>16);
|
|
if (IsWindow(newtilex,newtiley))
|
|
continue;
|
|
ActorMovement(ob);
|
|
if (ob->momentumx || ob->momentumy)
|
|
{
|
|
ob->temp1 = (GameRandomNumber("collector choose time",0) >> 2);
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
newtilex = ((ob->x + ob->momentumx)>>16);
|
|
newtiley = ((ob->y + ob->momentumy)>>16);
|
|
|
|
|
|
if (IsWindow(newtilex,newtiley))
|
|
{
|
|
ob->temp1 = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
ActorMovement(ob);
|
|
|
|
if (NOMOM)
|
|
ob->temp1 = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
boolean CheckDoor(objtype *ob,doorobj_t * door,int trytilex,int trytiley)
|
|
{boolean doorok=false;
|
|
|
|
|
|
switch(ob->dir)
|
|
{case north:
|
|
if ((ob->tiley == (door->tiley + 1)) && (trytilex == ob->tilex))
|
|
doorok = true;
|
|
break;
|
|
|
|
case east:
|
|
if ((ob->tilex == (door->tilex - 1)) && (trytiley == ob->tiley))
|
|
doorok = true;
|
|
break;
|
|
|
|
case south:
|
|
if ((ob->tiley == (door->tiley - 1)) && (trytilex == ob->tilex))
|
|
doorok = true;
|
|
break;
|
|
|
|
case west:
|
|
if ((ob->tilex == (door->tilex + 1)) && (trytiley == ob->tiley))
|
|
doorok = true;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
|
|
if (doorok)
|
|
{SetTilePosition(ob,ob->tilex,ob->tiley);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
boolean WallCheck(int tryx,int tryy)
|
|
{int tilexlow,tilexhigh,tileylow,tileyhigh,y,x;
|
|
|
|
tilexlow = (int)((tryx -PLAYERSIZE) >>TILESHIFT);
|
|
tileylow = (int)((tryy -PLAYERSIZE) >>TILESHIFT);
|
|
|
|
tilexhigh = (int)((tryx + PLAYERSIZE) >>TILESHIFT);
|
|
tileyhigh = (int)((tryy + PLAYERSIZE) >>TILESHIFT);
|
|
|
|
|
|
for (y=tileylow;y<=tileyhigh;y++)
|
|
for (x=tilexlow;x<=tilexhigh;x++)
|
|
{//tempwall = (wall_t*)actorat[x][y];
|
|
//if (tempwall && M_ISWALL(tempwall))
|
|
if (tilemap[x][y])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
boolean QuickSpaceCheck(objtype*ob,int tryx, int tryy)
|
|
{int xlow,xhigh,ylow,yhigh,x,y,dx,dy;
|
|
objtype* temp;
|
|
|
|
xlow = (int)((tryx-ACTORSIZE) >>TILESHIFT);
|
|
ylow = (int)((tryy-ACTORSIZE) >>TILESHIFT);
|
|
|
|
xhigh = (int)((tryx+ACTORSIZE) >>TILESHIFT);
|
|
yhigh = (int)((tryy+ACTORSIZE) >>TILESHIFT);
|
|
/******************* WALLS/PWALLS *****************************************/
|
|
|
|
for (y=ylow;y<=yhigh;y++)
|
|
for (x=xlow;x<=xhigh;x++)
|
|
{temp = (objtype*)actorat[x][y];
|
|
if ((temp && (temp->which != ACTOR)) ||
|
|
(sprites[x][y] && (sprites[x][y]->flags & FL_BLOCK))
|
|
|
|
|| tilemap[x][y])
|
|
return false;
|
|
}
|
|
|
|
for(temp=firstareaactor[ob->areanumber];temp;temp=temp->nextinarea)
|
|
{if (temp == ob)
|
|
continue;
|
|
if ((temp->flags & FL_NONMARK) || (temp->flags & FL_NEVERMARK))
|
|
continue;
|
|
dx = tryx - temp->x;
|
|
if ((dx < -MINACTORDIST) || (dx > MINACTORDIST))
|
|
continue;
|
|
dy = tryy - temp->y;
|
|
if ((dy < -MINACTORDIST) || (dy > MINACTORDIST))
|
|
continue;
|
|
if (ob->whatever == (void*)temp)
|
|
continue;
|
|
if (temp->whatever == ob->whatever)
|
|
continue;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
//=========================================================================
|
|
//
|
|
// ACTOR TRY MOVE MADNESS
|
|
//
|
|
//=========================================================================
|
|
|
|
typedef enum
|
|
{
|
|
NO_MOVEMENT,
|
|
Z_MOVEMENT_ONLY,
|
|
OK_TO_CONTINUE
|
|
|
|
}movement_status;
|
|
|
|
|
|
|
|
//==================== Some ActorTryMove macros ==============================
|
|
|
|
#define CheckProximitySpecials(ob,temp) \
|
|
{ \
|
|
if (ocl == b_heinrichobj) \
|
|
{ \
|
|
if (tcl == playerobj) \
|
|
{ \
|
|
playertype *pstate; \
|
|
\
|
|
M_LINKSTATE(temp,pstate); \
|
|
DamageThing(temp,5); \
|
|
temp->whatever = ob; \
|
|
temp->temp2 = COLUMNCRUSH; \
|
|
pstate->heightoffset += 4; \
|
|
if (pstate->heightoffset >= 30) \
|
|
pstate->heightoffset = 30; \
|
|
pstate->oldheightoffset = pstate->heightoffset; \
|
|
} \
|
|
else \
|
|
{ \
|
|
temp->momentumx = temp->momentumy = temp->momentumz = 0; \
|
|
temp->hitpoints = 0; \
|
|
} \
|
|
if (temp->hitpoints <= 0) \
|
|
temp->flags |= FL_HBM; \
|
|
Collision(temp,ob,0,0); \
|
|
continue; \
|
|
} \
|
|
\
|
|
else if ((ocl == b_darksnakeobj) && (tcl == playerobj)) \
|
|
{ \
|
|
DamageThing(temp,1); \
|
|
Collision(temp,ob,0,0); \
|
|
M_CheckPlayerKilled(temp); \
|
|
} \
|
|
\
|
|
if ((ocl == boulderobj) && (tcl >= lowguardobj) && (tcl < roboguardobj))\
|
|
{temp->momentumx = temp->momentumy = temp->momentumz = 0; \
|
|
temp->hitpoints = 0; \
|
|
temp->flags |= FL_HBM; \
|
|
Collision(temp,ob,0,0); \
|
|
SD_PlaySoundRTP(SD_ACTORSQUISHSND,temp->x,temp->y); \
|
|
continue; \
|
|
} \
|
|
\
|
|
if (pusher && (ocl != tcl) && (!(temp->flags & FL_DYING)) && \
|
|
(tcl < roboguardobj) \
|
|
) \
|
|
{if ((!ob->ticcount) && (ocl != collectorobj) && (ocl != diskobj))\
|
|
DamageThing(temp,5); \
|
|
\
|
|
if (tcl == playerobj) \
|
|
temp->flags |= FL_PUSHED; \
|
|
Collision(temp,ob,ob->momentumx-temp->momentumx,ob->momentumy-temp->momentumy);\
|
|
M_CheckPlayerKilled(temp); \
|
|
continue; \
|
|
} \
|
|
\
|
|
if (bouncer) \
|
|
{ob->momentumx = -ob->momentumx; \
|
|
continue; \
|
|
} \
|
|
}
|
|
|
|
|
|
|
|
#define CheckStepping(ob,step,minzdiff) \
|
|
{ \
|
|
int cz = (ob->z - step->z + minzdiff); \
|
|
\
|
|
if ((cz >= -MAXSTEPHEIGHT) && (cz <= MAXSTEPHEIGHT)) \
|
|
{if ((ob->obclass == playerobj) && (ob->temp2 == 0) && \
|
|
(ob->z != (step->z - minzdiff)) \
|
|
) \
|
|
{ \
|
|
playertype *pstate; \
|
|
\
|
|
M_LINKSTATE(ob,pstate); \
|
|
\
|
|
pstate->heightoffset = pstate->oldheightoffset + cz; \
|
|
ob->temp2 = (cz >= 0)?(STEPUP):(STEPDOWN); \
|
|
} \
|
|
ob->z = step->z - minzdiff; \
|
|
tryz = ob->z + (ob->momentumz >> 16); \
|
|
dzt = minzdiff; \
|
|
} \
|
|
} \
|
|
|
|
|
|
|
|
|
|
//============ Players crushing other players =====================
|
|
|
|
void BattleCrushCheck(objtype *ob,objtype *listrover) \
|
|
{
|
|
if ((ob->obclass == playerobj) && (listrover->obclass == playerobj))
|
|
{
|
|
playertype * pstate;
|
|
|
|
M_LINKSTATE(listrover,pstate);
|
|
if (pstate->health <= 0)
|
|
BATTLE_PlayerKilledPlayer(battle_kill_by_crushing,ob->dirchoosetime,
|
|
listrover->dirchoosetime);
|
|
}
|
|
}
|
|
|
|
|
|
//=================================================================
|
|
|
|
|
|
|
|
|
|
|
|
movement_status CheckOtherActors(objtype*ob,int tryx,int tryy,int tryz)
|
|
{
|
|
objtype *listrover;
|
|
int area;
|
|
int op;
|
|
int areatried[NUMAREAS]={0};
|
|
int tilexlow,tilexhigh,tileylow,tileyhigh;
|
|
int radius,actrad,oldrad;
|
|
boolean bouncer,pusher,thinkingactor,zstoppable,ACTORSTOP;
|
|
int dzt,dztp1,checkz;
|
|
int x,y,dx,dy;
|
|
int ocl,tcl;
|
|
int ISPLAYER = 0;
|
|
int hoffset;
|
|
|
|
ocl = ob->obclass;
|
|
|
|
actrad = MINACTORDIST;//ACTORSIZE+0x2800;
|
|
pusher = ((ocl == wallopobj) || (ocl == pillarobj) ||
|
|
(ocl == roboguardobj) || (ocl == collectorobj) ||
|
|
(ocl == boulderobj) || (ocl == diskobj)
|
|
);
|
|
|
|
thinkingactor = ((ocl != playerobj) && (ob->state->think != T_Collide) &&
|
|
(ocl < roboguardobj)
|
|
);
|
|
|
|
zstoppable = (!(ob->flags & FL_DYING));
|
|
bouncer = ((ocl == playerobj) && (ob->flags & FL_ELASTO));
|
|
radius = ACTORSIZE;
|
|
|
|
|
|
if (ocl != playerobj)
|
|
{
|
|
//actrad = MINACTORDIST;
|
|
//if ((ob->dir == nodir) && (ocl != b_robobossobj) &&
|
|
// (ocl != wallopobj) && (ocl != roboguardobj) && (ocl != diskobj)
|
|
// )
|
|
// Error("ob called with nodir");
|
|
if (ocl == boulderobj)
|
|
radius += (ACTORSIZE/4);
|
|
else if (ocl == b_darksnakeobj)
|
|
radius -= 6000;
|
|
else if (ocl == inertobj)
|
|
radius -= 0x2000;
|
|
}
|
|
|
|
else
|
|
{
|
|
ISPLAYER = 1;
|
|
if (ob->flags & FL_DOGMODE)
|
|
hoffset = 10;
|
|
}
|
|
|
|
|
|
|
|
tilexlow = (int)((tryx-radius) >>TILESHIFT);
|
|
tileylow = (int)((tryy-radius) >>TILESHIFT);
|
|
|
|
tilexhigh = (int)((tryx+radius) >>TILESHIFT);
|
|
tileyhigh = (int)((tryy+radius) >>TILESHIFT);
|
|
|
|
area = ob->areanumber;
|
|
areatried[area] = 1;
|
|
ACTORSTOP = false;
|
|
oldrad = actrad;
|
|
|
|
actors:
|
|
for(listrover=firstareaactor[area];listrover;listrover=listrover->nextinarea)
|
|
{
|
|
actrad = oldrad;
|
|
|
|
if (listrover == ob)
|
|
continue;
|
|
|
|
|
|
tcl = listrover->obclass;
|
|
|
|
if ((tcl == b_darksnakeobj) && (listrover != SNAKEHEAD))
|
|
continue;
|
|
|
|
if (((tcl == bladeobj) || (tcl == firejetobj)) && thinkingactor)
|
|
|
|
actrad += 0x3000;
|
|
|
|
|
|
dx = tryx - listrover->x;
|
|
if ((dx < -actrad) || (dx > actrad))
|
|
continue;
|
|
|
|
dy = tryy - listrover->y;
|
|
if ((dy < -actrad) || (dy > actrad))
|
|
continue;
|
|
|
|
|
|
if ((ocl == b_darksnakeobj) && (tcl == ocl))
|
|
continue;
|
|
|
|
if ((tcl == springobj) && (listrover->state->condition & SF_UP) &&
|
|
(listrover->temp1!=3) && (levelheight > 1) &&
|
|
(abs(listrover->z - ob->z) < 5) && (!ob->momentumz)
|
|
)
|
|
{
|
|
{
|
|
op = (FixedMul((int)GRAVITY,(int)((ob->z-10)<<16))<<1);
|
|
ob->momentumz = -FixedSqrtHP(op);
|
|
}
|
|
SD_PlaySoundRTP(SD_SPRINGBOARDSND,listrover->x,listrover->y);
|
|
NewState(listrover,&s_spring2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tcl == firejetobj) && (ob->z < listrover->z))
|
|
continue;
|
|
|
|
if ((!(listrover->flags & FL_BLOCK)) && (actrad == oldrad)) // if not blocking
|
|
// and actor not avoiding
|
|
// env. danger
|
|
continue;
|
|
|
|
|
|
|
|
if (tcl == crushcolobj)
|
|
checkz = listrover->temp2;
|
|
else
|
|
checkz = listrover->z;
|
|
|
|
#define MINACTORZDIFF 58
|
|
|
|
dzt = abs(checkz - ob->z);
|
|
dztp1 = abs(checkz - tryz);
|
|
|
|
if ((tcl == diskobj) && (dztp1 <= MINACTORZDIFF) && zstoppable &&
|
|
(ocl != b_heinrichobj)
|
|
)
|
|
CheckStepping(ob,listrover,MINACTORZDIFF);
|
|
|
|
dztp1 = abs(checkz - tryz);
|
|
|
|
|
|
|
|
if ((dzt > (MINACTORZDIFF - 25)) && (dzt < MINACTORZDIFF) &&
|
|
(dztp1 < MINACTORZDIFF) && (tcl < roboguardobj) &&
|
|
(ocl < roboguardobj)
|
|
)
|
|
{
|
|
int rdx,rdy;
|
|
|
|
rdx = abs(ob->x - listrover->x);
|
|
rdy = abs(ob->y - listrover->y);
|
|
if ((rdx < actrad) && (rdy < actrad))
|
|
{
|
|
if (ob->z > listrover->z)
|
|
listrover->z = ob->z - MINACTORZDIFF;
|
|
else
|
|
ob->z = listrover->z - MINACTORZDIFF;
|
|
|
|
dzt = dztp1 = MINACTORZDIFF;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if ((dztp1 >= MINACTORZDIFF) || (dzt >= MINACTORZDIFF))
|
|
{
|
|
if ((dzt >= MINACTORZDIFF) && (dztp1 <= MINACTORZDIFF) &&
|
|
zstoppable
|
|
)
|
|
{//ob->momentumz = 0;
|
|
if (ob->z < listrover->z)
|
|
{
|
|
ob->z = listrover->z - MINACTORZDIFF;
|
|
ob->momentumz = 0;
|
|
}
|
|
else
|
|
ob->momentumz = 2*GRAVITY;
|
|
if ((listrover->z > ob->z) && (tcl < roboguardobj) && (ocl < roboguardobj) &&
|
|
(!(listrover->flags & FL_DYING))
|
|
)
|
|
|
|
{
|
|
DamageThing(listrover,5);
|
|
BattleCrushCheck(ob,listrover);
|
|
Collision(listrover,ob,listrover->momentumx,listrover->momentumy);
|
|
/*
|
|
if ((ocl == playerobj) && (listrover->flags & FL_DYING))
|
|
GivePoints(starthitpoints[gamestate.difficulty][tcl]);
|
|
*/
|
|
}
|
|
|
|
if (((tcl == bladeobj) || (tcl == diskobj)) && (ob->z < listrover->z))
|
|
{
|
|
ob->whatever = listrover;
|
|
if (listrover->flags & FL_ACTIVE)
|
|
ob->flags |= FL_RIDING;
|
|
listrover->whatever = ob;
|
|
}
|
|
|
|
//Debug("\nplayerz %d, tryz %d momz zeroed at %d, clearances %d and %d",
|
|
// ob->z,tryz,listrover->z-64 + (listrover->momentumz >> 16),dzt,dztp1);
|
|
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
CheckProximitySpecials(ob,listrover);
|
|
|
|
|
|
ACTORSTOP = true;
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
|
|
}
|
|
|
|
|
|
for (y=tileylow;y<=tileyhigh;y++)
|
|
for (x=tilexlow;x<=tilexhigh;x++)
|
|
{
|
|
area = AREANUMBER(x,y);
|
|
if (ValidAreanumber(area) && (areatried[area]==0))
|
|
{
|
|
areatried[area] = 1;
|
|
goto actors;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (ACTORSTOP==true)
|
|
return Z_MOVEMENT_ONLY;
|
|
|
|
return OK_TO_CONTINUE;
|
|
}
|
|
|
|
|
|
|
|
movement_status CheckRegularWalls(objtype *ob,int tryx,int tryy,int tryz)
|
|
{
|
|
int tilexlow,tilexhigh,tileylow,tileyhigh,x,y,radius;
|
|
classtype ocl;
|
|
boolean WALLSTOP,ISPLAYER=false;
|
|
|
|
ocl = ob->obclass;
|
|
tryz=tryz;
|
|
|
|
|
|
if (ocl != playerobj)
|
|
{
|
|
radius = ACTORSIZE - 0x1000;
|
|
//actrad = MINACTORDIST;
|
|
//if ((ob->dir == nodir) && (ocl != b_robobossobj) &&
|
|
// (ocl != wallopobj) && (ocl != roboguardobj) && (ocl != diskobj)
|
|
// )
|
|
// Error("ob called with nodir");
|
|
if (ocl == boulderobj)
|
|
radius += (ACTORSIZE/4);
|
|
else if (ocl == b_darksnakeobj)
|
|
radius -= 6000;
|
|
else if (ocl == inertobj)
|
|
radius -= 0x2000;
|
|
}
|
|
|
|
else
|
|
{
|
|
radius = PLAYERSIZE;
|
|
ISPLAYER = true;
|
|
}
|
|
|
|
|
|
tilexlow = (int)((tryx-radius) >>TILESHIFT);
|
|
tileylow = (int)((tryy-radius) >>TILESHIFT);
|
|
|
|
tilexhigh = (int)((tryx+radius) >>TILESHIFT);
|
|
tileyhigh = (int)((tryy+radius) >>TILESHIFT);
|
|
|
|
|
|
WALLSTOP = false;
|
|
|
|
for (y=tileylow;y<=tileyhigh;y++)
|
|
for (x=tilexlow;x<=tilexhigh;x++)
|
|
{
|
|
wall_t *tempwall;
|
|
int wall;
|
|
|
|
tempwall = (wall_t*)actorat[x][y];
|
|
wall=tilemap[x][y];
|
|
if (tempwall)
|
|
{
|
|
if (tempwall->which==WALL)// && IsWindow(x,y)==false)
|
|
{
|
|
if (ocl == boulderobj)
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
NewState(ob,&s_bouldersink1);
|
|
#endif
|
|
SD_PlaySoundRTP(SD_BOULDERHITSND,ob->x,ob->y);
|
|
}
|
|
else if (ISPLAYER && (!(ob->flags & FL_DYING)) &&
|
|
(!(ob->flags & FL_AV)) &&
|
|
(tempwall->flags & FL_W_DAMAGE))
|
|
{
|
|
DamageThing(ob,5);
|
|
Collision(ob,(objtype*)tempwall,0,0);
|
|
M_CheckPlayerKilled(ob);
|
|
SD_PlaySoundRTP(SD_PLAYERBURNEDSND,ob->x,ob->y);
|
|
}
|
|
|
|
//return false;
|
|
WALLSTOP = true;
|
|
if ((ocl == inertobj) &&
|
|
(ob->dirchoosetime == GIBVALUE) &&
|
|
|
|
(((ob->tilex - x) == 0) != ((ob->tiley - y) == 0)) &&
|
|
(ob->z > -28)
|
|
|
|
)
|
|
{
|
|
// SoftError ("Blood Dripping oldpolltime=%ld\n",oldpolltime);
|
|
BloodDrip(ob,x,y);
|
|
return NO_MOVEMENT;
|
|
}
|
|
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
else// if (ocl != inertobj)
|
|
return Z_MOVEMENT_ONLY;
|
|
//else
|
|
//goto doors;
|
|
}
|
|
|
|
else if (tempwall->which==PWALL)
|
|
{
|
|
pwallobj_t*pw;
|
|
int dx,dy;
|
|
|
|
pw=(pwallobj_t *)tempwall;
|
|
dx = abs(pw->x - tryx);
|
|
if (dx > PWALLRAD+0x5000)
|
|
continue;
|
|
|
|
dy = abs(pw->y - tryy);
|
|
if (dy > PWALLRAD+0x5000)
|
|
continue;
|
|
|
|
return NO_MOVEMENT;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return OK_TO_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
movement_status CheckStaticObjects(objtype *ob,int tryx,int tryy,int tryz)
|
|
{
|
|
int dx,dy,dzt,dztp1,x,y;
|
|
statobj_t*tempstat;
|
|
int sprrad,oldsrad,sprtrad;
|
|
boolean specialstat=false,widestat=false,zstoppable;
|
|
int sprxlow,sprxhigh,sprylow,spryhigh;
|
|
boolean SPRSTOP;
|
|
classtype ocl;
|
|
|
|
ocl = ob->obclass;
|
|
|
|
if (ocl != playerobj)
|
|
sprtrad = ACTORSIZE - 0x1000;
|
|
else
|
|
sprtrad = ACTORSIZE - 0x1000 + 0x10000;
|
|
|
|
|
|
|
|
sprxlow = (int)((tryx-sprtrad) >>TILESHIFT);
|
|
sprylow = (int)((tryy-sprtrad) >>TILESHIFT);
|
|
|
|
sprxhigh = (int)((tryx+sprtrad) >>TILESHIFT);
|
|
spryhigh = (int)((tryy+sprtrad) >>TILESHIFT);
|
|
|
|
if (sprxlow < 0)
|
|
sprxlow = 0;
|
|
|
|
if (sprxhigh > (MAPSIZE-1))
|
|
sprxhigh = MAPSIZE-1;
|
|
|
|
if (sprylow < 0)
|
|
sprylow = 0;
|
|
|
|
if (spryhigh > (MAPSIZE-1))
|
|
spryhigh = MAPSIZE-1;
|
|
|
|
SPRSTOP = false;
|
|
sprrad = 0x4500;
|
|
zstoppable = (!(ob->flags & FL_DYING));
|
|
oldsrad = sprrad;
|
|
|
|
for (y=sprylow;y<=spryhigh;y++)
|
|
for (x=sprxlow;x<=sprxhigh;x++)
|
|
{
|
|
tempstat = sprites[x][y];
|
|
sprrad = oldsrad;
|
|
|
|
if (tempstat)
|
|
{
|
|
specialstat = ((tempstat->itemnumber == stat_heatgrate) ||
|
|
(tempstat->itemnumber == stat_pit)
|
|
);
|
|
|
|
widestat = (((tempstat->itemnumber >= stat_bcolumn) &&
|
|
(tempstat->itemnumber <= stat_icolumn)) ||
|
|
(tempstat->itemnumber == stat_disk)
|
|
);
|
|
|
|
if ((tempstat->flags & FL_BLOCK) || (specialstat==true))
|
|
{
|
|
if ((specialstat==true) && (ocl !=playerobj) &&
|
|
(ob->state->think != T_Collide)
|
|
)
|
|
sprrad += 0x5000;
|
|
|
|
if (widestat==true)
|
|
sprrad += 0x3b00;
|
|
|
|
|
|
if ((tempstat->itemnumber == stat_ironbarrel) ||
|
|
(tempstat->itemnumber == stat_bonusbarrel))
|
|
sprrad += 0x5000;
|
|
|
|
dx = abs(tryx - tempstat->x);
|
|
if (dx > sprrad)
|
|
continue;
|
|
dy = abs(tryy - tempstat->y);
|
|
if (dy > sprrad)
|
|
continue;
|
|
|
|
#define MINSTATZDIFF 58
|
|
|
|
dzt = abs(ob->z - tempstat->z);
|
|
dztp1 = abs(tryz - tempstat->z);
|
|
|
|
if (widestat && (dztp1 <= MINSTATZDIFF) && zstoppable &&
|
|
(ocl != b_heinrichobj)
|
|
)
|
|
CheckStepping(ob,tempstat,MINSTATZDIFF);
|
|
|
|
|
|
dztp1 = abs(tryz - tempstat->z);
|
|
|
|
#if (SHAREWARE == 0)
|
|
if ((ocl == b_darksnakeobj) && (tempstat->itemnumber == stat_heatgrate))
|
|
{
|
|
if (ob->state->think == T_DarkSnakeChase)
|
|
NewState(ob,&s_darkmonkredhead);
|
|
else
|
|
NewState(ob,&s_darkmonkredlink);
|
|
ob->temp3 ++; // make shootable
|
|
}
|
|
#endif
|
|
|
|
if (specialstat==true)
|
|
continue;
|
|
|
|
|
|
if ((dztp1 >= MINSTATZDIFF) || (dzt >= MINSTATZDIFF))
|
|
{if ((dzt >= MINSTATZDIFF) && (dztp1 <= MINSTATZDIFF) && zstoppable)
|
|
{//ob->momentumz = 0;
|
|
if (ob->z <= tempstat->z)
|
|
{
|
|
ob->z = tempstat->z - MINSTATZDIFF;
|
|
ob->momentumz = 0;
|
|
}
|
|
else
|
|
ob->momentumz = 2*GRAVITY; // ((2*GRAVITY + GRAVITY) >> 16) = 1
|
|
}
|
|
continue;
|
|
}
|
|
|
|
|
|
if (ocl == boulderobj)
|
|
{if ((tempstat->itemnumber < stat_bcolumn) ||
|
|
(tempstat->itemnumber > stat_icolumn)
|
|
)
|
|
{
|
|
tempstat->flags |= FL_SHOOTABLE;
|
|
DamageThing(tempstat,tempstat->hitpoints);
|
|
continue;
|
|
}
|
|
#if (SHAREWARE == 0)
|
|
else
|
|
NewState(ob,&s_bouldersink1);
|
|
#endif
|
|
}
|
|
//ob->momentumz=0;
|
|
//return false;
|
|
SPRSTOP=true;
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
}
|
|
}
|
|
}
|
|
if (SPRSTOP == true)
|
|
return Z_MOVEMENT_ONLY;
|
|
|
|
return OK_TO_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//============== Platform craziness ======================================
|
|
|
|
|
|
#define ClipHeight(ob,clipz) \
|
|
{ ob->momentumz = 0; \
|
|
\
|
|
if (ISPLAYER && (ob->z != clipz) && (ob->temp2 == 0)) \
|
|
{playertype *pstate; \
|
|
int dz = ob->z - clipz; \
|
|
\
|
|
M_LINKSTATE(ob,pstate); \
|
|
\
|
|
pstate->heightoffset = pstate->oldheightoffset + dz; \
|
|
ob->temp2 = (dz >= 0)?(STEPUP):(STEPDOWN); \
|
|
} \
|
|
\
|
|
ob->z = clipz; \
|
|
}
|
|
|
|
//======================
|
|
|
|
#define CheckSpecialGibMovement(blocker) \
|
|
{ \
|
|
int centerx = ((trytilex<<16) + 0x8000); \
|
|
int centery = ((trytiley<<16) + 0x8000); \
|
|
\
|
|
if (blocker->vertical==false) \
|
|
{ \
|
|
int dyt = centery - ob->y; \
|
|
int dytp1 = centery - tryy; \
|
|
\
|
|
if ((abs(dytp1) > abs(dyt)) && \
|
|
(SGN(dyt) == SGN(dytp1)) \
|
|
) \
|
|
return OK_TO_CONTINUE; \
|
|
\
|
|
} \
|
|
else \
|
|
{ \
|
|
int dxt = centerx - ob->x; \
|
|
int dxtp1 = centerx - tryx; \
|
|
\
|
|
if ((abs(dxtp1) > abs(dxt)) && \
|
|
(SGN(dxt) == SGN(dxtp1)) \
|
|
) \
|
|
return OK_TO_CONTINUE; \
|
|
\
|
|
} \
|
|
}
|
|
|
|
|
|
movement_status CheckMaskedWalls(objtype *ob,int tryx,int tryy,int tryz)
|
|
{
|
|
int trytilex,trytiley;
|
|
boolean MWALLSTOP;
|
|
int ISPLAYER = (ob->obclass == playerobj);
|
|
classtype ocl = ob->obclass;
|
|
|
|
trytilex = (tryx >> TILESHIFT);
|
|
trytiley = (tryy >> TILESHIFT);
|
|
MWALLSTOP = false;
|
|
//for (y=tileylow;y<=tileyhigh;y++)
|
|
// for (x=tilexlow;x<=tilexhigh;x++)
|
|
|
|
if (M_ISMWALL(trytilex,trytiley))
|
|
{
|
|
int wall = tilemap[trytilex][trytiley];
|
|
maskedwallobj_t * mw;
|
|
|
|
mw=maskobjlist[wall&0x3ff];
|
|
|
|
if (ocl == inertobj)
|
|
CheckSpecialGibMovement(mw);
|
|
|
|
if (!(mw->flags&MW_BLOCKING))
|
|
{
|
|
if (mw->flags&MW_NONDOGBLOCKING)
|
|
{
|
|
if ((ocl==playerobj)&&(ob->flags&FL_DOGMODE))
|
|
{
|
|
if (ob->z < nominalheight)
|
|
{
|
|
MWALLSTOP = true;
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MWALLSTOP = true;
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
}
|
|
}
|
|
|
|
|
|
else
|
|
{
|
|
if (mw->flags & MW_ABOVEPASSABLE)
|
|
{if (mw->flags & MW_MIDDLEPASSABLE) // ==> not bottom
|
|
{if (ob->z > LOWFALLCLIPZ+MAXSTEPHEIGHT)
|
|
MWALLSTOP = true;
|
|
else if (tryz >= LOWFALLCLIPZ)
|
|
ClipHeight(ob,LOWFALLCLIPZ);
|
|
}
|
|
else if (mw->flags & MW_BOTTOMPASSABLE)
|
|
{if ((ob->z > HIGHFALLCLIPZ+MAXSTEPHEIGHT) && (ob->z < LOWRISECLIPZ))
|
|
MWALLSTOP = true;
|
|
else if (ob->z <= HIGHFALLCLIPZ+MAXSTEPHEIGHT)
|
|
{if (tryz >= HIGHFALLCLIPZ)
|
|
ClipHeight(ob,HIGHFALLCLIPZ);
|
|
}
|
|
else if (tryz <= LOWRISECLIPZ)
|
|
ob->momentumz = 0;
|
|
|
|
}
|
|
else // ==> above only
|
|
{if (ob->z > HIGHFALLCLIPZ+MAXSTEPHEIGHT)
|
|
MWALLSTOP = true;
|
|
else if (tryz >= HIGHFALLCLIPZ)
|
|
ClipHeight(ob,HIGHFALLCLIPZ);
|
|
}
|
|
|
|
}
|
|
else if (mw->flags & MW_MIDDLEPASSABLE)
|
|
{if (mw->flags & MW_BOTTOMPASSABLE) //==> not above passable
|
|
{if (ob->z >= HIGHRISECLIPZ)
|
|
{if (tryz <= HIGHRISECLIPZ)
|
|
ob->momentumz = 0;
|
|
}
|
|
else if (tryz <= HIGHRISECLIPZ)
|
|
MWALLSTOP = true;
|
|
}
|
|
|
|
else //==> middle only
|
|
{if (ob->z > LOWFALLCLIPZ+MAXSTEPHEIGHT)
|
|
MWALLSTOP = true;
|
|
else if (tryz >= LOWFALLCLIPZ)
|
|
ClipHeight(ob,LOWFALLCLIPZ)
|
|
else
|
|
{if (ob->z >= HIGHRISECLIPZ)
|
|
{if (tryz <= HIGHRISECLIPZ)
|
|
ob->momentumz = 0;
|
|
}
|
|
else if (tryz <= HIGHRISECLIPZ)
|
|
MWALLSTOP = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
else // ==> bottompassable only
|
|
{if (ob->z < LOWRISECLIPZ)
|
|
MWALLSTOP = true;
|
|
else if (tryz < LOWRISECLIPZ)
|
|
ob->momentumz = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (mw->flags&MW_SHOOTABLE) &&
|
|
(mw->flags&MW_BLOCKINGCHANGES) &&
|
|
(ob->z >= nominalheight)
|
|
|
|
)
|
|
{
|
|
int speed=FindDistance(ob->momentumx,ob->momentumy);
|
|
if ((speed>0x2800) && (!(ob->flags & FL_DYING)))
|
|
{
|
|
if (ob->obclass == playerobj)
|
|
{
|
|
DamageThing(ob,10);
|
|
Collision(ob,(objtype*)mw,0,0);
|
|
}
|
|
UpdateMaskedWall(wall&0x3ff);
|
|
if (tryz < nominalheight)
|
|
ob->momentumz = 0;
|
|
}
|
|
else
|
|
{
|
|
MWALLSTOP = true;
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MWALLSTOP = true;
|
|
if (!ob->momentumz)
|
|
return NO_MOVEMENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MWALLSTOP == true)
|
|
return Z_MOVEMENT_ONLY;
|
|
|
|
return OK_TO_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
movement_status CheckDoors(objtype *ob,int tryx,int tryy,int tryz)
|
|
{
|
|
int trytilex,trytiley;
|
|
int ocl;
|
|
|
|
|
|
trytilex = (tryx >> TILESHIFT);
|
|
trytiley = (tryy >> TILESHIFT);
|
|
ocl = ob->obclass;
|
|
|
|
|
|
if (M_ISDOOR(trytilex,trytiley))
|
|
{
|
|
doorobj_t*tempdoor;
|
|
int doorn;
|
|
|
|
doorn = tilemap[trytilex][trytiley];
|
|
|
|
tempdoor = doorobjlist[doorn&0x3ff];
|
|
if (tempdoor->action == dr_open)
|
|
{
|
|
|
|
if (ob->z >= nominalheight)
|
|
{
|
|
if (tryz < nominalheight)
|
|
ob->momentumz = 0;
|
|
return OK_TO_CONTINUE;
|
|
}
|
|
|
|
}
|
|
if (ocl == inertobj)
|
|
{
|
|
CheckSpecialGibMovement(tempdoor);
|
|
}
|
|
else if ((ocl == playerobj) || (ocl > b_darksnakeobj))
|
|
return NO_MOVEMENT;
|
|
else if (ob->state->think != T_Collide)
|
|
{
|
|
|
|
#define DOOR_LOCKED(door) \
|
|
(((door->flags & DF_ELEVLOCKED) || (door->lock)) && \
|
|
(ob->obclass != b_darianobj) \
|
|
)
|
|
#define GAS_DOOR(x,y) (MISCVARS->GASON && (MAPSPOT(x,y,1) == GASVALUE))
|
|
|
|
if ((!DOOR_LOCKED(tempdoor)) &&
|
|
(!GAS_DOOR(trytilex,trytiley))
|
|
)
|
|
|
|
|
|
//)
|
|
{
|
|
ob->door_to_open = doorn&0x3ff;
|
|
LinkedOpenDoor(ob->door_to_open);
|
|
if (tempdoor->eindex != -1)
|
|
OperateElevatorDoor(doorn&0x3ff);
|
|
}
|
|
|
|
//if ((nstate = M_S(USE)) != NULL)
|
|
//{ob->whatever = ob->state;
|
|
// NewState(ob,nstate);
|
|
// ob->flags |= FL_USE;
|
|
// }
|
|
return NO_MOVEMENT;
|
|
}
|
|
else
|
|
return NO_MOVEMENT;
|
|
}
|
|
|
|
return OK_TO_CONTINUE;
|
|
}
|
|
|
|
|
|
|
|
boolean ActorTryMove(objtype*ob,int tryx, int tryy, int tryz)
|
|
{
|
|
|
|
movement_status (*reduced_movement_check[3])(objtype*,int,int,int)=
|
|
{
|
|
CheckRegularWalls,
|
|
CheckMaskedWalls,
|
|
CheckDoors,
|
|
};
|
|
|
|
|
|
movement_status (*complete_movement_check[5])(objtype*,int,int,int)=
|
|
{
|
|
CheckOtherActors,
|
|
CheckRegularWalls,
|
|
CheckStaticObjects,
|
|
CheckMaskedWalls,
|
|
CheckDoors,
|
|
};
|
|
|
|
movement_status (**movement_function)(objtype*,int,int,int);
|
|
movement_status movement_check_result;
|
|
int numcheckfunctions;
|
|
int i;
|
|
boolean xyblocked;
|
|
|
|
|
|
|
|
|
|
if ((tryz < -30) && (sky==0) && (ob->obclass != inertobj))
|
|
{
|
|
ob->z = -28;
|
|
ob->momentumz = 0;
|
|
return false;
|
|
}
|
|
|
|
if ((!InMapBounds(tryx>>16,tryy>>16)) ||
|
|
((ob->obclass != playerobj) && (IsWindow((tryx>>16),(tryy>>16))))
|
|
)
|
|
return false;
|
|
|
|
switch(ob->obclass)
|
|
{
|
|
case inertobj:
|
|
case bladeobj:
|
|
case firejetobj:
|
|
movement_function = &reduced_movement_check[0];
|
|
numcheckfunctions = 3;
|
|
break;
|
|
|
|
default:
|
|
movement_function = &complete_movement_check[0];
|
|
numcheckfunctions = 5;
|
|
break;
|
|
}
|
|
|
|
|
|
for(xyblocked=false,i=0;i<numcheckfunctions;i++)
|
|
{
|
|
movement_check_result = movement_function[i](ob,tryx,tryy,tryz);
|
|
if (movement_check_result == Z_MOVEMENT_ONLY)
|
|
xyblocked = true;
|
|
|
|
else if (movement_check_result == NO_MOVEMENT)
|
|
return false;
|
|
}
|
|
|
|
if (xyblocked == true)
|
|
return false;
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
void PushWallMove(int num)
|
|
{
|
|
int tcl;
|
|
pwallobj_t *pwall;
|
|
int dx,dy;
|
|
int actrad;
|
|
objtype *temp;
|
|
boolean pushem;
|
|
int tryx,tryy,areanumber,trytilex,trytiley;
|
|
|
|
|
|
pwall=pwallobjlist[num];
|
|
|
|
actrad = PWALLRAD + 0x5000;
|
|
tryx = (pwall->x + pwall->momentumx);
|
|
tryy = (pwall->y + pwall->momentumy);
|
|
trytilex = (tryx >> 16);
|
|
trytiley = (tryy >> 16);
|
|
|
|
areanumber = AREANUMBER(trytilex,trytiley);
|
|
|
|
|
|
for(temp=firstareaactor[areanumber];temp;temp=temp->nextinarea)
|
|
{
|
|
tcl = temp->obclass;
|
|
|
|
if (temp->flags & FL_HEAD) //ignore NME's head and wheels
|
|
continue;
|
|
|
|
if ((temp->flags & FL_DYING) || (!(temp->flags & FL_SHOOTABLE)))
|
|
continue;
|
|
|
|
if (tcl > b_darianobj)
|
|
continue;
|
|
|
|
|
|
dx = abs(tryx - temp->x);
|
|
if (dx > actrad)
|
|
continue;
|
|
|
|
dy = abs(tryy - temp->y);
|
|
if (dy > actrad)
|
|
continue;
|
|
|
|
if (pwall->flags&PW_DAMAGE)
|
|
{if (!((tcl == playerobj) && (temp->flags & FL_AV)))
|
|
DamageThing(temp,5);
|
|
|
|
Collision(temp,(objtype*)pwall,0,0);
|
|
M_CheckPlayerKilled(temp);
|
|
if (temp->flags & FL_DYING)
|
|
return;
|
|
|
|
}
|
|
|
|
pushem=false;
|
|
switch (pwall->dir)
|
|
{
|
|
|
|
#define PWALLTOL (0xc000)
|
|
|
|
case north:
|
|
if ((temp->y<pwall->y) && (dx<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case east:
|
|
if ((temp->x>pwall->x) && (dy<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case northeast:
|
|
if ((temp->y<pwall->y) && (dx<PWALLTOL))
|
|
pushem=true;
|
|
else if ((temp->x>pwall->x) && (dy<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case northwest:
|
|
if ((temp->y<pwall->y) && (dx<PWALLTOL))
|
|
pushem=true;
|
|
else if ((temp->x<pwall->x) && (dy<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case south:
|
|
if ((temp->y>pwall->y) && (dx<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case west:
|
|
if ((temp->x<pwall->x) && (dy<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case southeast:
|
|
if ((temp->y>pwall->y) && (dx<PWALLTOL))
|
|
pushem=true;
|
|
else if ((temp->x>pwall->x) && (dy<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
case southwest:
|
|
if ((temp->y>pwall->y) && (dx<PWALLTOL))
|
|
pushem=true;
|
|
else if ((temp->x<pwall->x) && (dy<PWALLTOL))
|
|
pushem=true;
|
|
break;
|
|
default:
|
|
//Error ("Pushwall #%d has an illegal direction %d \n",num,pwall->dir);
|
|
break;
|
|
}
|
|
|
|
|
|
//if (!pushem)
|
|
//continue;
|
|
|
|
//temp->momentumx = temp->momentumy = 0;
|
|
if (temp->obclass==playerobj)
|
|
temp->flags|=FL_PUSHED;
|
|
|
|
if (!pushem)
|
|
{
|
|
Collision(temp,(objtype*)pwall,-temp->momentumx,-temp->momentumy);
|
|
continue;
|
|
}
|
|
|
|
if ((temp->obclass >= lowguardobj) && (temp->obclass < roboguardobj))
|
|
{
|
|
|
|
temp->momentumx = temp->momentumy = temp->momentumz = 0;
|
|
temp->hitpoints = 0;
|
|
|
|
if (gamestate.violence >= vl_high)
|
|
temp->flags |= FL_HBM;
|
|
Collision(temp,(objtype*)pwall,0,0);
|
|
/*
|
|
if (gamestate.violence < vl_high)
|
|
{if ((tstate = UPDATE_STATES[CRUSH][temp->obclass - lowguardobj])!=NULL)
|
|
NewState(temp,tstate);
|
|
|
|
else
|
|
Error("\n\Null low-violence crush state in push wall crush, instance of %s",debugstr[temp->obclass]);
|
|
}
|
|
else
|
|
{temp->shapeoffset = 0;
|
|
//tactor->flags|=FL_HBM;
|
|
NewState(temp,&s_guts1);
|
|
//KillActor(temp);
|
|
}*/
|
|
|
|
SD_PlaySoundRTP(SD_ACTORSQUISHSND,temp->x,temp->y);
|
|
}
|
|
else
|
|
{
|
|
if (!ActorTryMove(temp,temp->x + temp->momentumx,temp->y + temp->momentumy,
|
|
temp->z + (temp->momentumz >> 16))
|
|
)
|
|
{
|
|
DamageThing(temp,30);
|
|
if ((temp->obclass==playerobj) && (temp->hitpoints <= 0))
|
|
temp->target = (objtype *)pwall;
|
|
}
|
|
|
|
Collision(temp,(objtype*)pwall,pwall->momentumx-temp->momentumx,pwall->momentumy-temp->momentumy);
|
|
M_CheckPlayerKilled(temp);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
void ActorMovement (objtype *ob)
|
|
{int tryx,tryy,tryz,limitok,max,friction,ocl;
|
|
|
|
|
|
|
|
if ((ob->obclass == strikeguardobj) && (!(ob->flags & FL_DYING)) &&
|
|
(gamestate.difficulty > gd_easy)
|
|
)
|
|
{
|
|
AvoidPlayerMissile(ob);
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
}
|
|
|
|
|
|
if ((!ob->momentumx) && (!ob->momentumy) && (!ob->momentumz))
|
|
{if (ob->flags & FL_RIDING)
|
|
goto ride;
|
|
else
|
|
return;
|
|
}
|
|
|
|
|
|
limitok = 1;
|
|
|
|
friction = ACTORFRICTION;
|
|
if (!(ob->flags & FL_DYING))
|
|
friction >>= 1;
|
|
ocl = ob->obclass;
|
|
if (ocl == playerobj)
|
|
{
|
|
playertype *pstate;
|
|
|
|
M_LINKSTATE(ob,pstate);
|
|
max = pstate->topspeed;
|
|
friction = PLAYERFRICTION;
|
|
if ((ob->temp2 == PITFALL) || (ob->temp2 == PITRISE))
|
|
friction >>= 4;
|
|
}
|
|
|
|
|
|
else if (/*(ob->state->think != T_Collide) &&*/ (ocl != b_robobossobj) &&
|
|
(ocl != boulderobj) && (ocl !=b_darkmonkobj) && (ocl != b_darksnakeobj) &&
|
|
(ocl != inertobj) && (ocl != collectorobj))
|
|
max = MAXMOVE;
|
|
|
|
else
|
|
limitok = 0;
|
|
|
|
if (limitok)
|
|
{if (ocl == playerobj)
|
|
{int dist,scale;
|
|
|
|
dist = FindDistance(ob->momentumx,ob->momentumy);
|
|
if (dist > max)
|
|
{scale = FixedDiv2(max,dist);
|
|
ob->momentumx = FixedMul(ob->momentumx,scale);
|
|
ob->momentumy = FixedMul(ob->momentumy,scale);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ob->momentumx > max)
|
|
ob->momentumx = max;
|
|
else if (ob->momentumx < -max)
|
|
ob->momentumx = -max;
|
|
if (ob->momentumy > max)
|
|
ob->momentumy = max;
|
|
else if (ob->momentumy < -max)
|
|
ob->momentumy = -max;
|
|
}
|
|
|
|
}
|
|
|
|
tryx = ob->x + ob->momentumx;
|
|
tryy = ob->y + ob->momentumy;
|
|
tryz = ob->z + (ob->momentumz >> 16);
|
|
|
|
if (ocl != playerobj)
|
|
ob->flags &= ~FL_STUCK;
|
|
|
|
|
|
if (!ActorTryMove (ob, tryx, tryy, tryz))
|
|
{if (ocl == playerobj)
|
|
{if (!(ob->flags & FL_ELASTO))
|
|
PlayerSlideMove (ob);
|
|
else
|
|
{if (ActorTryMove(ob,tryx, ob->y-ob->momentumy,tryz))
|
|
ob->momentumy = -(ob->momentumy);
|
|
else if (ActorTryMove(ob,ob->x-ob->momentumx,tryy,tryz))
|
|
ob->momentumx = -(ob->momentumx);
|
|
else
|
|
ZEROMOM;
|
|
}
|
|
}
|
|
|
|
else
|
|
{ZEROMOM;
|
|
ob->flags |= FL_STUCK;
|
|
return;
|
|
}
|
|
}
|
|
|
|
MoveActor(ob);
|
|
|
|
|
|
ride:
|
|
|
|
if (ob->flags & FL_RIDING)
|
|
{
|
|
objtype *ride = (objtype*)(ob->whatever);
|
|
|
|
ob->z += (ride->momentumz >> 16);
|
|
|
|
if ((ride->momentumx || ride->momentumy) &&
|
|
ActorTryMove(ob,ob->x+ride->momentumx,ob->y+ride->momentumy,tryz)
|
|
)
|
|
SetFinePosition(ob,ob->x+ride->momentumx,ob->y+ride->momentumy);
|
|
}
|
|
|
|
|
|
#define SLIDER(ob) ((ob->flags & FL_NOFRICTION) && (ob->state->think != T_Collide))
|
|
#define AIRBORNE(ob) ((ob->obclass != playerobj) && (ob->z != nominalheight) &&\
|
|
(!IsPlatform(ob->tilex,ob->tiley)) && \
|
|
(DiskAt(ob->tilex,ob->tiley) == NULL) \
|
|
)
|
|
|
|
if (SLIDER(ob) || AIRBORNE(ob))
|
|
return;
|
|
|
|
if ( (abs(ob->momentumx) < STOPSPEED) &&
|
|
(abs(ob->momentumy) < STOPSPEED)
|
|
)
|
|
{
|
|
ZEROMOM;
|
|
}
|
|
|
|
else if ((ob->flags & FL_DYING) && (ob->state == ob->state->next))
|
|
{ob->momentumx = FixedMul (ob->momentumx, DEADFRICTION);
|
|
ob->momentumy = FixedMul (ob->momentumy, DEADFRICTION);
|
|
|
|
}
|
|
|
|
else
|
|
{ob->momentumx = FixedMul (ob->momentumx, friction);
|
|
ob->momentumy = FixedMul (ob->momentumy, friction);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void T_Guts(objtype*ob)
|
|
{if (ob->ticcount)
|
|
return;
|
|
SpawnParticles(ob,GUTS,50);
|
|
|
|
}
|
|
|
|
|
|
void T_Special(objtype*ob)
|
|
{
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
#if (SHAREWARE == 0)
|
|
if (ob->state == &s_NMEheadexplosion)
|
|
{
|
|
ob->z -= 42;
|
|
SetGibSpeed(0x4000);
|
|
SpawnParticles(ob,gt_sparks,100);
|
|
ResetGibSpeed();
|
|
SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y);
|
|
return;
|
|
}
|
|
#endif
|
|
if (ob->obclass != b_robobossobj)
|
|
return;
|
|
|
|
NewState(ob,&s_bossdeath);
|
|
}
|
|
|
|
|
|
|
|
|
|
void SpawnBoulder(int tilex,int tiley,int dir)
|
|
{
|
|
#if (SHAREWARE == 1)
|
|
tilex = tilex;
|
|
tiley = tiley;
|
|
dir = dir;
|
|
Error("Boulders aren't allowed in shareware!");
|
|
#endif
|
|
#if (SHAREWARE == 0)
|
|
SpawnNewObj(tilex,tiley,&s_boulderspawn,inertobj);
|
|
new->z = 0;
|
|
PreCacheActor(boulderobj,0);
|
|
new->dir = 2*dir;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#define InitSprayPart(newflags) \
|
|
{ \
|
|
new->hitpoints = starthitpoints[gamestate.difficulty][b_robobossobj]; \
|
|
new->dir = dir*4; \
|
|
new->speed = 7*SPDPATROL; \
|
|
new->door_to_open = -1; \
|
|
new->flags |= (newflags); \
|
|
} \
|
|
|
|
|
|
void SpawnMultiSpriteActor(classtype actorclass, int tilex,int tiley,int dir)
|
|
{
|
|
|
|
|
|
|
|
#if (SHAREWARE==1)
|
|
|
|
actorclass = actorclass;
|
|
tilex = tilex;
|
|
tiley = tiley;
|
|
dir = dir;
|
|
Error("\nSPRAY not allowed in shareware !");
|
|
|
|
#else
|
|
|
|
{
|
|
objtype *temp;
|
|
|
|
gamestate.killtotal++;
|
|
|
|
SpawnNewObj(tilex,tiley,&s_NMEstand,actorclass);
|
|
InitSprayPart(FL_BLOCK|FL_NOFRICTION|FL_SHOOTABLE);
|
|
|
|
new->temp1 = -1; // temp1 used as one-event queue for directions when chasing
|
|
// -1 when isn't waiting to try new dir, dirnumber when waiting
|
|
temp = new;
|
|
|
|
SpawnNewObj(tilex,tiley,&s_NMEhead1,actorclass);
|
|
InitSprayPart(FL_NOFRICTION|FL_SHOOTABLE|FL_HEAD|FL_NEVERMARK);
|
|
|
|
//new->whatever = temp; // head points to body
|
|
|
|
temp->whatever = new; // body points to head
|
|
|
|
SpawnNewObj(tilex,tiley,&s_NMEwheels2,actorclass);
|
|
InitSprayPart(FL_NOFRICTION|FL_SHOOTABLE|FL_HEAD|FL_NEVERMARK);
|
|
|
|
|
|
//new->whatever = temp; // head points to body
|
|
temp->target = new; // body also points to wheels
|
|
actorat[tilex][tiley] = NULL;
|
|
PreCacheActor(b_robobossobj,0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void SpawnSnake(int tilex,int tiley)
|
|
{
|
|
|
|
#if (SHAREWARE == 1)
|
|
tilex = tilex;
|
|
tiley = tiley;
|
|
Error("snake not allowed in shareware!");
|
|
#else
|
|
|
|
GetNewActor();
|
|
MakeActive(new);
|
|
new->flags |= (FL_DONE|FL_ABP|FL_NEVERMARK);
|
|
SetTilePosition(new,tilex,tiley);
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
new->obclass = b_darkmonkobj;
|
|
new->which = ACTOR;
|
|
new->z = nominalheight;
|
|
if (SNAKELEVEL == 2)
|
|
NewState(new,&s_darkmonkfastspawn);
|
|
else
|
|
NewState(new,&s_darkmonkhspawn);
|
|
#endif
|
|
|
|
}
|
|
|
|
void SpawnGunThingy(classtype which, int tilex, int tiley, int dir)
|
|
{
|
|
#if (SHAREWARE == 1)
|
|
which = which;
|
|
tilex = tilex;
|
|
tiley = tiley;
|
|
dir = dir;
|
|
|
|
Error("no emplacements allowed in shareware!");
|
|
#else
|
|
SpawnNewObj(tilex,tiley,&s_gunstand,which);
|
|
|
|
|
|
if (!loadedgame)
|
|
gamestate.killtotal++;
|
|
|
|
PreCacheActor(patrolgunobj,0);
|
|
|
|
new->hitpoints = starthitpoints[gamestate.difficulty][which];
|
|
new->dir = dir*2;
|
|
// new->speed = 0x500;
|
|
// ParseMomentum(new,dirangle8[new->dir]);
|
|
new->flags |= (FL_BLOCK|FL_SHOOTABLE);
|
|
#endif
|
|
}
|
|
|
|
|
|
void SpawnFourWayGun(int tilex, int tiley)
|
|
{
|
|
#if (SHAREWARE == 1)
|
|
tilex = tilex;
|
|
tiley = tiley;
|
|
Error("no 4-way emplacements allowed in shareware!");
|
|
#else
|
|
|
|
|
|
SpawnNewObj(tilex,tiley,&s_4waygun,patrolgunobj);
|
|
if (!loadedgame)
|
|
gamestate.killtotal++;
|
|
|
|
PreCacheActor(patrolgunobj,0);
|
|
new->temp1 = -1;
|
|
new->hitpoints = starthitpoints[gamestate.difficulty][patrolgunobj]*3;
|
|
new->flags |= (FL_BLOCK|FL_SHOOTABLE);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
=======================================================================
|
|
=
|
|
= NON-SHAREWARE CODE
|
|
=
|
|
=======================================================================
|
|
*/
|
|
|
|
#if (SHAREWARE == 0)
|
|
|
|
void T_BoulderSpawn(objtype*ob)
|
|
{objtype *tactor;
|
|
int dx,dy,cl;
|
|
|
|
if (!(ob->flags & FL_ACTIVE))
|
|
return;
|
|
|
|
else if (!ob->ticcount)
|
|
{for(tactor = firstareaactor[ob->areanumber];tactor;tactor = tactor->nextinarea)
|
|
{cl = tactor->obclass;
|
|
if (tactor == ob)
|
|
continue;
|
|
|
|
if (!(tactor->flags & FL_SHOOTABLE))
|
|
continue;
|
|
dx = abs(tactor->x - ob->x);
|
|
if (dx > MINACTORDIST)
|
|
continue;
|
|
dy = abs(tactor->y - ob->y);
|
|
if (dy > MINACTORDIST)
|
|
continue;
|
|
if ((cl == b_heinrichobj) || (cl== b_darkmonkobj) ||
|
|
(cl == b_darianobj) || (cl == b_robobossobj) ||
|
|
(cl == pillarobj) || (cl == wallopobj) ||
|
|
(cl == boulderobj))
|
|
return;
|
|
else break;
|
|
}
|
|
|
|
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_boulderdrop1,boulderobj);
|
|
new->z = 0;
|
|
new->dir = ob->dir;
|
|
//new->angle = dirangle8[new->dir];
|
|
new->speed = 0x4000;
|
|
ParseMomentum(new,dirangle8[new->dir]);
|
|
new->flags |= (FL_BLOCK|FL_NOFRICTION);
|
|
new->flags &= ~FL_SHOOTABLE;
|
|
new->whatever = ob;
|
|
if (tactor)
|
|
new->target = tactor;
|
|
MakeActive(new);
|
|
new->flags |= FL_ABP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void T_BoulderDrop(objtype*ob)
|
|
{int dx,dy,dz;
|
|
objtype * tactor;
|
|
statetype *tstate;
|
|
|
|
|
|
if (ob->state == &s_boulderdrop12)
|
|
{
|
|
|
|
if (ob->z == nominalheight)
|
|
NewState(ob,&s_boulderroll1);
|
|
else if (ob->momentumz)
|
|
{ob->z += (ob->momentumz>>16);
|
|
ob->momentumz += (GRAVITY<<1);
|
|
if (ob->z > nominalheight)
|
|
{ob->z = nominalheight;
|
|
ob->momentumz = 0;
|
|
//ob->flags &= ~FL_NOFRICTION;
|
|
}
|
|
}
|
|
else if (!ob->temp1)
|
|
{ob->momentumz = (GRAVITY<<6);
|
|
ob->temp1 = 1;
|
|
}
|
|
|
|
}
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
if (ob->state->condition & SF_SOUND)
|
|
|
|
SD_PlaySoundRTP(SD_BOULDERFALLSND,ob->x,ob->y);
|
|
tactor = (objtype*)(ob->target);
|
|
if (tactor && (!(tactor->flags & FL_DYING)))
|
|
{dx = tactor->x - ob->x;
|
|
dy = tactor->y - ob->y;
|
|
dz = tactor->z - ob->z;
|
|
if ((abs(dx) < MINACTORDIST) && (abs(dy) < MINACTORDIST) &&
|
|
(abs(dz) < 50))
|
|
{if (tactor->obclass != playerobj)
|
|
{tactor->momentumx = tactor->momentumy = tactor->momentumz = 0;
|
|
tactor->flags |= FL_DYING;
|
|
tactor->hitpoints = 0;
|
|
if (gamestate.violence < vl_high)
|
|
{if ((tstate = UPDATE_STATES[CRUSH][tactor->obclass - lowguardobj])!=NULL)
|
|
NewState(tactor,tstate);
|
|
|
|
//else
|
|
//Error("\n\Null low-violence crush state in boulder drop, instance of %s",debugstr[tactor->obclass]);
|
|
}
|
|
else
|
|
{tactor->shapeoffset = 0;
|
|
//tactor->flags|=FL_HBM;
|
|
NewState(tactor,&s_guts1);
|
|
}
|
|
}
|
|
else
|
|
{DamageThing(tactor,200);
|
|
Collision(tactor,ob,0,0);
|
|
M_CheckPlayerKilled(tactor);
|
|
}
|
|
SD_PlaySoundRTP(SD_ACTORSQUISHSND,tactor->x,tactor->y);
|
|
ob->target = NULL;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void CheckCrush(objtype*ob)
|
|
{
|
|
objtype *temp;
|
|
int dx,dy,dz;
|
|
|
|
for(temp = PLAYER[0];temp != PLAYER[numplayers-1]->next;temp=temp->next)
|
|
{
|
|
if (ob->flags & FL_DYING)
|
|
continue;
|
|
|
|
dx = abs(temp->x - ob->x);
|
|
if (dx > MINACTORDIST)
|
|
continue;
|
|
|
|
dy = abs(temp->y - ob->y);
|
|
if (dy > MINACTORDIST)
|
|
continue;
|
|
|
|
dz = abs(temp->z - ob->z);
|
|
if (dz > (MINACTORDIST>>10))
|
|
continue;
|
|
|
|
if (!ob->ticcount)
|
|
DamageThing(temp,EnvironmentDamage(ob));
|
|
Collision(temp,ob,ob->momentumx-temp->momentumx,ob->momentumy-temp->momentumy);
|
|
M_CheckPlayerKilled(temp);
|
|
}
|
|
}
|
|
|
|
|
|
void T_BoulderMove(objtype*ob)
|
|
{
|
|
|
|
|
|
if (MAPSPOT(ob->tilex,ob->tiley,1) == 395)
|
|
{NewState(ob,&s_bouldersink1);
|
|
return;
|
|
}
|
|
if (NOMOM)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
if ((!ob->ticcount) && (ob->state->condition & SF_SOUND) &&
|
|
areabyplayer[ob->areanumber])
|
|
SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
|
|
SelectPathDir(ob);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=========================================================================
|
|
=
|
|
= Boss Functions
|
|
=
|
|
=========================================================================
|
|
*/
|
|
|
|
//***************************** Esau ************************************
|
|
|
|
|
|
enum {
|
|
ESAU_USING_HOLES=1,
|
|
ESAU_LEAVING_CONTROL_ROOM,
|
|
ESAU_USING_TOUCH_PEDASTALS,
|
|
ESAU_CHASING_PLAYER
|
|
};
|
|
|
|
|
|
|
|
|
|
void T_EsauWait(objtype*ob)
|
|
{
|
|
int dist;
|
|
|
|
dist = FindDistance(ob->tilex-PLAYER[0]->tilex,ob->tiley-PLAYER[0]->tiley);
|
|
MISCVARS->ESAU_SHOOTING = false;
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
|
|
if ((dist>81) || (dist<36))
|
|
{
|
|
if (CheckLine(ob,PLAYER[0],MISSILE))
|
|
{
|
|
NewState(ob,&s_darianshoot1);
|
|
ob->momentumx = ob->momentumy = 0;
|
|
}
|
|
return;
|
|
}
|
|
else if ((!ob->dirchoosetime) && (CheckLine(ob,PLAYER[0],SHOOT)))
|
|
{
|
|
NewState(ob,&s_dariandefend1);
|
|
ob->dirchoosetime = (GameRandomNumber("T_EsauWait",0) % 35) + 17;//35;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
void T_EsauRise(objtype*ob)
|
|
{
|
|
int newarea,oldarea;
|
|
|
|
// if (gamestate.victoryflag)
|
|
// return;
|
|
|
|
if (!ob->ticcount)
|
|
{//Debug("\n tx before: %d, ty before: %d",
|
|
// ob->targettilex,ob->targettiley);
|
|
|
|
SelectTouchDir(ob);
|
|
if (ob->targettilex || ob->targettiley)
|
|
{//Debug("\n ob->tilex: %d, ob->tiley: %d, targettilex: %d, targettiley: %d",
|
|
// ob->tilex, ob->tiley, ob->targettilex, ob->targettiley);
|
|
|
|
SetTilePosition(ob,ob->targettilex,ob->targettiley);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
oldarea = ob->areanumber;
|
|
newarea = AREANUMBER(ob->tilex,ob->tiley);
|
|
if (oldarea != newarea)
|
|
{
|
|
RemoveFromArea(ob);
|
|
ob->areanumber = newarea;
|
|
MakeLastInArea(ob);
|
|
}
|
|
}
|
|
else
|
|
MISCVARS->EPOP[ob->temp3].x = MISCVARS->EPOP[ob->temp3].y = 0;
|
|
|
|
ob->dirchoosetime= (GameRandomNumber("T_EsauRise",0) % 35) + 17;
|
|
MISCVARS->ESAU_HIDING = false;
|
|
MISCVARS->ESAU_SHOOTING = true;
|
|
ob->flags |= FL_SHOOTABLE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_EsauChase(objtype*ob)
|
|
{
|
|
int dx,dy,chance,dist;
|
|
statetype *temp;
|
|
|
|
|
|
|
|
if ((ob->tilex == ob->targettilex) && (ob->tiley == ob->targettiley))
|
|
{
|
|
if (MISCVARS->DSTATE == ESAU_USING_HOLES)
|
|
{
|
|
MISCVARS->ESAU_HIDING = true;
|
|
MISCVARS->ESAU_SHOOTING = false;
|
|
SD_PlaySoundRTP(SD_DARIANHIDESND,ob->x,ob->y);
|
|
NewState(ob,&s_dariansink1);
|
|
ob->flags &= ~FL_SHOOTABLE;
|
|
return;
|
|
}
|
|
else if (MISCVARS->DSTATE == ESAU_LEAVING_CONTROL_ROOM)
|
|
{
|
|
if (!MISCVARS->doorcount)
|
|
{
|
|
SetTilePosition(ob,ob->tilex,ob->tiley);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
}
|
|
MISCVARS->doorcount ++;
|
|
if (MISCVARS->doorcount == 4)
|
|
MISCVARS->DSTATE = ESAU_USING_HOLES;
|
|
else // hack to FORCE esau to walk through door
|
|
{
|
|
switch (ob->temp1)
|
|
{
|
|
case east:
|
|
ob->targettilex ++;
|
|
break;
|
|
case west:
|
|
ob->targettilex --;
|
|
break;
|
|
case north:
|
|
ob->targettiley --;
|
|
break;
|
|
case south:
|
|
ob->targettiley ++;
|
|
break;
|
|
}
|
|
}
|
|
SelectTouchDir(ob);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (touchsprite && (touchsprite->itemnumber == stats[stat_dariantouch].type))
|
|
{
|
|
dx = touchsprite->x - ob->x;
|
|
dy = touchsprite->y - ob->y;
|
|
|
|
if (((dx > -0x5000) && (dx < 0x5000)) &&
|
|
((dy > -0x5000) && (dy < 0x5000)))
|
|
{
|
|
SD_PlaySoundRTP(SD_DARIANGONNAUSESND,ob->x,ob->y);
|
|
NewState(ob,&s_darianuse1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
|
|
if (NOMOM || (!ob->dirchoosetime))
|
|
{
|
|
SelectTouchDir(ob);
|
|
ob->dirchoosetime = M_CHOOSETIME(ob);
|
|
}
|
|
else
|
|
ActorMovement(ob);
|
|
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
if (CheckLine(ob,PLAYER[0],MISSILE)) // got a shot at player?
|
|
{
|
|
if (Near(ob,PLAYER[0],1))
|
|
chance = 300;
|
|
else
|
|
{
|
|
dx = abs(PLAYER[0]->tilex-ob->tilex);
|
|
dy = abs(PLAYER[0]->tiley-ob->tiley);
|
|
dist = (dx>dy)?dx:dy;
|
|
chance = 400/dist;
|
|
}
|
|
if (GameRandomNumber("T_EsauChase",0) <chance)
|
|
{
|
|
if ((temp=M_S(AIM)) != NULL)
|
|
{
|
|
NewState(ob,temp);
|
|
ob->dirchoosetime = 0;
|
|
ob->momentumx = ob->momentumy = 0;
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
return;
|
|
}
|
|
}
|
|
if (MISCVARS->ESAU_SHOOTING)
|
|
{
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void T_EsauSpears(objtype*ob)
|
|
{
|
|
|
|
if (ob->ticcount == (ob->state->tictime>>1)-1)
|
|
{
|
|
OLDTILEX = PLAYER[0]->tilex;
|
|
OLDTILEY = PLAYER[0]->tiley;
|
|
}
|
|
|
|
else if (!ob->ticcount)
|
|
{
|
|
SpawnNewObj(OLDTILEX,OLDTILEY,&s_speardown1,spearobj);
|
|
new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void FindDoor(objtype*ob)
|
|
{
|
|
int i,area1,area2,min,curr,
|
|
dest1x,dest1y,dest2x,dest2y,
|
|
d1,d2;
|
|
|
|
dirtype tdir1,tdir2;
|
|
doorobj_t*dr;
|
|
|
|
min = 0x7fffffff;
|
|
for(i=0;i<doornum;i++)
|
|
{
|
|
dr = doorobjlist[i];
|
|
if (dr->vertical)
|
|
{
|
|
area1 = MAPSPOT(dr->tilex-1,dr->tiley,0)-AREATILE;
|
|
dest1x = dr->tilex-1;
|
|
dest1y = dr->tiley;
|
|
tdir1 = east;
|
|
area2 = MAPSPOT(dr->tilex+1,dr->tiley,0)-AREATILE;
|
|
dest2x = dr->tilex+1;
|
|
dest2y = dr->tiley;
|
|
tdir2 = west;
|
|
}
|
|
else
|
|
{
|
|
area1 = MAPSPOT(dr->tilex,dr->tiley-1,0)-AREATILE;
|
|
dest1x = dr->tilex;
|
|
dest1y = dr->tiley-1;
|
|
tdir1 = south;
|
|
area2 = MAPSPOT(dr->tilex,dr->tiley+1,0)-AREATILE;
|
|
dest2x = dr->tilex;
|
|
dest2y = dr->tiley+1;
|
|
tdir2 = north;
|
|
}
|
|
|
|
//============================================================
|
|
#define CheckMinDist(destx,desty,dir) \
|
|
{ \
|
|
curr = FindDistance(destx-ob->tilex,desty-ob->tiley); \
|
|
if (curr < min) \
|
|
{ \
|
|
min = curr; \
|
|
ob->targettilex = destx; \
|
|
ob->targettiley = desty; \
|
|
ob->temp1 = dir; \
|
|
} \
|
|
}
|
|
//============================================================
|
|
|
|
if (area1 == ob->areanumber)
|
|
{
|
|
if (area1 == area2)
|
|
{
|
|
d1 = FindDistance(dest1x-ob->tilex,dest1y-ob->tiley);
|
|
d2 = FindDistance(dest2x-ob->tilex,dest2y-ob->tiley);
|
|
if (d2 < d1) //swap areas
|
|
{
|
|
CheckMinDist(dest2x,dest2y,tdir2);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
CheckMinDist(dest1x,dest1y,tdir1);
|
|
|
|
}
|
|
else if (area2 == ob->areanumber)
|
|
CheckMinDist(dest2x,dest2y,tdir2);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
int FindTouch(objtype *ob)
|
|
{
|
|
int i,curr,min,tx,ty,noneleft;
|
|
statobj_t* tempstat;
|
|
|
|
|
|
min = 0x7fffffff;
|
|
noneleft = 1;
|
|
for(i=0;i<MISCVARS->nexttouch;i++)
|
|
{
|
|
if (MISCVARS->ETOUCH[i].x || MISCVARS->ETOUCH[i].y)
|
|
{
|
|
noneleft = 0;
|
|
tx = MISCVARS->ETOUCH[i].x;
|
|
ty = MISCVARS->ETOUCH[i].y;
|
|
tempstat = sprites[tx][ty];
|
|
curr = FindDistance(tx-ob->tilex,ty-ob->tiley);
|
|
|
|
if (curr < min)
|
|
{
|
|
min = curr;
|
|
ob->targettilex = tx;
|
|
ob->targettiley = ty;
|
|
touchsprite = tempstat;
|
|
}
|
|
}
|
|
}
|
|
return (!noneleft);
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
{
|
|
down_in_a_hole=-1,
|
|
no_holes_available=0,
|
|
holes_unreachable=1,
|
|
hole_targetted=2
|
|
|
|
}hiding_status;
|
|
|
|
|
|
|
|
hiding_status HoleStatus(objtype*ob)
|
|
{
|
|
int i,tx,ty,dist,noneleft,invisible,curr,min;
|
|
tpoint dummy,*dptr = &dummy;
|
|
objtype *tactor;
|
|
_2Dpoint *tdptr;
|
|
|
|
min = 0x7fffffff;
|
|
noneleft = 1;
|
|
|
|
|
|
|
|
for(i=0;i<MISCVARS->nextpop;i++)
|
|
{
|
|
tdptr = &(MISCVARS->EPOP[i]);
|
|
|
|
if (tdptr->x || tdptr->y)
|
|
{
|
|
tactor = (objtype*)actorat[tdptr->x][tdptr->y];
|
|
if (tactor && (tactor->obclass == pillarobj))
|
|
{
|
|
tdptr->x = 0;
|
|
tdptr->y = 0;
|
|
MISCVARS->popsleft --;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (MISCVARS->popsleft > 1)
|
|
{
|
|
for(i=0;i<MISCVARS->nextpop;i++)
|
|
{
|
|
tdptr = &(MISCVARS->EPOP[i]);
|
|
|
|
if (tdptr->x || tdptr->y)
|
|
{
|
|
tx = tdptr->x;
|
|
ty = tdptr->y;
|
|
|
|
if ((PLAYER[0]->tilex == tx) || (PLAYER[0]->tiley == ty))
|
|
continue;
|
|
|
|
if (MISCVARS->ESAU_HIDING)
|
|
{
|
|
dist = FindDistance(PLAYER[0]->tilex-tx,PLAYER[0]->tiley-ty);
|
|
if ((ob->tilex == tx) && (ob->tiley == ty) && (MISCVARS->popsleft != 1))
|
|
continue;
|
|
noneleft = 0;
|
|
if ((MAPSPOT(tx,ty,0)-AREATILE) == ob->areanumber)
|
|
{
|
|
ob->targettilex = tx;
|
|
ob->targettiley = ty;
|
|
ob->temp3 = i;
|
|
if ((dist < 81) && (dist > 36))
|
|
return down_in_a_hole;
|
|
}
|
|
}
|
|
|
|
else if (!MISCVARS->ESAU_SHOOTING)
|
|
{
|
|
curr = FindDistance(tx-ob->tilex,ty-ob->tiley);
|
|
if (curr < min)
|
|
{
|
|
min = curr;
|
|
noneleft = 0;
|
|
dptr->which = ACTOR;
|
|
SetTilePosition(dptr,tx,ty);
|
|
//dptr->x = (tx << TILESHIFT) + TILEGLOBAL/2;
|
|
//dptr->y = (ty << TILESHIFT) + TILEGLOBAL/2;
|
|
dptr->z = ob->z;
|
|
invisible = 0;
|
|
if ((!CheckLine(ob,dptr,SHOOT)) && (MISCVARS->DSTATE != ESAU_USING_HOLES))
|
|
{
|
|
invisible = 1;
|
|
MISCVARS->DSTATE = ESAU_LEAVING_CONTROL_ROOM;
|
|
}
|
|
else
|
|
MISCVARS->DSTATE = ESAU_USING_HOLES;
|
|
ob->targettilex = tx;
|
|
ob->targettiley = ty;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (MISCVARS->ESAU_HIDING)
|
|
return down_in_a_hole;
|
|
|
|
if (noneleft)
|
|
{
|
|
MISCVARS->DSTATE = ESAU_CHASING_PLAYER;
|
|
return no_holes_available;
|
|
}
|
|
|
|
if (invisible) //leave present room
|
|
return holes_unreachable;
|
|
|
|
return hole_targetted;
|
|
}
|
|
|
|
|
|
void SelectTouchDir (objtype *ob)
|
|
{
|
|
int dx,dy,noneleft,invisible;
|
|
hiding_status hole;
|
|
|
|
|
|
dirtype d[3];
|
|
dirtype tdir, olddir, turnaround;
|
|
|
|
|
|
olddir=ob->dir;
|
|
turnaround= opposite[olddir];
|
|
|
|
|
|
invisible = 0;
|
|
noneleft = 1;
|
|
|
|
if (!MISCVARS->notouch)
|
|
{
|
|
if (!FindTouch(ob))
|
|
MISCVARS->notouch = 1;
|
|
else
|
|
MISCVARS->DSTATE = ESAU_USING_TOUCH_PEDASTALS;
|
|
}
|
|
|
|
else if ((!MISCVARS->noholes) && (MISCVARS->DSTATE != ESAU_LEAVING_CONTROL_ROOM))
|
|
{
|
|
hole = HoleStatus(ob);
|
|
|
|
switch(hole)
|
|
{
|
|
case down_in_a_hole:
|
|
return;
|
|
|
|
case no_holes_available:
|
|
MISCVARS->noholes = 1;
|
|
break;
|
|
|
|
case holes_unreachable:
|
|
FindDoor(ob);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
else if (MISCVARS->DSTATE == ESAU_CHASING_PLAYER)
|
|
|
|
// only gets here if all gimmicks (touch tables,
|
|
// holes) are inoperative
|
|
{
|
|
ob->flags |= FL_SHOOTABLE;
|
|
ob->targettilex = PLAYER[0]->tilex;
|
|
ob->targettiley = PLAYER[0]->tiley;
|
|
}
|
|
/*
|
|
if (DSTATE == SDOOR)
|
|
{dx = ((ob->targettilex<<16)+TILEGLOBAL/2) - ob->x;
|
|
dy = ob->y - ((ob->targettiley<<16)+TILEGLOBAL/2);
|
|
angle = atan2_appx(dx,dy);
|
|
ZEROMOM;
|
|
ParseMomentum(ob,angle);
|
|
ActorMovement(ob);
|
|
if (ob->momentumx || ob->momentumy)
|
|
{ob->angle = angle;
|
|
ob->dir = angletodir[ob->angle];
|
|
return;
|
|
}
|
|
}
|
|
else */
|
|
dx = ob->targettilex - ob->tilex;
|
|
dy = ob->tiley - ob->targettiley;
|
|
|
|
|
|
|
|
|
|
d[1]=nodir;
|
|
d[2]=nodir;
|
|
|
|
|
|
if (dx>0)
|
|
d[1]= east;
|
|
else if (dx<0)
|
|
d[1]= west;
|
|
if (dy>0)
|
|
d[2]=north;
|
|
else if (dy<0)
|
|
d[2]=south;
|
|
|
|
|
|
if (GameRandomNumber("SelectTouchDir",0)<128)
|
|
{
|
|
tdir=d[1];
|
|
d[1]=d[2];
|
|
d[2]=tdir;
|
|
}
|
|
|
|
ZEROMOM;
|
|
|
|
|
|
if (d[1]!=nodir)
|
|
M_CHECKDIR(ob,d[1]);
|
|
|
|
|
|
if (d[2]!=nodir)
|
|
M_CHECKDIR(ob,d[2]);
|
|
|
|
|
|
|
|
if (GameRandomNumber("SelectTouchDir",ob->obclass)>128) //randomly determine direction of search
|
|
{
|
|
for (tdir=north;tdir<=west;tdir++)
|
|
{
|
|
if (tdir!=turnaround)
|
|
M_CHECKDIR(ob,tdir);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (tdir=west;tdir>=north;tdir--)
|
|
{
|
|
if (tdir!=turnaround)
|
|
M_CHECKDIR(ob,tdir);
|
|
}
|
|
}
|
|
|
|
if (turnaround != nodir)
|
|
M_CHECKDIR(ob,turnaround);
|
|
|
|
|
|
if (olddir!=nodir)
|
|
M_CHECKDIR(ob,olddir);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//************** Krist ****************************************************
|
|
|
|
|
|
|
|
void CheckRunover(objtype*ob)
|
|
{int dx,dy,dz;
|
|
|
|
dx = abs(PLAYER[0]->x - ob->x);
|
|
if (dx > MINACTORDIST)
|
|
return;
|
|
|
|
dy = abs(PLAYER[0]->y - ob->y);
|
|
if (dy > MINACTORDIST)
|
|
return;
|
|
|
|
dz = abs(PLAYER[0]->z - ob->z);
|
|
if (dz > 10)
|
|
return;
|
|
|
|
locplayerstate->heightoffset = 18 + locplayerstate->playerheight;
|
|
locplayerstate->oldheightoffset = locplayerstate->heightoffset;
|
|
PLAYER[0]->temp2 = RENORMALIZE;
|
|
DamageThing(PLAYER[0],30);
|
|
Collision(PLAYER[0],ob,0,0);
|
|
M_CheckPlayerKilled(PLAYER[0]);
|
|
|
|
}
|
|
|
|
|
|
void T_HeinrichChase(objtype*ob)
|
|
{
|
|
int dx,dy,dist,chance,perpangle;
|
|
// statetype *temp;
|
|
boolean doorok;
|
|
|
|
CheckRunover(ob);
|
|
|
|
// ob->flags &= ~FL_DODGE;
|
|
if (CheckLine(ob,PLAYER[0],SIGHT))
|
|
{ob->targettilex = PLAYER[0]->x;
|
|
ob->targettiley = PLAYER[0]->y;
|
|
}
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
|
|
// if (gamestate.victoryflag)
|
|
// return;
|
|
|
|
|
|
if (CheckLine(ob,PLAYER[0],SHOOT)) // got a shot at PLAYER[0]?
|
|
{dx = abs(ob->tilex - PLAYER[0]->tilex);
|
|
dy = abs(ob->tiley - PLAYER[0]->tiley);
|
|
dist = dx>dy ? dx : dy;
|
|
if (!dist || dist==1)
|
|
chance = 300;
|
|
else
|
|
chance = 2400/dist;
|
|
|
|
if (GameRandomNumber("T_HeinrichChase",0) <chance)
|
|
{tpoint dummy,*dptr=&dummy;
|
|
|
|
if (Near(ob,PLAYER[0],2))
|
|
goto cdoor;
|
|
|
|
|
|
perpangle = AngleBetween(ob,PLAYER[0]) + ANGLES/4;
|
|
Fix(perpangle);
|
|
dptr->which = ACTOR;
|
|
dptr->x = ob->x + FixedMul(0x10000l,costable[perpangle]);
|
|
dptr->y = ob->y - FixedMul(0x10000l,sintable[perpangle]);
|
|
|
|
dptr->z = ob->z;
|
|
if (!CheckLine(dptr,PLAYER[0],SHOOT))
|
|
goto cdoor;
|
|
|
|
ob->target = PLAYER[0];
|
|
NewState(ob,M_S(AIM));
|
|
ob->dirchoosetime = 0;
|
|
return;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
cdoor:
|
|
doorok = NextToDoor(ob);
|
|
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
|
|
if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime) || doorok)
|
|
{/*if ((ob->flags & FL_DODGE) && (!doorok))
|
|
SelectKristDodgeDir (ob);
|
|
else */
|
|
SD_PlaySoundRTP(SD_KRISTMOTORSND,ob->x,ob->y);
|
|
SelectKristChaseDir(ob);
|
|
|
|
ob->dirchoosetime = 4*M_CHOOSETIME(ob);
|
|
|
|
}
|
|
|
|
else
|
|
{if (NOMOM)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
ActorMovement(ob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void T_Heinrich_Defend (objtype*ob)
|
|
{
|
|
CheckRunover(ob);
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
|
|
|
|
if (MISCVARS->HRAMMING)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
|
|
|
|
if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime))
|
|
{if (MISCVARS->HRAMMING)
|
|
{if (!Near(ob,PLAYER[0],3))
|
|
{NewState(ob,M_S(CHASE));
|
|
ob->dirchoosetime = 0;
|
|
return;
|
|
}
|
|
SelectKristChaseDir(ob);
|
|
}
|
|
else if (MISCVARS->HMINING)
|
|
{SelectMineDir(ob);
|
|
if (!MISCVARS->HMINING)
|
|
goto hchase;
|
|
ob->dirchoosetime = 5;//10;
|
|
return;
|
|
}
|
|
else
|
|
hchase:
|
|
NewState(ob,M_S(CHASE));
|
|
ob->dirchoosetime = 0;
|
|
}
|
|
else
|
|
{if (NOMOM)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
ActorMovement(ob);
|
|
}
|
|
}
|
|
|
|
|
|
void T_Heinrich_Out_of_Control(objtype*ob)
|
|
{
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
else
|
|
{
|
|
if (!ob->temp1)
|
|
{
|
|
SetGibSpeed(0x4000);
|
|
SpawnParticles(ob,RANDOM,120);
|
|
ResetGibSpeed();
|
|
|
|
NewState(ob,&s_dexplosion1);
|
|
SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y);
|
|
}
|
|
else
|
|
{
|
|
ob->dir = dirorder[ob->dir][PREV];
|
|
ob->angle = dirangle8[ob->dir];
|
|
if (ob->dir == (unsigned)ob->temp2)
|
|
{
|
|
if (ob->temp1 > 1)
|
|
ob->temp1--;
|
|
else
|
|
{
|
|
if (ob->temp3 == 7)
|
|
{
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
|
|
new->temp1 = 25;
|
|
new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_superparticles,inertobj);
|
|
new->flags |= FL_ABP;
|
|
PARTICLE_GENERATOR = new;
|
|
MakeActive(new);
|
|
}
|
|
if (ob->temp3)
|
|
ob->temp3 --;
|
|
else
|
|
ob->temp1 --;
|
|
}
|
|
}
|
|
|
|
if (ob->temp1)
|
|
ob->dirchoosetime = ob->temp1;
|
|
else
|
|
{
|
|
ob->dirchoosetime = 70; // end of spin wait for megaexplosion
|
|
if (PARTICLE_GENERATOR)
|
|
{
|
|
NewState(PARTICLE_GENERATOR,&s_megaremove);
|
|
PARTICLE_GENERATOR = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void SelectKristChaseDir(objtype*ob)
|
|
{int dx,dy,tx,ty,angle;
|
|
dirtype dtry1,dtry2,tdir,olddir,next,prev,straight;
|
|
//tpoint dummy,*dptr=&dummy;
|
|
|
|
olddir=ob->dir;
|
|
|
|
|
|
//dptr->which = ACTOR;
|
|
//dptr->z = ob->z;
|
|
if (ob->targettilex || ob->targettiley)
|
|
{tx = ob->targettilex;
|
|
ty = ob->targettiley;
|
|
dx= tx - ob->x;
|
|
dy= ob->y - ty;
|
|
// SetFinePosition(dptr,tx,ty);
|
|
if ( ((dx < 0x20000) && (dx > -0x20000)) &&
|
|
((dy < 0x20000) && (dy > -0x20000)))
|
|
{
|
|
dx= PLAYER[0]->x-ob->x;
|
|
dy= ob->y-PLAYER[0]->y;
|
|
// SetFinePosition(dptr,PLAYER[0]->x,PLAYER[0]->y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dx= PLAYER[0]->x-ob->x;
|
|
dy= ob->y-PLAYER[0]->y;
|
|
//SetFinePosition(dptr,PLAYER[0]->x,PLAYER[0]->y);
|
|
|
|
}
|
|
|
|
angle = atan2_appx(dx,dy);
|
|
straight = angletodir[angle];
|
|
/*
|
|
if (ob->areanumber == PLAYER[0]->areanumber)
|
|
{//tpoint newpos1,newpos2;
|
|
//dirtype leftdir;
|
|
//int leftangle1,leftangle2;
|
|
|
|
if (CheckLine(ob,&dummy,DIRCHECK))
|
|
{//Debug("\ntrying straight dir %d",straight);
|
|
M_CHECKTURN(ob,straight);
|
|
//Debug("\nstraight dir %d failed",straight);
|
|
}
|
|
//leftdir = dirorder[straight][PREV];
|
|
//leftangle1 = dirangle8[leftdir];
|
|
//newpos1.which = ACTOR;
|
|
//rightangle = dirangle[dirorder[straight][NEXT]];
|
|
//newpos1.x = ob->x + FixedMul(0x10000,costable[leftangle1]);
|
|
//newpos1.y = ob->y - FixedMul(0x10000,sintable[leftangle1]);
|
|
//newpos1.z = ob->z;
|
|
|
|
//leftangle2 = dirangle8[dirorder[leftdir][PREV]];
|
|
//newpos2.which = ACTOR;
|
|
//rightangle = dirangle[dirorder[straight][NEXT]];
|
|
//newpos2.x = ob->x + FixedMul(0x10000,costable[leftangle2]);
|
|
//newpos2.y = ob->y - FixedMul(0x10000,sintable[leftangle2]);
|
|
//newpos2.z = ob->z;
|
|
//if (CheckLine(&newpos1,&dummy,SHOOT))// || CheckLine(&newpos2,&dummy,SHOOT))
|
|
{for(tdir = dirorder[straight][PREV];tdir != dirorder[straight][NEXT];tdir = dirorder[tdir][PREV])
|
|
{//Debug("\ntried left-hand rule dir %d",tdir);
|
|
M_CHECKTURN(ob,tdir);
|
|
}
|
|
}
|
|
//else
|
|
//{for(tdir = dirorder[straight][NEXT];tdir != dirorder[straight][PREV];tdir = dirorder[tdir][NEXT])
|
|
// {//Debug("\ntrying right-hand rule dir %d",tdir);
|
|
// M_CHECKTURN(ob,tdir);
|
|
//Debug("\nright-hand rule dir %d failed\n",tdir);
|
|
// }
|
|
// }
|
|
}
|
|
else*/
|
|
{dtry1=nodir;
|
|
dtry2=nodir;
|
|
|
|
if (dx> ACTORSIZE)
|
|
dtry1= east;
|
|
else if (dx< -ACTORSIZE)
|
|
dtry1= west;
|
|
if (dy> ACTORSIZE)
|
|
dtry2=north;
|
|
else if (dy < -ACTORSIZE)
|
|
dtry2= south;
|
|
|
|
|
|
if (abs(dy)>abs(dx))
|
|
{tdir=dtry1;
|
|
dtry1=dtry2;
|
|
dtry2=tdir;
|
|
}
|
|
|
|
// ZEROMOM;
|
|
ob->momentumx = FixedMul (ob->momentumx, DEADFRICTION>>gamestate.difficulty);
|
|
ob->momentumy = FixedMul (ob->momentumy, DEADFRICTION>>gamestate.difficulty);
|
|
|
|
|
|
M_CHECKTURN(ob,straight);
|
|
|
|
if (dtry1 != nodir)
|
|
M_CHECKTURN(ob,dtry1);
|
|
|
|
if (dtry2 != nodir)
|
|
M_CHECKTURN(ob,dtry2);
|
|
|
|
if (dtry1 != nodir)
|
|
{M_CHECKTURN(ob,dirorder[dtry1][NEXT]);
|
|
M_CHECKTURN(ob,dirorder[dtry1][PREV]);
|
|
}
|
|
|
|
for(tdir = dirorder[olddir][NEXT];tdir != olddir;tdir = dirorder[tdir][NEXT])
|
|
M_CHECKTURN(ob,tdir);
|
|
|
|
ob->dir = olddir;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_KristLeft(objtype*ob)
|
|
{CheckRunover(ob);
|
|
ActorMovement(ob);
|
|
if (!ob->ticcount)
|
|
{SD_PlaySoundRTP(SD_KRISTTURNSND,ob->x,ob->y);
|
|
if (ob->dir != (unsigned)ob->temp1)
|
|
ob->dir = dirorder[ob->dir][NEXT];
|
|
else
|
|
{ob->temp1 = 0;
|
|
NewState(ob,&s_heinrichchase);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void T_KristRight(objtype*ob)
|
|
{CheckRunover(ob);
|
|
ActorMovement(ob);
|
|
if (!ob->ticcount)
|
|
{SD_PlaySoundRTP(SD_KRISTTURNSND,ob->x,ob->y);
|
|
if (ob->dir != (unsigned)ob->temp1)
|
|
ob->dir = dirorder[ob->dir][PREV];
|
|
else
|
|
{ob->temp1 = 0;
|
|
NewState(ob,&s_heinrichchase);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void T_KristCheckFire(objtype*ob)
|
|
{int perpangle,angle;
|
|
tpoint dummy;
|
|
|
|
if (!ob->ticcount)
|
|
{angle = AngleBetween(ob,PLAYER[0]);
|
|
|
|
if (ob->state == &s_heinrichshoot1)
|
|
perpangle = angle + ANGLES/4;
|
|
else
|
|
perpangle = angle - ANGLES/4;
|
|
|
|
Fix(perpangle);
|
|
|
|
|
|
dummy.which = ACTOR;
|
|
dummy.x = ob->x + FixedMul(0x4000,costable[angle]) + FixedMul(0x4000l,costable[perpangle]) +
|
|
FixedMul(PROJSIZE,costable[perpangle]); // offset ahead plus
|
|
// offset for left/right missile plus offset for missile
|
|
// radius (will missile reach player without hitting wall,etc.)
|
|
|
|
dummy.y = ob->y - FixedMul(0x4000,sintable[angle]) - FixedMul(0x4000l,sintable[perpangle]) -
|
|
FixedMul(PROJSIZE,sintable[perpangle]);
|
|
|
|
dummy.x -= (FixedMul(PROJSIZE,costable[perpangle])<<1);
|
|
|
|
dummy.y += (FixedMul(PROJSIZE,sintable[perpangle])<<1);
|
|
dummy.z = ob->z;
|
|
|
|
if (!CheckLine(&dummy,PLAYER[0],SHOOT))
|
|
{NewState(ob,&s_heinrichchase);
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SelectMineDir(objtype*ob)
|
|
{int angle,missangle;
|
|
dirtype olddir,tdir,next,prev,destdir;
|
|
static int nummines=0;
|
|
|
|
if (!CheckLine(ob,PLAYER[0],SIGHT))
|
|
{NewState(ob,M_S(CHASE));
|
|
MISCVARS->HMINING = 0;
|
|
return;
|
|
}
|
|
|
|
olddir = ob->dir;
|
|
|
|
angle = AngleBetween(ob,PLAYER[0]);
|
|
tdir = angletodir[angle];
|
|
destdir = opposite[tdir];
|
|
|
|
if (destdir != olddir)
|
|
{next = dirorder[olddir][NEXT];
|
|
prev = dirorder[olddir][PREV];
|
|
if (dirdiff[destdir][next] < dirdiff[destdir][prev])
|
|
ob->dir = next;
|
|
else
|
|
ob->dir = prev;
|
|
return;
|
|
}
|
|
|
|
nummines ++;
|
|
missangle = angle;
|
|
if (nummines == 2)
|
|
missangle -= (ANGLES/36);
|
|
else if (nummines == 3)
|
|
missangle += (ANGLES/36);
|
|
|
|
Fix(missangle);
|
|
// if (missangle > (ANGLES - 1))
|
|
// missangle -= ANGLES;
|
|
// else if (missangle < 0)
|
|
// missangle += ANGLES;
|
|
|
|
|
|
SpawnMissile(ob,h_mineobj,0x2000,missangle,&s_mine1,0xa000);
|
|
new->dirchoosetime = 140;
|
|
SD_PlaySoundRTP(SD_KRISTDROPSND,ob->x,ob->y);
|
|
|
|
if (nummines == 3)
|
|
{MISCVARS->HMINING = 0;
|
|
nummines = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void A_HeinrichShoot(objtype* ob)
|
|
{int angle,perpangle;
|
|
|
|
if (!ob->ticcount)
|
|
{angle = AngleBetween(ob,PLAYER[0]);
|
|
if (ob->state == &s_heinrichshoot4)
|
|
perpangle = angle + ANGLES/4;
|
|
else
|
|
perpangle = angle - ANGLES/4;
|
|
|
|
Fix(perpangle);
|
|
|
|
SpawnMissile(ob,missileobj,0x4000,angle,&s_missile1,0x8000);
|
|
SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y);
|
|
|
|
SetFinePosition(new,new->x + FixedMul(0x4000l,costable[perpangle]),
|
|
new->y - FixedMul(0x4000l,sintable[perpangle]));
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//***************************///////**************************************
|
|
//***************************/ NME /**************************************
|
|
//***************************///////**************************************
|
|
|
|
|
|
|
|
|
|
|
|
void UpdateNMELinkedActors(objtype*ob)
|
|
{
|
|
objtype *head,*wheels;
|
|
int oldarea;
|
|
|
|
|
|
head = (objtype*)(ob->whatever);
|
|
wheels = (objtype*)(ob->target);
|
|
|
|
oldarea = head->areanumber;
|
|
|
|
SetFinePosition(head,ob->x,ob->y);
|
|
SetFinePosition(wheels,ob->x,ob->y);
|
|
SetVisiblePosition(head,ob->x,ob->y);
|
|
SetVisiblePosition(wheels,ob->x,ob->y);
|
|
|
|
if (oldarea != ob->areanumber)
|
|
{
|
|
RemoveFromArea(head);
|
|
head->areanumber = ob->areanumber;
|
|
MakeLastInArea(head);
|
|
RemoveFromArea(wheels);
|
|
wheels->areanumber = ob->areanumber;
|
|
MakeLastInArea(wheels);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void T_OrobotChase(objtype*ob)
|
|
{
|
|
int dx,dy;
|
|
|
|
|
|
if (CheckLine(ob,PLAYER[0],SIGHT))
|
|
{
|
|
|
|
ob->targettilex = PLAYER[0]->tilex;
|
|
ob->targettiley = PLAYER[0]->tiley;
|
|
}
|
|
|
|
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
if (NMEspincheck(ob))
|
|
return;
|
|
|
|
dx = PLAYER[0]->x - ob->x;
|
|
dy = ob->y - PLAYER[0]->y;
|
|
/*
|
|
if ((dx > -0x18000) && (dx < 0x18000) && (dy > -0x18000) && (dy < 0x18000))
|
|
{NewState(ob,&s_NMEavoid);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (CheckLine(ob,PLAYER[0],SIGHT))
|
|
{
|
|
int inrange;
|
|
|
|
switch(gamestate.difficulty)
|
|
{
|
|
case gd_baby: inrange = Near(ob,PLAYER[0],6);break;
|
|
case gd_easy: inrange = Near(ob,PLAYER[0],9);break;
|
|
case gd_medium: inrange = Near(ob,PLAYER[0],12);break;
|
|
case gd_hard: inrange = 1;break;
|
|
}
|
|
|
|
if ((!Near(ob,PLAYER[0],3)) && inrange)
|
|
{
|
|
SD_PlaySoundRTP(SD_NMEREADYSND,ob->x,ob->y);
|
|
if ((ob->hitpoints < 2000) && (GameRandomNumber("NME special attack",0) < 120))
|
|
{
|
|
int next,prev;
|
|
|
|
next = dirorder16[ob->dir][NEXT];
|
|
prev = dirorder16[ob->dir][PREV];
|
|
ob->targettilex = (angletodir[atan2_appx(dx,dy)]<<1);
|
|
|
|
if (dirdiff16[prev][ob->targettilex] < dirdiff16[next][ob->targettiley])
|
|
ob->temp3 = PREV;
|
|
else
|
|
ob->temp3 = NEXT;
|
|
NewState(ob,&s_NMEspinfire);
|
|
}
|
|
else
|
|
{
|
|
NewState(ob,&s_NMEwindup);
|
|
ob->temp3 = 0;
|
|
}
|
|
//NewState((objtype*)(ob->target),&s_NMEwheelspin);
|
|
|
|
NewState((objtype*)(ob->target),&s_NMEwheels120);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
|
|
if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime))
|
|
{
|
|
SelectOrobotChaseDir(ob);
|
|
ob->dirchoosetime = 4;//8;
|
|
}
|
|
|
|
else
|
|
{
|
|
ActorMovement(ob);
|
|
UpdateNMELinkedActors(ob);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_Saucer(objtype*ob)
|
|
{int angle,dangle;
|
|
|
|
if (!ob->ticcount) // if on track at end of each state, accelerate
|
|
// towards PLAYER[0]
|
|
{if (ob->state->condition & SF_SOUND)
|
|
SD_PlaySoundRTP(SD_NMEREADYSND,ob->x,ob->y);
|
|
angle = AngleBetween(ob,PLAYER[0]);
|
|
dangle = ob->angle - angle;
|
|
if ((dangle > -(ANGLES/72)) && (dangle < (ANGLES/72)))
|
|
{if (ob->speed < 0x10000)
|
|
{ob->speed += 0x200;
|
|
ZEROMOM;
|
|
ParseMomentum(ob,ob->angle);
|
|
}
|
|
}
|
|
else // off track; zero mom. and select new dir.
|
|
{ob->speed = 0x1000;
|
|
ZEROMOM;
|
|
ob->angle = angle;
|
|
ParseMomentum(ob,ob->angle);
|
|
}
|
|
}
|
|
MissileMovement(ob);
|
|
|
|
|
|
}
|
|
|
|
|
|
void T_NME_WindUp(objtype*ob)
|
|
{objtype *head,*wheels;
|
|
|
|
head = (objtype*)(ob->whatever);
|
|
wheels = (objtype*)(ob->target);
|
|
|
|
if (ob->dirchoosetime)
|
|
{ob->dirchoosetime--;
|
|
return;
|
|
}
|
|
|
|
ob->dirchoosetime = 0;//3;
|
|
|
|
if (MISCVARS->NMErotate < 3)
|
|
{head->dir = dirorder16[head->dir][NEXT];
|
|
MISCVARS->NMErotate ++;
|
|
}
|
|
else if (MISCVARS->NMErotate < 6)
|
|
{head->dir = dirorder16[head->dir][PREV];
|
|
MISCVARS->NMErotate ++;
|
|
}
|
|
else if (MISCVARS->NMErotate < 9)
|
|
{ob->dir = dirorder16[ob->dir][NEXT];
|
|
wheels->dir = ob->dir;
|
|
MISCVARS->NMErotate++;
|
|
}
|
|
else if (MISCVARS->NMErotate < 12)
|
|
{ob->dir = dirorder16[ob->dir][PREV];
|
|
wheels->dir = ob->dir;
|
|
MISCVARS->NMErotate ++;
|
|
}
|
|
else
|
|
{MISCVARS->NMErotate = 0;
|
|
|
|
NewState(ob,&s_NMEattack);
|
|
ob->dirchoosetime = 0;
|
|
//ob->dirchoosetime = 50 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70;
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1rl);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2rl);
|
|
NewState(wheels,&s_NMEwheels2);
|
|
}
|
|
|
|
}
|
|
|
|
#define SPRAYDIST 0x12000
|
|
|
|
void SelectOrobotChaseDir(objtype*ob) // this code is for head
|
|
{
|
|
int dx,dy,angle,tx,ty;
|
|
int tdir,olddir,nextdir,prevdir;
|
|
objtype* head,*wheels;
|
|
|
|
|
|
|
|
head = (objtype*)(ob->whatever);
|
|
wheels = (objtype*)(ob->target);
|
|
olddir=head->dir;
|
|
|
|
findplayer:
|
|
if (ob->temp1 == -1)
|
|
{
|
|
if (ob->targettilex || ob->targettiley)
|
|
{
|
|
tx = (int)((ob->targettilex << TILESHIFT) + HALFGLOBAL1);
|
|
ty = (int)((ob->targettiley << TILESHIFT) + HALFGLOBAL1);
|
|
dx= tx - ob->x;
|
|
dy= ob->y - ty;
|
|
if (((dx <SPRAYDIST ) && (dx > -SPRAYDIST)) &&
|
|
((dy <SPRAYDIST ) && (dy > -SPRAYDIST)))
|
|
{
|
|
dx= PLAYER[0]->x-ob->x;
|
|
dy= ob->y - PLAYER[0]->y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dx= PLAYER[0]->x - ob->x;
|
|
dy= ob->y - PLAYER[0]->y;
|
|
}
|
|
|
|
angle = atan2_appx(dx,dy);
|
|
|
|
tdir = (((angletodir[angle])<<1) & 0xf);
|
|
}
|
|
else
|
|
{
|
|
tdir = (ob->temp1 & 0xf);
|
|
|
|
if ((head->dir == (unsigned)tdir) && (ob->dir == (unsigned)tdir)) // increment
|
|
// tried dir if robot will attempt to move at tdir =>
|
|
// head and body are at move try dir
|
|
{//Debug("\ntrying next queue dir %d",tdir);
|
|
MISCVARS->NMEdirstried ++;
|
|
if (MISCVARS->NMEdirstried == MISCVARS->NMEqueuesize) //gone through all queue entries
|
|
{//Debug("\nqueue exhausted");
|
|
ob->temp1 = -1;
|
|
MISCVARS->NMEdirstried = 0;
|
|
goto findplayer;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (tdir != olddir) //rotate head to new chase direction
|
|
{
|
|
nextdir = dirorder16[olddir][NEXT];
|
|
prevdir = dirorder16[olddir][PREV];
|
|
if (dirdiff16[tdir][nextdir] < dirdiff16[tdir][prevdir])
|
|
head->dir = nextdir;
|
|
else
|
|
head->dir = prevdir;
|
|
return;
|
|
}
|
|
//Debug("\nhead aligned to dir %d",tdir);
|
|
|
|
//oddir = ob->dir;
|
|
if (ob->dir != head->dir) // align body and wheels with head
|
|
{
|
|
ZEROMOM;
|
|
NewState(wheels,&s_NMEwheels120); //rotate wheels for spinning
|
|
nextdir = dirorder16[ob->dir][NEXT];
|
|
prevdir = dirorder16[ob->dir][PREV];
|
|
if (dirdiff16[head->dir][nextdir] < dirdiff16[head->dir][prevdir])
|
|
ob->dir = nextdir;
|
|
else
|
|
ob->dir = prevdir;
|
|
wheels->dir = ob->dir;
|
|
return;
|
|
}
|
|
|
|
// Debug("\nbody aligned to head at dir %d",ob->dir);
|
|
|
|
ZEROMOM;
|
|
ParseMomentum(ob,dirangle16[head->dir]);
|
|
// Debug("\ntrying to move at dir %d",head->dir);
|
|
ActorMovement(ob);
|
|
UpdateNMELinkedActors(ob);
|
|
|
|
if (ob->momentumx || ob->momentumy)
|
|
{
|
|
NewState(wheels,&s_NMEwheels2); // align wheels for movement
|
|
//Debug("\nmove at dir %d succesful, resetting queue",head->dir);
|
|
ob->temp1 = -1; //clear direction queue
|
|
return;
|
|
}
|
|
else if (ob->temp1 == -1) // if queue is empty
|
|
//make a queue of directions (byte packed)
|
|
{
|
|
//Debug("\nmove at dir %d failed and queue empty",head->dir);
|
|
ob->temp1 = 0;
|
|
MISCVARS->NMEdirstried = 0;
|
|
MISCVARS->NMEqueuesize = 0;
|
|
|
|
nextdir = ((tdir + 6) & 0xf);
|
|
prevdir = ((tdir - 6) & 0xf);
|
|
|
|
for(; MISCVARS->NMEqueuesize < 6;MISCVARS->NMEqueuesize += 2)
|
|
{
|
|
ob->temp1 <<= 4;
|
|
ob->temp1 += nextdir;
|
|
ob->temp1 <<= 4;
|
|
ob->temp1 += prevdir;
|
|
nextdir = ((nextdir-2) & 0xf);
|
|
prevdir = ((prevdir+2) & 0xf);
|
|
|
|
}
|
|
#if 0
|
|
SoftError("\n straight dir: %d\n queue dirs ",tdir);
|
|
for(count = 0;count < MISCVARS->NMEqueuesize;count++)
|
|
{
|
|
SoftError("\n dir %d: %d",MISCVARS->NMEqueuesize-count,
|
|
((ob->temp1 >> (4*count)) &0xf)
|
|
);
|
|
|
|
}
|
|
#endif
|
|
}
|
|
else // else goto next queue dir;
|
|
{
|
|
ob->temp1 >>= 4;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_NME_Explode(objtype*ob)
|
|
{
|
|
|
|
if (ob->ticcount == 35)
|
|
{objtype*head;
|
|
int op;
|
|
|
|
head = (objtype*)(ob->whatever);
|
|
|
|
op = FixedMul(GRAVITY,(head->z-25)<<16) << 1;
|
|
head->momentumz = -FixedSqrtHP(op);
|
|
head->momentumx = (GameRandomNumber("NME head momx",0) << 2);
|
|
head->momentumy = (GameRandomNumber("NME head momy",0) << 2);
|
|
head->hitpoints = 0;
|
|
head->flags |= FL_DYING;
|
|
NewState(head,&s_shootinghead);
|
|
|
|
//RemoveObj((objtype*)(ob->whatever)); // remove head
|
|
}
|
|
else if (!ob->ticcount)
|
|
{ob->shapeoffset = 0;
|
|
NewState(ob,&s_explosion1);
|
|
SetGibSpeed(0x4000);
|
|
SpawnParticles(ob,gt_sparks,200);
|
|
ResetGibSpeed();
|
|
RemoveObj((objtype*)(ob->target));
|
|
}
|
|
|
|
}
|
|
|
|
void T_NME_HeadShoot(objtype*ob)
|
|
{//int randtheta,i,offx,offy;
|
|
|
|
ob->z += (ob->momentumz>>16);
|
|
|
|
/*if (ob->momentumz < 0)
|
|
{for(i=0;i<3;i++)
|
|
{randtheta = (GameRandomNumber("NME spark drop",0) << 3);
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_particle1,inertobj);
|
|
new->temp2 = 1;
|
|
offx = FixedMul(0x400,costable[randtheta]);
|
|
offy = -FixedMul(0x400,sintable[randtheta]);
|
|
new->x = new->drawx = ob->x + offx;
|
|
new->y = new->drawy = ob->y + offy;
|
|
new->z = ob->z-15;
|
|
new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP);
|
|
new->dir = west;
|
|
MakeActive(new);
|
|
}
|
|
}*/
|
|
|
|
ob->momentumz += GRAVITY;
|
|
if (ob->z >= (nominalheight+45))
|
|
{ob->z = nominalheight+45;
|
|
if (ob->temp2)
|
|
{ob->momentumz = -30000*ob->temp2;
|
|
ob->temp2--;
|
|
}
|
|
else
|
|
{ob->momentumx = ob->momentumy = ob->momentumz = 0;
|
|
ob->shapeoffset = 0;
|
|
NewState(ob,&s_NMEheadexplosion);
|
|
|
|
return;
|
|
}
|
|
}
|
|
ActorMovement(ob);
|
|
|
|
}
|
|
|
|
|
|
boolean NMEspincheck(objtype*ob)
|
|
{
|
|
int dx,dy,dz;
|
|
|
|
dx = abs(PLAYER[0]->x - ob->x);
|
|
dy = abs(PLAYER[0]->y - ob->y);
|
|
dz = abs(PLAYER[0]->z - ob->z);
|
|
if ((dx < 0x10000) && (dy < 0x10000) && (dz < 32))
|
|
{
|
|
NewState(ob,&s_NMEspinattack);
|
|
NewState((objtype*)(ob->target),&s_NMEwheelspin);
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2);
|
|
ob->dirchoosetime = 1;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void T_NME_SpinAttack(objtype* ob)
|
|
{int mx,my,mz;
|
|
objtype*head,*wheels;
|
|
|
|
|
|
|
|
if (ob->ticcount == 30) // knock player back
|
|
{GetMomenta(PLAYER[0],ob,&mx,&my,&mz,0x4000);
|
|
DamageThing(PLAYER[0],20);
|
|
Collision(PLAYER[0],ob,mx,my);
|
|
M_CheckPlayerKilled(PLAYER[0]);
|
|
}
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
else
|
|
{head = (objtype*)(ob->whatever);
|
|
wheels = (objtype*)(ob->target);
|
|
wheels->dir = head->dir = ob->dir = dirorder16[dirorder16[ob->dir][NEXT]][NEXT];
|
|
|
|
ob->dirchoosetime = 1;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void T_NME_SpinFire(objtype*ob)
|
|
{
|
|
int randtheta,oldyzangle,dx,dy,xydist,dz;
|
|
objtype *head,*wheels;
|
|
|
|
|
|
head = (objtype*)(ob->whatever);
|
|
wheels = (objtype*)(ob->target);
|
|
|
|
if (ob->dir != (unsigned)ob->targettilex)
|
|
{ob->dir = head->dir = wheels->dir = dirorder16[ob->dir][ob->temp3];
|
|
return;
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
{ob->dirchoosetime --;
|
|
return;
|
|
}
|
|
|
|
if (ob->temp3 < 20)
|
|
{//randphi = (GameRandomNumber("NME generate phi",0) << 3) & ((ANGLES/2) -1);
|
|
if (GameRandomNumber("NME generate theta",0) < 128)
|
|
randtheta = (GameRandomNumber("NME generate theta",0)>>4);
|
|
else
|
|
randtheta = -(GameRandomNumber("NME generate theta",0)>>4);
|
|
dx = PLAYER[0]->x-ob->x;
|
|
dy = ob->y-PLAYER[0]->y;
|
|
if (GameRandomNumber("bcraft shoot up/down",0) < 128)
|
|
dz = 5;
|
|
else
|
|
dz = -5;
|
|
xydist = FindDistance(dx,dy);
|
|
randtheta += atan2_appx(dx,dy);
|
|
Fix(randtheta);
|
|
oldyzangle = ob->yzangle;
|
|
ob->yzangle = atan2_appx(xydist,dz<<10);
|
|
//ob->yzangle = randphi;
|
|
SD_PlaySoundRTP(BAS[ob->obclass].fire+1,ob->x,ob->y);
|
|
//wheels->dir = head->dir = ob->dir = dirorder16[dirorder16[ob->dir][NEXT]][NEXT];
|
|
SpawnMissile(ob,fireballobj,0x6000,randtheta,&s_NMEminiball1,0x10000);
|
|
ob->dirchoosetime = 1;
|
|
ob->yzangle = oldyzangle;
|
|
ob->temp3 ++;
|
|
}
|
|
else
|
|
{ob->temp3 = 0;
|
|
NewState(ob,&s_NMEchase);
|
|
NewState((objtype*)(ob->target),&s_NMEwheels2);
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void T_NME_Attack(objtype*ob)
|
|
{int angle,perpangle,i;
|
|
|
|
|
|
|
|
if (NMEspincheck(ob))
|
|
{//ob->temp3 = 0;
|
|
return;
|
|
}
|
|
if (ob->dirchoosetime)
|
|
{ob->dirchoosetime --;
|
|
return;
|
|
}
|
|
|
|
|
|
if (!CheckLine(ob,PLAYER[0],SIGHT))
|
|
{//ob->temp3 = 0;
|
|
//#if ((DEVELOPMENT == 1))
|
|
//Debug("\nCheckLine failed in NME Attack");
|
|
//#endif
|
|
NewState(ob,&s_NMEchase);
|
|
NewState((objtype*)(ob->target),&s_NMEwheels2);
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2);
|
|
return;
|
|
}
|
|
//sound = BAS[ob->obclass].fire;
|
|
angle = AngleBetween(ob,PLAYER[0]);
|
|
|
|
|
|
|
|
if ((ob->temp3 == 0) || (ob->temp3 == 1)) //heatseek
|
|
|
|
{SD_PlaySoundRTP(BAS[ob->obclass].fire+2,ob->x,ob->y);
|
|
angle = AngleBetween(ob,PLAYER[0]);
|
|
SpawnMissile(ob,missileobj,0x6000,angle,&s_missile1,0x8000);
|
|
if (ob->temp3 == 3)
|
|
perpangle = angle + ANGLES/4;
|
|
else
|
|
perpangle = angle - ANGLES/4;
|
|
Fix(perpangle);
|
|
|
|
new->temp1 = NME_HEATSEEKINGTYPE;
|
|
SetFinePosition(new,new->x + FixedMul(0x8000l,costable[perpangle]),
|
|
new->y - FixedMul(0x8000l,sintable[perpangle]));
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
if (!ob->temp3)
|
|
ob->dirchoosetime = 20;
|
|
else
|
|
{ob->dirchoosetime = 35 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70;
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2);
|
|
}
|
|
ob->temp3 ++;
|
|
|
|
}
|
|
|
|
else if (ob->temp3 == 2) // saucer
|
|
{ SpawnMissile(ob,NMEsaucerobj,0x1000,angle,&s_NMEsaucer1,0xc000);
|
|
new->flags |= FL_SHOOTABLE;
|
|
ob->temp3++;
|
|
ob->dirchoosetime = 35 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70;
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1rl);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2rl);
|
|
}
|
|
|
|
else if ((ob->temp3 == 3) || (ob->temp3 == 4)) // drunk
|
|
{SD_PlaySoundRTP(BAS[ob->obclass].fire+2,ob->x,ob->y);
|
|
if (!ob->temp3)
|
|
perpangle = angle + ANGLES/4;
|
|
else
|
|
perpangle = angle - ANGLES/4;
|
|
Fix(perpangle);
|
|
for(i=0;i<(2+gamestate.difficulty);i++)
|
|
{
|
|
SpawnMissile(ob,missileobj,0x6000,angle,&s_missile1,0x8000);
|
|
new->temp1 = NME_DRUNKTYPE;
|
|
SetFinePosition(new,new->x + FixedMul(0x8000l,costable[perpangle]),
|
|
new->y - FixedMul(0x8000l,sintable[perpangle]));
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
}
|
|
|
|
if (ob->temp3 == 3)
|
|
ob->dirchoosetime = 20;
|
|
else
|
|
{ob->temp3 = 0;
|
|
NewState(ob,&s_NMEchase);
|
|
if (!ob->temp2)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead1);
|
|
else
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2);
|
|
}
|
|
|
|
ob->temp3 ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//================== Tom/Snake ============================================
|
|
|
|
|
|
|
|
|
|
|
|
void T_DarkSnakeSpawn(objtype*ob)
|
|
{
|
|
objtype * linkinfront;
|
|
|
|
if (((ob->state == &s_darkmonkhspawn) && (!(ob->ticcount%8))) ||
|
|
((ob->state == &s_darkmonkfastspawn) && (!(ob->ticcount%4))))
|
|
{
|
|
GetNewActor();
|
|
MakeActive(new);
|
|
SetFinePosition(new,ob->x,ob->y);
|
|
SetVisiblePosition(new,ob->x,ob->y);
|
|
new->z = nominalheight;
|
|
new->areanumber = MAPSPOT(new->tilex,new->tiley,0)-AREATILE;
|
|
MakeLastInArea(new);
|
|
new->obclass = b_darksnakeobj;
|
|
new->which = ACTOR;
|
|
new->angle = AngleBetween(ob,PLAYER[0]);
|
|
new->dir = angletodir[new->angle];
|
|
if (SNAKELEVEL == 1)
|
|
new->speed = 0x5000;
|
|
else if (SNAKELEVEL == 2)
|
|
new->speed = 0x5800;
|
|
else
|
|
new->speed = 0x2000;
|
|
|
|
|
|
new->hitpoints = 1000;
|
|
new->dirchoosetime = 0;
|
|
new->door_to_open = -1;
|
|
|
|
new->flags |= (FL_ABP|FL_NOFRICTION|FL_SHOOTABLE|FL_BLOCK);
|
|
|
|
if (ob->whatever)
|
|
{
|
|
linkinfront = (objtype*)(ob->whatever);
|
|
linkinfront->whatever = new;
|
|
new->target = linkinfront;
|
|
new->targettilex = linkinfront->x;
|
|
new->targettiley = linkinfront->y;
|
|
new->angle = AngleBetween(new,linkinfront);
|
|
new->dir = angletodir[new->angle];
|
|
new->flags |= FL_NEVERMARK;
|
|
ParseMomentum(new,new->angle);
|
|
NewState(new,&s_darkmonksnakelink);
|
|
}
|
|
|
|
else
|
|
{
|
|
SNAKEHEAD = new;
|
|
if (SNAKELEVEL == 3)
|
|
NewState(new,&s_darkmonkhead);
|
|
else if (SNAKELEVEL == 1)
|
|
{
|
|
NewState(new,&s_snakefindpath);
|
|
new->flags |= FL_ATTACKMODE;
|
|
}
|
|
else if (SNAKELEVEL == 2)
|
|
{
|
|
NewState(new,&s_snakepath);
|
|
new->angle = 3*ANGLES/4;
|
|
new->dir = angletodir[new->angle];
|
|
new->flags |= FL_ATTACKMODE;
|
|
|
|
}
|
|
ob->targettilex = ob->targettiley = 0;
|
|
ParseMomentum(new,new->angle);
|
|
}
|
|
|
|
if (!ob->ticcount)
|
|
SNAKEEND = new;
|
|
|
|
ob->whatever = new;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void T_GenericMove(objtype*ob)
|
|
{int dx,dy;
|
|
|
|
if (ob->temp3 == -1)
|
|
return;
|
|
|
|
|
|
if (!(SNAKEHEAD->flags & FL_ATTACKMODE))
|
|
return;
|
|
|
|
if (ob->hitpoints <= 0)
|
|
{KillActor(ob);
|
|
ob->temp3 = 0;
|
|
return;
|
|
}
|
|
|
|
if (!ob->ticcount)
|
|
{if (ob->state == &s_darkmonkredlink)
|
|
ob->temp3 = 0;
|
|
else if ((ob!=SNAKEEND) && (ob->state == &s_redlinkhit))
|
|
NewState((objtype*)(ob->whatever),&s_redlinkhit);
|
|
}
|
|
|
|
dx = ob->targettilex-ob->x;
|
|
dy = ob->y-ob->targettiley;
|
|
if ((dx > -0xa000) && (dx < 0xa000) && (dy > -0xa000) && (dy < 0xa000))
|
|
{if (ob->temp1 && ob->temp2)
|
|
{dx = ob->temp1 - ob->x;
|
|
dy = ob->y - ob->temp2;
|
|
ZEROMOM;
|
|
/*
|
|
if ((ob->targettilex == ob->temp1) && (ob->targettiley == ob->temp2))
|
|
return; */
|
|
//ob->x = ob->drawx = ob->targettilex;
|
|
//ob->y = ob->drawy = ob->targettiley;
|
|
//ob->tilex = ob->x >> TILESHIFT;
|
|
//ob->tiley = ob->y >> TILESHIFT;
|
|
//#if ((DEVELOPMENT == 1))
|
|
// Debug("\nfollower %d being moved to targetx %4x and targety %4x",
|
|
// ob-SNAKEHEAD,ob->x,ob->y);
|
|
// #endif
|
|
ob->targettilex = ob->temp1;
|
|
ob->targettiley = ob->temp2;
|
|
#if (0)
|
|
Debug("\nfollower %d's new targetx %4x, targety %4x",
|
|
ob-SNAKEHEAD,ob->temp1,ob->temp2);
|
|
#endif
|
|
ob->angle = atan2_appx(dx,dy);
|
|
ob->dir = angletodir[ob->angle];
|
|
ParseMomentum(ob,ob->angle);
|
|
}
|
|
}
|
|
else if (NOMOM)
|
|
{//SNAKEHEAD->dirchoosetime = 0;
|
|
ParseMomentum(ob,ob->angle);
|
|
}
|
|
if (ob->momentumx || ob->momentumy)
|
|
MoveActor(ob);
|
|
|
|
// ActorMovement(ob);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= SelectSnakeDir
|
|
=
|
|
===============
|
|
*/
|
|
|
|
|
|
void SelectSnakeDir (objtype *ob)
|
|
{
|
|
int spot,centerx,centery,dx,dy;
|
|
|
|
spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;
|
|
|
|
if ((spot >= 0) && (spot<= 7) && ((ob->dir!=(unsigned)spot)||(!(ob->flags & FL_DONE))))
|
|
{ centerx= (ob->tilex << 16) + HALFGLOBAL1;
|
|
centery= (ob->tiley << 16) + HALFGLOBAL1;
|
|
dx = abs(centerx - ob->x);
|
|
dy = abs(centery - ob->y);
|
|
|
|
if ((dx < SNAKERAD) && (dy < SNAKERAD))
|
|
// new direction
|
|
{ZEROMOM;
|
|
ob->dir = spot;
|
|
ob->flags |= FL_DONE;
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
SetFinePosition(ob,centerx,centery);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
|
|
if (ob==SNAKEHEAD) {
|
|
SoftError("\n path changed at %d, %d",ob->tilex,ob->tiley);
|
|
}
|
|
}
|
|
}
|
|
|
|
MoveActor(ob);
|
|
|
|
}
|
|
|
|
|
|
void T_SnakePath(objtype*ob)
|
|
{objtype*temp,*follower;
|
|
|
|
if (SNAKEEND && (SNAKELEVEL == 2))
|
|
{if (CheckLine(SNAKEEND,PLAYER[0],SIGHT))
|
|
{if (ob->temp3 == -1) //if snake can see player
|
|
//and he's presently stopped, restart
|
|
{for(temp=ob;temp;temp=(objtype*)(temp->whatever))
|
|
{temp->temp3 = 0;
|
|
temp->momentumx = temp->temp1;
|
|
temp->momentumy = temp->temp2;
|
|
}
|
|
ob->dirchoosetime = 0;
|
|
}
|
|
}
|
|
else if (ob->temp3 != -1) //else if he hasn't been stopped, stop him
|
|
{for(temp=ob;temp;temp = (objtype*)(temp->whatever))
|
|
{temp->temp1 = temp->momentumx;
|
|
temp->temp2 = temp->momentumy;
|
|
temp->temp3 = -1;
|
|
temp->momentumx = temp->momentumy = 0;
|
|
}
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
|
|
else
|
|
{int count = 0;
|
|
|
|
for(temp=ob;temp->whatever;temp=(objtype*)(temp->whatever))
|
|
{follower = (objtype*)(temp->whatever);
|
|
follower->temp1 = temp->x;
|
|
follower->temp2 = temp->y;
|
|
|
|
SoftError("\n follower %d temp1 set to %4x, temp2 set to %4x",
|
|
count,temp->x,temp->y);
|
|
count ++;
|
|
}
|
|
ob->dirchoosetime = 2 ;//15
|
|
}
|
|
|
|
if (ob->momentumx || ob->momentumy)
|
|
SelectSnakeDir(ob);
|
|
//else
|
|
// {ParseMomentum(ob,ob->angle);
|
|
// MoveActor(ob);
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
void FindClosestPath(objtype*ob)
|
|
{int tx,ty,dx,dy,angle;
|
|
|
|
|
|
tx = (ob->targettilex << 16) + TILEGLOBAL/2;
|
|
ty = (ob->targettiley << 16) + TILEGLOBAL/2;
|
|
|
|
dx= tx - ob->x;
|
|
dy= ob->y - ty;
|
|
angle = atan2_appx(dx,dy);
|
|
|
|
ZEROMOM;
|
|
ParseMomentum(ob,angle);
|
|
MoveActor(ob);
|
|
|
|
}
|
|
|
|
|
|
void T_SnakeFindPath(objtype*ob)
|
|
{int i,dx,dy,currdist,mindist,map;
|
|
tpoint dstruct,*dummy=&dstruct;
|
|
objtype*temp,*follower;
|
|
|
|
if (ob->targettilex || ob->targettiley)
|
|
{FindClosestPath(ob);
|
|
dx = ob->targettilex - ob->tilex;
|
|
dy = ob->targettiley - ob->tiley;
|
|
if ((!dx) && (!dy))
|
|
{SetTilePosition(ob,ob->tilex,ob->tiley);
|
|
SetVisiblePosition(ob,ob->x,ob->y);
|
|
ob->y = ob->drawy = (ob->tiley << TILESHIFT) + TILEGLOBAL/2;
|
|
NewState(ob,&s_snakepath);
|
|
return;
|
|
}
|
|
}
|
|
|
|
else
|
|
{dummy->which = ACTOR;
|
|
mindist = 0x7fffffff;
|
|
for(i=0;i<whichpath;i++)
|
|
{
|
|
SetTilePosition(dummy,SNAKEPATH[i].x,SNAKEPATH[i].y);
|
|
dummy->z = ob->z;
|
|
if (CheckLine(ob,dummy,SIGHT))
|
|
{currdist = FindDistance(ob->tilex-dummy->tilex,ob->tiley-dummy->tiley);
|
|
map = MAPSPOT(ob->tilex,ob->tiley,0)-AREATILE;
|
|
if ((currdist < mindist) && (map >= 0) && (map <= NUMAREAS))
|
|
{ob->targettilex = dummy->tilex;
|
|
ob->targettiley = dummy->tiley;
|
|
mindist = currdist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
else
|
|
{for(temp=ob;temp->whatever;temp=(objtype*)(temp->whatever))
|
|
{follower = (objtype*)(temp->whatever);
|
|
follower->temp1 = temp->x;
|
|
follower->temp2 = temp->y;
|
|
}
|
|
ob->dirchoosetime = 2 ;//15
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_SnakeFinale(objtype*ob)
|
|
{
|
|
|
|
if ((ob->state == &s_snakefireworks1)||(ob->state == &s_snakefireworks2))
|
|
{
|
|
if (ob->z != (maxheight-200))
|
|
{
|
|
ob->z --;
|
|
return;
|
|
}
|
|
SetGibSpeed(0x4500);
|
|
SpawnParticles(ob,RANDOM,100);
|
|
|
|
SpawnParticles(ob,gt_spit,100);
|
|
ResetGibSpeed();
|
|
NewState(ob,&s_dexplosion1);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (!ob->ticcount)
|
|
{
|
|
NewState(EXPLOSIONS,&s_megaremove);
|
|
// SpawnParticles(ob,RANDOM,100);
|
|
// SpawnParticles(ob,SPIT,100);
|
|
return;
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
else
|
|
{
|
|
ob->dirchoosetime = (GameRandomNumber("snake finale choose",0) % 7) + 15;
|
|
SetGibSpeed(0x3000);
|
|
SpawnParticles(ob,RANDOM,30);
|
|
SpawnParticles(ob,gt_spit,20);
|
|
ResetGibSpeed();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_DarkSnakeChase(objtype*ob)
|
|
{
|
|
objtype* temp,*follower;
|
|
int tdir,angle;
|
|
|
|
|
|
if (!(ob->flags & FL_ATTACKMODE))
|
|
{
|
|
if (!(CheckSight(ob,player) || Near(ob,player,4)))
|
|
return;
|
|
else
|
|
{
|
|
ob->flags |= FL_ATTACKMODE;
|
|
MU_StartSong(song_bosssee);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ob->hitpoints <= 0)
|
|
{
|
|
MU_StartSong(song_bossdie);
|
|
KillActor(ob);
|
|
AddMessage("Oscuro defeated!",MSG_CHEAT);
|
|
return;
|
|
}
|
|
|
|
angle = AngleBetween(ob,PLAYER[0]);
|
|
tdir = angletodir[angle];
|
|
if (Near(ob,PLAYER[0],6) && (ob->dir == (unsigned)tdir) && (!(ob->state->condition & SF_DOWN)))
|
|
{
|
|
NewState(ob,&s_snakefire1);
|
|
SD_PlaySoundRTP(SD_SNAKEREADYSND,ob->x,ob->y);
|
|
}
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
if (ob->state == &s_darkmonkredhead)
|
|
ob->temp3 = 0; // no longer hitable
|
|
else if ((ob->state == &s_redheadhit) && (ob != SNAKEEND))
|
|
NewState((objtype*)(ob->whatever),&s_redlinkhit);
|
|
else if (ob->state->condition & SF_UP)
|
|
{
|
|
SpawnMissile(ob,dm_spitobj,0x6000,angle,&s_spit1,0x6000);
|
|
SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y);
|
|
//new->z -= 5;
|
|
}
|
|
//spawn spit;
|
|
}
|
|
|
|
if (CheckLine(ob,PLAYER[0],SIGHT))
|
|
{
|
|
ob->targettilex = PLAYER[0]->x;
|
|
ob->targettiley = PLAYER[0]->y;
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
{
|
|
ob->dirchoosetime--;
|
|
ActorMovement(ob);
|
|
if (NOMOM)
|
|
ob->dirchoosetime = 0;
|
|
}
|
|
|
|
else
|
|
{//if (ob)
|
|
for(temp=ob;temp->whatever;temp=(objtype*)(temp->whatever))
|
|
{
|
|
follower = (objtype*)(temp->whatever);
|
|
follower->temp1 = temp->x;
|
|
follower->temp2 = temp->y;
|
|
}
|
|
SelectChaseDir(ob);
|
|
ob->dirchoosetime = 7 ;//15
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void T_DarkmonkReact(objtype*ob)
|
|
{
|
|
if (ob->z < nominalheight)
|
|
{MISCVARS->monkz += MZADJUST;
|
|
ob->z = nominalheight + (MISCVARS->monkz >> 16);
|
|
//ob->z++;
|
|
return;
|
|
}
|
|
|
|
else
|
|
{int ocl;
|
|
|
|
ocl = ob->temp3;
|
|
|
|
if (ocl == p_kesobj)
|
|
NewState(ob,&s_darkmonkabsorb1);
|
|
else if (ocl == p_heatseekobj)
|
|
NewState(ob,&s_darkmonkhball1);
|
|
else if (ocl == p_firebombobj)
|
|
NewState(ob,&s_darkmonkbreathe1);
|
|
else
|
|
NewState(ob,&s_darkmonkchase1);
|
|
ob->dirchoosetime = 0;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void T_DarkmonkCharge(objtype*ob)
|
|
{int dx,dy;
|
|
|
|
dx = abs(PLAYER[0]->x - ob->x);
|
|
dy = abs(PLAYER[0]->y - ob->y);
|
|
if ((dx < 0xa000) && (dy < 0xa000))
|
|
{DamageThing(PLAYER[0],10);
|
|
Collision(PLAYER[0],ob,0,0);
|
|
M_CheckPlayerKilled(PLAYER[0]);
|
|
}
|
|
|
|
if (!ob->ticcount)
|
|
ob->speed >>= 1;
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
|
|
if (NOMOM || (!ob->dirchoosetime))
|
|
{ob->angle = AngleBetween(ob,PLAYER[0]);
|
|
ob->dir = angletodir[ob->angle];
|
|
ParseMomentum(ob,ob->angle);
|
|
ob->dirchoosetime = 5;
|
|
}
|
|
|
|
ActorMovement(ob);
|
|
|
|
|
|
}
|
|
|
|
|
|
void T_DarkmonkLandAndFire(objtype*ob)
|
|
{
|
|
|
|
if (ob->z < nominalheight)
|
|
{MISCVARS->monkz += MZADJUST;
|
|
ob->z = nominalheight + (MISCVARS->monkz >> 16);
|
|
//ob->z++;
|
|
return;
|
|
}
|
|
if (Near(ob,PLAYER[0],3))
|
|
{if (GameRandomNumber("darkmonkland",0)<128)
|
|
NewState(ob,&s_darkmonkbball1);
|
|
else
|
|
{ob->angle = AngleBetween(ob,PLAYER[0]);
|
|
ob->dir = angletodir[ob->angle];
|
|
ob->speed <<= 1; // goes twice as fast
|
|
ZEROMOM;
|
|
ParseMomentum(ob,ob->angle);
|
|
ob->dirchoosetime = 5; // change dir every 5 tics
|
|
ob->hitpoints -= 200; // big penalty for charging
|
|
if (ob->hitpoints <= 0)
|
|
{objtype*column = (objtype*)(ob->whatever);
|
|
|
|
EnableObject((long)column);
|
|
ob->whatever = NULL;
|
|
|
|
KillActor(ob);
|
|
NewState(ob,&s_darkmonkfastspawn);
|
|
AddMessage("Oscuro flees!",MSG_CHEAT);
|
|
return;
|
|
}
|
|
NewState(ob,&s_darkmonkcharge1);
|
|
}
|
|
|
|
}
|
|
else if (ob->temp1)
|
|
NewState(ob,&s_darkmonklightning1);
|
|
else
|
|
NewState(ob,&s_dmgreenthing1);
|
|
ob->temp1 ^= 1;
|
|
ob->dirchoosetime = 0;
|
|
|
|
}
|
|
|
|
|
|
void T_DarkmonkChase(objtype*ob)
|
|
{int chance,dx,dy,dist;
|
|
|
|
|
|
if (!Near(ob,PLAYER[0],2))
|
|
{if (ob->z > (maxheight - 100))
|
|
{MISCVARS->monkz -= MZADJUST;
|
|
ob->z = nominalheight + (MISCVARS->monkz >> 16);
|
|
//ob->z--;
|
|
return;
|
|
}
|
|
}
|
|
else if (ob->z < nominalheight)
|
|
{MISCVARS->monkz += MZADJUST;
|
|
ob->z = nominalheight + (MISCVARS->monkz >> 16);
|
|
//ob->z++;
|
|
return;
|
|
}
|
|
|
|
|
|
if (CheckLine(ob,PLAYER[0],SIGHT))
|
|
{ob->targettilex = PLAYER[0]->x;
|
|
ob->targettiley = PLAYER[0]->y;
|
|
}
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
if (CheckLine(ob,PLAYER[0],SHOOT)) // got a shot at player?
|
|
{dx = abs(ob->tilex - PLAYER[0]->tilex);
|
|
dy = abs(ob->tiley - PLAYER[0]->tiley);
|
|
dist = dx>dy ? dx : dy;
|
|
if (!dist || dist==1)
|
|
chance = 300;//300;
|
|
else
|
|
chance = 400/dist;//300/dist;
|
|
|
|
if (GameRandomNumber("T_DarkMonkChase",0) < chance)
|
|
{NewState(ob,&s_dmlandandfire);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
|
|
if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime))
|
|
{SelectChaseDir(ob);
|
|
ob->dirchoosetime = M_CHOOSETIME(ob);
|
|
}
|
|
|
|
else
|
|
{if (NOMOM)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
ActorMovement(ob);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//====================== End of Boss Functions ===========================//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void T_GunStand(objtype*ob)
|
|
{int dy,dx,infrontof,dz;
|
|
objtype* temp;
|
|
|
|
// if (ob->target)
|
|
// Error("gun reset with non-null target");
|
|
for(temp = firstareaactor[ob->areanumber];temp;temp= temp->nextinarea)
|
|
{if (temp == ob)
|
|
continue;
|
|
if (temp->obclass == ob->obclass)
|
|
continue;
|
|
if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING))
|
|
continue;
|
|
|
|
dy = ob->y - temp->y;
|
|
dx = ob->x - temp->x;
|
|
dz = ob->z - temp->z;
|
|
if ((abs(dy)>0x40000) || (abs(dx)>0x40000) || (abs(dz) > 20))
|
|
continue;
|
|
|
|
|
|
infrontof = 0;
|
|
|
|
switch (ob->dir)
|
|
{case north:
|
|
if ((dy > 0) && (abs(dx)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
case east:
|
|
if ((dx < 0) && (abs(dy)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
case south:
|
|
if ((dy < 0) && (abs(dx)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
case west:
|
|
if ((dx > 0) && (abs(dy)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (infrontof && CheckLine(ob,temp,SHOOT))
|
|
{ob->target = temp;
|
|
NewState(ob,&s_gunraise1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void T_4WayGunStand(objtype*ob)
|
|
{
|
|
int dy,dx,dz;
|
|
objtype* temp;
|
|
|
|
if (ob->target)
|
|
Error("gun reset with non-null target");
|
|
for(temp = firstareaactor[ob->areanumber];temp;temp= temp->nextinarea)
|
|
{
|
|
if (temp == ob)
|
|
continue;
|
|
|
|
if (temp->obclass == ob->obclass)
|
|
continue;
|
|
|
|
if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING))
|
|
continue;
|
|
|
|
dy = abs(ob->x-temp->x);
|
|
dx = abs(ob->y-temp->y);
|
|
dz = abs(ob->z-temp->z);
|
|
if ((dx < 0x40000) && (dy < 0x40000) && (dz< 20) && CheckLine(ob,temp,SHOOT))
|
|
{//if ((dx < 0x8000) || (dy <0x8000))
|
|
ob->target = temp;
|
|
NewState(ob,&s_4waygunfire1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void A_GunShoot(objtype*ob)
|
|
{ int dx,dy,dz,damage,infrontof,tnear,savedangle;
|
|
objtype * target;
|
|
|
|
if (!ob->ticcount)
|
|
{target = (objtype*)(ob->target);
|
|
if (!target)
|
|
Error("an instance of %s called gunshoot without a target\n",debugstr[ob->obclass]);
|
|
if ((!(target->flags & FL_SHOOTABLE)) || (target->flags & FL_DYING))
|
|
{NewState(ob,&s_gunlower1);
|
|
ob->target = NULL;
|
|
return;
|
|
}
|
|
|
|
dx = target->x-ob->x;
|
|
dy = ob->y-target->y;
|
|
dz = ob->z-target->z;
|
|
|
|
|
|
tnear = ((abs(dy)<0x40000) && (abs(dx)<0x40000) && (abs(dz) < 20));
|
|
infrontof = 0;
|
|
|
|
switch (ob->dir)
|
|
{case north:
|
|
if ((dy > 0) && (abs(dx)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
case east:
|
|
if ((dx < 0) && (abs(dy)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
case south:
|
|
if ((dy < 0) && (abs(dx)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
case west:
|
|
if ((dx > 0) && (abs(dy)<0x8000))
|
|
infrontof = 1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((!infrontof) || (!CheckLine(ob,target,SHOOT)) ||
|
|
(!tnear))
|
|
{NewState(ob,&s_gunlower1);
|
|
ob->target = NULL;
|
|
return;
|
|
}
|
|
|
|
//SD_PlaySoundRTP(SD_FIRE,PLAYER[0]->x,PLAYER[0]->y,ob->x,ob->y);
|
|
//hitchance = 128;
|
|
|
|
// if (!target)
|
|
// Error("object called shoot without a target\n");
|
|
|
|
|
|
damage = DMG_AHGUN;
|
|
|
|
|
|
if (target->obclass == playerobj)
|
|
{target->target = ob;
|
|
if (target->flags & FL_BPV)
|
|
damage >>= 1;
|
|
|
|
}
|
|
savedangle = ob->angle;
|
|
ob->angle = atan2_appx(dx,dy);
|
|
RayShoot(ob,damage,GameRandomNumber("A_GunShoot Accuracy",0) % 20);
|
|
ob->angle = savedangle;
|
|
SD_PlaySoundRTP(SD_BIGEMPLACEFIRESND,ob->x,ob->y);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void A_4WayGunShoot(objtype*ob)
|
|
{
|
|
int dx,dy,dz,damage,savedangle;
|
|
objtype * target;
|
|
|
|
if (ob->ticcount == (ob->state->tictime >> 1))
|
|
{
|
|
target = (objtype*)(ob->target);
|
|
if (!target)
|
|
Error("an instance of %s called 4waygunshoot without a target\n",debugstr[ob->obclass]);
|
|
dx = abs(target->x-ob->x);
|
|
dy = abs(ob->y-target->y);
|
|
dz = abs(ob->z-target->z);
|
|
if ((dx > 0x40000) || (dy > 0x40000) || (dz > 20) ||
|
|
(!CheckLine(ob,target,SHOOT)) ||
|
|
(!(target->flags & FL_SHOOTABLE)) ||
|
|
(target->flags & FL_DYING)
|
|
)
|
|
{
|
|
ob->target = NULL;
|
|
NewState(ob,&s_4waygun);
|
|
return;
|
|
}
|
|
|
|
|
|
//SD_PlaySoundRTP(SD_FIRE,PLAYER[0]->x,PLAYER[0]->y,ob->x,ob->y);
|
|
//hitchance = 128;
|
|
|
|
// if (!target)
|
|
// Error("object called shoot without a target\n");
|
|
|
|
|
|
damage = DMG_AHGUN;
|
|
SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y);
|
|
|
|
if (target->obclass == playerobj)
|
|
{
|
|
target->target = ob;
|
|
if (target->flags & FL_BPV)
|
|
damage >>= 1;
|
|
}
|
|
|
|
savedangle = ob->angle;
|
|
ob->angle = 0;
|
|
RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
|
|
ob->angle = ANG90;
|
|
RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
|
|
ob->angle = ANG180;
|
|
RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
|
|
ob->angle = ANG270;
|
|
RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
|
|
ob->angle = savedangle;
|
|
}
|
|
}
|
|
|
|
|
|
void A_Drain (objtype *ob)
|
|
{
|
|
int dx,dy,dz,damage;
|
|
|
|
dx = abs(PLAYER[0]->x - ob->x);
|
|
dy = abs(PLAYER[0]->y - ob->y);
|
|
dz = abs(PLAYER[0]->z - ob->z);
|
|
|
|
if ((dx > TOUCHDIST) || (dy > TOUCHDIST) || (dz > (TOUCHDIST>>10)))
|
|
{
|
|
NewState(ob,&s_dmonkshoot5);
|
|
return;
|
|
}
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
else
|
|
{
|
|
damage = (GameRandomNumber("A_Drain",ob->obclass) >> 3);
|
|
DamageThing (PLAYER[0],damage);
|
|
ob->hitpoints += damage;
|
|
if (ob->hitpoints > starthitpoints[gamestate.difficulty][ob->obclass])
|
|
ob->hitpoints = starthitpoints[gamestate.difficulty][ob->obclass];
|
|
|
|
Collision(PLAYER[0],ob,0,0);
|
|
if (PLAYER[0]->flags & FL_DYING)
|
|
PLAYER[0]->target = ob;
|
|
M_CheckPlayerKilled(PLAYER[0]);
|
|
SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void A_DmonkAttack(objtype*ob)
|
|
{int angle,nobclass,nspeed,altangle1=0,altangle2=0,zoff=0,sound;
|
|
statetype *nstate;
|
|
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
ob->hitpoints -= 120;//120;
|
|
if (ob->hitpoints <= 0)
|
|
{
|
|
objtype*column = (objtype*)(ob->whatever);
|
|
|
|
EnableObject((long)column);
|
|
ob->whatever = NULL;
|
|
|
|
KillActor(ob);
|
|
NewState(ob,&s_darkmonkfastspawn);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime --;
|
|
|
|
else
|
|
{sound = BAS[ob->obclass].fire;
|
|
angle = AngleBetween(ob,PLAYER[0]);
|
|
nspeed = 0x6000;
|
|
|
|
if (ob->state == &s_darkmonksphere8)
|
|
{nstate = &s_kessphere1;
|
|
nobclass = p_kesobj;
|
|
ob->dirchoosetime = 70;
|
|
}
|
|
|
|
else if (ob->state == &s_darkmonkhball7)
|
|
{nstate = &s_handball1;
|
|
nobclass = dm_heatseekobj;
|
|
nspeed = 0x3000;
|
|
ob->dirchoosetime = 5;
|
|
}
|
|
|
|
else if (ob->state == &s_darkmonkbball7)
|
|
{nstate = &s_faceball1;
|
|
nobclass = dm_weaponobj;
|
|
nspeed = 0x3000;
|
|
ob->dirchoosetime = 5;
|
|
}
|
|
|
|
else if (ob->state == &s_darkmonklightning9)
|
|
{nstate = &s_lightning;
|
|
nobclass = dm_weaponobj;
|
|
ob->dirchoosetime = 3;
|
|
sound++;
|
|
}
|
|
|
|
else if (ob->state == &s_dmgreenthing8)
|
|
{nstate = &s_energysphere1;
|
|
nobclass = dm_weaponobj;
|
|
sound +=2;
|
|
ob->dirchoosetime = 70;
|
|
}
|
|
|
|
else if (ob->state == &s_darkmonkfspark5)
|
|
{nstate = &s_floorspark1;
|
|
|
|
altangle1 = angle + ANGLES/24;
|
|
altangle2 = angle - ANGLES/24;
|
|
Fix(altangle1);
|
|
Fix(altangle2);
|
|
nobclass = dm_weaponobj;
|
|
ob->dirchoosetime = 3;
|
|
sound += 3;
|
|
}
|
|
|
|
else if (ob->state == &s_darkmonkbreathe6)
|
|
{nstate = &s_crossfire1;
|
|
ob->dirchoosetime = 3;
|
|
nobclass = dm_weaponobj;
|
|
zoff = -15;
|
|
sound += 3;
|
|
}
|
|
|
|
SpawnMissile(ob,nobclass,nspeed,angle,nstate,0xb000);
|
|
SD_PlaySoundRTP(sound,ob->x,ob->y);
|
|
|
|
new->z = ob->z+zoff;
|
|
if (altangle1)
|
|
{
|
|
SpawnMissile(ob,nobclass,nspeed,altangle1,nstate,0xb000);
|
|
SpawnMissile(ob,nobclass,nspeed,altangle2,nstate,0xb000);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // SHAREWARE endif
|
|
|
|
|
|
|
|
|
|
//=====================================================================//
|
|
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= T_Stand
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void T_Stand (objtype *ob)
|
|
{
|
|
if (!ob->ticcount)
|
|
SightPlayer (ob);
|
|
else
|
|
SoftError("\n ob type %s ticcount of %d in T_Stand",debugstr[ob->obclass],
|
|
ob->ticcount);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DamagePlayerActor(objtype *ob, int damage)
|
|
|
|
{
|
|
playertype *pstate;
|
|
|
|
switch (gamestate.difficulty)
|
|
{
|
|
case 0: damage >>= 1;
|
|
break;
|
|
case 1: damage -= (damage >> 2);
|
|
break;
|
|
case 2: break;
|
|
case 3: //damage += (damage>>2);
|
|
break;
|
|
//default: Error("Um, Gamestate.Difficulty, uh, has problems.\n");
|
|
}
|
|
|
|
if (!damage) damage++;
|
|
|
|
M_LINKSTATE(ob,pstate);
|
|
|
|
|
|
pstate->health -= damage;
|
|
ob->hitpoints = pstate->health;
|
|
|
|
SD_PlaySoundRTP(SD_PLAYERTCHURTSND+(pstate->player),ob->x,ob->y);
|
|
if (ob==player)
|
|
{
|
|
damagecount += damage;
|
|
if (cybermanenabled)
|
|
SWIFT_TactileFeedback (10*damage, 15, 15);
|
|
if ( SHOW_BOTTOM_STATUS_BAR() )
|
|
DrawBarHealth (false);
|
|
}
|
|
|
|
if (pstate->health<=0)
|
|
{
|
|
pstate->health = 0;
|
|
ob->hitpoints = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void DamageNonPlayerActor(objtype *ob,int damage)
|
|
{
|
|
//if ((ob->obclass == b_darksnakeobj) && (!ob->temp3))
|
|
// return;
|
|
|
|
if (!(ob->flags & FL_ATTACKMODE))
|
|
damage <<= 1;
|
|
|
|
ob->hitpoints -= damage;
|
|
if (ob->hitpoints <= 0)
|
|
{
|
|
int sound;
|
|
|
|
sound = BAS[ob->obclass].die;
|
|
if (ob->obclass == lowguardobj)
|
|
{
|
|
if (ob->shapeoffset)
|
|
sound ++;
|
|
}
|
|
SD_PlaySoundRTP(sound,ob->x,ob->y);
|
|
}
|
|
else
|
|
SD_PlaySoundRTP(BAS[ob->obclass].hit,ob->x,ob->y);
|
|
|
|
#if (SHAREWARE == 0)
|
|
if ((ob->obclass == b_robobossobj) && (ob->temp2 <= 2))
|
|
{
|
|
if (ob->hitpoints <
|
|
((3-ob->temp2)*starthitpoints[gamestate.difficulty][ob->obclass]>>2)
|
|
)
|
|
{
|
|
SD_PlaySoundRTP(SD_NMEAPARTSND,ob->x,ob->y);
|
|
ob->temp2++;
|
|
ob->shapeoffset += 16;
|
|
ob->speed += 0x500;
|
|
SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
|
|
new->temp1 = 3;
|
|
new->flags |= FL_ABP;
|
|
MakeActive(new);
|
|
}
|
|
if (ob->temp2 == 1)
|
|
NewState((objtype*)(ob->whatever),&s_NMEhead2);
|
|
}
|
|
#endif
|
|
MISCVARS->madenoise = true;
|
|
}
|
|
|
|
|
|
|
|
void DamageStaticObject(statobj_t*tempstat,int damage)
|
|
{
|
|
|
|
|
|
tempstat->hitpoints -= damage;
|
|
if (tempstat->hitpoints <= 0)
|
|
{
|
|
sprites[tempstat->tilex][tempstat->tiley]=NULL;
|
|
tempstat->flags |= FL_NONMARK;
|
|
if (tempstat->flags&FL_LIGHT)
|
|
{
|
|
|
|
if (MAPSPOT(tempstat->tilex,tempstat->tiley,2))
|
|
{touchplatetype *tplate;
|
|
|
|
for(tplate=touchplate[tempstat->linked_to];tplate;tplate = tplate->nextaction)
|
|
if (tplate->whichobj == (long)(tempstat))
|
|
RemoveTouchplateAction(tplate,tempstat->linked_to);
|
|
}
|
|
|
|
if (tempstat->flags & FL_LIGHTON)
|
|
TurnOffLight(tempstat->tilex,tempstat->tiley);
|
|
|
|
|
|
if (tempstat->itemnumber<=stat_chandelier)
|
|
//SpawnFallingDebris(tempstat->x,tempstat->y,tempstat->z-32);
|
|
{
|
|
objtype *prevlast = LASTACTOR;
|
|
|
|
SpawnSlowParticles(gt_sparks,4,tempstat->x,tempstat->y,tempstat->z-32);
|
|
for(prevlast = prevlast->next;prevlast;prevlast= prevlast->next)
|
|
{
|
|
prevlast->momentumz = 1; // any positive value will do
|
|
prevlast->momentumx >>= 1;
|
|
prevlast->momentumy >>= 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_metalshards,-1);
|
|
LASTSTAT->flags |= (FL_ABP|FL_NONMARK);
|
|
sprites[tempstat->tilex][tempstat->tiley] = NULL;
|
|
MakeStatActive(LASTSTAT);
|
|
switch (tempstat->itemnumber)
|
|
{
|
|
case stat_lamp:
|
|
case stat_altbrazier1:
|
|
case stat_altbrazier2:
|
|
case stat_torch:
|
|
SpawnSlowParticles(gt_sparks,5,tempstat->x,tempstat->y,tempstat->z-32);
|
|
break;
|
|
case stat_floorfire:
|
|
SpawnSlowParticles(gt_sparks,5,tempstat->x,tempstat->y,tempstat->z);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
SpawnSolidStatic(tempstat);
|
|
SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y);
|
|
}
|
|
else
|
|
{
|
|
switch (tempstat->itemnumber)
|
|
{
|
|
case stat_dariantouch:
|
|
MISCVARS->ETOUCH[tempstat->linked_to].x = MISCVARS->ETOUCH[tempstat->linked_to].y = 0;
|
|
case stat_tntcrate:
|
|
case stat_bonusbarrel:
|
|
SpawnNewObj(tempstat->tilex,tempstat->tiley,&s_staticexplosion1,inertobj);
|
|
MakeActive(new);
|
|
new->flags |= FL_ABP;
|
|
new->whatever = tempstat;
|
|
new->temp2 = damage;
|
|
|
|
if (tempstat->itemnumber == stat_bonusbarrel)
|
|
{
|
|
int rand = GameRandomNumber("DamageThing",0);
|
|
|
|
if (rand < 80)
|
|
{
|
|
if (rand & 1)
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_monkmeal,-1);
|
|
else
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_priestporridge,-1);
|
|
gamestate.healthtotal ++;
|
|
}
|
|
else if (rand < 160)
|
|
{
|
|
if (rand & 1)
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_lifeitem1,-1);
|
|
else
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_lifeitem3,-1);
|
|
}
|
|
else
|
|
{
|
|
if (rand & 1)
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_mp40,-1);
|
|
else
|
|
{
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_heatseeker,-1);
|
|
gamestate.missiletotal ++;
|
|
}
|
|
LASTSTAT->flags &= ~FL_RESPAWN;
|
|
}
|
|
//LASTSTAT->flags &= ~FL_SHOOTABLE;
|
|
LASTSTAT->flags |= FL_ABP;
|
|
MakeStatActive(LASTSTAT);
|
|
SD_PlaySoundRTP(SD_BONUSBARRELSND,tempstat->x,tempstat->y);
|
|
}
|
|
else
|
|
{
|
|
ExplodeStatic(tempstat);
|
|
if (tempstat == touchsprite)
|
|
touchsprite = NULL;
|
|
}
|
|
SpawnSolidStatic(tempstat);
|
|
//SD_Play(SD_EXPL);
|
|
break;
|
|
#if (SHAREWARE == 0)
|
|
case stat_mine:
|
|
SpawnNewObj(tempstat->tilex,tempstat->tiley,&s_grexplosion1,inertobj);
|
|
MakeActive(new);
|
|
new->flags |= FL_ABP;
|
|
new->whatever = tempstat;
|
|
new->temp2 = damage;
|
|
RemoveStatic(tempstat);
|
|
break;
|
|
|
|
case stat_tomlarva:
|
|
SD_PlaySoundRTP(SD_ACTORSQUISHSND,tempstat->x,tempstat->y);
|
|
SpawnGroundExplosion(tempstat->x,tempstat->y,tempstat->z);
|
|
//MISCVARS->gibgravity = GRAVITY/2;
|
|
MISCVARS->fulllightgibs = true;
|
|
SetGibSpeed(0x4000);
|
|
SpawnSlowParticles(GUTS,30,tempstat->x, tempstat->y,tempstat->z);
|
|
ResetGibSpeed();
|
|
MISCVARS->fulllightgibs = false;
|
|
//MISCVARS->gibgravity = -1;
|
|
RemoveStatic(tempstat);
|
|
break;
|
|
#endif
|
|
|
|
case stat_lifeitem1:
|
|
case stat_lifeitem2:
|
|
case stat_lifeitem3:
|
|
case stat_lifeitem4:
|
|
SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y);
|
|
gamestate.treasurecount ++;
|
|
SpawnSlowParticles(gt_sparks,10,tempstat->x,tempstat->y,tempstat->z);
|
|
SpawnSolidStatic(tempstat);
|
|
break;
|
|
|
|
default:
|
|
|
|
if ((tempstat->itemnumber == stat_plant) ||
|
|
(tempstat->itemnumber == stat_tree))
|
|
gamestate.plantcount++;
|
|
|
|
//tempstat->shapenum = -1;
|
|
//tempstat->flags &= ~FL_SHOOTABLE;
|
|
ExplodeStatic(tempstat);
|
|
SpawnSolidStatic(tempstat);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DamageThing (void *thing, int damage)
|
|
{
|
|
objtype* tempactor;
|
|
statobj_t* tempstat;
|
|
|
|
|
|
tempactor = (objtype*)thing;
|
|
if (!tempactor)
|
|
return;
|
|
|
|
if ((tempactor->which == ACTOR) && (!(tempactor->flags & FL_SHOOTABLE)))
|
|
return;
|
|
|
|
if ((tempactor->which == ACTOR) || (tempactor->which == SPRITE))
|
|
{
|
|
if (tempactor->which == ACTOR)
|
|
{
|
|
if (tempactor->obclass == playerobj)
|
|
{
|
|
if ((tempactor->flags & FL_GODMODE) ||
|
|
(tempactor->flags & FL_DOGMODE) ||
|
|
godmode ||
|
|
(gamestate.battlemode == battle_Eluder)
|
|
)
|
|
return;
|
|
DamagePlayerActor(tempactor,damage);
|
|
}
|
|
|
|
else
|
|
{
|
|
if ((tempactor->obclass == collectorobj) && (gamestate.SpawnEluder))
|
|
return;
|
|
if (tempactor->hitpoints <= 0)
|
|
return;
|
|
DamageNonPlayerActor(tempactor,damage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tempstat = (statobj_t*)thing;
|
|
|
|
MISCVARS->madenoise = true;
|
|
if (!(tempstat->flags & FL_SHOOTABLE))
|
|
return;
|
|
DamageStaticObject(tempstat,damage);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ExplodeStatic(statobj_t*tempstat)
|
|
{
|
|
//SpawnSolidStatic(tempstat);
|
|
|
|
if (tempstat->flags & FL_WOODEN)
|
|
{
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_woodfrag,-1);
|
|
if ((gamestate.BattleOptions.RespawnItems) &&
|
|
(tempstat->itemnumber == stat_tntcrate)
|
|
)
|
|
{
|
|
tempstat->linked_to = (long)(LASTSTAT);
|
|
tempstat->flags |= FL_RESPAWN;
|
|
}
|
|
|
|
}
|
|
else if (tempstat->flags & FL_METALLIC)
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_metalfrag,-1);
|
|
else
|
|
SpawnStatic(tempstat->tilex,tempstat->tiley,stat_rubble,-1);
|
|
LASTSTAT->flags |= (FL_ABP|FL_NONMARK);
|
|
sprites[tempstat->tilex][tempstat->tiley] = NULL;
|
|
MakeStatActive(LASTSTAT);
|
|
SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EnableObject(long object)
|
|
{
|
|
objtype* ob;
|
|
int i,gasicon;
|
|
doorobj_t*tdoor;
|
|
|
|
ob = (objtype*)object;
|
|
|
|
#if (BNACRASHPREVENT == 1)//
|
|
if (ob == 0){return;}
|
|
#endif
|
|
|
|
ob->flags |= FL_ACTIVE;
|
|
if (ob->obclass == bladeobj)
|
|
{
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
if (ob->whatever)
|
|
{
|
|
objtype *passenger=(objtype*)(ob->whatever);
|
|
|
|
passenger->momentumx += ob->momentumx;
|
|
passenger->momentumy += ob->momentumy;
|
|
}
|
|
}
|
|
|
|
if (ob->obclass == gasgrateobj)
|
|
{
|
|
NewState(ob,&s_gas2);
|
|
SD_PlaySoundRTP(SD_GASSTARTSND,ob->x,ob->y);
|
|
ob->dirchoosetime = GASTICS;
|
|
for(i=0;i<doornum;i++)
|
|
{
|
|
|
|
tdoor = doorobjlist[i];
|
|
gasicon = MAPSPOT(tdoor->tilex,tdoor->tiley,1);
|
|
if (gasicon == GASVALUE)
|
|
LinkedCloseDoor(i);
|
|
}
|
|
|
|
MU_StoreSongPosition();
|
|
MU_StartSong(song_gason);
|
|
MISCVARS->GASON = 1;
|
|
ob->temp3 = 105;
|
|
}
|
|
else if (ob->obclass == pillarobj)
|
|
{
|
|
ob->flags |= FL_FLIPPED;
|
|
gamestate.secretcount++;
|
|
}
|
|
if (!(ob->flags & FL_ABP))
|
|
{
|
|
ob->flags |= FL_ABP;
|
|
MakeActive(ob);
|
|
}
|
|
}
|
|
|
|
void DisableObject(long object)
|
|
{objtype*ob;
|
|
|
|
ob = (objtype*)object;
|
|
ob->flags &= ~FL_ACTIVE;
|
|
}
|
|
|
|
|
|
void T_MoveColumn(objtype* ob)
|
|
{int spot,index;
|
|
|
|
|
|
if (!(ob->flags & FL_ACTIVE))
|
|
return;
|
|
/*
|
|
switch (ob->dir)
|
|
{case north:
|
|
ob->momentumy = -PILLARMOM;
|
|
break;
|
|
case south:
|
|
ob->momentumy = PILLARMOM;
|
|
break;
|
|
case east:
|
|
ob->momentumx = PILLARMOM;
|
|
break;
|
|
case west:
|
|
ob->momentumx = -PILLARMOM;
|
|
break;
|
|
}
|
|
|
|
*/
|
|
ActorMovement(ob);
|
|
index = touchindices[ob->tilex][ob->tiley];
|
|
if (index)
|
|
TRIGGER[index-1] = 1;
|
|
ob->temp1 -= PILLARMOM;
|
|
|
|
if ((ob->temp1 <= 0) || NOMOM)
|
|
{ZEROMOM;
|
|
ob->temp1 = 0x20000;
|
|
ob->flags &= ~FL_ACTIVE;
|
|
spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;
|
|
if ((spot >= 0) && (spot <= 7))
|
|
{ob->dir = spot;
|
|
if (!ob->temp2)
|
|
{
|
|
gamestate.secrettotal++;
|
|
}
|
|
else
|
|
{
|
|
ob->flags |= FL_ACTIVE;
|
|
}
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
}
|
|
else
|
|
ob->flags |= FL_DONE;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
boolean NextToDoor(objtype*ob)
|
|
{
|
|
int tilex,tiley,centerx,centery,dx,dy;
|
|
|
|
tilex = ob->tilex;
|
|
tiley = ob->tiley;
|
|
|
|
|
|
if (M_ISDOOR(tilex+1,tiley) || M_ISDOOR(tilex-1,tiley))
|
|
{centery = (tiley << TILESHIFT) + HALFGLOBAL1;
|
|
dy = abs(ob->y - centery);
|
|
if (dy < 0x2000)
|
|
return true;
|
|
}
|
|
|
|
if (M_ISDOOR(tilex,tiley+1) || M_ISDOOR(tilex,tiley-1))
|
|
{centerx = (tilex << TILESHIFT) + HALFGLOBAL1;
|
|
dx = abs(ob->x - centerx);
|
|
if (dx < 0x2000)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
=
|
|
= T_Use
|
|
=
|
|
=================
|
|
*/
|
|
|
|
|
|
void T_Use(objtype*ob)
|
|
{if (ob->ticcount)
|
|
return;
|
|
|
|
switch (ob->obclass)
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
case b_darianobj:
|
|
if (!DoPanicMapping())
|
|
touchsprite->flags |= FL_ACTIVE;
|
|
if ((!sprites[PLAYER[0]->tilex][PLAYER[0]->tiley]) && (ob->areanumber == PLAYER[0]->areanumber))
|
|
{SpawnNewObj(PLAYER[0]->tilex,PLAYER[0]->tiley,&s_dspear1,spearobj);
|
|
new->flags |= (FL_ABP);//|FL_INVULNERABLE);
|
|
new->z = 0;
|
|
MakeActive(new);
|
|
}
|
|
ZEROMOM;
|
|
ob->flags |= FL_STUCK;
|
|
SD_PlaySoundRTP(SD_DARIANUSESND,ob->x,ob->y);
|
|
//NewState(ob,&s_darianspears);
|
|
break;
|
|
#endif
|
|
default:
|
|
;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define RollStart(ob,state,angle) \
|
|
{ \
|
|
int oldspeed = ob->speed; \
|
|
\
|
|
ob->speed = ROLLMOMENTUM+0x200; \
|
|
NewState(ob,state); \
|
|
ParseMomentum(ob,angle); \
|
|
ob->speed = oldspeed; \
|
|
} \
|
|
|
|
|
|
|
|
|
|
|
|
void AvoidPlayerMissile(objtype*ob)
|
|
{
|
|
objtype *temp;
|
|
int dx,dy,dz;
|
|
int magangle,angle1,rollangle1,rollangle2,dangle1,dangle2;
|
|
|
|
if (PLAYER0MISSILE == NULL)
|
|
return;
|
|
|
|
if (GameRandomNumber("scott missile avoid",0) > 160)
|
|
return;
|
|
|
|
if (ob->momentumz)
|
|
return;
|
|
|
|
if ((ob->state->think == T_Roll) || (ob->state->think == T_Reset))
|
|
return;
|
|
|
|
temp = PLAYER0MISSILE;
|
|
|
|
dx = abs(temp->x - ob->x);
|
|
dy = abs(ob->y - temp->y);
|
|
dz = abs(ob->z - temp->z);
|
|
angle1 = AngleBetween(temp,ob);
|
|
magangle = abs(temp->angle - angle1);
|
|
|
|
if (magangle > VANG180)
|
|
magangle = ANGLES - magangle;
|
|
|
|
|
|
if ((magangle > ANGLES/48) || (dx > 0x50000) || (dy > 0x50000) ||
|
|
(dz > 32))
|
|
return;
|
|
|
|
rollangle1 = angle1 + ANGLES/4;
|
|
Fix(rollangle1);
|
|
dangle1 = abs(temp->angle - rollangle1);
|
|
if (dangle1 > VANG180)
|
|
dangle1 = ANGLES - dangle1;
|
|
|
|
rollangle2 = angle1 - ANGLES/4;
|
|
Fix(rollangle2);
|
|
dangle2 = abs(temp->angle - rollangle2);
|
|
if (dangle2 > VANG180)
|
|
dangle2 = ANGLES - dangle2;
|
|
|
|
ob->momentumx = ob->momentumy = 0;
|
|
|
|
if (dangle1 > dangle2)
|
|
{
|
|
RollStart(ob,&s_strikerollleft1,rollangle1);
|
|
}
|
|
else
|
|
{
|
|
RollStart(ob,&s_strikerollright1,rollangle2);
|
|
}
|
|
ob->flags |= FL_NOFRICTION;
|
|
|
|
ob->target = PLAYER[0];
|
|
//SelectRollDir(ob);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
=
|
|
= T_Chase
|
|
=
|
|
=================
|
|
*/
|
|
|
|
void T_Chase (objtype *ob)
|
|
{
|
|
int dx,dy,dz,dist,chance;
|
|
classtype ocl;
|
|
statetype *temp;
|
|
boolean doorok;
|
|
|
|
ocl = ob->obclass;
|
|
|
|
|
|
if ((ocl == deathmonkobj) || (ocl == blitzguardobj))
|
|
{dx = abs(PLAYER[0]->x - ob->x);
|
|
dy = abs(ob->y - PLAYER[0]->y);
|
|
dz = abs(ob->z - PLAYER[0]->z);
|
|
|
|
if ((dx < TOUCHDIST) && (dy < TOUCHDIST) && (dz < (TOUCHDIST >> 10)))
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if (ocl == deathmonkobj)
|
|
{NewState(ob,&s_dmonkshoot1);
|
|
STOPACTOR(ob);
|
|
return;
|
|
|
|
}
|
|
else
|
|
#endif
|
|
if ((!ob->temp3) && (PLAYERSTATE[0].missileweapon != -1) &&
|
|
(PLAYERSTATE[0].missileweapon < wp_godhand))
|
|
{NewState(ob,&s_blitzsteal1);
|
|
STOPACTOR(ob);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ob->flags &= ~FL_DODGE;
|
|
|
|
//if (CheckLine(ob,PLAYER[0],DIRCHECK) && (ocl != roboguardobj))
|
|
|
|
if (!ob->ticcount)
|
|
{if (CheckLine(ob,PLAYER[0],SIGHT)) // got a shot at player?
|
|
{if (ocl != roboguardobj)
|
|
{ob->targettilex = PLAYER[0]->x;
|
|
ob->targettiley = PLAYER[0]->y;
|
|
}
|
|
}
|
|
|
|
if (CheckLine(ob,PLAYER[0],SHOOT) && (!(player->flags&FL_DYING))) // got a shot at player?
|
|
{
|
|
dx = abs(ob->tilex - PLAYER[0]->tilex);
|
|
dy = abs(ob->tiley - PLAYER[0]->tiley);
|
|
dist = (dx>dy)?(dx):(dy);
|
|
if ((!dist) || (dist==1))
|
|
chance = 300;
|
|
else if (ocl >= b_darianobj)
|
|
chance = 400/dist;
|
|
else
|
|
chance = 300/dist;
|
|
|
|
if (GameRandomNumber("T_Chase",ocl) <chance)
|
|
{if ((ocl == b_heinrichobj) && (Near(ob,PLAYER[0],4)))
|
|
goto cdoor;
|
|
|
|
ob->dir = angletodir[AngleBetween(ob,PLAYER[0])];
|
|
STOPACTOR(ob);
|
|
#if (SHAREWARE == 0)
|
|
if ((ocl == overpatrolobj) && (!Near(ob,PLAYER[0],3)) &&
|
|
(!PLAYERSTATE[0].NETCAPTURED) && (!MISCVARS->NET_IN_FLIGHT))
|
|
{NewState(ob,&s_opbolo1);
|
|
MISCVARS->NET_IN_FLIGHT = true;
|
|
return;
|
|
}
|
|
#endif
|
|
if ((ocl == triadenforcerobj) && (!Near(ob,PLAYER[0],3)))
|
|
{NewState(ob,&s_enforcerthrow1);
|
|
return;
|
|
}
|
|
|
|
if ((temp=M_S(AIM)) != NULL)
|
|
{if ((ob->flags & FL_HASAUTO) && (!ob->temp3))
|
|
ob->temp3 = (GameRandomNumber("T_Chase FL_HASAUTO",ocl) % 5) + 3;
|
|
ob->target = PLAYER[0];
|
|
NewState(ob,temp);
|
|
return;
|
|
}
|
|
|
|
}
|
|
//if ((CheckSight(ob,PLAYER[0])) && (!ob->angle))// &&
|
|
//(ocl != b_heinrichobj))
|
|
//ob->flags |= FL_DODGE;
|
|
}
|
|
|
|
}
|
|
cdoor:
|
|
doorok = NextToDoor(ob);
|
|
|
|
|
|
if (ob->dirchoosetime)
|
|
ob->dirchoosetime--;
|
|
|
|
if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime) || doorok)
|
|
{//if (ob->flags & FL_DODGE)
|
|
// SelectDodgeDir(ob);
|
|
//else
|
|
SelectChaseDir(ob);
|
|
ob->dirchoosetime = M_CHOOSETIME(ob);
|
|
}
|
|
|
|
else
|
|
{if (NOMOM)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
ActorMovement(ob);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SpawnMissile(objtype* shooter,classtype nobclass,int nspeed,int nangle,statetype*nstate,int offset)
|
|
{
|
|
GetNewActor();
|
|
MakeActive(new);
|
|
new->which = ACTOR;
|
|
new->obclass = nobclass;
|
|
new->angle = nangle;
|
|
new->speed = nspeed;
|
|
if (shooter->obclass == playerobj)
|
|
offset += FindDistance(shooter->momentumx,shooter->momentumy);
|
|
|
|
SetFinePosition(new,shooter->x + FixedMul(offset,costable[nangle]),
|
|
shooter->y - FixedMul(offset,sintable[nangle]));
|
|
SetVisiblePosition(new,new->x,new->y);
|
|
//SoftError("\n missx:%d, missy:%d, speed:%d, offset:%d, angle%d, drawx:%d, drawy:%d",
|
|
// new->x,new->y,nspeed,offset,nangle,new->drawx,new->drawy);
|
|
|
|
new->z = shooter->z;
|
|
new->areanumber = shooter->areanumber;
|
|
new->soundhandle = -1;
|
|
if (nobclass != inertobj)
|
|
{
|
|
MakeLastInArea(new);
|
|
if (MissileSound == true)
|
|
new->soundhandle = SD_PlaySoundRTP(BAS[new->obclass].operate,new->x,new->y);
|
|
}
|
|
|
|
if ((shooter->obclass == playerobj) || (shooter->obclass == wallopobj) ||
|
|
(shooter->obclass == b_robobossobj))
|
|
{
|
|
Set_3D_Momenta(new,new->speed,new->angle,shooter->yzangle);
|
|
|
|
if (nobclass == p_drunkmissileobj)
|
|
new->temp1 = new->momentumz;
|
|
|
|
new->z -= FixedMulShift(offset,sintable[shooter->yzangle],26);
|
|
if ((shooter->obclass == playerobj) && (shooter->flags & FL_GODMODE))
|
|
new->z -= 10;
|
|
}
|
|
else
|
|
ParseMomentum(new,new->angle);
|
|
|
|
if (nobclass == p_drunkmissileobj)
|
|
new->temp3 = VBLCOUNTER/3;
|
|
|
|
|
|
new->flags |= (FL_NEVERMARK|FL_ABP|FL_NOFRICTION|FL_FULLLIGHT);
|
|
new->whatever = shooter; // keep track of missile possession
|
|
NewState(new,nstate);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================
|
|
=
|
|
= T_Roll
|
|
=
|
|
=================
|
|
*/
|
|
|
|
|
|
void T_Roll (objtype *ob)
|
|
{
|
|
ActorMovement(ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= T_Path
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void T_Path (objtype *ob)
|
|
{int dx,dy,dz,ocl,damage=1;
|
|
objtype*temp,*ttarg,*twhat;
|
|
|
|
|
|
ocl = ob->obclass;
|
|
|
|
if (((ocl == firejetobj) || (ocl == bladeobj)) && (!ob->ticcount))
|
|
{
|
|
if (ocl == bladeobj)
|
|
{
|
|
if (ob->state->condition & SF_DOWN )
|
|
ob->flags &= ~FL_BLOCK;
|
|
else if (ob->state->condition & SF_UP)
|
|
{
|
|
ob->flags |= FL_BLOCK;
|
|
damage = 0;
|
|
}
|
|
}
|
|
|
|
if ((ob->state->condition & SF_SOUND) && areabyplayer[ob->areanumber])
|
|
SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
|
|
|
|
if (damage)
|
|
{
|
|
for(temp=firstareaactor[ob->areanumber];temp;temp=temp->nextinarea)
|
|
{
|
|
if (temp == ob)
|
|
continue;
|
|
|
|
if (temp->obclass >= roboguardobj)
|
|
continue;
|
|
|
|
//WAS
|
|
ttarg = (objtype*)(temp->target);
|
|
twhat = (objtype*)(temp->whatever);
|
|
|
|
if ((M_ISACTOR(ttarg) && (ttarg->obclass == b_robobossobj)) ||
|
|
(M_ISACTOR(twhat) && (twhat->obclass == b_robobossobj))
|
|
)
|
|
continue;
|
|
|
|
if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING))
|
|
continue;
|
|
|
|
if (temp->obclass == playerobj)
|
|
{
|
|
if ((temp->flags & FL_GODMODE) || (temp->flags & FL_DOGMODE))
|
|
continue;
|
|
if ((temp->flags & FL_AV) && (ocl == firejetobj))
|
|
continue;
|
|
}
|
|
dx = temp->x - ob->x;
|
|
if (abs(dx) > 0xa000)
|
|
continue;
|
|
dy = temp->y - ob->y;
|
|
if (abs(dy) > 0xa000)
|
|
continue;
|
|
|
|
//if (temp->obclass == playerobj)
|
|
//Collision(temp,-temp->momentumx+ob->momentumx,-temp->momentumy + ob->momentumy);
|
|
dz = temp->z - ob->z;
|
|
if (abs(dz) > 32)
|
|
continue;
|
|
|
|
DamageThing(temp,EnvironmentDamage(ob));
|
|
if ((ocl == firejetobj) && (temp->obclass < roboguardobj))
|
|
SD_PlaySoundRTP(SD_PLAYERBURNEDSND,temp->x,temp->y);
|
|
|
|
if ((gamestate.violence == vl_excessive) && (temp->obclass < roboguardobj))
|
|
{
|
|
if (ocl == bladeobj)
|
|
{
|
|
SpawnParticles(temp,GUTS,1);
|
|
if (temp->hitpoints <= 0)
|
|
temp->flags |= FL_HBM;
|
|
}
|
|
else if (ocl == firejetobj)
|
|
{
|
|
if ((temp->hitpoints <= 0) && (temp->z == nominalheight))
|
|
{
|
|
|
|
temp->hitpoints = 0;
|
|
temp->flags |= FL_SKELETON;
|
|
if (temp->obclass == playerobj)
|
|
{
|
|
playertype *pstate;
|
|
|
|
temp->flags &= ~FL_COLORED;
|
|
M_LINKSTATE(temp,pstate);
|
|
pstate->health = 0;
|
|
pstate->weapon = -1;
|
|
}
|
|
|
|
Collision(temp,ob,-temp->momentumx,-temp->momentumy);
|
|
M_CheckPlayerKilled(temp);
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
//SD_PlaySoundRTP(SD_ACTORBURNEDSND,temp->x,temp->y);
|
|
|
|
// if ((ocl == bladeobj) || (ob->state->condition == SF_CRUSH))
|
|
Collision(temp,ob,-temp->momentumx,-temp->momentumy);
|
|
M_CheckPlayerKilled(temp);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (ob->dir == nodir)
|
|
return;
|
|
|
|
if ((ocl != firejetobj) && (ocl != bladeobj) && (ocl != diskobj))
|
|
{
|
|
if (!ob->ticcount)
|
|
{
|
|
if (SightPlayer (ob))
|
|
return;
|
|
}
|
|
else
|
|
SoftError("\n ob type %s with ticcount %d in T_Path",
|
|
debugstr[ob->obclass],ob->ticcount);
|
|
}
|
|
|
|
SelectPathDir (ob);
|
|
if (NOMOM)
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
}
|
|
|
|
|
|
|
|
int EnvironmentDamage(objtype *ob)
|
|
{
|
|
if (BATTLEMODE && (gamestate.BattleOptions.DangerDamage != bo_danger_normal))
|
|
{
|
|
return(gamestate.BattleOptions.DangerDamage);
|
|
}
|
|
else
|
|
{
|
|
int damage = 1;
|
|
|
|
switch(ob->obclass)
|
|
{
|
|
case firejetobj:
|
|
case bladeobj:
|
|
damage = 6;
|
|
break;
|
|
|
|
case boulderobj:
|
|
damage = 50;
|
|
break;
|
|
|
|
case spearobj:
|
|
damage = 7;
|
|
break;
|
|
|
|
case gasgrateobj:
|
|
damage = 20;
|
|
break;
|
|
|
|
case wallfireobj:
|
|
damage = ((GameRandomNumber("wallfire damage",0) >>3) + 10);
|
|
break;
|
|
|
|
case crushcolobj:
|
|
damage = 10;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
if (gamestate.difficulty < gd_hard)
|
|
damage >>= 1;
|
|
|
|
return damage;
|
|
}
|
|
//SoftError("unknown environment danger");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void T_AutoShootAlign(objtype*ob)
|
|
{
|
|
if (ob->dir != (dirtype)ob->temp1)
|
|
ob->dir = dirorder16[ob->dir][NEXT];
|
|
else
|
|
NewState(ob,M_S(AIM));
|
|
|
|
}
|
|
|
|
|
|
void T_AutoRealign(objtype*ob)
|
|
{
|
|
if (ob->dir != (dirtype)ob->targettilex)
|
|
ob->dir = dirorder16[ob->dir][NEXT];
|
|
else
|
|
{objtype *temp;
|
|
|
|
NewState(ob,M_S(PATH));
|
|
for(temp=firstareaactor[ob->areanumber];temp;temp=temp->nextinarea)
|
|
{if (temp == ob)
|
|
continue;
|
|
if (temp->obclass != ob->obclass)
|
|
continue;
|
|
if (!temp->state->think)
|
|
NewState(temp,UPDATE_STATES[PATH][temp->obclass-lowguardobj]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= T_AutoPath
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void T_AutoPath (objtype *ob)
|
|
{objtype *temp;
|
|
|
|
// ob->temp3 holds random number of shots before resuming path
|
|
|
|
if (CheckLine(ob,PLAYER[0],SIGHT) && (Near(ob,PLAYER[0],4) || MISCVARS->madenoise))
|
|
|
|
{int dx,dy,destdir,ocl;
|
|
statetype *align,*wait;
|
|
|
|
ocl = ob->obclass;
|
|
dx = player->x - ob->x;
|
|
dy = ob->y - player->y;
|
|
destdir = (angletodir[atan2_appx(dx,dy)] << 1);
|
|
ob->temp1 = destdir;
|
|
ob->targettilex = ob->dir; //save old dir
|
|
#if (SHAREWARE == 0)
|
|
if (ocl == wallopobj)
|
|
{//if (ob->temp3)
|
|
// Error("may be writing over temp3");
|
|
ob->temp3 = (GameRandomNumber("T_WallPath",0)%4) + 1;
|
|
align = &s_wallalign;
|
|
wait = &s_wallwait;
|
|
}
|
|
else
|
|
#endif
|
|
{align = &s_roboalign;
|
|
wait = &s_robowait;
|
|
}
|
|
|
|
NewState(ob,align);
|
|
for(temp=firstareaactor[ob->areanumber];temp;temp=temp->nextinarea)
|
|
{if (temp == ob)
|
|
continue;
|
|
|
|
if (temp->obclass != ob->obclass)
|
|
continue;
|
|
|
|
if (temp->flags & FL_DYING)
|
|
continue;
|
|
|
|
if (CheckLine(temp,PLAYER[0],SIGHT) && (Near(temp,PLAYER[0],4) || MISCVARS->madenoise))
|
|
{dx = PLAYER[0]->x - temp->x;
|
|
dy = temp->y - PLAYER[0]->y;
|
|
destdir = (angletodir[atan2_appx(dx,dy)] << 1);
|
|
|
|
temp->temp1 = destdir;
|
|
temp->targettilex = temp->dir;
|
|
NewState(temp,align);
|
|
temp->temp3 = ob->temp3;
|
|
}
|
|
else
|
|
NewState(temp,wait);
|
|
}
|
|
return;
|
|
}
|
|
|
|
SD_PlaySoundRTP(SD_ROBOTMOVESND,ob->x,ob->y);
|
|
|
|
SelectPathDir(ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= A_Shoot
|
|
=
|
|
= Try to damage the player, based on skill level and player's speed
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void A_Shoot (objtype *ob)
|
|
{
|
|
int dx,dy,dz,dist;
|
|
int accuracy,damage,sound;
|
|
objtype * target;
|
|
int num;
|
|
int savedangle;
|
|
|
|
ActorMovement(ob);
|
|
|
|
ob->flags |= FL_FULLLIGHT;
|
|
//if (!(ob->flags & FL_SHOOTABLE))
|
|
//Error("\na dead instance of %s is shooting at you",debugstr[ob->obclass]);
|
|
|
|
if (!ob->ticcount)
|
|
{if (ob->obclass == strikeguardobj)
|
|
ob->flags &= ~FL_NOFRICTION;
|
|
|
|
target = (objtype*)(ob->target);
|
|
if (!target)
|
|
Error("an instance of %s called shoot without a target\n",debugstr[ob->obclass]);
|
|
|
|
|
|
ob->flags &= ~FL_FULLLIGHT;
|
|
|
|
|
|
dx = (target->x - ob->x);
|
|
dy = (ob->y - target->y);
|
|
dz = target->z-ob->z;
|
|
|
|
|
|
if ((ob->obclass == blitzguardobj) && (ob->temp3) &&
|
|
(ob->temp3 != stat_gasmask) && (ob->temp3 != stat_asbesto) &&
|
|
(ob->temp3 != stat_bulletproof) &&
|
|
(gamestate.difficulty >= gd_medium) &&
|
|
((abs(dx) > 0xc000) || (abs(dy) > 0xc000))
|
|
)
|
|
{
|
|
int i;
|
|
missile_stats* newmissiledata;
|
|
|
|
newmissiledata = &PlayerMissileData[GetWeaponForItem(ob->temp3)];
|
|
|
|
// ready to annihilate this poor bastard
|
|
|
|
SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed,
|
|
AngleBetween(ob,player), newmissiledata->state,
|
|
newmissiledata->offset);
|
|
|
|
if (newmissiledata->obclass == p_drunkmissileobj)
|
|
{
|
|
for(i=0;i<4;i++)
|
|
{
|
|
SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed,
|
|
AngleBetween(ob,player), newmissiledata->state,
|
|
newmissiledata->offset);
|
|
}
|
|
}
|
|
ob->target = NULL;
|
|
ob->temp2 --;
|
|
if (ob->temp2 == 0)
|
|
ob->temp3 = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
if ((!areabyplayer[ob->areanumber]) && (target->obclass == playerobj))
|
|
return;
|
|
|
|
//if (!CheckLine(ob,target,SHOOT)) // player is behind a wall
|
|
//return;
|
|
|
|
|
|
savedangle=ob->angle;
|
|
ob->angle = atan2_appx (dx,dy);
|
|
dist = FindDistance(dx,dy);
|
|
ob->yzangle = FINEANGLES-atan2_appx(dist, dz<<10);
|
|
|
|
if ((ob->yzangle>MAXYZANGLE) && (ob->yzangle<FINEANGLES-MAXYZANGLE))
|
|
ob->yzangle=MAXYZANGLE;
|
|
|
|
dist>>=16;
|
|
|
|
accuracy=(WHICHACTOR<<4)+((gamestate.difficulty) << 6);
|
|
|
|
num = GameRandomNumber("A_Shoot3",ob->obclass);
|
|
|
|
if (num<128) num=128; // Don't let accuracy fall below 50% original
|
|
|
|
accuracy=FixedMulShift(num,accuracy,8); // scale accuracy based off randomness
|
|
|
|
// check for maximum accuracy;
|
|
|
|
if (accuracy>255) accuracy=255;
|
|
|
|
if (ob->obclass==highguardobj)
|
|
damage=DMG_MP40;
|
|
else if (ob->obclass == triadenforcerobj)
|
|
damage=DMG_MP40;
|
|
else
|
|
damage=DMG_ENEMYBULLETWEAPON;
|
|
|
|
|
|
RayShoot (ob, damage, 255-accuracy);
|
|
|
|
ob->angle=savedangle;
|
|
sound = BAS[ob->obclass].fire;
|
|
SD_PlaySoundRTP(sound,ob->x,ob->y);
|
|
MISCVARS->madenoise = true;
|
|
if ((!(ob->flags& FL_HASAUTO)) || (!ob->temp3))
|
|
ob->target = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void A_Repeat(objtype*ob)
|
|
{
|
|
ActorMovement(ob);
|
|
|
|
if (!ob->ticcount)
|
|
{ob->temp3 --;
|
|
if (ob->temp3 <= 0)
|
|
NewState(ob,M_S(CHASE));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void A_MissileWeapon(objtype *ob)
|
|
{
|
|
int sound,nspeed,noffset,zoffset;
|
|
|
|
#if (SHAREWARE == 0)
|
|
int oldyzangle;
|
|
#endif
|
|
classtype nobclass;
|
|
statetype*nstate;
|
|
|
|
|
|
if ((ob->obclass == wallopobj) || (ob->obclass == roboguardobj));
|
|
//SelectPathDir(ob);
|
|
else
|
|
ActorMovement(ob);
|
|
|
|
if (!ob->ticcount)
|
|
{
|
|
#if (SHAREWARE == 0)
|
|
if ((ob->obclass == wallopobj) && (!ob->temp3))
|
|
{
|
|
NewState(ob,&s_wallrestore);
|
|
return;
|
|
}
|
|
#endif
|
|
if ((ob->obclass == b_darianobj) && (!CheckLine(ob,PLAYER[0],SHOOT)))
|
|
{NewState(ob,M_S(CHASE));
|
|
return;
|
|
}
|
|
// Move sounds, flags into switch cases
|
|
|
|
sound = BAS[ob->obclass].fire;
|
|
nspeed = 0x4000;
|
|
noffset = 0x8000;
|
|
zoffset = 0;
|
|
switch (ob->obclass)
|
|
{
|
|
|
|
case triadenforcerobj:
|
|
nobclass = grenadeobj;
|
|
nstate= &s_grenade1;
|
|
sound++;
|
|
break;
|
|
|
|
|
|
case roboguardobj:
|
|
nobclass = shurikenobj;
|
|
nspeed = 0x2000;
|
|
noffset = 0x10000;
|
|
nstate = &s_robogrdshuriken1;
|
|
break;
|
|
|
|
/*
|
|
case b_darkmonkobj:
|
|
nobclass = dmfballobj;
|
|
nstate = &s_dmfball1;
|
|
break;
|
|
*/
|
|
/*
|
|
case b_robobossobj:
|
|
nobclass = bigshurikenobj;
|
|
nstate = &s_oshuriken1;
|
|
break;
|
|
*/
|
|
#if (SHAREWARE == 0)
|
|
case b_darianobj:
|
|
nobclass = missileobj;
|
|
//nspeed = 0x100;
|
|
//noffset = 0x18000;
|
|
nstate = &s_missile1;
|
|
zoffset = -20;
|
|
break;
|
|
|
|
|
|
case dfiremonkobj:
|
|
nobclass = fireballobj;
|
|
nstate = &s_monkfire1;
|
|
break;
|
|
|
|
|
|
case overpatrolobj:
|
|
nobclass = netobj;
|
|
nstate = &s_bolocast1;
|
|
sound ++;
|
|
break;
|
|
|
|
|
|
case wallopobj:
|
|
{int dx,dy,dz,xydist;
|
|
|
|
ob->temp2 ^= 1; // increment numfired
|
|
ob->temp3 --; // decrement random fire no.
|
|
|
|
|
|
dx = PLAYER[0]->x-ob->x;
|
|
dy = ob->y-PLAYER[0]->y;
|
|
if (GameRandomNumber("bcraft shoot up/down",0) < 128)
|
|
dz = 10;
|
|
else
|
|
dz = -10;
|
|
xydist = FindDistance(dx,dy);
|
|
oldyzangle = ob->yzangle;
|
|
ob->yzangle = atan2_appx(xydist,dz<<10);
|
|
if (ob->temp2)
|
|
{nobclass = missileobj;
|
|
nstate = &s_missile1;
|
|
}
|
|
else
|
|
{nobclass = bigshurikenobj;
|
|
nstate = &s_bstar1;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
;
|
|
}
|
|
|
|
SpawnMissile(ob,nobclass,nspeed,AngleBetween(ob,PLAYER[0]),nstate,noffset);
|
|
new->z += zoffset;
|
|
SD_PlaySoundRTP(sound,ob->x,ob->y);
|
|
MISCVARS->madenoise = true;
|
|
if (ob->obclass == triadenforcerobj)
|
|
{//new->flags |= (FL_SHOOTABLE);
|
|
new->temp1 = 0x50000;
|
|
}
|
|
#if (SHAREWARE == 0)
|
|
else if (ob->obclass == wallopobj)
|
|
ob->yzangle = oldyzangle;
|
|
#endif
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void A_Wallfire(objtype *ob)
|
|
{
|
|
if (!(ob->flags & FL_ACTIVE))
|
|
return;
|
|
|
|
if (!ob->ticcount)
|
|
{SpawnMissile(ob,wallfireobj,0x4000,ob->angle,&s_crossfire1,0xa000);
|
|
if (areabyplayer[ob->areanumber])
|
|
SD_PlaySoundRTP(SD_FIRECHUTESND,ob->x,ob->y);
|
|
new->dir = angletodir[new->angle];
|
|
new->z = nominalheight;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void T_Reset(objtype *ob)
|
|
{
|
|
ActorMovement(ob);
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
ob->momentumx = ob->momentumy = ob->dirchoosetime = 0;
|
|
ob->flags &= ~FL_NOFRICTION;
|
|
|
|
|
|
|
|
}
|
|
|
|
void SelectRollDir(objtype *ob)
|
|
{int angle,tryx,tryy;
|
|
|
|
|
|
if (ob->state == &s_strikerollright1)
|
|
angle = AngleBetween(ob,PLAYER[0]) + ANGLES/4;
|
|
else
|
|
angle = AngleBetween(ob,PLAYER[0]) - ANGLES/4;
|
|
|
|
Fix(angle);
|
|
tryx = ob->x + FixedMul(0x20000l,costable[angle]);
|
|
tryy = ob->y - FixedMul(0x20000l,sintable[angle]);
|
|
ZEROMOM;
|
|
if (QuickSpaceCheck(ob,tryx,tryy))
|
|
{int oldspeed;
|
|
|
|
oldspeed = ob->speed;
|
|
ob->speed = ROLLMOMENTUM;
|
|
ParseMomentum(ob,angle);
|
|
ob->speed = oldspeed;
|
|
ob->dirchoosetime = 5;
|
|
ob->flags |= FL_NOFRICTION;
|
|
}
|
|
else
|
|
ob->dirchoosetime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
void SelectDodgeDir (objtype *ob)
|
|
{
|
|
int dx,dy,i,tx,ty;
|
|
unsigned absdx,absdy;
|
|
dirtype dirtry[5];
|
|
dirtype turnaround,tdir,olddir;
|
|
|
|
|
|
olddir = ob->dir;
|
|
if (ob->flags & FL_FIRSTATTACK)
|
|
{
|
|
//
|
|
// turning around is only ok the very first time after noticing the
|
|
// player
|
|
//
|
|
turnaround = nodir;
|
|
ob->flags &= ~FL_FIRSTATTACK;
|
|
}
|
|
else
|
|
turnaround=opposite[ob->dir];
|
|
|
|
|
|
if (ob->targettilex || ob->targettiley)
|
|
{tx = ob->targettilex;
|
|
ty = ob->targettiley;
|
|
dx= tx - ob->x;
|
|
dy= ty - ob->y;
|
|
if ( ((dx < MINACTORDIST) && (dx > -MINACTORDIST)) &&
|
|
((dy < MINACTORDIST) && (dy > -MINACTORDIST)))
|
|
{dx= PLAYER[0]->x-ob->x;
|
|
dy= PLAYER[0]->y-ob->y;
|
|
}
|
|
}
|
|
else
|
|
{dx= PLAYER[0]->x-ob->x;
|
|
dy= PLAYER[0]->y-ob->y;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// arange 5 direction choices in order of preference
|
|
// the four cardinal directions plus the diagonal straight towards
|
|
// the player
|
|
//
|
|
if (dx>ACTORSIZE)
|
|
{
|
|
dirtry[1]= east;
|
|
dirtry[3]= west;
|
|
}
|
|
else if (dx < -ACTORSIZE)
|
|
{
|
|
dirtry[1]= west;
|
|
dirtry[3]= east;
|
|
}
|
|
|
|
if (dy>ACTORSIZE)
|
|
{
|
|
dirtry[2]= south; // south
|
|
dirtry[4]= north; // north
|
|
}
|
|
else if (dy <-ACTORSIZE)
|
|
{
|
|
dirtry[2]= north; // north
|
|
dirtry[4]= south; // south
|
|
}
|
|
|
|
//
|
|
// randomize a bit for dodging
|
|
//
|
|
absdx = abs(dx);
|
|
absdy = abs(dy);
|
|
|
|
if (absdx > absdy)
|
|
{
|
|
tdir = dirtry[1];
|
|
dirtry[1] = dirtry[2];
|
|
dirtry[2] = tdir;
|
|
tdir = dirtry[3];
|
|
dirtry[3] = dirtry[4];
|
|
dirtry[4] = tdir;
|
|
}
|
|
|
|
if (GameRandomNumber("SelectDogeDir",ob->obclass) < 128)
|
|
{
|
|
tdir = dirtry[1];
|
|
dirtry[1] = dirtry[2];
|
|
dirtry[2] = tdir;
|
|
tdir = dirtry[3];
|
|
dirtry[3] = dirtry[4];
|
|
dirtry[4] = tdir;
|
|
}
|
|
|
|
dirtry[0] = diagonal [dirtry[1]] [dirtry[2]];
|
|
|
|
ZEROMOM;
|
|
|
|
for (i=0;i<5;i++)
|
|
{if ((dirtry[i] == nodir) || (dirdiff[dirtry[i]][olddir] > 1))
|
|
continue;
|
|
|
|
M_CHECKDIR(ob,dirtry[i]);
|
|
}
|
|
|
|
//
|
|
// turn around only as a last resort
|
|
//
|
|
// for(tdir = east;tdir<=southeast;tdir++)
|
|
//if (tdir != turnaround)
|
|
// M_CHECKDIR(ob,tdir);
|
|
|
|
if (turnaround != nodir)
|
|
M_CHECKDIR(ob,turnaround);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define TryAbruptProximalDirections(trydir) \
|
|
{ \
|
|
next = dirorder[trydir][NEXT]; \
|
|
prev = dirorder[trydir][PREV]; \
|
|
if (GameRandomNumber("actor choose dir",0) < 128) \
|
|
{ \
|
|
dirtype temp = next; \
|
|
\
|
|
next = prev; \
|
|
prev = temp; \
|
|
} \
|
|
\
|
|
if (!dirtried[next]) \
|
|
{ \
|
|
M_CHECKDIR(ob,next); \
|
|
dirtried[next]=1; \
|
|
} \
|
|
\
|
|
if (!dirtried[prev]) \
|
|
{ \
|
|
M_CHECKDIR(ob,prev); \
|
|
dirtried[prev]=1; \
|
|
} \
|
|
\
|
|
}
|
|
|
|
|
|
#define TrySmoothProximalDirections(trydir) \
|
|
{ \
|
|
\
|
|
if (((trydir == olddir) || (dirdiff[trydir][olddir] < 2)) && \
|
|
(!dirtried[trydir])) \
|
|
{ \
|
|
M_CHECKDIR(ob,trydir); \
|
|
dirtried[trydir] = 1; \
|
|
} \
|
|
next = dirorder[olddir][NEXT]; \
|
|
prev = dirorder[olddir][PREV]; \
|
|
\
|
|
if (dirdiff[trydir][next] <= dirdiff[trydir][prev]) \
|
|
{ \
|
|
start = next; \
|
|
whichway = NEXT; \
|
|
} \
|
|
else \
|
|
{ \
|
|
start = prev; \
|
|
whichway = PREV; \
|
|
} \
|
|
\
|
|
for (tdir= start; tdir != dirorder[trydir][whichway]; \
|
|
tdir = dirorder[tdir][whichway] \
|
|
) \
|
|
{ \
|
|
if (dirtried[tdir]) \
|
|
continue; \
|
|
M_CHECKDIR(ob,tdir); \
|
|
dirtried[tdir]=1; \
|
|
} \
|
|
\
|
|
}
|
|
|
|
|
|
|
|
#define ChasePlayer(ob) \
|
|
{ \
|
|
dx= player->x-ob->x; \
|
|
dy= ob->y-player->y; \
|
|
if ((abs(dx) < 0xb000) && (abs(dy) < 0xb000)) \
|
|
return; \
|
|
dummy.x = player->x; \
|
|
dummy.y = player->y; \
|
|
}
|
|
|
|
|
|
void SelectChaseDir (objtype *ob)
|
|
{
|
|
int dx,dy,whichway,tx,ty,actrad,visible,
|
|
realdiff;
|
|
dirtype dtry1,dtry2,tdir,olddir,next,prev,start,straight;
|
|
tpoint dummy;
|
|
byte dirtried[9] = {0};
|
|
|
|
olddir=ob->dir;
|
|
visible = CheckLine(ob,PLAYER[0],SIGHT);
|
|
|
|
/*
|
|
if (ob->flags & FL_FIRSTATTACK)
|
|
{
|
|
//
|
|
// turning around is only ok the very first time after noticing the
|
|
// player
|
|
//
|
|
turnaround = opposite[ob->dir];
|
|
ob->flags &= ~FL_FIRSTATTACK;
|
|
}
|
|
else
|
|
turnaround=nodir;
|
|
*/
|
|
dummy.which = ACTOR;
|
|
dummy.z = ob->z;
|
|
if (ob->targettilex || ob->targettiley)
|
|
{
|
|
tx = ob->targettilex;
|
|
ty = ob->targettiley;
|
|
dx= tx - ob->x;
|
|
dy= ob->y - ty;
|
|
dummy.x = tx;
|
|
dummy.y = ty;
|
|
if ((abs(dx) < 0x2000) && (abs(dy) < 0x2000))
|
|
ChasePlayer(ob);
|
|
}
|
|
else
|
|
ChasePlayer(ob);
|
|
|
|
//if ((abs(dx) < 0x10000) && (abs(dy) < 0x10000))
|
|
//return;
|
|
straight = angletodir[atan2_appx(dx,dy)];
|
|
realdiff = dirdiff[straight][ob->dir];
|
|
ZEROMOM;
|
|
|
|
//insertion 20
|
|
|
|
actrad = ACTORSIZE;
|
|
dtry1=nodir;
|
|
dtry2=nodir;
|
|
|
|
if (dx> actrad)
|
|
dtry1= east;
|
|
else if (dx< -actrad)
|
|
dtry1= west;
|
|
if (dy> actrad)
|
|
dtry2=north;
|
|
else if (dy < -actrad)
|
|
dtry2= south;
|
|
|
|
|
|
if (abs(dy)>abs(dx))
|
|
{
|
|
tdir=dtry1;
|
|
dtry1=dtry2;
|
|
dtry2=tdir;
|
|
}
|
|
|
|
if (GameRandomNumber("chase minor",0) < 80)
|
|
{
|
|
tdir=dtry1;
|
|
dtry1=dtry2;
|
|
dtry2=tdir;
|
|
}
|
|
|
|
ZEROMOM;
|
|
|
|
if ((!visible) || (realdiff > 2)) // don't worry about abrupt or unrealistic turns if player
|
|
// can't see guards
|
|
{
|
|
M_CHECKDIR(ob,straight);
|
|
dirtried[straight]=1;
|
|
next = dirorder[straight][NEXT];
|
|
prev = dirorder[straight][PREV];
|
|
|
|
if ((dtry1 != nodir) && (dtry1 != straight))
|
|
{
|
|
M_CHECKDIR(ob,dtry1);
|
|
dirtried[dtry1]=1;
|
|
}
|
|
|
|
|
|
if ((dtry2 != nodir) && (!dirtried[dtry2]))
|
|
{
|
|
M_CHECKDIR(ob,dtry2);
|
|
dirtried[dtry2]=1;
|
|
}
|
|
|
|
if (dtry1 != nodir)
|
|
TryAbruptProximalDirections(dtry1);
|
|
|
|
if (dtry2 != nodir)
|
|
TryAbruptProximalDirections(dtry2);
|
|
}
|
|
|
|
else
|
|
{
|
|
if (realdiff < 2)
|
|
{
|
|
M_CHECKDIR(ob,straight);
|
|
dirtried[straight]=1;
|
|
}
|
|
|
|
if (dtry1 != nodir)
|
|
TrySmoothProximalDirections(dtry1);
|
|
|
|
if (dtry2 != nodir)
|
|
TrySmoothProximalDirections(dtry2);
|
|
|
|
}
|
|
|
|
if ((dtry1!=nodir) || (dtry2!=nodir))
|
|
{
|
|
if (GameRandomNumber("actor choose dir",0) < 128)
|
|
whichway = NEXT;
|
|
else
|
|
whichway = PREV;
|
|
|
|
for(tdir = dirorder[olddir][whichway];tdir != olddir;tdir = dirorder[tdir][whichway])
|
|
{
|
|
if (dirtried[tdir])
|
|
continue;
|
|
M_CHECKDIR(ob,tdir);
|
|
}
|
|
}
|
|
|
|
ob->dir = olddir;
|
|
}
|
|
|
|
|
|
|
|
int Near(objtype *ob,void* what,int distance)
|
|
|
|
{
|
|
objtype *aw;
|
|
|
|
aw = (objtype*) what;
|
|
|
|
if (FindDistance((aw->x - ob->x),(aw->y - ob->y)) <= (distance<<16))
|
|
return 1;
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= SelectPathDir
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void SelectPathDir (objtype *ob)
|
|
{
|
|
int spot,centerx,centery,dx,dy,set,done,radius,ocl;
|
|
|
|
ocl = ob->obclass;
|
|
|
|
|
|
if ((ocl == bladeobj) && (!(ob->flags & FL_ACTIVE)))
|
|
return;
|
|
|
|
spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;
|
|
set = ((ocl == wallopobj) || (ocl == roboguardobj));
|
|
done = (((!set) && (ob->dir == (dirtype)spot)) ||
|
|
(set && (ob->dir == (dirtype)(spot<<1))));
|
|
|
|
if ((spot >= 0) && (spot<= 7) && (!done))
|
|
{
|
|
centerx= (ob->tilex << 16) + HALFGLOBAL1;
|
|
centery= (ob->tiley << 16) + HALFGLOBAL1;
|
|
dx = abs(centerx - ob->x);
|
|
dy = abs(centery - ob->y);
|
|
//radius = (ob->speed > 0x4800)?(0xb000):(0x4000);
|
|
radius = 0x4000;
|
|
|
|
if ((dx < radius) && (dy < radius))
|
|
// new direction
|
|
{
|
|
ZEROMOM;
|
|
if ((ocl == wallopobj) || (ocl == roboguardobj))
|
|
{
|
|
ob->dir = spot<<1;
|
|
ParseMomentum(ob,dirangle16[ob->dir]);
|
|
}
|
|
else
|
|
{
|
|
ob->dir = spot;
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
}
|
|
dx = centerx - ob->x;
|
|
dy = centery - ob->y;
|
|
SetFinePosition(ob,centerx,centery);
|
|
SetVisiblePosition(ob,centerx,centery);
|
|
/*
|
|
if (((ocl == bladeobj) || (ocl == diskobj)) && ob->whatever)
|
|
{objtype*passenger = (objtype*)(ob->whatever);
|
|
|
|
passenger->x += dx;
|
|
passenger->y += dy;
|
|
passenger->drawx = passenger->x;
|
|
passenger->drawy = passenger->y;
|
|
passenger->tilex = passenger->x >> 16;
|
|
passenger->tiley = passenger->y >> 16;
|
|
}*/
|
|
// if (ob==SNAKEHEAD)
|
|
// Debug("\n path changed at %d, %d",
|
|
// ob->tilex,ob->tiley);
|
|
}
|
|
}
|
|
if (NOMOM)
|
|
{
|
|
if ((ocl == wallopobj) || (ocl == roboguardobj))
|
|
ParseMomentum(ob,dirangle16[ob->dir]);
|
|
else
|
|
ParseMomentum(ob,dirangle8[ob->dir]);
|
|
}
|
|
|
|
//if ((ob->obclass == firejetobj) || (ob->obclass == bladeobj))
|
|
//MoveActor(ob);
|
|
//else
|
|
ActorMovement(ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
================
|
|
=
|
|
= CheckSight
|
|
=
|
|
= Checks a straight line between player and current object
|
|
=
|
|
= If the sight is ok, check alertness and angle to see if they notice
|
|
=
|
|
= returns true if the player has been spoted
|
|
=
|
|
================
|
|
*/
|
|
|
|
|
|
boolean CheckSight (objtype *ob,void *atwhat)
|
|
{
|
|
long deltax,deltay;
|
|
objtype * what;
|
|
//
|
|
// don't bother tracing a line if the area isn't connected to the player's
|
|
//
|
|
if (!areabyplayer[ob->areanumber])
|
|
return false;
|
|
|
|
//
|
|
// if the player is real close, sight is automatic
|
|
//
|
|
what = (objtype*)atwhat;
|
|
deltax = what->x - ob->x;
|
|
deltay = what->y - ob->y;
|
|
|
|
if ((deltax > -MINSIGHT) && (deltax < MINSIGHT) &&
|
|
(deltay > -MINSIGHT) && (deltay < MINSIGHT))
|
|
return true;
|
|
|
|
//
|
|
// see if they are looking in the right direction
|
|
//
|
|
switch (ob->dir)
|
|
{
|
|
case north:
|
|
if (deltay > 0)
|
|
return false;
|
|
break;
|
|
|
|
case east:
|
|
if (deltax < 0)
|
|
return false;
|
|
break;
|
|
|
|
case south:
|
|
if (deltay < 0)
|
|
return false;
|
|
break;
|
|
|
|
case west:
|
|
if (deltax > 0)
|
|
return false;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
//
|
|
// trace a line to check for blocking tiles (corners)
|
|
//
|
|
return CheckLine (ob,atwhat,SIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ActivateEnemy(objtype*ob)
|
|
{statetype *temp;
|
|
|
|
|
|
ob->flags |= (FL_ATTACKMODE|FL_FIRSTATTACK);
|
|
if (ob->obclass == roboguardobj)
|
|
return;
|
|
|
|
if (ob->temp3 == SNEAKY)
|
|
{ NewState(ob,&s_sneakyrise1);
|
|
ob->temp3=0;
|
|
}
|
|
else if ((temp = M_S(CHASE)) != NULL)
|
|
NewState(ob,temp);
|
|
/*
|
|
ob->speed = ENEMYRUNSPEED;
|
|
*/
|
|
if (ob->obclass == b_heinrichobj)
|
|
ob->speed = 7*ob->speed/2;
|
|
else if (ob->obclass == b_darianobj)
|
|
ob->speed = 3*SPDPATROL;
|
|
if (ob->door_to_open != -1)
|
|
ob->door_to_open = -1; // ignore the door opening command
|
|
ob->dirchoosetime = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= FirstSighting
|
|
=
|
|
= Puts an actor into attack mode and possibly reverses the direction
|
|
= if the player is behind it
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void FirstSighting (objtype *ob)
|
|
{
|
|
statetype *temp;
|
|
int sound;
|
|
|
|
if (ob->temp3 == SNEAKY)
|
|
{
|
|
NewState(ob,&s_sneakyrise1);
|
|
ob->temp3=0;
|
|
if (ob->shapeoffset==0)
|
|
SD_PlaySoundRTP(SD_SNEAKYSPRINGMSND,ob->x,ob->y);
|
|
else
|
|
SD_PlaySoundRTP(SD_SNEAKYSPRINGFSND,ob->x,ob->y);
|
|
}
|
|
else if ((temp = M_S(CHASE)) != NULL)
|
|
{
|
|
int rand;
|
|
|
|
NewState(ob,temp);
|
|
sound = BAS[ob->obclass].see;
|
|
rand = GameRandomNumber("FirstSighting low",0);
|
|
if ((ob->obclass > lowguardobj) && (ob->obclass <= blitzguardobj) && (rand < 128)) //hack for alternate
|
|
sound++;
|
|
//if ((ob->obclass == lowguardobj) && (rand < 80))
|
|
//sound ++;
|
|
else if (ob->obclass == lowguardobj)
|
|
{if (rand < 128)
|
|
{if ((PLAYERSTATE[0].player == 1) || (PLAYERSTATE[0].player == 3))
|
|
sound++;
|
|
}
|
|
else
|
|
sound += 2;
|
|
|
|
if (ob->shapeoffset)
|
|
sound += 4;
|
|
|
|
}
|
|
SD_PlaySoundRTP(sound,ob->x,ob->y);
|
|
if ((ob->obclass>=b_darianobj) && (ob->obclass<=b_darksnakeobj))
|
|
{
|
|
MU_StartSong(song_bosssee);
|
|
}
|
|
}
|
|
|
|
/*
|
|
ob->speed = ENEMYRUNSPEED;*/
|
|
if (ob->obclass == b_heinrichobj)
|
|
ob->speed = 7*ob->speed/2;
|
|
else if (ob->obclass == b_darianobj)
|
|
ob->speed = 3*SPDPATROL;
|
|
if (ob->door_to_open != -1)
|
|
ob->door_to_open = -1; // ignore the door opening command
|
|
ob->dirchoosetime = 0;
|
|
ob->flags |= (FL_ATTACKMODE|FL_FIRSTATTACK);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= SightPlayer
|
|
=
|
|
= Called by actors that ARE NOT chasing the player. If the player
|
|
= is detected (by sight, noise, or proximity), the actor is put into
|
|
= it's combat frame and true is returned.
|
|
=
|
|
= Incorporates a random reaction delay
|
|
=
|
|
===============
|
|
*/
|
|
|
|
boolean SightPlayer (objtype *ob)
|
|
{
|
|
//if (ob->flags & FL_ATTACKMODE)
|
|
//Error ("An instance of %s in ATTACKMODE called SightPlayer!",debugstr[ob->obclass]);
|
|
|
|
if (!areabyplayer[ob->areanumber])
|
|
return false;
|
|
|
|
if (ob->obclass == b_robobossobj)
|
|
{
|
|
if (!(CheckSight(ob,player) || Near(ob,player,6)))
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
else if (ob->flags & FL_AMBUSH)
|
|
{
|
|
if (!CheckSight (ob,PLAYER[0]))
|
|
{
|
|
//SoftError("\n failed from ambush in SightPlayer");
|
|
return false;
|
|
}
|
|
ob->flags &= ~FL_AMBUSH;
|
|
}
|
|
else
|
|
{
|
|
if (ob->temp3 == SNEAKY)
|
|
{
|
|
if (!Near(ob,PLAYER[0],2))
|
|
return false;
|
|
}
|
|
else if (!((MISCVARS->madenoise) ||
|
|
(CheckSight (ob,player)) ||
|
|
(Near(ob,player,4))
|
|
)
|
|
)
|
|
{
|
|
//SoftError("\n failed from SightPlayer");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FirstSighting (ob);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= CheckLine
|
|
=
|
|
= Returns true if a straight line between two obs is unobstructed
|
|
=
|
|
=====================
|
|
*/
|
|
|
|
|
|
|
|
boolean CheckLine (void *from, void *to, int condition)
|
|
{
|
|
objtype *tempactor,*ob,*orig;
|
|
statobj_t *checksprite;
|
|
int destx,desty,destz;
|
|
int desttilex,desttiley;
|
|
int snx,sny;
|
|
int incr[2];
|
|
int thedir[2];
|
|
int cnt;
|
|
int grid[2];
|
|
int index;
|
|
int vx,vy;
|
|
int yzangle;
|
|
int value;
|
|
int dx,dy,dz;
|
|
int xydist;
|
|
int otx,oty,count=0;
|
|
|
|
|
|
|
|
ob = (objtype*)to;
|
|
orig = (objtype*)from;
|
|
if (ob->which == SPRITE)
|
|
{
|
|
destx = ((statobj_t*)to)->x;
|
|
desty = ((statobj_t*)to)->y;
|
|
destz = ((statobj_t*)to)->z;
|
|
}
|
|
else
|
|
{
|
|
destx = ob->x;
|
|
desty = ob->y;
|
|
destz = ob->z;
|
|
}
|
|
|
|
desttilex=destx>>16;
|
|
desttiley=desty>>16;
|
|
|
|
if ((desttilex == orig->tilex) && (desttiley == orig->tiley))
|
|
return true;
|
|
|
|
|
|
dx=destx-orig->x;
|
|
dy=orig->y-desty;
|
|
dz=orig->z-destz;
|
|
xydist = FindDistance(dx,dy);
|
|
yzangle = atan2_appx(xydist,dz<<10);
|
|
|
|
if ((yzangle>MAXYZANGLE) && (yzangle<FINEANGLES-MAXYZANGLE))
|
|
{
|
|
#if (0)
|
|
Debug("\nfailed from yzangle");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
//angle = atan2_appx(dx,dy);
|
|
otx = orig->x >> TILESHIFT;
|
|
oty = orig->y >> TILESHIFT;
|
|
|
|
if (xydist==0)
|
|
{
|
|
/*
|
|
SoftError("\nCheckLine:xydist=0");
|
|
if (orig->which == ACTOR)
|
|
SoftError("shooter: %s",debugstr[orig->obclass]);
|
|
if (ob->which == ACTOR)
|
|
SoftError("target: %s",debugstr[ob->obclass]);*/
|
|
vy=-dy;
|
|
vx=dx;
|
|
}
|
|
else
|
|
{
|
|
vy = -FixedDiv2(dy,xydist);
|
|
vx = FixedDiv2(dx,xydist);
|
|
}
|
|
snx=orig->x&0xffff;
|
|
sny=orig->y&0xffff;
|
|
|
|
grid[0]=otx;
|
|
grid[1]=oty;
|
|
|
|
if (vx>0)
|
|
{
|
|
thedir[0]=1;
|
|
snx^=0xffff;
|
|
incr[1]=-vx;
|
|
}
|
|
else
|
|
{
|
|
thedir[0]=-1;
|
|
incr[1]=vx;
|
|
}
|
|
if (vy>0)
|
|
{
|
|
thedir[1]=1;
|
|
sny^=0xffff;
|
|
incr[0]=vy;
|
|
}
|
|
else
|
|
{
|
|
thedir[1]=-1;
|
|
incr[0]=-vy;
|
|
}
|
|
cnt=FixedMul(snx,incr[0])+FixedMul(sny,incr[1]);
|
|
|
|
|
|
do
|
|
{count ++;
|
|
/*
|
|
if (count > 1000)
|
|
Error("possible infinite loop in CheckLine");
|
|
if ((grid[0] < 0) || (grid[0] > (MAPSIZE-1)) ||
|
|
(grid[1] < 0) || (grid[1] > (MAPSIZE-1)))
|
|
Error("out of bounds in check line, grid[0] = %d, grid[1] = %d",grid[0],grid[1]);
|
|
*/
|
|
if ((grid[0]==desttilex) && (grid[1]==desttiley))
|
|
return true;
|
|
tempactor = (objtype*)actorat[grid[0]][grid[1]];
|
|
value = tilemap[grid[0]][grid[1]];
|
|
checksprite = sprites[grid[0]][grid[1]];
|
|
if (value)
|
|
{
|
|
if (value&0x8000)
|
|
{
|
|
if (!(value&0x4000))
|
|
{
|
|
doorobj_t*dptr = doorobjlist[value&0x3ff];
|
|
|
|
if (dptr->position < 0x8000)
|
|
{
|
|
|
|
int x = (grid[0] << 16) + 0x8000;
|
|
int y = (grid[1] << 16) + 0x8000;
|
|
|
|
if (dptr->vertical)
|
|
{
|
|
if (abs(dx) > abs(x-orig->x))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (abs(dy) > abs(orig->y-y))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (condition == SHOOT)
|
|
{
|
|
if ( maskobjlist[value&0x3ff]->flags & MW_SHOOTABLE )
|
|
{
|
|
#if (0)
|
|
SoftError("\nfailed from shootable mask");
|
|
#endif
|
|
return false;
|
|
}
|
|
else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING )
|
|
{
|
|
#if (0)
|
|
SoftError("\nfailed from block mask");
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
else if ((condition == MISSILE) &&
|
|
( maskobjlist[value&0x3ff]->flags & MW_BLOCKING )
|
|
)
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if (0)
|
|
SoftError("\n obx %d, oby %d, origx %d, origy %d"
|
|
"\n xydist %d, vx %d, vy %d",ob->x,ob->y,orig->x,
|
|
orig->y,xydist,vx,vy);
|
|
|
|
SoftError("\nfailed from normal wall");
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
if (condition == SHOOT)
|
|
{
|
|
if (tempactor && (tempactor->which == ACTOR) &&
|
|
(tempactor->flags & FL_BLOCK) && (tempactor != orig) &&
|
|
(tempactor != ob)) //&&
|
|
// (InRange(orig,tempactor,
|
|
// FindDistance(orig->x-tempactor->x,orig->y-tempactor->y) )
|
|
// ==true) )
|
|
{
|
|
#if (0)
|
|
SoftError("\nfailed from actor");
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (checksprite && (checksprite->flags & FL_BLOCK) && (condition == SHOOT) &&
|
|
((void *)checksprite != to) &&
|
|
(checksprite->itemnumber!=stat_disk) &&
|
|
(InRange(orig,(objtype *)checksprite,
|
|
FindDistance(orig->x-checksprite->x,orig->y-checksprite->y) )
|
|
==true) )
|
|
|
|
{
|
|
#if (0)
|
|
SoftError("\nfailed from sprite");
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
if (tempactor && (tempactor->which == PWALL))
|
|
{
|
|
#if (0)
|
|
SoftError("\nfailed from pushwall");
|
|
#endif
|
|
return false;
|
|
}
|
|
index=(cnt>=0);
|
|
cnt+=incr[index];
|
|
grid[index]+=thedir[index];
|
|
}
|
|
while (1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= ShootActor
|
|
=
|
|
= Shoot an actor.
|
|
=
|
|
=====================
|
|
*/
|
|
void ShootActor(objtype * shooter, objtype * target, int damage, int accuracy, int angle)
|
|
{
|
|
int dx,dy,dist;
|
|
int newmomx, newmomy;
|
|
int tcl;
|
|
|
|
|
|
if (target->flags & FL_DYING)
|
|
return;
|
|
|
|
dx = abs(shooter->x - target->x);
|
|
dy = abs(shooter->y - target->y);
|
|
dist = FindDistance(dx,dy)>>16;
|
|
|
|
tcl=target->obclass;
|
|
|
|
if (tcl==playerobj)
|
|
{
|
|
target->target=shooter;
|
|
if (target->flags&FL_BPV)
|
|
{
|
|
playertype *pstate;
|
|
|
|
M_LINKSTATE(target,pstate);
|
|
pstate->protectiontime -= (damage<<1);
|
|
if (pstate->protectiontime < 1)
|
|
pstate->protectiontime = 1;
|
|
if (target==player)
|
|
GM_UpdateBonus (pstate->protectiontime, false);
|
|
return;
|
|
}
|
|
else if ((target->flags&FL_GODMODE) || (target->flags&FL_DOGMODE) || godmode)
|
|
return;
|
|
//damage=FixedMulShift((gamestate.difficulty+1),damage,2); // player object difficulty
|
|
}
|
|
|
|
else if (tcl == NMEsaucerobj)
|
|
{
|
|
target->momentumx = target->momentumy = 0;
|
|
NewState(target,&s_explosion1);
|
|
target->flags &= ~FL_SHOOTABLE;
|
|
return;
|
|
}
|
|
else if (tcl == b_darianobj)
|
|
MISCVARS->ESAU_SHOOTING = false;
|
|
else if ((tcl == strikeguardobj) || (tcl == b_heinrichobj))
|
|
target->target = shooter;
|
|
|
|
if (( (!(target->flags & FL_SHOOTABLE)) ||
|
|
(tcl == roboguardobj) || (tcl == wallopobj) ||
|
|
(tcl == patrolgunobj) ) &&
|
|
(tcl!=playerobj) )
|
|
SpawnMetalSparks(target,angle);
|
|
|
|
else if ((tcl < b_darianobj) || (tcl > b_darksnakeobj))
|
|
{
|
|
|
|
//target->flags &= ~FL_USE;
|
|
|
|
damage=FixedMulShift(511-accuracy,damage,9); // Min half damage
|
|
if (dist<64)
|
|
{
|
|
if (dist>2)
|
|
damage=FixedMulShift(63-dist,damage,6);
|
|
if (damage<1)
|
|
damage=1;
|
|
}
|
|
else
|
|
damage=1;
|
|
|
|
if (damage>MAXDAMAGE) damage=MAXDAMAGE; // absolutely clip it
|
|
|
|
DamageThing(target,damage);
|
|
if ((tcl == collectorobj) && gamestate.SpawnDeluder)
|
|
{
|
|
Collision(target,shooter,0,0);
|
|
if (target->hitpoints <= 0)
|
|
BATTLE_CheckGameStatus(battle_shot_deluder,shooter->dirchoosetime);
|
|
}
|
|
|
|
else
|
|
{newmomx = FixedMul(damage<<7,costable[angle]);
|
|
newmomy = -FixedMul(damage<<7,sintable[angle]);
|
|
Collision(target,shooter,-(target->momentumx)+newmomx,-(target->momentumy)+newmomy);
|
|
if (tcl == playerobj)
|
|
{
|
|
playertype * pstate;
|
|
|
|
M_LINKSTATE(target,pstate);
|
|
if (pstate->health <= 0)
|
|
{
|
|
|
|
if (shooter->obclass == playerobj) {
|
|
if (!target->momentumz)
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_bullet,shooter->dirchoosetime,target->dirchoosetime);
|
|
else
|
|
BATTLE_PlayerKilledPlayer(battle_kill_with_bullet_in_air,shooter->dirchoosetime,target->dirchoosetime);
|
|
}
|
|
}
|
|
}
|
|
// SoftError("ShootActor: damage=%ld dist=%ld\n",damage,dist);
|
|
|
|
if ((GameRandomNumber("disembowel",0)<64) && (gamestate.violence == vl_excessive))
|
|
{
|
|
int temp;
|
|
temp=target->temp1;
|
|
target->temp1=angle;
|
|
|
|
SpawnParticles(target,DISEMBOWEL,damage>>3);
|
|
target->temp1=temp;
|
|
}
|
|
else if (gamestate.violence > 0)
|
|
SpawnBlood(target,angle);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= ShootSprite
|
|
=
|
|
= Shoot a sprite.
|
|
=
|
|
=====================
|
|
*/
|
|
void ShootSprite(objtype * shooter, statobj_t * target, int damage, int accuracy, int angle)
|
|
{
|
|
int dx,dy,dist;
|
|
|
|
|
|
if (!(target->flags & FL_SHOOTABLE))
|
|
// Watchout for sprite being passed in as actor WARNING
|
|
SpawnMetalSparks((objtype *)target,angle);
|
|
|
|
else
|
|
{
|
|
dx = abs(shooter->x - target->x);
|
|
dy = abs(shooter->y - target->y);
|
|
dist = FindDistance(dx,dy)>>16;
|
|
|
|
damage=FixedMulShift(511-accuracy,damage,9); // Min half damage
|
|
if (dist<64)
|
|
{
|
|
if (dist>2)
|
|
damage=FixedMulShift(63-dist,damage,6);
|
|
if (damage<1)
|
|
damage=1;
|
|
}
|
|
else
|
|
damage=1;
|
|
|
|
if (damage>MAXDAMAGE) damage=MAXDAMAGE; // absolutely clip it
|
|
|
|
// SoftError("ShootSprite: damage=%ld dist=%ld\n",damage,dist);
|
|
|
|
DamageThing((objtype *)target,damage);
|
|
if (FirstExplosionState(new->state))
|
|
new->whatever = shooter;
|
|
|
|
SpawnStaticDamage(target, angle);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= RayShoot
|
|
=
|
|
= Cast a ray out at the shooter's angle and yzangle, return
|
|
=
|
|
=====================
|
|
*/
|
|
|
|
|
|
|
|
void RayShoot (objtype * shooter, int damage, int accuracy)
|
|
{
|
|
objtype *tempactor;
|
|
statobj_t *checksprite;
|
|
int snx,sny;
|
|
int incr[2];
|
|
int zincr[2];
|
|
int thedir[2];
|
|
int cnt;
|
|
int grid[2];
|
|
int index;
|
|
int vx,vy;
|
|
int angle;
|
|
int yzangle;
|
|
int value;
|
|
int offset;
|
|
int z;
|
|
int lastcnt;
|
|
int bullethole=0;
|
|
enum {gs_door, gs_wall, gs_floor, gs_ceiling, gs_pushwall};
|
|
int smokecondition=0;
|
|
|
|
|
|
if ((shooter->areanumber==player->areanumber) && (Near(shooter,player,3)))
|
|
SetIllumination(2);
|
|
|
|
offset = ((GameRandomNumber("RayShoot",0)-128)>>MAXSHOOTSHIFT);
|
|
offset = FixedMulShift(accuracy,offset,8);
|
|
|
|
if (offset>MAXSHOOTOFFSET)
|
|
offset=MAXSHOOTOFFSET;
|
|
|
|
else if (offset<-MAXSHOOTOFFSET)
|
|
offset=-MAXSHOOTOFFSET;
|
|
|
|
angle=(shooter->angle+offset)&(FINEANGLES-1);
|
|
|
|
offset = ((GameRandomNumber("RayShoot",1)-128)>>MAXSHOOTSHIFT);
|
|
offset = FixedMulShift(accuracy,offset,8);
|
|
|
|
if (offset>MAXSHOOTOFFSET)
|
|
offset=MAXSHOOTOFFSET;
|
|
|
|
else if (offset<-MAXSHOOTOFFSET)
|
|
offset=-MAXSHOOTOFFSET;
|
|
|
|
yzangle=(shooter->yzangle+offset)&(FINEANGLES-1);
|
|
|
|
vy = -sintable[angle];
|
|
vx = costable[angle];
|
|
snx=shooter->x&0xffff;
|
|
sny=shooter->y&0xffff;
|
|
grid[0]=shooter->tilex;
|
|
grid[1]=shooter->tiley;
|
|
if (shooter->obclass==playerobj)
|
|
{
|
|
playertype * pstate;
|
|
|
|
M_LINKSTATE(shooter,pstate);
|
|
z=shooter->z+pstate->playerheight-32;
|
|
}
|
|
else
|
|
z=shooter->z-7;
|
|
if (vx>0)
|
|
{
|
|
thedir[0]=1;
|
|
snx^=0xffff;
|
|
incr[1]=-vx;
|
|
}
|
|
else
|
|
{
|
|
thedir[0]=-1;
|
|
incr[1]=vx;
|
|
}
|
|
if (vy>0)
|
|
{
|
|
thedir[1]=1;
|
|
sny^=0xffff;
|
|
incr[0]=vy;
|
|
}
|
|
else
|
|
{
|
|
thedir[1]=-1;
|
|
incr[0]=-vy;
|
|
}
|
|
zincr[0]=-FixedMulShift(sintable[yzangle],abs(vx),26);
|
|
zincr[1]=-FixedMulShift(sintable[yzangle],abs(vy),26);
|
|
|
|
cnt=FixedMul(snx,incr[0])+FixedMul(sny,incr[1]);
|
|
index= (cnt >= 0);
|
|
do
|
|
{
|
|
tempactor = (objtype*)actorat[grid[0]][grid[1]];
|
|
value = tilemap[grid[0]][grid[1]];
|
|
checksprite = sprites[grid[0]][grid[1]];
|
|
if (value)
|
|
{
|
|
if (value&0x8000)
|
|
{
|
|
if (!(value&0x4000))
|
|
{
|
|
if ((doorobjlist[value&0x3ff]->action==dr_closed) || (z<maxheight-64))
|
|
{
|
|
smokecondition=gs_door;
|
|
break;
|
|
}
|
|
else if (doorobjlist[value&0x3ff]->position<=0x8000)
|
|
{
|
|
smokecondition=gs_door;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( maskobjlist[value&0x3ff]->flags & MW_SHOOTABLE )
|
|
{
|
|
if (z>maxheight-64) // Are we shooting above the glass
|
|
{
|
|
UpdateMaskedWall(value&0x3ff);
|
|
return;
|
|
}
|
|
}
|
|
else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING )
|
|
{
|
|
smokecondition=gs_door;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
smokecondition=gs_wall;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (checksprite &&
|
|
((checksprite->flags & FL_BLOCK)||(checksprite->flags & FL_SHOOTABLE)) &&
|
|
(abs(checksprite->z-z)<32) &&
|
|
(InRange(shooter,(objtype *)checksprite,
|
|
FindDistance(shooter->x-checksprite->x,shooter->y-checksprite->y) )
|
|
==true
|
|
)
|
|
)
|
|
{
|
|
ShootSprite(shooter, checksprite, damage, accuracy, angle);
|
|
return;
|
|
}
|
|
|
|
|
|
if (tempactor)
|
|
{if (tempactor->which == ACTOR)
|
|
{if ((abs(tempactor->z-z)<32 ) && (!(tempactor->flags & FL_DYING)) &&
|
|
(tempactor->flags & FL_BLOCK) && (tempactor != shooter) &&
|
|
(tempactor->obclass!=diskobj) &&
|
|
(InRange(shooter,tempactor,
|
|
FindDistance(shooter->x-tempactor->x,shooter->y-tempactor->y) )
|
|
==true
|
|
)
|
|
)
|
|
{ShootActor(shooter, tempactor, damage, accuracy, angle);
|
|
return;
|
|
}
|
|
}
|
|
else if (tempactor->which == PWALL)
|
|
return;
|
|
}
|
|
|
|
if (z<-32)
|
|
{
|
|
smokecondition=gs_ceiling;
|
|
break;
|
|
}
|
|
else if (z>maxheight)
|
|
{
|
|
smokecondition=gs_floor;
|
|
break;
|
|
}
|
|
index= (cnt >= 0);
|
|
cnt+=incr[index];
|
|
z +=zincr[index];
|
|
grid[index]+=thedir[index];
|
|
}
|
|
while (1);
|
|
|
|
if (IsWindow(grid[0],grid[1]))
|
|
return;
|
|
|
|
lastcnt=cnt-incr[index];
|
|
|
|
if (smokecondition==gs_floor)
|
|
{
|
|
int dist;
|
|
int tangentangle;
|
|
|
|
tangentangle=tantable[yzangle];
|
|
if (tangentangle!=0)
|
|
{
|
|
dist=FixedDiv2(((shooter->z-maxheight)<<10),(tangentangle<<1));
|
|
xintercept=shooter->x+FixedMul(dist,costable[angle]);
|
|
yintercept=shooter->y-FixedMul(dist,sintable[angle]);
|
|
}
|
|
z=maxheight;
|
|
// bullethole=5;
|
|
}
|
|
else if (smokecondition==gs_ceiling)
|
|
{
|
|
int dist;
|
|
int tangentangle;
|
|
|
|
if (sky!=0)
|
|
return;
|
|
tangentangle=tantable[yzangle];
|
|
if (tangentangle!=0)
|
|
{
|
|
dist=FixedDiv2(((shooter->z+32)<<10),(tangentangle<<1));
|
|
xintercept=shooter->x+FixedMul(dist,costable[angle]);
|
|
yintercept=shooter->y-FixedMul(dist,sintable[angle]);
|
|
}
|
|
z=-32;
|
|
// bullethole=5;
|
|
}
|
|
else
|
|
{
|
|
int dx,dy,xydist;
|
|
|
|
|
|
#define CORNERVALUE 0x500
|
|
|
|
|
|
if (IsWindow(grid[0],grid[1]))
|
|
return;
|
|
if (lastcnt<0)
|
|
{
|
|
xintercept=grid[0]<<16;
|
|
if (smokecondition==gs_door)
|
|
{
|
|
if (thedir[0]<0)
|
|
xintercept+=0x9fff;
|
|
else
|
|
xintercept+=0x5fff;
|
|
yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y;
|
|
if ((yintercept>>16)!=grid[1])
|
|
{
|
|
if ((yintercept>>16)>grid[1])
|
|
yintercept=(grid[1]<<16)+0xffff;
|
|
else
|
|
yintercept=(grid[1]<<16);
|
|
}
|
|
}
|
|
else if (smokecondition==gs_wall)
|
|
{
|
|
if (thedir[0]<0)
|
|
{
|
|
objtype * ta;
|
|
|
|
xintercept += 0x10000;
|
|
yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y;
|
|
|
|
xintercept += SMOKEWALLOFFSET;
|
|
bullethole=1;
|
|
if (yintercept < ((grid[1] << 16) + CORNERVALUE))
|
|
bullethole = 0;
|
|
else if (yintercept > ((grid[1] << 16) + 0x10000 - CORNERVALUE))
|
|
bullethole = 0;
|
|
|
|
ta = (objtype*)actorat[grid[0]][grid[1]];
|
|
if ((ta) && (ta->which==PWALL))
|
|
bullethole=0;
|
|
}
|
|
else
|
|
{
|
|
objtype * ta;
|
|
|
|
yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y;
|
|
xintercept-=SMOKEWALLOFFSET;
|
|
bullethole=2;
|
|
if (yintercept < ((grid[1] << 16) + CORNERVALUE))
|
|
bullethole = 0;
|
|
else if (yintercept > ((grid[1] << 16) + 0x10000 - CORNERVALUE))
|
|
bullethole = 0;
|
|
|
|
ta = (objtype*)actorat[grid[0]][grid[1]];
|
|
if ((ta) && (ta->which==PWALL))
|
|
bullethole=0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
yintercept=grid[1]<<16;
|
|
if (smokecondition==gs_door)
|
|
{
|
|
if (thedir[1]<0)
|
|
yintercept+=0x9fff;
|
|
else
|
|
yintercept+=0x5fff;
|
|
xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x;
|
|
if ((xintercept>>16)!=grid[0])
|
|
{
|
|
if ((xintercept>>16)>grid[0])
|
|
xintercept=(grid[0]<<16)+0xffff;
|
|
else
|
|
xintercept=(grid[0]<<16);
|
|
}
|
|
}
|
|
else if (smokecondition==gs_wall)
|
|
{
|
|
if (thedir[1]<0)
|
|
{
|
|
objtype * ta;
|
|
|
|
|
|
yintercept += 0x10000;
|
|
xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x;
|
|
|
|
yintercept += SMOKEWALLOFFSET;
|
|
bullethole=3;
|
|
if (xintercept < ((grid[0] << 16) + CORNERVALUE))
|
|
bullethole = 0;
|
|
else if (xintercept > ((grid[0] << 16) + 0x10000 - CORNERVALUE))
|
|
bullethole = 0;
|
|
|
|
ta = (objtype*)actorat[grid[0]][grid[1]];
|
|
if ((ta) && (ta->which==PWALL))
|
|
bullethole=0;
|
|
}
|
|
else
|
|
{
|
|
objtype * ta;
|
|
|
|
xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x;
|
|
yintercept-=SMOKEWALLOFFSET;
|
|
bullethole=4;
|
|
if (xintercept < ((grid[0] << 16) + CORNERVALUE))
|
|
bullethole = 0;
|
|
else if (xintercept > ((grid[0] << 16) + 0x10000 - CORNERVALUE))
|
|
bullethole = 0;
|
|
|
|
ta = (objtype*)actorat[grid[0]][grid[1]];
|
|
if ((ta) && (ta->which==PWALL))
|
|
bullethole=0;
|
|
}
|
|
}
|
|
}
|
|
dx = xintercept - shooter->x;
|
|
dy = shooter->y - yintercept;
|
|
xydist = FindDistance(dx,dy);
|
|
if (shooter->obclass==playerobj)
|
|
{
|
|
playertype * pstate;
|
|
|
|
M_LINKSTATE(shooter,pstate);
|
|
z=shooter->z-FixedMulShift(xydist,tantable[yzangle],25)+pstate->playerheight-32;
|
|
}
|
|
else
|
|
z=shooter->z-FixedMulShift(xydist,tantable[yzangle],25);
|
|
if (smokecondition==gs_wall)
|
|
{
|
|
if (z<-32)
|
|
z=-32;
|
|
}
|
|
}
|
|
SpawnGunSmoke(xintercept,yintercept,z,angle,bullethole);
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= T_BossDied ()
|
|
=
|
|
=====================
|
|
*/
|
|
|
|
void T_BossDied (objtype *ob)
|
|
{
|
|
|
|
if (ob->ticcount)
|
|
return;
|
|
|
|
switch (ob->obclass)
|
|
{
|
|
case b_darianobj:
|
|
case b_heinrichobj:
|
|
case b_darkmonkobj:
|
|
case b_robobossobj:
|
|
case b_darksnakeobj:
|
|
playstate = ex_bossdied;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= T_Wind ()
|
|
=
|
|
=====================
|
|
*/
|
|
|
|
static int WindDistance = 1000;
|
|
static int WindCurrentDistance = 1000;
|
|
static int WindHandle = -1;
|
|
static int WindLastTic = -1;
|
|
static int WindPlaying = false;
|
|
static int WindPitch = 0;
|
|
static int WindDestPitch = 0;
|
|
static int WindPitchRate = 0;
|
|
|
|
void T_Wind
|
|
(
|
|
objtype *ob
|
|
)
|
|
|
|
{
|
|
int distance;
|
|
int dx;
|
|
int dy;
|
|
|
|
if ( ( GetTicCount() - WindLastTic ) > 0 )
|
|
{
|
|
WindDistance = 1000;
|
|
|
|
WindPitch += WindPitchRate;
|
|
if ( WindPitch == WindDestPitch )
|
|
{
|
|
WindDestPitch = ( RandomNumber( "Wind Pitch", 0 ) - 128 ) << 3;
|
|
WindPitchRate = 1;
|
|
if ( WindDestPitch < WindPitch )
|
|
{
|
|
WindPitchRate = -WindPitchRate;
|
|
}
|
|
}
|
|
}
|
|
WindLastTic = GetTicCount();
|
|
|
|
dx = ( ob->x - PLAYER[0]->x );
|
|
dy = ( PLAYER[0]->y - ob->y );
|
|
|
|
distance = 1000;
|
|
if ( areabyplayer[ ob->areanumber ] )
|
|
{
|
|
distance = ( FindDistance( dx, dy ) ) >> 13;
|
|
}
|
|
|
|
if ( distance < WindDistance )
|
|
{
|
|
WindDistance = distance;
|
|
}
|
|
|
|
if ( WindDistance < 255 )
|
|
{
|
|
WindPlaying = true;
|
|
WindCurrentDistance = WindDistance;
|
|
}
|
|
else
|
|
{
|
|
if ( WindPlaying )
|
|
{
|
|
WindCurrentDistance += 3;
|
|
}
|
|
}
|
|
|
|
if ( WindPlaying )
|
|
{
|
|
if ( WindCurrentDistance < 255 )
|
|
{
|
|
if ( !SD_SoundActive( WindHandle ) )
|
|
{
|
|
WindHandle = SD_PlayPitchedSound( SD_WINDSND,
|
|
255 - WindCurrentDistance, WindPitch );
|
|
}
|
|
else
|
|
{
|
|
SD_SetSoundPitch( WindHandle, WindPitch );
|
|
SD_SetPan( WindHandle, 255 - WindCurrentDistance, 255 - WindCurrentDistance,
|
|
255 - WindCurrentDistance );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SD_StopSound( WindHandle );
|
|
WindPlaying = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
=
|
|
= StopWind ()
|
|
=
|
|
=====================
|
|
*/
|
|
|
|
void StopWind
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
objtype *temp;
|
|
|
|
FX_SetReverb( 0 );
|
|
|
|
SD_StopSound( WindHandle );
|
|
WindDistance = 1000;
|
|
WindCurrentDistance = 1000;
|
|
WindHandle = -1;
|
|
WindLastTic = -1;
|
|
WindPlaying = false;
|
|
WindPitch = 0;
|
|
WindDestPitch = 0;
|
|
WindPitchRate = 0;
|
|
|
|
|
|
for(temp=FIRSTACTOR;temp;temp=temp->next)
|
|
{
|
|
if (temp->soundhandle != -1)
|
|
SD_StopSound(temp->soundhandle);
|
|
}
|
|
}
|
|
|