Files
hovertank_3d/HOVACTS.C
T
2026-07-14 19:31:07 +02:00

1551 lines
27 KiB
C++

/* Hovertank 3-D Source Code
* Copyright (C) 1993-2014 Flat Rock Software
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "HOVERDEF.H"
//==========================================================================
#define GUNARM 50
#define GUNCHARGE 70
#define EXPLODEANM 8
#define SHOTSPEED 8192l
#define MSHOTSPEED 6000l
#define REFUGEEANM 30
#define WARPANM 10
#define SPDMUTANT 2500l
#define MUTANTANM 20
#define MUTANTATTACK 120
#define SPDDRONE 16384l
#define DRONEANM 10
#define TANKRELOAD 200
#define ANGLESTEP 1
#define PLAYERTHRUST 4096l
#define PLAYERREVERSE 2048l
#define PLAYERAFTERBURN 8192l
#define SHIELDANM 10
#define MINCHASE 4096
dirtype opposite[9] =
{south,southwest,west,northwest,north,northeast,east,southeast,nodir};
//==========================================================================
enum {rearming,ready,charging,maxpower} gunstate;
int guncount;
//==========================================================================
void SpawnPlayer (fixed gx, fixed gy);
void GetRefugee (objtype *hit);
void CheckFire (void);
void DamagePlayer (void);
void Thrust (void);
void Reverse (void);
void AfterBurn (void);
void PlayerThink (void);
void SpawnShot (fixed gx, fixed gy, int angle, classtype class);
void ExplodeShot(void);
int ClipPointMove (void);
void ShotThink (void);
void InertThink (void);
void ExplodeThink (void);
void SpawnWarp (fixed gx, fixed gy);
void WarpThink (void);
void SpawnRefugee (fixed gx, fixed gy,int sex);
void KillRefugee (objtype *hit);
void RefugeeThink (void);
void SpawnDrone (fixed gx, fixed gy);
void KillDrone (objtype *hit);
void DroneThink (void);
void SpawnTank (fixed gx, fixed gy);
void KillTank (objtype *hit);
void AimAtPlayer (void);
void TankThink (void);
void SpawnMutant (fixed gx, fixed gy);
void KillMutant (objtype *hit);
int Walk (void);
void ChaseThing (objtype *chase);
void MutantThink (void);
void SpawnShield (fixed gx, fixed gy);
void ShieldThink (void);
/*
=============================================================================
PLAYER
=============================================================================
*/
/*
===================
=
= SpawnPlayer
=
===================
*/
void SpawnPlayer (fixed gx, fixed gy)
{
objlist[0].x = gx;
objlist[0].y = gy;
objlist[0].angle = 0;
objlist[0].think = PlayerThink;
objlist[0].class = playerobj;
objlist[0].size = MINDIST;
objlist[0].radarcolor = 15;
objlist[0].hitpoints = 3;
objlist[0].xl = objlist[0].x - objlist[0].size;
objlist[0].xh = objlist[0].x + objlist[0].size;
objlist[0].yl = objlist[0].y - objlist[0].size;
objlist[0].yh = objlist[0].y + objlist[0].size;
gunstate = ready;
}
/*
===================
=
= GetRefugee
=
===================
*/
void GetRefugee (objtype *hit)
{
PlaySound (SAVEHOSTAGESND);
hit->class = nothing;
if (hit->radarx)
XPlot (hit->radarx,hit->radary,hit->radarcolor);
DrawPic (2*savedcount+1,6,SAVEDGUYPIC);
savedcount++;
if (!--numrefugees)
{
PlaySound (LASTHOSTAGESND);
SpawnWarp (warpx,warpy);
}
}
/*
===================
=
= CheckFire
=
===================
*/
void CheckFire (void)
{
if (gunstate == rearming)
{
if ( (guncount+=tics) > GUNARM)
{
gunstate = ready;
PlaySound(GUNREADYSND);
DrawPic (14,40,READYPIC);
}
return;
}
if (c.button1)
{
// holding down button
if (gunstate==ready)
{
gunstate = charging;
DrawPic (14,40,CHARGINGPIC);
guncount = 0;
}
if ( gunstate == charging && (guncount+=tics) > GUNCHARGE)
{
PlaySound(MAXPOWERSND);
gunstate = maxpower;
DrawPic (14,40,MAXPOWERPIC);
}
}
else
// button up
if (gunstate>ready) // fire small shot if charging, large if maxed
{
DrawPic (14,40,REARMINGPIC);
guncount = 0;
SpawnShot (obon.x,obon.y,obon.angle,pshotobj+(gunstate-charging));
gunstate = rearming;
PlaySound (FIRESND);
}
}
/*
===================
=
= DamagePlayer
=
===================
*/
void DamagePlayer (void)
{
PlaySound (TAKEDAMAGESND);
if (!godmode && !--objlist[0].hitpoints)
{
PlaySound(PLAYERDEADSND);
while (SoundPlaying())
;
leveldone = -1; // all hit points gone
}
else
DrawPic (24,36,SHIELDLOWPIC+1-objlist[0].hitpoints);
ColorBorder(12);
bordertime = 60;
}
void HealPlayer (void)
{
PlaySound (ARMORUPSND);
if (objlist[0].hitpoints<3)
objlist[0].hitpoints++;
DrawPic (24,36,SHIELDLOWPIC+1-objlist[0].hitpoints);
ColorBorder(9);
bordertime = 60;
}
/*
===================
=
= Thrust
=
===================
*/
void Thrust (void)
{
xmove = FixedByFrac(PLAYERTHRUST*tics,costable[obon.angle]);
ymove = FixedByFrac(PLAYERTHRUST*tics,sintable[obon.angle])^SIGNBIT;
ClipMove();
}
/*
===================
=
= Reverse
=
===================
*/
void Reverse (void)
{
xmove = FixedByFrac(PLAYERREVERSE*tics,costable[obon.angle])^SIGNBIT;
ymove = FixedByFrac(PLAYERREVERSE*tics,sintable[obon.angle]);
ClipMove();
}
/*
===================
=
= AfterBurn
=
===================
*/
void AfterBurn (void)
{
xmove = FixedByFrac(PLAYERAFTERBURN*tics,costable[obon.angle]);
ymove = FixedByFrac(PLAYERAFTERBURN*tics,sintable[obon.angle])^SIGNBIT;
ClipMove();
if (!SoundPlaying())
PlaySound(AFTERBURNSND);
}
void BeforeBurn (void)
{
xmove = FixedByFrac(PLAYERAFTERBURN*tics,costable[obon.angle]^SIGNBIT);
ymove = FixedByFrac(PLAYERAFTERBURN*tics,sintable[obon.angle]);
ClipMove();
if (!SoundPlaying())
PlaySound(AFTERBURNSND);
}
/*
===================
=
= PlayerThink
=
===================
*/
void PlayerThink (void)
{
int anglechange;
objtype *check;
if (c.button1) // hold down fire for slow adjust
{
if (tics<=4)
{
anglechange = ANGLESTEP*tics/2;
if (!anglechange)
anglechange=1;
}
else
anglechange = ANGLESTEP*2;
}
else
anglechange = ANGLESTEP*tics;
if (c.dir==west || c.dir==northwest || c.dir==southwest)
{
obon.angle+=anglechange;
if ( obon.angle >= ANGLES )
obon.angle -= ANGLES;
}
else if (c.dir==east || c.dir==northeast || c.dir==southeast)
{
obon.angle-=anglechange;
if ( obon.angle < 0)
obon.angle += ANGLES;
}
if (c.button2 && (c.dir==south || c.dir==southeast || c.dir==southwest) )
BeforeBurn();
else if (c.button2)
AfterBurn();
else if (c.dir==north || c.dir==northeast || c.dir==northwest)
Thrust();
else if (c.dir==south || c.dir==southeast || c.dir==southwest)
Reverse();
CheckFire ();
for (check = &objlist[1];check<=lastobj;check++)
if
(check->class
&& check->xl <= obon.xh
&& check->xh >= obon.xl
&& check->yl <= obon.yh
&& check->yh >= obon.yl)
{
switch (check->class)
{
case refugeeobj:
GetRefugee (check);
break;
case shieldobj:
objlist[0] = obon;
HealPlayer ();
obon = objlist[0];
check->class = nothing;
if (check->radarx)
XPlot (check->radarx,check->radary,check->radarcolor);
break;
case warpobj:
leveldone = 1;
break;
}
}
StartView(); // calculate view position and trace walls
// FinishView in PlayLoop draws everything
}
/*
=============================================================================
SHOTS
=============================================================================
*/
/*
====================
=
= SpawnShot
=
====================
*/
void SpawnShot (fixed gx, fixed gy, int angle, classtype class)
{
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = angle;
new->think = ShotThink;
new->class = class;
new->size = TILEGLOBAL/8;
switch (class)
{
case pshotobj:
new->shapenum = PSHOTPIC;
new->speed = SHOTSPEED;
break;
case pbigshotobj:
new->shapenum = BIGSHOTPIC;
new->speed = SHOTSPEED;
break;
case mshotobj:
new->shapenum = MSHOTPIC;
new->speed = MSHOTSPEED;
break;
}
CalcBoundsNew();
}
/*
===================
=
= ExplodeShot
=
===================
*/
void ExplodeShot(void)
{
PlaySound (SHOOTWALLSND);
obon.class = inertobj;
obon.shapenum = obon.temp1 = SHOTDIE1PIC;
obon.think = ExplodeThink;
obon.stage = 0;
TransformObon();
}
/*
====================
=
= ClipPointMove
=
====================
*/
#define SHOTCLIP 0x1000 // explode 1/16th tile from wall
int ClipPointMove (void)
{
int xt,yt;
long xclipdist,yclipdist,intersect,basex,basey;
unsigned inside,total;
xmove = FixedByFrac(obon.speed*tics,costable[obon.angle]);
if (xmove<0)
xmove=-(xmove^SIGNBIT);
ymove = FixedByFrac(obon.speed*tics,sintable[obon.angle])^SIGNBIT;
if (ymove<0)
ymove=-(ymove^SIGNBIT);
obon.x += xmove;
obon.y += ymove;
xt = obon.x>>TILESHIFT;
yt = obon.y>>TILESHIFT;
if (!tilemap[xt][yt])
return 0;
//
// intersect the path with the tile edges to determine point of impact
//
basex = obon.x & 0xffff0000;
basey = obon.y & 0xffff0000;
obon.x &= 0xffff; // move origin to ul corner of tile
obon.y &= 0xffff;
if (xmove>0)
{
inside = obon.x;
total = xmove;
intersect = obon.y - ymove*inside/total;
if (intersect <= TILEGLOBAL)
{
obon.x = basex - SHOTCLIP;
obon.y = basey + intersect;
return 1;
}
}
else if (xmove<0)
{
inside = TILEGLOBAL-obon.x;
total = -xmove;
intersect = obon.y - ymove*inside/total;
if (intersect <= TILEGLOBAL)
{
obon.x = basex + TILEGLOBAL + SHOTCLIP;
obon.y = basey + intersect;
return 1;
}
}
if (ymove>0)
{
inside = obon.y;
total = ymove;
intersect = obon.x - xmove*inside/total;
if (intersect <= TILEGLOBAL)
{
obon.x = basex + intersect;
obon.y = basey - SHOTCLIP;
return 1;
}
}
else if (ymove < 0)
{
inside = TILEGLOBAL-obon.y;
total = -ymove;
intersect = obon.x - xmove*inside/total;
if (intersect <= TILEGLOBAL)
{
obon.x = basex + intersect;
obon.y = basey + TILEGLOBAL + SHOTCLIP;
return 1;
}
}
return 1;
}
/*
===================
=
= ShotThink
=
===================
*/
void ShotThink (void)
{
int i,xt,yt;
objtype *check;
for (i=0;i<3;i++) // so it can move over one tile distance
{
if (ClipPointMove())
{
ExplodeShot();
return;
}
CalcBounds();
for (check = &objlist[0];check<=lastobj;check++)
if
(check->class
&& check->xl <= obon.xh
&& check->xh >= obon.xl
&& check->yl <= obon.yh
&& check->yh >= obon.yl)
{
switch (check->class)
{
case playerobj:
if (obon.class == mshotobj)
{
DamagePlayer ();
obon.class = nothing;
return;
}
break;
case refugeeobj:
KillRefugee (check);
if (obon.class == pbigshotobj)
break;
obon.class = nothing;
return;
case mutantobj:
KillMutant (check);
if (obon.class == pbigshotobj)
break;
obon.class = nothing;
return;
case tankobj:
if (obon.class != mshotobj)
{
KillTank (check);
if (obon.class == pbigshotobj)
break;
obon.class = nothing;
return;
}
break;
case droneobj:
KillDrone (check);
if (obon.class == pbigshotobj)
break;
obon.class = nothing;
return;
}
}
}
TransformObon();
}
/*
==================
=
= InertThink
=
= Corpses, etc...
=
==================
*/
void InertThink (void)
{
TransformObon();
}
/*
===================
=
= ExplodeThink
=
===================
*/
void ExplodeThink (void)
{
obon.ticcount+=tics;
if (obon.ticcount>EXPLODEANM)
{
obon.ticcount -= EXPLODEANM;
if (++obon.stage == 5)
{
if (obon.temp1 == SHOTDIE1PIC) // shopt explosions go away
obon.class = nothing;
else
obon.think = InertThink;
return;
}
obon.shapenum = obon.temp1 + obon.stage;
}
TransformObon();
}
/*
=============================================================================
WARP GATE
=============================================================================
*/
/*
==================
=
= SpawnWarp
=
==================
*/
void SpawnWarp (fixed gx, fixed gy)
{
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = 0;
new->think = WarpThink;
new->class = warpobj;
new->size = MINDIST;
new->radarcolor = 14;
new->shapenum = WARP1PIC;
CalcBoundsNew();
}
/*
===================
=
= WarpThink
=
===================
*/
void WarpThink (void)
{
obon.ticcount+=tics;
if (obon.ticcount>WARPANM)
{
obon.ticcount -= WARPANM;
if (++obon.stage == 4)
obon.stage = 0;
obon.shapenum = WARP1PIC + obon.stage;
}
TransformObon();
}
/*
=============================================================================
REFUGEE
=============================================================================
*/
/*
==================
=
= SpawnRefugee
=
= obon.temp2 is true if a drone is seeking it
=
==================
*/
void SpawnRefugee (fixed gx, fixed gy,int sex)
{
numrefugees++;
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = 0;
new->think = RefugeeThink;
new->class = refugeeobj;
new->size = TILEGLOBAL/3;
new->radarcolor = 15;
if (sex)
{
new->shapenum = MAN1PIC;
new->temp1 = MAN1PIC;
}
else
{
new->shapenum = WOMAN1PIC;
new->temp1 = WOMAN1PIC;
}
new->temp2 = 0;
new->ticcount = Rnd(REFUGEEANM*3);
CalcBoundsNew();
}
/*
===================
=
= KillRefugee
=
===================
*/
void KillRefugee (objtype *hit)
{
PlaySound (HOSTAGEDEADSND);
if (hit->radarx)
XPlot (hit->radarx,hit->radary,hit->radarcolor);
killedcount++;
DrawPic (2*(totalrefugees-killedcount)+1,6,DEADGUYPIC);
if (!--numrefugees)
{
PlaySound (WARPGATESND);
SpawnWarp (warpx,warpy);
}
hit->radarcolor = 0;
hit->class = inertobj;
if (hit->temp1 == MAN1PIC)
hit->shapenum = hit->temp1 = MANDIE1PIC;
else
hit->shapenum = hit->temp1 = WOMANDIE1PIC;
hit->think = ExplodeThink;
hit->stage = hit->ticcount = 0;
}
/*
===================
=
= RefugeeThink
=
===================
*/
void RefugeeThink (void)
{
obon.ticcount+=tics;
if (obon.ticcount>REFUGEEANM)
{
obon.ticcount -= REFUGEEANM;
if (++obon.stage == 2)
obon.stage = 0;
obon.shapenum = obon.temp1 + obon.stage;
}
TransformObon();
}
/*
=============================================================================
DRONE
=============================================================================
*/
/*
==================
=
= SpawnDrone
=
= obon.temp1 is a pointer to the refugee the drone is currently seeking
=
==================
*/
void SpawnDrone (fixed gx, fixed gy)
{
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = 0;
new->think = DroneThink;
new->class = droneobj;
new->size = MINDIST;
new->radarcolor = 10;
new->hitpoints = 2;
new->shapenum = DRONE1PIC;
new->ticcount = Rnd(DRONEANM*3);
new->temp1 = (int)new; // will hunt first think
CalcBoundsNew();
}
/*
===================
=
= KillDrone
=
===================
*/
void KillDrone (objtype *hit)
{
PlaySound (SHOOTTHINGSND);
if (hit->radarx)
XPlot (hit->radarx,hit->radary,hit->radarcolor);
hit->radarcolor = 0;
hit->class = inertobj;
hit->shapenum = hit->temp1 = DRONEDIE1PIC;
hit->think = ExplodeThink;
hit->stage = hit->ticcount = 0;
}
/*
==================
=
= DroneLockOn
=
==================
*/
void DroneLockOn (void)
{
objtype *check;
for (check=&objlist[2];check<lastobj;check++)
if (check->class == refugeeobj && !check->temp2)
{
check->temp2++;
obon.temp1 = (int)check;
return;
}
obon.temp1 = (int)&objlist[0]; // go after player last
}
/*
===================
=
= DroneThink
=
===================
*/
void DroneThink (void)
{
if ( ((objtype *)obon.temp1)->class != refugeeobj &&
((objtype *)obon.temp1)->class != playerobj)
DroneLockOn (); // target died
obon.ticcount+=tics;
if (obon.ticcount>DRONEANM)
{
obon.ticcount -= DRONEANM;
if (++obon.stage == 4)
obon.stage = 0;
obon.shapenum = DRONE1PIC + obon.stage;
}
ChaseThing ((objtype *)obon.temp1);
CalcBounds ();
TransformObon();
for (check = &objlist[0];check<=lastobj;check++)
if
(check->class
&& check->xl <= obon.xh
&& check->xh >= obon.xl
&& check->yl <= obon.yh
&& check->yh >= obon.yl)
{
switch (check->class)
{
case playerobj: // kill player and blow up
DamagePlayer ();
PlaySound (SHOOTTHINGSND);
if (obon.radarx)
XPlot (obon.radarx,obon.radary,obon.radarcolor);
obon.radarcolor = 0;
obon.class = inertobj;
obon.shapenum = obon.temp1 = DRONEDIE1PIC;
obon.think = ExplodeThink;
obon.stage = obon.ticcount = 0;
return;
case refugeeobj:
KillRefugee (check);
break;
}
}
}
/*
=============================================================================
TANK
=============================================================================
*/
/*
==================
=
= SpawnTank
=
==================
*/
void SpawnTank (fixed gx, fixed gy)
{
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = 0;
new->think = TankThink;
new->class = tankobj;
new->size = MINDIST;
new->shapenum = TANK1PIC;
new->radarcolor = 13;
new->hitpoints = 3;
CalcBoundsNew();
}
/*
===================
=
= KillTank
=
===================
*/
void KillTank (objtype *hit)
{
PlaySound (SHOOTTHINGSND);
if (hit->radarx)
XPlot (hit->radarx,hit->radary,hit->radarcolor);
hit->radarcolor = 0;
hit->class = inertobj;
hit->shapenum = hit->temp1 = TANKDIE1PIC;
hit->think = ExplodeThink;
hit->stage = hit->ticcount = 0;
}
/*
======================
=
= AimAtPlayer
=
= Hunt for player
=
======================
*/
void AimAtPlayer (void)
{
long deltax,deltay;
int i,xstep,ystep,tx,ty,steps;
dirtype d[3],tdir, olddir, turnaround;
olddir=obon.dir;
turnaround=opposite[olddir];
deltax=objlist[0].x-obon.x;
deltay=objlist[0].y-obon.y;
d[1]=nodir;
d[2]=nodir;
if (deltax>MINCHASE)
d[1]= east;
else if (deltax<-MINCHASE)
d[1]= west;
if (deltay>MINCHASE)
d[2]=south;
else if (deltay<-MINCHASE)
d[2]=north;
if (LABS(deltay)<LABS(deltax))
{
tdir=d[1];
d[1]=d[2];
d[2]=tdir;
}
if (d[1]==turnaround)
d[1]=nodir;
if (d[2]==turnaround)
d[2]=nodir;
//
// shoot at player if even aim and not reloading
//
if (d[1]==nodir && !obon.stage)
{
xstep = ystep = 0;
if (deltax>MINCHASE)
{
xstep = 1;
steps = ((objlist[0].x - obon.x)>>TILESHIFT)-1;
obon.angle = 0;
}
else if (deltax<-MINCHASE)
{
xstep = -1;
steps = ((obon.x - objlist[0].x)>>TILESHIFT)-1;
obon.angle = 180;
}
if (deltay>MINCHASE)
{
ystep = 1;
steps = ((objlist[0].y - obon.y)>>TILESHIFT)-1;
obon.angle = 270;
}
else if (deltay<-MINCHASE)
{
ystep = -1;
steps = ((obon.y - objlist[0].y)>>TILESHIFT)-1;
obon.angle = 90;
}
tx = obon.x >> TILESHIFT;
ty = obon.y >> TILESHIFT;
for (i=0;i<steps;i++)
{
tx += xstep;
ty += ystep;
if (tilemap[tx][ty])
goto cantshoot; // shot is blocked
}
PlaySound (FIRESND);
SpawnShot (obon.x,obon.y,obon.angle,mshotobj);
obon.ticcount = 0;
obon.stage = 1;
}
if (d[1]!=nodir)
{
obon.dir=d[1];
if (Walk())
return;
}
cantshoot:
if (d[2]!=nodir)
{
obon.dir=d[2];
if (Walk())
return;
}
/* there is no direct path to the player, so pick another direction */
obon.dir=olddir;
if (Walk())
return;
if (RndT()>128) /*randomly determine direction of search*/
{
for (tdir=north;tdir<=west;tdir+=2)
if (tdir!=turnaround)
{
obon.dir=tdir;
if (Walk())
return;
}
}
else
{
for (tdir=west;tdir>=north;tdir-=2)
if (tdir!=turnaround)
{
obon.dir=tdir;
if (Walk())
return;
}
}
obon.dir=turnaround;
Walk(); // last chance, don't worry about returned value
}
/*
===================
=
= TankThink
=
===================
*/
void TankThink (void)
{
if (obon.stage == 1) // just fired?
{
if ( (obon.ticcount += tics) >= TANKRELOAD)
obon.stage = 0;
}
AimAtPlayer ();
TransformObon();
}
/*
=============================================================================
MUTANT
=============================================================================
*/
/*
==================
=
= SpawnMutant
=
==================
*/
void SpawnMutant (fixed gx, fixed gy)
{
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = 0;
new->think = MutantThink;
new->class = mutantobj;
new->size = MINDIST;
new->shapenum = MUTANT1PIC;
new->radarcolor = 12;
new->hitpoints = 1;
new->ticcount = Rnd(MUTANTANM*3);
CalcBoundsNew();
}
/*
===================
=
= KillMutant
=
===================
*/
void KillMutant (objtype *hit)
{
PlaySound (SHOOTTHINGSND);
if (hit->radarx)
XPlot (hit->radarx,hit->radary,hit->radarcolor);
hit->radarcolor = 0;
hit->class = inertobj;
hit->shapenum = hit->temp1 = MUTANTDIE1PIC;
hit->think = ExplodeThink;
hit->stage = hit->ticcount = 0;
}
/*
======================
=
= Walk
=
= Returns true if a movement of obon.dir/obon.speed is ok or causes
= an attack at player
=
======================
*/
#define WALLZONE (2*TILEGLOBAL/3)
int Walk (void)
{
int xmove,ymove,xl,yl,xh,yh,xt,yt;
switch (obon.dir)
{
case north:
xmove = 0;
ymove = -SPDMUTANT;
break;
case east:
xmove = SPDMUTANT;
ymove = 0;
break;
case south:
xmove = 0;
ymove = SPDMUTANT;
break;
case west:
xmove = -SPDMUTANT;
ymove = 0;
break;
default:
Quit ("Walk: Bad dir!");
}
obon.x += xmove;
obon.y += ymove;
//
// calculate a hit rect to stay away from walls in
//
obon.xl = obon.x - WALLZONE;
obon.xh = obon.x + WALLZONE;
obon.yl = obon.y - WALLZONE;
obon.yh = obon.y + WALLZONE;
//
// tile coordinate edges
//
xt = obon.x>>TILESHIFT;
yt = obon.y>>TILESHIFT;
xl = obon.xl>>TILESHIFT;
yl = obon.yl>>TILESHIFT;
xh = obon.xh>>TILESHIFT;
yh = obon.yh>>TILESHIFT;
//
// check corners
//
if (tilemap[xl][yl] || tilemap[xh][yl]
|| tilemap[xl][yh] || tilemap[xh][yh]
|| tilemap[xt][yh] || tilemap[xt][yl]
|| tilemap[xl][yt] || tilemap[xh][yt] )
{
obon.x -= xmove;
obon.y -= ymove;
return 0;
}
//
// check contact with player
//
return 1;
}
/*
======================
=
= ChaseThing
=
= Hunt for player
=
======================
*/
void ChaseThing (objtype *chase)
{
long deltax,deltay;
int i;
dirtype d[3],tdir, olddir, turnaround;
olddir=obon.dir;
turnaround=opposite[olddir];
deltax=chase->x-obon.x;
deltay=chase->y-obon.y;
d[1]=nodir;
d[2]=nodir;
if (deltax>MINCHASE)
d[1]= east;
else if (deltax<-MINCHASE)
d[1]= west;
if (deltay>MINCHASE)
d[2]=south;
else if (deltay<-MINCHASE)
d[2]=north;
if (LABS(deltay)>LABS(deltax))
{
tdir=d[1];
d[1]=d[2];
d[2]=tdir;
}
if (d[1]==turnaround)
d[1]=nodir;
if (d[2]==turnaround)
d[2]=nodir;
if (d[1]!=nodir)
{
obon.dir=d[1];
if (Walk())
{
if (d[2]!=nodir)
{
obon.dir=d[2];
Walk(); // try to go diagonal if possible
}
return;
}
}
if (d[2]!=nodir)
{
obon.dir=d[2];
if (Walk())
return;
}
/* there is no direct path to the player, so pick another direction */
obon.dir=olddir;
if (Walk())
return;
if (RndT()>128) /*randomly determine direction of search*/
{
for (tdir=north;tdir<=west;tdir+=2)
if (tdir!=turnaround)
{
obon.dir=tdir;
if (Walk())
return;
}
}
else
{
for (tdir=west;tdir>=north;tdir-=2)
if (tdir!=turnaround)
{
obon.dir=tdir;
if (Walk())
return;
}
}
obon.dir=turnaround;
Walk(); // last chance, don't worry about returned value
}
/*
===================
=
= MutantThink
=
===================
*/
#define ATTACKZONE (TILEGLOBAL)
void MutantThink (void)
{
obon.ticcount += tics;
if (obon.stage==4) // attack stage
{
if (obon.ticcount < MUTANTATTACK)
{
TransformObon();
return;
}
obon.ticcount = MUTANTANM+1-tics;
obon.stage = 0;
}
if (obon.ticcount>MUTANTANM)
{
obon.ticcount -= MUTANTANM;
if (++obon.stage == 4)
obon.stage = 0;
obon.shapenum = MUTANT1PIC + obon.stage;
}
if (objlist[0].xl <= obon.x + ATTACKZONE
&& objlist[0].xh >= obon.x - ATTACKZONE
&& objlist[0].yl <= obon.y + ATTACKZONE
&& objlist[0].yh >= obon.y - ATTACKZONE)
{
obon.stage = 4;
obon.ticcount = 0;
obon.shapenum = MUTANTHITPIC;
DamagePlayer();
}
else
ChaseThing(&objlist[0]);
CalcBounds ();
TransformObon();
}
/*
=============================================================================
SHIELD
=============================================================================
*/
/*
==================
=
= SpawnShield
=
==================
*/
void SpawnShield (fixed gx, fixed gy)
{
FindFreeObj();
new->x = gx;
new->y = gy;
new->angle = 0;
new->think = ShieldThink;
new->class = shieldobj;
new->size = MINDIST;
new->shapenum = SHIELD1PIC;
new->radarcolor = 9;
new->hitpoints = 3;
CalcBoundsNew();
}
/*
===================
=
= ShieldThink
=
===================
*/
void ShieldThink (void)
{
obon.ticcount += tics;
if (obon.ticcount>SHIELDANM)
{
obon.ticcount -= SHIELDANM;
if (++obon.stage == 2)
obon.stage = 0;
obon.shapenum = SHIELD1PIC + obon.stage;
}
TransformObon();
}