Files
Catacomb3D/C3_WIZ.C
2026-03-12 19:22:23 +01:00

2047 lines
36 KiB
C

/* Catacomb 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.
*/
// C3_WIZ.C
#include "C3_DEF.H"
#pragma hdrstop
/*
=============================================================================
LOCAL CONSTANTS
=============================================================================
*/
#define NUMSCROLLS 8
#define SHOWITEMS 9
#define NUKETIME 40
#define NUMBOLTS 10
#define BOLTTICS 6
#define STATUSCOLOR 4
#define TEXTCOLOR 14
#define SIDEBARWIDTH 5
#define BODYLINE 8
#define POWERLINE 80
#define SPECTILESTART 18
#define SHOTDAMAGE 1
#define BIGSHOTDAMAGE 3
#define PLAYERSPEED 5120
#define RUNSPEED 8192
#define SHOTSPEED 10000
#define LASTWALLTILE 17
#define LASTSPECIALTILE 37
#define FIRETIME 4 // DEBUG 60
#define HANDPAUSE 60
#define COMPASSX 33
#define COMPASSY 0
/*
=============================================================================
GLOBAL VARIABLES
=============================================================================
*/
long lastnuke,lasthand;
int handheight;
int boltsleft;
/*
=============================================================================
LOCAL VARIABLES
=============================================================================
*/
int lasttext,lastcompass;
int bolttimer;
unsigned lastfiretime;
int strafeangle[9] = {0,90,180,270,45,135,225,315,0};
//===========================================================================
void DrawChar (unsigned x, unsigned y, unsigned tile);
void RedrawStatusWindow (void);
void GiveBolt (void);
void TakeBolt (void);
void GiveNuke (void);
void TakeNuke (void);
void GivePotion (void);
void TakePotion (void);
void GiveKey (int keytype);
void TakeKey (int keytype);
void GiveScroll (int scrolltype,boolean show);
void ReadScroll (int scroll);
void GivePoints (int points);
void DrawLevelNumber (int number);
void DrawText (void);
void DrawBars (void);
//----------
void Shoot (void);
void BigShoot (void);
void CastBolt (void);
void CastNuke (void);
void DrinkPotion (void);
//----------
void SpawnPlayer (int tilex, int tiley, int dir);
void Thrust (int angle, unsigned speed);
void T_Player (objtype *ob);
void AddPoints (int points);
void ClipMove (objtype *ob, long xmove, long ymove);
boolean ShotClipMove (objtype *ob, long xmove, long ymove);
//===========================================================================
/*
===============
=
= DrawChar
=
===============
*/
void DrawChar (unsigned x, unsigned y, unsigned tile)
{
unsigned junk = latchpics[0];
EGAWRITEMODE(1);
asm mov bx,[y]
asm shl bx,1
asm mov di,[WORD PTR ylookup+bx]
asm add di,[x]
asm mov si,[tile]
asm shl si,1
asm shl si,1
asm shl si,1
asm add si,[junk] // the damn inline assembler won't reference latchpics
asm mov ax,[screenseg]
asm mov es,ax
asm mov ds,ax
asm mov dx,SCREENWIDTH-1
asm movsb
asm add di,dx
asm movsb
asm add di,dx
asm movsb
asm add di,dx
asm movsb
asm add di,dx
asm movsb
asm add di,dx
asm movsb
asm add di,dx
asm movsb
asm add di,dx
asm movsb
asm mov ax,ss
asm mov ds,ax
EGAWRITEMODE(0);
}
//===========================================================================
/*
===============
=
= RedrawStatusWindow
=
===============
*/
void RedrawStatusWindow (void)
{
int i,j,x;
DrawLevelNumber (gamestate.mapon);
lasttext = -1;
lastcompass = -1;
j = gamestate.bolts < SHOWITEMS ? gamestate.bolts : SHOWITEMS;
for (i=0;i<j;i++)
DrawChar(7+i,20,BOLTCHAR);
j = gamestate.nukes < SHOWITEMS ? gamestate.nukes : SHOWITEMS;
for (i=0;i<j;i++)
DrawChar(7+i,30,NUKECHAR);
j = gamestate.potions < SHOWITEMS ? gamestate.potions : SHOWITEMS;
for (i=0;i<j;i++)
DrawChar(7+i,40,POTIONCHAR);
x=24;
for (i=0;i<4;i++)
for (j=0;j<gamestate.keys[i];j++)
DrawChar(x++,20,KEYCHARS+i);
x=24;
for (i=0;i<8;i++)
if (gamestate.scrolls[i])
DrawChar(x++,30,SCROLLCHARS+i);
AddPoints(0);
DrawBars ();
}
//===========================================================================
/*
===============
=
= GiveBolt
=
===============
*/
void GiveBolt (void)
{
SD_PlaySound (GETBOLTSND);
if (++gamestate.bolts<=9)
DrawChar(6+gamestate.bolts,20,BOLTCHAR);
}
/*
===============
=
= TakeBolt
=
===============
*/
void TakeBolt (void)
{
SD_PlaySound (USEBOLTSND);
if (--gamestate.bolts<=9)
DrawChar(7+gamestate.bolts,20,BLANKCHAR);
}
//===========================================================================
/*
===============
=
= GiveNuke
=
===============
*/
void GiveNuke (void)
{
SD_PlaySound (GETNUKESND);
if (++gamestate.nukes<=9)
DrawChar(6+gamestate.nukes,30,NUKECHAR);
}
/*
===============
=
= TakeNuke
=
===============
*/
void TakeNuke (void)
{
SD_PlaySound (USENUKESND);
if (--gamestate.nukes<=9)
DrawChar(7+gamestate.nukes,30,BLANKCHAR);
}
//===========================================================================
/*
===============
=
= GivePotion
=
===============
*/
void GivePotion (void)
{
SD_PlaySound (GETPOTIONSND);
if (++gamestate.potions<=9)
DrawChar(6+gamestate.potions,40,POTIONCHAR);
}
/*
===============
=
= TakePotion
=
===============
*/
void TakePotion (void)
{
SD_PlaySound (USEPOTIONSND);
if (--gamestate.potions<=9)
DrawChar(7+gamestate.potions,40,BLANKCHAR);
}
//===========================================================================
/*
===============
=
= GiveKey
=
===============
*/
void GiveKey (int keytype)
{
int i,j,x;
SD_PlaySound (GETKEYSND);
gamestate.keys[keytype]++;
x=24;
for (i=0;i<4;i++)
for (j=0;j<gamestate.keys[i];j++)
DrawChar(x++,20,KEYCHARS+i);
}
/*
===============
=
= TakeKey
=
===============
*/
void TakeKey (int keytype)
{
int i,j,x;
SD_PlaySound (USEKEYSND);
gamestate.keys[keytype]--;
x=24;
for (i=0;i<4;i++)
for (j=0;j<gamestate.keys[i];j++)
DrawChar(x++,20,KEYCHARS+i);
DrawChar(x,20,BLANKCHAR);
}
//===========================================================================
/*
===============
=
= GiveScroll
=
===============
*/
void GiveScroll (int scrolltype,boolean show)
{
int i,x;
SD_PlaySound (GETSCROLLSND);
gamestate.scrolls[scrolltype] = true;
x=24;
for (i=0;i<8;i++)
if (gamestate.scrolls[i])
DrawChar(x++,30,SCROLLCHARS+i);
if (show)
ReadScroll(scrolltype);
}
//===========================================================================
/*
===============
=
= GivePoints
=
===============
*/
void GivePoints (int points)
{
pointcount = 1;
pointsleft += points;
}
//===========================================================================
/*
===============
=
= AddPoints
=
===============
*/
void AddPoints (int points)
{
char str[10];
int len,x,i;
gamestate.score += points;
ltoa (gamestate.score,str,10);
len = strlen (str);
x=24+(8-len);
for (i=0;i<len;i++)
DrawChar(x++,40,NUMBERCHARS+str[i]-'0');
}
//===========================================================================
/*
===============
=
= GiveChest
=
===============
*/
void GiveChest (void)
{
SD_PlaySound (GETPOINTSSND);
GivePoints ((gamestate.mapon+1)*100);
}
//===========================================================================
/*
===============
=
= GiveGoal
=
===============
*/
void GiveGoal (void)
{
SD_PlaySound (GETPOINTSSND);
GivePoints (100000);
playstate = ex_victorious;
}
//===========================================================================
/*
===============
=
= DrawLevelNumber
=
===============
*/
void DrawLevelNumber (int number)
{
char str[10];
int len;
unsigned temp;
bufferofs = 0;
if (number<9)
PrintX=13;
else
PrintX = 5;
PrintY = 4;
VW_Bar (5,4,16,9,STATUSCOLOR);
temp = fontcolor;
fontcolor = TEXTCOLOR^STATUSCOLOR;
US_PrintUnsigned (number+1);
fontcolor = temp;
}
//===========================================================================
/*
===============
=
= DrawText
=
===============
*/
void DrawText (void)
{
unsigned number;
char str[80];
char far *text;
unsigned temp;
//
// draw a new text description if needed
//
number = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex)-NAMESTART;
if ( number>26 )
number = 0;
if (number == lasttext)
return;
bufferofs = 0;
lasttext = number;
PrintY = 4;
WindowX = 26;
WindowW = 232;
text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number];
_fmemcpy (str,text,80);
VW_Bar (26,4,232,9,STATUSCOLOR);
temp = fontcolor;
fontcolor = TEXTCOLOR^STATUSCOLOR;
US_CPrintLine (str);
fontcolor = temp;
}
//===========================================================================
/*
===============
=
= DrawCompass
=
===============
*/
void DrawCompass (void)
{
int angle,number;
//
// draw the compass if needed
//
angle = player->angle-ANGLES/4;
angle -= ANGLES/32;
if (angle<0)
angle+=ANGLES;
number = angle/(ANGLES/16);
if (number>15) // because 360 angles doesn't divide by 16
number = 15;
if (number == lastcompass)
return;
lastcompass = number;
bufferofs = 0;
LatchDrawPic (COMPASSX,COMPASSY,COMPAS1PIC+15-number);
}
//===========================================================================
/*
===============
=
= DrawBars
=
===============
*/
void DrawBars (void)
{
int i;
unsigned source,dest,topline;
for (i=0;i<3;i++)
{
bufferofs = screenloc[i];
VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1);
}
EGAWRITEMODE(1);
asm mov es,[screenseg]
//
// shot power
//
if (gamestate.shotpower)
{
topline = MAXSHOTPOWER - gamestate.shotpower;
source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH;
dest = (POWERLINE+topline)*SCREENWIDTH+34;
asm mov si,[source]
asm mov di,[dest]
asm mov cx,[WORD PTR gamestate.shotpower]
newline:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline
}
//
// body
//
if (gamestate.body)
{
source = latchpics[BODYPIC-FIRSTLATCHPIC];
dest = BODYLINE*SCREENWIDTH+34;
asm mov si,[source]
asm mov di,[dest]
asm mov cx,[WORD PTR gamestate.body]
newline2:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline2
}
if (gamestate.body != MAXBODY)
{
source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH;
dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;
topline = MAXBODY-gamestate.body;
asm mov si,[source]
asm mov di,[dest]
asm mov cx,[WORD PTR topline]
newline3:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline3
}
EGAWRITEMODE(0);
}
/*
=============================================================================
SHOTS
=============================================================================
*/
void T_Pshot (objtype *ob);
extern statetype s_pshot1;
extern statetype s_pshot2;
extern statetype s_bigpshot1;
extern statetype s_bigpshot2;
statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2};
statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1};
statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL};
statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2};
statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1};
/*
===================
=
= SpawnPShot
=
===================
*/
void SpawnPShot (void)
{
SpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*14);
new->obclass = pshotobj;
new->speed = SHOTSPEED;
new->angle = player->angle;
}
void SpawnBigPShot (void)
{
SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);
new->obclass = bigpshotobj;
new->speed = SHOTSPEED;
new->angle = player->angle;
}
/*
===============
=
= T_Pshot
=
===============
*/
void T_Pshot (objtype *ob)
{
objtype *check;
long xmove,ymove,speed;
//
// check current position for monsters having moved into it
//
for (check = player->next; check; check=check->next)
if (check->shootable
&& ob->xl <= check->xh
&& ob->xh >= check->xl
&& ob->yl <= check->yh
&& ob->yh >= check->yl)
{
SD_PlaySound (SHOOTMONSTERSND);
if (ob->obclass == bigpshotobj)
ShootActor (check,BIGSHOTDAMAGE);
else
ShootActor (check,SHOTDAMAGE);
ob->state = &s_shotexplode;
ob->ticcount = ob->state->tictime;
return;
}
//
// move ahead, possibly hitting a wall
//
speed = ob->speed*tics;
xmove = FixedByFrac(speed,costable[ob->angle]);
ymove = -FixedByFrac(speed,sintable[ob->angle]);
if (ShotClipMove(ob,xmove,ymove))
{
ob->state = &s_shotexplode;
ob->ticcount = ob->state->tictime;
return;
}
ob->tilex = ob->x >> TILESHIFT;
ob->tiley = ob->y >> TILESHIFT;
//
// check final position for monsters hit
//
for (check = player->next; check; check=check->next)
if (ob->shootable
&& ob->xl <= check->xh
&& ob->xh >= check->xl
&& ob->yl <= check->yh
&& ob->yh >= check->yl)
{
ShootActor (check,SHOTDAMAGE);
ob->state = &s_shotexplode;
ob->ticcount = ob->state->tictime;
return;
}
}
/*
=============================================================================
PLAYER ACTIONS
=============================================================================
*/
/*
===============
=
= BuildShotPower
=
===============
*/
void BuildShotPower (void)
{
int newlines,topline;
long i;
unsigned source,dest;
if (gamestate.shotpower == MAXSHOTPOWER)
return;
newlines = 0;
for (i=lasttimecount-tics;i<lasttimecount;i++)
newlines += (i&1);
gamestate.shotpower += newlines;
if (gamestate.shotpower > MAXSHOTPOWER)
{
newlines -= (gamestate.shotpower - MAXSHOTPOWER);
gamestate.shotpower = MAXSHOTPOWER;
}
topline = MAXSHOTPOWER - gamestate.shotpower;
source = latchpics[L_SHOTBAR]+topline*SIDEBARWIDTH;
dest = (POWERLINE+topline)*SCREENWIDTH+34;
asm mov es,[screenseg]
asm mov si,[source]
asm mov di,[dest]
EGAWRITEMODE(1);
if (newlines)
{
asm mov cx,[newlines]
newline:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline
}
EGAWRITEMODE(0);
}
//===========================================================================
/*
===============
=
= ClearShotPower
=
===============
*/
void ClearShotPower (void)
{
unsigned source,dest,topline;
topline = MAXSHOTPOWER - gamestate.shotpower;
source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH;
dest = (POWERLINE+topline)*SCREENWIDTH+34;
asm mov es,[screenseg]
asm mov si,[source]
asm mov di,[dest]
if (!gamestate.shotpower)
return;
EGAWRITEMODE(1);
asm mov cx,[WORD PTR gamestate.shotpower]
newline:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline
EGAWRITEMODE(0);
gamestate.shotpower = 0;
}
//===========================================================================
/*
===============
=
= Shoot
=
===============
*/
void Shoot (void)
{
ClearShotPower ();
SD_PlaySound (SHOOTSND);
SpawnPShot ();
}
//===========================================================================
/*
===============
=
= BigShoot
=
===============
*/
void BigShoot (void)
{
ClearShotPower ();
SD_PlaySound (BIGSHOOTSND);
SpawnBigPShot ();
}
//===========================================================================
/*
===============
=
= CastBolt
=
===============
*/
void CastBolt (void)
{
if (!gamestate.bolts)
{
SD_PlaySound (NOITEMSND);
return;
}
TakeBolt ();
boltsleft = NUMBOLTS;
bolttimer = BOLTTICS;
BigShoot ();
}
/*
===============
=
= ContinueBolt
=
===============
*/
void ContinueBolt (void)
{
bolttimer-=tics;
if (bolttimer<0)
{
boltsleft--;
bolttimer = BOLTTICS;
BigShoot ();
}
}
//===========================================================================
/*
===============
=
= CastNuke
=
===============
*/
void CastNuke (void)
{
int angle;
if (!gamestate.nukes)
{
SD_PlaySound (NOITEMSND);
return;
}
TakeNuke ();
lastnuke = TimeCount;
for (angle = 0; angle < ANGLES; angle+= ANGLES/16)
{
SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);
new->obclass = bigpshotobj;
new->speed = SHOTSPEED;
new->angle = angle;
}
}
//===========================================================================
/*
===============
=
= DrinkPotion
=
===============
*/
void DrinkPotion (void)
{
unsigned source,dest,topline;
if (!gamestate.potions)
{
SD_PlaySound (NOITEMSND);
return;
}
TakePotion ();
gamestate.body = MAXBODY;
//
// draw a full up bar
//
source = latchpics[L_BODYBAR];
dest = BODYLINE*SCREENWIDTH+34;
asm mov es,[screenseg]
asm mov si,[source]
asm mov di,[dest]
EGAWRITEMODE(1);
asm mov cx,MAXBODY
newline:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline
EGAWRITEMODE(0);
}
//===========================================================================
/*
===============
=
= ReadScroll
=
===============
*/
extern boolean tileneeded[NUMFLOORS];
void ReadScroll (int scroll)
{
int i;
//
// make wall pictures purgable
//
for (i=0;i<NUMSCALEWALLS;i++)
if (walldirectory[i])
MM_SetPurge (&(memptr)walldirectory[i],3);
CA_CacheGrChunk (SCROLLTOPPIC);
CA_CacheGrChunk (SCROLL1PIC + scroll);
VW_DrawPic (0,0,SCROLLTOPPIC);
VW_DrawPic (0,32,SCROLL1PIC + scroll);
UNMARKGRCHUNK(SCROLL1PIC + scroll);
UNMARKGRCHUNK(SCROLLTOPPIC);
MM_FreePtr (&grsegs[SCROLL1PIC + scroll]);
MM_FreePtr (&grsegs[SCROLLTOPPIC]);
//
// cache wall pictures back in
//
for (i=1;i<NUMFLOORS;i++)
if (tileneeded[i])
{
SetupScaleWall (walllight1[i]);
SetupScaleWall (walllight2[i]);
SetupScaleWall (walldark1[i]);
SetupScaleWall (walldark2[i]);
}
VW_WaitVBL(80);
waitkey:
IN_ClearKeysDown ();
IN_Ack();
}
/*
===============
=
= TakeDamage
=
===============
*/
void TakeDamage (int points)
{
unsigned source,dest,topline;
if (!gamestate.body || bordertime || godmode)
return;
if (points >= gamestate.body)
{
points = gamestate.body;
playstate = ex_died;
}
bordertime = points*FLASHTICS;
VW_ColorBorder (FLASHCOLOR);
if (gamestate.body<MAXBODY/3)
SD_PlaySound (TAKEDMGHURTSND);
else
SD_PlaySound (TAKEDAMAGESND);
gamestate.body -= points;
//
// shrink the body bar
//
source = latchpics[L_NOBODY]+gamestate.body*SIDEBARWIDTH;
dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;
asm mov es,[screenseg]
asm mov si,[source]
asm mov di,[dest]
EGAWRITEMODE(1);
asm mov cx,[points]
newline:
asm mov al,[es:si]
asm mov [es:di+PAGE1START],al
asm mov [es:di+PAGE2START],al
asm mov [es:di+PAGE3START],al
asm mov al,[es:si+1]
asm mov [es:di+1+PAGE1START],al
asm mov [es:di+1+PAGE2START],al
asm mov [es:di+1+PAGE3START],al
asm mov al,[es:si+2]
asm mov [es:di+2+PAGE1START],al
asm mov [es:di+2+PAGE2START],al
asm mov [es:di+2+PAGE3START],al
asm mov al,[es:si+3]
asm mov [es:di+3+PAGE1START],al
asm mov [es:di+3+PAGE2START],al
asm mov [es:di+3+PAGE3START],al
asm mov al,[es:si+4]
asm mov [es:di+4+PAGE1START],al
asm mov [es:di+4+PAGE2START],al
asm mov [es:di+4+PAGE3START],al
asm add di,SCREENWIDTH
asm add si,5
asm loop newline
EGAWRITEMODE(0);
}
/*
=============================================================================
INTERACTION
=============================================================================
*/
/*
==================
=
= OpenDoor
=
==================
*/
void OpenDoor (unsigned bx, unsigned by, unsigned doorbase)
{
int x,y;
unsigned far *map;
x=bx;
y=by;
map = mapsegs[0]+farmapylookup[y]+x;
while (tilemap[x][y]-doorbase<4)
{
tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
map--;
x--;
}
x=bx+1;
map = mapsegs[0]+farmapylookup[y]+x;
while (tilemap[x][y]-doorbase<4)
{
tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
map++;
x++;
}
x=bx;
y=by-1;
map = mapsegs[0]+farmapylookup[y]+x;
while (tilemap[x][y]-doorbase<4)
{
tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
map-=mapwidth;
y--;
}
y=by+1;
map = mapsegs[0]+farmapylookup[y]+x;
while (tilemap[x][y]-doorbase<4)
{
tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;
map+=mapwidth;
y++;
}
}
/*
==================
=
= HitSpecialTile
=
= Returns true if the move is blocked
=
==================
*/
boolean HitSpecialTile (unsigned x, unsigned y, unsigned tile)
{
switch (tile)
{
case 0:
case 1:
case 2:
case 3:
if (!gamestate.keys[0])
return true;
TakeKey(0);
OpenDoor (x,y,SPECTILESTART+0);
return false;
case 4:
case 5:
case 6:
case 7:
if (!gamestate.keys[1])
return true;
TakeKey(1);
OpenDoor (x,y,SPECTILESTART+4);
return false;
case 8:
case 9:
case 10:
case 11:
if (!gamestate.keys[2])
return true;
TakeKey(2);
OpenDoor (x,y,SPECTILESTART+8);
return false;
case 12:
case 13:
case 14:
case 15:
if (!gamestate.keys[3])
return true;
TakeKey(3);
OpenDoor (x,y,SPECTILESTART+12);
return false;
}
return true;
}
/*
==================
=
= TouchActor
=
= Returns true if the move is blocked
=
==================
*/
boolean TouchActor (objtype *ob, objtype *check)
{
if (ob->xh < check->xl || ob->xl > check->xh ||
ob->yh < check->yl || ob->yl > check->yh)
return false; // not quite touching
switch (check->obclass)
{
case bonusobj:
if (check->temp1 == B_BOLT)
GiveBolt ();
else if (check->temp1 == B_NUKE)
GiveNuke ();
else if (check->temp1 == B_POTION)
GivePotion ();
else if (check->temp1 >= B_RKEY && check->temp1 <= B_BKEY)
GiveKey (check->temp1-B_RKEY);
else if (check->temp1 >= B_SCROLL1 && check->temp1 <= B_SCROLL8)
GiveScroll (check->temp1-B_SCROLL1,true);
else if (check->temp1 == B_CHEST)
GiveChest ();
else if (check->temp1 == B_GOAL)
GiveGoal ();
(unsigned)actorat[check->tilex][check->tiley] = 0;
RemoveObj (check);
return false;
}
return true;
}
/*
==================
=
= CalcBounds
=
==================
*/
void CalcBounds (objtype *ob)
{
//
// calculate hit rect
//
ob->xl = ob->x - ob->size;
ob->xh = ob->x + ob->size;
ob->yl = ob->y - ob->size;
ob->yh = ob->y + ob->size;
}
/*
===================
=
= LocationInActor
=
===================
*/
boolean LocationInActor (objtype *ob)
{
int x,y,xmin,ymin,xmax,ymax;
objtype *check;
CalcBounds (ob);
xmin = (ob->x >> TILESHIFT)-2;
ymin = (ob->y >> TILESHIFT)-2;
xmax = xmin+5;
ymax = ymin+5;
for (x=xmin;x<xmax;x++)
for (y=ymin;y<ymax;y++)
{
check = actorat[x][y];
if (check>(objtype *)LASTSPECIALTILE
&& check->shootable
&& ob->xl <= check->xh
&& ob->xh >= check->xl
&& ob->yl <= check->yh
&& ob->yh >= check->yl)
return true;
}
return false;
}
/*
===================
=
= ClipMove
=
= Only checks corners, so the object better be less than one tile wide!
=
===================
*/
void ClipMove (objtype *ob, long xmove, long ymove)
{
int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
long intersect,basex,basey,pointx,pointy;
unsigned inside,total,tile;
objtype *check;
boolean moveok;
//
// move player and check to see if any corners are in solid tiles
//
basex = ob->x;
basey = ob->y;
ob->x += xmove;
ob->y += ymove;
CalcBounds (ob);
xl = ob->xl>>TILESHIFT;
yl = ob->yl>>TILESHIFT;
xh = ob->xh>>TILESHIFT;
yh = ob->yh>>TILESHIFT;
for (y=yl;y<=yh;y++)
for (x=xl;x<=xh;x++)
{
check = actorat[x][y];
if (!check)
continue; // blank floor, walk ok
if ((unsigned)check<=LASTWALLTILE)
goto blockmove; // solid wall
if ((unsigned)check<=LASTSPECIALTILE)
{
if ( HitSpecialTile (x,y,(unsigned)check-SPECTILESTART) )
goto blockmove; // whatever it was, it blocked the move
else
continue;
}
TouchActor(ob,check); // pick up items
}
//
// check nearby actors
//
if (LocationInActor(ob))
{
ob->x -= xmove;
if (LocationInActor(ob))
{
ob->x += xmove;
ob->y -= ymove;
if (LocationInActor(ob))
ob->x -= xmove;
}
}
return; // move is OK!
blockmove:
if (!SD_SoundPlaying())
SD_PlaySound (HITWALLSND);
moveok = false;
do
{
xmove /= 2;
ymove /= 2;
if (moveok)
{
ob->x += xmove;
ob->y += ymove;
}
else
{
ob->x -= xmove;
ob->y -= ymove;
}
CalcBounds (ob);
xl = ob->xl>>TILESHIFT;
yl = ob->yl>>TILESHIFT;
xh = ob->xh>>TILESHIFT;
yh = ob->yh>>TILESHIFT;
if (tilemap[xl][yl] || tilemap[xh][yl]
|| tilemap[xh][yh] || tilemap[xl][yh] )
{
moveok = false;
if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
{
ob->x = basex;
ob->y = basey;
return;
}
}
else
{
if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
return;
moveok = true;
}
} while (1);
}
//==========================================================================
/*
===================
=
= ShotClipMove
=
= Only checks corners, so the object better be less than one tile wide!
=
===================
*/
boolean ShotClipMove (objtype *ob, long xmove, long ymove)
{
int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;
long intersect,basex,basey,pointx,pointy;
unsigned inside,total,tile;
objtype *check;
boolean moveok;
//
// move shot and check to see if any corners are in solid tiles
//
basex = ob->x;
basey = ob->y;
ob->x += xmove;
ob->y += ymove;
CalcBounds (ob);
xl = ob->xl>>TILESHIFT;
yl = ob->yl>>TILESHIFT;
xh = ob->xh>>TILESHIFT;
yh = ob->yh>>TILESHIFT;
for (y=yl;y<=yh;y++)
for (x=xl;x<=xh;x++)
{
tile = tilemap[x][y];
if (tile)
{
if ((unsigned)(tile-EXPWALLSTART)<NUMEXPWALLS)
ExplodeWall (x,y);
goto blockmove;
}
}
return false; // move is OK!
blockmove:
SD_PlaySound (SHOOTWALLSND);
moveok = false;
do
{
xmove /= 2;
ymove /= 2;
if (moveok)
{
ob->x += xmove;
ob->y += ymove;
}
else
{
ob->x -= xmove;
ob->y -= ymove;
}
CalcBounds (ob);
xl = ob->xl>>TILESHIFT;
yl = ob->yl>>TILESHIFT;
xh = ob->xh>>TILESHIFT;
yh = ob->yh>>TILESHIFT;
if (tilemap[xl][yl] || tilemap[xh][yl]
|| tilemap[xh][yh] || tilemap[xl][yh] )
{
moveok = false;
if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
{
ob->x = basex;
ob->y = basey;
return true;
}
}
else
{
if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)
return true;
moveok = true;
}
} while (1);
}
/*
=============================================================================
PLAYER CONTROL
=============================================================================
*/
void T_Player (objtype *ob);
statetype s_player = {0,0,&T_Player,&s_player};
/*
===============
=
= SpawnPlayer
=
===============
*/
void SpawnPlayer (int tilex, int tiley, int dir)
{
player->obclass = playerobj;
player->active = true;
player->tilex = tilex;
player->tiley = tiley;
player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
player->state = &s_player;
player->angle = (1-dir)*90;
player->size = MINDIST;
CalcBounds (player);
if (player->angle<0)
player->angle += ANGLES;
}
/*
===================
=
= Thrust
=
===================
*/
void Thrust (int angle, unsigned speed)
{
long xmove,ymove;
if (lasttimecount>>5 != ((lasttimecount-tics)>>5) )
{
//
// walk sound
//
if (lasttimecount&32)
SD_PlaySound (WALK1SND);
else
SD_PlaySound (WALK2SND);
}
xmove = FixedByFrac(speed,costable[angle]);
ymove = -FixedByFrac(speed,sintable[angle]);
ClipMove(player,xmove,ymove);
player->tilex = player->x >> TILESHIFT;
player->tiley = player->y >> TILESHIFT;
}
/*
=======================
=
= ControlMovement
=
=======================
*/
void ControlMovement (objtype *ob)
{
int angle;
long speed;
if (c.button1)
{
//
// strafing
//
//
// side to side move
//
if (!mousexmove)
speed = 0;
else if (mousexmove<0)
speed = -(long)mousexmove*300;
else
speed = -(long)mousexmove*300;
if (c.xaxis == -1)
{
if (running)
speed += RUNSPEED*tics;
else
speed += PLAYERSPEED*tics;
}
else if (c.xaxis == 1)
{
if (running)
speed -= RUNSPEED*tics;
else
speed -= PLAYERSPEED*tics;
}
if (speed > 0)
{
if (speed >= TILEGLOBAL)
speed = TILEGLOBAL-1;
angle = ob->angle + ANGLES/4;
if (angle >= ANGLES)
angle -= ANGLES;
Thrust (angle,speed); // move to left
}
else if (speed < 0)
{
if (speed <= -TILEGLOBAL)
speed = -TILEGLOBAL+1;
angle = ob->angle - ANGLES/4;
if (angle < 0)
angle += ANGLES;
Thrust (angle,-speed); // move to right
}
}
else
{
//
// not strafing
//
//
// turning
//
if (c.xaxis == 1)
{
ob->angle -= tics;
if (running) // fast turn
ob->angle -= tics;
}
else if (c.xaxis == -1)
{
ob->angle+= tics;
if (running) // fast turn
ob->angle += tics;
}
ob->angle -= (mousexmove/10);
if (ob->angle >= ANGLES)
ob->angle -= ANGLES;
if (ob->angle < 0)
ob->angle += ANGLES;
}
//
// forward/backwards move
//
if (!mouseymove)
speed = 0;
else if (mouseymove<0)
speed = -(long)mouseymove*500;
else
speed = -(long)mouseymove*200;
if (c.yaxis == -1)
{
if (running)
speed += RUNSPEED*tics;
else
speed += PLAYERSPEED*tics;
}
else if (c.yaxis == 1)
{
if (running)
speed -= RUNSPEED*tics;
else
speed -= PLAYERSPEED*tics;
}
if (speed > 0)
{
if (speed >= TILEGLOBAL)
speed = TILEGLOBAL-1;
Thrust (ob->angle,speed); // move forwards
}
else if (speed < 0)
{
if (speed <= -TILEGLOBAL)
speed = -TILEGLOBAL+1;
angle = ob->angle + ANGLES/2;
if (angle >= ANGLES)
angle -= ANGLES;
Thrust (angle,-speed); // move backwards
}
}
/*
===============
=
= T_Player
=
===============
*/
void T_Player (objtype *ob)
{
int angle,speed,scroll;
unsigned text,tilex,tiley;
long lspeed;
ControlMovement (ob);
//
// firing
//
if (boltsleft)
{
handheight+=(tics<<2);
if (handheight>MAXHANDHEIGHT)
handheight = MAXHANDHEIGHT;
ContinueBolt ();
lasthand = lasttimecount;
}
else
{
if (c.button0)
{
handheight+=(tics<<2);
if (handheight>MAXHANDHEIGHT)
handheight = MAXHANDHEIGHT;
if ((unsigned)TimeCount/FIRETIME != lastfiretime)
BuildShotPower ();
lasthand = lasttimecount;
}
else
{
if (lasttimecount > lasthand+HANDPAUSE)
{
handheight-=(tics<<1);
if (handheight<0)
handheight = 0;
}
if (gamestate.shotpower == MAXSHOTPOWER)
{
lastfiretime = (unsigned)TimeCount/FIRETIME;
BigShoot ();
}
else if (gamestate.shotpower)
{
lastfiretime = (unsigned)TimeCount/FIRETIME;
Shoot ();
}
}
}
//
// special actions
//
if ( (Keyboard[sc_Space] || Keyboard[sc_H]) && gamestate.body != MAXBODY)
DrinkPotion ();
if (Keyboard[sc_B] && !boltsleft)
CastBolt ();
if ( (Keyboard[sc_Enter] || Keyboard[sc_N]) && TimeCount-lastnuke > NUKETIME)
CastNuke ();
scroll = LastScan-2;
if ( scroll>=0 && scroll<NUMSCROLLS && gamestate.scrolls[scroll])
ReadScroll (scroll);
DrawText ();
DrawCompass ();
}