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