From 52fab2ff9ef6a39ed9303b0df1ce0ad9c9180ef1 Mon Sep 17 00:00:00 2001 From: sparky4 Date: Fri, 24 Mar 2017 12:34:44 -0500 Subject: [PATCH] [16_ca needs huge amounts of work and I should remember what needs to be done soon][OpenVGMFile needs to be ported to 16_snd.c]going to port rest of code to borland c some time so we can use the core components of id engine here [going to add 16_us.c eventually but the debug system and CA_ PM_ and MM_ usage is priority now] --- {src => 16/src}/lib/16_in_o.c | 0 {src => 16/src}/lib/16_in_o.h | 0 _pm_use.txt | 6 +- makefile | 4 +- src/16.c | 6 +- src/lib/16_dbg.c | 6 +- src/lib/16_dbg.h | 1 + src/lib/16_pm.c | 9 +- src/lib/16_tail.c | 62 +- src/lib/16_tdef.h | 6 +- src/lib/16_vl.h | 3 + src/lib/hb/c3_act1.c | 1259 +++++++++++ src/lib/hb/c3_asm.asm | 197 ++ src/lib/hb/c3_debug.c | 606 +++++ src/lib/hb/c3_def.h | 533 +++++ src/lib/hb/c3_draw.c | 1592 +++++++++++++ src/lib/hb/c3_game.c | 1199 ++++++++++ src/lib/hb/c3_main.c | 756 +++++++ src/lib/hb/c3_play.c | 584 +++++ src/lib/hb/c3_sca_a.asm | 153 ++ src/lib/hb/c3_scale.c | 690 ++++++ src/lib/hb/c3_state.c | 546 +++++ src/lib/hb/c3_trace.c | 872 ++++++++ src/lib/hb/c3_wiz.c | 2046 +++++++++++++++++ src/lib/hb/c6_act1.c | 930 ++++++++ src/lib/hb/c6_act2.c | 891 ++++++++ src/lib/hb/c6_act3.c | 802 +++++++ src/lib/hb/c6_act4.c | 260 +++ src/lib/hb/c6_asm.asm | 248 ++ src/lib/hb/c6_debug.c | 799 +++++++ src/lib/hb/c6_draw.c | 1970 ++++++++++++++++ src/lib/hb/c6_game.c | 1599 +++++++++++++ src/lib/hb/c6_main.c | 1083 +++++++++ src/lib/hb/c6_play.c | 1429 ++++++++++++ src/lib/hb/c6_sca_a.asm | 153 ++ src/lib/hb/c6_scale.c | 697 ++++++ src/lib/hb/c6_state.c | 736 ++++++ src/lib/hb/c6_trace.c | 872 ++++++++ src/lib/hb/c6_wiz.c | 3349 +++++++++++++++++++++++++++ src/lib/hb/demokd.c | 126 ++ src/lib/hb/demowl.c | 170 ++ src/lib/hb/kd_act1.c | 1130 ++++++++++ src/lib/hb/kd_act2.c | 1509 +++++++++++++ src/lib/hb/kd_def.h | 366 +++ src/lib/hb/kd_demo.c | 545 +++++ src/lib/hb/kd_keen.c | 2528 +++++++++++++++++++++ src/lib/hb/kd_main.c | 533 +++++ src/lib/hb/kd_play.c | 1928 ++++++++++++++++ src/lib/hb/wl_act1.c | 900 ++++++++ src/lib/hb/wl_act2.c | 3872 ++++++++++++++++++++++++++++++++ src/lib/hb/wl_agent.c | 1421 ++++++++++++ src/lib/hb/wl_asm.asm | 69 + src/lib/{ => hb}/wl_debug.c | 0 src/lib/hb/wl_def.h | 1276 +++++++++++ src/lib/hb/wl_dr_a.asm | 795 +++++++ src/lib/hb/wl_draw.c | 1419 ++++++++++++ src/lib/hb/wl_game.c | 1484 ++++++++++++ src/lib/hb/wl_inter.c | 1718 ++++++++++++++ src/lib/hb/wl_main.c | 1616 +++++++++++++ src/lib/hb/wl_menu.c | 3986 +++++++++++++++++++++++++++++++++ src/lib/hb/wl_menu.h | 234 ++ src/lib/hb/wl_play.c | 1473 ++++++++++++ src/lib/hb/wl_scale.c | 741 ++++++ src/lib/hb/wl_state.c | 1480 ++++++++++++ src/lib/hb/wl_text.c | 859 +++++++ 65 files changed, 59083 insertions(+), 49 deletions(-) rename {src => 16/src}/lib/16_in_o.c (100%) rename {src => 16/src}/lib/16_in_o.h (100%) create mode 100755 src/lib/hb/c3_act1.c create mode 100755 src/lib/hb/c3_asm.asm create mode 100755 src/lib/hb/c3_debug.c create mode 100755 src/lib/hb/c3_def.h create mode 100755 src/lib/hb/c3_draw.c create mode 100755 src/lib/hb/c3_game.c create mode 100755 src/lib/hb/c3_main.c create mode 100755 src/lib/hb/c3_play.c create mode 100755 src/lib/hb/c3_sca_a.asm create mode 100755 src/lib/hb/c3_scale.c create mode 100755 src/lib/hb/c3_state.c create mode 100755 src/lib/hb/c3_trace.c create mode 100755 src/lib/hb/c3_wiz.c create mode 100755 src/lib/hb/c6_act1.c create mode 100755 src/lib/hb/c6_act2.c create mode 100755 src/lib/hb/c6_act3.c create mode 100755 src/lib/hb/c6_act4.c create mode 100755 src/lib/hb/c6_asm.asm create mode 100755 src/lib/hb/c6_debug.c create mode 100755 src/lib/hb/c6_draw.c create mode 100755 src/lib/hb/c6_game.c create mode 100755 src/lib/hb/c6_main.c create mode 100755 src/lib/hb/c6_play.c create mode 100755 src/lib/hb/c6_sca_a.asm create mode 100755 src/lib/hb/c6_scale.c create mode 100755 src/lib/hb/c6_state.c create mode 100755 src/lib/hb/c6_trace.c create mode 100755 src/lib/hb/c6_wiz.c create mode 100755 src/lib/hb/demokd.c create mode 100755 src/lib/hb/demowl.c create mode 100755 src/lib/hb/kd_act1.c create mode 100755 src/lib/hb/kd_act2.c create mode 100755 src/lib/hb/kd_def.h create mode 100755 src/lib/hb/kd_demo.c create mode 100755 src/lib/hb/kd_keen.c create mode 100755 src/lib/hb/kd_main.c create mode 100755 src/lib/hb/kd_play.c create mode 100755 src/lib/hb/wl_act1.c create mode 100755 src/lib/hb/wl_act2.c create mode 100755 src/lib/hb/wl_agent.c create mode 100755 src/lib/hb/wl_asm.asm rename src/lib/{ => hb}/wl_debug.c (100%) create mode 100755 src/lib/hb/wl_def.h create mode 100755 src/lib/hb/wl_dr_a.asm create mode 100755 src/lib/hb/wl_draw.c create mode 100755 src/lib/hb/wl_game.c create mode 100755 src/lib/hb/wl_inter.c create mode 100755 src/lib/hb/wl_main.c create mode 100755 src/lib/hb/wl_menu.c create mode 100755 src/lib/hb/wl_menu.h create mode 100755 src/lib/hb/wl_play.c create mode 100755 src/lib/hb/wl_scale.c create mode 100755 src/lib/hb/wl_state.c create mode 100755 src/lib/hb/wl_text.c diff --git a/src/lib/16_in_o.c b/16/src/lib/16_in_o.c similarity index 100% rename from src/lib/16_in_o.c rename to 16/src/lib/16_in_o.c diff --git a/src/lib/16_in_o.h b/16/src/lib/16_in_o.h similarity index 100% rename from src/lib/16_in_o.h rename to 16/src/lib/16_in_o.h diff --git a/_pm_use.txt b/_pm_use.txt index 934d267f..43ba6c4a 100755 --- a/_pm_use.txt +++ b/_pm_use.txt @@ -31,7 +31,11 @@ 16/wf3d8086/wl_game.c: PM_CheckMainMem (); 16/wf3d8086/wl_game.c: PM_CheckMainMem (); -16/wf3d8086/wl_inter.c: PM_Preload (PreloadUpdate); + + + 16/wf3d8086/wl_inter.c: PM_Preload (PreloadUpdate); //related to chunksinfile + + 16/wf3d8086/wl_main.c: PM_Shutdown (); 16/wf3d8086/wl_main.c: PM_Startup (); diff --git a/makefile b/makefile index b7a7a580..ea59beda 100755 --- a/makefile +++ b/makefile @@ -34,7 +34,7 @@ # comment this out on game release. # serial output goes to COM1 at 9600 baud 1 stop bit odd parity. # serial output is plain text ASCII. -DEBUGSERIAL=0 +DEBUGSERIAL=1 DELLOGFILE=1 @@ -357,7 +357,7 @@ mapread.$(OBJ):$(SRCLIB)/mapread.c $(SRCLIB)/mapread.h 16_map.$(OBJ):$(SRCLIB)/16_map.c $(SRCLIB)/16_map.h 16_timer.$(OBJ):$(SRCLIB)/16_timer.c $(SRCLIB)/16_timer.h 16_in.$(OBJ): $(SRCLIB)/16_in.c $(SRCLIB)/16_in.h -16_in_1.$(OBJ): $(SRCLIB)/16_in_1.c $(SRCLIB)/16_in_1.h +#16_in_1.$(OBJ): $(SRCLIB)/16_in_1.c $(SRCLIB)/16_in_1.h 16_rf.$(OBJ): $(SRCLIB)/16_rf.c $(SRCLIB)/16_rf.h 16_mm.$(OBJ): $(SRCLIB)/16_mm.c $(SRCLIB)/16_mm.h 16_pm.$(OBJ): $(SRCLIB)/16_pm.c $(SRCLIB)/16_pm.h diff --git a/src/16.c b/src/16.c index af711c37..e87446c6 100755 --- a/src/16.c +++ b/src/16.c @@ -28,8 +28,6 @@ main(int argc, char *argv[]) static global_game_variables_t gvar; Startup16(&gvar); - gvar.engi_stat = ENGI_RUN; - /* save the palette */ modexPalSave(gvar.video.dpal); modexFadeOff(4, gvar.video.dpal); @@ -39,10 +37,10 @@ main(int argc, char *argv[]) // modexPalBlack(); //so player will not see loadings~ IN_Default(0,&gvar.player[0],ctrl_Joystick, &gvar); //modexprint(&screen, 32, 32, 1, 2, 0, "a", 1); - while(ENGI_QUIT != gvar.engi_stat) + while(1) { IN_ReadControl(&gvar.player[0], &gvar); - if(IN_KeyDown(sc_Escape)) gvar.engi_stat = ENGI_QUIT; + if(IN_KeyDown(sc_Escape)) break; shinku(&gvar); _DEBUGF("Serial debug output printf test %u %u %u\n",1U,2U,3U); } diff --git a/src/lib/16_dbg.c b/src/lib/16_dbg.c index db8f71a1..9878998b 100755 --- a/src/lib/16_dbg.c +++ b/src/lib/16_dbg.c @@ -257,12 +257,10 @@ static char buf[10]; // VW_UpdateScreen(); while (!(scan = gvar->in.inst->LastScan)) - { - } -// scan = *IN_GetScanName(scan); + {} // SD_Poll(); -if(IN_KeyDown(sc_Escape)) break; +//if(IN_KeyDown(sc_Escape)) break; IN_ClearKey(scan); switch (scan) diff --git a/src/lib/16_dbg.h b/src/lib/16_dbg.h index 3d7ef4af..3be65fa6 100755 --- a/src/lib/16_dbg.h +++ b/src/lib/16_dbg.h @@ -9,6 +9,7 @@ #define __DEBUG__ #define __DEBUG_InputMgr__ #define __DEBUG_MAP__ +//#define __DEBUG_2__ //#define __DEBUG_CA__ //#define __DEBUG_PM__ //#define __DEBUG_MM__ diff --git a/src/lib/16_pm.c b/src/lib/16_pm.c index 4d9ac9d8..9fabd9be 100755 --- a/src/lib/16_pm.c +++ b/src/lib/16_pm.c @@ -1207,7 +1207,7 @@ PM_GetPage(int pagenum, global_game_variables_t *gvar) if (pagenum >= gvar->pm.fi.ChunksInFile) Quit (gvar, "PM_GetPage: Invalid page request"); -#ifdef __DEBUG_2__ // for debugging +//#ifdef __DEBUG_2__ // for debugging __asm { mov dx,STATUS_REGISTER_1 in al,dx @@ -1217,7 +1217,7 @@ PM_GetPage(int pagenum, global_game_variables_t *gvar) mov al,10 // bright green out dx,al } -#endif +//#endif if (!(result = PM_GetPageAddress(pagenum, gvar))) { @@ -1235,7 +1235,7 @@ if (!gvar->pm.PMPages[pagenum].offset) // JDC: sparse page } gvar->pm.PMPages[pagenum].lastHit = gvar->pm.PMFrameCount; -#ifdef __DEBUG_2__ // for debugging +//#ifdef __DEBUG_2__ // for debugging __asm{ mov dx,STATUS_REGISTER_1 in al,dx @@ -1246,7 +1246,8 @@ if (!gvar->pm.PMPages[pagenum].offset) // JDC: sparse page out dx,al mov al,0x20 // normal out dx,al -#endif + } +//#endif return(result); } diff --git a/src/lib/16_tail.c b/src/lib/16_tail.c index 7b5fdcb4..3b1cb42c 100755 --- a/src/lib/16_tail.c +++ b/src/lib/16_tail.c @@ -394,37 +394,37 @@ char global_temp_status_text2[512]; void turboXT(byte bakapee) { __asm { - push ax - push bx - push cx - in al, 61h //; Read equipment flags - xor al, bakapee //; toggle speed - out 61h, al //; Write new flags back - - mov bx, 0F89h //; low pitch blip - and al, 4 //; Is turbo mode set? - jz @@do_beep - mov bx, 52Eh //; high pitch blip - -@@do_beep: - mov al, 10110110b //; Timer IC 8253 square waves - out 43h, al //; channel 2, speaker - mov ax, bx - out 42h, al //; send low order - mov al, ah //; load high order - out 42h, al //; send high order - in al, 61h //; Read IC 8255 machine status - push ax - or al, 00000011b - out 61h, al //; Turn speaker on - mov cx, 2000h -@@delay: - loop @@delay - pop ax - out 61h, al //; Turn speaker off - pop cx - pop bx - pop ax + push ax + push bx + push cx + in al, 61h //; Read equipment flags + xor al, bakapee //; toggle speed + out 61h, al //; Write new flags back + + mov bx, 0F89h //; low pitch blip + and al, 4 //; Is turbo mode set? + jz @@do_beep + mov bx, 52Eh //; high pitch blip + + @@do_beep: + mov al, 10110110b //; Timer IC 8253 square waves + out 43h, al //; channel 2, speaker + mov ax, bx + out 42h, al //; send low order + mov al, ah //; load high order + out 42h, al //; send high order + in al, 61h //; Read IC 8255 machine status + push ax + or al, 00000011b + out 61h, al //; Turn speaker on + mov cx, 2000h + @@delay: + loop @@delay + pop ax + out 61h, al //; Turn speaker off + pop cx + pop bx + pop ax } } #endif diff --git a/src/lib/16_tdef.h b/src/lib/16_tdef.h index 08b96996..e9f19bbd 100755 --- a/src/lib/16_tdef.h +++ b/src/lib/16_tdef.h @@ -642,17 +642,17 @@ typedef struct //TODO: USE THIS!!!! //========================================================================== //actual global game varables! -typedef enum { +/*typedef enum { ENGI_QUIT, ENGI_RUN, ENGI_MENU, ENGI_PAUSE -} engi_stat_t; +} engi_stat_t;*/ //ENGI_INPUT, typedef struct { - engi_stat_t engi_stat; +//---- engi_stat_t engi_stat; video_t video; // video settings variable ca_t ca; // ca stuff pm_t pm; // pm stuff diff --git a/src/lib/16_vl.h b/src/lib/16_vl.h index b1b03a21..cec79507 100755 --- a/src/lib/16_vl.h +++ b/src/lib/16_vl.h @@ -84,6 +84,9 @@ extern byte far* VGA; /* The VGA Memory */ #define LOW_ADDRESS 0x0D #define VRETRACE 0x08 //#define INPUT_STATUS_1 0x03da defined in 16_head +#define STATUS_REGISTER_1 INPUT_STATUS_1 +#define ATR_INDEX AC_INDEX +#define ATR_OVERSCAN 17 #define DISPLAY_ENABLE 0x01 #define MAP_MASK 0x02 #define PAL_READ_REG 0x03C7 /* Color register, read address */ diff --git a/src/lib/hb/c3_act1.c b/src/lib/hb/c3_act1.c new file mode 100755 index 00000000..2706b4f1 --- /dev/null +++ b/src/lib/hb/c3_act1.c @@ -0,0 +1,1259 @@ +/* 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_PLAY.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east, + southwest,south,southeast}; + + + +/* +============================================================================= + + BONUS ITEMS + +============================================================================= +*/ + +extern statetype s_boltbonus2; +extern statetype s_nukebonus2; + +statetype s_boltbonus = {BOLTOBJPIC,8,NULL,&s_boltbonus2}; +statetype s_boltbonus2 = {BOLTOBJ2PIC,8,NULL,&s_boltbonus}; + +statetype s_nukebonus = {NUKEOBJPIC,8,NULL,&s_nukebonus2}; +statetype s_nukebonus2 = {NUKEOBJ2PIC,8,NULL,&s_nukebonus}; + +statetype s_potionbonus = {POTIONOBJPIC,0,NULL,&s_potionbonus}; +statetype s_rkeybonus = {RKEYOBJPIC,0,NULL,&s_rkeybonus}; +statetype s_ykeybonus = {YKEYOBJPIC,0,NULL,&s_ykeybonus}; +statetype s_gkeybonus = {GKEYOBJPIC,0,NULL,&s_gkeybonus}; +statetype s_bkeybonus = {BKEYOBJPIC,0,NULL,&s_bkeybonus}; +statetype s_scrollbonus = {SCROLLOBJPIC,0,NULL,&s_scrollbonus}; +statetype s_chestbonus = {CHESTOBJPIC,0,NULL,&s_chestbonus}; +statetype s_goalbonus = {NEMESISPIC,0,NULL,&s_goalbonus}; + +/* +=============== += += SpawnBonus += +=============== +*/ + +void SpawnBonus (int tilex, int tiley, int number) +{ + statetype *state; + + if (number == B_BOLT) + state = &s_boltbonus; + else if (number == B_NUKE) + state = &s_nukebonus; + else if (number == B_POTION) + state = &s_potionbonus; + else if (number == B_RKEY) + state = &s_rkeybonus; + else if (number == B_YKEY) + state = &s_ykeybonus; + else if (number == B_GKEY) + state = &s_gkeybonus; + else if (number == B_BKEY) + state = &s_bkeybonus; + else if (number >= B_SCROLL1 && number <= B_SCROLL8) + state = &s_scrollbonus; + else if (number == B_CHEST) + state = &s_chestbonus; + else if (number == B_GOAL) + state = &s_goalbonus; + + SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2); + new->tileobject = true; + new->temp1 = number; + new->obclass = bonusobj; + new->shootable = false; +} + + +/* +============================================================================= + + EXPLODING WALL + +============================================================================= +*/ + + +void T_WallDie (objtype *ob); + +extern statetype s_walldie1; +extern statetype s_walldie2; +extern statetype s_walldie3; +extern statetype s_walldie4; +extern statetype s_walldie5; +extern statetype s_walldie6; + +statetype s_walldie1 = {0,20,NULL,&s_walldie2}; +statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3}; +statetype s_walldie3 = {0,20,NULL,&s_walldie4}; +statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5}; +statetype s_walldie5 = {0,20,NULL,&s_walldie6}; +statetype s_walldie6 = {0,-1,T_WallDie,NULL}; + +/* +================ += += ExplodeWall += +================ +*/ + +void ExplodeWall (int tilex, int tiley) +{ + SpawnNewObj (tilex,tiley,&s_walldie1,0); + new->obclass = inertobj; + new->active = true; + (unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] = + *(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = WALLEXP; +} + + +/* +================ += += T_WallDie += +================ +*/ + +void T_WallDie (objtype *ob) +{ + unsigned tile,other; + + if (++ob->temp1 == 3) + tile = 0; + else + tile = WALLEXP-1 + ob->temp1; + + (unsigned)actorat[ob->tilex][ob->tiley] = tilemap[ob->tilex][ob->tiley] = + *(mapsegs[0]+farmapylookup[ob->tiley]+ob->tilex) = tile; + + if (ob->temp1 == 1) + { + // + // blow up nearby walls + // + other = tilemap[ob->tilex-1][ob->tiley]; + if ((unsigned)(other-EXPWALLSTART)tilex-1,ob->tiley); + other = tilemap[ob->tilex+1][ob->tiley]; + if ((unsigned)(other-EXPWALLSTART)tilex+1,ob->tiley); + other = tilemap[ob->tilex][ob->tiley-1]; + if ((unsigned)(other-EXPWALLSTART)tilex,ob->tiley-1); + other = tilemap[ob->tilex][ob->tiley+1]; + if ((unsigned)(other-EXPWALLSTART)tilex,ob->tiley+1); + } +} + + +/* +============================================================================= + + WARP GATE + +============================================================================= +*/ + +void T_Gate (objtype *ob); + +extern statetype s_gate1; +extern statetype s_gate2; +extern statetype s_gate3; +extern statetype s_gate4; + +extern statetype s_fgate1; +extern statetype s_fgate2; +extern statetype s_fgate3; +extern statetype s_fgate4; + +statetype s_gate1 = {WARP1PIC,12,T_Gate,&s_gate2}; +statetype s_gate2 = {WARP2PIC,12,T_Gate,&s_gate3}; +statetype s_gate3 = {WARP3PIC,12,T_Gate,&s_gate4}; +statetype s_gate4 = {WARP4PIC,12,T_Gate,&s_gate1}; + +statetype s_fgate1 = {WARP1PIC,6,T_Gate,&s_fgate2}; +statetype s_fgate2 = {WARP2PIC,6,T_Gate,&s_fgate3}; +statetype s_fgate3 = {WARP3PIC,6,T_Gate,&s_fgate4}; +statetype s_fgate4 = {WARP4PIC,6,T_Gate,&s_fgate1}; + +/* +=============== += += SpawnWarp += +=============== +*/ + +void SpawnWarp (int tilex, int tiley, int type) +{ + if (type) + SpawnNewObj (tilex,tiley,&s_fgate1,TILEGLOBAL/3); + else + SpawnNewObj (tilex,tiley,&s_gate1,TILEGLOBAL/3); + new->obclass = gateobj; + new->temp1 = type; +} + + +/* +=============== += += T_Gate += +=============== +*/ + +#define STATUSCOLOR 4 + +void T_Gate (objtype *ob) +{ + int spot; + objtype *check; + unsigned temp; + + if (CheckHandAttack (ob) && !playstate) + { + // + // warp + // + temp = bufferofs; + bufferofs = 0; + VW_Bar (26,4,232,9,STATUSCOLOR); // clear text description + bufferofs = temp; + IN_ClearKeysDown (); + if (ob->temp1) + { + // + // teleport inside level + // + for (check=player->next;check;check=check->next) + if (check->obclass==gateobj && check->temp1==ob->temp1 && + check != ob) + { + player->x = check->x; + player->y = check->y; + Thrust (player->angle,TILEGLOBAL/2); // move forwards + Thrust (player->angle,TILEGLOBAL/2); // move forwards + Thrust (player->angle,TILEGLOBAL/2); // move forwards + fizzlein=true; + } + } + else + { + // + // teleport out of level + // + playstate = ex_warped; + spot = *(mapsegs[0]+farmapylookup[ob->tiley]+ob->tilex)-NAMESTART; + if (spot<1) + gamestate.mapon++; + else + gamestate.mapon=spot-1; + SD_PlaySound(WARPUPSND); + } + } +} + + +/* +============================================================================= + + TROLLS + +============================================================================= +*/ + +void T_Troll (objtype *ob); + +extern statetype s_trollpause; + +extern statetype s_troll1; +extern statetype s_troll2; +extern statetype s_troll3; +extern statetype s_troll4; + +extern statetype s_trollattack1; +extern statetype s_trollattack2; +extern statetype s_trollattack3; + +extern statetype s_trollouch; + +extern statetype s_trolldie1; +extern statetype s_trolldie2; +extern statetype s_trolldie3; + + +statetype s_trollpause = {TROLL1PIC,40,NULL,&s_troll2}; + +statetype s_troll1 = {TROLL1PIC,13,T_Troll,&s_troll2}; +statetype s_troll2 = {TROLL2PIC,13,T_Troll,&s_troll3}; +statetype s_troll3 = {TROLL3PIC,13,T_Troll,&s_troll4}; +statetype s_troll4 = {TROLL4PIC,13,T_Troll,&s_troll1}; + +statetype s_trollattack1 = {TROLLATTACK1PIC,20,NULL,&s_trollattack2}; +statetype s_trollattack2 = {TROLLATTACK2PIC,10,T_DoDamage,&s_trollattack3}; +statetype s_trollattack3 = {TROLLATTACK2PIC,40,NULL,&s_trollpause}; + +statetype s_trollouch = {TROLLOUCHPIC,8,NULL,&s_troll1}; + +statetype s_trolldie1 = {TROLLDIE1PIC,8,NULL,&s_trolldie2}; +statetype s_trolldie2 = {TROLLDIE2PIC,8,NULL,&s_trolldie3}; +statetype s_trolldie3 = {TROLLDIE3PIC,0,NULL,&s_trolldie3}; + + +/* +=============== += += SpawnTroll += +=============== +*/ + +void SpawnTroll (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_troll1,40*PIXRADIUS); + new->speed = 2500; + new->obclass = trollobj; + new->shootable = true; + new->hitpoints = 10; +} + + +/* +=============== += += T_Troll += +=============== +*/ + +void T_Troll (objtype *ob) +{ + if (Chase (ob,true)) + { + ob->state = &s_trollattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + + + +/* +============================================================================= + + ORCS + +============================================================================= +*/ + +void T_Orc (objtype *ob); + +extern statetype s_orcpause; + +extern statetype s_orc1; +extern statetype s_orc2; +extern statetype s_orc3; +extern statetype s_orc4; + +extern statetype s_orcattack1; +extern statetype s_orcattack2; +extern statetype s_orcattack3; + +extern statetype s_orcouch; + +extern statetype s_orcdie1; +extern statetype s_orcdie2; +extern statetype s_orcdie3; + + + +statetype s_orcpause = {ORC1PIC,40,NULL,&s_orc2}; + +statetype s_orc1 = {ORC1PIC,20,T_Orc,&s_orc2}; +statetype s_orc2 = {ORC2PIC,20,T_Orc,&s_orc3}; +statetype s_orc3 = {ORC3PIC,20,T_Orc,&s_orc4}; +statetype s_orc4 = {ORC4PIC,20,T_Orc,&s_orc1}; + +statetype s_orcattack1 = {ORCATTACK1PIC,20,NULL,&s_orcattack2}; +statetype s_orcattack2 = {ORCATTACK2PIC,10,T_DoDamage,&s_orcattack3}; +statetype s_orcattack3 = {ORCATTACK2PIC,40,NULL,&s_orcpause}; + +statetype s_orcouch = {ORCOUCHPIC,10,NULL,&s_orc1}; + +statetype s_orcdie1 = {ORCDIE1PIC,8,NULL,&s_orcdie2}; +statetype s_orcdie2 = {ORCDIE2PIC,8,NULL,&s_orcdie3}; +statetype s_orcdie3 = {ORCDIE3PIC,0,NULL,&s_orcdie3}; + + +/* +=============== += += SpawnOrc += +=============== +*/ + +void SpawnOrc (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_orc1,PIXRADIUS*32); + new->obclass = orcobj; + new->speed = 1536; + new->shootable = true; + new->hitpoints = 3; +} + + +/* +=============== += += T_Orc += +=============== +*/ + +void T_Orc (objtype *ob) +{ + if (Chase (ob,true)) + { + ob->state = &s_orcattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + + +/* +============================================================================= + + DEMON + +============================================================================= +*/ + +void T_Demon (objtype *ob); + + +extern statetype s_demonpause; + +extern statetype s_demon1; +extern statetype s_demon2; +extern statetype s_demon3; +extern statetype s_demon4; + +extern statetype s_demonattack1; +extern statetype s_demonattack2; +extern statetype s_demonattack3; + +extern statetype s_demonouch; + +extern statetype s_demondie1; +extern statetype s_demondie2; +extern statetype s_demondie3; + + +statetype s_demonpause = {DEMON1PIC,40,NULL,&s_demon2}; + +statetype s_demon1 = {DEMON1PIC,20,T_Demon,&s_demon2}; +statetype s_demon2 = {DEMON2PIC,20,T_Demon,&s_demon3}; +statetype s_demon3 = {DEMON3PIC,20,T_Demon,&s_demon4}; +statetype s_demon4 = {DEMON4PIC,20,T_Demon,&s_demon1}; + +statetype s_demonattack1 = {DEMONATTACK1PIC,20,NULL,&s_demonattack2}; +statetype s_demonattack2 = {DEMONATTACK2PIC,20,T_DoDamage,&s_demonattack3}; +statetype s_demonattack3 = {DEMONATTACK3PIC,30,NULL,&s_demonpause}; + +statetype s_demonouch = {DEMONOUCHPIC,10,NULL,&s_demon1}; + +statetype s_demondie1 = {DEMONDIE1PIC,20,NULL,&s_demondie2}; +statetype s_demondie2 = {DEMONDIE2PIC,20,NULL,&s_demondie3}; +statetype s_demondie3 = {DEMONDIE3PIC,0,NULL,&s_demondie3}; + + + +/* +=============== += += SpawnDemon += +=============== +*/ + +void SpawnDemon (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_demon1,TILEGLOBAL/2); + new->obclass = demonobj; + new->speed = 2048; + new->shootable = true; + new->hitpoints = 50; +} + + +/* +=============== += += T_Demon += +=============== +*/ + +void T_Demon (objtype *ob) +{ + if (Chase (ob,true)) + { + ob->state = &s_demonattack1; + ob->ticcount = ob->state->tictime; + return; + } +} + +/* +============================================================================= + + MSHOTS + +temp1 = dir + +============================================================================= +*/ + +#define MSHOTDAMAGE 2 +#define MSHOTSPEED 10000 + +void T_Mshot (objtype *ob); + + +extern statetype s_mshot1; +extern statetype s_mshot2; + +statetype s_mshot1 = {PSHOT1PIC,8,&T_Mshot,&s_mshot2}; +statetype s_mshot2 = {PSHOT2PIC,8,&T_Mshot,&s_mshot1}; + + +/* +=============== += += T_Mshot += +=============== +*/ + +void T_Mshot (objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + + xmove = ymove = 0; + + switch (ob->dir) + { + case north: + ymove = -ob->speed*tics; + break; + case east: + xmove = ob->speed*tics; + break; + case south: + ymove = ob->speed*tics; + break; + case west: + xmove = -ob->speed*tics; + break; + } + + ob->x+=xmove; + ob->y+=ymove; + + CalcBounds (ob); + + ob->tilex = ob->x>>TILESHIFT; + ob->tiley = ob->y>>TILESHIFT; + + if (tilemap[ob->tilex][ob->tiley]) + { + SD_PlaySound (SHOOTWALLSND); + ob->state = NULL; + return; + } + +// +// check final position for monsters hit +// + if ( ob->xl <= player->xh + && ob->xh >= player->xl + && ob->yl <= player->yh + && ob->yh >= player->yl) + { + TakeDamage (MSHOTDAMAGE*2); + ob->state = NULL; + return; + } + + for (check = player->next; check; check=check->next) + if (ob->shootable && ob->obclass != mageobj + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + ShootActor (check,MSHOTDAMAGE); + ob->state = NULL; + return; + } +} + + + + +/* +============================================================================= + + MAGE + +============================================================================= +*/ + + +void T_Mage (objtype *ob); +void T_MageShoot (objtype *ob); + +extern statetype s_magepause; + +extern statetype s_mage1; +extern statetype s_mage2; + +extern statetype s_mageattack1; +extern statetype s_mageattack2; +extern statetype s_mageattack3; + +extern statetype s_mageouch; + +extern statetype s_magedie1; +extern statetype s_magedie2; + + +statetype s_magepause = {MAGE1PIC,100,NULL,&s_mage2}; + +statetype s_mage1 = {MAGE1PIC,20,T_Mage,&s_mage2}; +statetype s_mage2 = {MAGE2PIC,20,T_Mage,&s_mage1}; + +statetype s_mageattack1 = {MAGEATTACKPIC,20,NULL,&s_mageattack2}; +statetype s_mageattack2 = {MAGEATTACKPIC,-1,T_MageShoot,&s_mageattack3}; +statetype s_mageattack3 = {MAGEATTACKPIC,30,NULL,&s_magepause}; + +statetype s_mageouch = {MAGEOUCHPIC,10,NULL,&s_mage1}; + +statetype s_magedie1 = {MAGEDIE1PIC,20,NULL,&s_magedie2}; +statetype s_magedie2 = {MAGEDIE2PIC,0,NULL,&s_magedie2}; + + +/* +=============== += += SpawnMage += +=============== +*/ + +void SpawnMage (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_mage1,TILEGLOBAL/2); + new->obclass = mageobj; + new->speed = 2048; + new->shootable = true; + new->hitpoints = 5; +} + + +/* +=============== += += T_Mage += +=============== +*/ + +void T_Mage (objtype *ob) +{ + Chase (ob,false); +// +// check for line up with player +// + + if (ob->x-PIXRADIUS*14 < player->xh + && ob->x+PIXRADIUS > player->xl) + { + ob->temp1 = 1; + ob->state = &s_mageattack1; + } + else if (ob->y-PIXRADIUS*14 < player->yh + && ob->y+PIXRADIUS > player->yl) + { + ob->temp1 = 0; + ob->state = &s_mageattack1; + } +} + + +/* +=============== += += T_MageShoot += +=============== +*/ + +void T_MageShoot (objtype *ob) +{ + SpawnNewObjFrac (ob->x,ob->y,&s_mshot1,PIXRADIUS*14); + new->obclass = mshotobj; + new->speed = MSHOTSPEED; + if (ob->temp1) + { + if (ob->tiley < player->tiley) + new->dir = south; + else + new->dir = north; + } + else + { + if (ob->tilex < player->tilex) + new->dir = east; + else + new->dir = west; + } +} + + +/* +============================================================================= + + nemesis + +============================================================================= +*/ + + +void T_Nemesis (objtype *ob); +void T_NemesisShoot (objtype *ob); + +extern statetype s_grelpause; + +extern statetype s_grel1; +extern statetype s_grel2; + +extern statetype s_grelattack1; +extern statetype s_grelattack2; +extern statetype s_grelattack3; + +extern statetype s_grelouch; + +extern statetype s_greldie1; +extern statetype s_greldie2; +extern statetype s_greldie3; +extern statetype s_greldie4; +extern statetype s_greldie5; +extern statetype s_greldie6; + + +statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2}; + +statetype s_grel1 = {GREL1PIC,20,T_Nemesis,&s_grel2}; +statetype s_grel2 = {GREL2PIC,20,T_Nemesis,&s_grel1}; + +statetype s_grelattack1 = {GRELATTACKPIC,20,NULL,&s_grelattack2}; +statetype s_grelattack2 = {GRELATTACKPIC,-1,T_NemesisShoot,&s_grelattack3}; +statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause}; + +statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1}; + +statetype s_greldie1 = {GRELDIE1PIC,20,NULL,&s_greldie2}; +statetype s_greldie2 = {GRELDIE2PIC,20,NULL,&s_greldie3}; +statetype s_greldie3 = {GRELDIE3PIC,20,NULL,&s_greldie4}; +statetype s_greldie4 = {GRELDIE4PIC,20,NULL,&s_greldie5}; +statetype s_greldie5 = {GRELDIE5PIC,20,NULL,&s_greldie6}; +statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6}; + + +/* +=============== += += SpawnNemesis += +=============== +*/ + +void SpawnNemesis (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*56); + new->obclass = grelmobj; + new->speed = 2048; + new->shootable = true; + new->hitpoints = 100; +} + + +/* +=============== += += T_Nemesis += +=============== +*/ + +void T_Nemesis (objtype *ob) +{ + Chase (ob,false); +// +// check for line up with player +// + if (ob->tilex == player->tilex) + { + ob->temp1 = 1; + ob->state = &s_grelattack1; + } + else if (ob->tiley == player->tiley) + { + ob->temp1 = 0; + ob->state = &s_grelattack1; + } +} + + +/* +=============== += += T_NemesisShoot += +=============== +*/ + +void T_NemesisShoot (objtype *ob) +{ + SpawnNewObjFrac (ob->x,ob->y,&s_mshot1,PIXRADIUS*14); + new->obclass = mshotobj; + new->speed = MSHOTSPEED; + if (ob->temp1) + { + if (ob->tiley < player->tiley) + new->dir = south; + else + new->dir = north; + } + else + { + if (ob->tilex < player->tilex) + new->dir = east; + else + new->dir = west; + } +} + + +/* +============================================================================= + + BAT + +============================================================================= +*/ + +void T_Bat (objtype *ob); +void T_BatPast (objtype *ob); + +extern statetype s_bat1; +extern statetype s_bat2; +extern statetype s_bat3; +extern statetype s_bat4; + +extern statetype s_batdie1; +extern statetype s_batdie2; + + +statetype s_bat1 = {BAT1PIC,6,T_Bat,&s_bat2}; +statetype s_bat2 = {BAT2PIC,6,T_Bat,&s_bat3}; +statetype s_bat3 = {BAT3PIC,6,T_Bat,&s_bat4}; +statetype s_bat4 = {BAT4PIC,6,T_Bat,&s_bat1}; + +statetype s_batpast = {BAT4PIC,80,T_BatPast,&s_bat1}; + +statetype s_batdie1 = {BATDIE1PIC,8,NULL,&s_batdie2}; +statetype s_batdie2 = {BATDIE2PIC,8,NULL,NULL}; + + +/* +=============== += += SpawnBat += +=============== +*/ + +void SpawnBat (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_bat1,PIXRADIUS*24); + new->obclass =batobj; + new->shootable = true; + + new->hitpoints = 1; + new->speed = 2000; +} + + +/* +================================== += += BatChaseThink += +================================== +*/ + +void BatChaseThink (objtype *obj) +{ + int deltax,deltay; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + if (deltax>0) + deltax = 2; + else if (deltax<0) + deltax = 0; + else deltax = 1; + + if (deltay>0) + deltay = 2; + else if (deltay<0) + deltay = 0; + else deltay = 1; + + obj->dir = dirtable[deltay*3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[deltay*3+1]; + if (Walk(obj)) + return; + + obj->dir = nodir; +} + + +void BatRunThink (objtype *obj) +{ + int deltax,deltay; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + if (deltax>=0) + deltax = 0; + else + deltax = 2; + + if (deltay>=0) + deltay = 0; + else + deltay = 2; + + obj->dir = dirtable[deltay*3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[3+deltax]; + if (Walk(obj)) + return; + + obj->dir = dirtable[deltay*3+1]; + Walk(obj); +} + + + +/* +=============== += += T_Bat += +=============== +*/ + +void T_Bat (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move; + + + do + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size && !ob->temp1) + { + TakeDamage (4); + ob->temp1 = 2; + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (ob->temp1) + { + Walk (ob); // go straight + if (!--ob->temp1) + { + ob->state = &s_batpast; + ob->ticcount = ob->state->tictime; + } + } + else + BatChaseThink (ob); // head towards player + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } while (0); // just once + CalcBounds (ob); +} + + +/* +=============== += += T_BatPast += +=============== +*/ + +void T_BatPast (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + + do + { + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + BatRunThink (ob); + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } while (0); //(move) + CalcBounds (ob); +} + + + +/* +============================================================================= + + BOUNCE + +temp2 = set when hit player, reset when hit wall + +============================================================================= +*/ + +#define SPDBOUNCE 4096 +#define DMGBOUNCE 10 + +void T_Bounce (objtype *ob); + +extern statetype s_bounce1; +extern statetype s_bounce2; + + +statetype s_bounce1 = {BIGPSHOT1PIC,8,T_Bounce,&s_bounce2}; +statetype s_bounce2 = {BIGPSHOT2PIC,8,T_Bounce,&s_bounce1}; + +/* +=============== += += SpawnBounce += +=============== +*/ + +void SpawnBounce (int tilex, int tiley, boolean towest) +{ + SpawnNewObj(tilex,tiley,&s_bounce1,24*PIXRADIUS); + new->obclass = bounceobj; + if (towest) + new->dir = west; + else + new->dir = north; +} + + +/* +=============== += += T_Bounce += +=============== +*/ + +void T_Bounce (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = SPDBOUNCE*tics; + size = (long)ob->size + player->size + move; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size && !ob->temp2) + { + ob->temp2 = 1; + TakeDamage (DMGBOUNCE); + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + // + // bounce if hit wall + // + switch (ob->dir) + { + case north: + if (tilemap[ob->tilex][--ob->tiley]) + { + ob->dir = south; + ob->tiley+=2; + ob->temp2 = 0; + } + break; + case east: + if (tilemap[++ob->tilex][ob->tiley]) + { + ob->dir = west; + ob->tilex-=2; + ob->temp2 = 0; + } + break; + case south: + if (tilemap[ob->tilex][++ob->tiley]) + { + ob->dir = north; + ob->tiley-=2; + ob->temp2 = 0; + } + break; + case west: + if (tilemap[--ob->tilex][ob->tiley]) + { + ob->dir = east; + ob->tilex+=2; + ob->temp2 = 0; + } + break; + } + + ob->distance = TILEGLOBAL; + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); +} + diff --git a/src/lib/hb/c3_asm.asm b/src/lib/hb/c3_asm.asm new file mode 100755 index 00000000..d9da6eb6 --- /dev/null +++ b/src/lib/hb/c3_asm.asm @@ -0,0 +1,197 @@ +; 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. + +IDEAL + +MODEL MEDIUM,C + +VIEWWIDTH = (33*8) +GC_INDEX = 03CEh + +DATASEG +EVEN + +;=================== Tables filled in by DrawVWall ========================== + +; +; wallheight has the height (scale number) of that collumn of scaled wall +; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height) +; +wallheight dw VIEWWIDTH dup (?) + +; +; wallwidth has the pixel width (1-7) of that collumn +; +wallwidth dw VIEWWIDTH dup (?) + +; +; wallseg has the segment of the wall picture +; +wallseg dw VIEWWIDTH dup (?) + +; +; wallofs has the offset of the wall picture +; +wallofs dw VIEWWIDTH dup (?) + +;============================================================================ + +; +; screenbyte is just position/8 +; +LABEL screenbyte WORD +pos = 0 +REPT VIEWWIDTH + dw pos/8 +pos = pos+1 +ENDM + +; +; screenbit is (position&7)*16 +; +LABEL screenbit WORD +pos = 0 +REPT VIEWWIDTH + dw (pos AND 7)*16 +pos = pos+1 +ENDM + +; +; Use offset: screenbit[]+pixwidth*2 +; acess from bitmasks-2+offset for one biased pixwidth +; the low byte of bitmasks is for the first screen byte, the high byte +; is the bitmask for the second screen byte (if non 0) +; + +bitmasks dw 0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh + dw 0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh + dw 0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh + dw 0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh + dw 0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh + dw 0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h + dw 0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h + dw 0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h + + +; +; wallscalecall is a far pointer to the start of a compiled scaler +; The low word will never change, while the high word is set to +; compscaledirectory[scale] +; +wallscalecall dd (65*6) ; offset of t_compscale->code[0] + + +PUBLIC wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit +PUBLIC bitmasks,wallscalecall + + +EXTRN scaledirectory:WORD ; array of MAXSCALE segment pointers to + ; compiled scalers +EXTRN screenseg:WORD ; basically just 0xa000 +EXTRN bufferofs:WORD ; offset of the current work screen + +CODESEG + +;============================================================================ +; +; ScaleWalls +; +; AX AL is scratched in bit mask setting and scaling +; BX table index +; CX pixwidth*2 +; DX GC_INDEX +; SI offset into wall data to scale from, allways 0,64,128,...4032 +; DI byte at top of screen that the collumn is contained in +; BP x pixel * 2, index into VIEWWIDTH wide tables +; DS segment of the wall data to texture map +; ES screenseg +; SS addressing DGROUP variables +; +;============================================================================ + +PROC ScaleWalls +PUBLIC ScaleWalls +USES SI,DI,BP + + xor bp,bp ; start at location 0 in the tables + mov dx,GC_INDEX+1 + mov es,[screenseg] + +; +; scale one collumn of data, possibly across two bytes +; +nextcollumn: + + mov bx,[wallheight+bp] ; height of walls (1-MAXSCALE) + shl bx,1 + mov ax,[ss:scaledirectory+bx] ; segment of the compiled scaler + mov [WORD PTR ss:wallscalecall+2],ax + + mov cx,[wallwidth+bp] + or cx,cx + jnz okwidth + mov cx,2 + jmp next + +okwidth: + shl cx,1 + mov ds,[wallseg+bp] + mov si,[wallofs+bp] + + mov di,[screenbyte+bp] ; byte at the top of the scaled collumn + add di,[ss:bufferofs] ; offset of current page flip + mov bx,[screenbit+bp] ; 0-7 << 4 + add bx,cx + mov ax,[ss:bitmasks-2+bx] + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels + or ah,ah ; is there anything in the second byte? + jnz secondbyte +; +; next +; +next: + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + jmp done + +; +; draw a second byte for vertical strips that cross two bytes +; +secondbyte: + mov al,ah + inc di ; next byte over + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels +; +; next +; + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + +done: + mov ax,ss + mov ds,ax + ret + +ENDP + + +END + diff --git a/src/lib/hb/c3_debug.c b/src/lib/hb/c3_debug.c new file mode 100755 index 00000000..4aa68312 --- /dev/null +++ b/src/lib/hb/c3_debug.c @@ -0,0 +1,606 @@ +/* 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_DEBUG.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define VIEWTILEX 20 +#define VIEWTILEY (VIEWHEIGHT/16) + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +int maporgx; +int maporgy; +enum {mapview,tilemapview,actoratview,visview} viewtype; + +void ViewMap (void); + +//=========================================================================== + + + +/* +================== += += DebugMemory += +================== +*/ + +void DebugMemory (void) +{ + int i; + char scratch[80],str[10]; + long mem; + spritetype _seg *block; + + VW_FixRefreshBuffer (); + US_CenterWindow (16,7); + +#if 0 + CA_OpenDebug (); + for (i=0;i=0;y--) + VW_ScreenToScreen (source+y*64,y*40,40,1); + } + + IN_Shutdown (); + + VW_WaitVBL(70); + bioskey(0); + VW_WaitVBL(70); + Quit (NULL); +} + + +//=========================================================================== + +/* +================ += += ShapeTest += +================ +*/ + +void ShapeTest (void) +{ + +} + + +//=========================================================================== + +#define sc_1 0x02 +#define sc_2 0x03 +#define sc_3 0x04 +#define sc_4 0x05 +#define sc_5 0x06 +#define sc_6 0x07 +#define sc_7 0x08 +#define sc_8 0x09 +#define sc_9 0x0a +#define sc_0 0x0b + + + +/* +================ += += DebugKeys += +================ +*/ + +int DebugKeys (void) +{ + boolean esc; + int level,i; + + if (Keyboard[sc_B]) // B = border color + { + CenterWindow(24,3); + PrintY+=6; + US_Print(" Border color (0-15):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=15) + VW_ColorBorder (level); + } + return 1; + } + +#if 0 + + if (Keyboard[sc_C]) // C = count objects + { + CountObjects(); + return 1; + } + + + if (Keyboard[sc_D]) // D = start / end demo record + { + if (DemoMode == demo_Off) + StartDemoRecord (); + else if (DemoMode == demo_Record) + { + EndDemoRecord (); + playstate = ex_completed; + } + return 1; + } + +#endif + + if (Keyboard[sc_E]) // E = quit level + { + if (tedlevel) + TEDDeath(); + playstate = ex_warped; + gamestate.mapon++; + } + + if (Keyboard[sc_F]) // F = facing spot + { + CenterWindow (12,4); + US_Print ("X:"); + US_PrintUnsigned (player->x); + US_Print ("Y:"); + US_PrintUnsigned (player->y); + US_Print ("A:"); + US_PrintUnsigned (player->angle); + VW_UpdateScreen(); + IN_Ack(); + return 1; + } + + if (Keyboard[sc_G]) // G = god mode + { + CenterWindow (12,2); + if (godmode) + US_PrintCentered ("God mode OFF"); + else + US_PrintCentered ("God mode ON"); + VW_UpdateScreen(); + IN_Ack(); + godmode ^= 1; + return 1; + } + if (Keyboard[sc_H]) // H = hurt self + { + TakeDamage (5); + } + else if (Keyboard[sc_I]) // I = item cheat + { + CenterWindow (12,3); + US_PrintCentered ("Free items!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); + if (!gamestate.keys[i]) + GiveKey (i); + } + for (i=0;i<8;i++) + GiveScroll (i,false); + + IN_Ack (); + return 1; + } + else if (Keyboard[sc_M]) // M = memory info + { + DebugMemory(); + return 1; + } + else if (Keyboard[sc_O]) // O = overhead + { + ViewMap(); + return 1; + } + else if (Keyboard[sc_P]) // P = pause with no screen disruptioon + { + PicturePause (); + return 1; + } + else if (Keyboard[sc_S]) // S = slow motion + { + singlestep^=1; + CenterWindow (18,3); + if (singlestep) + US_PrintCentered ("Slow motion ON"); + else + US_PrintCentered ("Slow motion OFF"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } + else if (Keyboard[sc_S]) // T = shape test + { + ShapeTest (); + return 1; + } + else if (Keyboard[sc_V]) // V = extra VBLs + { + CenterWindow(30,3); + PrintY+=6; + US_Print(" Add how many extra VBLs(0-8):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=8) + extravbls = level; + } + return 1; + } + else if (Keyboard[sc_W]) // W = warp to level + { + CenterWindow(26,3); + PrintY+=6; + US_Print(" Warp to which level(1-21):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>0 && level<21) + { + gamestate.mapon = level-1; + playstate = ex_warped; + } + } + return 1; + } + else if (Keyboard[sc_X]) // X = item cheat + { + CenterWindow (12,3); + US_PrintCentered ("Extra stuff!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); + } + IN_Ack (); + return 1; + } + else if (Keyboard[sc_Z]) // Z = game over + { + + } + else if (LastScan >= sc_1 && LastScan <= sc_8) // free scrolls + { + GiveScroll (LastScan-sc_1,false); + IN_ClearKeysDown (); + } + + return 0; +} + + +/* +===================== += += LatchDrawChar += +===================== +*/ + +void LatchDrawChar (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = latchpics[0]+picnum*8; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm dec bx + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] + +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + + +/* +===================== += += LatchDrawTile += +===================== +*/ + +void LatchDrawTile (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = tileoffsets[picnum]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,2 + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,16 + +lineloop: +asm movsb +asm movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + + +/* +=================== += += OverheadRefresh += +=================== +*/ + +void OverheadRefresh (void) +{ + unsigned x,y,endx,endy,sx,sy; + unsigned tile; + + + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + endx = maporgx+VIEWTILEX; + endy = maporgy+VIEWTILEY; + + for (y=maporgy;y>12)); + LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8)); + LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4)); + LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f)); + } + } + + VW_SetScreen (bufferofs,0); + displayofs = bufferofs; +} + + +/* +=================== += += ViewMap += +=================== +*/ + +void ViewMap (void) +{ + boolean button0held; + + viewtype = actoratview; + button0held = false; + + + maporgx = player->tilex - VIEWTILEX/2; + if (maporgx<0) + maporgx = 0; + maporgy = player->tiley - VIEWTILEY/2; + if (maporgy<0) + maporgy = 0; + + do + { +// +// let user pan around +// + IN_ReadControl(0,&c); + if (c.xaxis == -1 && maporgx>0) + maporgx--; + if (c.xaxis == 1 && maporgx0) + maporgy--; + if (c.yaxis == 1 && maporgyvisview) + viewtype = mapview; + } + if (!c.button0) + button0held = false; + + + OverheadRefresh (); + + } while (!Keyboard[sc_Escape]); + + IN_ClearKeysDown (); + DrawPlayScreen (); +} + + diff --git a/src/lib/hb/c3_def.h b/src/lib/hb/c3_def.h new file mode 100755 index 00000000..c0fe07fb --- /dev/null +++ b/src/lib/hb/c3_def.h @@ -0,0 +1,533 @@ +/* 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. + */ + +#include "ID_HEADS.H" +#include +#include + +//#define PROFILE + +/* +============================================================================= + + GLOBAL CONSTANTS + +============================================================================= +*/ + +#define NAMESTART 180 + + +#define UNMARKGRCHUNK(chunk) (grneeded[chunk]&=~ca_levelbit) + +#define MOUSEINT 0x33 + +#define EXPWALLSTART 8 +#define NUMEXPWALLS 7 +#define WALLEXP 15 +#define NUMFLOORS 36 + +#define NUMFLOORS 36 + +#define NUMLATCHPICS 100 +#define NUMSCALEPICS 100 +#define NUMSCALEWALLS 30 + + +#define FLASHCOLOR 5 +#define FLASHTICS 4 + + +#define NUMLEVELS 20 + +#define VIEWX 0 // corner of view window +#define VIEWY 0 +#define VIEWWIDTH (33*8) // size of view window +#define VIEWHEIGHT (18*8) +#define VIEWXH (VIEWX+VIEWWIDTH-1) +#define VIEWYH (VIEWY+VIEWHEIGHT-1) + +#define CENTERX (VIEWX+VIEWWIDTH/2-1) // middle of view window +#define CENTERY (VIEWY+VIEWHEIGHT/2-1) + +#define GLOBAL1 (1l<<16) +#define TILEGLOBAL GLOBAL1 +#define TILESHIFT 16l + +#define MINDIST (2*GLOBAL1/5) +#define FOCALLENGTH (TILEGLOBAL) // in global coordinates + +#define ANGLES 360 // must be divisable by 4 + +#define MAPSIZE 64 // maps are 64*64 max +#define MAXACTORS 150 // max number of tanks, etc / map + +#define NORTH 0 +#define EAST 1 +#define SOUTH 2 +#define WEST 3 + +#define SIGN(x) ((x)>0?1:-1) +#define ABS(x) ((int)(x)>0?(x):-(x)) +#define LABS(x) ((long)(x)>0?(x):-(x)) + +#define MAXSCALE (VIEWWIDTH/2) + + +#define MAXBODY 64 +#define MAXSHOTPOWER 56 + +#define SCREEN1START 0 +#define SCREEN2START 8320 + +#define PAGE1START 0x900 +#define PAGE2START 0x2000 +#define PAGE3START 0x3700 +#define FREESTART 0x4e00 + +#define PIXRADIUS 512 + +#define STATUSLINES (200-VIEWHEIGHT) + +enum bonusnumbers {B_BOLT,B_NUKE,B_POTION,B_RKEY,B_YKEY,B_GKEY,B_BKEY,B_SCROLL1, + B_SCROLL2,B_SCROLL3,B_SCROLL4,B_SCROLL5,B_SCROLL6,B_SCROLL7,B_SCROLL8, + B_GOAL,B_CHEST}; + + +/* +============================================================================= + + GLOBAL TYPES + +============================================================================= +*/ + +enum {BLANKCHAR=9,BOLTCHAR,NUKECHAR,POTIONCHAR,KEYCHARS,SCROLLCHARS=17, + NUMBERCHARS=25}; + +typedef long fixed; + +typedef struct {int x,y;} tilept; +typedef struct {fixed x,y;} globpt; + +typedef struct +{ + int x1,x2,leftclip,rightclip;// first pixel of wall (may not be visable) + unsigned height1,height2,color,walllength,side; + long planecoord; +} walltype; + +typedef enum + {nothing,playerobj,bonusobj,orcobj,batobj,skeletonobj,trollobj,demonobj, + mageobj,pshotobj,bigpshotobj,mshotobj,inertobj,bounceobj,grelmobj + ,gateobj} classtype; + +typedef enum {north,east,south,west,northeast,southeast,southwest, + northwest,nodir} dirtype; // a catacombs 2 carryover + + +typedef struct statestruct +{ + int shapenum; + int tictime; + void (*think) (); + struct statestruct *next; +} statetype; + + +typedef struct objstruct +{ + enum {no,yes} active; + int ticcount; + classtype obclass; + statetype *state; + + boolean shootable; + boolean tileobject; // true if entirely inside one tile + + long distance; + dirtype dir; + fixed x,y; + unsigned tilex,tiley; + int viewx; + unsigned viewheight; + + int angle; + int hitpoints; + long speed; + + unsigned size; // global radius for hit rect calculation + fixed xl,xh,yl,yh; // hit rectangle + + int temp1,temp2; + struct objstruct *next,*prev; +} objtype; + + +typedef struct +{ + int difficulty; + int mapon; + int bolts,nukes,potions,keys[4],scrolls[8]; + long score; + int body,shotpower; +} gametype; + +typedef enum {ex_stillplaying,ex_died,ex_warped,ex_resetgame + ,ex_loadedgame,ex_victorious,ex_abort} exittype; + + +/* +============================================================================= + + C3_MAIN DEFINITIONS + +============================================================================= +*/ + +extern char str[80],str2[20]; +extern unsigned tedlevelnum; +extern boolean tedlevel; +extern gametype gamestate; +extern exittype playstate; + + +void NewGame (void); +boolean SaveTheGame(int file); +boolean LoadTheGame(int file); +void ResetGame(void); +void ShutdownId (void); +void InitGame (void); +void Quit (char *error); +void TEDDeath(void); +void DemoLoop (void); +void SetupScalePic (unsigned picnum); +void SetupScaleWall (unsigned picnum); +void SetupScaling (void); +void main (void); + +/* +============================================================================= + + C3_GAME DEFINITIONS + +============================================================================= +*/ + +extern unsigned latchpics[NUMLATCHPICS]; +extern unsigned tileoffsets[NUMTILE16]; +extern unsigned textstarts[27]; + + +#define L_CHARS 0 +#define L_NOSHOT 1 +#define L_SHOTBAR 2 +#define L_NOBODY 3 +#define L_BODYBAR 4 + + +void ScanInfoPlane (void); +void ScanText (void); +void SetupGameLevel (void); +void Victory (void); +void Died (void); +void NormalScreen (void); +void DrawPlayScreen (void); +void LoadLatchMem (void); +void FizzleFade (unsigned source, unsigned dest, + unsigned width,unsigned height, boolean abortable); +void FizzleOut (int showlevel); +void FreeUpMemory (void); +void GameLoop (void); + + +/* +============================================================================= + + C3_PLAY DEFINITIONS + +============================================================================= +*/ + +extern ControlInfo c; +extern boolean running,slowturn; + +extern int bordertime; + +extern byte tilemap[MAPSIZE][MAPSIZE]; +extern objtype *actorat[MAPSIZE][MAPSIZE]; +extern byte spotvis[MAPSIZE][MAPSIZE]; + +extern objtype objlist[MAXACTORS],*new,*obj,*player; + +extern unsigned farmapylookup[MAPSIZE]; +extern byte *nearmapylookup[MAPSIZE]; +extern byte update[]; + +extern boolean godmode,singlestep; +extern int extravbls; + +extern int mousexmove,mouseymove; +extern int pointcount,pointsleft; + + +void CenterWindow(word w,word h); +void DebugMemory (void); +void PicturePause (void); +int DebugKeys (void); +void CheckKeys (void); +void InitObjList (void); +void GetNewObj (boolean usedummy); +void RemoveObj (objtype *gone); +void PollControlls (void); +void PlayLoop (void); + + +/* +============================================================================= + + C3_STATE DEFINITIONS + +============================================================================= +*/ + +void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size); +void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size); +boolean CheckHandAttack (objtype *ob); +void T_DoDamage (objtype *ob); +boolean Walk (objtype *ob); +void ChaseThink (objtype *obj, boolean diagonal); +void MoveObj (objtype *ob, long move); +boolean Chase (objtype *ob, boolean diagonal); + +extern dirtype opposite[9]; + +/* +============================================================================= + + C3_TRACE DEFINITIONS + +============================================================================= +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +int BackTrace (int finish); +void ForwardTrace (void); +int FinishWall (void); +void InsideCorner (void); +void OutsideCorner (void); +void FollowWalls (void); + +extern boolean aborttrace; + +/* +============================================================================= + + C3_DRAW DEFINITIONS + +============================================================================= +*/ + +#define MAXWALLS 50 +#define DANGERHIGH 45 + +#define MIDWALL (MAXWALLS/2) + +//========================================================================== + +extern tilept tile,lasttile,focal,left,mid,right; + +extern globpt edge,view; + +extern unsigned screenloc[3]; +extern unsigned freelatch; + +extern int screenpage; + +extern boolean fizzlein; + +extern long lasttimecount; + +extern int firstangle,lastangle; + +extern fixed prestep; + +extern int traceclip,tracetop; + +extern fixed sintable[ANGLES+ANGLES/4],*costable; + +extern fixed viewx,viewy,viewsin,viewcos; // the focal point +extern int viewangle; + +extern fixed scale,scaleglobal; +extern unsigned slideofs; + +extern int zbuffer[VIEWXH+1]; + +extern walltype walls[MAXWALLS],*leftwall,*rightwall; + + +extern fixed tileglobal; +extern fixed focallength; +extern fixed mindist; +extern int viewheight; +extern fixed scale; + +extern int walllight1[NUMFLOORS]; +extern int walldark1[NUMFLOORS]; +extern int walllight2[NUMFLOORS]; +extern int walldark2[NUMFLOORS]; + +//========================================================================== + +void DrawLine (int xl, int xh, int y,int color); +void DrawWall (walltype *wallptr); +void TraceRay (unsigned angle); +fixed FixedByFrac (fixed a, fixed b); +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight); +fixed TransformX (fixed gx, fixed gy); +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +void ForwardTrace (void); +int FinishWall (void); +int TurnClockwise (void); +int TurnCounterClockwise (void); +void FollowWall (void); + +void NewScene (void); +void BuildTables (void); + + +/* +============================================================================= + + C3_SCALE DEFINITIONS + +============================================================================= +*/ + + +#define COMPSCALECODESTART (65*6) // offset to start of code in comp scaler + +typedef struct +{ + unsigned codeofs[65]; + unsigned start[65]; + unsigned width[65]; + byte code[]; +} t_compscale; + +typedef struct +{ + unsigned width; + unsigned codeofs[64]; +} t_compshape; + + +extern unsigned scaleblockwidth, + scaleblockheight, + scaleblockdest; + +extern byte plotpix[8]; +extern byte bitmasks1[8][8]; +extern byte bitmasks2[8][8]; + + +extern t_compscale _seg *scaledirectory[MAXSCALE+1]; +extern t_compshape _seg *shapedirectory[NUMSCALEPICS]; +extern memptr walldirectory[NUMSCALEWALLS]; +extern unsigned shapesize[MAXSCALE+1]; + +void DeplanePic (int picnum); +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale); +unsigned BuildCompShape (t_compshape _seg **finalspot); + + +/* +============================================================================= + + C3_ASM DEFINITIONS + +============================================================================= +*/ + +extern unsigned wallheight [VIEWWIDTH]; +extern unsigned wallwidth [VIEWWIDTH]; +extern unsigned wallseg [VIEWWIDTH]; +extern unsigned wallofs [VIEWWIDTH]; +extern unsigned screenbyte [VIEWWIDTH]; +extern unsigned screenbit [VIEWWIDTH]; +extern unsigned bitmasks [64]; + +extern long wallscalecall; + +void ScaleWalls (void); + +/* +============================================================================= + + C3_WIZ DEFINITIONS + +============================================================================= +*/ + +#define MAXHANDHEIGHT 72 + +extern long lastnuke; +extern int handheight; +extern int boltsleft; + +/* +============================================================================= + + C3_ACT1 DEFINITIONS + +============================================================================= +*/ + +extern statetype s_trollouch; +extern statetype s_trolldie1; + + +extern statetype s_orcpause; + +extern statetype s_orc1; +extern statetype s_orc2; +extern statetype s_orc3; +extern statetype s_orc4; + +extern statetype s_orcattack1; +extern statetype s_orcattack2; +extern statetype s_orcattack3; + +extern statetype s_orcouch; + +extern statetype s_orcdie1; +extern statetype s_orcdie2; +extern statetype s_orcdie3; + + +extern statetype s_demonouch; +extern statetype s_demondie1; + +extern statetype s_mageouch; +extern statetype s_magedie1; + +extern statetype s_grelouch; +extern statetype s_greldie1; + +extern statetype s_batdie1; diff --git a/src/lib/hb/c3_draw.c b/src/lib/hb/c3_draw.c new file mode 100755 index 00000000..dfb055e5 --- /dev/null +++ b/src/lib/hb/c3_draw.c @@ -0,0 +1,1592 @@ +/* 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_DRAW.C + +#include "C3_DEF.H" +#pragma hdrstop + +//#define DRAWEACH // draw walls one at a time for debugging + +unsigned highest; +unsigned mostwalls,numwalls; + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define PI 3.141592657 +#define ANGLEQUAD (ANGLES/4) + +unsigned oldend; + +#define FINEANGLES 3600 + +#define MINRATIO 16 + + +const unsigned MAXSCALEHEIGHT = (VIEWWIDTH/2); +const unsigned MAXVISHEIGHT = (VIEWHEIGHT/2); +const unsigned BASESCALE = 32; + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +// +// calculate location of screens in video memory so they have the +// maximum possible distance seperating them (for scaling overflow) +// + +unsigned screenloc[3]= {0x900,0x2000,0x3700}; +unsigned freelatch = 0x4e00; + +boolean fizzlein; + +long scaleshapecalll; +long scaletablecall; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +long bytecount,endcount; // for profiling +int animframe; +int pixelangle[VIEWWIDTH]; +int far finetangent[FINEANGLES+1]; +int fineviewangle; +unsigned viewxpix,viewypix; + +/* +============================================================================ + + 3 - D DEFINITIONS + +============================================================================ +*/ + +fixed tileglobal = TILEGLOBAL; +fixed focallength = FOCALLENGTH; +fixed mindist = MINDIST; +int viewheight = VIEWHEIGHT; +fixed scale; + + +tilept tile,lasttile, // tile of wall being followed + focal, // focal point in tiles + left,mid,right; // rightmost tile in view + +globpt edge,view; + +int segstart[VIEWHEIGHT], // addline tracks line segment and draws + segend[VIEWHEIGHT], + segcolor[VIEWHEIGHT]; // only when the color changes + + +walltype walls[MAXWALLS],*leftwall,*rightwall; + + +//========================================================================== + +// +// refresh stuff +// + +int screenpage; + +long lasttimecount; + +// +// rendering stuff +// + +int firstangle,lastangle; + +fixed prestep; + +fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4); + +fixed viewx,viewy; // the focal point +int viewangle; +fixed viewsin,viewcos; + +int zbuffer[VIEWXH+1]; // holds the height of the wall at that point + +//========================================================================== + +void DrawLine (int xl, int xh, int y,int color); +void DrawWall (walltype *wallptr); +void TraceRay (unsigned angle); +fixed FixedByFrac (fixed a, fixed b); +fixed FixedAdd (void); +fixed TransformX (fixed gx, fixed gy); +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max); +int BackTrace (int finish); +void ForwardTrace (void); +int TurnClockwise (void); +int TurnCounterClockwise (void); +void FollowWall (void); + +void NewScene (void); +void BuildTables (void); + +//========================================================================== + + +/* +================== += += DrawLine += += Must be in write mode 2 with all planes enabled += The bit mask is left set to the end value, so clear it after all lines are += drawn += += draws a black dot at the left edge of the line += +================== +*/ + +unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1}; +unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1}; +unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void DrawLine (int xl, int xh, int y,int color) +{ + unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid; + + xlb=xl/8; + xhb=xh/8; + + if (xhVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +//========================================================================== + +void DrawLineDot (int xl, int xh, int y,int color) +{ + unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid; + + xlb=xl/8; + xhb=xh/8; + + if (xhVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskdot = dotmask[xlp]; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + + // + // write the black dot at the start + // + asm mov al,[BYTE PTR maskdot] + asm out dx,al // mask off pixels + + asm xor al,al + asm xchg al,[es:di] // load latches and write pixels + + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels + +// +// write the black dot at the start +// +asm mov al,[BYTE PTR maskdot] +asm out dx,al // mask off pixels +asm xor al,al +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +//========================================================================== + + +long wallscalesource; + +#ifdef DRAWEACH +/* +==================== += += ScaleOneWall += +==================== +*/ + +void near ScaleOneWall (int xl, int xh) +{ + int x,pixwidth,height; + + *(((unsigned *)&wallscalesource)+1) = wallseg[xl]; + + for (x=xl;x<=xh;x+=pixwidth) + { + height = wallheight[x]; + pixwidth = wallwidth[x]; + (unsigned)wallscalesource = wallofs[x]; + + *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height]; + (unsigned)scaletablecall = scaledirectory[height]->codeofs[0]; + + // + // scale a byte wide strip of wall + // + asm mov bx,[x] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm mov es,[screenseg] + asm lds si,[wallscalesource] + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + + asm mov al,BYTE PTR [ss:bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm out dx,al // set bit mask register + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + nosecond: + asm mov ax,ss + asm mov ds,ax + } +} + +#endif + +int walllight1[NUMFLOORS] = {0, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC, + YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC, + GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC, + BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC}; + +int walldark1[NUMFLOORS] = {0, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR1PIC,RDOOR2PIC,RDOOR1PIC,RDOOR2PIC, + YDOOR1PIC,YDOOR2PIC,YDOOR1PIC,YDOOR2PIC, + GDOOR1PIC,GDOOR2PIC,GDOOR1PIC,GDOOR2PIC, + BDOOR1PIC,BDOOR2PIC,BDOOR1PIC,BDOOR2PIC}; + +int walllight2[NUMFLOORS] = {0, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + WALL1LPIC,WALL2LPIC,WALL3LPIC,WALL4LPIC,WALL5LPIC,WALL6LPIC,WALL7LPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC, + YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC, + GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC, + BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC}; + +int walldark2[NUMFLOORS] = {0, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + WALL1DPIC,WALL2DPIC,WALL3DPIC,WALL4DPIC,WALL5DPIC,WALL6DPIC,WALL7DPIC, + EXPWALL1PIC,EXPWALL2PIC,EXPWALL3PIC, + RDOOR2PIC,RDOOR1PIC,RDOOR2PIC,RDOOR1PIC, + YDOOR2PIC,YDOOR1PIC,YDOOR2PIC,YDOOR1PIC, + GDOOR2PIC,GDOOR1PIC,GDOOR2PIC,GDOOR1PIC, + BDOOR2PIC,BDOOR1PIC,BDOOR2PIC,BDOOR1PIC}; + +/* +===================== += += DrawVWall += += Draws a wall by vertical segments, for texture mapping! += += wallptr->side is true for east/west walls (constant x) += += fracheight and fracstep are 16.16 bit fractions += +===================== +*/ + +void DrawVWall (walltype *wallptr) +{ + int x,i; + unsigned source; + unsigned width,sourceint; + unsigned wallpic,wallpicseg; + unsigned skip; + long fracheight,fracstep,longheightchange; + unsigned height; + int heightchange; + unsigned slope,distance; + int traceangle,angle; + int mapadd; + unsigned lastpix,lastsource,lastwidth; + + + if (wallptr->rightclip < wallptr->leftclip) + Quit ("DrawVWall: Right < Left"); + +// +// setup for height calculation +// + wallptr->height1 >>= 1; + wallptr->height2 >>= 1; + wallptr->planecoord>>=10; // remove non significant bits + + width = wallptr->x2 - wallptr->x1; + if (width) + { + heightchange = wallptr->height2 - wallptr->height1; + asm mov ax,[heightchange] + asm mov WORD PTR [longheightchange+2],ax + asm mov WORD PTR [longheightchange],0 // avoid long shift by 16 + fracstep = longheightchange/width; + } + + fracheight = ((long)wallptr->height1<<16)+0x8000; + skip = wallptr->leftclip - wallptr->x1; + if (skip) + fracheight += fracstep*skip; + +// +// setup for texture mapping +// +// mapadd is 64*64 (to keep source positive) + the origin wall intercept +// distance has 6 unit bits, and 6 frac bits +// traceangle is the center view angle in FINEANGLES, moved to be in +// the +-90 degree range (to thew right of origin) +// + traceangle = fineviewangle; + // + // find wall picture to map from + // + if (wallptr->side) + { // east or west wall + if (animframe) + wallpic = walllight2[wallptr->color]; + else + wallpic = walllight1[wallptr->color]; + + if (wallptr->planecoord < viewxpix) + { + distance = viewxpix-wallptr->planecoord; + traceangle -= FINEANGLES/2; + mapadd = (64-viewypix&63); // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewxpix; + // traceangle is correct + mapadd = viewypix&63; // the pixel spot of the origin + } + } + else + { // north or south wall + if (animframe) + wallpic = walldark2[wallptr->color]; + else + wallpic = walldark1[wallptr->color]; + + if (wallptr->planecoord < viewypix) + { + distance = viewypix-wallptr->planecoord; + traceangle -= FINEANGLES/4; + mapadd = viewxpix&63; // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewypix; + traceangle -= FINEANGLES*3/4; + mapadd = (64-viewxpix&63); // the pixel spot of the origin + } + } + + mapadd = 64*64-mapadd; // make sure it stays positive + + wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC]; + if (traceangle > FINEANGLES/2) + traceangle -= FINEANGLES; + +// +// calculate everything +// +// IMPORTANT! This loop is executed around 5000 times / second! +// + lastpix = lastsource = (unsigned)-1; + + for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++) + { + // + // height + // + asm mov ax,WORD PTR [fracheight] + asm mov dx,WORD PTR [fracheight+2] + asm mov cx,dx + asm add ax,WORD PTR [fracstep] + asm adc dx,WORD PTR [fracstep+2] + asm mov WORD PTR [fracheight],ax + asm mov WORD PTR [fracheight+2],dx + asm mov bx,[x] + asm shl bx,1 + asm cmp cx,MAXSCALEHEIGHT + asm jbe storeheight + asm mov cx,MAXSCALEHEIGHT +storeheight: + asm mov WORD PTR [wallheight+bx],cx + asm mov WORD PTR [zbuffer+bx],cx + +// height = fracheight>>16; +// fracheight += fracstep; +// if (height > MAXSCALEHEIGHT) +// height = MAXSCALEHEIGHT; +// wallheight[x] = zbuffer[x] = height; + + // + // texture map + // + angle = pixelangle[x]+traceangle; + if (angle<0) + angle+=FINEANGLES; + + slope = finetangent[angle]; + +// +// distance is an unsigned 6.6 bit number (12 pixel bits) +// slope is a signed 5.10 bit number +// result is a signed 11.16 bit number +// + +#if 0 + source = distance*slope; + source >>=20; + + source += mapadd; + source &= 63; // mask off the unused units + source = 63-source; + source <<= 6; // multiply by 64 for offset into pic +#endif + asm mov ax,[distance] + asm imul [slope] // ax is the source pixel + asm mov al,ah + asm shr al,1 + asm shr al,1 // low 6 bits is now pixel number + asm add ax,[mapadd] + asm and ax,63 + asm mov dx,63 + asm sub dx,ax // otherwise it is backwards + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 // *64 to index into shape + asm mov [source],dx + + if (source != lastsource) + { + if (lastpix != (unsigned)-1) + { + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; + } + lastpix = x; + lastsource = source; + lastwidth = 1; + } + else + lastwidth++; // optimized draw, same map as last one + } + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; +} + + +//========================================================================== + + +/* +================= += += TraceRay += += Used to find the left and rightmost tile in the view area to be traced from += Follows a ray of the given angle from viewx,viewy in the global map until += it hits a solid tile += sets: += tile.x,tile.y : tile coordinates of contacted tile += tilecolor : solid tile's color += +================== +*/ + +int tilecolor; + +void TraceRay (unsigned angle) +{ + long tracex,tracey,tracexstep,traceystep,searchx,searchy; + fixed fixtemp; + int otx,oty,searchsteps; + + tracexstep = costable[angle]; + traceystep = sintable[angle]; + +// +// advance point so it is even with the view plane before we start checking +// + fixtemp = FixedByFrac(prestep,tracexstep); + tracex = viewx+fixtemp; + fixtemp = FixedByFrac(prestep,traceystep); + tracey = viewy-fixtemp; + + tile.x = tracex>>TILESHIFT; // starting point in tiles + tile.y = tracey>>TILESHIFT; + + + if (tracexstep<0) // use 2's complement, not signed magnitude + tracexstep = -(tracexstep&0x7fffffff); + + if (traceystep<0) // use 2's complement, not signed magnitude + traceystep = -(traceystep&0x7fffffff); + +// +// we assume viewx,viewy is not inside a solid tile, so go ahead one step +// + + do // until a solid tile is hit + { + otx = tile.x; + oty = tile.y; + spotvis[otx][oty] = true; + tracex += tracexstep; + tracey -= traceystep; + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) ) + { + // + // trace crossed two solid tiles, so do a binary search along the line + // to find a spot where only one tile edge is crossed + // + searchsteps = 0; + searchx = tracexstep; + searchy = traceystep; + do + { + searchx/=2; + searchy/=2; + if (tile.x!=otx && tile.y!=oty) + { + // still too far + tracex -= searchx; + tracey += searchy; + } + else + { + // not far enough, no tiles crossed + tracex += searchx; + tracey -= searchy; + } + + // + // if it is REAL close, go for the most clockwise intersection + // + if (++searchsteps == 16) + { + tracex = (long)otx<0) + { + if (traceystep<0) + { + tracex += TILEGLOBAL-1; + tracey += TILEGLOBAL; + } + else + { + tracex += TILEGLOBAL; + } + } + else + { + if (traceystep<0) + { + tracex --; + tracey += TILEGLOBAL-1; + } + else + { + tracey --; + } + } + } + + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) ); + } + } while (!(tilecolor = tilemap[tile.x][tile.y]) ); + +} + +//========================================================================== + + +/* +======================== += += FixedByFrac += += multiply a 16/16 bit, 2's complement fixed point number by a 16 bit += fraction, passed as a signed magnitude 32 bit number += +======================== +*/ + +#pragma warn -rvl // I stick the return value in with ASMs + +fixed FixedByFrac (fixed a, fixed b) +{ + fixed value; + +// +// setup +// +asm mov si,[WORD PTR b+2] // sign of result = sign of fraction + +asm mov ax,[WORD PTR a] +asm mov cx,[WORD PTR a+2] + +asm or cx,cx +asm jns aok: // negative? +asm not ax +asm not cx +asm add ax,1 +asm adc cx,0 +asm xor si,0x8000 // toggle sign of result +aok: + +// +// multiply cx:ax by bx +// +asm mov bx,[WORD PTR b] +asm mul bx // fraction*fraction +asm mov di,dx // di is low word of result +asm mov ax,cx // +asm mul bx // units*fraction +asm add ax,di +asm adc dx,0 + +// +// put result dx:ax in 2's complement +// +asm test si,0x8000 // is the result negative? +asm jz ansok: +asm not ax +asm not dx +asm add ax,1 +asm adc dx,0 + +ansok:; + +} + +#pragma warn +rvl + +//========================================================================== + + +/* +======================== += += TransformPoint += += Takes paramaters: += gx,gy : globalx/globaly of point += += globals: += viewx,viewy : point of view += viewcos,viewsin : sin/cos of viewangle += += += defines: += CENTERX : pixel location of center of view window += TILEGLOBAL : size of one += FOCALLENGTH : distance behind viewx/y for center of projection += scale : conversion from global value to screen value += += returns: += screenx,screenheight: projected edge location and size += +======================== +*/ + +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + *screenx = CENTERX + ny/ratio; + + *screenheight = TILEGLOBAL/ratio; + +} + + +// +// transform actor +// +void TransformActor (objtype *ob) +{ + int ratio; + fixed gx,gy,gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = ob->x-viewx; + gy = ob->y-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt-ob->size; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + ob->viewx = CENTERX + ny/ratio; + + ob->viewheight = TILEGLOBAL/ratio; +} + +//========================================================================== + +fixed TransformX (fixed gx, fixed gy) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + + return gxt-gyt; +} + +//========================================================================== + +/* +================== += += BuildTables += += Calculates: += += scale projection constant += sintable/costable overlapping fractional tables += firstangle/lastangle angles from focalpoint to left/right view edges += prestep distance from focal point before checking for tiles += +================== +*/ + +void BuildTables (void) +{ + int i; + long intang; + long x; + float angle,anglestep,radtoint; + double tang; + fixed value; + +// +// calculate the angle offset from view angle of each pixel's ray +// + radtoint = (float)FINEANGLES/2/PI; + for (i=0;iMAXINT) + intang = 0x8f00 | (intang & 0xff); + else if (intangx1 = VIEWXH+1; + rightwall->height1 = 32000; + (rightwall+1)->x1 = 32000; + + leftx = -1; + + for (wall=&walls[1];wall= wall->x2) + continue; + + rightclip = wall->x2; + + check = wall+1; + while (check->x1 <= rightclip && check->height1 >= wall->height2) + { + rightclip = check->x1-1; + check++; + } + + if (rightclip>VIEWXH) + rightclip=VIEWXH; + + if (leftx < wall->x1 - 1) + newleft = wall->x1-1; // there was black space between walls + else + newleft = leftx; + + if (rightclip > newleft) + { + wall->leftclip = newleft+1; + wall->rightclip = rightclip; + DrawVWall (wall); + leftx = rightclip; + } + } + +#ifndef DRAWEACH + ScaleWalls (); // draw all the walls +#endif +} + +//========================================================================== + +/* +===================== += += DrawScaleds += += Draws all objects that are visable += +===================== +*/ + +objtype *depthsort[MAXACTORS]; + +void DrawScaleds (void) +{ + int i,j,least,numvisable,height; + objtype *obj,**vislist,*farthest; + memptr shape; + byte *tilespot,*visspot; + + numvisable = 0; + +// +// calculate base positions of all objects +// + vislist = &depthsort[0]; + + for (obj = player->next;obj;obj=obj->next) + { + if (!obj->state->shapenum) + continue; + + tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley; + visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley; + // + // could be in any of the nine surrounding tiles + // + if (*visspot + || ( *(visspot-1) && !*(tilespot-1) ) + || ( *(visspot+1) && !*(tilespot+1) ) + || ( *(visspot-65) && !*(tilespot-65) ) + || ( *(visspot-64) && !*(tilespot-64) ) + || ( *(visspot-63) && !*(tilespot-63) ) + || ( *(visspot+65) && !*(tilespot+65) ) + || ( *(visspot+64) && !*(tilespot+64) ) + || ( *(visspot+63) && !*(tilespot+63) ) ) + { + obj->active = true; + TransformActor (obj); + if (!obj->viewheight || obj->viewheight > VIEWWIDTH) + continue; // too close or far away + + *vislist++ = obj; + numvisable++; + } + } + + if (vislist == &depthsort[0]) + return; // no visable objects + +// +// draw from back to front +// + for (i = 0; iviewheight; + if (height < least) + { + least = height; + farthest = depthsort[j]; + } + } + // + // draw farthest + // + shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC]; + ScaleShape(farthest->viewx,shape,farthest->viewheight); + farthest->viewheight = 32000; + } +} + +//========================================================================== + + +/* +===================== += += CalcTics += +===================== +*/ + +void CalcTics (void) +{ + long newtime,oldtimecount; + + +#ifdef PROFILE + tics = 1; + return; +#endif + +// +// calculate tics since last refresh for adaptive timing +// + if (lasttimecount > TimeCount) + TimeCount = lasttimecount; // if the game was paused a LONG time + + if (DemoMode) // demo recording and playback needs + { // to be constant +// +// take DEMOTICS or more tics, and modify Timecount to reflect time taken +// + oldtimecount = lasttimecount; + while (TimeCountMAXTICS) + { + TimeCount -= (tics-MAXTICS); + tics = MAXTICS; + } + } +} + + +//========================================================================== + + +/* +======================== += += DrawHand += +======================== +*/ + +void DrawHand (void) +{ + int picnum; + memptr source; + unsigned dest,width,height; + + picnum = HAND1PICM; + if (gamestate.shotpower || boltsleft) + picnum += (((unsigned)TimeCount>>3)&1); + + source = grsegs[picnum]; + dest = ylookup[VIEWHEIGHT-handheight]+12+bufferofs; + width = picmtable[picnum-STARTPICM].width; + height = picmtable[picnum-STARTPICM].height; + + VW_MaskBlock(source,0,dest,width,handheight,width*height); + EGAMAPMASK(15); +} + +//========================================================================== + + +/* +======================== += += ThreeDRefresh += +======================== +*/ + +void ThreeDRefresh (void) +{ + int tracedir; + +restart: + aborttrace = false; + +// +// clear out the traced array +// +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET spotvis +asm xor ax,ax +asm mov cx,[mapwidth] // mapheight*32 words +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm rep stosw + + +// +// set up variables for this view +// + + viewangle = player->angle; + fineviewangle = viewangle*(FINEANGLES/ANGLES); + viewsin = sintable[viewangle]; + viewcos = costable[viewangle]; + viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos); + viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin); + viewx &= 0xfffffc00; // stop on a pixel boundary + viewy &= 0xfffffc00; + viewx += 0x180; + viewy += 0x180; + viewxpix = viewx>>10; + viewypix = viewy>>10; + + focal.x = viewx>>TILESHIFT; + focal.y = viewy>>TILESHIFT; + +// +// find the rightmost visable tile in view +// + tracedir = viewangle + lastangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + right.x = tile.x; + right.y = tile.y; + +// +// find the leftmost visable tile in view +// + tracedir = viewangle + firstangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + +// +// follow the walls from there to the right +// + rightwall = &walls[1]; + FollowWalls (); + + if (aborttrace) + goto restart; + +// +// actually draw stuff +// + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + +// +// draw the wall list saved be FollowWalls () +// + animframe = (TimeCount&8)>>3; + +// +// draw all the scaled images +// + asm mov dx,GC_INDEX + + asm mov ax,GC_COLORDONTCARE + asm out dx,ax // don't look at any of the planes + + asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2 + asm out dx,ax + + asm mov al,GC_BITMASK + asm out dx,al + + DrawWallList(); + DrawScaleds(); + + EGAWRITEMODE(0); + EGABITMASK(0xff); + +// +// draw hand +// + if (handheight) + DrawHand (); + +// +// show screen and time last cycle +// + if (fizzlein) + { + fizzlein = false; + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true); + lasttimecount = TimeCount; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +asm cli +asm mov cx,[bufferofs] +asm mov dx,3d4h // CRTC address register +asm mov al,0ch // start address high register +asm out dx,al +asm inc dx +asm mov al,ch +asm out dx,al // set the high byte +asm dec dx +asm mov al,0dh // start address low register +asm out dx,al +asm inc dx +asm mov al,cl +asm out dx,al // set the low byte +asm sti + + displayofs = bufferofs; + + CalcTics (); + +} + diff --git a/src/lib/hb/c3_game.c b/src/lib/hb/c3_game.c new file mode 100755 index 00000000..a71d2546 --- /dev/null +++ b/src/lib/hb/c3_game.c @@ -0,0 +1,1199 @@ +/* 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_GAME.C + +#include "C3_DEF.H" +#pragma hdrstop + +#ifdef PROFILE +#include "TIME.H" +#endif + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define NUMLUMPS 25 + +#define CONTROLSLUMP 0 +#define ORCLUMP 1 +#define TROLLLUMP 2 +#define WARPLUMP 3 +#define BOLTLUMP 4 +#define NUKELUMP 5 +#define POTIONLUMP 6 +#define RKEYLUMP 7 +#define YKEYLUMP 8 +#define GKEYLUMP 9 +#define BKEYLUMP 10 +#define SCROLLLUMP 11 +#define CHESTLUMP 12 +#define PLAYERLUMP 13 +#define WALL1LUMP 14 +#define WALL2LUMP 15 +#define BDOORLUMP 16 +#define DEMONLUMP 17 +#define MAGELUMP 18 +#define BATLUMP 19 +#define GRELLUMP 20 +#define GOALLUMP 21 + + +int lumpstart[NUMLUMPS] = { +CONTROLS_LUMP_START, +ORC_LUMP_START, +TROLL_LUMP_START, +WARP_LUMP_START, +BOLT_LUMP_START, +NUKE_LUMP_START, +POTION_LUMP_START, +RKEY_LUMP_START, +YKEY_LUMP_START, +GKEY_LUMP_START, +BKEY_LUMP_START, +SCROLL_LUMP_START, +CHEST_LUMP_START, +PLAYER_LUMP_START, +WALL1_LUMP_START, +WALL2_LUMP_START, +BDOOR_LUMP_START, +DEMON_LUMP_START, +MAGE_LUMP_START, +BAT_LUMP_START, +GREL_LUMP_START, +NEMESISPIC +}; + + +int lumpend[NUMLUMPS] = { +CONTROLS_LUMP_END, +ORC_LUMP_END, +TROLL_LUMP_END, +WARP_LUMP_END, +BOLT_LUMP_END, +NUKE_LUMP_END, +POTION_LUMP_END, +RKEY_LUMP_END, +YKEY_LUMP_END, +GKEY_LUMP_END, +BKEY_LUMP_END, +SCROLL_LUMP_END, +CHEST_LUMP_END, +PLAYER_LUMP_END, +WALL1_LUMP_END, +WALL2_LUMP_END, +BDOOR_LUMP_END, +DEMON_LUMP_END, +MAGE_LUMP_END, +BAT_LUMP_END, +GREL_LUMP_END, +NEMESISPIC +}; + + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +unsigned latchpics[NUMLATCHPICS]; +unsigned tileoffsets[NUMTILE16]; +unsigned textstarts[27]; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean lumpneeded[NUMLUMPS]; + + +//=========================================================================== + + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +void ScanInfoPlane (void) +{ + unsigned x,y,i,j; + int tile; + unsigned far *start; + + InitObjList(); // start spawning things with a clean slate + + memset (lumpneeded,0,sizeof(lumpneeded)); + + start = mapsegs[2]; + for (y=0;ywidth; + mapheight = mapheaderseg[mapon]->height; + +// +// make a lookup table for the maps left edge +// + spot = 0; + for (y=0;y=EXPWALLSTART && tile0) + (unsigned)actorat[x][y] = tile; + } + } + + +// +// decide which graphics are needed and spawn actors +// + ScanInfoPlane (); + +// +// have the caching manager load and purge stuff to make sure all marks +// are in memory +// + CA_LoadAllSounds (); + +} + + +//========================================================================== + +/* +===================== += += LatchDrawPic += +===================== +*/ + +void LatchDrawPic (unsigned x, unsigned y, unsigned picnum) +{ + unsigned wide, height, source, dest; + + wide = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + dest = bufferofs + ylookup[y]+x; + source = latchpics[picnum-FIRSTLATCHPIC]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,[wide] + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,[height] // scan lines to draw +asm mov ax,[wide] + +lineloop: +asm mov cx,ax +asm rep movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + + +//========================================================================== + +/* +===================== += += Victory += +===================== +*/ + +void Victory (void) +{ + FreeUpMemory (); + NormalScreen (); + CA_CacheGrChunk (FINALEPIC); + VWB_DrawPic (0,0,FINALEPIC); + UNMARKGRCHUNK(FINALEPIC); + VW_UpdateScreen (); + SD_PlaySound (GETBOLTSND); + SD_WaitSoundDone (); + SD_PlaySound (GETNUKESND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOTIONSND); + SD_WaitSoundDone (); + SD_PlaySound (GETKEYSND); + SD_WaitSoundDone (); + SD_PlaySound (GETSCROLLSND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOINTSSND); + SD_WaitSoundDone (); + IN_ClearKeysDown (); + IN_Ack(); +} + +//========================================================================== + +/* +=================== += += Died += +=================== +*/ + +void Died (void) +{ + unsigned page1,page2; +// +// fizzle fade screen to grey +// + FreeUpMemory (); + SD_PlaySound (GAMEOVERSND); + bufferofs = screenloc[(screenpage+1)%3]; + LatchDrawPic(0,0,DEADPIC); + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false); + IN_ClearKeysDown(); + IN_Ack(); + VW_SetScreen (bufferofs,0); +} + +//========================================================================== + +/* +=================== += += NormalScreen += +=================== +*/ + +void NormalScreen (void) +{ + VW_SetSplitScreen (200); + bufferofs = displayofs = SCREEN1START; + VW_Bar(0,0,320,200,0); + bufferofs = SCREEN2START; + VW_Bar(0,0,320,200,0); + VW_SetScreen (displayofs,0); +} + +//========================================================================== + +/* +=================== += += DrawPlayScreen += +=================== +*/ + +void DrawPlayScreen (void) +{ + int i,j,p,m; + + screenpage = 0; + + bufferofs = 0; + VW_Bar (0,0,320,STATUSLINES,7); + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar (0,0,320,VIEWHEIGHT,7); + } + + + VW_SetSplitScreen(144); + VW_SetScreen(screenloc[0],0); + bufferofs = 0; + + CA_CacheGrChunk (STATUSPIC); + CA_CacheGrChunk (SIDEBARSPIC); + + VW_DrawPic (0,0,STATUSPIC); + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_DrawPic (33,0,SIDEBARSPIC); + } + + grneeded[STATUSPIC]&= ~ca_levelbit; + grneeded[SIDEBARSPIC]&= ~ca_levelbit; + MM_SetPurge(&grsegs[STATUSPIC],3); + MM_SetPurge(&grsegs[SIDEBARSPIC],3); + + RedrawStatusWindow (); + bufferofs = displayofs = screenloc[0]; +} + + +//========================================================================== + +/* +=================== += += LoadLatchMem += +=================== +*/ + +void LoadLatchMem (void) +{ + int i,j,p,m; + byte far *src, far *dest; + unsigned destoff; + + EGAWRITEMODE(0); + +// +// draw some pics into latch memory +// + +// +// tile 8s +// + latchpics[0] = freelatch; + src = (byte _seg *)grsegs[STARTTILE8]; + dest = MK_FP(0xa000,freelatch); + + for (i=0;iwidth || y>height) + continue; + drawofs = source+ylookup[y]; + + asm mov cx,[x] + asm mov si,cx + asm and si,7 + asm mov dx,GC_INDEX + asm mov al,GC_BITMASK + asm mov ah,BYTE PTR [maskb+si] + asm out dx,ax + + asm mov si,[drawofs] + asm shr cx,1 + asm shr cx,1 + asm shr cx,1 + asm add si,cx + asm mov di,si + asm add di,[pagedelta] + + asm mov dx,GC_INDEX + asm mov al,GC_READMAP // leave GC_INDEX set to READMAP + asm out dx,al + + asm mov dx,SC_INDEX+1 + asm mov al,1 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,0 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,2 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,1 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,4 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,2 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + asm mov dx,SC_INDEX+1 + asm mov al,8 + asm out dx,al + asm mov dx,GC_INDEX+1 + asm mov al,3 + asm out dx,al + + asm mov bl,[es:si] + asm xchg [es:di],bl + + if (rndval == 1) // entire sequence has been completed + { + EGABITMASK(255); + EGAMAPMASK(15); + return; + }; + } + frame++; + while (TimeCountname); + + // + // level + // + ultoa(s->completed,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (25 * 8) - 8 - w; + US_Print(buffer); + + // + // score + // + ultoa(s->score,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (34 * 8) - 8 - w; + US_Print(buffer); + } + + fontcolor = F_BLACK; +} + + + +/* +======================= += += CheckHighScore += +======================= +*/ + +void CheckHighScore (long score,word other) +{ + word i,j; + int n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + n = i; + HighScoresDirty = true; + break; + } + } + + if (n != -1) + { + // + // got a high score + // + DrawHighScores (); + PrintY = 68 + (16 * n); + PrintX = 60; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100); + } +} + + +//========================================================================== + +/* +=================== += += GameLoop += +=================== +*/ + +void GameLoop (void) +{ + int i,xl,yl,xh,yh; + char num[20]; +#ifdef PROFILE + clock_t start,end; +#endif + + DrawPlayScreen (); + +restart: + if (!loadedgame) + { + gamestate.difficulty = restartgame; + restartgame = gd_Continue; + DrawEnterScreen (); + } + + do + { + playstate = gd_Continue; + if (!loadedgame) + SetupGameLevel (); + else + loadedgame = false; + + CacheScaleds (); + +#ifdef PROFILE +start = clock(); +while (start == clock()); +start++; +#endif + PlayLoop (); +#ifdef PROFILE +end = clock(); +itoa(end-start,str,10); + Quit (str); +#endif + + + switch (playstate) + { + case ex_died: + Died (); + NormalScreen (); + FreeUpMemory (); + CheckHighScore (gamestate.score,gamestate.mapon+1); + return; + case ex_warped: + FizzleOut (true); + if (gamestate.mapon >= NUMLEVELS) + { + Victory (); + FreeUpMemory (); + CheckHighScore(gamestate.score,gamestate.mapon+1); + return; + } + break; + case ex_abort: + FreeUpMemory (); + return; + case ex_resetgame: + case ex_loadedgame: + goto restart; + case ex_victorious: + Victory (); + FreeUpMemory(); + CheckHighScore(gamestate.score,gamestate.mapon+1); + return; + } + + } while (1); + +} diff --git a/src/lib/hb/c3_main.c b/src/lib/hb/c3_main.c new file mode 100755 index 00000000..684cb329 --- /dev/null +++ b/src/lib/hb/c3_main.c @@ -0,0 +1,756 @@ +/* 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_MAIN.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + CATACOMB 3-D + + An Id Software production + + by John Carmack + +============================================================================= +*/ + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +memptr scalesegs[NUMPICS]; +char str[80],str2[20]; +unsigned tedlevelnum; +boolean tedlevel; +gametype gamestate; +exittype playstate; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +//=========================================================================== + +// JAB Hack begin +#define MyInterrupt 0x60 +void interrupt (*intaddr)(); +void interrupt (*oldintaddr)(); + char *JHParmStrings[] = {"no386",nil}; + +void +jabhack(void) +{ +extern void far jabhack2(void); +extern int far CheckIs386(void); + + int i; + + oldintaddr = getvect(MyInterrupt); + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],JHParmStrings) == 0) + return; + + if (CheckIs386()) + { + jabhack2(); + setvect(MyInterrupt,intaddr); + } +} + +void +jabunhack(void) +{ + setvect(MyInterrupt,oldintaddr); +} +// JAB Hack end + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +void NewGame (void) +{ + memset (&gamestate,0,sizeof(gamestate)); + gamestate.mapon = 0; + gamestate.body = MAXBODY; +} + +//=========================================================================== + +#define RLETAG 0xABCD + +/* +================== += += SaveTheGame += +================== +*/ + +boolean SaveTheGame(int file) +{ + word i,compressed,expanded; + objtype *o; + memptr bigbuffer; + + if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Write planes 0 and 2 + { +// +// leave a word at start of compressed data for compressed length +// + compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i] + ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG); + + *(unsigned huge *)bigbuffer = compressed; + + if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + } + + for (o = player;o;o = o->next) + if (!CA_FarWrite(file,(void far *)o,sizeof(objtype))) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + MM_FreePtr (&bigbuffer); + + return(true); +} + +//=========================================================================== + +/* +================== += += LoadTheGame += +================== +*/ + +boolean LoadTheGame(int file) +{ + unsigned i,x,y; + objtype *obj,*prev,*next,*followed; + unsigned compressed,expanded; + unsigned far *map,tile; + memptr bigbuffer; + + if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + SetupGameLevel (); // load in and cache the base old level + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Read planes 0 and 2 + { + if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + if (!CA_FarRead(file,(void far *)bigbuffer,compressed) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + CA_RLEWexpand ((unsigned huge *)bigbuffer, + (unsigned huge *)mapsegs[i],expanded,RLETAG); + } + + MM_FreePtr (&bigbuffer); +// +// copy the wall data to a data segment array again, to handle doors and +// bomb walls that are allready opened +// + memset (tilemap,0,sizeof(tilemap)); + memset (actorat,0,sizeof(actorat)); + map = mapsegs[0]; + for (y=0;y0) + (unsigned)actorat[x][y] = tile; + } + } + + + // Read the object list back in - assumes at least one object in list + + InitObjList (); + new = player; + while (true) + { + prev = new->prev; + next = new->next; + if (!CA_FarRead(file,(void far *)new,sizeof(objtype))) + return(false); + followed = new->next; + new->prev = prev; + new->next = next; + actorat[new->tilex][new->tiley] = new; // drop a new marker + + if (followed) + GetNewObj (false); + else + break; + } + + return(true); +} + +//=========================================================================== + +/* +================== += += ResetGame += +================== +*/ + +void ResetGame(void) +{ + NewGame (); + + ca_levelnum--; + ca_levelbit>>=1; + CA_ClearMarks(); + ca_levelbit<<=1; + ca_levelnum++; +} + +//=========================================================================== + + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); +#ifndef PROFILE + SD_Shutdown (); + IN_Shutdown (); +#endif + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + + +//=========================================================================== + +/* +========================== += += InitGame += += Load a few things right away += +========================== +*/ + +void InitGame (void) +{ + unsigned segstart,seglength; + int i,x,y; + unsigned *blockstart; + +// US_TextScreen(); + + MM_Startup (); + VW_Startup (); +#ifndef PROFILE + IN_Startup (); + SD_Startup (); +#endif + US_Startup (); + +// US_UpdateTextScreen(); + + CA_Startup (); + US_Setup (); + + US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame); + +// +// load in and lock down some basic chunks +// + + CA_ClearMarks (); + + CA_MarkGrChunk(STARTFONT); + CA_MarkGrChunk(STARTTILE8); + CA_MarkGrChunk(STARTTILE8M); + CA_MarkGrChunk(HAND1PICM); + CA_MarkGrChunk(HAND2PICM); + CA_MarkGrChunk(ENTERPLAQUEPIC); + + CA_CacheMarks (NULL); + + MM_SetLock (&grsegs[STARTFONT],true); + MM_SetLock (&grsegs[STARTTILE8],true); + MM_SetLock (&grsegs[STARTTILE8M],true); + MM_SetLock (&grsegs[HAND1PICM],true); + MM_SetLock (&grsegs[HAND2PICM],true); + MM_SetLock (&grsegs[ENTERPLAQUEPIC],true); + + fontcolor = WHITE; + + +// +// build some tables +// + for (i=0;i= MINMEMORY) + return; + + CA_CacheGrChunk (OUTOFMEM); + finscreen = (unsigned)grsegs[OUTOFMEM]; + ShutdownId (); + movedata (finscreen,7,0xb800,0,4000); + gotoxy (1,24); + exit(1); +} + +//=========================================================================== + + +/* +========================== += += main += +========================== +*/ + +void main (void) +{ + short i; + + if (stricmp(_argv[1], "/VER") == 0) + { + printf("Catacomb 3-D version 1.22 (Rev 1)\n"); + printf("Copyright 1991-93 Softdisk Publishing\n"); + printf("Developed for use with 100%% IBM compatibles\n"); + printf("that have 640K memory and DOS version 3.3 or later\n"); + printf("and EGA graphics or better.\n"); + exit(0); + } + + if (stricmp(_argv[1], "/?") == 0) + { + printf("Catacomb 3-D version 1.22\n"); + printf("Copyright 1991-93 Softdisk Publishing\n\n"); + printf("Syntax:\n"); + printf("CAT3D [/]\n\n"); + printf("Switch What it does\n"); + printf("/? This Information\n"); + printf("/VER Display Program Version Information\n"); + printf("/COMP Fix problems with SVGA screens\n"); + printf("/NOAL No AdLib or SoundBlaster detection\n"); + printf("/NOJOYS Tell program to ignore joystick\n"); + printf("/NOMOUSE Tell program to ignore mouse\n"); + printf("/HIDDENCARD Overrides video detection\n\n"); + printf("Each switch must include a '/' and multiple switches\n"); + printf("must be seperated by at least one space.\n\n"); + + exit(0); + } + + jabhack(); + + InitGame (); + + CheckMemory (); + + LoadLatchMem (); + +#ifdef PROFILE + NewGame (); + GameLoop (); +#endif + +//NewGame (); +//GameLoop (); + + DemoLoop(); + Quit("Demo loop exited???"); +} diff --git a/src/lib/hb/c3_play.c b/src/lib/hb/c3_play.c new file mode 100755 index 00000000..112041f9 --- /dev/null +++ b/src/lib/hb/c3_play.c @@ -0,0 +1,584 @@ +/* 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_PLAY.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define POINTTICS 6 + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +ControlInfo c; +boolean running,slowturn; + +int bordertime; +objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist; + +unsigned farmapylookup[MAPSIZE]; +byte *nearmapylookup[MAPSIZE]; + +boolean singlestep,godmode; +int extravbls; + +// +// replacing refresh manager +// +unsigned mapwidth,mapheight,tics; +boolean compatability; +byte *updateptr; +unsigned mapwidthtable[64]; +unsigned uwidthtable[UPDATEHIGH]; +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2) +#define UPDATESPARESIZE (UPDATEWIDE*2+4) +#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE) +byte update[UPDATESIZE]; + +int mousexmove,mouseymove; +int pointcount,pointsleft; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void CalcBounds (objtype *ob); +void DrawPlayScreen (void); + + +// +// near data map array (wall values only, get text number from far data) +// +byte tilemap[MAPSIZE][MAPSIZE]; +byte spotvis[MAPSIZE][MAPSIZE]; +objtype *actorat[MAPSIZE][MAPSIZE]; + +objtype dummyobj; + +int bordertime; +int objectcount; + +void StopMusic(void); +void StartMusic(void); + + +//========================================================================== + +/////////////////////////////////////////////////////////////////////////// +// +// CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// + +#define MAXX 264 +#define MAXY 146 + +void CenterWindow(word w,word h) +{ + US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); +} + +//=========================================================================== + + +/* +===================== += += CheckKeys += +===================== +*/ + +void CheckKeys (void) +{ + if (screenfaded) // don't do anything with a faded screen + return; + +// +// pause key wierdness can't be checked as a scan code +// + if (Paused) + { + CenterWindow (8,3); + US_PrintCentered ("PAUSED"); + VW_UpdateScreen (); + SD_MusicOff(); + IN_Ack(); + SD_MusicOn(); + Paused = false; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +// +// F1-F7/ESC to enter control panel +// + if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape) + { + StopMusic (); + NormalScreen (); + FreeUpMemory (); + US_CenterWindow (20,8); + US_CPrint ("Loading"); + VW_UpdateScreen (); + US_ControlPanel(); + if (abortgame) + { + playstate = ex_abort; + return; + } + StartMusic (); + IN_ClearKeysDown(); + if (restartgame) + playstate = ex_resetgame; + if (loadedgame) + playstate = ex_loadedgame; + DrawPlayScreen (); + CacheScaleds (); + lasttimecount = TimeCount; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +// +// F10-? debug keys +// + if (Keyboard[sc_F10]) + { + DebugKeys(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + lasttimecount = TimeCount; + } + +} + + +//=========================================================================== + +/* +############################################################################# + + The objlist data structure + +############################################################################# + +objlist containt structures for every actor currently playing. The structure +is accessed as a linked list starting at *player, ending when ob->next == +NULL. GetNewObj inserts a new object at the end of the list, meaning that +if an actor spawn another actor, the new one WILL get to think and react the +same frame. RemoveObj unlinks the given object and returns it to the free +list, but does not damage the objects ->next pointer, so if the current object +removes itself, a linked list following loop can still safely get to the +next element. + + + +############################################################################# +*/ + + +/* +========================= += += InitObjList += += Call to clear out the entire object list, returning them all to the free += list. Allocates a special spot for the player. += +========================= +*/ + +void InitObjList (void) +{ + int i; + + for (i=0;iprev; + memset (new,0,sizeof(*new)); + + if (lastobj) + lastobj->next = new; + new->prev = lastobj; // new->next is allready NULL from memset + + new->active = false; + lastobj = new; + + objectcount++; +} + +//=========================================================================== + +/* +========================= += += RemoveObj += += Add the given object back into the free list, and unlink it from it's += neighbors += +========================= +*/ + +void RemoveObj (objtype *gone) +{ + objtype **spotat; + + if (gone == player) + Quit ("RemoveObj: Tried to remove the player!"); + +// +// fix the next object's back link +// + if (gone == lastobj) + lastobj = (objtype *)gone->prev; + else + gone->next->prev = gone->prev; + +// +// fix the previous object's forward link +// + gone->prev->next = gone->next; + +// +// add it back in to the free list +// + gone->prev = objfreelist; + objfreelist = gone; +} + +//========================================================================== + +/* +=================== += += PollControls += +=================== +*/ + +void PollControls (void) +{ + unsigned buttons; + + IN_ReadControl(0,&c); + + if (MousePresent) + { + Mouse(MButtons); + buttons = _BX; + Mouse(MDelta); + mousexmove = _CX; + mouseymove = _DX; + + if (buttons&1) + c.button0 = 1; + if (buttons&2) + c.button1 = 1; + + } + + if (Controls[0]==ctrl_Joystick) + { + if (c.x>120 || c.x <-120 || c.y>120 || c.y<-120) + running = true; + else + running = false; + if (c.x>-48 && c.x<48) + slowturn = true; + else + slowturn = false; + } + else + { + if (Keyboard[sc_RShift]) + running = true; + else + running = false; + if (c.button0) + slowturn = true; + else + slowturn = false; + } +} + +//========================================================================== + +/* +================= += += StopMusic += +================= +*/ + +void StopMusic(void) +{ + int i; + + SD_MusicOff(); + for (i = 0;i < LASTMUSIC;i++) + if (audiosegs[STARTMUSIC + i]) + { + MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3); + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false); + } +} + +//========================================================================== + + +/* +================= += += StartMusic += +================= +*/ + +// JAB - Cache & start the appropriate music for this level +void StartMusic(void) +{ + musicnames chunk; + + SD_MusicOff(); + chunk = TOOHOT_MUS; +// if ((chunk == -1) || (MusicMode != smm_AdLib)) +//DEBUG control panel return; + + MM_BombOnError (false); + CA_CacheAudioChunk(STARTMUSIC + chunk); + MM_BombOnError (true); + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]); + } +} + +//========================================================================== + + +/* +=================== += += PlayLoop += +=================== +*/ + +void PlayLoop (void) +{ + int give; + + void (*think)(); + + ingame = true; + playstate = TimeCount = 0; + gamestate.shotpower = handheight = 0; + pointcount = pointsleft = 0; + + DrawLevelNumber (gamestate.mapon); + DrawBars (); + +#ifndef PROFILE + fizzlein = true; // fizzle fade in the first refresh +#endif + TimeCount = lasttimecount = lastnuke = 0; + + PollControls (); // center mouse + StartMusic (); + do + { +#ifndef PROFILE + PollControls(); +#else + c.xaxis = 1; + if (++TimeCount == 300) + return; +#endif + + for (obj = player;obj;obj = obj->next) + if (obj->active) + { + if (obj->ticcount) + { + obj->ticcount-=tics; + while ( obj->ticcount <= 0) + { + think = obj->state->think; + if (think) + { + think (obj); + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + } + + obj->state = obj->state->next; + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + if (!obj->state->tictime) + { + obj->ticcount = 0; + goto nextactor; + } + if (obj->state->tictime>0) + obj->ticcount += obj->state->tictime; + } + } + think = obj->state->think; + if (think) + { + think (obj); + if (!obj->state) + RemoveObj (obj); + } +nextactor:; + } + + + if (bordertime) + { + bordertime -= tics; + if (bordertime<=0) + { + bordertime = 0; + VW_ColorBorder (3); + } + } + + if (pointcount) + { + pointcount -= tics; + if (pointcount <= 0) + { + pointcount += POINTTICS; + give = (pointsleft > 1000)? 1000 : + ( + (pointsleft > 100)? 100 : + ((pointsleft < 20)? pointsleft : 20) + ); + SD_PlaySound (GETPOINTSSND); + AddPoints (give); + pointsleft -= give; + if (!pointsleft) + pointcount = 0; + } + } + + ThreeDRefresh (); + + CheckKeys(); + if (singlestep) + { + VW_WaitVBL(14); + lasttimecount = TimeCount; + } + if (extravbls) + VW_WaitVBL(extravbls); + + }while (!playstate); + StopMusic (); + + ingame = false; + if (bordertime) + { + bordertime = 0; + VW_ColorBorder (3); + } + + if (!abortgame) + AddPoints (pointsleft); + else + abortgame = false; +} + diff --git a/src/lib/hb/c3_sca_a.asm b/src/lib/hb/c3_sca_a.asm new file mode 100755 index 00000000..58a8737a --- /dev/null +++ b/src/lib/hb/c3_sca_a.asm @@ -0,0 +1,153 @@ +; 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. + +IDEAL +MODEL MEDIUM,C + +include "ID_ASM.EQU" + +;=========================================================================== +; +; SCALING GRAPHICS +; +;=========================================================================== + + + +MACRO MAKELAB NUM + +lab&NUM: + +ENDM + +MACRO MAKEREF NUM + +dw OFFSET lab&NUM + +ENDM + + +;========================================================================= + +MAXSCALES equ 256 + + DATASEG + +EXTRN screenseg:WORD +EXTRN linewidth:WORD + +LABEL endtable WORD +labcount = 0 +REPT MAXSCALES +MAKEREF %labcount +labcount = labcount + 1 +ENDM + + + CODESEG + +;================================================== +; +; void scaleline (int scale, unsigned picseg, unsigned maskseg, +; unsigned screen, unsigned width) +; +;================================================== + +PROC ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word +USES si,di +PUBLIC ScaleLine + +; +; modify doline procedure for proper width +; + mov bx,[pixels] + cmp bx,MAXSCALES + jbe @@scaleok + mov bx,MAXSCALES +@@scaleok: + shl bx,1 + mov bx,[endtable+bx] + push [cs:bx] ;save the code that will be modified over + mov [WORD cs:bx],0d18eh ;mov ss,cx + push [cs:bx+2] ;save the code that will be modified over + mov [WORD cs:bx+2],90c3h ;ret / nop + push bx + + mov dx,[linewidth] + + mov di,[WORD screen] + mov es,[screenseg] + + mov si,[WORD scaleptr] + mov ds,[WORD scaleptr+2] + + mov bx,[WORD picptr] + mov ax,[WORD picptr+2] ;will be moved into ss after call + + mov bp,bx + + cli + call doline + sti +; +; restore doline to regular state +; + pop bx ;address of modified code + pop [cs:bx+2] + pop [cs:bx] + + mov ax,ss + mov ds,ax + ret + +;================ +; +; doline +; +; Big unwound scaling routine +; +; ds:si = scale table +; ss:bx = pic data +; es:di = screen location +; +;================ + +doline: + + mov cx,ss + mov ss,ax ;can't call a routine with ss used... + +labcount = 0 + +REPT MAXSCALES + +MAKELAB %labcount +labcount = labcount + 1 + + lodsb ; get scaled pixel number + xlat [ss:bx] ; look it up in the picture + xchg [es:di],al ; load latches and write pixel to screen + add di,dx ; down to next line + +ENDM + + mov ss,cx + ret + +ENDP + +END \ No newline at end of file diff --git a/src/lib/hb/c3_scale.c b/src/lib/hb/c3_scale.c new file mode 100755 index 00000000..018083d3 --- /dev/null +++ b/src/lib/hb/c3_scale.c @@ -0,0 +1,690 @@ +/* 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_SCALE.C + +#include "C3_DEF.H" +#pragma hdrstop + +//const unsigned viewheight = 144; +const unsigned screenbwide = 40; +const byte BACKGROUNDPIX = 5; + +unsigned shapesize[MAXSCALE+1]; +t_compscale _seg *scaledirectory[MAXSCALE+1]; +t_compshape _seg *shapedirectory[NUMSCALEPICS]; +memptr walldirectory[NUMSCALEWALLS]; + +/* +=========================== += += DeplanePic += += Takes a raw bit map of width bytes by height and creates a scaleable shape += += Returns the length of the shape in bytes += += Fills in spotvis (a convenient 64*64 array) with the color values += +=========================== +*/ + +void DeplanePic (int picnum) +{ + byte far *plane0,far *plane1,far *plane2,far *plane3; + byte by0,by1,by2,by3; + unsigned x,y,b,color,shift,width,height; + byte *dest; + +// +// convert ega pixels to byte color values in a temp buffer +// + width = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + + if (width>64 || height!=64) + Quit ("DePlanePic: Bad size shape"); + + memset (spotvis,BACKGROUNDPIX,sizeof(spotvis)); + + plane0 = (byte _seg *)grsegs[picnum]; + plane1 = plane0 + width*height; + plane2 = plane1 + width*height; + plane3 = plane2 + width*height; + + for (y=0;ycode[0]; + toppix = (viewheight-height)/2; + fix = 0; + + for (src=0;src<=64;src++) + { + startpix = fix>>16; + fix += step; + endpix = fix>>16; + + work->start[src] = startpix; + if (endpix>startpix) + work->width[src] = endpix-startpix; + else + work->width[src] = 0; + +// +// mark the start of the code +// + work->codeofs[src] = FP_OFF(code); + +// +// compile some code if the source pixel generates any screen pixels +// + startpix+=toppix; + endpix+=toppix; + + if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64) + continue; + + // + // mov al,[si+src] + // + *code++ = 0x8a; + *code++ = 0x44; + *code++ = src; + + for (;startpix= VIEWHEIGHT) + break; // off the bottom of the view area + if (startpix < 0) + continue; // not into the view area + + // + // and [es:di+heightofs],al + // + *code++ = 0x26; + *code++ = 0x20; + *code++ = 0x85; + *((unsigned far *)code)++ = startpix*screenbwide; + } + + } + +// +// retf +// + *code++ = 0xcb; + + totalsize = FP_OFF(code); + MM_GetPtr (finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); + MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + + +/* +======================== += += BuildCompShape += += typedef struct += { += unsigned width; += unsigned codeofs[64]; += } t_compshape; += += Width is the number of compiled line draws in the shape. The shape += drawing code will assume that the midpoint of the shape is in the += middle of the width. += += The non background pixel data will start at codeofs[width], so codeofs += greater than width will be invalid. += += Each code offset will draw one vertical line of the shape, consisting += of 0 or more segments of scaled pixels. += += The scaled shapes use ss:0-4 as a scratch variable for the far call to += the compiled scaler, so zero it back out after the shape is scaled, or += a "null pointer assignment" will result upon termination. += += Setup for a call to a compiled shape += ----------------------------------- += ax toast += bx toast += cx toast += dx segment of compiled shape += si toast += di byte at top of view area to draw the line in += bp 0 += ss:2 and ds the segment of the compiled scaler to use += es screenseg += += Upon return, ds IS NOT SET to the data segment. Do: += mov ax,ss += mov ds,ax += += += GC_BITMASK set to the pixels to be drawn in the row of bytes under DI += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += += += Code generated for each segment += ------------------------------- += mov bx,[(segend+1)*2] += mov cx,[bx] += mov [BYTE PTR bx],0xc8 // far return += mov ax,[segstart*2] += mov [ss:0],ax // entry point into the compiled scaler += mov ds,dx // (mov ds,cs) the data is after the compiled code += mov si,ofs data += call [bp] // scale some pixels += mov ds,[bp+2] += mov [bx],cx // un patch return += += Code generated after all segments on a line += ------------------------------------------- += retf += +======================== +*/ + +unsigned BuildCompShape (t_compshape _seg **finalspot) +{ + t_compshape _seg *work; + byte far *code; + int firstline,lastline,x,y; + unsigned firstpix,lastpix,width; + unsigned totalsize,pixelofs; + unsigned buff; + + +// MM_GetPtr (&(memptr)work,20000); + EGAWRITEMODE(0); + EGAREADMAP(0); // use ega screen memory for temp buffer + EGAMAPMASK(1); + buff = screenloc[1]; + work = (t_compshape _seg *)(0xa000+(buff+15)/16); + +// +// find the width of the shape +// + firstline = -1; + x=0; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + firstline = x; + break; + } + if (++x == 64) + Quit ("BuildCompShape: No shape data!"); + } while (firstline == -1); + + lastline = -1; + x=63; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + lastline = x; + break; + } + x--; + } while (lastline == -1); + + width = lastline-firstline+1; + + work->width = width; + code = (byte far *)&work->codeofs[width]; + +// +// copy all non background pixels to the work space +// + pixelofs = FP_OFF(code); + + for (x=firstline;x<=lastline;x++) + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + *code++ = spotvis[y][x]; + +// +// start compiling the vertical lines +// + for (x=firstline;x<=lastline;x++) + { + work->codeofs[x-firstline] = FP_OFF(code); + + y=0; + do + { + // + // scan past black background pixels + // + while (spotvis[y][x] == BACKGROUNDPIX && y<64) + y++; + + if (y>63) // no more segments + break; + + firstpix = y+1; // +1 because width is before codeofs + + // + // scan past scalable pixels + // + while (spotvis[y][x] != BACKGROUNDPIX && y<64) + y++; + + if (y>63) + lastpix = 65; + else + lastpix = y+1; // actually one pixel past the last displayed + + // + // compile the scale call + // + *code++ = 0x8b; // mov bx,[lastpix*2] + *code++ = 0x1e; + *((unsigned far *)code)++ = lastpix*2; + + *code++ = 0x8b; // mov cx,[bx] + *code++ = 0x0f; + + *code++ = 0xc6; // move [BYTE bx],0xcb + *code++ = 0x07; + *code++ = 0xcb; + + *code++ = 0xa1; // mov ax,[firstpix*2] /************* + *((unsigned far *)code)++ = firstpix*2; + + *code++ = 0x36; // mov [ss:0],ax + *code++ = 0xa3; + *code++ = 0x00; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,dx (mov ds,cs) + *code++ = 0xda; + + *code++ = 0xbe; // mov si,OFFSET pixelofs-firstpixel + *((unsigned far *)code)++ = pixelofs-firstpix; + + *code++ = 0xff; // call [DWORD bp] + *code++ = 0x5e; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,[bp+2] + *code++ = 0x5e; + *code++ = 0x02; + + *code++ = 0x89; // mov [bx],cx + *code++ = 0x0f; + + pixelofs += (lastpix-firstpix); + } while (y<63); + + // + // retf + // + *code++ = 0xcb; + } + + +// +// copy the final shape to a properly sized buffer +// + totalsize = FP_OFF(code); + MM_GetPtr ((memptr *)finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); +// MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + +/* +======================= += += ScaleShape += += Draws a compiled shape at [scale] pixels high += += Setup for call += -------------- += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += GC_INDEX pointing at GC_BITMASK += +======================= +*/ + +static long longtemp; + +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale) +{ + t_compscale _seg *comptable; + unsigned width,scalewidth; + int x,pixel,lastpixel,pixwidth,min; + unsigned far *codehandle, far *widthptr; + unsigned badcodeptr; + int rightclip; + + if (!compshape) + Quit ("ScaleShape: NULL compshape ptr!"); + + scale = (scale+1)/2; + if (!scale) + return; // too far away + if (scale>MAXSCALE) + scale = MAXSCALE; + comptable = scaledirectory[scale]; + + width = compshape->width; + scalewidth = comptable->start[width]; + + pixel = xcenter - scalewidth/2; + lastpixel = pixel+scalewidth-1; + if (pixel >= VIEWWIDTH || lastpixel < 0) + return; // totally off screen + +// +// scan backwards from the right edge until pixels are visable +// rightclip is the first NON VISABLE pixel +// + if (lastpixel>=VIEWWIDTH-1) + rightclip = VIEWWIDTH-1; + else + rightclip = lastpixel; + + if (zbuffer[rightclip]>scale) + { + if (pixel>0) + min = pixel; + else + min = 0; + do + { + if (--rightclip < min) + return; // totally covered or before 0 + if (zbuffer[rightclip]<=scale) + break; + } while (1); + } + rightclip++; + +// +// scan from the left until it is on screen, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + *(((unsigned *)&longtemp)+1) = (unsigned)compshape; // seg of shape + codehandle = &compshape->codeofs[0]; + badcodeptr = compshape->codeofs[width]; + widthptr = &comptable->width[0]; + asm mov ax,[comptable] + asm mov WORD PTR [2],ax // ds:0-4 is used as a far call pointer + // by the compiled shapes + pixwidth = *widthptr; // scaled width of this pixel + while (!pixwidth) + { + pixwidth = *++widthptr; // find the first visable pixel + codehandle++; + } + + if (pixel<0) + { + do + { + if (pixel+pixwidth>0) + { + pixwidth += pixel; + pixel = 0; + break; + } + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + pixel+=pixwidth; + } while (1); + } + +// +// scan until it is visable, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + do + { + if (zbuffer[pixel] <= scale) + break; // start drawing here + pixel++; + if (!--pixwidth) + { + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + } + } while (1); + + if (pixel+pixwidth>rightclip) + pixwidth = rightclip-pixel; +// +// draw lines +// + do // while (1) + { + // + // scale a vertical segment [pixwidth] pixels wide at [pixel] + // + (unsigned)longtemp = *codehandle; // offset of compiled code + if ((unsigned)longtemp == badcodeptr) + Quit ("ScaleShape: codehandle past end!"); + + asm mov bx,[pixel] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm push bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + + asm mov es,[screenseg] + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + asm pop bx + asm mov al,BYTE PTR [bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + + // + // advance to the next drawn line + // +nosecond:; + if ( (pixel+=pixwidth) == rightclip ) + { + asm mov WORD PTR [0],0 + asm mov WORD PTR [2],0 + return; // all done! + } + + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + + if (pixel+pixwidth > rightclip) + pixwidth = rightclip-pixel; + + } while (1); + +} + +// +// bit mask tables for drawing scaled strips up to eight pixels wide +// + +byte bitmasks1[8][8] = { +{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}, +{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f}, +{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f}, +{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f}, +{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf}, +{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7}, +{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3}, +{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} }; + +byte bitmasks2[8][8] = { +{0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0x80}, +{0,0,0,0,0,0,0x80,0xc0}, +{0,0,0,0,0,0x80,0xc0,0xe0}, +{0,0,0,0,0x80,0xc0,0xe0,0xf0}, +{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8}, +{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc}, +{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} }; + + + + + + diff --git a/src/lib/hb/c3_state.c b/src/lib/hb/c3_state.c new file mode 100755 index 00000000..8c31eb06 --- /dev/null +++ b/src/lib/hb/c3_state.c @@ -0,0 +1,546 @@ +/* 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_STATE.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype opposite[9] = + {south,west,north,east,southwest,northwest,northeast,southeast,nodir}; + + + +//=========================================================================== + + +/* +=================== += += SpawnNewObj += +=================== +*/ + +void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size) +{ + GetNewObj (false); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + + new->tilex = x; + new->tiley = y; + new->x = ((long)x<y = ((long)y<dir = nodir; + + actorat[new->tilex][new->tiley] = new; +} + +void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size) +{ + GetNewObj (false); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + new->active = true; + + new->x = x; + new->y = y; + new->tilex = x>>TILESHIFT; + new->tiley = y>>TILESHIFT; + CalcBounds(new); + new->distance = 100; + new->dir = nodir; +} + + + +/* +=================== += += CheckHandAttack += += If the object can move next to the player, it will return true += +=================== +*/ + +boolean CheckHandAttack (objtype *ob) +{ + long deltax,deltay,size; + + size = (long)ob->size + player->size + ob->speed*tics; + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax > size || deltax < -size || deltay > size || deltay < -size) + return false; + + return true; +} + + +/* +=================== += += T_DoDamage += += Attacks the player if still nearby, then immediately changes to next state += +=================== +*/ + +void T_DoDamage (objtype *ob) +{ + int points; + + + if (!CheckHandAttack (ob)) + { + SD_PlaySound (MONSTERMISSSND); + } + else + { + points = 0; + + switch (ob->obclass) + { + case orcobj: + points = 4; + break; + case trollobj: + points = 8; + break; + case demonobj: + points = 15; + break; + } + TakeDamage (points); + } + + ob->state = ob->state->next; +} + + +//========================================================================== + +/* +================================== += += Walk += +================================== +*/ + +boolean Walk (objtype *ob) +{ + switch (ob->dir) + { + case north: + if (actorat[ob->tilex][ob->tiley-1]) + return false; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case northeast: + if (actorat[ob->tilex+1][ob->tiley-1]) + return false; + ob->tilex++; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case east: + if (actorat[ob->tilex+1][ob->tiley]) + return false; + ob->tilex++; + ob->distance = TILEGLOBAL; + return true; + + case southeast: + if (actorat[ob->tilex+1][ob->tiley+1]) + return false; + ob->tilex++; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case south: + if (actorat[ob->tilex][ob->tiley+1]) + return false; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case southwest: + if (actorat[ob->tilex-1][ob->tiley+1]) + return false; + ob->tilex--; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case west: + if (actorat[ob->tilex-1][ob->tiley]) + return false; + ob->tilex--; + ob->distance = TILEGLOBAL; + return true; + + case northwest: + if (actorat[ob->tilex-1][ob->tiley-1]) + return false; + ob->tilex--; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case nodir: + return false; + } + + Quit ("Walk: Bad dir"); + return false; +} + + + +/* +================================== += += ChaseThink += have the current monster go after the player, += either diagonally or straight on += +================================== +*/ + +void ChaseThink (objtype *obj, boolean diagonal) +{ + int deltax,deltay,i; + dirtype d[3]; + dirtype tdir, olddir, turnaround; + + + olddir=obj->dir; + turnaround=opposite[olddir]; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + d[1]=nodir; + d[2]=nodir; + + if (deltax>0) + d[1]= east; + if (deltax<0) + d[1]= west; + if (deltay>0) + d[2]=south; + if (deltay<0) + d[2]=north; + + if (abs(deltay)>abs(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 (diagonal) + { /*ramdiagonals try the best dir first*/ + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + } + else + { /*ramstraights try the second best dir first*/ + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; + } + } + +/* there is no direct path to the player, so pick another direction */ + + obj->dir=olddir; + if (Walk(obj)) + return; + + if (US_RndT()>128) /*randomly determine direction of search*/ + { + for (tdir=north;tdir<=west;tdir++) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + + obj->dir=turnaround; + Walk(obj); /*last chance, don't worry about returned value*/ +} + + +/* +================= += += MoveObj += +================= +*/ + +void MoveObj (objtype *ob, long move) +{ + ob->distance -=move; + + switch (ob->dir) + { + case north: + ob->y -= move; + return; + case northeast: + ob->x += move; + ob->y -= move; + return; + case east: + ob->x += move; + return; + case southeast: + ob->x += move; + ob->y += move; + return; + case south: + ob->y += move; + return; + case southwest: + ob->x -= move; + ob->y += move; + return; + case west: + ob->x -= move; + return; + case northwest: + ob->x -= move; + ob->y -= move; + return; + + case nodir: + return; + } +} + + +/* +================= += += Chase += += returns true if hand attack range += +================= +*/ + +boolean Chase (objtype *ob, boolean diagonal) +{ + long move; + long deltax,deltay,size; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) + { + CalcBounds (ob); + return true; + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + ChaseThink (ob,diagonal); + if (!ob->distance) + break; // no possible move + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); + return false; +} + +//=========================================================================== + + +/* +=================== += += ShootActor += +=================== +*/ + +void ShootActor (objtype *ob, unsigned damage) +{ + ob->hitpoints -= damage; + if (ob->hitpoints<=0) + { + switch (ob->obclass) + { + case orcobj: + ob->state = &s_orcdie1; + GivePoints (100); + break; + case trollobj: + ob->state = &s_trolldie1; + GivePoints (400); + break; + case demonobj: + ob->state = &s_demondie1; + GivePoints (1000); + break; + case mageobj: + ob->state = &s_magedie1; + GivePoints (600); + break; + case batobj: + ob->state = &s_batdie1; + GivePoints (100); + break; + case grelmobj: + ob->state = &s_greldie1; + GivePoints (10000); + break; + + } + ob->obclass = inertobj; + ob->shootable = false; + actorat[ob->tilex][ob->tiley] = NULL; + } + else + { + switch (ob->obclass) + { + case orcobj: + ob->state = &s_orcouch; + break; + case trollobj: + ob->state = &s_trollouch; + break; + case demonobj: + ob->state = &s_demonouch; + break; + case mageobj: + ob->state = &s_mageouch; + break; + case grelmobj: + ob->state = &s_grelouch; + break; + + } + } + ob->ticcount = ob->state->tictime; +} + diff --git a/src/lib/hb/c3_trace.c b/src/lib/hb/c3_trace.c new file mode 100755 index 00000000..45cb1bca --- /dev/null +++ b/src/lib/hb/c3_trace.c @@ -0,0 +1,872 @@ +/* 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_TRACE.C + +#include "C3_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +// +// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0 +// depending on if tile.x,tile.y,wallon is visable from focal point +// +#define TESTWALLVISABLE { \ + if (tile.yfocal.x) \ + voffset += 2; \ + wallvisable = visable[voffset][wallon]; } + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean aborttrace; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +unsigned wallvisable,voffset; + + +fixed edgex,edgey; + +int wallon; +int basecolor; + +walltype *oldwall; + +// +// offsets from upper left corner of a tile to the left and right edges of +// a given wall (NORTH-WEST) +// +fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 }; +fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 }; + +fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 }; +fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1}; + + +// +// offset from tile.x,tile.y of the tile that shares wallon side +// (side is not visable if it is shared) +// +int sharex[4] = { 0, 1, 0,-1}; +int sharey[4] = {-1, 0, 1, 0}; + +// +// amount to move tile.x,tile.y to follow wallon to another tile +// +int followx[4] = {-1, 0, 1, 0}; +int followy[4] = { 0,-1, 0, 1}; + +// +// cornerwall gives the wall on the same tile to start following when the +// wall ends at an empty tile (go around an edge on same tile) +// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following +// when the wall hits another tile (right angle corner) +// +int cornerwall[4] = {WEST,NORTH,EAST,SOUTH}; +int turnwall[4] = {EAST,SOUTH,WEST,NORTH}; + +// +// wall visabilities in reletive locations +// -,- 0,- +,- +// -,0 0,0 +,0 +// -,+ 0,+ +,+ +// +int visable[9][4] = +{ + {0,1,1,0}, {0,0,1,0}, {0,0,1,1}, + {0,1,0,0}, {0,0,0,0}, {0,0,0,1}, + {1,1,0,0}, {1,0,0,0}, {1,0,0,1} +}; + +int startwall[9] = {2,2,3, 1,0,3, 1,0,0}; +int backupwall[9] = {3,3,0, 2,0,0, 2,1,1}; + + +int walllength; + +/* +============================================================================= + + FUNCTIONS + +============================================================================= +*/ + +/* +======================== += += FollowTrace += +======================== +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max) +{ + int tx,ty,otx,oty; + long absdx,absdy,xstep,ystep; + + tx = tracex>>TILESHIFT; + ty = tracey>>TILESHIFT; + + spotvis[tx][ty] = true; + + absdx=LABS(deltax); + absdy=LABS(deltay); + + if (absdx>absdy) + { + ystep = (deltay<<8)/(absdx>>8); + + if (!ystep) + ystep = deltay>0 ? 1 : -1; + + oty = (tracey+ystep)>>TILESHIFT; + if (deltax>0) + { +//############### +// +// step x by +1 +// +//############### + do + { + tx++; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx-1][ty]) + { + tile.x = tx-1; + tile.y = ty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step x by -1 +// +//############### + do + { + tx--; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx][oty]) + { + tile.x = tx; + tile.y = oty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + + } + } + else + { + xstep = (deltax<<8)/(absdy>>8); + if (!xstep) + xstep = deltax>0 ? 1 : -1; + + + otx = (tracex+xstep)>>TILESHIFT; + if (deltay>0) + { +//############### +// +// step y by +1 +// +//############### + do + { + ty++; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[tx][ty-1]) + { + tile.x = tx; + tile.y = ty-1; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step y by -1 +// +//############### + do + { + ty--; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[otx][ty]) + { + tile.x = otx; + tile.y = ty; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + + } + +} + + +//=========================================================================== + + +/* +================= += += BackTrace += += Traces backwards from edgex,edgey to viewx,viewy to see if a closer += tile obscures the given point. If it does, it finishes the wall and += starts a new one. += Returns true if a tile is hit. += Call with a 1 to have it automatically finish the current wall += +================= +*/ + +int BackTrace (int finish) +{ + fixed tracex,tracey; + long deltax,deltay,absdx,absdy; + int steps,otx,oty,testx,testheight,offset,wall; + + deltax = viewx-edgex; + deltay = viewy-edgey; + + absdx = LABS(deltax); + absdy = LABS(deltay); + + if (absdx>absdy) + steps = ABS(focal.x-(edgex>>TILESHIFT))-1; + else + steps = ABS(focal.y-(edgey>>TILESHIFT))-1; + + if (steps<=0) + return 0; + + otx = tile.x; + oty = tile.y; + if (!FollowTrace(edgex,edgey,deltax,deltay,steps)) + return 0; + +// +// if the start wall is behind the focal point, the trace went too far back +// + if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close + { + if (tile.x == focal.x && tile.y == focal.y) + { + tile.x = otx; + tile.y = oty; + return 0; + } + + if (tile.xx1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return 0; + } + + +// +// back up along the intersecting face to find the rightmost wall +// + + if (tile.yfocal.x) + offset += 2; + + wallon = backupwall[offset]; + + while (tilemap[tile.x][tile.y]) + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + }; + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + + return 1; +} + +//=========================================================================== + + +/* +================= += += ForwardTrace += += Traces forwards from edgex,edgey along the line from viewx,viewy until += a solid tile is hit. Sets tile.x,tile.y += +================= +*/ + +void ForwardTrace (void) +{ + int offset; + fixed tracex,tracey; + long deltax,deltay; + + deltax = edgex-viewx; + deltay = edgey-viewy; + + FollowTrace(edgex,edgey,deltax,deltay,0); + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// start the new wall +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + +// +// if entire first wall is invisable, corner +// + TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon], + &rightwall->x2,&rightwall->height2); + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]] + || rightwall->x2 < (rightwall-1)->x2 ) + wallon = cornerwall [wallon]; + +// +// transform first point +// + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; +} + + +//=========================================================================== + + +/* +================= += += FinishWall += += Transforms edgex,edgey as the next point of the current wall += and sticks it in the wall list += +================= +*/ + +int FinishWall (void) +{ + char num[20]; + + oldwall = rightwall; + + rightwall->color = basecolor; + + TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2); + + if (rightwall->x2 <= (rightwall-1)->x2+2 + && rightwall->height2 < (rightwall-1)->height2 ) + return 0; + + rightwall->walllength = walllength; + + switch (wallon) + { + case north: + case south: + rightwall->side = 0; + rightwall->planecoord = edgey; + break; + + case west: + case east: + rightwall->side = 1; + rightwall->planecoord = edgex; + break; + } + + walllength = 1; + + rightwall++; + + return 1; +} + +//=========================================================================== + + +/* +================= += += InsideCorner += +================= +*/ + +void InsideCorner (void) +{ + int offset; + + // + // the wall turned -90 degrees, so draw what we have, move to the new tile, + // change wallon, change color, and continue following. + // + FinishWall (); + + tile.x += sharex[wallon]; + tile.y += sharey[wallon]; + + wallon = turnwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // follow it backwards until it turns + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // just turn to the next wall and continue + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + basecolor = tilemap[tile.x][tile.y]; + return; // continue from here + } + + // + // back follow the invisable wall until it turns, then follow that + // + do + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + } while (tilemap[tile.x][tile.y]); + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16)+point1x[wallon]; + edgey = ((long)tile.y<<16)+point1y[wallon]; + + if (!BackTrace(0)) // backtrace without finishing a wall + { + TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1); + basecolor = tilemap[tile.x][tile.y]; + } +} + +//=========================================================================== + + +/* +================= += += OutsideCorner += +================= +*/ + +void OutsideCorner (void) +{ + int offset; + + // + // edge is the outside edge of a corner, so draw the current wall and + // turn the corner (+90 degrees) + // + FinishWall (); + + tile.x -= followx[wallon]; // backup to the real tile + tile.y -= followy[wallon]; + wallon = cornerwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // trace a ray from the corner to find a wall in the distance to + // follow + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // the new wall is visable, so just continue on + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return; // still on same tile, so color is ok + } + +// +// start from a new tile further away +// + ForwardTrace(); // find the next wall further back + +} + + +//=========================================================================== + + +/* +================= += += FollowWalls += += Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it += until something else is seen or the entire view area is covered += +================= +*/ + +void FollowWalls (void) +{ + int height,newcolor,offset,wall; + +//#################### +// +// figure leftmost wall of new tile +// +//#################### + +restart: + + walllength = 1; + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// if the start wall is inside a block, skip it by cornering to the second wall +// + if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + wallon = cornerwall [wallon]; + +// +// transform first edge to screen coordinates +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + +//################## +// +// follow the wall as long as possible +// +//################## + +advance: + + do // while ( tile.x != right.x || tile.y != right.y) + { +// +// check for conditions that shouldn't happed... +// + if (rightwall->x1 > VIEWXH) // somehow missed right tile... + return; + + if (rightwall == &walls[DANGERHIGH]) + { + // + // somethiing got messed up! Correct by thrusting ahead... + // + VW_ColorBorder(6); + bordertime = 60; + Thrust(player->angle,TILEGLOBAL/4); + player->angle+=5; + if (player->angle>ANGLES) + player->angle-=ANGLES; + aborttrace = true; + return; + +#if 0 + strcpy (str,"Wall list overflow at LE:"); + itoa(mapon+1,str2,10); + strcat (str,str2); + strcat (str," X:"); + ltoa(objlist[0].x,str2,10); + strcat (str,str2); + strcat (str," Y:"); + ltoa(objlist[0].y,str2,10); + strcat (str,str2); + strcat (str," AN:"); + itoa(objlist[0].angle,str2,10); + strcat (str,str2); + + Quit (str); +#endif + } + +// +// proceed along wall +// + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + + if (BackTrace(1)) // went behind a closer wall + continue; + + // + // advance to next tile along wall + // + tile.x += followx[wallon]; + tile.y += followy[wallon]; + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + { + InsideCorner (); // turn at a corner + continue; + } + + newcolor = tilemap[tile.x][tile.y]; + + if (!newcolor) // turn around an edge + { + OutsideCorner (); + continue; + } + + if (newcolor != basecolor) + { + // + // wall changed color, so draw what we have and continue following + // + FinishWall (); + rightwall->x1 = oldwall->x2; // new wall shares this edge + rightwall->height1 = oldwall->height2; + basecolor = newcolor; + + continue; + } + walllength++; + } while (tile.x != right.x || tile.y != right.y); + + + +//###################### +// +// draw the last tile +// +//###################### + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + + wallon = cornerwall[wallon]; + + // + // if the corner wall is visable, draw it + // + TESTWALLVISABLE; + + if (wallvisable) + { + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + } + +} + +//=========================================================================== diff --git a/src/lib/hb/c3_wiz.c b/src/lib/hb/c3_wiz.c new file mode 100755 index 00000000..1d68bc75 --- /dev/null +++ b/src/lib/hb/c3_wiz.c @@ -0,0 +1,2046 @@ +/* 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;itiley]+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 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= gamestate.body) + { + points = gamestate.body; + playstate = ex_died; + } + + bordertime = points*FLASHTICS; + VW_ColorBorder (FLASHCOLOR); + + if (gamestate.bodyxh < 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(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)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<y = ((long)tiley<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 && scrolltemp1 = number; + new->obclass = bonusobj; + + switch (number) + { + case B_POTION: + case B_OLDCHEST: + case B_CHEST: + case B_BOLT: + case B_NUKE: + new->flags |= of_shootable; + break; + + default: + new->flags &= ~of_shootable; + break; + } +} + + + +/* +============================================================================ + + FREEZE TIME OBJECT + +============================================================================ +*/ + +extern statetype s_ftimebonus; +extern statetype s_ftimebonus2; + +statetype s_ftimebonus = {TIMEOBJ1PIC,6,NULL,&s_ftimebonus2}; +statetype s_ftimebonus2 = {TIMEOBJ2PIC,6,NULL,&s_ftimebonus}; + +/* +=============== += += SpawnFTime += +=============== +*/ +void SpawnFTime(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_ftimebonus,TILEGLOBAL/2); +// new->tileobject = true; + new->obclass = freezeobj; + new->flags |= of_shootable; +} + +/* +============================================================================= + + EXPLODING WALL + +============================================================================= +*/ + + +void T_WallDie (objtype *ob); + +extern statetype s_walldie1; +extern statetype s_walldie2; +extern statetype s_walldie3; +extern statetype s_walldie4; +extern statetype s_walldie5; +extern statetype s_walldie6; + +statetype s_walldie1 = {0,20,NULL,&s_walldie2}; +statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3}; +statetype s_walldie3 = {0,20,NULL,&s_walldie4}; +statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5}; +statetype s_walldie5 = {0,20,NULL,&s_walldie6}; +statetype s_walldie6 = {0,-1,T_WallDie,NULL}; + + +/* +================ += += ExplodeWall += +================ +*/ + +void ExplodeWall (int tilex, int tiley) +{ + extern unsigned gcolor; + unsigned tilenum; + + DSpawnNewObj (tilex,tiley,&s_walldie1,0); + if (new == &dummyobj) + return; + new->obclass = inertobj; + new->active = always; + if (gcolor == 0x0101) + tilenum = WATEREXP; + else + tilenum = WALLEXP; + (unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] = + *(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = tilenum; + *(mapsegs[2]+farmapylookup[new->tiley]+new->tilex) &= 0xFF; +} + + +/* +================ += += T_WallDie += +================ +*/ + +void T_WallDie (objtype *ob) +{ + extern unsigned gcolor; + unsigned tile,other,spot,x,y; + + if (++ob->temp1 == 3) + tile = 0; + else + if (gcolor == 0x0101) + tile = WATEREXP-1 + ob->temp1; + else + tile = WALLEXP-1 + ob->temp1; + x = ob->tilex; + y = ob->tiley; + + (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = tile; + + if (ob->temp1 == 1) + { + // + // blow up nearby walls + // + spot = (*(mapsegs[2]+farmapylookup[y]+(x-1))) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x-1,y); + spot = (*(mapsegs[2]+farmapylookup[y]+(x+1))) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x+1,y); + spot = (*(mapsegs[2]+farmapylookup[y-1]+x)) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x,y-1); + spot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8; + if (spot == EXP_WALL_CODE) + ExplodeWall (x,y+1); + } +} +/* +============================================================================= + + OBJ_WARP GATE + +============================================================================= +*/ + +void T_Gate (objtype *ob); +void T_Gate_Wait (objtype *ob); + +extern statetype s_portal_wait; +statetype s_portal_wait = {0, 10, &T_Gate_Wait, &s_portal_wait}; + +statetype s_portal1 = {PORTAL1PIC, 6, &T_Gate, &s_portal2}; +statetype s_portal2 = {PORTAL2PIC, 6, &T_Gate, &s_portal3}; +statetype s_portal3 = {PORTAL3PIC, 6, &T_Gate, &s_portal4}; +statetype s_portal4 = {PORTAL4PIC, 6, &T_Gate, &s_portal5}; +statetype s_portal5 = {PORTAL5PIC, 6, &T_Gate, &s_portal6}; +statetype s_portal6 = {PORTAL6PIC, 6, &T_Gate, &s_portal1}; + +//--------------------------------------------------------------------------- +// SpawnWarp() +//--------------------------------------------------------------------------- +void SpawnWarp (int tilex, int tiley) +{ + unsigned spot; + objtype *ob; + + spot = (*(mapsegs[2]+farmapylookup[tiley]+tilex+1)) >> 8; + + if (spot) + { + SpawnNewObj (tilex, tiley, &s_portal_wait, TILEGLOBAL/3); + new->temp1 = spot*70; + } + else + SpawnNewObj (tilex, tiley, &s_portal1, TILEGLOBAL/3); + + new->obclass = gateobj; +} + +/* +=============== += += T_Gate_Wait += +=============== +*/ + +void T_Gate_Wait (objtype *ob) +{ + if ((ob->temp1 -= tics) <= 0) + { + if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley)) + return; + if (CheckHandAttack(ob)) + return; + + SD_PlaySound(PORTALSND); + ob->state = &s_portal1; + ob->ticcount = ob->state->tictime; + + } + +} + + + +/* +=============== += += T_Gate += +=============== +*/ + +void T_Gate (objtype *ob) +{ + objtype *check; + unsigned temp,spot; + + if (CheckHandAttack (ob) && !playstate) + { + // + // teleport out of level + // + playstate = ex_warped; + spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8; + gamestate.mapon=spot; + SD_PlaySound(WARPUPSND); + } +} + + + +/* +============================================================================= + + AQUAMAN + +============================================================================= +*/ + +void T_AquaMan(objtype *ob); + +statetype s_aqua_under1 = {EYESTALKUNDER1PIC, 25, &T_AquaMan, &s_aqua_under2}; +statetype s_aqua_under2 = {EYESTALKUNDER2PIC, 20, &T_AquaMan, &s_aqua_under3}; +statetype s_aqua_under3 = {EYESTALKUNDER3PIC, 20, &T_AquaMan, &s_aqua_under2}; + +statetype s_aqua_left = {EYESTALKUNDER4PIC, 40, NULL, &s_aqua_under3}; +statetype s_aqua_right = {EYESTALKUNDER5PIC, 40, NULL, &s_aqua_under3}; + +statetype s_aqua_rise1 = {EYESTALKRISE1PIC, 20, NULL, &s_aqua_rise2}; +statetype s_aqua_rise2 = {EYESTALKRISE2PIC, 15, NULL, &s_aqua_walk1}; + +statetype s_aqua_sink1 = {EYESTALKRISE2PIC, 15, NULL, &s_aqua_sink2}; +statetype s_aqua_sink2 = {EYESTALKRISE1PIC, 20, NULL, &s_aqua_under1}; + +statetype s_aqua_walk1 = {EYESTALKWALK1PIC, 12, &T_AquaMan, &s_aqua_walk2}; +statetype s_aqua_walk2 = {EYESTALKWALK2PIC, 12, &T_AquaMan, &s_aqua_walk1}; + +statetype s_aqua_attack1 = {EYESTALKATTACKPIC, 10, NULL, &s_aqua_attack2}; +statetype s_aqua_attack2 = {EYESTALKWALK1PIC, 10, &T_DoDamage, &s_aqua_walk1}; + +statetype s_aqua_die1 = {EYESTALKDEATH1PIC, 8, NULL, &s_aqua_die2}; +statetype s_aqua_die2 = {EYESTALKDEATH2PIC, 8, NULL, &s_aqua_die3}; +statetype s_aqua_die3 = {EYESTALKDEATH2PIC, -1, &T_AlternateStates, &s_aqua_die1}; +statetype s_aqua_die4 = {EYESTALKDEATH2PIC, 30, NULL, &s_aqua_die5}; +statetype s_aqua_die5 = {EYESTALKDEATH3PIC, 40, NULL, &s_aqua_die6}; +statetype s_aqua_die6 = {EYESTALKDEATH4PIC, 30, &ExplosionSnd, &s_aqua_die7}; +statetype s_aqua_die7 = {EYESTALKDEATH5PIC, 20, NULL, NULL}; + +typedef enum {wt_UNDER, wt_WALK} AquaManTypes; + +#define AQ_TIMEREMAIN (ob->temp1) +#define AQ_STAGE (ob->temp2) + +/* +=============== += += SpawnAquaMan += +=============== +*/ +void SpawnAquaMan(int tilex, int tiley) +{ + objtype *ob; + SpawnNewObj(tilex,tiley,&s_aqua_under1,PIXRADIUS*32); + ob = new; + + AQ_STAGE = wt_UNDER; + AQ_TIMEREMAIN = 60*4+random(60*3); + + new->obclass = aquamanobj; + new->speed = 1000; + new->flags &= ~of_shootable; + new->hitpoints = EasyHitPoints(15); +} + +void ExplosionSnd(objtype *ob) +{ + if (ob->temp1 != SOUNDPLAYED) + { + SD_PlaySound(BODY_EXPLODESND); + ob->temp1 = SOUNDPLAYED; + + } +} + + +/* +=============== += += T_AquaMan += +=============== +*/ + +void T_AquaMan(objtype *ob) +{ + switch (AQ_STAGE) + { + case wt_UNDER: + ob->flags &= ~of_shootable; + if (Chase(ob,true)) + { + // RISE & GOTO WALK STAGE + // + + AQ_STAGE = wt_WALK; + AQ_TIMEREMAIN = 60*5+random(60*5); + ob->state = &s_aqua_rise1; + ob->speed = 2200; + ob->ticcount = ob->state->tictime; + } + else + { + // DEC COUNTER - And check for WALK + // + if ((AQ_TIMEREMAIN-=realtics) < 0) + { + // RISE & GOTO WALK STAGE + // + + if (CheckHandAttack(ob)) + break; + + AQ_STAGE = wt_WALK; + AQ_TIMEREMAIN = 60+random(60*2); + ob->state = &s_aqua_rise1; + ob->speed = 2200; + ob->ticcount = ob->state->tictime; + } + else + if (random(1000)<5) + { + // RANDOM PEEK UP OUT OF WATER + // + if (random(2) == 0) + ob->state = &s_aqua_left; + else + ob->state = &s_aqua_right; + ob->ticcount = ob->state->tictime; + } + } + break; + + + case wt_WALK: + ob->flags |= of_shootable; + if (Chase(ob,true) || (random(1000)state = &s_aqua_attack1; + ob->ticcount = ob->state->tictime; + } + else + { + // DEC COUNTER - And check for SINK + // + if ((AQ_TIMEREMAIN-=realtics) < 0) + { + // SINK & GOTO BUBBLE STAGE + // + + AQ_STAGE = wt_UNDER; + AQ_TIMEREMAIN = 60*4+random(60*3); + ob->state = &s_aqua_sink1; + ob->speed = 1200; + ob->ticcount = ob->state->tictime; + ob->flags &= ~of_shootable; + } + + } + break; + } +} + + + + +/* +============================================================================= + + WIZARD + +============================================================================= +*/ + +void T_Wizard(objtype *ob); +void T_WizardShoot(objtype *ob); + +statetype s_wizard_walk1 = {WIZARDWALK1PIC, 20, &T_Wizard, &s_wizard_walk2}; +statetype s_wizard_walk2 = {WIZARDWALK2PIC, 20, &T_Wizard, &s_wizard_walk3}; +statetype s_wizard_walk3 = {WIZARDWALK3PIC, 20, &T_Wizard, &s_wizard_walk4}; +statetype s_wizard_walk4 = {WIZARDWALK4PIC, 20, &T_Wizard, &s_wizard_walk1}; + +statetype s_wizard_attack1 = {WIZARDATTACK1PIC, 20, NULL, &s_wizard_attack2}; +statetype s_wizard_attack2 = {WIZARDATTACK2PIC, 20, &T_DoDamage, &s_wizard_walk1}; + +statetype s_wizard_ouch = {WIZARDOUCHPIC, 15, NULL, &s_wizard_walk1}; + +statetype s_wizard_die1 = {WIZARDDEATH1PIC, 45, &SmallSound, &s_wizard_die2}; +statetype s_wizard_die2 = {WIZARDDEATH2PIC, 30, NULL, &s_wizard_die3}; +statetype s_wizard_die3 = {WIZARDDEATH3PIC, 15, NULL, &s_wizard_die4}; +statetype s_wizard_die4 = {WIZARDDEATH4PIC, 15, NULL, &s_wizard_die4}; + +statetype s_wizard_shoot1 = {WIZARDATTACK1PIC, 20, NULL, &s_wizard_shoot2}; +statetype s_wizard_shoot2 = {WIZARDATTACK1PIC, -1, &T_WizardShoot, &s_wizard_shoot3}; +statetype s_wizard_shoot3 = {WIZARDATTACK2PIC, 20, NULL, &s_wizard_walk1}; + +statetype s_wizard_shot1 = {WIZARD_SHOT1PIC, 8, &T_ShootPlayer, &s_wizard_shot2}; +statetype s_wizard_shot2 = {WIZARD_SHOT2PIC, 8, &T_ShootPlayer, &s_wizard_shot1}; + + +/* +=============== += += SpawnWizard += +=============== +*/ + +void SpawnWizard (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_wizard_walk1,TILEGLOBAL/2); + new->obclass = wizardobj; + new->speed = 1536; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(10); +} + + +/* +=============== += += T_Wizard += +=============== +*/ + +void T_Wizard(objtype *ob) +{ + if (Chase (ob,true))// || (random(1000)state = &s_wizard_attack1; + ob->ticcount = ob->state->tictime; + return; + } + else + if (AngleNearPlayer(ob) != -1) + { + ob->state = &s_wizard_shoot1; + ob->ticcount = ob->state->tictime; + return; + } +} + +/* +=============== += += T_Wizard += +=============== +*/ +void T_WizardShoot(objtype *ob) +{ + ShootPlayer(ob, wshotobj, 10000, &s_wizard_shot1); +} + + + +/* +============================================================================= + + RAY + +============================================================================= +*/ + +void T_BlobRay(objtype *ob); +void T_RayShoot (objtype *ob); + +statetype s_ray_under = {0, 20, &T_BlobRay, &s_ray_under}; + +statetype s_ray_rise = {RAYRISEPIC, 30, NULL, &s_ray_fly1}; + +statetype s_ray_sink = {RAYRISEPIC, 30, NULL, &s_ray_under}; + +statetype s_ray_fly1 = {RAYFLY1PIC, 10, &T_BlobRay, &s_ray_fly2}; +statetype s_ray_fly2 = {RAYFLY2PIC, 10, &T_BlobRay, &s_ray_fly3}; +statetype s_ray_fly3 = {RAYFLY1PIC, 10, &T_BlobRay, &s_ray_fly4}; +statetype s_ray_fly4 = {RAYFLY3PIC, 10, &T_BlobRay, &s_ray_fly1}; + +statetype s_ray_attack1 = {RAYSHOOT1PIC, 15, NULL, &s_ray_attack2}; +statetype s_ray_attack2 = {RAYSHOOT2PIC, -1, &T_RayShoot, &s_ray_attack3}; +statetype s_ray_attack3 = {RAYSHOOT2PIC, 20, NULL, &s_ray_fly1}; + +statetype s_ray_die1 = {RAYDEATH1PIC, 50, &SmallSound, &s_ray_die2}; +statetype s_ray_die2 = {RAYDEATH2PIC, 30, NULL, NULL}; + +statetype s_ray_shot1 = {RAYSHOT1PIC, 8, &T_ShootPlayer, &s_ray_shot2}; +statetype s_ray_shot2 = {RAYSHOT2PIC, 8, &T_ShootPlayer, &s_ray_shot1}; + + +typedef enum {br_GND, br_WALK, br_CORNER1, br_CORNER2, br_CORNER3, br_CORNER4} BlobTypes; + +#define BR_TIMEREMAIN (ob->temp1) +#define BR_STAGE (ob->temp2) +#define BLOB_LEAVE 0x04 + +/* +=============== += += SpawnRay += +=============== +*/ +void SpawnRay(int tilex, int tiley) +{ + objtype *ob; + SpawnNewObj(tilex, tiley, &s_ray_under, PIXRADIUS*25); + ob=new; + + BR_STAGE = br_GND; + BR_TIMEREMAIN = random(60)+random(100); + + new->obclass = rayobj; + new->speed = 1700; + new->flags &= ~of_shootable; + new->hitpoints = EasyHitPoints(15); +} + + + +/* +============================================================================= + + BLOB + +============================================================================= +*/ + + +statetype s_blob_gnd1 = {BLOBGND1PIC, 13, T_BlobRay, &s_blob_gnd2}; +statetype s_blob_gnd2 = {BLOBGND2PIC, 15, T_BlobRay, &s_blob_gnd1}; + +statetype s_blob_rise1 = {BLOBRISE1PIC, 20, NULL, &s_blob_rise2}; +statetype s_blob_rise2 = {BLOBRISE2PIC, 20, NULL, &s_blob_walk1}; + +statetype s_blob_sink1 = {BLOBRISE2PIC, 20, NULL, &s_blob_sink2}; +statetype s_blob_sink2 = {BLOBRISE1PIC, 20, NULL, &s_blob_gnd1}; + +statetype s_blob_walk1 = {BLOBWALK1PIC, 15, T_BlobRay, &s_blob_walk2}; +statetype s_blob_walk2 = {BLOBWALK2PIC, 15, T_BlobRay, &s_blob_walk3}; +statetype s_blob_walk3 = {BLOBWALK3PIC, 15, T_BlobRay, &s_blob_walk1}; + +statetype s_blob_ouch = {BLOBRISE2PIC, 10, T_BlobRay, &s_blob_walk1}; + +statetype s_blob_die1 = {BLOBDEATH1PIC, 30, &ExplosionSnd, &s_blob_die2}; +statetype s_blob_die2 = {BLOBDEATH2PIC, 30, NULL, &s_blob_die3}; +statetype s_blob_die3 = {BLOBDEATH3PIC, 30, NULL, NULL}; + +statetype s_blob_shot1 = {BLOB_SHOT1PIC, 8, &T_ShootPlayer, &s_blob_shot2}; +statetype s_blob_shot2 = {BLOB_SHOT2PIC, 8, &T_ShootPlayer, &s_blob_shot1}; + + +/* +=============== += += SpawnBlob += +=============== +*/ +void SpawnBlob(int tilex, int tiley) +{ + objtype *ob; + SpawnNewObj(tilex, tiley, &s_blob_gnd1, PIXRADIUS*14); + ob=new; + + BR_STAGE = br_GND; + BR_TIMEREMAIN = random(60)+random(100); + + new->obclass = blobobj; + new->speed = 1200; + new->flags &= ~of_shootable; + new->hitpoints = EasyHitPoints(13); +} + + +/* +=============== += += T_BlobRay += +=============== +*/ + +void T_BlobRay(objtype *ob) +{ + switch (BR_STAGE) + { + case br_GND: + ob->flags &= ~of_shootable; + if (Chase(ob,true)) + { + // RISE & GOTO WALK STAGE + // + + BR_STAGE = br_WALK; + BR_TIMEREMAIN = 60*8+random(60*5); + if (ob->obclass == blobobj) + ob->state = &s_blob_rise1; + else + ob->state = &s_ray_rise; + ob->speed = 2200; + ob->ticcount = ob->state->tictime; + } + else + { + // DEC COUNTER - And check for WALK + // + if ((BR_TIMEREMAIN -= realtics) < 0) + { + // RISE & GOTO WALK STAGE + // + + BR_STAGE = br_WALK; + BR_TIMEREMAIN = 60*8+random(60*5); + if (ob->obclass == blobobj) + ob->state = &s_blob_rise1; + else + ob->state = &s_ray_rise; + ob->speed = 2200; + ob->ticcount = ob->state->tictime; + } + } + break; + + + case br_WALK: + ob->flags |= of_shootable; + + if (Chase(ob,true) || (CheckHandAttack(ob))) + + { + ob->flags |= BLOB_LEAVE; + BR_STAGE = random(br_CORNER3) + 2; + BR_TIMEREMAIN = 60*2+(random(6)*60); + if (ob->obclass == blobobj) + ob->state = &s_blob_gnd1; + else + ob->state = &s_ray_under; + ob->ticcount = ob->state->tictime; + } + else + if (AngleNearPlayer(ob) != -1) + { + if (ob->obclass == blobobj) + { + if (!(random(15))) + ShootPlayer(ob, bshotobj, 10000, &s_blob_shot1); + } + else + if (!(random(7))) + { + ob->state = &s_ray_attack1; + ob->ticcount = ob->state->tictime; + } + } + + else + { + // DEC COUNTER - And check for SINK + // + if ((BR_TIMEREMAIN -= realtics) < 0) + { + // SINK & GOTO GROUND STAGE + // + + BR_STAGE = br_GND; + BR_TIMEREMAIN = 60*2+random(60*2); + if (ob->obclass == blobobj) + { + ob->state = &s_blob_sink1; + ob->speed = 1200; + } + else + { + ob->state = &s_ray_sink; + ob->speed = 1700; + } + ob->ticcount = ob->state->tictime; + ob->flags &= ~of_shootable; + } + + } + break; + case br_CORNER1: + case br_CORNER2: + case br_CORNER3: + case br_CORNER4: + ob->flags &= ~of_shootable; + if ((BR_TIMEREMAIN -= realtics) < 0) + { + BR_STAGE = br_GND; + ob->flags &= ~BLOB_LEAVE; + } + else + { + fixed tempx,tempy; + unsigned temp_tilex,temp_tiley; + + tempx = player->x; + tempy = player->y; + temp_tilex = player->tilex; + temp_tiley = player->tiley; + + player->x = ((long)other_x[BR_STAGE-2]<y = ((long)other_y[BR_STAGE-2]<tilex = other_x[BR_STAGE-2]; + player->tiley = other_y[BR_STAGE-2]; + + + Chase(ob,true); + + player->x = tempx; + player->y = tempy; + player->tilex = temp_tilex; + player->tiley = temp_tiley; + } + break; + } +} + +/* +=============== += += T_RayShoot += +=============== +*/ +void T_RayShoot (objtype *ob) +{ + ShootPlayer(ob, rshotobj, 10000, &s_ray_shot1); +} \ No newline at end of file diff --git a/src/lib/hb/c6_act2.c b/src/lib/hb/c6_act2.c new file mode 100755 index 00000000..803fe8b9 --- /dev/null +++ b/src/lib/hb/c6_act2.c @@ -0,0 +1,891 @@ +/* Catacomb Apocalypse 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_PLAY.C + +#include "DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state); +void T_ShootPlayer(objtype *ob); + +short head_base_delay; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +void T_ShooterObj(objtype *ob); + +void SpawnRamBone(int tilex, int tiley); +void T_SkeletonShoot(objtype *ob); +void SpawnFutureMage (int tilex, int tiley); +void T_FMageShoot(objtype *ob); +void SpawnRoboTank(int tilex, int tiley); +void T_RoboTankShoot(objtype *ob); +void SpawnStompy(int tilex, int tiley); +void T_StompyShoot(objtype *ob); +void SpawnBug(int tilex, int tiley); +void T_BugShoot(objtype *ob); +void SpawnShooterEye(int tilex, int tiley); +void T_EyeShootPlayer(objtype *ob); +void SpawnRunningEye(int tilex, int tiley); +void T_RunningEye(objtype *ob); + + +/* +============================================================================= + + LARGE SOUND + +============================================================================= +*/ +void LargeSound (objtype *ob) +{ + if (ob->temp1 != SOUNDPLAYED) + { + SD_PlaySound(LARGEMONSTERSND); + ob->temp1 = SOUNDPLAYED; + + } +} + + +/* +============================================================================= + + SMALL SOUND + +============================================================================= +*/ +void SmallSound (objtype *ob) +{ + if (ob->temp1 != SOUNDPLAYED) + { + SD_PlaySound(SMALLMONSTERSND); + ob->temp1 = SOUNDPLAYED; + + } +} + + + +/* +============================================================================= + + RAMBONE + +============================================================================= +*/ + + +statetype s_skel_1 = {RAMBONEWALK1PIC, 10, &T_ShooterObj, &s_skel_2}; +statetype s_skel_2 = {RAMBONEWALK2PIC, 10, &T_ShooterObj, &s_skel_3}; +statetype s_skel_3 = {RAMBONEWALK3PIC, 10, &T_ShooterObj, &s_skel_4}; +statetype s_skel_4 = {RAMBONEWALK4PIC, 10, &T_ShooterObj, &s_skel_1}; + +statetype s_skel_attack1 = {RAMBONEATTACK1PIC, 12, NULL, &s_skel_attack2}; +statetype s_skel_attack2 = {RAMBONEATTACK2PIC, 20, NULL, &s_skel_attack3}; +statetype s_skel_attack3 = {RAMBONEATTACK2PIC, -1, T_SkeletonShoot, &s_skel_attack4}; +statetype s_skel_attack4 = {RAMBONEATTACK3PIC, 20, NULL, &s_skel_ouch}; + +statetype s_skel_ouch = {RAMBONEATTACK1PIC, 10, NULL, &s_skel_1}; + +statetype s_skel_die1 = {RAMBONEDEATH1PIC, 40, NULL, &s_skel_die2}; +statetype s_skel_die2 = {RAMBONEDEATH2PIC, 30, NULL, &s_skel_die3}; +statetype s_skel_die3 = {RAMBONEDEATH3PIC, 20, &LargeSound, NULL}; + +statetype s_skel_shot1 = {RAMBONESHOT1PIC, 10, &T_ShootPlayer, &s_skel_shot2}; +statetype s_skel_shot2 = {RAMBONESHOT2PIC, 10, &T_ShootPlayer, &s_skel_shot1}; + +#define shooter_mode ob->temp1 +#define shooter_delay ob->temp2 + + +/* +=============== += += SpawnSkeleton += +=============== +*/ +void SpawnRamBone(int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_skel_1,PIXRADIUS*20); + new->obclass = ramboneobj; + new->speed = 2036; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(12); +} + + +/* +================= += += T_SkeletonShoot += +================= +*/ +void T_SkeletonShoot(objtype *ob) +{ + ShootPlayer(ob, rbshotobj, MSHOTSPEED, &s_skel_shot1); +} + + + +/* +============================================================================= + + FUTURE MAGE + +============================================================================= +*/ + +statetype s_fmage1 = {FMAGEWALK1PIC, 20, &T_ShooterObj, &s_fmage2}; +statetype s_fmage2 = {FMAGEWALK2PIC, 20, &T_ShooterObj, &s_fmage3}; +statetype s_fmage3 = {FMAGEWALK3PIC, 20, &T_ShooterObj, &s_fmage1}; + +statetype s_fmageattack1 = {FMAGEATTACK1PIC, 20, NULL, &s_fmageattack2}; +statetype s_fmageattack2 = {FMAGEATTACK1PIC, -1, &T_FMageShoot, &s_fmageattack3}; +statetype s_fmageattack3 = {FMAGEATTACK2PIC, 30, NULL, &s_fmage1}; + +statetype s_fmageouch = {FMAGEATTACK1PIC, 10, NULL, &s_fmage1}; + +statetype s_fmagedie1 = {FMAGEDEATH1PIC, 40, NULL, &s_fmagedie2}; +statetype s_fmagedie2 = {FMAGEDEATH2PIC, 30, &SmallSound, &s_fmagedie3}; +statetype s_fmagedie3 = {FMAGEDEATH3PIC, 0, NULL, &s_fmagedie3}; + +statetype s_fmshot1 = {FMAGESHOT1PIC, 8, &T_ShootPlayer, &s_fmshot2}; +statetype s_fmshot2 = {FMAGESHOT2PIC, 8, &T_ShootPlayer, &s_fmshot1}; + +/* +================= += += SpawnFutureMage += +================= +*/ + +void SpawnFutureMage (int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_fmage1, PIXRADIUS*15); + new->obclass = fmageobj; + new->speed = 3072; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(12); +} + + +/* +================= += += T_FMageShoot += +================= +*/ +void T_FMageShoot(objtype *ob) +{ + ShootPlayer(ob, fmshotobj, MSHOTSPEED, &s_fmshot1); +} + + + +/* +============================================================================= + + ROBO TANK + +============================================================================= +*/ + +statetype s_robotank_walk1 = {ROBOTANKWALK1PIC, 15, &T_ShooterObj, &s_robotank_walk2}; +statetype s_robotank_walk2 = {ROBOTANKWALK2PIC, 15, &T_ShooterObj, &s_robotank_walk3}; +statetype s_robotank_walk3 = {ROBOTANKWALK3PIC, 15, &T_ShooterObj, &s_robotank_walk4}; +statetype s_robotank_walk4 = {ROBOTANKWALK4PIC, 15, &T_ShooterObj, &s_robotank_walk1}; + +statetype s_robotank_attack1 = {ROBOTANKWALK1PIC, 15, NULL, &s_robotank_attack2}; +statetype s_robotank_attack2 = {ROBOTANKATTACK1PIC, 15, NULL, &s_robotank_attack3}; +statetype s_robotank_attack3 = {ROBOTANKATTACK1PIC, -1, &T_RoboTankShoot, &s_robotank_attack4}; +statetype s_robotank_attack4 = {ROBOTANKWALK1PIC, 15, NULL, &s_robotank_walk1}; + +statetype s_robotank_death1 = {ROBOTANKDEATH1PIC, 8, NULL, &s_robotank_death2}; +statetype s_robotank_death2 = {ROBOTANKDEATH2PIC, 8, NULL, &s_robotank_death3}; +statetype s_robotank_death3 = {ROBOTANKDEATH2PIC, -1, &T_AlternateStates, &s_robotank_death1}; +statetype s_robotank_death4 = {ROBOTANKDEATH3PIC, 25, &ExplosionSnd, &s_robotank_death5}; +statetype s_robotank_death5 = {ROBOTANKDEATH4PIC, 20, NULL, &s_robotank_death5}; + +statetype s_robotank_shot1 = {PSHOT1PIC, 10, &T_ShootPlayer, &s_robotank_shot2}; +statetype s_robotank_shot2 = {PSHOT2PIC, 10, &T_ShootPlayer, &s_robotank_shot1}; + + +/* +================= += += SpawnRoboTank += +================= +*/ +void SpawnRoboTank(int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_robotank_walk1, PIXRADIUS*35); + new->obclass = robotankobj; + new->speed = 1700; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(25); +} + + +/* +================= += += T_RoboTankShoot += +================= +*/ +void T_RoboTankShoot(objtype *ob) +{ + ShootPlayer(ob, rtshotobj, 7000, &s_robotank_shot1); +} + + + +/* +==================== += += T_AlternateStates += +==================== +*/ +void T_AlternateStates(objtype *ob) +{ + if (ob->temp1--) + { + ob->state = ob->state->next; + } + else + { + if (ob->state == &s_robotank_death3) + ob->state = &s_robotank_death4; + else + ob->state = &s_aqua_die4; + } + ob->ticcount = ob->state->tictime; +} + + + + + + +/* +============================================================================= + + STOMPY + +============================================================================= +*/ + +statetype s_stompy_walk1 = {STOMPYWALK1PIC, 15, &T_ShooterObj, &s_stompy_walk2}; +statetype s_stompy_walk2 = {STOMPYWALK2PIC, 15, &T_ShooterObj, &s_stompy_walk3}; +statetype s_stompy_walk3 = {STOMPYWALK3PIC, 15, &T_ShooterObj, &s_stompy_walk4}; +statetype s_stompy_walk4 = {STOMPYWALK4PIC, 15, &T_ShooterObj, &s_stompy_walk1}; + +statetype s_stompy_attack1 = {STOMPYATTACK1PIC, 10, NULL, &s_stompy_attack2}; +statetype s_stompy_attack2 = {STOMPYATTACK2PIC, 15, NULL, &s_stompy_attack3}; +statetype s_stompy_attack3 = {STOMPYATTACK2PIC, -1, T_StompyShoot, &s_stompy_attack4}; +statetype s_stompy_attack4 = {STOMPYATTACK1PIC, 10, NULL, &s_stompy_walk1}; + +statetype s_stompy_ouch = {STOMPYATTACK1PIC, 10, NULL, &s_stompy_walk2}; + +statetype s_stompy_death1 = {STOMPYDEATH1PIC, 45, &ExplosionSnd, &s_stompy_death2}; +statetype s_stompy_death2 = {STOMPYDEATH2PIC, 30, NULL, &s_stompy_death3}; +statetype s_stompy_death3 = {STOMPYDEATH3PIC, 25, NULL, &s_stompy_death4}; +statetype s_stompy_death4 = {STOMPYDEATH4PIC, 20, NULL, NULL}; + +statetype s_stompy_shot1 = {STOMPYSHOT1PIC, 6, &T_ShootPlayer, &s_stompy_shot2}; +statetype s_stompy_shot2 = {STOMPYSHOT2PIC, 6, &T_ShootPlayer, &s_stompy_shot3}; +statetype s_stompy_shot3 = {STOMPYSHOT1PIC, 6, &T_ShootPlayer, &s_stompy_shot4}; +statetype s_stompy_shot4 = {STOMPYSHOT3PIC, 6, &T_ShootPlayer, &s_stompy_shot5}; +statetype s_stompy_shot5 = {STOMPYSHOT4PIC, 6, &T_ShootPlayer, &s_stompy_shot4}; + + +/* +================= += += SpawnStompy += +================= +*/ +void SpawnStompy(int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_stompy_walk1, PIXRADIUS*25); + new->obclass = stompyobj; + new->speed = 1800; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(20); +} + + +/* +================= += += T_StompyShoot += +================= +*/ +void T_StompyShoot(objtype *ob) +{ + ShootPlayer(ob, syshotobj, 8500, &s_stompy_shot1); +} + + + +/* +============================================================================= + + BUG + +============================================================================= +*/ + +statetype s_bug_walk1 = {BUG_WALK1PIC, 15, &T_ShooterObj, &s_bug_walk2}; +statetype s_bug_walk2 = {BUG_WALK2PIC, 15, &T_ShooterObj, &s_bug_walk3}; +statetype s_bug_walk3 = {BUG_WALK3PIC, 15, &T_ShooterObj, &s_bug_walk1}; + +statetype s_bug_attack1 = {BUG_ATTACK1PIC, 20, NULL, &s_bug_attack2}; +statetype s_bug_attack2 = {BUG_ATTACK2PIC, 20, NULL, &s_bug_attack3}; +statetype s_bug_attack3 = {BUG_ATTACK2PIC, -1, &T_BugShoot, &s_bug_attack4}; +statetype s_bug_attack4 = {BUG_ATTACK1PIC, 15, NULL, &s_bug_walk1}; + +statetype s_bug_ouch = {BUG_WALK1PIC, 10, NULL, &s_bug_walk2}; + +statetype s_bug_death1 = {BUG_DEATH1PIC, 35, &SmallSound, &s_bug_death2}; +statetype s_bug_death2 = {BUG_DEATH2PIC, 10, NULL, &s_bug_death2}; + +statetype s_bug_shot1 = {BUG_SHOT1PIC, 10, &T_ShootPlayer, &s_bug_shot2}; +statetype s_bug_shot2 = {BUG_SHOT2PIC, 10, &T_ShootPlayer, &s_bug_shot1}; + + +/* +================= += += SpawnBug += +================= +*/ +void SpawnBug(int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_bug_walk1, PIXRADIUS*20); + new->obclass = bugobj; + new->speed = 1500; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(10); +} + + +/* +================= += += T_BugShoot += +================= +*/ +void T_BugShoot(objtype *ob) +{ + ShootPlayer(ob, bgshotobj, 8000, &s_bug_shot1); +} + + + + + +/* +============================================================================= + + MEC EYE + +============================================================================= +*/ + +void T_EyeShootPlayer (objtype *ob); + +statetype s_eye_pause = {EYE_WALK1PIC,40,NULL,&s_eye_2}; + +statetype s_eye_1 = {EYE_WALK1PIC,20,T_ShooterObj,&s_eye_2}; +statetype s_eye_2 = {EYE_WALK2PIC,20,T_ShooterObj,&s_eye_3}; +statetype s_eye_3 = {EYE_WALK3PIC,20,T_ShooterObj,&s_eye_4}; +statetype s_eye_4 = {EYE_WALK2PIC,20,T_ShooterObj,&s_eye_1}; +statetype s_eye_shootplayer_1 = {EYE_WALK1PIC,1,T_EyeShootPlayer,&s_eye_shootplayer_2}; +statetype s_eye_shootplayer_2 = {EYE_WALK1PIC,20,NULL,&s_eye_1}; + +statetype s_eye_ouch = {EYE_OUCH1PIC,8,NULL,&s_eye_ouch2}; +statetype s_eye_ouch2 = {EYE_OUCH2PIC,8,NULL,&s_eye_1}; + +statetype s_eye_die1 = {EYE_DEATH1PIC,22,NULL,&s_eye_die2}; +statetype s_eye_die2 = {EYE_DEATH2PIC,22,&SmallSound,&s_eye_die3}; +statetype s_eye_die3 = {EYE_DEATH3PIC,22,NULL,&s_eye_die4}; +statetype s_eye_die4 = {EYE_DEATH4PIC,22,NULL,&s_eye_die4}; + +statetype s_eshot1 = {EYE_SHOT1PIC,8,&T_ShootPlayer,&s_eshot2}; +statetype s_eshot2 = {EYE_SHOT2PIC,8,&T_ShootPlayer,&s_eshot1}; + + +//------------------------------------------------------------------------- +// SpawnEye() +//------------------------------------------------------------------------- +void SpawnShooterEye(int tilex, int tiley) +{ + objtype *ob; + + SpawnNewObj(tilex,tiley,&s_eye_1,PIXRADIUS*10); + ob = new; + new->obclass = eyeobj; + new->speed = 3000; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(15); + shooter_mode = sm_other1; +} + + +//--------------------------------------------------------------------------- +// T_EyeShootPlayer +//--------------------------------------------------------------------------- +void T_EyeShootPlayer (objtype *ob) +{ + ShootPlayer(ob, eshotobj, ESHOTSPEED, &s_eshot1); +} + + +/* +=============== += += T_ShooterObj += += ********** += ***NOTE*** This routine controls the thinks for the RamBone, RoboTank, += ********** Stompy, Future Mage, Bug, and Old Mage += +=============== +*/ + +void T_ShooterObj(objtype *ob) +{ + fixed tempx,tempy; + unsigned temp_tilex,temp_tiley; + int angle; + + shooter_delay -= realtics; + if (shooter_delay < 0) + { + shooter_mode = random(sm_dummy); + shooter_delay = random(10*60)+random(50); + } + + tempx = player->x; + tempy = player->y; + temp_tilex = player->tilex; + temp_tiley = player->tiley; + + + switch (shooter_mode) + { + case sm_other1: + case sm_other2: + case sm_other3: + case sm_other4: + player->x = ((long)other_x[shooter_mode]<y = ((long)other_y[shooter_mode]<tilex = other_x[shooter_mode]; + player->tiley = other_y[shooter_mode]; + break; + } + + if (Chase(ob,true)) + shooter_delay = 0; + + player->x = tempx; + player->y = tempy; + player->tilex = temp_tilex; + player->tiley = temp_tiley; + + angle = AngleNearPlayer(ob); + + + // Handle shooting for the different characters controlled by this think. + switch (ob->obclass) + { + case ramboneobj: + if (!random(2) && (angle != -1)) + { + ob->state = &s_skel_attack1; + ob->ticcount = ob->state->tictime; + } + break; + + case fmageobj: + if (!random(8) && (angle != -1)) + { + ob->state = &s_fmageattack1; + ob->ticcount = ob->state->tictime; + } + break; + + case robotankobj: + if (!random(15) && (angle != -1)) + { + ob->state = &s_robotank_attack1; + ob->ticcount = ob->state->tictime; + } + break; + + case stompyobj: + if (angle != -1) + { + ob->state = &s_stompy_attack1; + ob->ticcount = ob->state->tictime; + } + break; + + case bugobj: + if (!random(5) && (angle != -1)) + { + ob->state = &s_bug_attack1; + ob->ticcount = ob->state->tictime; + } + break; + + case eyeobj: + if (!random(2) && (angle != -1)) + { + ob->state = &s_eye_shootplayer_1; + ob->ticcount = ob->state->tictime; + } + break; + } +} + + + + + + +/* +============================================================================= + + RUNNING EYE + +============================================================================= +*/ + +statetype s_reye_1 = {EYE_WALK1PIC, 20, &T_RunningEye, &s_reye_2}; +statetype s_reye_2 = {EYE_WALK2PIC, 20, &T_RunningEye, &s_reye_3}; +statetype s_reye_3 = {EYE_WALK3PIC, 20, &T_RunningEye, &s_reye_4}; +statetype s_reye_4 = {EYE_WALK2PIC, 20, &T_RunningEye, &s_reye_1}; + +statetype s_reye_ouch = {EYE_OUCH1PIC, 8, NULL, &s_reye_ouch2}; +statetype s_reye_ouch2 = {EYE_OUCH2PIC, 8, NULL, &s_reye_1}; + +statetype s_reye_die1 = {EYE_DEATH1PIC, 22, NULL, &s_reye_die2}; +statetype s_reye_die2 = {EYE_DEATH2PIC, 22, &SmallSound, &s_reye_die3}; +statetype s_reye_die3 = {EYE_DEATH3PIC, 22, NULL, &s_reye_die4}; +statetype s_reye_die4 = {EYE_DEATH4PIC, 22, NULL, &s_reye_die4}; + +/* +==================== += += SpawnRunningEye() += +==================== +*/ +void SpawnRunningEye(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_reye_1,PIXRADIUS*25); + new->obclass = reyeobj; + new->speed = 3500; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(15); + new->temp2 = (*(mapsegs[2]+farmapylookup[tiley+1]+tilex))>>8; + *(mapsegs[2]+farmapylookup[tiley+1]+tilex) = 0; + + new->temp1 = 2; + + if (!new->temp2) + Quit("Initialize the running eye!\n"); +} + + +/* +==================== += += T_RunningEye += +==================== +*/ +void T_RunningEye(objtype *ob) +{ + int x, y, dir_num, switch_num; + fixed tempx,tempy; + unsigned temp_tilex,temp_tiley; + + dir_num = *(mapsegs[2]+farmapylookup[ob->tiley]+ob->tilex); + dir_num = dir_num>>8; + + if (!dir_num) + dir_num = ob->temp2; + + if (dir_num == 5) + { + if (ob->temp1) + { + ob->temp1--; + } + else + { + ob->temp1 = 2; + actorat[ob->tilex][ob->tiley] = 0; + switch (ob->temp2) + { + case 1: + ob->tiley = ob->tiley-1; + ob->y = ((long)(ob->tiley)<tilex = ob->tilex+1; + ob->x = ((long)(ob->tilex)<tiley = ob->tiley+1; + ob->y = ((long)(ob->tiley)<tilex = ob->tilex-1; + ob->x = ((long)(ob->tilex)<tilex][ob->tiley] = ob; + return; + } + } + + tempx = player->x; + tempy = player->y; + temp_tilex = player->tilex; + temp_tiley = player->tiley; + + if (dir_num == 5) + switch_num = ob->temp2; + else + switch_num = dir_num; + + switch (switch_num) + { + case 1: + player->x = ((long)ob->tilex<y = ((long)(ob->tiley-2)<tilex = ob->tilex; + player->tiley = ob->tiley-2; + break; + + case 2: + player->x = ((long)(ob->tilex+2)<y = ((long)ob->tiley<tilex = ob->tilex+2; + player->tiley = ob->tiley; + break; + + case 3: + player->x = ((long)ob->tilex<y = ((long)(ob->tiley+2)<tilex = ob->tilex; + player->tiley = ob->tiley+2; + break; + + case 0: + case 4: + player->x = ((long)(ob->tilex-2)<y = ((long)ob->tiley<tilex = ob->tilex-2; + player->tiley = ob->tiley; + break; + } + + Chase(ob, false); + + player->x = tempx; + player->y = tempy; + player->tilex = temp_tilex; + player->tiley = temp_tiley; + + if (dir_num != 5) + ob->temp2 = dir_num; +} + + +/* +============================================================================= + + EGYPTIAN HEAD + +============================================================================= +*/ + +void T_Head(objtype *ob); + +statetype s_head = {HEADPIC, 20, &T_Head, &s_head}; + +statetype s_head_shot1 = {PSHOT1PIC, 10, &T_ShootPlayer, &s_head_shot2}; +statetype s_head_shot2 = {PSHOT2PIC, 10, &T_ShootPlayer, &s_head_shot1}; + + +/* +=================== += += SpawnEgyptianHead += +=================== +*/ + +void SpawnEgyptianHead (int tilex, int tiley) +{ + objtype *ob; + short current_head_delay; + unsigned tile; + + SpawnNewObj(tilex, tiley, &s_head, PIXRADIUS*35); + ob = new; + head_mode = h_wait_to_rise; + + tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (tile) + head_delay = (tile>>8)*30; + else + { + current_head_delay = (3*60)+random(3*60); + head_delay = head_base_delay+current_head_delay; + head_base_delay += current_head_delay; + if (head_base_delay > 8*60) + head_base_delay = 0; + } + + new->obclass = realsolidobj; + new->speed = 3000; + new->flags |= of_shootable; +} + + + +//-------------------------------------------------------------------------- +// T_Head() +//-------------------------------------------------------------------------- +void T_Head(objtype *ob) +{ + fixed tempx,tempy; + unsigned temp_tilex,temp_tiley; + int angle; + + switch (head_mode) + { + case h_wait_to_rise: + if (head_delay < 0) + { + if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley)) + break; + if (CheckHandAttack(ob)) + break; + + ob->obclass = headobj; + ob->active = always; + head_mode = h_active; + head_delay = random(100)+random(60); + ob->hitpoints = EasyHitPoints(16); + } + else + head_delay -= tics; + + break; + + case h_player1: + case h_player2: + case h_player3: + case h_player4: + case h_active: + Chase (ob,true); + + if (!random(2) && (angle != -1)) + ShootPlayer(ob, hshotobj, 10000, &s_head_shot1); + + head_delay -= tics; + if (head_delay < 0) + { + head_mode = random(h_other4)+1; + head_delay = random(10*60)+random(50); + } + break; + + case h_other1: + case h_other2: + case h_other3: + case h_other4: + + tempx = player->x; + tempy = player->y; + temp_tilex = player->tilex; + temp_tiley = player->tiley; + + player->x = ((long)other_x[head_mode]<y = ((long)other_y[head_mode]<tilex = other_x[head_mode]; + player->tiley = other_y[head_mode]; + + if (Chase(ob,true)) + head_delay = 0; + + player->x = tempx; + player->y = tempy; + player->tilex = temp_tilex; + player->tiley = temp_tiley; + + head_delay -= tics; + if (head_delay <= 0) + { + head_mode = h_active; + head_delay = random(10*60)+random(50); + } + break; + } +} diff --git a/src/lib/hb/c6_act3.c b/src/lib/hb/c6_act3.c new file mode 100755 index 00000000..37402895 --- /dev/null +++ b/src/lib/hb/c6_act3.c @@ -0,0 +1,802 @@ +/* Catacomb Apocalypse 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_PLAY.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state); +void T_ShootPlayer(objtype *ob); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + + + +/* +============================================================================= + + DEMON + +============================================================================= +*/ + +void T_TrollDemon (objtype *ob); + +statetype s_demonpause = {DEMON1PIC,40,NULL,&s_demon2}; + +statetype s_demon1 = {DEMON1PIC,20,&T_TrollDemon,&s_demon2}; +statetype s_demon2 = {DEMON2PIC,20,&T_TrollDemon,&s_demon3}; +statetype s_demon3 = {DEMON3PIC,20,&T_TrollDemon,&s_demon4}; +statetype s_demon4 = {DEMON4PIC,20,&T_TrollDemon,&s_demon1}; + +statetype s_demonattack1 = {DEMONATTACK1PIC,20,NULL,&s_demonattack2}; +statetype s_demonattack2 = {DEMONATTACK2PIC,20,NULL,&s_demonattack3}; +statetype s_demonattack3 = {DEMONATTACK3PIC,30,&T_DoDamage,&s_demonpause}; + +statetype s_demonouch = {DEMONOUCHPIC,15,&T_TrollDemon,&s_demon1}; + +statetype s_demondie1 = {DEMONDIE1PIC,40,NULL,&s_demondie2}; +statetype s_demondie2 = {DEMONDIE2PIC,30,&LargeSound,&s_demondie3}; +statetype s_demondie3 = {DEMONDIE3PIC,0,NULL,&s_demondie3}; + + + +/* +=============== += += SpawnDemon += +=============== +*/ + +void SpawnDemon (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_demon1,PIXRADIUS*35); + new->obclass = demonobj; + new->speed = 2048; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(30); +} + + +/* +============================================================================= + + TROLL + +============================================================================= +*/ + +statetype s_trollpause = {TROLL1PIC, 30, &T_DoDamage, &s_troll2}; + +statetype s_troll1 = {TROLL1PIC, 13, &T_TrollDemon, &s_troll2}; +statetype s_troll2 = {TROLL2PIC, 13, &T_TrollDemon, &s_troll3}; +statetype s_troll3 = {TROLL3PIC, 13, &T_TrollDemon, &s_troll4}; +statetype s_troll4 = {TROLL4PIC, 13, &T_TrollDemon, &s_troll1}; + +statetype s_trollattack1 = {TROLLATTACK1PIC, 15, NULL, &s_trollattack2}; +statetype s_trollattack2 = {TROLLATTACK2PIC, 20, NULL, &s_trollpause}; + +statetype s_trollouch = {TROLLOUCHPIC, 14, &T_TrollDemon, &s_troll1}; + +statetype s_trolldie1 = {TROLLDIE1PIC, 18, NULL, &s_trolldie2}; +statetype s_trolldie2 = {TROLLDIE2PIC, 15, &LargeSound, &s_trolldie3}; +statetype s_trolldie3 = {TROLLDIE3PIC, 0, NULL, &s_trolldie3}; + + +/* +=============== += += SpawnTroll += +=============== +*/ + +void SpawnTroll (int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_troll1,35*PIXRADIUS); + new->speed = 2500; + new->obclass = trollobj; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(15); +} + + +/* +============================================================================= + + CYBORG DEMON + +============================================================================= +*/ + +void T_Demon (objtype *ob); + +statetype s_cyborg_demon1 = {CYBORG1PIC, 20, T_TrollDemon, &s_cyborg_demon2}; +statetype s_cyborg_demon2 = {CYBORG2PIC, 20, T_TrollDemon, &s_cyborg_demon3}; +statetype s_cyborg_demon3 = {CYBORG3PIC, 20, T_TrollDemon, &s_cyborg_demon4}; +statetype s_cyborg_demon4 = {CYBORG4PIC, 20, T_TrollDemon, &s_cyborg_demon1}; + +statetype s_cyborg_demonattack1 = {CYBORGATTACK1PIC, 20, NULL, &s_cyborg_demonattack2}; +statetype s_cyborg_demonattack2 = {CYBORGATTACK2PIC, 20, NULL, &s_cyborg_demonattack3}; +statetype s_cyborg_demonattack3 = {CYBORGATTACK3PIC, 30, T_DoDamage, &s_cyborg_demon2}; + +statetype s_cyborg_demonouch = {CYBORGOUCHPIC, 30, NULL, &s_cyborg_demon1}; + +statetype s_cyborg_demondie1 = {CYBORGOUCHPIC, 40, NULL, &s_cyborg_demondie2}; +statetype s_cyborg_demondie2 = {CYBORGDIE1PIC, 30, &LargeSound, &s_cyborg_demondie3}; +statetype s_cyborg_demondie3 = {CYBORGDIE2PIC, 20, NULL, &s_cyborg_demondie3}; + +/* +=============== += += SpawnCyborgDemon += +=============== +*/ + +void SpawnCyborgDemon (int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_cyborg_demon1, PIXRADIUS*35); + new->obclass = cyborgdemonobj; + new->speed = 2048; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(30); +} + + +/* +=============== += += T_TrollDemon += +=============== +*/ + +void T_TrollDemon (objtype *ob) +{ + if (Chase (ob,true) || (random(1000)obclass == cyborgdemonobj) + ob->state = &s_cyborg_demonattack1; + else + if (ob->obclass == trollobj) + ob->state = &s_trollattack1; + else + ob->state = &s_demonattack1; + ob->ticcount = ob->state->tictime; + } +} + + + + + +/* +============================================================================= + + INVISIBLE DUDE! + +============================================================================= +*/ + +void T_InvisibleDude (objtype *ob); + +statetype s_invis_fizz1 = {INVIS_FIZZ1PIC, 8, &T_InvisibleDude, &s_invis_fizz2}; +statetype s_invis_fizz2 = {INVIS_FIZZ2PIC, 8, &T_InvisibleDude, &s_invis_fizz3}; +statetype s_invis_fizz3 = {INVIS_FIZZ3PIC, 8, &T_InvisibleDude, &s_invis_walk}; + +statetype s_invis_walk = {0, 25, &T_InvisibleDude, &s_invis_walk}; +statetype s_invis_attack = {0, -1, &T_DoDamage, &s_invis_pause}; +statetype s_invis_pause = {0, 40, NULL, &s_invis_walk}; + +statetype s_invis_flash1 = {INVIS_FIZZ1PIC, 8, &T_InvisibleDude, &s_invis_walk}; +statetype s_invis_flash2 = {INVIS_FIZZ2PIC, 8, &T_InvisibleDude, &s_invis_walk}; +statetype s_invis_flash3 = {INVIS_FIZZ3PIC, 8, &T_InvisibleDude, &s_invis_walk}; + +statetype s_invis_death1 = {INVIS_DEATH1PIC, 40, NULL, &s_invis_death2}; +statetype s_invis_death2 = {INVIS_DEATH2PIC, 30, &LargeSound, &s_invis_death3}; +statetype s_invis_death3 = {INVIS_DEATH3PIC, 20, NULL, &s_invis_death3}; + +/* +=============== += += SpawnInvisDude += +=============== +*/ +void SpawnInvisDude(int tilex, int tiley) +{ + SpawnNewObj(tilex, tiley, &s_invis_walk, PIXRADIUS*20); + new->obclass = invisdudeobj; + new->speed = 2048; + new->flags |= of_shootable; + new->hitpoints = EasyHitPoints(20); + new->temp1 = 0; // for random flashing of pictures +} + + +/* +=============== += += T_InvisibleDude += +=============== +*/ +void T_InvisibleDude (objtype *ob) +{ + if (!random(100)) + { + switch (ob->temp1++) + { + case 0: + ob->state = &s_invis_flash1; + break; + + case 1: + ob->state = &s_invis_flash2; + break; + + case 2: + ob->state = &s_invis_flash3; + ob->temp1 = 0; + break; + } + ob->ticcount = ob->state->tictime; + } + + + if (Chase (ob,true)) + { + ob->state = &s_invis_attack; + ob->ticcount = ob->state->tictime; + } + +} + + + +/* +============================================================================= + + BOUNCE + +temp2 = set when hit player, reset when hit wall + +============================================================================= +*/ + +#define SPDBOUNCE 4096 +#define DMGBOUNCE 10 + +void T_Bounce (objtype *ob); +void T_Bounce_Death (objtype *ob); + +statetype s_bounce1 = {PSHOT1PIC, 8, &T_Bounce, &s_bounce2}; +statetype s_bounce2 = {PSHOT2PIC, 8, &T_Bounce, &s_bounce1}; + +/* +=============== += += SpawnBounce += +=============== +*/ + +void SpawnBounce (int tilex, int tiley, boolean towest) +{ + SpawnNewObj(tilex, tiley, &s_bounce1, 24*PIXRADIUS); + new->obclass = bounceobj; + new->hitpoints = EasyHitPoints(10); + new->flags |= of_shootable; + if (towest) + new->dir = west; + else + new->dir = north; +} + + +/* +=============== += += T_Bounce += +=============== +*/ + +void T_Bounce (objtype *ob) +{ + long move; + long deltax,deltay,size; + + move = SPDBOUNCE*tics; + size = (long)ob->size + player->size + move; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size && !ob->temp2) + { + ob->temp2 = 1; + TakeDamage (DMGBOUNCE); + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + // + // bounce if hit wall + // + switch (ob->dir) + { + case north: + if (tilemap[ob->tilex][--ob->tiley]) + { + ob->dir = south; + ob->tiley+=2; + ob->temp2 = 0; + } + break; + case east: + if (tilemap[++ob->tilex][ob->tiley]) + { + ob->dir = west; + ob->tilex-=2; + ob->temp2 = 0; + } + break; + case south: + if (tilemap[ob->tilex][++ob->tiley]) + { + ob->dir = north; + ob->tiley-=2; + ob->temp2 = 0; + } + break; + case west: + if (tilemap[--ob->tilex][ob->tiley]) + { + ob->dir = east; + ob->tilex+=2; + ob->temp2 = 0; + } + break; + } + + ob->distance = TILEGLOBAL; + + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); +} + + +/* +============================================================================= + + GRELMINAR + +============================================================================= +*/ + + +void T_Grelminar (objtype *ob); +void T_GrelminarShoot (objtype *ob); +void T_Grelm_DropKey(objtype *ob); + +statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2}; + +statetype s_grel1 = {GREL1PIC,20,T_Grelminar,&s_grel2}; +statetype s_grel2 = {GREL2PIC,20,T_Grelminar,&s_grel1}; + +statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause}; + +statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1}; + +statetype s_greldie1 = {GRELDIE1PIC,22,NULL,&s_greldie2}; +statetype s_greldie2 = {GRELDIE2PIC,22,NULL,&s_greldie3}; +statetype s_greldie3 = {GRELDIE3PIC,22,NULL,&s_greldie4}; +statetype s_greldie4 = {GRELDIE4PIC,22,NULL,&s_greldie5}; +statetype s_greldie5 = {GRELDIE5PIC,22,NULL,&s_greldie5a}; +statetype s_greldie5a = {GRELDIE5PIC,-1,T_Grelm_DropKey,&s_greldie6}; +statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6}; + +statetype s_gshot1 = {SKULL_SHOTPIC,8,T_ShootPlayer,&s_gshot1}; + +/* +=============== += += SpawnGrelminar += +=============== +*/ + +void SpawnGrelminar (int tilex, int tiley) +{ + unsigned Grel_Hard; + unsigned DropKey; + + SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*25); + new->obclass = grelmobj; + new->speed = 2048; + new->flags |= of_shootable; + + // + // if Grelminar is to drop a key the info-plane byte to the right + // should have a 1 in the highbyte, else he will not drop the key. + // + DropKey = *(mapsegs[2]+farmapylookup[tiley]+tilex+1); + if (DropKey) + new->temp1 = DropKey>>8; + else + new->temp1 = 0; + + // + // The info-plane byte below Grelminar will determine how powerful + // Grelminar is. If nothing is there, he is the most powerful. + // -- affected are the hit points and the shot damage. + // The hit points are controlled here, the shot damage is controlled + // within the spawning of the shot. See ShootPlayer for more info. + // + Grel_Hard = *(mapsegs[2]+farmapylookup[tiley+1]+tilex); + if (Grel_Hard) + { + new->temp2 = Grel_Hard>>8; + new->hitpoints = EasyHitPoints((new->temp2 * 10)); + } + else + { + new->hitpoints = EasyHitPoints(100); + new->temp2 = 10; + } +} + + +/* +=============== += += T_Grelminar += +=============== +*/ + +void T_Grelminar (objtype *ob) +{ + Chase (ob,false); + + if (!random(10)) + if (ShootPlayer(ob,gshotobj,ob->temp2,&s_gshot1)) + { + ob->state = &s_grelattack3; + ob->ticcount = ob->state->tictime; + } + if (CheckHandAttack(ob)) + TakeDamage (ob->temp2*3); + +} + + +//================================= +// +// T_Grelm_DropKey +// +//================================= +void T_Grelm_DropKey(objtype *ob) +{ + if (!(ob->temp1)) + { + ob->state = NULL; + return; + } + + SpawnBonus(ob->tilex,ob->tiley,B_RKEY); + SD_PlaySound(GRELM_DEADSND); + ob->temp1 = false; +} + + + +//-------------------------------------------------------------------------- +// ShootPlayer() +//-------------------------------------------------------------------------- +boolean ShootPlayer(objtype *ob, short obclass, short speed, statetype *state) +{ + int angle = AngleNearPlayer(ob); + + if (angle == -1) + return(false); + + DSpawnNewObjFrac (ob->x,ob->y,state,PIXRADIUS*14); + new->obclass = obclass; + new->active = always; + new->angle = angle; + + // + // If the shot is Grelminar's, then determine the power of the shot. + // The shot speed is hard-wired as 10000. But the shot power is + // determined by speed. Speed now contains "Grelminar's level of + // hardness" and this is multiplied by 3 to get the shot power. + // + if (obclass == gshotobj) + { + new->speed = 10000; + new->temp1 = speed*3; + } + else + new->speed = speed; + + + return(true); +} + +//-------------------------------------------------------------------------- +// T_ShootPlayer() +//-------------------------------------------------------------------------- +void T_ShootPlayer(objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + + speed = ob->speed*tics; + + xmove = FixedByFrac(speed,costable[ob->angle]); + ymove = -FixedByFrac(speed,sintable[ob->angle]); + + if (ShotClipMove(ob,xmove,ymove)) + { + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + + +// check for collision with wall +// + if (tilemap[ob->tilex][ob->tiley]) + { +// SD_PlaySound (SHOOTWALLSND); + ob->state = &s_pshot_exp1; + ob->ticcount = s_pshot_exp1.tictime; + return; + } + + + +// check for collision with player +// + if ( ob->xl <= player->xh + && ob->xh >= player->xl + && ob->yl <= player->yh + && ob->yh >= player->yl) + { + switch (ob->obclass) + { + case wshotobj: // Wizard's shot + TakeDamage (7); + break; + + case hshotobj: // Egyptian Head's shot + TakeDamage (5); + break; + + case bshotobj: // Blob's shot + TakeDamage (5); + break; + + case rshotobj: // Ray's shot + TakeDamage (5); + break; + + case rbshotobj: // RamBone's shot + TakeDamage(7); + break; + + case fmshotobj: // Future Mage's shot + TakeDamage(7); + break; + + case rtshotobj: // RoboTank's shot + TakeDamage(15); + break; + + case syshotobj: // Stompy's shot + TakeDamage(7); + break; + + case bgshotobj: // Bug's shot + TakeDamage(7); + break; + + case eshotobj: // Eye's shot + TakeDamage(5); + break; + + case gshotobj: + TakeDamage (ob->temp1); // the damage of Grelminar's shot - + break; // see Grelminar's spawning + + } + ob->state = NULL; + return; + } + +// check for collision with other solid and realsolid objects. +// Great terminology!! -- solid objects really aren't solid +// -- realsolid objects ARE solid +// if ((actorat[ob->tilex][ob->tiley]) && (actorat[ob->tilex][ob->tiley]->obclass != ob->obclass)) + if (((actorat[ob->tilex][ob->tiley]->obclass == realsolidobj) || + (actorat[ob->tilex][ob->tiley]->obclass == solidobj)) && + (actorat[ob->tilex][ob->tiley]->flags & of_shootable)) + { + ob->state = &s_pshot_exp1; + ob->ticcount = s_pshot_exp1.tictime; + return; + } + + +// check for collision with player +// + for (check = player->next; check; check=check->next) + if ((ob->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + switch (ob->obclass) + { +// APOCALYPSE + case wshotobj: // Wizard's shot + ShootActor (check, 3); + break; + + case hshotobj: // Egyptian Head's shot + ShootActor (check, 5); + break; + + case bshotobj: // Blob's shot + ShootActor (check, 2); + break; + + case rshotobj: // Ray's shot + ShootActor (check, 5); + break; + + case rbshotobj: // RamBone's shot + ShootActor (check, 5); + break; + + case fmshotobj: // Future Mage's shot + ShootActor (check, 5); + break; + + case rtshotobj: // RoboTank's shot + ShootActor (check, 15); + break; + + case syshotobj: // Stompy's shot + ShootActor (check, 5); + break; + + case bgshotobj: // Bug's shot + ShootActor (check, 3); + break; + + case eshotobj: // Eye's shot + ShootActor (check, 2); + break; + + case gshotobj: + ShootActor (check,25); //NOLAN--check on me!!!!!!! + break; + + case pshotobj: + ShootActor (check,25); + break; + + } + ob->state = &s_pshot_exp1; + ob->ticcount = s_pshot_exp1.tictime; + return; + } +} + +//------------------------------------------------------------------------- +// AngleNearPlayer() +//------------------------------------------------------------------------- +int AngleNearPlayer(objtype *ob) +{ + int angle=-1; + int xdiff = ob->tilex-player->tilex; + int ydiff = ob->tiley-player->tiley; + + if (ob->tiley == player->tiley) + { + if (ob->tilex < player->tilex) + angle = 0; + else + angle = 180; + } + else + if (ob->tilex == player->tilex) + { + if (ob->tiley < player->tiley) + angle = 270; + else + angle = 90; + } + else + if (xdiff == ydiff) + if (ob->tilex < player->tilex) + { + if (ob->tiley < player->tiley) + angle = 315; + else + angle = 45; + } + else + { + if (ob->tiley < player->tiley) + angle = 225; + else + angle = 135; + } + + return(angle); +} + + diff --git a/src/lib/hb/c6_act4.c b/src/lib/hb/c6_act4.c new file mode 100755 index 00000000..ced11788 --- /dev/null +++ b/src/lib/hb/c6_act4.c @@ -0,0 +1,260 @@ +/* Catacomb Apocalypse 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. + */ + +// C4_PLAY.C + +#include "DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +//------------------------------------------------------------------------- +// +// MISC OBJECTS +// +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// COLUMN, SULPHUR GAS HOLE, FIRE POT, FOUNTAIN +//------------------------------------------------------------------------- + + +void SpawnMiscObjects(int tilex, int tiley, int num); + +statetype s_column1 = {COLUMN1PIC, 20, NULL, &s_column1}; +statetype s_column2 = {COLUMN2PIC, 20, NULL, &s_column2}; +statetype s_column3 = {COLUMN3PIC, 20, NULL, &s_column3}; +statetype s_column4 = {COLUMN4PIC, 20, NULL, &s_column4}; +statetype s_column5 = {COLUMN5PIC, 20, NULL, &s_column5}; +statetype s_ffire_pot = {FFIRE_POTPIC, 20, NULL, &s_ffire_pot}; +statetype s_ofire_pot1 = {OFIRE_POT1PIC, 20, NULL, &s_ofire_pot2}; +statetype s_ofire_pot2 = {OFIRE_POT2PIC, 20, NULL, &s_ofire_pot1}; +statetype s_tomb1 = {TOMB1PIC, 20, NULL, &s_tomb1}; +statetype s_tomb2 = {TOMB2PIC, 20, NULL, &s_tomb2}; +void SpawnMiscObjects(int tilex, int tiley, int num) +{ + statetype *objstate; + + switch (num) + { + case 1: + objstate = &s_column1; + break; + + case 2: + objstate = &s_column2; + break; + + case 3: + objstate = &s_column3; + break; + + case 4: + objstate = &s_ffire_pot; + break; + + case 5: + objstate = &s_column4; + break; + + case 6: + objstate = &s_ofire_pot1; + break; + + case 7: + objstate = &s_tomb1; + break; + + case 8: + objstate = &s_tomb2; + break; + + case 9: + objstate = &s_column5; + break; + } + + SpawnNewObj(tilex, tiley, objstate, PIXRADIUS*10); + new->obclass = realsolidobj; + new->flags |= of_shootable; +} + + + +//------------------------------------------------------------------------ +// FORCE FIELD +//------------------------------------------------------------------------ + +void SpawnForceField(int tilex, int tiley); +void T_ForceField(objtype *ob); +void T_ForceFieldRemove(objtype *ob); + +statetype s_force_field_1 = {FORCE_FIELD_1PIC, 10, T_ForceField, &s_force_field_2}; +statetype s_force_field_2 = {FORCE_FIELD_2PIC, 10, T_ForceField, &s_force_field_3}; +statetype s_force_field_3 = {FORCE_FIELD_3PIC, 10, T_ForceField, &s_force_field_4}; +statetype s_force_field_4 = {FORCE_FIELD_4PIC, 10, T_ForceField, &s_force_field_1}; + +statetype s_force_field_die = {0,0,T_ForceFieldRemove,&s_force_field_die1}; +statetype s_force_field_die1 = {0,0,NULL,NULL}; + +void SpawnForceField(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_force_field_1,PIXRADIUS*35); + new->obclass = solidobj; + new->hitpoints = EasyHitPoints(20); + new->temp1 = 0; + new->flags |= of_forcefield; //sets bit 7 :: makes it nonsolid, but also detectable + // without adding another object type! + new->flags |= of_shootable; +} + +void T_ForceField(objtype *ob) +{ + long move,deltax,deltay,size; + + size = (long)ob->size + player->size; + + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) + if (!new->temp1) + { + TakeDamage (94); + new->temp1 = 1; + return; + } + else + return; + new->temp1 = 0; +} + +void T_ForceFieldRemove(objtype *ob) +{ + actorat[ob->tilex][ob->tiley] = 0; +} + + + + +//------------------------------------------------------------------------- +// +// INVISIBLE WALL CONTROLLER +// +//------------------------------------------------------------------------- + +void SpawnInvisWallCntroller(int x, int y); +void T_InvisWall(objtype *ob); + +extern statetype s_invis_wall_control; + +statetype s_invis_wall_control = {0, 10, T_InvisWall, &s_invis_wall_control}; + +void SpawnInvisWallCntroller(int tilex, int tiley) +{ + SpawnNewObj(tilex,tiley,&s_invis_wall_control,PIXRADIUS*35); + new->obclass = solidobj; + new->flags &= ~of_shootable; + new->temp1 = tilemap[tilex][tiley]; // Number for the wall tile here + // Used for replacing the wall tile +} + +void T_InvisWall(objtype *ob) +{ + long move,deltax,deltay,size; + + size = (long)ob->size + player->size; + + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if ((deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) || + (ob->tilex == player->tilex) && (ob->tiley == player->tiley)) + { + // Get rid of the wall tile if you are on it + tilemap[ob->tilex][ob->tiley] = 0; + } + else + { + // Replace wall tile + tilemap[ob->tilex][ob->tiley] = ob->temp1; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// EasyHitPoints +// +// Checks to see if the player has selected the easy mode for playing. +// If so then the normal hit points are cut in half. +// This is when the object is spawned. +// +// Parms +// NrmHitPts - the normal hit points +// +// Returns +// Half of NrmHitPts +// +///////////////////////////////////////////////////////////////////////////// + +int EasyHitPoints(int NrmHitPts) +{ + if (EASYMODEON) // Wimpy, Wimpy, Wimpy!!!!! + { + return(NrmHitPts/4); + } + else + return(NrmHitPts); +} + +///////////////////////////////////////////////////////////////////////////// +// +// EasyDoDamage +// +// Checks to see if the player has selected the easy mode for playing. +// If so then the normal amount of damage is cut in half. +// This is called each time a monster does damage. +// +// Parms +// Damage - the normal damage taken +// +// Returns +// Half of Damage +// +///////////////////////////////////////////////////////////////////////////// + +int EasyDoDamage(int Damage) +{ + if (EASYMODEON) // Wimpy, Wimpy, Wimpy!!!!! + return(Damage/2); + else + return(Damage); +} + diff --git a/src/lib/hb/c6_asm.asm b/src/lib/hb/c6_asm.asm new file mode 100755 index 00000000..004c20ae --- /dev/null +++ b/src/lib/hb/c6_asm.asm @@ -0,0 +1,248 @@ +; Catacomb Apocalypse 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. + +IDEAL + +MODEL MEDIUM,C + +INCLUDE "ID_ASM.EQU" + +VIEWWIDTH = (40*8) ;33 +GC_INDEX = 03CEh + +DATASEG +EVEN + +;=================== Tables filled in by DrawVWall ========================== + +; +; wallheight has the height (scale number) of that collumn of scaled wall +; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height) +; +wallheight dw VIEWWIDTH dup (?) + +; +; wallwidth has the pixel width (1-7) of that collumn +; +wallwidth dw VIEWWIDTH dup (?) + +; +; wallseg has the segment of the wall picture +; +wallseg dw VIEWWIDTH dup (?) + +; +; wallofs has the offset of the wall picture +; +wallofs dw VIEWWIDTH dup (?) + +;============================================================================ + +; +; screenbyte is just position/8 +; +LABEL screenbyte WORD +pos = 0 +REPT VIEWWIDTH + dw pos/8 +pos = pos+1 +ENDM + +; +; screenbit is (position&7)*16 +; +LABEL screenbit WORD +pos = 0 +REPT VIEWWIDTH + dw (pos AND 7)*16 +pos = pos+1 +ENDM + +; +; Use offset: screenbit[]+pixwidth*2 +; acess from bitmasks-2+offset for one biased pixwidth +; the low byte of bitmasks is for the first screen byte, the high byte +; is the bitmask for the second screen byte (if non 0) +; + +bitmasks dw 0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh + dw 0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh + dw 0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh + dw 0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh + dw 0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh + dw 0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h + dw 0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h + dw 0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h + + +; +; wallscalecall is a far pointer to the start of a compiled scaler +; The low word will never change, while the high word is set to +; compscaledirectory[scale] +; +wallscalecall dd (65*6) ; offset of t_compscale->code[0] + + +PUBLIC wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit +PUBLIC bitmasks,wallscalecall + + +EXTRN scaledirectory:WORD ; array of MAXSCALE segment pointers to + ; compiled scalers +EXTRN screenseg:WORD ; basically just 0xa000 +EXTRN bufferofs:WORD ; offset of the current work screen +EXTRN ylookup:WORD +EXTRN screenpage:WORD + +CODESEG + +;============================================================================ +; +; ScaleWalls +; +; AX AL is scratched in bit mask setting and scaling +; BX table index +; CX pixwidth*2 +; DX GC_INDEX +; SI offset into wall data to scale from, allways 0,64,128,...4032 +; DI byte at top of screen that the collumn is contained in +; BP x pixel * 2, index into VIEWWIDTH wide tables +; DS segment of the wall data to texture map +; ES screenseg +; SS addressing DGROUP variables +; +;============================================================================ + +PROC ScaleWalls +PUBLIC ScaleWalls +USES SI,DI,BP + + xor bp,bp ; start at location 0 in the tables + mov dx,GC_INDEX+1 + mov es,[screenseg] + +; +; scale one collumn of data, possibly across two bytes +; +nextcollumn: + + mov bx,[wallheight+bp] ; height of walls (1-MAXSCALE) + shl bx,1 + mov ax,[ss:scaledirectory+bx] ; segment of the compiled scaler + mov [WORD PTR ss:wallscalecall+2],ax + + mov cx,[wallwidth+bp] + or cx,cx + jnz okwidth + mov cx,2 + jmp next + +okwidth: + shl cx,1 + mov ds,[wallseg+bp] + mov si,[wallofs+bp] + + mov di,[screenbyte+bp] ; byte at the top of the scaled collumn + add di,[ss:bufferofs] ; offset of current page flip + mov bx,[screenbit+bp] ; 0-7 << 4 + add bx,cx + mov ax,[ss:bitmasks-2+bx] + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels + or ah,ah ; is there anything in the second byte? + jnz secondbyte +; +; next +; +next: + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + jmp done + +; +; draw a second byte for vertical strips that cross two bytes +; +secondbyte: + mov al,ah + inc di ; next byte over + out dx,al ; set bit mask register + call [DWORD PTR ss:wallscalecall] ; scale the line of pixels +; +; next +; + add bp,cx + cmp bp,VIEWWIDTH*2 + jb nextcollumn + +done: + mov ax,ss + mov ds,ax + ret + +ENDP + +;--------------------------------------------------------------------------- +; +; RadarBlip() +; +; Displays a 'blip' (1 pixel wide X 2 pixel high) on the radar at +; an (X,Y) relative to (RADAR_X,RADAR_Y) (defined below...) +; +;--------------------------------------------------------------------------- + +PROC RadarBlip x:WORD, y:WORD, color:WORD +USES SI,DI +PUBLIC RadarBlip + + mov ax,[screenseg] + + mov es,ax + xor di,di + + lea si,[ylookup] + add si,[y] + add si,[y] + add di,[si] + + mov ax,[x] + shr ax,1 + shr ax,1 + shr ax,1 + add di,ax + + mov ax,[x] + and ax,7 + mov cl,al + mov ah,080h + shr ah,cl + cli + mov al,GC_BITMASK + mov dx,GC_INDEX + out dx,ax + sti + + mov ax,[color] + mov ah,[es:di] ; read into latches + mov [es:di],al ; write latches / color bit + + ret + +ENDP + +END + diff --git a/src/lib/hb/c6_debug.c b/src/lib/hb/c6_debug.c new file mode 100755 index 00000000..f3ce665a --- /dev/null +++ b/src/lib/hb/c6_debug.c @@ -0,0 +1,799 @@ +/* Catacomb Apocalypse 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_DEBUG.C + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define DEBUG_OVERHEAD 0 + + +#define VIEWTILEX 20 +#define VIEWTILEY (VIEWHEIGHT/16) + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +short colordelay=0; +//boolean autofire=false; +int maporgx; +int maporgy; +enum {mapview,tilemapview,actoratview,visview,mapseg2,lastview} viewtype; + +void ViewMap (void); + +//=========================================================================== + +#if 0 +/* +================ += += PicturePause += +================ +*/ + +void PicturePause (void) +{ + int y; + unsigned source; + + source = displayofs+panadjust; + +// VW_ColorBorder (15); + VW_SetLineWidth (40); + VW_SetScreen (0,0); + + if (source<0x10000l-200*64) + { + // + // copy top line first + // + for (y=0;y<200;y++) + VW_ScreenToScreen (source+y*64,y*40,40,1); + } + else + { + // + // copy bottom line first + // + for (y=199;y>=0;y--) + VW_ScreenToScreen (source+y*64,y*40,40,1); + } + + IN_Shutdown (); + + VW_WaitVBL(70); + bioskey(0); + VW_WaitVBL(70); + Quit (NULL); +} +#endif + + +//=========================================================================== + +//=========================================================================== + +#define sc_1 0x02 +#define sc_2 0x03 +#define sc_3 0x04 +#define sc_4 0x05 +#define sc_5 0x06 +#define sc_6 0x07 +#define sc_7 0x08 +#define sc_8 0x09 +#define sc_9 0x0a +#define sc_0 0x0b + + + +/* +================ += += DebugKeys += +================ +*/ + +int DebugKeys (void) +{ + boolean esc; + int level,i; + +#if DEBUG_KEYS_AVAILABLE + if (Keyboard[sc_R]) + { + CenterWindow (12,2); + if (autofire) + US_PrintCentered ("Rapid-Fire OFF"); + else + US_PrintCentered ("Rapid-Fire ON"); + VW_UpdateScreen(); + IN_Ack(); + autofire ^= 1; + return 1; + } +#endif + +#if DEBUG_KEYS_AVAILABLE + if (Keyboard[sc_A]) + { + char levelstr[50]; + unsigned org_tile,org_mapon,msgnum; + boolean newmsg=true,newlevel=false; + + VW_FixRefreshBuffer (); + CenterWindow (16,3); + US_Print("\n"); + US_CPrint("Message Test"); + VW_UpdateScreen(); + + org_mapon = mapon; + msgnum = (org_tile = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex))-NAMESTART; + while (1) + { + // Get outta' here + // + if (Keyboard[sc_Escape]) + { + while (Keyboard[sc_Escape]); + break; + } + + // Move to previous message + // + if (Keyboard[sc_UpArrow]) + { + if (msgnum) + { + msgnum--; + newmsg = true; + } + } + + // Move to next message + // + if (Keyboard[sc_DownArrow]) + { + if (msgnum < 24) + { + msgnum++; + newmsg = true; + } + } + + // Move to previous level + // + if (Keyboard[sc_LeftArrow]) + { + if (mapon) + { + MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3); + mapon--; + newlevel = true; + } + } + + // Move to next level + // + if (Keyboard[sc_RightArrow]) + { + if (mapon < LASTMAP-1) + { + MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3); + mapon++; + newlevel = true; + } + } + + // Load new level text + // + if (newlevel) + { + CA_CacheGrChunk(LEVEL1TEXT+mapon); + ScanText(); + newmsg = true; + newlevel=false; + } + + // Display new message text + // + if (newmsg) + { + *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = msgnum+NAMESTART; + DrawText(true); + strcpy(levelstr,"Level: "); + itoa(mapon,levelstr+strlen(levelstr),10); + strcat(levelstr," Msg: "); + itoa(msgnum,levelstr+strlen(levelstr),10); + DisplaySMsg(levelstr,NULL); + newmsg = false; + + if (Keyboard[sc_UpArrow] || Keyboard[sc_DownArrow] || Keyboard[sc_LeftArrow] || Keyboard[sc_RightArrow]) + VW_WaitVBL(6); + } + + } +// Restore game +// + MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3); + mapon = org_mapon; + CA_CacheGrChunk(LEVEL1TEXT+mapon); + ScanText(); + *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = org_tile; + DrawText(true); + status_flag = 0; + } + + + if (Keyboard[sc_V]) + { + displayofs = bufferofs = screenloc[screenpage]; + CenterWindow (16,4); + US_CPrint("\n"GAMENAME); + US_CPrint(VERSION); + US_CPrint(REVISION); + VW_UpdateScreen(); + IN_Ack (); + } + +#endif + if (Keyboard[sc_Q]) // Q = Insta-Quit! + Quit("Insta-Quit!"); +#if 0 + if (Keyboard[sc_Z]) // Z = freeze Time + { + if (FreezeTime) + FreezeTime = 1; // Allow refresh to dec to zero.. + else + StopTime(); + + IN_Ack(); + return 1; + } +#endif + + +// if (Keyboard[sc_E]) +// FaceDoor((player->x>>16l)+1,(player->y>>16l)); +// FaceAngle(90); + +#if 0 + if (Keyboard[sc_B]) // B = border color + { + CenterWindow(24,3); + PrintY+=6; + US_Print(" Border color (0-15):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=15) + VW_ColorBorder (level); + } + return 1; + } +#endif + + +#if 1//DEBUG_KEYS_AVAILABLE + if (Keyboard[sc_O]) + { + extern unsigned objectcount,latchmemavail; + unsigned unused,total; + + CenterWindow (30,13); + US_Print ("Objects: "); + US_PrintUnsigned (objectcount); + + US_Print("\n\nTics: "); + US_PrintUnsigned (tics); + US_Print(" Real Tics: "); + US_PrintUnsigned(realtics); + + US_Print ("\n\n Total Available: "); + US_PrintUnsigned (mminfo.mainmem/1024); + US_Print ("k\n Mem In Use: "); + unused=MM_UnusedMemory()/1024; + US_PrintUnsigned (unused); + US_Print ("k\n Mem After Purge: "); + total=MM_TotalFree()/1024; + US_PrintUnsigned (total); + US_Print ("k ("); + US_PrintUnsigned (total-unused); + + US_Print (")\n\nLatch Mem Free: "); + US_PrintUnsigned (latchmemavail); + US_Print ("\n"); + VW_UpdateScreen(); + IN_Ack(); + } + + if (colordelay<1) + { + if (Keyboard[26]) + { + extern unsigned *groundcolor,debug_gnd; + + groundcolor = &debug_gnd; + debug_gnd += 0x0101; + if (debug_gnd == 0x1010) + debug_gnd = 0; + colordelay = 10; + } + + if (Keyboard[27]) + { + extern unsigned *skycolor,debug_sky; + + skycolor = &debug_sky; + debug_sky += 0x0101; + if (debug_sky == 0x1010) + debug_sky = 0; + colordelay = 10; + } + } + else + colordelay -= realtics; +#endif + + +#if 0 + if (Keyboard[sc_C]) // C = count objects + { + CountObjects(); + return 1; + } + + + if (Keyboard[sc_D]) // D = start / end demo record + { + if (DemoMode == demo_Off) + StartDemoRecord (); + else if (DemoMode == demo_Record) + { + EndDemoRecord (); + playstate = ex_completed; + } + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_E]) // E = quit level + { + if (tedlevel) + TEDDeath(); + playstate = ex_warped; + gamestate.mapon++; + } +#endif + +#if 0 + if (Keyboard[sc_F]) // F = facing spot + { + CenterWindow (12,4); + US_Print ("X:"); + US_PrintUnsigned (player->x); + US_Print ("Y:"); + US_PrintUnsigned (player->y); + US_Print ("A:"); + US_PrintUnsigned (player->angle); + VW_UpdateScreen(); + IN_Ack(); + return 1; + } +#endif + + if (Keyboard[sc_G]) // G = god mode + { + CenterWindow (12,2); + if (godmode) + US_PrintCentered ("God mode OFF"); + else + US_PrintCentered ("God mode ON"); + VW_UpdateScreen(); + IN_Ack(); + godmode ^= 1; + return 1; + } + +#if 0 + if (Keyboard[sc_H]) // H = hurt self + { + TakeDamage (5); + } +#endif + + if (Keyboard[sc_I]) // I = item cheat + { + extern boolean redraw_gems; + + CenterWindow (12,3); + US_PrintCentered ("Free items!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); +// if (!gamestate.keys[i]) + GiveKey (i); + gamestate.gems[i] = GEM_DELAY_TIME; + } + gamestate.gems[4] = GEM_DELAY_TIME; + redraw_gems = true; +///////// for (i=0;i<8;i++) +///////// GiveScroll (i,false); + + IN_Ack (); + return 1; + } + +#if DEBUG_OVERHEAD + if (Keyboard[sc_Z]) // O is used elsewhere... + { + ViewMap(); + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_P]) // P = pause with no screen disruptioon + { + PicturePause (); + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_S]) // S = slow motion + { + singlestep^=1; + CenterWindow (18,3); + if (singlestep) + US_PrintCentered ("Slow motion ON"); + else + US_PrintCentered ("Slow motion OFF"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } +#endif + +#if 0 + if (Keyboard[sc_V]) // V = extra VBLs + { + CenterWindow(30,3); + PrintY+=6; + US_Print(" Add how many extra VBLs(0-8):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=8) + extravbls = level; + } + return 1; + } +#endif + + if (Keyboard[sc_W]) // W = warp to level + { + CenterWindow(26,3); + PrintY+=6; + US_Print(" Warp to which level(0-17):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=LASTMAP-1) + { + gamestate.mapon = level; + playstate = ex_warped; + lasttext = -1; + } + } + return 1; + } + +#if 0 + if (Keyboard[sc_X]) // X = item cheat + { + CenterWindow (12,3); + US_PrintCentered ("Extra stuff!"); + VW_UpdateScreen(); + for (i=0;i<4;i++) + { + GiveBolt (); + GiveNuke (); + GivePotion (); + } + IN_Ack (); + return 1; + } +#endif + +//////// if (LastScan >= sc_1 && LastScan <= sc_8) // free scrolls +//////// { +//////// GiveScroll (LastScan-sc_1,false); +//////// IN_ClearKeysDown (); +//////// } + + return 0; +} + + +#if DEBUG_OVERHEAD + +/* +===================== += += LatchDrawChar += +===================== +*/ + +void LatchDrawChar (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = latchpics[0]+picnum*8; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm dec bx + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] + +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + +#endif + + +#if DEBUG_OVERHEAD +/* +===================== += += LatchDrawTile += +===================== +*/ + +void LatchDrawTile (unsigned x, unsigned y, unsigned picnum) +{ + unsigned source, dest; + + dest = bufferofs + ylookup[y]+x; + source = tileoffsets[picnum]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,2 + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,16 + +lineloop: +asm movsb +asm movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} +#endif + + +#if DEBUG_OVERHEAD +/* +=================== += += OverheadRefresh += +=================== +*/ + +void OverheadRefresh (void) +{ + unsigned x,y,endx,endy,sx,sy; + unsigned tile; + + + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + endx = maporgx+VIEWTILEX; + endy = maporgy+VIEWTILEY; + + for (y=maporgy;y>12)); + LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8)); + LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4)); + LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f)); + } + } + + VW_SetScreen (bufferofs,0); + displayofs = bufferofs; +} + + +/* +=================== += += ViewMap += +=================== +*/ + +void ViewMap (void) +{ + boolean button0held; + + viewtype = actoratview; + button0held = false; + + + maporgx = player->tilex - VIEWTILEX/2; + if (maporgx<0) + maporgx = 0; + maporgy = player->tiley - VIEWTILEY/2; + if (maporgy<0) + maporgy = 0; + + do + { +// +// let user pan around +// + IN_ReadControl(0,&control); + if (control.xaxis == -1 && maporgx>0) + maporgx--; + if (control.xaxis == 1 && maporgx0) + maporgy--; + if (control.yaxis == 1 && maporgyVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +//========================================================================== + +void DrawLineDot (int xl, int xh, int y,int color) +{ + unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid; + + xlb=xl/8; + xhb=xh/8; + + if (xhVIEWYH) + Quit("DrawLine: y>VIEWYH"); + + xlp = xl&7; + maskdot = dotmask[xlp]; + maskleft = leftmask[xlp]; + maskright = rightmask[xh&7]; + + mid = xhb-xlb-1; + dest = bufferofs+ylookup[y]+xlb; + + // + // set the GC index register to point to the bit mask register + // + asm mov al,GC_BITMASK + asm mov dx,GC_INDEX + asm out dx,al + + if (xlb==xhb) + { + // + // entire line is in one byte + // + + maskleft&=maskright; + + asm mov es,[screenseg] + asm mov di,[dest] + asm mov dx,GC_INDEX+1 + + asm mov al,[BYTE PTR maskleft] + asm out dx,al // mask off pixels + + asm mov al,[BYTE PTR color] + asm xchg al,[es:di] // load latches and write pixels + + + // + // write the black dot at the start + // + asm mov al,[BYTE PTR maskdot] + asm out dx,al // mask off pixels + + asm xor al,al + asm xchg al,[es:di] // load latches and write pixels + + + return; + } + +asm mov es,[screenseg] +asm mov di,[dest] +asm mov dx,GC_INDEX+1 +asm mov bh,[BYTE PTR color] + +// +// draw left side +// +asm mov al,[BYTE PTR maskleft] +asm out dx,al // mask off pixels + +asm mov al,bh +asm xchg al,[es:di] // load latches and write pixels + +// +// write the black dot at the start +// +asm mov al,[BYTE PTR maskdot] +asm out dx,al // mask off pixels +asm xor al,al +asm xchg al,[es:di] // load latches and write pixels +asm inc di + +// +// draw middle +// +asm mov al,255 +asm out dx,al // no masking + +asm mov al,bh +asm mov cx,[mid] +asm rep stosb + +// +// draw right side +// +asm mov al,[BYTE PTR maskright] +asm out dx,al // mask off pixels +asm xchg bh,[es:di] // load latches and write pixels + +} + +#endif + +//========================================================================== + + +long wallscalesource; + +#ifdef DRAWEACH +/* +==================== += += ScaleOneWall += +==================== +*/ + +void near ScaleOneWall (int xl, int xh) +{ + int x,pixwidth,height; + + *(((unsigned *)&wallscalesource)+1) = wallseg[xl]; + + for (x=xl;x<=xh;x+=pixwidth) + { + height = wallheight[x]; + pixwidth = wallwidth[x]; + (unsigned)wallscalesource = wallofs[x]; + + *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height]; + (unsigned)scaletablecall = scaledirectory[height]->codeofs[0]; + + // + // scale a byte wide strip of wall + // + asm mov bx,[x] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm mov es,[screenseg] + asm lds si,[wallscalesource] + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + + asm mov al,BYTE PTR [ss:bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm out dx,al // set bit mask register + asm call [DWORD PTR ss:scaletablecall] // scale the line of pixels + nosecond: + asm mov ax,ss + asm mov ds,ax + } +} + +#endif + +char wall_anim_pos[NUMFLOORS]; + +// EAST / WEST WALLS +// +int far walllight1[NUMFLOORS] = {0, + + 0,//CRYSTAL1LIGHTPIC, + 0,//EGYPT1LIGHTPIC, + EGYPT2LIGHTPIC, + EGYPT3LIGHTPIC, + + FIREWALL1PIC, + FIREWALL2PIC, + FIREWALL3PIC, + FIREWALL4PIC, + + + NEMESISPIC, + + ALTARLEFTPIC, + ALTARRIGHTPIC, + + TEMPLEWALLLIGHTPIC, + + TORCHWALL1PIC, + TORCHWALL2PIC, + + BRNBRKLIGHTPIC, + BRNBRKEMLIGHTPIC, + + IRONGATEPIC, + + BRNFLGLIGHTPIC, + BRNFLGWINDOWLIGHTPIC, + BRNFLGVINELIGHTPIC, + BRNFLGDMGLIGHTPIC, + + SPACEDMG1LIGHTPIC, + SPACEDMG2LIGHTPIC, + + SPACE1LIGHTPIC, + SPACE2LIGHTPIC, + SPACE3LIGHTPIC, + SPACE4LIGHTPIC, + + SPACE5LIGHTPIC, + SPACE6LIGHTPIC, + SPACE7LIGHTPIC, + SPACE8LIGHTPIC, + + 0,//SPACE9LIGHTPIC, + 0,//SPACEDMG9LIGHTPIC, + SPACE10LIGHTPIC, + RUSTDOORLIGHTPIC, + + SPACE11LIGHTPIC, + SPACE12LIGHTPIC, + SPACE13LIGHTPIC, + SPACE14LIGHTPIC, + + SPACEDMG5LIGHTPIC, + SPACEDMG6LIGHTPIC, + + TAP1PIC, + TAP2PIC, + ENDPIC, + 0,//SIRONLIGHTPIC, + + SPCDOOR1LIGHTPIC, + SPCDOOR2LIGHTPIC, + SPCDOOR3LIGHTPIC, + SPCDOOR4LIGHTPIC, + + COLUMNSLIGHTPIC, + + DEMONSTATUELIGHTPIC, + + 0,//CRYSTALBWALL1LIGHTPIC, + + 0,//SRUSTLIGHTPIC, + + TROLLSTATUELIGHTPIC, + + BRNDMGVINELIGHTPIC, + TAP3PIC, + HORNDOORPIC, + RUNEDOORPIC, + + EXP_WALL_1PIC, + EXP_WALL_2PIC, + EXP_WALL_3PIC, + WATER_EXP_WALL_1PIC, + WATER_EXP_WALL_2PIC, + WATER_EXP_WALL_3PIC, + + IRONDMGLIGHTPIC, + IRONLIGHTPIC, + 0, + TROLLBLOODYLIGHTPIC, + TROLLLIGHTPIC, + + 0, // INVISIBLE WALL + + STONEDOORLIGHTPIC, + 0, + + IRONWTR1LIGHTPIC, + IRONWTR2LIGHTPIC, + IRONWTR3LIGHTPIC, + + RUSTWTR1LIGHTPIC, + RUSTWTR2LIGHTPIC, + RUSTWTR3LIGHTPIC, + + CEMETARYLIGHTPIC, + 0, // STAIRDWNLIGHTPIC, + + WGRATE1LIGHTPIC, + WGRATE2LIGHTPIC, + WGRATE3LIGHTPIC, + + MAS_WIN_LIGHTPIC, + MAS_DOOR_LIGHTPIC, + MAS_VINE1_LIGHTPIC, + MAS_VINE2_LIGHTPIC, + + // Start of non-solid walls + 0, + 0, + 0, + 0, + 0, + 0, + + // solid walls + SGRATEPIC, +}; + +// NORTH / SOUTH WALLS +// +int far walldark1[NUMFLOORS] = {0, + + 0,//CRYSTAL1DARKPIC, + 0,//EGYPT1DARKPIC, + EGYPT2DARKPIC, + EGYPT3DARKPIC, + + FIREWALL1PIC, + FIREWALL2PIC, + FIREWALL3PIC, + FIREWALL4PIC, + + NEMESISPIC, + + ALTARLEFTPIC, + ALTARRIGHTPIC, + + TEMPLEWALLDARKPIC, + + TORCHWALL1PIC, + TORCHWALL2PIC, + + BRNBRKDARKPIC, + BRNBRKEMDARKPIC, + + IRONGATEPIC, + + BRNFLGDARKPIC, + BRNFLGWINDOWDARKPIC, + BRNFLGVINEDARKPIC, + BRNFLGDMGDARKPIC, + + SPACEDMG1DARKPIC, + SPACEDMG2DARKPIC, + + SPACE1DARKPIC, + SPACE2DARKPIC, + SPACE3DARKPIC, + SPACE4DARKPIC, + + SPACE5DARKPIC, + SPACE6DARKPIC, + SPACE7DARKPIC, + SPACE8DARKPIC, + + 0,//SPACE9DARKPIC, + 0,//SPACEDMG9DARKPIC, + SPACE10DARKPIC, + RUSTDOORDARKPIC, + + SPACE11DARKPIC, + SPACE12DARKPIC, + SPACE13DARKPIC, + SPACE14DARKPIC, + + SPACEDMG5DARKPIC, + SPACEDMG6DARKPIC, + + TAP1PIC, + TAP2PIC, + ENDPIC, + 0,//SIRONDARKPIC, + + SPCDOOR1DARKPIC, + SPCDOOR2DARKPIC, + SPCDOOR3DARKPIC, + SPCDOOR4DARKPIC, + + COLUMNSDARKPIC, + + DEMONSTATUEDARKPIC, + + 0,//CRYSTALBWALL1DARKPIC, + + 0,//SRUSTDARKPIC, + + TROLLSTATUEDARKPIC, + + BRNDMGVINEDARKPIC, + TAP3PIC, + HORNDOORPIC, + RUNEDOORPIC, + + EXP_WALL_1PIC, + EXP_WALL_2PIC, + EXP_WALL_3PIC, + + WATER_EXP_WALL_1PIC, + WATER_EXP_WALL_2PIC, + WATER_EXP_WALL_3PIC, + + IRONDMGDARKPIC, + IRONDARKPIC, + 0, + TROLLBLOODYDARKPIC, + + TROLLDARKPIC, + + 0, // INVISIBLE WALL + + STONEDOORDARKPIC, + 0, + + IRONWTR1DARKPIC, + IRONWTR2DARKPIC, + IRONWTR3DARKPIC, + + RUSTWTR1DARKPIC, + RUSTWTR2DARKPIC, + RUSTWTR3DARKPIC, + + CEMETARYDARKPIC, + 0, + + WGRATE1DARKPIC, + WGRATE2DARKPIC, + WGRATE3DARKPIC, + + MAS_WIN_DARKPIC, + MAS_DOOR_DARKPIC, + MAS_VINE1_DARKPIC, + MAS_VINE2_DARKPIC, + + // Start of non-solid walls + 0, + 0, + 0, + 0, + 0, + 0, + + // solid walls + SGRATEPIC, +}; + + +/* +===================== += += DrawVWall += += Draws a wall by vertical segments, for texture mapping! += += wallptr->side is true for east/west walls (constant x) += += fracheight and fracstep are 16.16 bit fractions += +===================== +*/ + +void DrawVWall (walltype *wallptr) +{ + int x,i; + unsigned source; + unsigned width,sourceint; + unsigned wallpic,wallpicseg; + unsigned skip; + long fracheight,fracstep,longheightchange; + unsigned height; + int heightchange; + unsigned slope,distance; + int traceangle,angle; + int mapadd; + unsigned lastpix,lastsource,lastwidth; + + if (wallptr->rightclip < wallptr->leftclip) + Quit ("DrawVWall: Right < Left"); + +// +// setup for height calculation +// + wallptr->height1 >>= 1; + wallptr->height2 >>= 1; + wallptr->planecoord>>=10; // remove non significant bits + + width = wallptr->x2 - wallptr->x1; + if (width) + { + heightchange = wallptr->height2 - wallptr->height1; + asm mov ax,[heightchange] + asm mov WORD PTR [longheightchange+2],ax + asm mov WORD PTR [longheightchange],0 // avoid long shift by 16 + fracstep = longheightchange/width; + } + + fracheight = ((long)wallptr->height1<<16)+0x8000; + skip = wallptr->leftclip - wallptr->x1; + if (skip) + fracheight += fracstep*skip; + +// +// setup for texture mapping +// +// mapadd is 64*64 (to keep source positive) + the origin wall intercept +// distance has 6 unit bits, and 6 frac bits +// traceangle is the center view angle in FINEANGLES, moved to be in +// the +-90 degree range (to thew right of origin) +// + traceangle = fineviewangle; + // + // find wall picture to map from + // + if (wallptr->side) + { // east or west wall + + wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]]; + if (wallptr->planecoord < viewxpix) + { + distance = viewxpix-wallptr->planecoord; + traceangle -= FINEANGLES/2; + mapadd = (64-viewypix&63); // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewxpix; + // traceangle is correct + mapadd = viewypix&63; // the pixel spot of the origin + } + } + else + { // north or south wall + + wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]]; + if (wallptr->planecoord < viewypix) + { + distance = viewypix-wallptr->planecoord; + traceangle -= FINEANGLES/4; + mapadd = viewxpix&63; // the pixel spot of the origin + } + else + { + distance = wallptr->planecoord-viewypix; + traceangle -= FINEANGLES*3/4; + mapadd = (64-viewxpix&63); // the pixel spot of the origin + } + } + + mapadd = 64*64-mapadd; // make sure it stays positive + + wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC]; + if (traceangle > FINEANGLES/2) + traceangle -= FINEANGLES; + +// +// calculate everything +// +// IMPORTANT! This loop is executed around 5000 times / second! +// + lastpix = lastsource = (unsigned)-1; + + for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++) + { + // + // height + // + asm mov ax,WORD PTR [fracheight] + asm mov dx,WORD PTR [fracheight+2] + asm mov cx,dx + asm add ax,WORD PTR [fracstep] + asm adc dx,WORD PTR [fracstep+2] + asm mov WORD PTR [fracheight],ax + asm mov WORD PTR [fracheight+2],dx + asm mov bx,[x] + asm shl bx,1 + asm cmp cx,MAXSCALEHEIGHT + asm jbe storeheight + asm mov cx,MAXSCALEHEIGHT +storeheight: + asm mov WORD PTR [wallheight+bx],cx + asm mov WORD PTR [zbuffer+bx],cx + +// height = fracheight>>16; +// fracheight += fracstep; +// if (height > MAXSCALEHEIGHT) +// height = MAXSCALEHEIGHT; +// wallheight[x] = zbuffer[x] = height; + + // + // texture map + // + angle = pixelangle[x]+traceangle; + if (angle<0) + angle+=FINEANGLES; + + slope = finetangent[angle]; + +// +// distance is an unsigned 6.6 bit number (12 pixel bits) +// slope is a signed 5.10 bit number +// result is a signed 11.16 bit number +// + +#if 0 + source = distance*slope; + source >>=20; + + source += mapadd; + source &= 63; // mask off the unused units + source = 63-source; + source <<= 6; // multiply by 64 for offset into pic +#endif + asm mov ax,[distance] + asm imul [slope] // ax is the source pixel + asm mov al,ah + asm shr al,1 + asm shr al,1 // low 6 bits is now pixel number + asm add ax,[mapadd] + asm and ax,63 + asm mov dx,63 + asm sub dx,ax // otherwise it is backwards + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 + asm shl dx,1 // *64 to index into shape + asm mov [source],dx + + if (source != lastsource) + { + if (lastpix != (unsigned)-1) + { + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; + } + lastpix = x; + lastsource = source; + lastwidth = 1; + } + else + lastwidth++; // optimized draw, same map as last one + } + wallofs[lastpix] = lastsource; + wallseg[lastpix] = wallpicseg; + wallwidth[lastpix] = lastwidth; +} + + +//========================================================================== + + +/* +================= += += TraceRay += += Used to find the left and rightmost tile in the view area to be traced from += Follows a ray of the given angle from viewx,viewy in the global map until += it hits a solid tile += sets: += tile.x,tile.y : tile coordinates of contacted tile += tilecolor : solid tile's color += +================== +*/ + +int tilecolor; + +void TraceRay (unsigned angle) +{ + long tracex,tracey,tracexstep,traceystep,searchx,searchy; + fixed fixtemp; + int otx,oty,searchsteps; + + tracexstep = costable[angle]; + traceystep = sintable[angle]; + +// +// advance point so it is even with the view plane before we start checking +// + fixtemp = FixedByFrac(prestep,tracexstep); + tracex = viewx+fixtemp; + fixtemp = FixedByFrac(prestep,traceystep); + tracey = viewy-fixtemp; + + tile.x = tracex>>TILESHIFT; // starting point in tiles + tile.y = tracey>>TILESHIFT; + + + if (tracexstep<0) // use 2's complement, not signed magnitude + tracexstep = -(tracexstep&0x7fffffff); + + if (traceystep<0) // use 2's complement, not signed magnitude + traceystep = -(traceystep&0x7fffffff); + +// +// we assume viewx,viewy is not inside a solid tile, so go ahead one step +// + + do // until a solid tile is hit + { + otx = tile.x; + oty = tile.y; + spotvis[otx][oty] = true; + tracex += tracexstep; + tracey -= traceystep; + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) ) + { + // + // trace crossed two solid tiles, so do a binary search along the line + // to find a spot where only one tile edge is crossed + // + searchsteps = 0; + searchx = tracexstep; + searchy = traceystep; + do + { + searchx/=2; + searchy/=2; + if (tile.x!=otx && tile.y!=oty) + { + // still too far + tracex -= searchx; + tracey += searchy; + } + else + { + // not far enough, no tiles crossed + tracex += searchx; + tracey -= searchy; + } + + // + // if it is REAL close, go for the most clockwise intersection + // + if (++searchsteps == 16) + { + tracex = (long)otx<0) + { + if (traceystep<0) + { + tracex += TILEGLOBAL-1; + tracey += TILEGLOBAL; + } + else + { + tracex += TILEGLOBAL; + } + } + else + { + if (traceystep<0) + { + tracex --; + tracey += TILEGLOBAL-1; + } + else + { + tracey --; + } + } + } + + tile.x = tracex>>TILESHIFT; + tile.y = tracey>>TILESHIFT; + + } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) ); + } + } while (!(tilecolor = tilemap[tile.x][tile.y]) ); + +} + +//========================================================================== + + +/* +======================== += += FixedByFrac += += multiply a 16/16 bit, 2's complement fixed point number by a 16 bit += fraction, passed as a signed magnitude 32 bit number += +======================== +*/ + +#pragma warn -rvl // I stick the return value in with ASMs + +fixed FixedByFrac (fixed a, fixed b) +{ + fixed value; + +// +// setup +// +asm mov si,[WORD PTR b+2] // sign of result = sign of fraction + +asm mov ax,[WORD PTR a] +asm mov cx,[WORD PTR a+2] + +asm or cx,cx +asm jns aok: // negative? +asm not ax +asm not cx +asm add ax,1 +asm adc cx,0 +asm xor si,0x8000 // toggle sign of result +aok: + +// +// multiply cx:ax by bx +// +asm mov bx,[WORD PTR b] +asm mul bx // fraction*fraction +asm mov di,dx // di is low word of result +asm mov ax,cx // +asm mul bx // units*fraction +asm add ax,di +asm adc dx,0 + +// +// put result dx:ax in 2's complement +// +asm test si,0x8000 // is the result negative? +asm jz ansok: +asm not ax +asm not dx +asm add ax,1 +asm adc dx,0 + +ansok:; + +} + +#pragma warn +rvl + +#if 0 +/* +========================= += += FixedAdd += += add two 16 bit fixed point numbers += to subtract, invert the sign of B before invoking += +========================= +*/ + +fixed FixedAdd (fixed a, fixed b) +{ + fixed value; + +asm mov ax,[WORD PTR a] +asm mov dx,[WORD PTR a+2] + +asm mov bx,[WORD PTR b] +asm mov cx,[WORD PTR b+2] + +asm or dx,dx +asm jns aok: // negative? +asm and dx,0x7fff +asm not ax // convert a from signed magnitude to 2's compl +asm not dx +asm add ax,1 +asm adc dx,0 +aok: + +asm or cx,cx +asm jns bok: // negative? +asm and cx,0x7fff +asm not bx // convert b from signed magnitude to 2's compl +asm not cx +asm add bx,1 +asm adc cx,0 +bok: + +asm add ax,bx // perform the addition +asm adc dx,cx +asm jns done + +asm and dx,0x7fff // value was negative +asm not ax // back to signed magnitude +asm not dx +asm add ax,1 +asm adc dx,0 + +done: + +asm mov [WORD PTR value],ax +asm mov [WORD PTR value+2],dx + + return value; +} +#endif + +//========================================================================== + + +/* +======================== += += TransformPoint += += Takes paramaters: += gx,gy : globalx/globaly of point += += globals: += viewx,viewy : point of view += viewcos,viewsin : sin/cos of viewangle += += += defines: += CENTERX : pixel location of center of view window += TILEGLOBAL : size of one += FOCALLENGTH : distance behind viewx/y for center of projection += scale : conversion from global value to screen value += += returns: += screenx,screenheight: projected edge location and size += +======================== +*/ + +void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + *screenx = CENTERX + ny/ratio; + + *screenheight = TILEGLOBAL/ratio; + +} + + +// +// transform actor +// +void TransformActor (objtype *ob) +{ + int ratio; + fixed gx,gy,gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = ob->x-viewx; + gy = ob->y-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt-ob->size; + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + if (nx<0) + nx = 0; + + ratio = nx*scale/FOCALLENGTH; + + if (ratio<=MINRATIO) + ratio = MINRATIO; + + ob->viewx = CENTERX + ny/ratio; + + ob->viewheight = TILEGLOBAL/ratio; +} + +//========================================================================== + +fixed TransformX (fixed gx, fixed gy) +{ + int ratio; + fixed gxt,gyt,nx,ny; + +// +// translate point to view centered coordinates +// + gx = gx-viewx; + gy = gy-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + + return gxt-gyt; +} + +//========================================================================== + +/* +================== += += BuildTables += += Calculates: += += scale projection constant += sintable/costable overlapping fractional tables += firstangle/lastangle angles from focalpoint to left/right view edges += prestep distance from focal point before checking for tiles += +================== +*/ + +void BuildTables (void) +{ + int i; + long intang; + long x; + float angle,anglestep,radtoint; + double tang; + fixed value; + +// +// calculate the angle offset from view angle of each pixel's ray +// + radtoint = (float)FINEANGLES/2/PI; + for (i=0;iMAXINT) + intang = 0x8f00 | (intang & 0xff); + else if (intangangle),12); + while (pfoffset >= 640) + pfoffset -= 640; + LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8); + } + + if (bottomimage) + { +//// pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320; +// pfoffset += 320; +// while (pfoffset >= 640) +// pfoffset -= 640; +// LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8); + bottomimage -= 16; + LatchDrawPic(0,64,GND1PIC+bottomimage); + } +#endif + + +asm mov dx,GC_INDEX +asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2 +asm out dx,ax +asm mov al,GC_BITMASK +asm out dx,al + +} + +//========================================================================== + +/* +===================== += += DrawWallList += += Clips and draws all the walls traced this refresh += +===================== +*/ + +void DrawWallList (void) +{ + int i,leftx,newleft,rightclip; + walltype *wall, *check; + +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET wallwidth +asm xor ax,ax +asm mov cx,VIEWWIDTH/2 +asm rep stosw + + ClearScreen (); + + rightwall->x1 = VIEWXH+1; + rightwall->height1 = 32000; + (rightwall+1)->x1 = 32000; + + leftx = -1; + + for (wall=&walls[1];wall= wall->x2) + continue; + + rightclip = wall->x2; + + check = wall+1; + while (check->x1 <= rightclip && check->height1 >= wall->height2) + { + rightclip = check->x1-1; + check++; + } + + if (rightclip>VIEWXH) + rightclip=VIEWXH; + + if (leftx < wall->x1 - 1) + newleft = wall->x1-1; // there was black space between walls + else + newleft = leftx; + + if (rightclip > newleft) + { + wall->leftclip = newleft+1; + wall->rightclip = rightclip; + DrawVWall (wall); + leftx = rightclip; + } + } + +#ifndef DRAWEACH + ScaleWalls (); // draw all the walls +#endif +} + +//========================================================================== + +/* +===================== += += DrawScaleds += += Draws all objects that are visable += +===================== +*/ + +objtype *depthsort[MAXACTORS]; + +void DrawScaleds (void) +{ +#if USE_INERT_LIST + extern inertobjtype inertobjlist[], *inert; + + boolean inertlist=false; +#endif + int i,j,least,numvisable,height; + objtype *obj,**vislist,*farthest; + memptr shape; + byte *tilespot,*visspot; + + numvisable = 0; + +// +// calculate base positions of all objects +// + vislist = &depthsort[0]; + + obj = player->next; + while (obj) + { + tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley; + visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley; + // + // could be in any of the nine surrounding tiles + // + if (*visspot + || ( *(visspot-1) && !*(tilespot-1) ) + || ( *(visspot+1) && !*(tilespot+1) ) + || ( *(visspot-65) && !*(tilespot-65) ) + || ( *(visspot-64) && !*(tilespot-64) ) + || ( *(visspot-63) && !*(tilespot-63) ) + || ( *(visspot+65) && !*(tilespot+65) ) + || ( *(visspot+64) && !*(tilespot+64) ) + || ( *(visspot+63) && !*(tilespot+63) ) ) + { +#if USE_INERT_LIST + if (!inertlist) +#endif + if ((obj->active == noalways) || (obj->active == always)) + obj->active = always; + else + obj->active = yes; + TransformActor (obj); + if (!obj->viewheight || obj->viewheight > VIEWWIDTH) + goto cont; // too close or far away + + if (!obj->state->shapenum) + goto cont; + + *vislist++ = obj; + numvisable++; + } + else +#if USE_INERT_LIST + if (!inertlist) +#endif + if ((obj->active != always) && (obj->active != noalways)) + obj->active = no; + +cont:; + obj = obj->next; +#if USE_INERT_LIST + if ((!obj) && (!inertlist)) + { + if (inert != inertobjlist) + obj = (objtype *)inertobjlist; + inertlist = true; + } +#endif + } + + if (vislist == &depthsort[0]) + return; // no visable objects + +// +// draw from back to front +// + for (i = 0; iviewheight; + if (height < least) + { + least = height; + farthest = depthsort[j]; + } + } + // + // draw farthest + // + shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC]; + ScaleShape(farthest->viewx,shape,farthest->viewheight); + farthest->viewheight = 32000; + } +} + +//========================================================================== + + +/* +===================== += += CalcTics += +===================== +*/ + +void CalcTics (void) +{ + long newtime,oldtimecount; + + +#ifdef PROFILE + tics = 1; + return; +#endif + +// +// calculate tics since last refresh for adaptive timing +// + if (lasttimecount > TimeCount) + TimeCount = lasttimecount; // if the game was paused a LONG time + +#if 0 + if (DemoMode) // demo recording and playback needs + { // to be constant +// +// take DEMOTICS or more tics, and modify Timecount to reflect time taken +// + oldtimecount = lasttimecount; + while (TimeCountMAXTICS) + { + TimeCount -= (tics-MAXTICS); + tics = MAXTICS; + } + + if (realtics>MAXREALTICS) + realtics = MAXREALTICS; + } +} + + +//========================================================================== + + +/* +======================== += += DrawHand += +======================== +*/ + +void DrawHand (void) +{ + #define HAND_X_POS ((VIEWWIDTH/16)-(10/2)) // "10" = hand width in bytes + + #define picnum HAND1PICM + + memptr source; + unsigned dest,width,height; + +// if (gamestate.shotpower || boltsleft) +// picnum += (((unsigned)TimeCount>>3)&1); + + source = grsegs[picnum]; + dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs; // 12 + width = picmtable[picnum-STARTPICM].width; + height = picmtable[picnum-STARTPICM].height; + + VW_MaskBlock(source,0,dest,width,handheight,width*height); + EGAMAPMASK(15); +} + +//========================================================================== + + +/* +======================== += += ThreeDRefresh += +======================== +*/ + +void ThreeDRefresh (void) +{ + int tracedir; + +restart: + aborttrace = false; + +// +// clear out the traced array +// +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET spotvis +asm xor ax,ax +asm mov cx,[mapwidth] // mapheight*32 words +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm shl cx,1 +asm rep stosw + + +// +// set up variables for this view +// + + viewangle = player->angle; + fineviewangle = viewangle*(FINEANGLES/ANGLES); + viewsin = sintable[viewangle]; + viewcos = costable[viewangle]; + viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos); + viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin); + viewx &= 0xfffffc00; // stop on a pixel boundary + viewy &= 0xfffffc00; + viewx += 0x180; + viewy += 0x180; + viewxpix = viewx>>10; + viewypix = viewy>>10; + + focal.x = viewx>>TILESHIFT; + focal.y = viewy>>TILESHIFT; + +// +// find the rightmost visable tile in view +// + tracedir = viewangle + lastangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + right.x = tile.x; + right.y = tile.y; + +// +// find the leftmost visable tile in view +// + tracedir = viewangle + firstangle; + if (tracedir<0) + tracedir+=ANGLES; + else if (tracedir>=ANGLES) + tracedir-=ANGLES; + TraceRay( tracedir ); + +// +// follow the walls from there to the right +// + rightwall = &walls[1]; + FollowWalls (); + + if (aborttrace) + goto restart; + +// +// actually draw stuff +// + if (++screenpage == 3) + screenpage = 0; + + bufferofs = screenloc[screenpage]; + + EGAWRITEMODE(2); + EGAMAPMASK(15); + +// +// draw the wall list saved be FollowWalls () +// +// animframe = (TimeCount&8)>>3; + +// +// draw all the scaled images +// + asm mov dx,GC_INDEX + + asm mov ax,GC_COLORDONTCARE + asm out dx,ax // don't look at any of the planes + + asm mov ax,GC_MODE + 256*(10) // read mode 1, write mode 2 + asm out dx,ax + + asm mov al,GC_BITMASK + asm out dx,al + + AnimateWallList(); + DrawWallList(); + DrawScaleds(); + + EGAWRITEMODE(0); + EGABITMASK(0xff); + +// +// draw hand +// + if (handheight) + DrawHand (); + +// +// show screen and time last cycle +// + if (fizzlein) + { + fizzlein = false; + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true); + lasttimecount = TimeCount; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + +asm cli +asm mov cx,[bufferofs] +asm mov dx,3d4h // CRTC address register +asm mov al,0ch // start address high register +asm out dx,al +asm inc dx +asm mov al,ch +asm out dx,al // set the high byte +asm dec dx +asm mov al,0dh // start address low register +asm out dx,al +asm inc dx +asm mov al,cl +asm out dx,al // set the low byte +asm sti + + displayofs = bufferofs; + + CalcTics (); + +} + diff --git a/src/lib/hb/c6_game.c b/src/lib/hb/c6_game.c new file mode 100755 index 00000000..d31a0470 --- /dev/null +++ b/src/lib/hb/c6_game.c @@ -0,0 +1,1599 @@ +/* Catacomb Apocalypse 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_GAME.C + +#include + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +#ifdef PROFILE +#include "TIME.H" +#endif + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define NUMLUMPS 45 + +#define EYESTALKLUMP 0 +#define BLOBLUMP 1 +#define BOLTLUMP 2 +#define NUKELUMP 3 +#define POTIONLUMP 4 +#define RKEYLUMP 5 +#define YKEYLUMP 6 +#define GKEYLUMP 7 +#define BKEYLUMP 8 +#define RGEMLUMP 9 +#define GGEMLUMP 10 +#define BGEMLUMP 11 +#define YGEMLUMP 12 +#define PGEMLUMP 13 +#define CHESTLUMP 14 +#define PLAYERLUMP 15 +#define FTIMELUMP 16 +#define PORTALLUMP 17 +#define COLUMN1LUMP 18 +#define FIREPOTLUMP 19 +#define COLUMN2LUMP 20 +#define EYELUMP 21 +#define FUTUREMAGELUMP 22 +#define FORCEFIELDLUMP 23 +#define ROBOTANKLUMP 24 +#define RAMBONELUMP 25 +#define STOMPYLUMP 26 +#define TROLLLUMP 27 +#define WIZARDLUMP 28 +#define HEADLUMP 29 +#define INVISDUDELUMP 30 +#define BUGLUMP 31 +#define CYBORGLUMP 32 +#define WATERCHESTLUMP 33 +#define GRELLUMP 34 +#define RAYLUMP 35 +#define COLUMN3LUMP 36 +#define OLDCHESTLUMP 37 +#define OLDFIREPOTLUMP 38 +#define COLUMN4LUMP 39 +#define TOMB1LUMP 40 +#define TOMB2LUMP 41 +#define DEMONLUMP 42 +#define COLUMN5LUMP 43 + +int lumpstart[NUMLUMPS] = { +EYESTALK_LUMP_START, +BLOB_LUMP_START, +BOLT_LUMP_START, +NUKE_LUMP_START, +POTION_LUMP_START, +RKEY_LUMP_START, +YKEY_LUMP_START, +GKEY_LUMP_START, +BKEY_LUMP_START, +RGEM_LUMP_START, +GGEM_LUMP_START, +BGEM_LUMP_START, +YGEM_LUMP_START, +PGEM_LUMP_START, +CHEST_LUMP_START, +PLAYER_LUMP_START, +TIME_LUMP_START, +PORTAL_LUMP_START, +COLUMN1_LUMP_START, +FFIREPOT_LUMP_START, +COLUMN2_LUMP_START, +EYE_LUMP_START, +FUTUREMAGE_LUMP_START, +FORCEFIELD_LUMP_START, +ROBOTANK_LUMP_START, +RAMBONE_LUMP_START, +STOMPY_LUMP_START, +TROLL_LUMP_START, +WIZARD_LUMP_START, +HEAD_LUMP_START, +INVISDUDE_LUMP_START, +BUG_LUMP_START, +CYBORG_LUMP_START, +O_WATER_CHEST_LUMP_START, +GREL_LUMP_START, +RAY_LUMP_START, +COLUMN3_LUMP_START, +OLD_CHEST_LUMP_START, +OFIREPOT_LUMP_START, +COLUMN4_LUMP_START, +TOMB1_LUMP_START, +TOMB2_LUMP_START, +DEMON_LUMP_START, +COLUMN5_LUMP_START, +}; + + +int lumpend[NUMLUMPS] = { +EYESTALK_LUMP_END, +BLOB_LUMP_END, +BOLT_LUMP_END, +NUKE_LUMP_END, +POTION_LUMP_END, +RKEY_LUMP_END, +YKEY_LUMP_END, +GKEY_LUMP_END, +BKEY_LUMP_END, +RGEM_LUMP_END, +GGEM_LUMP_END, +BGEM_LUMP_END, +YGEM_LUMP_END, +PGEM_LUMP_END, +CHEST_LUMP_END, +PLAYER_LUMP_END, +TIME_LUMP_END, +PORTAL_LUMP_END, +COLUMN1_LUMP_END, +FFIREPOT_LUMP_END, +COLUMN2_LUMP_END, +EYE_LUMP_END, +FUTUREMAGE_LUMP_END, +FORCEFIELD_LUMP_END, +ROBOTANK_LUMP_END, +RAMBONE_LUMP_END, +STOMPY_LUMP_END, +TROLL_LUMP_END, +WIZARD_LUMP_END, +HEAD_LUMP_END, +INVISDUDE_LUMP_END, +BUG_LUMP_END, +CYBORG_LUMP_END, +O_WATER_CHEST_LUMP_END, +GREL_LUMP_END, +RAY_LUMP_END, +COLUMN3_LUMP_END, +OLD_CHEST_LUMP_END, +OFIREPOT_LUMP_END, +COLUMN4_LUMP_END, +TOMB1_LUMP_END, +TOMB2_LUMP_END, +DEMON_LUMP_END, +COLUMN5_LUMP_END, +}; + + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +unsigned latchpics[NUMLATCHPICS]; +unsigned tileoffsets[NUMTILE16]; +unsigned textstarts[27]; + +boolean splitscreen=false; +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean lumpneeded[NUMLUMPS]; + + +//=========================================================================== + + + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +void ScanInfoPlane (void) +{ + unsigned char hibyte; + unsigned x,y,i,j; + unsigned int tile; + unsigned far *start; + + InitObjList(); // start spawning things with a clean slate + + scolor = gcolor = 0; + skycolor = &scolor; + groundcolor = &gcolor; + + + memset (lumpneeded,0,sizeof(lumpneeded)); + + start = mapsegs[2]; + for (y=0;y> 8; + tile &= 0xff; + + switch (hibyte) + { + char hi; + + case 0xFB: + wall_anim_time = tile; + tile = 0; + break; + + case 0xfa: // sky/ground color + x++; + tile = *start++; + hi = tile >> 8; + tile &= 0xff; + switch (hibyte) + { + case 0xfa: // sky / ground color + scolor = ((hi)|(hi<<8)); + gcolor = ((tile)|(tile<<8)); + skycolor = &scolor; + groundcolor = &gcolor; + break; + + } + break; + } + + if ((!tile) || (hibyte)) + continue; + + switch (tile) + { + case 1: + case 2: + case 3: + case 4: + lumpneeded[PLAYERLUMP] = true; + SpawnPlayer(x,y,NORTH+tile-1); + break; + + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + lumpneeded[tile-5+BOLTLUMP] = true; + SpawnBonus(x,y,tile-5); + break; + + case 12: + lumpneeded[EYESTALKLUMP] = true; + SpawnAquaMan(x, y); + break; + + + case 13: + lumpneeded[BLOBLUMP] = true; + SpawnBlob(x, y); + break; + + + case 14: + lumpneeded[BUGLUMP] = true; + SpawnBug(x, y); + break; + + case 15: + lumpneeded[CYBORGLUMP] = true; + SpawnCyborgDemon(x, y); + break; + + case 16: + lumpneeded[EYELUMP] = true; + SpawnShooterEye(x, y); + break; + + case 17: + lumpneeded[FUTUREMAGELUMP] = true; + SpawnFutureMage(x, y); + break; + + case 18: + lumpneeded[INVISDUDELUMP] = true; + SpawnInvisDude(x, y); + break; + + case 19: + lumpneeded[ROBOTANKLUMP] = true; + SpawnRoboTank(x, y); + break; + + case 20: + lumpneeded[RAMBONELUMP] = true; + SpawnRamBone(x, y); + break; + + case 21: + lumpneeded[STOMPYLUMP] = true; + SpawnStompy(x, y); + break; + + case 22: + lumpneeded[TROLLLUMP] = true; + SpawnTroll(x, y); + break; + + case 23: + lumpneeded[WIZARDLUMP] = true; + SpawnWizard(x, y); + break; + + case 24: + SpawnBounce(x, y, 0); + break; + + case 25: + SpawnBounce(x, y, 1); + break; + + case 26: + lumpneeded[RKEYLUMP] = lumpneeded[GRELLUMP] = true; + SpawnGrelminar (x,y); + break; + + case 27: + lumpneeded[EYELUMP] = true; + SpawnRunningEye(x,y); + break; + + case 28: + lumpneeded[RAYLUMP] = true; + SpawnRay(x, y); + break; + + case 29: + lumpneeded[HEADLUMP] = true; + SpawnEgyptianHead(x, y); + break; + + case 30: + lumpneeded[DEMONLUMP] = true; + SpawnDemon(x, y); + break; + + case 31: + lumpneeded[COLUMN5LUMP] = true; + SpawnMiscObjects(x, y, 9); + break; + + case 32: + SpawnInvisWallCntroller(x, y); + break; + + case 33: + break; + + case 34: + break; + + case 35: + break; + + case 36: + lumpneeded[COLUMN1LUMP] = true; + SpawnMiscObjects(x, y, 1); + break; + + case 37: + lumpneeded[FIREPOTLUMP] = true; + SpawnMiscObjects(x, y, 4); + break; + + case 38: + lumpneeded[PORTALLUMP] = true; + SpawnWarp(x, y); + break; + + case 39: + lumpneeded[FTIMELUMP] = true; + SpawnFTime(x,y); + break; + + + case 40: + case 41: + case 42: + case 43: + case 44: + lumpneeded[tile-40+RGEMLUMP] = true; + SpawnBonus(x,y,tile-40+B_RGEM); + break; + + case 45: + lumpneeded[COLUMN2LUMP] = true; + SpawnMiscObjects(x, y, 2); + break; + + case 46: + lumpneeded[COLUMN3LUMP] = true; + SpawnMiscObjects(x, y, 3); + break; + + case 47: + lumpneeded[FORCEFIELDLUMP] = true; + SpawnForceField(x, y); + break; + + case 48: + lumpneeded[OLDCHESTLUMP] = true; + SpawnBonus(x, y, B_OLDCHEST); + break; + + case 49: // chest + if (gcolor == 0x0101) + lumpneeded[WATERCHESTLUMP] = true; + else + lumpneeded[CHESTLUMP] = true; + SpawnBonus(x,y,B_CHEST); + break; + + case 50: + lumpneeded[COLUMN4LUMP] = true; + SpawnMiscObjects(x, y, 5); + break; + + case 51: + lumpneeded[OLDFIREPOTLUMP] = true; + SpawnMiscObjects(x, y, 6); + break; + + case 52: + lumpneeded[TOMB1LUMP] = true; + SpawnMiscObjects(x, y, 7); + break; + + case 53: + lumpneeded[TOMB2LUMP] = true; + SpawnMiscObjects(x, y, 8); + break; + + case 54: + break; + + case 55: + break; + + case 56: + break; + + case 57: + break; + + case 58: + break; + + case 59: + break; + + case 60: + break; + + case 61: + break; + + case 62: + break; + + case 63: + break; + + case 64: + break; + + case 65: + break; + + case 66: + break; + + case 67: + break; + + case 68: + break; + + case 69: + break; + + case 70: + break; + + case 71: + break; + + } + } + +} + +//========================================================================== + +/* +================== += += ScanText += +================== +*/ + +void ScanText (void) +{ + int i; + char far *text; + + text = (char _seg *)grsegs[LEVEL1TEXT+mapon]; + + textstarts[0] = 0; + + for (i=1;i<=26;i++) + { + while (*text != '\n') + { + if (*text == '\r') + *text = 0; + text++; + } + text++; + textstarts[i] = FP_OFF(text); + } + +} + +//========================================================================== + +/* +================== += += DrawEnterScreen += +================== +*/ +#if 0 +static char *levelnames[] = + { + "Programmers Test Map", + "The Garden of Tears", + "The Den of Zombies", + "The Mausoleum Grounds", + "The Main Floor of the Mausoleum", + "Mike's Blastable Passage", + "The Crypt of Nemesis the Undead", + "The Subterranean Vault", + "The Ancient Aqueduct", + "The Orc Mines", + "The Lair of the Troll", + "The Demon's Inferno", + "The Battleground of the Titans", + "The Coven of Mages", + "The Inner Sanctum", + "The Haunt of Nemesis", + "The Passage to the Surface", + "Big Jim's Domain", + "Nolan", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + }; +#endif + +void DrawEnterScreen () +{ + int width; + + bufferofs = displayofs = screenloc[screenpage]; + VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0); + +// width = strlen(levelnames[gamestate.mapon]); + width = strlen("A new challenge awaits you."); + if (width < 20) + width = 20; + CenterWindow(width,3); + US_CPrint("\nA new challenge awaits you.\n"); +// US_CPrint(levelnames[gamestate.mapon]); +} + +//========================================================================== + +boolean tileneeded[NUMFLOORS]; + + +/* +================== += += CacheScaleds += +================== +*/ + +void CacheScaleds (void) +{ + int i,j; + unsigned source,dest; + + FreeUpMemory (); + CA_CacheGrChunk(LEVEL1TEXT+mapon); + ScanText (); + +// +// make sure we are displaying screenpage 0 +// + if (screenpage) + { + source = screenloc[screenpage]; + dest = screenloc[0]; + VW_ScreenToScreen (source,dest,40,VIEWHEIGHT); + screenpage = 0; + VW_SetScreen (dest,0); + displayofs = dest; + } + +// +// cache wall pictures +// + for (i=1;iwidth; + mapheight = mapheaderseg[mapon]->height; + +// +// make a lookup table for the maps left edge +// + spot = 0; + for (y=0;y>8) == EXP_WALL_CODE) + { + exploding_walls_present = true; + } + + if (tile0) + (unsigned)actorat[x][y] = tile; + } + spotptr++; + } + + + // + // Mark any gfx chunks needed + // + +// CA_MarkGrChunk(NORTHICONSPR); +// CA_CacheMarks(NULL); + + +// +// decide which graphics are needed and spawn actors +// + head_base_delay = 0; // (1*60) + random(1*60); + ScanInfoPlane (); + _fmemset(wall_anim_pos,0,sizeof(wall_anim_pos)); + + +// +// mark which exploding walls are needed ---- the check for floor color +// is preformed in ScanInfoPlane. +// + + if (exploding_walls_present) + { + extern unsigned gnd_colors[]; + + if (gcolor == 0x0101) + tileneeded[WATEREXP] = tileneeded[WATEREXP+1] = tileneeded[WATEREXP+2] = true; + else + tileneeded[WALLEXP] = tileneeded[WALLEXP+1] = tileneeded[WALLEXP+2] = true; + + } + + +// +// have the caching manager load and purge stuff to make sure all marks +// are in memory +// + CA_LoadAllSounds (); +} + +//========================================================================== + +/* +===================== += += LatchDrawPic += +===================== +*/ + +void LatchDrawPic (unsigned x, unsigned y, unsigned picnum) +{ + unsigned height, source, dest; + unsigned wide; + + wide = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + dest = bufferofs + ylookup[y]+x; + source = latchpics[picnum-FIRSTLATCHPIC]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,[wide] + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm mov di,[dest] +asm mov dx,[height] // scan lines to draw +asm mov ax,[wide] + +lineloop: +asm mov cx,ax +asm rep movsb +asm add di,bx + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + +#if USE_STRIPS + +//-------------------------------------------------------------------------- +// LatchDrawPicStrip() - srcoff is distance into source file (in PIXELS!) +//-------------------------------------------------------------------------- +void LatchDrawPicStrip (unsigned x, unsigned y, unsigned picnum, unsigned srcoff) +{ + unsigned wide, height, source, dest, shift, srcmod; + + shift = (srcoff & 7) >> 1; + srcoff >>= 3; + wide = pictable[picnum-STARTPICS].width; + srcmod = wide - linewidth + (shift != 3); + if (wide > linewidth) + wide = linewidth; + height = pictable[picnum-STARTPICS].height; + dest = bufferofs + ylookup[y]+x; + + picnum = ((picnum - (FIRSTSTRIPPIC+1)) >> 2) + (shift); + source = latchpics[(FIRSTSTRIPPIC-FIRSTLATCHPIC+1)+picnum]; + + EGAWRITEMODE(1); + EGAMAPMASK(15); + +asm mov bx,[linewidth] +asm sub bx,[wide] + +asm mov ax,[screenseg] +asm mov es,ax +asm mov ds,ax + +asm mov si,[source] +asm add si,[srcoff] +asm mov di,[dest] +asm mov dx,[height] // scan lines to draw +asm mov ax,[wide] + +lineloop: +asm mov cx,ax +asm rep movsb +asm add di,bx +asm add si,[srcmod] + +asm dec dx +asm jnz lineloop + +asm mov ax,ss +asm mov ds,ax // restore turbo's data segment + + EGAWRITEMODE(0); +} + +#endif + + +//========================================================================== + +/* +===================== += += Victory += +===================== +*/ + +void Victory (boolean playsounds) +{ + struct Shape shape; + + if (playsounds) + { + SD_PlaySound (GETBOLTSND); + SD_WaitSoundDone (); + SD_PlaySound (GETNUKESND); + SD_WaitSoundDone (); + SD_PlaySound (GETPOTIONSND); + SD_WaitSoundDone (); + SD_PlaySound (GETKEYSND); + SD_WaitSoundDone (); +// SD_PlaySound (GETSCROLLSND); +// SD_WaitSoundDone (); + SD_PlaySound (GETPOINTSSND); + } + + FreeUpMemory(); + + if (!screenfaded) + VW_FadeOut(); + + NormalScreen (); + + screenpage = bufferofs = 0; + + CA_CacheGrChunk (FINALEPIC); + UNMARKGRCHUNK(FINALEPIC); + VW_DrawPic(0, 0, FINALEPIC); + + VW_FadeIn(); + +} + +//========================================================================== + +#if 0 +/* +=================== += += Died += +=================== +*/ + +void Died (void) +{ + unsigned page1,page2; +// +// fizzle fade screen to grey +// + FreeUpMemory (); + SD_PlaySound (GAMEOVERSND); + bufferofs = screenloc[(screenpage+1)%3]; + DisplayMsg("Though fallen, your Spirit ...",NULL); +// LatchDrawPic(0,0,DEADPIC); +// FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false); + IN_ClearKeysDown(); + while (!Keyboard[sc_Enter]); +// IN_Ack(); + VW_SetScreen (bufferofs,0); + VW_ColorBorder(0); +} +#endif + +//========================================================================== + +/* +=================== += += NormalScreen += +=================== +*/ + +void NormalScreen (void) +{ + VW_SetSplitScreen (200); + bufferofs = displayofs = SCREEN1START; + VW_Bar(0,0,320,200,0); + bufferofs = SCREEN2START; + VW_Bar(0,0,320,200,0); + VW_SetScreen (displayofs,0); + splitscreen = false; +} + +//========================================================================== + +/* +=================== += += DrawPlayScreen += +=================== +*/ + +void DrawPlayScreen (void) +{ + int i,j,p,m; + + screenpage = 0; + + bufferofs = 0; + VW_Bar (0,0,320,STATUSLINES,0); + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar (0,0,320,VIEWHEIGHT,0); + } + + splitscreen = true; + VW_SetSplitScreen(120); + VW_SetScreen(screenloc[0],0); + + CA_CacheGrChunk (STATUSPIC); + + bufferofs = 0; + VW_DrawPic (0,0,STATUSPIC); + + grneeded[STATUSPIC] &= ~ca_levelbit; + MM_SetPurge(&grsegs[STATUSPIC],3); + +// RedrawStatusWindow (); + bufferofs = displayofs = screenloc[0]; +} + + +//========================================================================== + +/* +=================== += += LoadLatchMem += +=================== +*/ + +unsigned latchmemavail; + +void LoadLatchMem (void) +{ + static unsigned base_destoff=0; + static int base_numpics=0; + int i,j,p,m,numpics; + byte far *src, far *dest; + unsigned destoff; + + EGAWRITEMODE(0); + +// +// draw some pics into latch memory +// + + if (!base_numpics) + { + +// +// tile 8s +// + latchpics[0] = freelatch; + src = (byte _seg *)grsegs[STARTTILE8]; + dest = MK_FP(0xa000,freelatch); + + for (i=0;i>4)),destoff,planesize,1); + destoff+=planesize; + } + + // Copy unshifted strip to latch + // + latchpics[numpics++] = destoff; + planesize = pictable[pic].width * pictable[pic].height; + VW_MemToScreen (work,destoff,planesize,1); + destoff+=planesize; + + // Free work buffer + // + MM_FreePtr(&work); + } +#endif + +// Keep track of how much latch memory we have... +// + latchmemavail = 65535-destoff; + + EGAMAPMASK(15); +} + +//========================================================================== + +/* +=================== += += FizzleOut += +=================== +*/ + +void FizzleOut (int showlevel) +{ + unsigned page1,page2; +// +// fizzle fade screen to grey +// + bufferofs = screenloc[(screenpage+1)%3]; + if (showlevel) + DrawEnterScreen (); + FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false); +} + +//========================================================================== + +/* +==================== += += FreeUpMemory += +==================== +*/ + +void FreeUpMemory (void) +{ + int i; + + for (i=0;iname); + + // + // level + // + ultoa(s->completed,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (25 * 8) - 8 - w; + US_Print(buffer); + + // + // score + // + ultoa(s->score,buffer,10); + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (34 * 8) - 8 - w; + US_Print(buffer); + } + + fontcolor = F_BLACK; +} + + + +/* +======================= += += CheckHighScore += +======================= +*/ + +void CheckHighScore (long score,word other) +{ + word i,j; + int n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + n = i; + HighScoresDirty = true; + break; + } + } + + if (n != -1) + { + // + // got a high score + // + DrawHighScores (); + PrintY = 68 + (16 * n); + PrintX = 60; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100); + } +} + +#endif + + +//========================================================================== + +/* +=================== += += GameLoop += +=================== +*/ + +void GameLoop (void) +{ + boolean wait = false; + int i,xl,yl,xh,yh; + char num[20]; +#ifdef PROFILE + clock_t start,end; +#endif + + DrawPlayScreen (); + IN_ClearKeysDown(); + +restart: + if (!loadedgame) + { + gamestate.difficulty = restartgame; + restartgame = gd_Continue; + DrawEnterScreen (); + if (gamestate.mapon != 8) + fizzlein = true; + wait = true; + } + + do + { + playstate = gd_Continue; + if (!loadedgame) + SetupGameLevel (); + else + loadedgame = false; + + FreeUpMemory(); + LoadLatchMem(); + CacheScaleds (); + + if (EASYMODEON) + DisplaySMsg("*** NOVICE ***", NULL); + else + DisplaySMsg("*** WARRIOR ***", NULL); + + status_delay = 250; + + RedrawStatusWindow(); + if (wait) + { + VW_WaitVBL(120); + wait = false; + } + +#ifdef PROFILE +start = clock(); +while (start == clock()); +start++; +#endif + + PlayLoop (); + +#ifdef PROFILE +end = clock(); +itoa(end-start,str,10); + Quit (str); +#endif + + + switch (playstate) + { + case ex_abort: + FreeUpMemory (); + return; + case ex_resetgame: + NewGame(); + case ex_loadedgame: + case ex_warped: + FreeUpMemory(); + if (playstate != ex_resetgame) + DisplayMsg(" ", NULL); + DisplaySMsg(" ", NULL); + goto restart; + case ex_victorious: + screenpage = 0; + bufferofs = 0; + status_flag = 0; + return; + } + + } while (1); + +} + + +#if 0 +// +// make wall pictures purgable +// + for (i=0;i +#include + +#include "DEF.H" +#include "GELIB.H" +#pragma hdrstop +#include + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +typedef enum demo_screens {sc_logo,sc_title,sc_credits1,sc_credits2,sc_credits3,sc_credits4,sc_end} demo_screens; +struct Shape shape, + SdLogoShp, + TitleShp, + CreditBKShp, + Credit1Shp, + Credit2Shp, + Credit3Shp, + Credit4Shp, + Credit5Shp, + Credit6Shp, + Credit7Shp, + Credit8Shp, + Credit9Shp, + Credit10Shp; + + +PresenterInfo MainHelpText; + +GameDiff restartgame; +boolean loadedgame,abortgame,ingame; + + +memptr scalesegs[NUMPICS]; +char str[80],str2[20]; +unsigned tedlevelnum; +boolean tedlevel; +gametype gamestate; +exittype playstate; +char SlowMode = 0; +int starting_level; + +short NumGames=0; +unsigned Flags=0; + +boolean LoadShapes = true; +boolean EASYMODEON = false; + +void DisplayIntroText(void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +//=========================================================================== + +#if 0 +// JAB Hack begin +#define MyInterrupt 0x60 +void interrupt (*intaddr)(); +void interrupt (*oldintaddr)(); + char *JHParmStrings[] = {"no386",nil}; + +void +jabhack(void) +{ +extern void far jabhack2(void); +extern int far CheckIs386(void); + + int i; + + oldintaddr = getvect(MyInterrupt); + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],JHParmStrings) == 0) + return; + + if (CheckIs386()) + { + jabhack2(); + setvect(MyInterrupt,intaddr); + } +} + +void +jabunhack(void) +{ + setvect(MyInterrupt,oldintaddr); +} +// JAB Hack end +#endif + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +void NewGame (void) +{ + if (!loadedgame) + { + memset (&gamestate,0,sizeof(gamestate)); + gamestate.mapon = starting_level; + gamestate.body = MAXBODY; + } + + BGFLAGS = BGF_NOT_LIGHTNING; + Flags &= FL_CLEAR; + + boltsleft = bolttimer = 0; + +// memset (gamestate.levels,-1,sizeof(gamestate.levels)); +} + +//=========================================================================== + +#define RLETAG 0xABCD + +/* +================== += += SaveTheGame += +================== +*/ + +boolean SaveTheGame(int file) +{ + word i,compressed,expanded; + objtype *o; + memptr bigbuffer; + + // save the sky and ground colors + if (!CA_FarWrite(file,(void far *)&skycolor,sizeof(skycolor))) + return(false); + if (!CA_FarWrite(file,(void far *)&groundcolor,sizeof(groundcolor))) + return(false); + + if (!CA_FarWrite(file,(void far *)&FreezeTime,sizeof(FreezeTime))) + return(false); + + if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + if (!CA_FarWrite(file,(void far *)&EASYMODEON,sizeof(EASYMODEON))) + return(false); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Write planes 0 and 2 + { +// +// leave a word at start of compressed data for compressed length +// + compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i] + ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG); + + *(unsigned huge *)bigbuffer = compressed; + + if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + } + + for (o = player;o;o = o->next) + if (!CA_FarWrite(file,(void far *)o,sizeof(objtype))) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + MM_FreePtr (&bigbuffer); + + return(true); +} + +//=========================================================================== + + +/* +================== += += LoadTheGame += +================== +*/ + +boolean LoadTheGame(int file) +{ + unsigned i,x,y; + objtype *obj,*prev,*next,*followed; + unsigned compressed,expanded; + unsigned far *map,tile; + memptr bigbuffer; + + screenpage = 0; + FreeUpMemory(); + + playstate = ex_loadedgame; + // load the sky and ground colors + if (!CA_FarRead(file,(void far *)&skycolor,sizeof(skycolor))) + return(false); + if (!CA_FarRead(file,(void far *)&groundcolor,sizeof(groundcolor))) + return(false); + + if (!CA_FarRead(file,(void far *)&FreezeTime,sizeof(FreezeTime))) + return(false); + + if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + if (!CA_FarRead(file,(void far *)&EASYMODEON,sizeof(EASYMODEON))) + return(false); + + SetupGameLevel (); // load in and cache the base old level + + if (!FindFile(Filename,"SAVE GAME",-1)) + Quit("Error: Can't find saved game file!"); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i+=2) // Read planes 0 and 2 + { + if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + if (!CA_FarRead(file,(void far *)bigbuffer,compressed) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + CA_RLEWexpand ((unsigned huge *)bigbuffer, + (unsigned huge *)mapsegs[i],expanded,RLETAG); + } + + MM_FreePtr (&bigbuffer); +// +// copy the wall data to a data segment array again, to handle doors and +// bomb walls that are allready opened +// + memset (tilemap,0,sizeof(tilemap)); + memset (actorat,0,sizeof(actorat)); + map = mapsegs[0]; + for (y=0;y0) + (unsigned)actorat[x][y] = tile; + } + } + + + // Read the object list back in - assumes at least one object in list + + InitObjList (); + new = player; + while (true) + { + prev = new->prev; + next = new->next; + if (!CA_FarRead(file,(void far *)new,sizeof(objtype))) + return(false); + followed = new->next; + new->prev = prev; + new->next = next; + actorat[new->tilex][new->tiley] = new; // drop a new marker + + if (followed) + GetNewObj (false); + else + break; + } + + return(true); +} + +//=========================================================================== + +/* +================== += += ResetGame += +================== +*/ + +void ResetGame(void) +{ + NewGame (); + + ca_levelnum--; + ca_levelbit>>=1; + CA_ClearMarks(); + ca_levelbit<<=1; + ca_levelnum++; +} + +//=========================================================================== + + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); +#ifndef PROFILE + SD_Shutdown (); + IN_Shutdown (); +#endif + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + + +//=========================================================================== + +/* +========================== += += InitGame += += Load a few things right away += +========================== +*/ + +void InitGame (void) +{ + unsigned segstart,seglength; + int i,x,y; + unsigned *blockstart; + +// US_TextScreen(); + + MM_Startup (); + VW_Startup (); +#ifndef PROFILE + IN_Startup (); + SD_Startup (); +#endif + US_Startup (); + + CA_Startup (); + + US_Setup (); + + US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame); + + +// +// load in and lock down some basic chunks +// + + CA_ClearMarks (); + + CA_MarkGrChunk(STARTFONT); + CA_MarkGrChunk(STARTTILE8); + CA_MarkGrChunk(STARTTILE8M); + CA_MarkGrChunk(HAND1PICM); + + CA_MarkGrChunk(NORTHICONSPR); + CA_CacheMarks (NULL); + + MM_SetLock (&grsegs[STARTFONT],true); + MM_SetLock (&grsegs[STARTTILE8],true); + MM_SetLock (&grsegs[STARTTILE8M],true); + MM_SetLock (&grsegs[HAND1PICM],true); + + fontcolor = WHITE; + + +// +// build some tables +// + for (i=0;i= MINMEMORY) + return; + + CA_CacheGrChunk (OUTOFMEM); + finscreen = (unsigned)grsegs[OUTOFMEM]; + ShutdownId (); + movedata (finscreen,7,0xb800,0,4000); + gotoxy (1,24); + exit(1); +} +#endif + +//=========================================================================== + + +/* +========================== += += main += +========================== +*/ + +char *MainParmStrings[] = {"q","l","ver","nomemcheck","helptest",nil}; + +void main (void) +{ + short i; + + starting_level = 0; + + for (i = 1;i < _argc;i++) + { + switch (US_CheckParm(_argv[i],MainParmStrings)) + { + case 0: + Flags |= FL_QUICK; + break; + + case 1: + starting_level = atoi(_argv[i]+1); + if ((starting_level < 0) || (starting_level > LASTMAP-1)) + starting_level = 0; + break; + + case 2: + printf("%s\n", GAMENAME); + printf("Copyright 1992-93 Softdisk Publishing\n"); + printf("%s %s\n",VERSION,REVISION); + printf("\n"); + exit(0); + break; + + case 3: + Flags |= FL_NOMEMCHECK; + break; + + case 4: + Flags |= (FL_HELPTEST|FL_QUICK); + break; + + } + } + + if (stricmp(_argv[1], "^(a@&r`")) + Quit("You must type CATAPOC to run CATACOMB APOCALYPSE\n"); + + +#if 0 + MainHelpText.xl = 0; + MainHelpText.yl = 0; + MainHelpText.xh = 639; + MainHelpText.yh = 199; + MainHelpText.bgcolor = 7; + MainHelpText.ltcolor = 15; + MainHelpText.dkcolor = 8; +#endif + +// jabhack(); + + randomize(); + + InitGame (); +// CheckMemory (); + LoadLatchMem (); + +// if (!LoadTextFile("MAINHELP."EXT,&MainHelpText)) +// Quit("Can't load MAINHELP."EXT); + +#ifdef PROFILE + NewGame (); + GameLoop (); +#endif + + DemoLoop(); + Quit(NULL); +} + +//------------------------------------------------------------------------- +// Display640() +//------------------------------------------------------------------------- +void Display640(void) +{ +// Can you believe it takes all this just to change to 640 mode!!???! +// + VW_ScreenToScreen(0,FREESTART-STATUSLEN,40,80); + VW_SetLineWidth(80); + MoveScreen(0,0); + VW_Bar (0,0,640,200,0); + VW_SetScreenMode(EGA640GR); + VW_SetLineWidth(80); + BlackPalette(); +} + +//------------------------------------------------------------------------- +// Display320() +//------------------------------------------------------------------------- +void Display320(void) +{ +// Can you believe it takes all this just to change to 320 mode!!???! +// + VW_ColorBorder(0); + VW_FadeOut(); + VW_SetLineWidth(40); + MoveScreen(0,0); + VW_Bar (0,0,320,200,0); + VW_SetScreenMode(EGA320GR); + VW_SetLineWidth(40); + BlackPalette(); + VW_ScreenToScreen(FREESTART-STATUSLEN,0,40,80); +} + +void PrintHelp(void) +{ + char oldfontcolor = fontcolor; + PrintY = 1; + WindowX = 135; + WindowW = 640; + + VW_FadeOut(); + bufferofs = displayofs = screenloc[0]; + VW_Bar(0,0,320,200,0); + + Display640(); + + VW_Bar(0, 0, 640, 200, 7); + + fontcolor = (7 ^ 1); + US_Print ("\n\n SUMMARY OF GAME CONTROLS\n\n"); + + fontcolor = (7 ^ 4); + US_Print (" ACTION\n\n"); + + US_Print ("Arrow keys, joystick, or mouse\n"); + US_Print ("TAB or V while turning\n"); + US_Print ("ALT or Button 2 while turning\n"); + US_Print ("CTRL or Button 1\n"); + US_Print ("Z\n"); + US_Print ("X or Enter\n"); + US_Print ("F1\n"); + US_Print ("F2\n"); + US_Print ("F3\n"); + US_Print ("F4\n"); + US_Print ("F5\n"); + US_Print ("ESC\n\n"); + fontcolor = (7 ^ 0); +#ifndef CATALOG + US_Print (" (See complete Instructions for more info)\n"); +#endif + US_Print ("\n copyright (c) 1992-93 Softdisk Publishing\n"); + + fontcolor = (7 ^ 8); + PrintX = 400; + PrintY = 37; + WindowX = 400; + US_Print (" REACTION\n\n"); + US_Print ("Move and turn\n"); + US_Print ("Turn quickly (Quick Turn)\n"); + US_Print ("Move sideways\n"); + US_Print ("Shoot a Missile\n"); + US_Print ("Shoot a Zapper\n"); + US_Print ("Shoot an Xterminator\n"); + US_Print ("Help (this screen)\n"); + US_Print ("Sound control\n"); + US_Print ("Save game position\n"); + US_Print ("Restore a saved game\n"); + US_Print ("Joystick control\n"); + US_Print ("System options\n\n\n"); + + VW_UpdateScreen(); + VW_FadeIn(); + VW_ColorBorder(8 | 56); + IN_Ack(); + Display320(); + fontcolor = oldfontcolor; +} \ No newline at end of file diff --git a/src/lib/hb/c6_play.c b/src/lib/hb/c6_play.c new file mode 100755 index 00000000..995e6c78 --- /dev/null +++ b/src/lib/hb/c6_play.c @@ -0,0 +1,1429 @@ +/* Catacomb Apocalypse 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_PLAY.C + +#include "DEF.H" +#include "gelib.h" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define POINTTICS 6 +#define PAUSE 300 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +byte bcolor; +short skytimer=-1,skytimer_reset; +short groundtimer=-1,groundtimer_reset; + +unsigned scolor,gcolor; +unsigned *skycolor,*groundcolor,debug_sky,debug_gnd; + +unsigned nocolorchange=0xFFFF; +byte BGFLAGS, // global that holds all current flags + bgflag; // used by BG changer, this flag is set when done + + +unsigned sky_daytonight[]={0x0909,0x0101,0x0808,0x0000,0xFFFF}; +//unsigned gnd_daytonight[]={0x0202,0xFFFF}; + +unsigned sky_lightning[]={0x0101,0x0909,0x0f0f,0x0808,0x0000,0xFFFF}; + +unsigned sky_colors[NUMLEVELS]={0x0000,0x0000,0x0000,0x0000,0x0808, + 0x0404,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0606, + 0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000}; +unsigned gnd_colors[NUMLEVELS]={0x0202,0x0202,0x0606,0x0202,0x0707, + 0x0505,0x0808,0x0606,0x0101,0x0808, + 0x0606,0x0404,0x0808,0x0c0c,0x0e0e, + 0x0808,0x0808,0x0c0c,0x0000,0x0707, + 0x0808}; + + +ControlInfo control; +boolean running=false; //,slowturn; + +int bordertime; +objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist; + +#if USE_INERT_LIST +inertobjtype inertobjlist[MAXINERTOBJ],*inert; +#endif + +unsigned farmapylookup[MAPSIZE]; +byte *nearmapylookup[MAPSIZE]; + +boolean singlestep,godmode; +int extravbls; +status_flags status_flag; +int status_delay; + +// +// replacing refresh manager +// +unsigned mapwidth,mapheight,tics,realtics; +boolean compatability; +byte *updateptr; +unsigned mapwidthtable[64]; +unsigned uwidthtable[UPDATEHIGH]; +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +#define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2) +#define UPDATESPARESIZE (UPDATEWIDE*2+4) +#define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE) +byte update[UPDATESIZE]; + +int mousexmove,mouseymove; +int pointcount,pointsleft; + +short BeepTime = 0; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void CalcBounds (objtype *ob); +void DrawPlayScreen (void); +void PreFullDisplay(void); +void PostFullDisplay(boolean draw_view); + + +// +// near data map array (wall values only, get text number from far data) +// +byte tilemap[MAPSIZE][MAPSIZE]; +byte spotvis[MAPSIZE][MAPSIZE]; +objtype *actorat[MAPSIZE][MAPSIZE]; + +objtype dummyobj; + +int bordertime; +int objectcount; + +void StopMusic(void); +void StartMusic(void); + +void CalibrateJoystick(short joynum); + +//========================================================================== + +/////////////////////////////////////////////////////////////////////////// +// +// CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// + +#define MAXX 320 +#define MAXY 120 + +void CenterWindow(word w,word h) +{ + US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); +} + +//=========================================================================== + + +/* +===================== += += CheckKeys += +===================== +*/ + +void CheckKeys (void) +{ + extern boolean autofire; + + if (screenfaded) // don't do anything with a faded screen + return; + +#if 0 +// +// pause key wierdness can't be checked as a scan code +// + if (Paused) + { + CenterWindow (8,3); + US_PrintCentered ("PAUSED"); + VW_UpdateScreen (); +// SD_MusicOff(); + IN_Ack(); +// SD_MusicOn(); + Paused = false; + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + else + if (Keyboard[sc_Enter]) // P = pause with no screen disruptioon + { +// SD_MusicOff(); + DisplaySMsg("PAUSED",NULL); + IN_Ack(); +// SD_MusicOn(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } + else + if (Keyboard[sc_S]) + { + char *Text[] = {{"Slow Mode ON"},{"Slow Mode OFF"}}; + + SlowMode ^= 1; + extravbls = SlowMode << 3; + CenterWindow (8,3); + US_PrintCentered (Text[SlowMode]); + VW_UpdateScreen (); +// SD_MusicOff(); + IN_Ack(); +// SD_MusicOn(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + } +#endif + + +// F2 - SOUND OPTIONS +// + if (Keyboard[sc_F2]) + { + int height=7; + boolean ChoiceMade = false; + + if (AdLibPresent) + height++; + + VW_FixRefreshBuffer(); + CenterWindow(22,height); + US_Print( "\n 1 ) NO SOUND \n"); + US_Print( " 2 ) PC AUDIO \n"); + + if (AdLibPresent) + US_Print(" 3 ) ADLIB AUDIO\n"); + + US_Print( "\n ESC) EXIT "); + VW_UpdateScreen(); + + // Switch audio device ON/OFF & load sounds if there + // was a change in the device. + + do { + + if (Keyboard[1]) // ESC - Exit + ChoiceMade = true; + else + if (Keyboard[2]) // 1 - No Sound + { + SD_SetSoundMode(sdm_Off); + ChoiceMade = true; + } + else + if (Keyboard[3]) // 2 - PC Audio + { + SD_SetSoundMode(sdm_PC); +// if (oldsoundmode != sdm_PC) + CA_LoadAllSounds(); + ChoiceMade = true; + } + else + if ((Keyboard[4]) && AdLibPresent) // 3 - AdLib Audio + { + SD_SetSoundMode(sdm_AdLib); +// if (oldsoundmode != sdm_AdLib) + CA_LoadAllSounds(); + ChoiceMade = true; + } + + } while (!ChoiceMade); + tics = realtics = 1; + IN_ClearKeysDown(); + } + +// F5 - CALIBRATE JOYSTICK +// + if (Keyboard[sc_F5]) + { + CalibrateJoystick(0); + tics = realtics = 1; + IN_ClearKeysDown(); + } + +deadloop:; +// ESCAPE - quits game +// + if ((Keyboard[sc_Escape]) || (Flags & FL_DEAD)) + { + char ch; + + DisplaySMsg("Options", NULL); + status_flag = S_NONE; + + + if (Flags & FL_DEAD) + { + char choices[] = {sc_Escape,sc_R,sc_N,sc_Q,0}; + ch = DisplayMsg("Restore New Quit",choices); + DisplayMsg(" ", NULL); + } + else + { + char choices[] = {sc_Escape,sc_S,sc_R,sc_N,sc_Q,0}; + ch = DisplayMsg("Save Restore New Quit",choices); + } + DrawText(true); + + switch (ch) + { + case sc_S: + if (!(Flags & FL_DEAD)) + Keyboard[sc_F3] = true; + break; + + case sc_R: + Keyboard[sc_F4] = true; + break; + + case sc_N: + DisplaySMsg("Starting anew", NULL); + VW_WaitVBL(60); + playstate = ex_resetgame; + Flags &= ~FL_DEAD; + break; + + case sc_Q: + DisplaySMsg("FARE THEE WELL!", NULL); + VW_WaitVBL(120); + if (!Flags & FL_QUICK) + VW_FadeOut(); + NormalScreen(); + FreeUpMemory(); + Quit(NULL); + break; + } + tics = realtics = 1; + } + +// F1 - DISPLAY HELP +// + if (Keyboard[sc_F1]) + { + PrintHelp(); + +#ifdef TEXT_PRESENTER + + extern PresenterInfo MainHelpText; + + VW_FadeOut(); + + FreeUpMemory(); + if (!LoadPresenterScript("HELP.TXT",&MainHelpText)) + { + VW_FadeIn(); + CenterWindow(30,5); + US_CPrint("\nError loading HELP file.\n"); + US_CPrint("Press any key."); + IN_Ack(); + VW_FadeOut(); + } + else + { + VW_SetSplitScreen(200); + bufferofs = displayofs = screenloc[0]; + VW_Bar(0,0,320,200,0); + + Display640(); + Presenter(&MainHelpText); + Display320(); + } + FreePresenterScript(&MainHelpText); +#endif + VW_SetSplitScreen(120); + VW_SetScreen(screenloc[0],0); + screenpage = 0; + CacheScaleds(); + + bufferofs = 0; + RedrawStatusWindow(); + ThreeDRefresh(); + VW_FadeIn(); + Keyboard[sc_F1] = false; + tics = realtics = 1; + IN_ClearKeysDown(); + } + +// F3 - SAVE GAME +// + if ((Keyboard[sc_F3]) && (!(Flags & FL_DEAD))) + { + PreFullDisplay(); + GE_SaveGame(); + PostFullDisplay(true); + tics = realtics = 1; + IN_ClearKeysDown(); + } + +// F4 - LOAD GAME +// + if (Keyboard[sc_F4]) + { + PreFullDisplay(); + if (GE_LoadGame()) + { + loadedgame = true; + playstate = ex_loadedgame; + Flags &= ~FL_DEAD; + lasttext = -1; + PostFullDisplay(false); + } + else + if (playstate == ex_victorious) + { + PostFullDisplay(false); + Victory(false); + IN_Ack(); + Quit(NULL); + } + else + PostFullDisplay(true); + Keyboard[sc_F5] = false; + tics = realtics = 1; + IN_ClearKeysDown(); + } + + if (Flags & FL_DEAD) + goto deadloop; + +// +// F10-? debug keys +// + if (Keyboard[sc_BackSpace]) + { + DebugKeys(); + if (MousePresent) Mouse(MDelta); // Clear accumulated mouse movement + lasttimecount = TimeCount; + } +} + +//------------------------------------------------------------------------- +// PreFullDisplay() +//------------------------------------------------------------------------- +void PreFullDisplay() +{ + VW_FadeOut(); + VW_SetSplitScreen(200); + bufferofs = displayofs = screenloc[0]; + VW_Bar(0,0,320,200,0); +} + +//------------------------------------------------------------------------- +// PostFullDisplay() +//------------------------------------------------------------------------- +void PostFullDisplay(boolean draw_view) +{ + VW_SetSplitScreen(120); + bufferofs = 0; + RedrawStatusWindow(); + if (draw_view) + { + ThreeDRefresh(); + VW_FadeIn(); + } +} + + +//=========================================================================== + +/* +############################################################################# + + The objlist data structure + +############################################################################# + +objlist containt structures for every actor currently playing. The structure +is accessed as a linked list starting at *player, ending when ob->next == +NULL. GetNewObj inserts a new object at the end of the list, meaning that +if an actor spawn another actor, the new one WILL get to think and react the +same frame. RemoveObj unlinks the given object and returns it to the free +list, but does not damage the objects ->next pointer, so if the current object +removes itself, a linked list following loop can still safely get to the +next element. + + + +############################################################################# +*/ + + +/* +========================= += += InitObjList += += Call to clear out the entire object list, returning them all to the free += list. Allocates a special spot for the player. += +========================= +*/ + +void InitObjList (void) +{ + int i; + + for (i=0;iprev; + memset (new,0,sizeof(*new)); + + if (lastobj) + lastobj->next = new; + new->prev = lastobj; // new->next is allready NULL from memset + + new->active = false; + lastobj = new; + + objectcount++; +} + +//=========================================================================== + +/* +========================= += += RemoveObj += += Add the given object back into the free list, and unlink it from it's += neighbors += +========================= +*/ + +void RemoveObj (objtype *gone) +{ + objtype **spotat; + + if (gone == player) + Quit ("RemoveObj: Tried to remove the player!"); + +// +// fix the next object's back link +// + if (gone == lastobj) + lastobj = (objtype *)gone->prev; + else + gone->next->prev = gone->prev; + +// +// fix the previous object's forward link +// + gone->prev->next = gone->next; + +// +// add it back in to the free list +// + gone->prev = objfreelist; + objfreelist = gone; + + objectcount--; +} + +#if USE_INERT_LIST + +//-------------------------------------------------------------------------- +// MoveObjToInert() +//-------------------------------------------------------------------------- +void MoveObjToInert(objtype *obj) +{ + + if (inert == &inertobjlist[MAXINERTOBJ]) + return; + +// Transfer info needed by inert objtype +// + inert->x = obj->x; + inert->y = obj->y; + inert->size = obj->size; + inert->viewx = obj->viewx; + inert->tilex = obj->tilex; + inert->tiley = obj->tiley; + inert->state = obj->state; + inert->ticcount = obj->ticcount; + +// Setup links between inert objects +// + if (inert != inertobjlist) + (inert-1)->next = inert; + inert->next = NULL; + inert++; + +// Free 'real' object from list. +// + RemoveObj(obj); +} + +#endif + +//========================================================================== + +/* +=================== += += PollControls += +=================== +*/ + +void PollControls (void) +{ + unsigned buttons; + + IN_ReadControl(0,&control); + + if (MousePresent) + { + Mouse(MButtons); + buttons = _BX; + Mouse(MDelta); + mousexmove = _CX; + mouseymove = _DX; + + if (buttons&1) + control.button0 = 1; + if (buttons&2) + control.button1 = 1; + + } + + if (Keyboard[sc_V] || Keyboard[sc_Tab]) + running = true; + else + running = false; +} + +//========================================================================== + +#if 0 +/* +================= += += StopMusic += +================= +*/ + +void StopMusic(void) +{ + int i; + + SD_MusicOff(); + for (i = 0;i < LASTMUSIC;i++) + if (audiosegs[STARTMUSIC + i]) + { + MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3); + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false); + } +} + +//========================================================================== + + +/* +================= += += StartMusic += +================= +*/ + +// JAB - Cache & start the appropriate music for this level +void StartMusic(void) +{ + musicnames chunk; + + SD_MusicOff(); + chunk = TOOHOT_MUS; +// if ((chunk == -1) || (MusicMode != smm_AdLib)) +//DEBUG control panel return; + + MM_BombOnError (false); + CA_CacheAudioChunk(STARTMUSIC + chunk); + MM_BombOnError (true); + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]); + } +} +#endif + +//========================================================================== + + +/* +=================== += += PlayLoop += +=================== +*/ + +void PlayLoop (void) +{ + char shot_color[3] = {4,9,14}; + + int allgems[5]={GEM_DELAY_TIME, // used for Q & D comparison + GEM_DELAY_TIME, // for having all gems... + GEM_DELAY_TIME, // the "allgems" declaration MUST + GEM_DELAY_TIME, // match the "gems" declaration in + GEM_DELAY_TIME // the gametype structure! + }; + +// int originx=0; +// int i=100; + signed long dx,dy,radius,psin,pcos,newx,newy; + int give; + short objnum; + signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy; + short o_radius; + + void (*think)(); + + ingame = true; + playstate = TimeCount = 0; + gamestate.shotpower = handheight = 0; + pointcount = pointsleft = 0; + + status_flag = S_NONE; + +#if 0 + // setup sky/ground colors and effects (based on level) + // + switch (gamestate.mapon) + { + case 255: + if (!(BGFLAGS & BGF_NIGHT)) + { + InitBgChange(3*60,sky_daytonight,-1,NULL,BGF_NIGHT); + groundcolor = &gnd_colors[0]; + } + else + { + skycolor = &sky_colors[0]; + groundcolor = &gnd_colors[0]; + } + break; + + default: + skycolor = &sky_colors[gamestate.mapon]; + groundcolor = &gnd_colors[gamestate.mapon]; + skytimer = groundtimer = -1; + break; + } +#endif + + BGFLAGS |= BGF_NOT_LIGHTNING; + skytimer = groundtimer = -1; + + debug_gnd = *groundcolor; + debug_sky = *skycolor; + RedrawStatusWindow(); + ThreeDRefresh(); + if (screenfaded) + VW_FadeIn(); + +#ifndef PROFILE + fizzlein = true; // fizzle fade in the first refresh +#endif + TimeCount = lasttimecount = lastnuke = 0; + + PollControls (); // center mouse +// StartMusic (); + do + { +#ifndef PROFILE + PollControls(); +#else + control.xaxis = 1; + if (++TimeCount == 300) + return; +#endif + DisplayStatus(&status_flag); + + objnum=0; + for (obj = player;obj;obj = obj->next) + { + if ((obj->active >= yes) && (!(FreezeTime && (obj!=player)))) + { + if (obj->ticcount) + { + obj->ticcount-=realtics; + while ( obj->ticcount <= 0) + { + think = obj->state->think; + if (think) + { + statetype *oldstate=obj->state; + + think (obj); + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + if (obj->state != oldstate) + break; + } + + obj->state = obj->state->next; + if (!obj->state) + { + RemoveObj (obj); + goto nextactor; + } + if (!obj->state->tictime) + { + obj->ticcount = 0; + goto nextactor; + } + if (obj->state->tictime>0) + obj->ticcount += obj->state->tictime; + } + } + + think = obj->state->think; + if (think) + { + think (obj); + if (!obj->state) + RemoveObj (obj); + } +nextactor:; + } + + // keep a list of objects around the player for radar updates + // + if (obj == player) + { + px = player->x; + py = player->y; + psin = sintable[player->angle]; + pcos = costable[player->angle]; + xl = px-((long)RADAR_WIDTH< MAX_RADAR_BLIPS-2) + objnum = MAX_RADAR_BLIPS-2; + + ox = obj->x; + oy = obj->y; + + + if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh)) + { + norm_dx = (dx = px-ox)>>TILESHIFT; + norm_dy = (dy = oy-py)>>TILESHIFT; + + o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy)); + + if (o_radius < RADAR_RADIUS) + { + newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin); + newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos); + + RadarXY[objnum][0]=newx>>TILESHIFT; + RadarXY[objnum][1]=newy>>TILESHIFT; + + // Define color to use for this object... + // + + switch (obj->obclass) + { + // NO GEM NEEDED + // + // THE WIZARD! (YOU) + // + case playerobj: + RadarXY[objnum++][2]=15; + break; + + // WIZARD'S SHOTS + // + case bounceobj: + case pshotobj: + case bigpshotobj: + RadarXY[objnum++][2]=shot_color[screenpage]; + break; + + // RED GEM + // + // STOMPY (DK RED) + // + case invisdudeobj: + case stompyobj: + if (gamestate.gems[B_RGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=4; + break; + + // BLOB (LT RED) + // + case blobobj: + if (gamestate.gems[B_RGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=12; + break; + + // BLUE GEM + // + // ROBOTANK (LT BLUE) + // + case robotankobj: + case fmageobj: + if (gamestate.gems[B_BGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=9; + break; + +#if 1 + // BLUE DEMON (DK BLUE) + // + case demonobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=1; + break; +#endif + + // GREEN GEM + // + // WIZARD (LT GREEN) + // + case wizardobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=10; + break; + + // AQUA MAN (DK GREEN) + // + case aquamanobj: + if (gamestate.gems[B_GGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=2; + break; + + // YELLOW GEM + // + // EQYPTIAN HEAD (BROWN) + // + case headobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=6; + break; + + // RAMBONE (YELLOW) + // TROLL + case ramboneobj: + case trollobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=14; + break; + + // BUG (LIGHT GRAY) + case bugobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=7; + break; + + // RAY (DARK GRAY) + case rayobj: + if (gamestate.gems[B_YGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=8; + break; + + // PURPLE GEM + // + // MEC DEMON (PURPLE) + // + case cyborgdemonobj: + if (gamestate.gems[B_PGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=5; + break; + + // EYE (LT PURPLE) + // + case eyeobj: + case reyeobj: + if (gamestate.gems[B_PGEM-B_RGEM]) + if (obj->active == always) + RadarXY[objnum++][2]=13; + break; + + // ALL GEMS NEEDED + // + // NEMESIS + // + case grelmobj: + if (!memcmp(gamestate.gems,allgems,sizeof(gamestate.gems))) + if (obj->active == always) + RadarXY[objnum++][2]=15; + break; + } + } + } + } + RadarXY[objnum][2]=-1; // Signals end of RadarXY list... + +#if USE_INERT_LIST + if (inert != inertobjlist) + for (obj=(objtype *)inertobjlist;obj;obj=obj->next) + if (obj->ticcount) + { + obj->ticcount-=realtics; + while ( obj->ticcount <= 0) + { + obj->state = obj->state->next; + if (!obj->state) + Quit("Removable object in INERT list."); + + if (!obj->state->tictime) + { + obj->ticcount = 0; + goto nextactor; + } + + if (obj->state->tictime>0) + obj->ticcount += obj->state->tictime; + } + } +#endif + + if (bordertime) + { + bordertime -= realtics; + if (bordertime<=0) + { + bordertime = 0; + VW_ColorBorder(0); + } + } + +#if 1 +// random lightning? +// + if (BGFLAGS & (BGF_NOT_LIGHTNING)) + { + if ((scolor & 0xe0) && (!(random(20-realtics)))) + { + BGFLAGS &= ~BGF_NOT_LIGHTNING; + InitBgChange(1,sky_lightning,-1,NULL,BGF_NOT_LIGHTNING); + } + } + +// handle sky/ground color changes +// + if (skytimer != -1) + { + skytimer -= realtics; + if (skytimer < 0) + { + skycolor++; + if (*skycolor == 0xffff) + { + skytimer = -1; +// skycolor--; + skycolor = &scolor; + if (groundtimer == -1) + BGFLAGS |= bgflag; + } + else + skytimer = skytimer_reset; + } + } + + if (groundtimer != -1) + { + groundtimer -= realtics; + if (groundtimer < 0) + { + groundcolor++; + if (*groundcolor == 0xffff) + { + groundtimer = -1; +// groundcolor--; + groundcolor = &gcolor; + if (skytimer == -1) + BGFLAGS |= bgflag; + } + else + groundtimer = groundtimer_reset; + } + } +#endif + + +// +// Handle FreezeTime counter.. +// + if (FreezeTime) + { + if (FreezeTime<20*30) + if ((BeepTime+=realtics)>=60) + { + BeepTime -= 60; + SD_PlaySound(TICKSND); + } + + if ((FreezeTime-=realtics)<=0) + { + FreezeTime=0; + SD_PlaySound(TIMERETURNSND); + DisplaySMsg(NULL,NULL); + status_flag = S_NONE; + } + } + + +// refresh all +// + ThreeDRefresh (); + + if (Flags & FL_DEAD) + { + SD_PlaySound (GAMEOVERSND); + DisplaySMsg("DEAD",NULL); + DrawHealth(); + } + +// check for win +// + if (playstate == ex_victorious) + { + Victory(true); + IN_Ack(); + Quit(NULL); + } + + CheckKeys(); + + }while (!playstate); +// StopMusic (); + + ingame = false; + if (bordertime) + { + bordertime = 0; + VW_ColorBorder(0); + } + + if (abortgame) + abortgame = false; +} + +//-------------------------------------------------------------------------- +// IntSqrt() - by Master Programmer, George Leritte! +//-------------------------------------------------------------------------- +int IntSqrt(long va) +{ +asm mov AX, word ptr va +asm mov DX, word ptr va+2 +asm mov bx,dx // {bx = integer square root of dx:ax} +asm or bx,ax // {if dx:ax=0 then return} +asm jz isq01 +asm mov bx,dx +asm shl bx,1 +asm or bl,ah +asm or bl,al +asm dec bx +asm add bx,dx // { initial guess} +asm jg isq10 +asm inc bx // { don't return zero} +asm jg isq10 +asm mov bx,7fffh +isq01:; + goto exitrout; + +isq10:; +asm push ax +asm push dx +asm div bx +asm sub ax,bx +asm cmp ax,1 +asm jbe isq90 +asm cmp ax,-1 +asm jae isq90 +asm sar ax,1 +asm add bx,ax +asm pop dx +asm pop ax +asm jmp isq10 +isq90:; +asm pop dx +asm pop ax +exitrout:; +asm mov ax,bx +} + +//------------------------------------------------------------------------- +// InitBgChange() +//------------------------------------------------------------------------- +void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag) +{ + skytimer_reset = skytimer = stimer; + if (scolors) + skycolor = scolors; + + groundtimer_reset = groundtimer = gtimer; + if (gcolors) + groundcolor = gcolors; + + bgflag = flag; +} + +//////////////////////////////////////////////////////// +// +// DisplayStatus +// +// Stat_Flag - contains the type of status displayed +// -- also uses status_delay (global variable) will not +// change display until this variable is zero. +// -- heirarchy is determined by the series of if statements, +// to change it, rearrange th if statements. +// +//////////////////////////////////////////////////////// + +#define MESSAGEDELAY 25 +void DisplayStatus (status_flags *stat_flag) +{ + status_flags temp_status; + + + if (*stat_flag == S_TIMESTOP) + return; + + if (status_delay > 0) + { + status_delay -= realtics; + return; + } + else + status_delay = 0; + + // check for a change in status from previous call + + temp_status = S_VIEWING; //precaution + + if (Keyboard[sc_Control] || control.button0) + temp_status = S_MISSLE; + + if (Keyboard[sc_Z] && !Keyboard[sc_F10]) + temp_status = S_ZAPPER; + + if ((Keyboard[sc_X] && !Keyboard[sc_F10]) || Keyboard[sc_Enter]) + temp_status = S_XTER; + + if (control.x) + temp_status = S_TURN; + + if ((Keyboard[sc_V] || Keyboard[sc_Tab]) && control.x) + temp_status = S_QTURN; + + if (Keyboard[sc_Alt] && control.x) + temp_status = S_SIDESTEP; + + if (control.y < 0) + temp_status = S_ADVANCE; + + if (control.y > 0) + temp_status = S_RETREAT; + + if (Keyboard[sc_F5]) + temp_status = S_JOYSTICK; + + if (Keyboard[sc_F4]) + temp_status = S_RESTORING; + + if (Keyboard[sc_F3]) + temp_status = S_SAVING; + + if (Keyboard[sc_F2]) + temp_status = S_SND; + + if (Keyboard[sc_F1]) + temp_status = S_HELP; + + if (temp_status != *stat_flag) + { + *stat_flag = temp_status; + + + switch (*stat_flag) + { + case S_MISSLE: + DisplaySMsg("Magick Missile", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_ZAPPER: + if (gamestate.bolts) + { + DisplaySMsg("Zapper", NULL); + status_delay = MESSAGEDELAY+10; + } + break; + + case S_XTER: + if (gamestate.nukes) + { + DisplaySMsg("Xterminator", NULL); + status_delay = MESSAGEDELAY+5; + } + break; + + case S_TURN: + DisplaySMsg("Turning", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_QTURN: + DisplaySMsg("Quick Turning", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_SIDESTEP: + DisplaySMsg("Sidestepping", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_ADVANCE: + DisplaySMsg("Advancing", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_RETREAT: + DisplaySMsg("Retreating", NULL); + status_delay = MESSAGEDELAY; + break; + + case S_JOYSTICK: + DisplaySMsg("Adjusting Joystick", NULL); + break; + + case S_RESTORING: + DisplaySMsg("Restoring", NULL); + break; + + case S_SAVING: + DisplaySMsg("Saving", NULL); + break; + + case S_SND: + DisplaySMsg("Select Sound", NULL); + break; + + case S_HELP: + DisplaySMsg("Getting Help", NULL); + break; + + case S_VIEWING: + DisplaySMsg("Viewing", NULL); + break; + } + bufferofs = displayofs = screenloc[screenpage]; + + } +} diff --git a/src/lib/hb/c6_sca_a.asm b/src/lib/hb/c6_sca_a.asm new file mode 100755 index 00000000..74160c32 --- /dev/null +++ b/src/lib/hb/c6_sca_a.asm @@ -0,0 +1,153 @@ +; Catacomb Apocalypse 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. + +IDEAL +MODEL MEDIUM,C + +include "ID_ASM.EQU" + +;=========================================================================== +; +; SCALING GRAPHICS +; +;=========================================================================== + + + +MACRO MAKELAB NUM + +lab&NUM: + +ENDM + +MACRO MAKEREF NUM + +dw OFFSET lab&NUM + +ENDM + + +;========================================================================= + +MAXSCALES equ 256 + + DATASEG + +EXTRN screenseg:WORD +EXTRN linewidth:WORD + +LABEL endtable WORD +labcount = 0 +REPT MAXSCALES +MAKEREF %labcount +labcount = labcount + 1 +ENDM + + + CODESEG + +;================================================== +; +; void scaleline (int scale, unsigned picseg, unsigned maskseg, +; unsigned screen, unsigned width) +; +;================================================== + +PROC ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word +USES si,di +PUBLIC ScaleLine + +; +; modify doline procedure for proper width +; + mov bx,[pixels] + cmp bx,MAXSCALES + jbe @@scaleok + mov bx,MAXSCALES +@@scaleok: + shl bx,1 + mov bx,[endtable+bx] + push [cs:bx] ;save the code that will be modified over + mov [WORD cs:bx],0d18eh ;mov ss,cx + push [cs:bx+2] ;save the code that will be modified over + mov [WORD cs:bx+2],90c3h ;ret / nop + push bx + + mov dx,[linewidth] + + mov di,[WORD screen] + mov es,[screenseg] + + mov si,[WORD scaleptr] + mov ds,[WORD scaleptr+2] + + mov bx,[WORD picptr] + mov ax,[WORD picptr+2] ;will be moved into ss after call + + mov bp,bx + + cli + call doline + sti +; +; restore doline to regular state +; + pop bx ;address of modified code + pop [cs:bx+2] + pop [cs:bx] + + mov ax,ss + mov ds,ax + ret + +;================ +; +; doline +; +; Big unwound scaling routine +; +; ds:si = scale table +; ss:bx = pic data +; es:di = screen location +; +;================ + +doline: + + mov cx,ss + mov ss,ax ;can't call a routine with ss used... + +labcount = 0 + +REPT MAXSCALES + +MAKELAB %labcount +labcount = labcount + 1 + + lodsb ; get scaled pixel number + xlat [ss:bx] ; look it up in the picture + xchg [es:di],al ; load latches and write pixel to screen + add di,dx ; down to next line + +ENDM + + mov ss,cx + ret + +ENDP + +END \ No newline at end of file diff --git a/src/lib/hb/c6_scale.c b/src/lib/hb/c6_scale.c new file mode 100755 index 00000000..bb824acf --- /dev/null +++ b/src/lib/hb/c6_scale.c @@ -0,0 +1,697 @@ +/* Catacomb Apocalypse 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_SCALE.C + +#include "DEF.H" +#pragma hdrstop + +//const unsigned viewheight = 144; +const unsigned screenbwide = 40; +const byte BACKGROUNDPIX = 5; + +unsigned shapesize[NUMSCALEPICS]; +t_compscale _seg *scaledirectory[NUMSCALEPICS]; +t_compshape _seg *shapedirectory[NUMSCALEPICS]; +memptr walldirectory[NUMSCALEWALLS]; + +/* +=========================== += += DeplanePic += += Takes a raw bit map of width bytes by height and creates a scaleable shape += += Returns the length of the shape in bytes += += Fills in spotvis (a convenient 64*64 array) with the color values += +=========================== +*/ + +void DeplanePic (int picnum) +{ + byte far *plane0,far *plane1,far *plane2,far *plane3; + byte by0,by1,by2,by3; + unsigned x,y,b,color,shift,width,height; + byte *dest; + +// +// convert ega pixels to byte color values in a temp buffer +// + width = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + + if (width>8 || height!=64) + Quit ("DePlanePic: Bad size shape"); + + memset (spotvis,BACKGROUNDPIX,sizeof(spotvis)); + + plane0 = (byte _seg *)grsegs[picnum]; + plane1 = plane0 + width*height; + plane2 = plane1 + width*height; + plane3 = plane2 + width*height; + + for (y=0;ycode[0]; + toppix = (viewheight-height)/2; + fix = 0; + + for (src=0;src<=64;src++) + { + startpix = fix>>16; + fix += step; + endpix = fix>>16; + + work->start[src] = startpix; + if (endpix>startpix) + work->width[src] = endpix-startpix; + else + work->width[src] = 0; + +// +// mark the start of the code +// + work->codeofs[src] = FP_OFF(code); + +// +// compile some code if the source pixel generates any screen pixels +// + startpix+=toppix; + endpix+=toppix; + + if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64) + continue; + + // + // mov al,[si+src] + // + *code++ = 0x8a; + *code++ = 0x44; + *code++ = src; + + for (;startpix= VIEWHEIGHT) + break; // off the bottom of the view area + if (startpix < 0) + continue; // not into the view area + + // + // and [es:di+heightofs],al + // + *code++ = 0x26; + *code++ = 0x20; + *code++ = 0x85; + *((unsigned far *)code)++ = startpix*screenbwide; + } + + } + +// +// retf +// + *code++ = 0xcb; + + totalsize = FP_OFF(code); + MM_GetPtr (finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); + MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + + +/* +======================== += += BuildCompShape += += typedef struct += { += unsigned width; += unsigned codeofs[64]; += } t_compshape; += += Width is the number of compiled line draws in the shape. The shape += drawing code will assume that the midpoint of the shape is in the += middle of the width. += += The non background pixel data will start at codeofs[width], so codeofs += greater than width will be invalid. += += Each code offset will draw one vertical line of the shape, consisting += of 0 or more segments of scaled pixels. += += The scaled shapes use ss:0-4 as a scratch variable for the far call to += the compiled scaler, so zero it back out after the shape is scaled, or += a "null pointer assignment" will result upon termination. += += Setup for a call to a compiled shape += ----------------------------------- += ax toast += bx toast += cx toast += dx segment of compiled shape += si toast += di byte at top of view area to draw the line in += bp 0 += ss:2 and ds the segment of the compiled scaler to use += es screenseg += += Upon return, ds IS NOT SET to the data segment. Do: += mov ax,ss += mov ds,ax += += += GC_BITMASK set to the pixels to be drawn in the row of bytes under DI += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += += += Code generated for each segment += ------------------------------- += mov bx,[(segend+1)*2] += mov cx,[bx] += mov [BYTE PTR bx],0xc8 // far return += mov ax,[segstart*2] += mov [ss:0],ax // entry point into the compiled scaler += mov ds,dx // (mov ds,cs) the data is after the compiled code += mov si,ofs data += call [bp] // scale some pixels += mov ds,[bp+2] += mov [bx],cx // un patch return += += Code generated after all segments on a line += ------------------------------------------- += retf += +======================== +*/ + +unsigned BuildCompShape (t_compshape _seg **finalspot) +{ + t_compshape _seg *work; + byte far *code; + int firstline,lastline,x,y; + unsigned firstpix,lastpix,width; + unsigned totalsize,pixelofs; + unsigned buff; + + +// MM_GetPtr (&(memptr)work,20000); + EGAWRITEMODE(0); + EGAREADMAP(0); // use ega screen memory for temp buffer + EGAMAPMASK(1); + buff = screenloc[1]; + work = (t_compshape _seg *)(0xa000+(buff+15)/16); + +// +// find the width of the shape +// + firstline = -1; + x=0; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + firstline = x; + break; + } + if (++x == 64) + Quit ("BuildCompShape: No shape data!"); + } while (firstline == -1); + + lastline = -1; + x=63; + do + { + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + { + lastline = x; + break; + } + x--; + } while (lastline == -1); + + width = lastline-firstline+1; + + work->width = width; + code = (byte far *)&work->codeofs[width]; + +// +// copy all non background pixels to the work space +// + pixelofs = FP_OFF(code); + + for (x=firstline;x<=lastline;x++) + for (y=0;y<64;y++) + if (spotvis[y][x] != BACKGROUNDPIX) + *code++ = spotvis[y][x]; + +// +// start compiling the vertical lines +// + for (x=firstline;x<=lastline;x++) + { + work->codeofs[x-firstline] = FP_OFF(code); + + y=0; + do + { + // + // scan past black background pixels + // + while (spotvis[y][x] == BACKGROUNDPIX && y<64) + y++; + + if (y>63) // no more segments + break; + + firstpix = y+1; // +1 because width is before codeofs + + // + // scan past scalable pixels + // + while (spotvis[y][x] != BACKGROUNDPIX && y<64) + y++; + + if (y>63) + lastpix = 65; + else + lastpix = y+1; // actually one pixel past the last displayed + + // + // compile the scale call + // + *code++ = 0x8b; // mov bx,[lastpix*2] + *code++ = 0x1e; + *((unsigned far *)code)++ = lastpix*2; + + *code++ = 0x8b; // mov cx,[bx] + *code++ = 0x0f; + + *code++ = 0xc6; // move [BYTE bx],0xcb + *code++ = 0x07; + *code++ = 0xcb; + + *code++ = 0xa1; // mov ax,[firstpix*2] /************* + *((unsigned far *)code)++ = firstpix*2; + + *code++ = 0x36; // mov [ss:0],ax + *code++ = 0xa3; + *code++ = 0x00; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,dx (mov ds,cs) + *code++ = 0xda; + + *code++ = 0xbe; // mov si,OFFSET pixelofs-firstpixel + *((unsigned far *)code)++ = pixelofs-firstpix; + + *code++ = 0xff; // call [DWORD bp] + *code++ = 0x5e; + *code++ = 0x00; + + *code++ = 0x8e; // mov ds,[bp+2] + *code++ = 0x5e; + *code++ = 0x02; + + *code++ = 0x89; // mov [bx],cx + *code++ = 0x0f; + + pixelofs += (lastpix-firstpix); + } while (y<63); + + // + // retf + // + *code++ = 0xcb; + } + + +// +// copy the final shape to a properly sized buffer +// + totalsize = FP_OFF(code); + + if (totalsize >= (PAGELEN*2)) + Quit("BuildCompShape(): Shape is too complex!"); + + MM_GetPtr ((memptr *)finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); +// MM_FreePtr (&(memptr)work); + + return totalsize; +} + + + +/* +======================= += += ScaleShape += += Draws a compiled shape at [scale] pixels high += += Setup for call += -------------- += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += GC_INDEX pointing at GC_BITMASK += +======================= +*/ + +static long longtemp; + +void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale) +{ + #define MAX_OBJ_SCALE (MAXSCALE) + + + t_compscale _seg *comptable; + unsigned width,scalewidth; + int x,pixel,lastpixel,pixwidth,min; + unsigned far *codehandle, far *widthptr; + unsigned badcodeptr; + int rightclip; + + if (!compshape) + Quit ("ScaleShape: NULL compshape ptr!"); + + scale = (scale+1)/2; + if (!scale) + return; // too far away + if (scale>MAX_OBJ_SCALE) + scale = MAX_OBJ_SCALE; + comptable = scaledirectory[scale]; + + width = compshape->width; + scalewidth = comptable->start[width]; + + pixel = xcenter - scalewidth/2; + lastpixel = pixel+scalewidth-1; + if (pixel >= VIEWWIDTH || lastpixel < 0) + return; // totally off screen + +// +// scan backwards from the right edge until pixels are visable +// rightclip is the first NON VISABLE pixel +// + if (lastpixel>=VIEWWIDTH-1) + rightclip = VIEWWIDTH-1; + else + rightclip = lastpixel; + + if (zbuffer[rightclip]>scale) + { + if (pixel>0) + min = pixel; + else + min = 0; + do + { + if (--rightclip < min) + return; // totally covered or before 0 + if (zbuffer[rightclip]<=scale) + break; + } while (1); + } + rightclip++; + +// +// scan from the left until it is on screen, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + *(((unsigned *)&longtemp)+1) = (unsigned)compshape; // seg of shape + codehandle = &compshape->codeofs[0]; + badcodeptr = compshape->codeofs[width]; + widthptr = &comptable->width[0]; + asm mov ax,[comptable] + asm mov WORD PTR [2],ax // ds:0-4 is used as a far call pointer + // by the compiled shapes + pixwidth = *widthptr; // scaled width of this pixel + while (!pixwidth) + { + pixwidth = *++widthptr; // find the first visable pixel + codehandle++; + } + + if (pixel<0) + { + do + { + if (pixel+pixwidth>0) + { + pixwidth += pixel; + pixel = 0; + break; + } + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + pixel+=pixwidth; + } while (1); + } + +// +// scan until it is visable, leaving +// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly +// + do + { + if (zbuffer[pixel] <= scale) + break; // start drawing here + pixel++; + if (!--pixwidth) + { + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + } + } while (1); + + if (pixel+pixwidth>rightclip) + pixwidth = rightclip-pixel; +// +// draw lines +// + do // while (1) + { + // + // scale a vertical segment [pixwidth] pixels wide at [pixel] + // + (unsigned)longtemp = *codehandle; // offset of compiled code + if ((unsigned)longtemp == badcodeptr) + Quit ("ScaleShape: codehandle past end!"); + + asm mov bx,[pixel] + asm mov di,bx + asm shr di,1 + asm shr di,1 + asm shr di,1 // X in bytes + asm add di,[bufferofs] + asm and bx,7 + asm shl bx,1 + asm shl bx,1 + asm shl bx,1 + asm add bx,[pixwidth] // bx = pixel*8+pixwidth-1 + asm dec bx + asm push bx + asm mov al,BYTE PTR [bitmasks1+bx] + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + + asm mov es,[screenseg] + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + asm pop bx + asm mov al,BYTE PTR [bitmasks2+bx] + asm or al,al + asm jz nosecond + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm mov dx,GC_INDEX+1 + asm out dx,al // set bit mask register + asm push si + asm push di + asm push bp + asm xor bp,bp + asm mov dx,[WORD PTR longtemp+2] + asm mov ds,[2] + asm call ss:[DWORD PTR longtemp] // scale the line of pixels + asm mov ax,ss + asm mov ds,ax + asm pop bp + asm pop di + asm pop si + + + // + // advance to the next drawn line + // +nosecond:; + if ( (pixel+=pixwidth) == rightclip ) + { + asm mov WORD PTR [0],0 + asm mov WORD PTR [2],0 + return; // all done! + } + + do + { + pixwidth = *++widthptr; + codehandle++; + } while (!pixwidth); + + if (pixel+pixwidth > rightclip) + pixwidth = rightclip-pixel; + + } while (1); + +} + +// +// bit mask tables for drawing scaled strips up to eight pixels wide +// + +byte bitmasks1[8][8] = { +{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}, +{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f}, +{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f}, +{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f}, +{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf}, +{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7}, +{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3}, +{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} }; + +byte bitmasks2[8][8] = { +{0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0x80}, +{0,0,0,0,0,0,0x80,0xc0}, +{0,0,0,0,0,0x80,0xc0,0xe0}, +{0,0,0,0,0x80,0xc0,0xe0,0xf0}, +{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8}, +{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc}, +{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} }; + + + + + + diff --git a/src/lib/hb/c6_state.c b/src/lib/hb/c6_state.c new file mode 100755 index 00000000..e17b77f4 --- /dev/null +++ b/src/lib/hb/c6_state.c @@ -0,0 +1,736 @@ +/* Catacomb Apocalypse 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_STATE.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype opposite[9] = + {south,west,north,east,southwest,northwest,northeast,southeast,nodir}; + + + +//=========================================================================== + + +/* +=================== += += Internal_SpawnNewObj += +=================== +*/ +void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat) +{ + extern objtype dummyobj; + + GetNewObj(UseDummy); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + + new->tilex = x; + new->tiley = y; + new->x = ((long)x<y = ((long)y<dir = nodir; + new->active = noalways; + + if (new != &dummyobj && PutInActorat) + actorat[new->tilex][new->tiley] = new; +} + +void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy) +{ + GetNewObj(UseDummy); + new->size = size; + new->state = state; + new->ticcount = random (state->tictime)+1; + new->active = noalways; + + new->x = x; + new->y = y; + new->tilex = x>>TILESHIFT; + new->tiley = y>>TILESHIFT; + CalcBounds(new); + new->distance = 100; + new->dir = nodir; +} + + + + +/* +=================== += += CheckHandAttack += += If the object can move next to the player, it will return true += +=================== +*/ + +boolean CheckHandAttack (objtype *ob) +{ + long deltax,deltay,size; + + size = (long)ob->size + player->size + ob->speed*tics + SIZE_TEST; + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax > size || deltax < -size || deltay > size || deltay < -size) + return false; + + return true; +} + + +/* +=================== += += T_DoDamage += += Attacks the player if still nearby, then immediately changes to next state += +=================== +*/ + +void T_DoDamage (objtype *ob) +{ + int points; + + + if (CheckHandAttack(ob) && (!(ob->flags & of_damagedone))) + { + points = 0; + + switch (ob->obclass) + { + case aquamanobj: + points = 7; + break; + + case wizardobj: + points = 7; + break; + + case trollobj: + points = 10; + break; + + case invisdudeobj: + points = 10; + break; + + case demonobj: + case cyborgdemonobj: + points = 15; + break; + + } + points = EasyDoDamage(points); + TakeDamage (points); + ob->flags |= of_damagedone; + } +} + + +//========================================================================== + +/* +================================== += += Walk += +================================== +*/ + +boolean Walk (objtype *ob) +{ + switch (ob->dir) + { + case north: + if (actorat[ob->tilex][ob->tiley-1]) + return false; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case northeast: + if (actorat[ob->tilex+1][ob->tiley-1]) + return false; + ob->tilex++; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case east: + if (actorat[ob->tilex+1][ob->tiley]) + return false; + ob->tilex++; + ob->distance = TILEGLOBAL; + return true; + + case southeast: + if (actorat[ob->tilex+1][ob->tiley+1]) + return false; + ob->tilex++; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case south: + if (actorat[ob->tilex][ob->tiley+1]) + return false; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case southwest: + if (actorat[ob->tilex-1][ob->tiley+1]) + return false; + ob->tilex--; + ob->tiley++; + ob->distance = TILEGLOBAL; + return true; + + case west: + if (actorat[ob->tilex-1][ob->tiley]) + return false; + ob->tilex--; + ob->distance = TILEGLOBAL; + return true; + + case northwest: + if (actorat[ob->tilex-1][ob->tiley-1]) + return false; + ob->tilex--; + ob->tiley--; + ob->distance = TILEGLOBAL; + return true; + + case nodir: + return false; + } + + Quit ("Walk: Bad dir"); + return false; +} + + + +/* +================================== += += ChaseThink += have the current monster go after the player, += either diagonally or straight on += +================================== +*/ + +void ChaseThink (objtype *obj, boolean diagonal) +{ + int deltax,deltay,i; + dirtype d[3]; + dirtype tdir, olddir, turnaround; + + + olddir=obj->dir; + turnaround=opposite[olddir]; + + deltax=player->tilex - obj->tilex; + deltay=player->tiley - obj->tiley; + + d[1]=nodir; + d[2]=nodir; + + if (deltax>0) + d[1]= east; + if (deltax<0) + d[1]= west; + if (deltay>0) + d[2]=south; + if (deltay<0) + d[2]=north; + + if (abs(deltay)>abs(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 (diagonal) + { /*ramdiagonals try the best dir first*/ + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + } + else + { /*ramstraights try the second best dir first*/ + + if (d[2]!=nodir) + { + obj->dir=d[2]; + if (Walk(obj)) + return; + } + + if (d[1]!=nodir) + { + obj->dir=d[1]; + if (Walk(obj)) + return; + } + } + + // Kluge to make the running eye stay in place if blocked, ie, not divert + // from path + if (obj->obclass == reyeobj) + return; + + +/* there is no direct path to the player, so pick another direction */ + + obj->dir=olddir; + if (Walk(obj)) + return; + + if (US_RndT()>128) /*randomly determine direction of search*/ + { + for (tdir=north;tdir<=west;tdir++) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + if (tdir!=turnaround) + { + obj->dir=tdir; + if (Walk(obj)) + return; + } + } + } + + obj->dir=turnaround; + Walk(obj); /*last chance, don't worry about returned value*/ +} + + +/* +================= += += MoveObj += +================= +*/ + +void MoveObj (objtype *ob, long move) +{ + ob->distance -=move; + + switch (ob->dir) + { + case north: + ob->y -= move; + return; + case northeast: + ob->x += move; + ob->y -= move; + return; + case east: + ob->x += move; + return; + case southeast: + ob->x += move; + ob->y += move; + return; + case south: + ob->y += move; + return; + case southwest: + ob->x -= move; + ob->y += move; + return; + case west: + ob->x -= move; + return; + case northwest: + ob->x -= move; + ob->y -= move; + return; + + case nodir: + return; + } +} + + +/* +================= += += Chase += += returns true if hand attack range += +================= +*/ + +boolean Chase (objtype *ob, boolean diagonal) +{ + long move; + long deltax,deltay,size; + + ob->flags &= ~of_damagedone; + + move = ob->speed*tics; + size = (long)ob->size + player->size + move + SIZE_TEST; + + while (move) + { + deltax = ob->x - player->x; + deltay = ob->y - player->y; + + if (deltax <= size && deltax >= -size + && deltay <= size && deltay >= -size) + { + CalcBounds (ob); + return true; + } + + if (move < ob->distance) //ob->distance - distance before you move + { // over into next tile + MoveObj (ob,move); + break; + } + else + if (ob->obclass == reyeobj) // Kludge for the "running eye" + { + if (ob->temp1 < 2) + { + MoveObj(ob, ob->distance/2); + ob->temp1 = 0; + } + } + + actorat[ob->tilex][ob->tiley] = 0; // pick up marker from goal + if (ob->dir == nodir) + ob->dir = north; + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + ChaseThink (ob, diagonal); + if (!ob->distance) + break; // no possible move + actorat[ob->tilex][ob->tiley] = ob; // set down a new goal marker + } + CalcBounds (ob); + return false; +} + +//=========================================================================== + + +/* +=================== += += ShootActor += +=================== +*/ + +void ShootActor (objtype *ob, unsigned damage) +{ + + ob->hitpoints -= damage; + + if (ob->hitpoints<=0) + { + switch (ob->obclass) + { + + case headobj: + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L)); + break; + + case aquamanobj: + ob->state = &s_aqua_die1; + ob->temp1 = 10; + break; + + case wizardobj: + ob->state = &s_wizard_die1; + break; + + case trollobj: + ob->state = &s_trolldie1; + break; + + case blobobj: + ob->state = &s_blob_die1; + break; + + case rayobj: + ob->state = &s_ray_die1; + break; + + case ramboneobj: + ob->state = &s_skel_die1; + break; + + case fmageobj: + ob->state = &s_fmagedie1; + break; + + case robotankobj: + ob->state = &s_robotank_death1; + ob->temp1 = 10; + break; + + case stompyobj: + ob->state = &s_stompy_death1; + break; + + case bugobj: + ob->state = &s_bug_death1; + break; + + case demonobj: + ob->state = &s_demondie1; + break; + + case cyborgdemonobj: + ob->state = &s_cyborg_demondie1; + break; + + case invisdudeobj: + ob->state = &s_invis_death1; + break; + + case grelmobj: + ob->state = &s_greldie1; + break; + + case eyeobj: + ob->state = &s_eye_die1; + break; + + case reyeobj: + ob->state = &s_reye_die1; + break; + + case bounceobj: + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L)); + break; + + case rshotobj: + case eshotobj: + case wshotobj: + case hshotobj: + case bshotobj: + case rbshotobj: + case fmshotobj: + case rtshotobj: + case syshotobj: + case bgshotobj: + ob->state = &s_bonus_die; +#if USE_INERT_LIST + ob->obclass = solidobj; // don't add these objs to inert list +#endif + break; + + case bonusobj: + case freezeobj: + switch (ob->temp1) + { + case B_POTION: + case B_OLDCHEST: + case B_CHEST: + case B_NUKE: + case B_BOLT: + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L)); + bordertime = FLASHTICS<<2; + bcolor = 14; + VW_ColorBorder(14 | 56); + DisplaySMsg("Item destroyed", NULL); + status_flag = S_NONE; + status_delay = 80; + break; + } +#if USE_INERT_LIST + ob->obclass = solidobj; // don't add this obj to inert list +#endif + break; + } + + if (ob->obclass != solidobj && ob->obclass != realsolidobj) + { + ob->obclass = inertobj; + ob->flags &= ~of_shootable; + actorat[ob->tilex][ob->tiley] = NULL; +#if USE_INERT_LIST + MoveObjToInert(ob); +#endif + } + else + { + if (ob->flags & of_forcefield) + { + ob->state = &s_force_field_die; + ob->flags &= ~of_shootable; + } + } + } + else + { + switch (ob->obclass) + { + case wizardobj: + ob->state = &s_wizard_ouch; + break; + + case trollobj: + if (!random(5)) + ob->state = &s_trollouch; + else + return; + break; + + case blobobj: + ob->state = &s_blob_ouch; + break; + + case ramboneobj: + ob->state = &s_skel_ouch; + break; + + case fmageobj: + ob->state = &s_fmageouch; + break; + + case stompyobj: + ob->state = &s_stompy_ouch; + break; + + case bugobj: + ob->state = &s_bug_ouch; + break; + + case cyborgdemonobj: + if (!(random(8))) + ob->state = &s_cyborg_demonouch; + else + return; + break; + + case demonobj: + if (!(random(8))) + ob->state = &s_demonouch; + else + return; + break; + + case invisdudeobj: + ob->state = &s_invis_fizz1; + break; + + case grelmobj: + ob->state = &s_grelouch; + break; + + case eyeobj: + ob->state = &s_eye_ouch; + break; + + case reyeobj: + ob->state = &s_reye_ouch; + break; + } + } + + ob->ticcount = ob->state->tictime; +} + + diff --git a/src/lib/hb/c6_trace.c b/src/lib/hb/c6_trace.c new file mode 100755 index 00000000..82ace1c0 --- /dev/null +++ b/src/lib/hb/c6_trace.c @@ -0,0 +1,872 @@ +/* Catacomb Apocalypse 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_TRACE.C + +#include "DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +// +// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0 +// depending on if tile.x,tile.y,wallon is visable from focal point +// +#define TESTWALLVISABLE { \ + if (tile.yfocal.x) \ + voffset += 2; \ + wallvisable = visable[voffset][wallon]; } + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean aborttrace; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +unsigned wallvisable,voffset; + + +fixed edgex,edgey; + +int wallon; +int basecolor; + +walltype *oldwall; + +// +// offsets from upper left corner of a tile to the left and right edges of +// a given wall (NORTH-WEST) +// +fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 }; +fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 }; + +fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 }; +fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1}; + + +// +// offset from tile.x,tile.y of the tile that shares wallon side +// (side is not visable if it is shared) +// +int sharex[4] = { 0, 1, 0,-1}; +int sharey[4] = {-1, 0, 1, 0}; + +// +// amount to move tile.x,tile.y to follow wallon to another tile +// +int followx[4] = {-1, 0, 1, 0}; +int followy[4] = { 0,-1, 0, 1}; + +// +// cornerwall gives the wall on the same tile to start following when the +// wall ends at an empty tile (go around an edge on same tile) +// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following +// when the wall hits another tile (right angle corner) +// +int cornerwall[4] = {WEST,NORTH,EAST,SOUTH}; +int turnwall[4] = {EAST,SOUTH,WEST,NORTH}; + +// +// wall visabilities in reletive locations +// -,- 0,- +,- +// -,0 0,0 +,0 +// -,+ 0,+ +,+ +// +int visable[9][4] = +{ + {0,1,1,0}, {0,0,1,0}, {0,0,1,1}, + {0,1,0,0}, {0,0,0,0}, {0,0,0,1}, + {1,1,0,0}, {1,0,0,0}, {1,0,0,1} +}; + +int startwall[9] = {2,2,3, 1,0,3, 1,0,0}; +int backupwall[9] = {3,3,0, 2,0,0, 2,1,1}; + + +int walllength; + +/* +============================================================================= + + FUNCTIONS + +============================================================================= +*/ + +/* +======================== += += FollowTrace += +======================== +*/ + +int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max) +{ + int tx,ty,otx,oty; + long absdx,absdy,xstep,ystep; + + tx = tracex>>TILESHIFT; + ty = tracey>>TILESHIFT; + + spotvis[tx][ty] = true; + + absdx=LABS(deltax); + absdy=LABS(deltay); + + if (absdx>absdy) + { + ystep = (deltay<<8)/(absdx>>8); + + if (!ystep) + ystep = deltay>0 ? 1 : -1; + + oty = (tracey+ystep)>>TILESHIFT; + if (deltax>0) + { +//############### +// +// step x by +1 +// +//############### + do + { + tx++; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx-1][ty]) + { + tile.x = tx-1; + tile.y = ty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step x by -1 +// +//############### + do + { + tx--; + spotvis[tx][ty] = true; + tracey+=ystep; + ty = tracey>>TILESHIFT; + + if (ty!=oty) + { + if (tilemap[tx][oty]) + { + tile.x = tx; + tile.y = oty; + return 1; + } + oty = ty; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + + } + } + else + { + xstep = (deltax<<8)/(absdy>>8); + if (!xstep) + xstep = deltax>0 ? 1 : -1; + + + otx = (tracex+xstep)>>TILESHIFT; + if (deltay>0) + { +//############### +// +// step y by +1 +// +//############### + do + { + ty++; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[tx][ty-1]) + { + tile.x = tx; + tile.y = ty-1; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + else + { +//############### +// +// step y by -1 +// +//############### + do + { + ty--; + spotvis[tx][ty] = true; + tracex+=xstep; + tx = tracex>>TILESHIFT; + + if (tx!=otx) + { + if (tilemap[otx][ty]) + { + tile.x = otx; + tile.y = ty; + return 1; + } + otx = tx; + } + if (tilemap[tx][ty]) + { + tile.x = tx; + tile.y = ty; + return 1; + } + } while (--max); + return 0; + } + + } + +} + + +//=========================================================================== + + +/* +================= += += BackTrace += += Traces backwards from edgex,edgey to viewx,viewy to see if a closer += tile obscures the given point. If it does, it finishes the wall and += starts a new one. += Returns true if a tile is hit. += Call with a 1 to have it automatically finish the current wall += +================= +*/ + +int BackTrace (int finish) +{ + fixed tracex,tracey; + long deltax,deltay,absdx,absdy; + int steps,otx,oty,testx,testheight,offset,wall; + + deltax = viewx-edgex; + deltay = viewy-edgey; + + absdx = LABS(deltax); + absdy = LABS(deltay); + + if (absdx>absdy) + steps = ABS(focal.x-(edgex>>TILESHIFT))-1; + else + steps = ABS(focal.y-(edgey>>TILESHIFT))-1; + + if (steps<=0) + return 0; + + otx = tile.x; + oty = tile.y; + if (!FollowTrace(edgex,edgey,deltax,deltay,steps)) + return 0; + +// +// if the start wall is behind the focal point, the trace went too far back +// + if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close + { + if (tile.x == focal.x && tile.y == focal.y) + { + tile.x = otx; + tile.y = oty; + return 0; + } + + if (tile.xx1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return 0; + } + + +// +// back up along the intersecting face to find the rightmost wall +// + + if (tile.yfocal.x) + offset += 2; + + wallon = backupwall[offset]; + + while (tilemap[tile.x][tile.y]) + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + }; + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + + return 1; +} + +//=========================================================================== + + +/* +================= += += ForwardTrace += += Traces forwards from edgex,edgey along the line from viewx,viewy until += a solid tile is hit. Sets tile.x,tile.y += +================= +*/ + +void ForwardTrace (void) +{ + int offset; + fixed tracex,tracey; + long deltax,deltay; + + deltax = edgex-viewx; + deltay = edgey-viewy; + + FollowTrace(edgex,edgey,deltax,deltay,0); + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// start the new wall +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + +// +// if entire first wall is invisable, corner +// + TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon], + &rightwall->x2,&rightwall->height2); + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]] + || rightwall->x2 < (rightwall-1)->x2 ) + wallon = cornerwall [wallon]; + +// +// transform first point +// + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; +} + + +//=========================================================================== + + +/* +================= += += FinishWall += += Transforms edgex,edgey as the next point of the current wall += and sticks it in the wall list += +================= +*/ + +int FinishWall (void) +{ + char num[20]; + + oldwall = rightwall; + + rightwall->color = basecolor; + + TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2); + + if (rightwall->x2 <= (rightwall-1)->x2+2 + && rightwall->height2 < (rightwall-1)->height2 ) + return 0; + + rightwall->walllength = walllength; + + switch (wallon) + { + case north: + case south: + rightwall->side = 0; + rightwall->planecoord = edgey; + break; + + case west: + case east: + rightwall->side = 1; + rightwall->planecoord = edgex; + break; + } + + walllength = 1; + + rightwall++; + + return 1; +} + +//=========================================================================== + + +/* +================= += += InsideCorner += +================= +*/ + +void InsideCorner (void) +{ + int offset; + + // + // the wall turned -90 degrees, so draw what we have, move to the new tile, + // change wallon, change color, and continue following. + // + FinishWall (); + + tile.x += sharex[wallon]; + tile.y += sharey[wallon]; + + wallon = turnwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // follow it backwards until it turns + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // just turn to the next wall and continue + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + basecolor = tilemap[tile.x][tile.y]; + return; // continue from here + } + + // + // back follow the invisable wall until it turns, then follow that + // + do + { + tile.x += followx[wallon]; + tile.y += followy[wallon]; + } while (tilemap[tile.x][tile.y]); + + tile.x -= followx[wallon]; + tile.y -= followy[wallon]; + + wallon = cornerwall[wallon]; // turn to first visable face + + edgex = ((long)tile.x<<16)+point1x[wallon]; + edgey = ((long)tile.y<<16)+point1y[wallon]; + + if (!BackTrace(0)) // backtrace without finishing a wall + { + TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1); + basecolor = tilemap[tile.x][tile.y]; + } +} + +//=========================================================================== + + +/* +================= += += OutsideCorner += +================= +*/ + +void OutsideCorner (void) +{ + int offset; + + // + // edge is the outside edge of a corner, so draw the current wall and + // turn the corner (+90 degrees) + // + FinishWall (); + + tile.x -= followx[wallon]; // backup to the real tile + tile.y -= followy[wallon]; + wallon = cornerwall[wallon]; + + // + // if the new wall is visable, continue following it. Otherwise + // trace a ray from the corner to find a wall in the distance to + // follow + // + TESTWALLVISABLE; + + if (wallvisable) + { + // + // the new wall is visable, so just continue on + // + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + return; // still on same tile, so color is ok + } + +// +// start from a new tile further away +// + ForwardTrace(); // find the next wall further back + +} + + +//=========================================================================== + + +/* +================= += += FollowWalls += += Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it += until something else is seen or the entire view area is covered += +================= +*/ + +void FollowWalls (void) +{ + int height,newcolor,offset,wall; + +//#################### +// +// figure leftmost wall of new tile +// +//#################### + +restart: + + walllength = 1; + + if (tile.yfocal.x) + offset += 2; + + wallon = startwall[offset]; + +// +// if the start wall is inside a block, skip it by cornering to the second wall +// + if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + wallon = cornerwall [wallon]; + +// +// transform first edge to screen coordinates +// + edgex = ((long)tile.x<<16); + edgey = ((long)tile.y<<16); + + TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], + &rightwall->x1,&rightwall->height1); + + basecolor = tilemap[tile.x][tile.y]; + +//################## +// +// follow the wall as long as possible +// +//################## + +advance: + + do // while ( tile.x != right.x || tile.y != right.y) + { +// +// check for conditions that shouldn't happed... +// + if (rightwall->x1 > VIEWXH) // somehow missed right tile... + return; + + if (rightwall == &walls[DANGERHIGH]) + { + // + // somethiing got messed up! Correct by thrusting ahead... + // +// VW_ColorBorder(6); + bordertime = 60; + Thrust(player->angle,TILEGLOBAL/4); + player->angle+=5; + if (player->angle>ANGLES) + player->angle-=ANGLES; + aborttrace = true; + return; + +#if 0 + strcpy (str,"Wall list overflow at LE:"); + itoa(mapon+1,str2,10); + strcat (str,str2); + strcat (str," X:"); + ltoa(objlist[0].x,str2,10); + strcat (str,str2); + strcat (str," Y:"); + ltoa(objlist[0].y,str2,10); + strcat (str,str2); + strcat (str," AN:"); + itoa(objlist[0].angle,str2,10); + strcat (str,str2); + + Quit (str); +#endif + } + +// +// proceed along wall +// + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + + if (BackTrace(1)) // went behind a closer wall + continue; + + // + // advance to next tile along wall + // + tile.x += followx[wallon]; + tile.y += followy[wallon]; + + if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) + { + InsideCorner (); // turn at a corner + continue; + } + + newcolor = tilemap[tile.x][tile.y]; + + if (!newcolor) // turn around an edge + { + OutsideCorner (); + continue; + } + + if (newcolor != basecolor) + { + // + // wall changed color, so draw what we have and continue following + // + FinishWall (); + rightwall->x1 = oldwall->x2; // new wall shares this edge + rightwall->height1 = oldwall->height2; + basecolor = newcolor; + + continue; + } + walllength++; + } while (tile.x != right.x || tile.y != right.y); + + + +//###################### +// +// draw the last tile +// +//###################### + + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + + wallon = cornerwall[wallon]; + + // + // if the corner wall is visable, draw it + // + TESTWALLVISABLE; + + if (wallvisable) + { + rightwall->x1 = oldwall->x2; // common edge with last wall + rightwall->height1 = oldwall->height2; + edgex = ((long)tile.x<<16)+point2x[wallon]; + edgey = ((long)tile.y<<16)+point2y[wallon]; + FinishWall(); + } + +} + +//=========================================================================== diff --git a/src/lib/hb/c6_wiz.c b/src/lib/hb/c6_wiz.c new file mode 100755 index 00000000..571ae3f7 --- /dev/null +++ b/src/lib/hb/c6_wiz.c @@ -0,0 +1,3349 @@ +/* Catacomb Apocalypse 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 "DEF.H" +#include "gelib.h" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +////////#define NUMSCROLLS 8 + +#define SHOWITEMS 9 + +#define NUKETIME 40 +#define NUMBOLTS 10 +#define BOLTTICS 6 + +#define STATUSCOLOR 1 +#define TEXTCOLOR 14 + +#define SIDEBARWIDTH 5 + +#define BODYLINE 8 +#define POWERLINE 80 + +#define SPECTILESTART 0 // 18 + + +#define SHOTDAMAGE 1 +#define BIGSHOTDAMAGE 3 + + +#define PLAYERSPEED 5120 +#define RUNSPEED (8192<<1) + +#define SHOTSPEED 10000 + +//#define LASTWALLTILE 47 +//#define LASTSPECIALTILE 37 + +#define LASTTILE (LASTWALLPIC-FIRSTWALLPIC) // 47 + +#define FIRETIME 2 + +#define HANDPAUSE 30 + +#define RIGHTEDGE 205; +#define LEFTEDGE 95; +#define PRNY 32; +#define WINX 10; +#define WINY 32; + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +long lastnuke,lasthand; +int lasttext; +int handheight; +int boltsleft,bolttimer; +short RadarXY[MAX_RADAR_BLIPS][3]={-1,-1,-1}; +short radarx=RADARX,radary=RADARY,radar_xcenter=RADAR_XCENTER,radar_ycenter=RADAR_YCENTER; +int key_x[4]={24,27,27,24},key_y[4]={30,57,30,57}; + +boolean redraw_gems,button0down; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +int lastradar; +unsigned lastfiretime; + +int strafeangle[9] = {0,90,180,270,45,135,225,315,0}; + +short RotateAngle = -1; // -1 == No Angle to turn to... +short FreezeTime = 0; // Stops all think (except player) +short RotateSpeed; // Speed (and dir) to rotate.. + + +//=========================================================================== + +void CalcBounds(objtype *ob); +boolean VerifyGateExit(void); +void DrawNSEWIcons(void); +void DrawGems(void); +void DrawRadar (void); +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 DrawScrolls(void); + +void DrawNum(short x,short y,short value,short maxdigits); + +//---------- + +void Shoot (void); +void BigShoot (void); +void CastBolt (void); +void CastNuke (void); +void DrinkPotion (void); + +//---------- +void DrawHealth(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) +{ + short keytype; + + EGABITMASK(0xff); + for (keytype=0; keytype<4; keytype++) + DrawNum(key_x[keytype],key_y[keytype],gamestate.keys[keytype],2); + DrawNum(20,54,gamestate.potions,2); + DrawNum(20,36,gamestate.nukes,2); + DrawNum(20,18,gamestate.bolts,2); + + DrawHealth(); + DrawRadar(); + EGAWRITEMODE(0); + DrawGems(); +//////// DrawScrolls(); + redraw_gems = false; +} + + +//=========================================================================== + +/* +=============== += += GiveBolt += +=============== +*/ + +void GiveBolt (void) +{ + if (gamestate.bolts == 99) + return; + + SD_PlaySound (GETBOLTSND); + DrawNum(20,18,++gamestate.bolts,2); +} + + +/* +=============== += += TakeBolt += +=============== +*/ + +void TakeBolt (void) +{ + SD_PlaySound (USEBOLTSND); + DrawNum(20,18,--gamestate.bolts,2); +} + +//=========================================================================== + +/* +=============== += += GiveNuke += +=============== +*/ + +void GiveNuke (void) +{ + if (gamestate.nukes == 99) + return; + + SD_PlaySound (GETNUKESND); + DrawNum(20,36,++gamestate.nukes,2); +} + + +/* +=============== += += TakeNuke += +=============== +*/ + +void TakeNuke (void) +{ + SD_PlaySound (USENUKESND); + DrawNum(20,36,--gamestate.nukes,2); +} + +//=========================================================================== + +/* +=============== += += GivePotion += +=============== +*/ + +void GivePotion (void) +{ + if (gamestate.potions == 99) + return; + + SD_PlaySound (GETPOTIONSND); + DrawNum(20,54,++gamestate.potions,2); +} + + +/* +=============== += += TakePotion += +=============== +*/ + +void TakePotion (void) +{ + SD_PlaySound (USEPOTIONSND); + DrawNum(20,54,--gamestate.potions,2); +} + +//=========================================================================== + +/* +=============== += += GiveKey += +=============== +*/ + +void GiveKey (int keytype) +{ + int i,j,x; + + if (gamestate.keys[keytype] == 99) + return; + + SD_PlaySound (GETKEYSND); + DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2); +} + + +/* +=============== += += TakeKey += +=============== +*/ + +void TakeKey (int keytype) +{ + int i,j,x; + char *key_colors[] = {"a RED key", + "a YELLOW key", + "a GREEN key", + "a BLUE key"}; + + + SD_PlaySound (USEKEYSND); + DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2); + displayofs = bufferofs = screenloc[screenpage]; + CenterWindow(20,5); + US_CPrint("\nYou use\n"); + US_CPrint(key_colors[keytype]); + VW_UpdateScreen(); + VW_WaitVBL(120); +} + + +//=========================================================================== + +/* +=============== += += GiveGem += +=============== +*/ + +void GiveGem (int gemtype) +{ +#if 0 + int i,j,x; + + SD_PlaySound (GETKEYSND); + DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2); +#endif +} + + +/* +=============== += += TakeGem += +=============== +*/ + +void TakeGem (int gemtype) +{ +#if 0 + int i,j,x; + + SD_PlaySound (USEKEYSND); + DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2); +#endif +} + +/* +=============== += += DrawGem += +=============== +*/ + +void DrawGems() +{ + short loop; + + redraw_gems = false; + + bufferofs = 0; + LatchDrawPic (31,51,RADAR_BOTTOMPIC); + for (loop=0; loop<5; loop++) + if (gamestate.gems[loop]) + LatchDrawPic (32+loop,53,RADAR_RGEMPIC+loop); +} + +//=========================================================================== + +#if 0 + +/* +=============== += += GiveScroll += +=============== +*/ + +void GiveScroll (int scrolltype,boolean show) +{ + int i,j,x,y,scrollnum; + + SD_PlaySound (GETSCROLLSND); + gamestate.scrolls[scrolltype] = true; + + y = 30 + ((scrolltype > 3) * 10); + x = 26 + (scrolltype % 4); + DrawChar(x,y,SCROLLCHARS+scrolltype); + + if (show) + ReadScroll(scrolltype); +} + +/* +=============== += += DrawScrolls += += Force draw of all scrolls += +=============== +*/ +void DrawScrolls() +{ + int loop,x,y; + + VW_Bar(210,30,30,18,0xf); + + for (loop=0;loop<8;loop++) + if (gamestate.scrolls[loop]) + { + y = 30 + ((loop > 3) * 10); + x = 26 + (loop % 4); + DrawChar(x,y,SCROLLCHARS+loop); + } +} +#endif + + +//=========================================================================== + +#if 0 +/* +=============== += += GivePoints += +=============== +*/ + +void GivePoints (int points) +{ + pointcount = 1; + pointsleft += points; +} +#endif + + +//=========================================================================== + +#if 0 +/* +=============== += += 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 75) + picnum = FACE1PIC; + else + if (percentage > 50) + picnum = FACE2PIC; + else + if (percentage > 25) + picnum = FACE3PIC; + else + if (percentage) + picnum = FACE4PIC; + else + { + picnum = FACE5PIC; + CA_CacheGrChunk (picnum); + } + + bufferofs = 0; + if (!percentage) + { + UNMARKGRCHUNK(picnum); +// VW_DrawPic(8,14,picnum); + VW_DrawPic(10,14,picnum); + } + else + LatchDrawPic(10,14,picnum); +} + +//=========================================================================== + +/* +=============== += += DrawFreezeTime += +=============== +*/ +void DrawFreezeTime() +{ + short temp = fontcolor; + long percentage; + percentage = PERCENTAGE(100,MAXFREEZETIME,(long)FreezeTime,7); + fontcolor = 1 ^ 14; + DrawNum(23,70,percentage,3); + fontcolor = temp; +} + +//=========================================================================== + +/* +=============== += += DrawNum += +=============== +*/ +void DrawNum(short x,short y,short value,short maxdigits) +{ + char str[10],len,i; + + itoa(value,str,10); + len=strlen(str); + + for (i=len; itiley]+player->tilex)-NAMESTART; + + if ( number>26 ) + number = 0; + + if ((number == lasttext) && (!draw_text_whether_it_needs_it_or_not)) + return; + + lasttext = number; + + text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number]; + + if (text[0] == '@') + { + bordertime = 20;//FLASHTICS; + bcolor = 15; + VW_ColorBorder (15 | 56); + text++; + } + + _fmemcpy (str,text,80); + DisplayMsg(str,NULL); +} + +//=========================================================================== + +/* +=============== += += DisplayMsg += +=============== +*/ + +char DisplayMsg(char *text,char *choices) +{ + char ch=true; + short temp; + + bufferofs = 0; + PrintY = 1; + WindowX = 20; + WindowW = 270; + + VW_Bar (WindowX,2,WindowW,8,STATUSCOLOR); + temp = fontcolor; + fontcolor = TEXTCOLOR^STATUSCOLOR; + US_CPrintLine (text); + fontcolor = temp; + + if (choices) + { + ch=GetKeyChoice(choices,true); + LastScan = 0; + } + + return(ch); +} + +/* +=============== += += DisplaySMsg += +=============== +*/ +char DisplaySMsg(char *text,char *choices) +{ + char ch=true; + short temp; + + bufferofs = 0; + PrintY = 69; + WindowX = 98; + WindowW = 115; + + VW_Bar(WindowX,PrintY+1,WindowW,8,STATUSCOLOR); + temp = fontcolor; + fontcolor = TEXTCOLOR^STATUSCOLOR; + US_CPrintLine (text); + fontcolor = temp; + + if (choices) + { + ch=GetKeyChoice(choices,true); + LastScan = 0; + } + + return(ch); +} + +//=========================================================================== + +/* +=============== += += DrawRadar += +=============== +*/ + +void DrawRadar (void) +{ + int angle,number; + short objnum; + + bufferofs = 0; + LatchDrawPic (radarx,radary,RADAR_TOPPIC); + + asm cli + asm mov dx,GC_INDEX + asm mov ax,2*256+GC_MODE + asm out dx,ax // write mode 2 + + asm mov ax,GC_DATAROTATE + asm out dx,ax // no rotation / logical operation + + asm mov dx,SC_INDEX + asm mov al,SC_MAPMASK + asm mov ah,15 + asm out dx,ax // write to all four planes + asm sti + + objnum = 0; + while (RadarXY[objnum][2] != -1) + { + RadarBlip(radar_xcenter+RadarXY[objnum][0],radar_ycenter+RadarXY[objnum][1],RadarXY[objnum][2]); + objnum++; + } + + asm cli + asm mov dx,GC_INDEX + asm mov ax,255*256+GC_BITMASK + asm out dx,ax // reset bitmask to %11111111 + asm sti +} + +//=========================================================================== + + +//-------------------------------------------------------------------------- +// DrawNSEWIcons(void) +//-------------------------------------------------------------------------- + +void DrawRadarObj(short dx, short dy, unsigned sprnum,signed long psin,signed long pcos); + +void DrawNSEWIcons() +{ + signed x,y; + + x = -FixedByFrac(RADAR_X_IRADIUS,costable[player->angle]); + y = -FixedByFrac(RADAR_Y_IRADIUS,sintable[player->angle]); + + VWB_DrawSprite(radar_xcenter+x-3,radar_ycenter+y-3,NORTHICONSPR); + +} + +#if 0 +/* +=============== += += 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); +} +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Check the object and make sure it is a monster. Used in making the sound +// of a monster being shot. +// +///////////////////////////////////////////////////////////////////////////// + +boolean PlayMonsterSound(classtype objclass) +{ + switch (objclass) + { + case solidobj: + case realsolidobj: + return false; + default: + return true; + } +} + + +/* +============================================================================= + + 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_pshot_exp1 = {PSHOT_EXP1PIC,7,NULL,&s_pshot_exp2}; +statetype s_pshot_exp2 = {PSHOT_EXP2PIC,7,NULL,&s_pshot_exp3}; +statetype s_pshot_exp3 = {PSHOT_EXP3PIC,7,NULL,NULL}; + + +//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) +{ + DSpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*2); + new->obclass = pshotobj; + new->speed = SHOTSPEED; + new->angle = player->angle; + new->active = always; +} + +#if 0 +void SpawnBigPShot (void) +{ + SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS); + new->obclass = bigpshotobj; + new->speed = SHOTSPEED; + new->angle = player->angle; +} +#endif + + +/* +=================== += += JimsShotClipMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ +boolean JimsShotClipMove (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) || (check == player) || (!(check->flags & of_shootable))) + continue; + + ob->x -= xmove; + ob->y -= ymove; + + if (check->obclass != solidobj) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + else + if (check->obclass == solidobj && (check->flags & of_forcefield)) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return(true); + } + + return(false); // move is OK! + +} + + +/* +=============== += += T_Pshot += +=============== +*/ +#if 0 +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->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + + if (check->obclass != solidobj) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + + ob->state = &s_pshot_exp1; + 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_pshot_exp1; + 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->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + ShootActor (check,SHOTDAMAGE); + ob->state = &s_pshot_exp1; + ob->ticcount = ob->state->tictime; + return; + } +} +#endif + + + +void T_Pshot (objtype *ob) +{ + objtype *check; + long xmove,ymove,speed; + int xl,yl,xh,yh,tx,ty,nt1,nt2,x,y; + long intersect,basex,basey,pointx,pointy; + unsigned inside,total,tile; + boolean moveok; + +// +// check current position for monsters having moved into it +// + for (check = player->next; check; check=check->next) + if ((check->flags & of_shootable) + && ob->xl <= check->xh + && ob->xh >= check->xl + && ob->yl <= check->yh + && ob->yh >= check->yl) + { + + if (check->obclass != solidobj) + { + if (PlayMonsterSound(check->obclass)) + SD_PlaySound (SHOOTMONSTERSND); + if (ob->obclass == bigpshotobj) + ShootActor (check,BIGSHOTDAMAGE); + else + ShootActor (check,SHOTDAMAGE); + } + + ob->state = &s_pshot_exp1; + ob->obclass = expobj; + 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_pshot_exp1; + ob->obclass = expobj; + ob->ticcount = ob->state->tictime; + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + +// +// check final position for monsters hit +// + + JimsShotClipMove(obj,xmove,ymove); + +} + + +/* +============================================================================= + + PLAYER ACTIONS + +============================================================================= +*/ + +/* +=============== += += BuildShotPower += +=============== +*/ + +void BuildShotPower (void) +{ + int newlines,topline; + long i; + unsigned source,dest; + + if (gamestate.shotpower == MAXSHOTPOWER) + return; + + newlines = 0; + for (i=lasttimecount-realtics;i MAXSHOTPOWER) + { + newlines -= (gamestate.shotpower - MAXSHOTPOWER); + gamestate.shotpower = MAXSHOTPOWER; + } +} + + +//=========================================================================== + +/* +=============== += += ClearShotPower += +=============== +*/ + +void ClearShotPower (void) +{ + unsigned source,dest,topline; + +#if 0 + 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); +#endif + + gamestate.shotpower = 0; +} + +//=========================================================================== + +/* +=============== += += Shoot += +=============== +*/ + +void Shoot (void) +{ + ClearShotPower (); + SD_PlaySound (SHOOTSND); + SpawnPShot (); +} + +//=========================================================================== + +#if 0 +/* +=============== += += BigShoot += +=============== +*/ + +void BigShoot (void) +{ + ClearShotPower (); + SD_PlaySound (BIGSHOOTSND); + SpawnBigPShot (); +} +#endif + +//=========================================================================== + +/* +=============== += += CastBolt += +=============== +*/ + +void CastBolt (void) +{ + if (!gamestate.bolts) + { + SD_PlaySound (NOITEMSND); + return; + } + + TakeBolt (); + boltsleft = NUMBOLTS; + bolttimer = BOLTTICS; + Shoot (); +} + + +/* +=============== += += ContinueBolt += +=============== +*/ + +void ContinueBolt (void) +{ + bolttimer-=realtics; + if (bolttimer<0) + { + boltsleft--; + bolttimer = BOLTTICS; + Shoot (); + } +} + + +//=========================================================================== + +/* +=============== += += CastNuke += +=============== +*/ + +void CastNuke (void) +{ +// extern boolean autofire; + + int angle; + + if (!gamestate.nukes) + { + SD_PlaySound (NOITEMSND); + return; + } + +// if (!autofire) + TakeNuke (); + lastnuke = TimeCount; + + for (angle = 0; angle < ANGLES; angle+= ANGLES/16) + { + DSpawnNewObjFrac (player->x,player->y,&s_pshot1,24*PIXRADIUS); + new->obclass = bigpshotobj; + new->speed = SHOTSPEED; + new->angle = angle; + new->active = always; + } +} + +//=========================================================================== + +/* +=============== += += DrinkPotion += +=============== +*/ + +void DrinkPotion (void) +{ + unsigned source,dest,topline; + + if (!gamestate.potions) + { + SD_PlaySound (NOITEMSND); + return; + } + + DisplaySMsg("Curing", NULL); + TakePotion (); + gamestate.body = MAXBODY; + VW_WaitVBL(30); + status_flag = S_NONE; + +#if 0 +// +// 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); +#endif +} + + + +//=========================================================================== + +#if 0 + +//////////////////////////////////////////////////////////////////////////// +// +// GetScrollText +// +// parms - scroll -- the number of the scroll to display +// returns - a far pointer to the scroll text +// +//////////////////////////////////////////////////////////////////////////// + +char far *GetScrollText (int scroll) +{ + boolean found; + int i; + char far *txt; + unsigned ofset; + + CA_CacheGrChunk(SCROLLTEXT); + + found = false; + i = 0; + + txt = (char _seg *)grsegs[SCROLLTEXT]; + + while (!found) + { + while (*txt != '\n') + { + if (*txt == '\r') + *txt = 0; + txt++; + } + txt++; + if (i == scroll) + { + found = true; + ofset = FP_OFF(txt); + + while (*txt != '\n') + { + if (*txt == '\r') + *txt = 0; + txt++; + } + } + i++; + } + txt = (char _seg *)grsegs[SCROLLTEXT]+ofset; + + UNMARKGRCHUNK(SCROLLTEXT); + return(txt); +} //End of GetScrollText + +//=========================================================================== + +/* +=============== += += ReadScroll += +=============== +*/ + +extern boolean tileneeded[NUMFLOORS]; + +void ReadScroll (int scroll) +{ + PresenterInfo pi; + int i; + unsigned *skytemp,*gndtemp,blackcolor=0; + char far *scrolltext; + + DisplaySMsg("Reading Scroll", NULL); + bufferofs = displayofs = screenloc[screenpage]; + + if (status_flag != S_TIMESTOP) + status_flag = S_NONE; + + FreeUpMemory(); + + CA_CacheGrChunk (SCROLLTOPPIC); + CA_CacheGrChunk (SCROLL1PIC); + CA_CacheGrChunk (SCROLLBOTTOMPIC); + + skytemp = skycolor; + gndtemp = groundcolor; + skycolor = groundcolor = &blackcolor; + + VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0); + VW_DrawPic (10,0,SCROLLTOPPIC); + VW_DrawPic (10,32,SCROLL1PIC); + VW_DrawPic (10,88,SCROLLBOTTOMPIC); + + scrolltext = GetScrollText(scroll); + + pi.xl = LEFTEDGE; + pi.yl = PRNY; + pi.xh = RIGHTEDGE; + pi.yh = PRNY+1; + pi.bgcolor = 7; + pi.script[0] = (char far *)scrolltext; + Presenter(&pi); + + skycolor = skytemp; + groundcolor = gndtemp; + + UNMARKGRCHUNK(SCROLL1PIC); + UNMARKGRCHUNK(SCROLLTOPPIC); + UNMARKGRCHUNK(SCROLLBOTTOMPIC); + MM_FreePtr (&grsegs[SCROLL1PIC]); + MM_FreePtr (&grsegs[SCROLLTOPPIC]); + MM_FreePtr (&grsegs[SCROLLBOTTOMPIC]); + + CacheScaleds(); + + IN_ClearKeysDown (); + lasttext = -1; + DisplayMsg("Press ENTER or ESC to exit.",NULL); + while ((!Keyboard[sc_Escape]) && (!Keyboard[sc_Enter])); + IN_ClearKeysDown (); + + if (status_flag == S_TIMESTOP) + DisplaySMsg("Time Stopped: ",NULL); +} + +#endif + + +//=============== +// +// StopTime() +// +// +//=============== +void StopTime() +{ + FreezeTime = MAXFREEZETIME; + SD_PlaySound(FREEZETIMESND); + DisplaySMsg("Time Stopped: ",NULL); + status_flag = S_TIMESTOP; +} + + +/* +=============== += += TakeDamage += +=============== +*/ + +void TakeDamage (int points) +{ + unsigned source,dest,topline; + + if (!gamestate.body || (bordertime && bcolor==FLASHCOLOR) || godmode) + return; + + points = EasyDoDamage(points); + + if (points >= gamestate.body) + { + points = gamestate.body; + Flags |= FL_DEAD; + } + + bordertime = FLASHTICS<<2; + bcolor = FLASHCOLOR; + VW_ColorBorder (FLASHCOLOR); + + DisplaySMsg("Damaging blows!", NULL); + status_flag = S_NONE; + status_delay = 80; + + if (gamestate.body> 8; + if (spot == CANT_OPEN_CODE) // CAN'T EVER OPEN (it's just for looks) + { + CenterWindow(30,4); + US_CPrint("\nThis door is permanently blocked"); + VW_UpdateScreen(); + IN_ClearKeysDown(); + IN_Ack(); + return; + } + + // make sure player has key to get into door + // + + if (TILE_FLAGS(tile) & tf_EMBEDDED_KEY_COLOR) + keyspot = GATE_KEY_COLOR(tile); + else + keyspot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8; + + if (keyspot--) + if (!gamestate.keys[keyspot]) + { + SD_PlaySound(HIT_GATESND); + CenterWindow(20,5); + US_CPrint("\nYou need\n"); + US_CPrint(key_colors[keyspot]); + VW_UpdateScreen(); + IN_ClearKeysDown(); + IN_Ack(); + return; + } + + // + // deal with this gate (warp? simply open? whatever...) + // + switch (spot) + { + case NEXT_LEVEL_CODE: // WARP TO NEXT LEVEL + newlevel = gamestate.mapon+1; + playstate = ex_warped; + break; + + case REMOVE_DOOR_CODE: // REMOVE DOOR + (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = 0; + *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed + if (keyspot>=0) + TakeKey(keyspot); + break; + + default: // WARP TO A LEVEL + newlevel = spot; + playstate = ex_warped; + break; + } + + if (playstate == ex_warped) + { + SD_PlaySound(HIT_GATESND); +// levelinfo *li=&gamestate.levels[curmap]; + +// OldAngle = FaceDoor(x,y); + + if (!VerifyGateExit()) + { + IN_ClearKeysDown (); + playstate = ex_stillplaying; + break; + } + +// FaceAngle(OldAngle); + + if (keyspot>=0) + TakeKey(keyspot); + *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed + + gamestate.mapon = newlevel; + SD_PlaySound(WARPUPSND); + IN_ClearKeysDown (); + +// li->x = player->tilex; +// li->y = player->tiley; +// li->angle = player->angle+180; +// if (li->angle > 360) +// li->angle -= 360; + } + } + break; + } + + return true; +} + +//------------------------------------------------------------------------- +// VerifyGateExit() +//------------------------------------------------------------------------- +boolean VerifyGateExit() +{ + char choices[] = {sc_Escape,sc_Y,sc_N,0},ch; + + ch=DisplayMsg("Pass this way? Y/N",choices); + DrawText(true); + + return(ch == sc_Y); +} + + +/* +================== += += 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: + switch (check->temp1) + { + case B_BOLT: GiveBolt (); break; + + case B_NUKE: GiveNuke (); break; + + case B_POTION: GivePotion (); break; + +// case B_RKEY2: GiveKey(B_RKEY-B_RKEY); break; + + case B_RKEY: + case B_YKEY: + case B_GKEY: + case B_BKEY: GiveKey (check->temp1-B_RKEY); break; + +#if 0 + case B_SCROLL1: + case B_SCROLL2: + case B_SCROLL3: + case B_SCROLL4: + case B_SCROLL5: + case B_SCROLL6: + case B_SCROLL7: + case B_SCROLL8: GiveScroll (check->temp1-B_SCROLL1,true); break; +#endif + + case B_OLDCHEST: + case B_CHEST: GiveChest (); break; + + case B_RGEM: + case B_YGEM: + case B_GGEM: + case B_BGEM: + case B_PGEM: + SD_PlaySound(GETGEMSND); + gamestate.gems[check->temp1-B_RGEM] = GEM_DELAY_TIME; + redraw_gems = true; + break; + + default: + Quit("TouchActor(): INVALID BONUS"); + break; + } + + (unsigned)actorat[check->tilex][check->tiley] = 0; + RemoveObj (check); + + return false; + + case freezeobj: + StopTime(); + (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(objtype *)LASTTILE + && (check->flags & of_shootable) + && (check->obclass != bonusobj) + && (check->obclass != freezeobj) + && (check->obclass != solidobj) + && ob->xl-SIZE_TEST <= check->xh + && ob->xh+SIZE_TEST >= check->xl + && ob->yl-SIZE_TEST <= check->yh + && ob->yh+SIZE_TEST >= check->yl) + return true; + } + + return false; +} + +/* +=================== += += ClipXMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ +void ClipXMove (objtype *ob, long xmove) +{ + 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; + boolean invisible_present = false; + +// +// move player and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + ob->x += xmove; + + 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 <= LASTTILE) + { + if (TILE_FLAGS((unsigned)check) & tf_SPECIAL) + { + HitSpecialTile(x,y,(unsigned)check-SPECTILESTART); + goto blockmove; + } + + if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL) + { + invisible_present = true; + goto blockmove; + } + + + if (TILE_FLAGS((unsigned)check) & tf_SOLID) + { + goto blockmove; // solid wall + } + } + + TouchActor(ob,check); // pick up items + } + +// +// check nearby actors +// + if (LocationInActor(ob)) + { + ob->x -= xmove; + if (LocationInActor(ob)) + { + ob->x += xmove; + if (LocationInActor(ob)) + ob->x -= xmove; + } + } + return; // move is OK! + + +blockmove: + +// if (!SD_SoundPlaying()) +// SD_PlaySound (HITWALLSND); + + moveok = false; + + do + { + xmove /= 2; + if (moveok) + { + ob->x += xmove; + } + else + { + ob->x -= xmove; + } + 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) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (invisible_present) + { + moveok = false; + if (xmove>=-2048 && xmove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (xmove>=-2048 && xmove <=2048) + return; + moveok = true; + } while (1); +} + + +/* +=================== += += ClipYMove += += Only checks corners, so the object better be less than one tile wide! += +=================== +*/ +void ClipYMove (objtype *ob, 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; + boolean invisible_present = false; + +// +// move player and check to see if any corners are in solid tiles +// + basex = ob->x; + basey = ob->y; + + 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 <= LASTTILE) + { + if (TILE_FLAGS((unsigned)check) & tf_SPECIAL) // <=LASTSPECIALTILE) + { + HitSpecialTile (x,y,(unsigned)check-SPECTILESTART); + goto blockmove; + } + + if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL) + { + invisible_present = true; + goto blockmove; + } + + + if (TILE_FLAGS((unsigned)check) & tf_SOLID) // LASTWALLTILE) + { + goto blockmove; // solid wall + } + } + + TouchActor(ob,check); // pick up items + } + +// +// check nearby actors +// + if (LocationInActor(ob)) + { + if (LocationInActor(ob)) + { + ob->y -= ymove; + } + } + return; // move is OK! + + +blockmove: + +// if (!SD_SoundPlaying()) +// SD_PlaySound (HITWALLSND); + + moveok = false; + + do + { + ymove /= 2; + if (moveok) + { + ob->y += ymove; + } + else + { + 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 (ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (invisible_present) + { + moveok = false; + if (ymove>=-2048 && ymove <=2048) + { + ob->x = basex; + ob->y = basey; + return; + } + } + else + if (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,spot,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++) + { + spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8; + if (spot == EXP_WALL_CODE) + switch (ob->obclass) + { + case pshotobj: + case bigpshotobj: + ExplodeWall (x,y); + goto blockmove; +// break; + } + + tile = *(mapsegs[0]+farmapylookup[y]+x); + if (TILE_FLAGS(tile) & tf_SOLID) + goto blockmove; + } + return false; // move is OK! + + +blockmove: + + if (ob->obclass == pshotobj) + 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) +{ +#if 0 + levelinfo *li=&gamestate.levels[gamestate.mapon]; + + if (li->x != -1) + { + tilex = li->x; + tiley = li->y; + player->angle = li->angle; + } + else + player->angle = (1-dir)*90; +#endif + + player->obclass = playerobj; + player->active = always; + player->tilex = tilex; + player->tiley = tiley; + player->x = ((long)tilex<y = ((long)tiley<state = &s_player; + player->size = MINDIST; + CalcBounds(player); + player->angle = (1-dir)*90; + 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]); + + ClipXMove(player,xmove); + ClipYMove(player,ymove); + player->tilex = player->x >> TILESHIFT; + player->tiley = player->y >> TILESHIFT; +} + + + +/* +======================= += += ControlMovement += +======================= +*/ + +void ControlMovement (objtype *ob) +{ + int angle; + long speed; + + + if (control.button1) + { + // + // strafing + // + // + // side to side move + // + if (!mousexmove) + speed = 0; + else if (mousexmove<0) + speed = -(long)mousexmove*300; + else + speed = -(long)mousexmove*300; + + if (control.xaxis == -1) + { + speed += PLAYERSPEED*tics; + } + else if (control.xaxis == 1) + { + 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 (control.xaxis == 1) + { + ob->angle -= tics; + if (running) // fast turn + ob->angle -= (tics<<1); + } + else if (control.xaxis == -1) + { + ob->angle+= tics; + if (running) // fast turn + ob->angle += (tics<<1); + } + + 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 (control.yaxis == -1) + { + speed += PLAYERSPEED*tics; + } + else if (control.yaxis == 1) + { + 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) +{ +// extern boolean autofire; + + int angle,speed,scroll,loop; + unsigned text,tilex,tiley; + long lspeed; + +// boolean radar_moved=false; + + + ControlMovement (ob); + + + // + // firing + // + if (boltsleft) + { + handheight+=(realtics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + + ContinueBolt (); + lasthand = lasttimecount; + } + else + { + if (control.button0) + { + handheight+=(realtics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + lasthand = lasttimecount; + + if (!button0down) + Shoot(); + +// if (!autofire) + button0down=true; + } + else + { + if (lasttimecount > lasthand+HANDPAUSE) + { + handheight-=(realtics<<1); + if (handheight<0) + handheight = 0; + } + + button0down = false; + } +} + +#if 0 + if (control.button0) + { + handheight+=(realtics<<2); + if (handheight>MAXHANDHEIGHT) + handheight = MAXHANDHEIGHT; + + if ((unsigned)TimeCount/FIRETIME != lastfiretime) + BuildShotPower (); + lasthand = lasttimecount; + } + else + { + if (lasttimecount > lasthand+HANDPAUSE) + { + handheight-=(realtics<<1); + if (handheight<0) + handheight = 0; + } + + if (gamestate.shotpower) + { + lastfiretime = (unsigned)TimeCount/FIRETIME; + Shoot (); + } + } + } +#endif + + // + // special actions + // + + if ((Keyboard[sc_Space] || Keyboard[sc_C]) && gamestate.body != MAXBODY) + DrinkPotion (); + + if (Keyboard[sc_Z] && !boltsleft) + CastBolt (); + + if ( (Keyboard[sc_Enter] || Keyboard[sc_X]) && ((TimeCount-lastnuke > NUKETIME))) //|| (autofire))) + CastNuke (); + +#if 0 + scroll = LastScan-2; + if ( scroll>=0 && scrollx>>16l),(player->y>>16l)-y); + FreezeTime = StopTime; + + diff = player->angle - RotateAngle; + + if (((diff>0) && (diff<180)) || ((diff<0) && (diff>-180))) + RotateSpeed = -ROTATE_SPEED; + else + RotateSpeed = ROTATE_SPEED; +} +#endif + +#if 0 +//------------------------------------------------------------------------ +// CalcAngle() - +// +// DESC: Calculates the angle from a given dy & dx +//------------------------------------------------------------------------ +short CalcAngle(short dx,short dy) +{ + #define degtorad (180/PI) + float angle; + short diff; + float rad_angle; + + if (dx) + { + angle = atan((float)dy/dx)* degtorad; + if (angle<=0) + angle += 180; + if (dy>0) + angle += 180; + } + else + { + // 90 Deg shift + + if (dy < 0) + angle = 0 + 90; // Above player (NORTH) + else + angle = 180 + 90; // Below player (SOUTH) + } + + if (!angle) // HACK + angle++; + + return((short)abs(angle)); +} + +#endif + +#if 0 + +//------------------------------------------------------------------------- +// RotateView() - +// +// DESC : Rotates view (current view of game) to a dest angle. +//------------------------------------------------------------------------- +void RotateView() +{ + short LastPos; + + // Store old angle position then change angle... + // + + LastPos = player->angle; + + player->angle += RotateSpeed; + + // Check to see if we cranked past out dest angle... + // + + + if ((player->angle>ANGLES) || (!player->angle)) + player->angle = 1; + else + if (player->angle<1) + player->angle = ANGLES; + + // Check to see if we over shot our dest angle... + // + + if (((LastPos < RotateAngle) && (player->angle > RotateAngle) && (RotateSpeed > 0)) || + ((LastPos > RotateAngle) && (player->angle < RotateAngle) && (RotateSpeed < 0))) + player->angle = RotateAngle; + + // Check for ending force turn.... + // + + if (player->angle == RotateAngle) + RotateAngle = -1; + +} + + +//-------------------------------------------------------------------------- +// InitRotate() +//-------------------------------------------------------------------------- +void InitRotate(short DestAngle) +{ + if (player->angle != DestAngle) + { + RotateAngle = DestAngle; + + if (player->angle > DestAngle) + RotateSpeed = -ROTATE_SPEED; + else + RotateSpeed = ROTATE_SPEED; + + if (abs(player->angle - RotateAngle) > 180) + RotateSpeed =- RotateSpeed; + } +} + + + +//------------------------------------------------------------------------ +// FaceAngle() - +// +// PARAMS : DestAngle - Destination angle to turn to +//------------------------------------------------------------------------ +void FaceAngle(short DestAngle) +{ + signed long dx,dy,radius,psin,pcos,newx,newy; + int give; + short objnum,LastPos; + signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy; + short o_radius; + void (*think)(); + + + // Calculate the direction we want to turn to... + // + + InitRotate(DestAngle); + + RedrawStatusWindow(); + + while (RotateAngle != -1) + { + + RotateView(); + +// PollControls(); + + objnum=0; + + for (obj = player;obj;obj = obj->next) + { + if (obj->active >= yes) + { + + // keep a list of objects around the player for radar updates + // + if (obj == player) + { + px = player->x; + py = player->y; + psin = sintable[player->angle]; + pcos = costable[player->angle]; + xl = px-((long)RADAR_WIDTH< MAX_RADAR_BLIPS-2) + objnum = MAX_RADAR_BLIPS-2; + + ox = obj->x; + oy = obj->y; + + + if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh)) + { + norm_dx = (dx = px-ox)>>TILESHIFT; + norm_dy = (dy = oy-py)>>TILESHIFT; + + o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy)); + + if (o_radius < RADAR_RADIUS) + { + newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin); + newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos); + + RadarXY[objnum][0]=newx>>TILESHIFT; + RadarXY[objnum][1]=newy>>TILESHIFT; + + // Define color to use for this object... + // + + switch (obj->obclass) + { + case playerobj: + RadarXY[objnum++][2]=15; + break; + + // RED GEM + // + // STOMPY (DK RED) + // + case invisdudeobj: + case stompyobj: + RadarXY[objnum++][2]=4; + break; + + // BLOB (LT RED) + // + case blobobj: + RadarXY[objnum++][2]=12; + break; + + // BLUE GEM + // + // ROBOTANK (LT BLUE) + // + case robotankobj: + case fmageobj: + RadarXY[objnum++][2]=9; + break; + +#if 1 + // BLUE DEMON (DK BLUE) + // + case demonobj: + RadarXY[objnum++][2]=1; + break; +#endif + + // GREEN GEM + // + // WIZARD (LT GREEN) + // + case wizardobj: + RadarXY[objnum++][2]=10; + break; + + // AQUA MAN (DK GREEN) + // + case aquamanobj: + RadarXY[objnum++][2]=2; + break; + + // YELLOW GEM + // + // EQYPTIAN HEAD (BROWN) + // + case headobj: + RadarXY[objnum++][2]=6; + break; + + // RAMBONE (YELLOW) + // TROLL + case ramboneobj: + case trollobj: + RadarXY[objnum++][2]=14; + break; + + // BUG (LIGHT GRAY) + case bugobj: + RadarXY[objnum++][2]=7; + break; + + // RAY (DARK GRAY) + case rayobj: + RadarXY[objnum++][2]=8; + break; + + // PURPLE GEM + // + // MEC DEMON (PURPLE) + // + case cyborgdemonobj: + RadarXY[objnum++][2]=5; + break; + + // EYE (LT PURPLE) + // + case eyeobj: + case reyeobj: + RadarXY[objnum++][2]=13; + break; + } + } + } + } + } + + RadarXY[objnum][2]=-1; // Signals end of RadarXY list... + +// refresh all +// + + ThreeDRefresh(); + DrawRadar(); + EGAWRITEMODE(0); + DrawNSEWIcons(); + +// CheckKeys(); + } +} + + +//------------------------------------------------------------------------- +// FaceDoor() - Turns the player to face a door (a tile) at a given TILE x & y +// +// RETURNS : Returns the orginal angle of the player. +//------------------------------------------------------------------------ +short FaceDoor(short x, short y) +{ + short p_x,p_y,angle,old_angle; + + old_angle = player->angle; + + p_x = player->x>>16l; + p_y = player->y>>16l; + + if (p_x != x) + { + if (p_x > x) + angle = 180; // Face Left + else + angle = 1; // Face Right + } + + if (p_y != y) + { + if (p_y > y) + angle = 90; // Face Up + else + angle = 270; // Face Down + } + + FaceAngle(angle); + + return(old_angle); +} + + +#endif + + + +/*========================================================================== + + EXPLOSION SPAWNING ROUTINES + +===========================================================================*/ + +statetype s_explode = {0,1,T_ExpThink,&s_explode}; + +//------------------------------------------------------------------------- +// SpawnExplosion() +//------------------------------------------------------------------------ +void SpawnExplosion(fixed x, fixed y, short Delay) +{ + DSpawnNewObjFrac(x,y,&s_explode,PIXRADIUS*7); + new->obclass = expobj; + new->active = always; + new->temp1 = Delay; +} + + +//--------------------------------------------------------------------------- +// T_ExpThink() +//--------------------------------------------------------------------------- +void T_ExpThink(objtype *obj) +{ + if (obj->temp1) + { + if ((obj->temp1-=realtics) <= 0) + obj->temp1 = 0; + } + else + { + obj->state = &s_pshot_exp1; + obj->ticcount = obj->state->tictime; + SD_PlaySound(BOOMSND); + } +} + + + +//------------------------------------------------------------------------- +// SpawnBigExplosion() +//------------------------------------------------------------------------ +void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range) +{ + SpawnExplosion(x-random(Range),y+random(Range),random(Delay)); + SpawnExplosion(x+random(Range),y-random(Range),random(Delay)); + SpawnExplosion(x-random(Range),y-random(Range),random(Delay)); + SpawnExplosion(x+random(Range),y+random(Range),random(Delay)); +} + diff --git a/src/lib/hb/demokd.c b/src/lib/hb/demokd.c new file mode 100755 index 00000000..1b19e369 --- /dev/null +++ b/src/lib/hb/demokd.c @@ -0,0 +1,126 @@ +/* +===================== += += DemoLoop += +===================== +*/ + +void +DemoLoop (void) +{ + char *s; + word move; + longword lasttime; + char *FileName1; + struct Shape FileShape1; +#if CREDITS + char *FileName2; + struct Shape FileShape2; +#endif + struct ffblk ffblk; + WindowRec mywin; + int bufsave = bufferofs; + int dissave = displayofs; + + +#if FRILLS +// +// check for launch from ted +// + if (tedlevel) + { + NewGame(); + gamestate.mapon = tedlevelnum; + GameLoop(); + TEDDeath(); + } +#endif + +// +// demo loop +// + US_SetLoadSaveHooks(LoadGame,SaveGame,ResetGame); + restartgame = gd_Continue; + + if (findfirst("KDREAMS.CMP", &ffblk, 0) == -1) + Quit("Couldn't find KDREAMS.CMP"); + + while (true) + { + + loadedgame = false; + + FileName1 = "TITLESCR.LBM"; + if (LoadLIBShape("KDREAMS.CMP", FileName1, &FileShape1)) + Quit("Can't load TITLE SCREEN"); +#if CREDITS + FileName2 = "CREDITS.LBM"; + if (LoadLIBShape("KDREAMS.CMP", FileName2, &FileShape2)) + Quit("Can't load CREDITS SCREEN"); +#endif + + while (!restartgame && !loadedgame) + { + + VW_InitDoubleBuffer(); + IN_ClearKeysDown(); + + while (true) + { + + VW_SetScreen(0, 0); + MoveGfxDst(0, 200); + UnpackEGAShapeToScreen(&FileShape1, 0, 0); + VW_ScreenToScreen (64*200,0,40,200); + +#if CREDITS + if (IN_UserInput(TickBase * 8, false)) + break; +#else + if (IN_UserInput(TickBase * 4, false)) + break; +#endif + +#if CREDITS + MoveGfxDst(0, 200); + UnpackEGAShapeToScreen(&FileShape2, 0, 0); + VW_ScreenToScreen (64*200,0,40,200); + + if (IN_UserInput(TickBase * 7, false)) + break; +#else + MoveGfxDst(0, 200); + UnpackEGAShapeToScreen(&FileShape1, 0, 0); + VW_ScreenToScreen (64*200,0,40,200); + + if (IN_UserInput(TickBase * 3, false)) + break; +#endif + + displayofs = 0; + VWB_Bar(0,0,320,200,FIRSTCOLOR); + US_DisplayHighScores(-1); + + if (IN_UserInput(TickBase * 6, false)) + break; + + } + + bufferofs = bufsave; + displayofs = dissave; + + VW_FixRefreshBuffer(); + US_ControlPanel (); + } + + if (!loadedgame) + NewGame(); + + FreeShape(&FileShape1); +#if CREDITS + FreeShape(&FileShape2); +#endif + GameLoop(); + } +} diff --git a/src/lib/hb/demowl.c b/src/lib/hb/demowl.c new file mode 100755 index 00000000..85958eff --- /dev/null +++ b/src/lib/hb/demowl.c @@ -0,0 +1,170 @@ +/* +===================== += += DemoLoop += +===================== +*/ + +static char *ParmStrings[] = {"baby","easy","normal","hard",""}; + +void DemoLoop (void) +{ + static int LastDemo; + int i,level; + long nsize; + memptr nullblock; + +// +// check for launch from ted +// + if (tedlevel) + { + NoWait = true; + NewGame(1,0); + + for (i = 1;i < _argc;i++) + { + if ( (level = US_CheckParm(_argv[i],ParmStrings)) != -1) + { + gamestate.difficulty=level; + break; + } + } + +#ifndef SPEAR + gamestate.episode = tedlevelnum/10; + gamestate.mapon = tedlevelnum%10; +#else + gamestate.episode = 0; + gamestate.mapon = tedlevelnum; +#endif + GameLoop(); + Quit (NULL); + } + + +// +// main game cycle +// + + +// nsize = (long)40*1024; +// MM_GetPtr(&nullblock,nsize); + +#ifndef DEMOTEST + + #ifndef UPLOAD + + #ifndef GOODTIMES + #ifndef SPEAR + #ifndef JAPAN + if (!NoWait) + NonShareware(); + #endif + #else + + #ifndef GOODTIMES + #ifndef SPEARDEMO + CopyProtection(); + #endif + #endif + + #endif + #endif + #endif + + StartCPMusic(INTROSONG); + +#ifndef JAPAN + if (!NoWait) + PG13 (); +#endif + +#endif + + while (1) + { + while (!NoWait) + { +// +// title page +// + MM_SortMem (); +#ifndef DEMOTEST + +#ifdef SPEAR + CA_CacheGrChunk (TITLEPALETTE); + + CA_CacheGrChunk (TITLE1PIC); + VWB_DrawPic (0,0,TITLE1PIC); + UNCACHEGRCHUNK (TITLE1PIC); + + CA_CacheGrChunk (TITLE2PIC); + VWB_DrawPic (0,80,TITLE2PIC); + UNCACHEGRCHUNK (TITLE2PIC); + VW_UpdateScreen (); + VL_FadeIn(0,255,grsegs[TITLEPALETTE],30); + + UNCACHEGRCHUNK (TITLEPALETTE); +#else + CA_CacheScreen (TITLEPIC); + VW_UpdateScreen (); + VW_FadeIn(); +#endif + if (IN_UserInput(TickBase*15)) + break; + VW_FadeOut(); +// +// credits page +// + CA_CacheScreen (CREDITSPIC); + VW_UpdateScreen(); + VW_FadeIn (); + if (IN_UserInput(TickBase*10)) + break; + VW_FadeOut (); +// +// high scores +// + DrawHighScores (); + VW_UpdateScreen (); + VW_FadeIn (); + + if (IN_UserInput(TickBase*10)) + break; +#endif +// +// demo +// + + #ifndef SPEARDEMO + PlayDemo (LastDemo++%4); + #else + PlayDemo (0); + #endif + + if (playstate == ex_abort) + break; + StartCPMusic(INTROSONG); + } + + VW_FadeOut (); + +#ifndef SPEAR + if (Keyboard[sc_Tab] && MS_CheckParm("goobers")) +#else + if (Keyboard[sc_Tab] && MS_CheckParm("debugmode")) +#endif + RecordDemo (); + else + US_ControlPanel (0); + + if (startgame || loadedgame) + { + GameLoop (); + VW_FadeOut(); + StartCPMusic(INTROSONG); + } + } +} diff --git a/src/lib/hb/kd_act1.c b/src/lib/hb/kd_act1.c new file mode 100755 index 00000000..d2cf917c --- /dev/null +++ b/src/lib/hb/kd_act1.c @@ -0,0 +1,1130 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_ACT1.C +#include "KD_DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +#define PLACESPRITE RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum, \ + spritedraw,0); + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +int flowertime[4] = {700,700,350,175}; + +/* +============================================================================= + + MISC ACTOR STUFF + +============================================================================= +*/ + +/* +================== += += DoGravity += += Changes speed and location += +================== +*/ + + +void DoGravity (objtype *ob) +{ + long i; +// +// only accelerate on odd tics, because of limited precision +// + for (i=lasttimecount-tics;iyspeed < 0 && ob->yspeed >= -ACCGRAVITY) + { + // stop at apex of jump + ob->ymove += ob->yspeed; + ob->yspeed = 0; + return; + } + ob->yspeed+=ACCGRAVITY; + if (ob->yspeed>SPDMAXY) + ob->yspeed=SPDMAXY; + } + ob->ymove+=ob->yspeed; + } +} + + +/* +=============== += += AccelerateX += +=============== +*/ + +void AccelerateX (objtype *ob,int dir,int max) +{ + long i; + unsigned olddir; + + olddir = ob->xspeed & 0x8000; +// +// only accelerate on odd tics, because of limited precision +// + for (i=lasttimecount-tics;ixspeed+=dir; + if ( (ob->xspeed & 0x8000) != olddir) + { + olddir = ob->xspeed & 0x8000; + ob->xdir = olddir ? -1 : 1; + } + if (ob->xspeed>max) + ob->xspeed=max; + else if (ob->xspeed<-max) + ob->xspeed=-max; + } + ob->xmove+=ob->xspeed; + } +} + + +/* +=============== += += FrictionX += +=============== +*/ + +void FrictionX (objtype *ob) +{ + long i; + int dir; + unsigned olddir; + + olddir = ob->xspeed & 0x8000; + + if (ob->xspeed > 0) + dir = -1; + else if (ob->xspeed < 0) + dir = 1; + else + dir = 0; +// +// only accelerate on odd tics, because of limited precision +// + for (i=lasttimecount-tics;ixspeed+=dir; + if ( (ob->xspeed & 0x8000) != olddir) + ob->xspeed = 0; + } + ob->xmove+=ob->xspeed; + } +} + + +/* +=============== += += ProjectileThink += +=============== +*/ + +void ProjectileThink (objtype *ob) +{ + DoGravity (ob); + ob->xmove = tics*ob->xspeed; +} + +/* +=============== += += VelocityThink += +=============== +*/ + +void VelocityThink (objtype *ob) +{ + ob->xmove = tics*ob->xspeed; + ob->ymove = tics*ob->yspeed; +} + +/* +=============== += += DrawReact += +=============== +*/ + +void DrawReact (objtype *ob) +{ + RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum,spritedraw,1); +} + +void DrawReact2 (objtype *ob) +{ + RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum,spritedraw,2); +} + +void DrawReact3 (objtype *ob) +{ + RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum,spritedraw,3); +} + +/* +================== += += ChangeState += +================== +*/ + +void ChangeState (objtype *ob, statetype *state) +{ + ob->state = state; + ob->ticcount = 0; + if (state->rightshapenum) + { + if (ob->xdir>0) + ob->shapenum = state->rightshapenum; + else + ob->shapenum = state->leftshapenum; + } + + ob->xmove = 0; + ob->ymove = 0; + + ob->needtoreact = true; // it will need to be redrawn this frame + ClipToWalls (ob); +} + + +/* +==================== += += WalkReact += +==================== +*/ + +void WalkReact (objtype *ob) +{ + if (ob->xdir == 1 && ob->hitwest) + { + ob->x -= ob->xmove; + ob->xdir = -1; + ob->nothink = US_RndT()>>5; + ChangeState (ob,ob->state); + } + else if (ob->xdir == -1 && ob->hiteast) + { + ob->x -= ob->xmove; + ob->xdir = 1; + ob->nothink = US_RndT()>>5; + ChangeState (ob,ob->state); + } + else if (!ob->hitnorth) + { + ob->x -= 2*ob->xmove; + ob->y -= ob->ymove; + + ob->xdir = -ob->xdir; + ob->nothink = US_RndT()>>5; + ChangeState (ob,ob->state); + } + + PLACESPRITE; +} + + +/* +============================================================================= + + DOOR + +============================================================================= +*/ + +void DoorContact (objtype *ob, objtype *hit); + +extern statetype s_door; +extern statetype s_doorraise; + +statetype s_door = {DOORSPR,DOORSPR,think,false, + false,0, 0,0, NULL, DoorContact, DrawReact, &s_door}; +statetype s_doorraise = {DOORSPR,DOORSPR,slide,false, + false,24, 0,32, NULL, DoorContact, DrawReact, NULL}; + +/* +====================== += += SpawnDoor += +====================== +*/ + +void SpawnDoor (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = doorobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = -1; + new->needtoclip = false; + NewState (new,&s_door); +} + +/* +====================== += += DoorContact += +====================== +*/ + +void DoorContact (objtype *ob, objtype *hit) +{ + ClipToSpriteSide (hit,ob); +} + +/* +============================================================================= + + FLOWER + +temp1 = original class +temp2 = original state +temp3 = flower count + +============================================================================= +*/ + +void ChangeToFlower (objtype *ob); +void FlowerThink (objtype *ob); +void ChangeFromFlower (objtype *ob); + +extern statetype s_flower1; +extern statetype s_flower2; +extern statetype s_flower3; +extern statetype s_flower4; +extern statetype s_flower5; +extern statetype s_flower6; + +extern statetype s_poofto1; +extern statetype s_poofto2; +extern statetype s_poofto3; +extern statetype s_poofto4; + +extern statetype s_pooffrom1; +extern statetype s_pooffrom2; +extern statetype s_pooffrom3; +extern statetype s_pooffrom4; +extern statetype s_pooffrom5; +extern statetype s_pooffrom6; +extern statetype s_pooffrom7; + +extern statetype s_bonus1; + +#pragma warn -sus + +statetype s_flower1 = {FLOWER1SPR,FLOWER1SPR,stepthink,false, + false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower2}; +statetype s_flower2 = {FLOWER2SPR,FLOWER2SPR,stepthink,false, + false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower3}; +statetype s_flower3 = {FLOWER3SPR,FLOWER3SPR,stepthink,false, + false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower4}; +statetype s_flower4 = {FLOWER4SPR,FLOWER4SPR,stepthink,false, + false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower5}; +statetype s_flower5 = {FLOWER3SPR,FLOWER3SPR,stepthink,false, + false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower6}; +statetype s_flower6 = {FLOWER2SPR,FLOWER2SPR,stepthink,false, + false,20, 0,0, FlowerThink, NULL, DrawReact, &s_flower1}; + +statetype s_poofto1 = {POOF1SPR,POOF1SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_poofto2}; +statetype s_poofto2 = {POOF2SPR,POOF2SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_poofto3}; +statetype s_poofto3 = {POOF3SPR,POOF3SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_poofto4}; +statetype s_poofto4 = {POOF4SPR,POOF4SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, NULL}; + +statetype s_pooffrom1 = {POOF4SPR,POOF4SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom2}; +statetype s_pooffrom2 = {POOF3SPR,POOF3SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom3}; +statetype s_pooffrom3 = {POOF2SPR,POOF2SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom4}; +statetype s_pooffrom4 = {POOF1SPR,POOF1SPR,step,false, + false,20, 0,0, ChangeFromFlower, NULL, DrawReact2, &s_pooffrom5}; +statetype s_pooffrom5 = {POOF2SPR,POOF2SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom6}; +statetype s_pooffrom6 = {POOF3SPR,POOF3SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, &s_pooffrom7}; +statetype s_pooffrom7 = {POOF4SPR,POOF4SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact2, NULL}; + + +#pragma warn +sus + + +/* +====================== += += ChangeToFlower += +====================== +*/ + +void ChangeToFlower (objtype *ob) +{ + SD_PlaySound (FLOWERPOWERSND); + ob->y = ob->bottom-TILEGLOBAL*2; + ob->temp1 = (int)ob->obclass; + ob->temp2 = (int)ob->state; + ob->temp3 = 0; + ob->needtoclip = true; + ob->obclass = inertobj; + ob->xspeed = 0; + ChangeState (ob,&s_flower1); + ob->active = allways; // flower never deactivated + GetNewObj (true); + new->x = ob->x; + new->y = ob->y; + NewState (new,&s_poofto1); + new->active = removable; +} + + +/* +====================== += += FlowerThink += +====================== +*/ + +void FlowerThink (objtype *ob) +{ + ProjectileThink (ob); + if ( (ob->temp3+=tics) >= flowertime[gamestate.difficulty]) + { + GetNewObj (true); + new->active = allways; + new->temp1 = (int)ob; + new->x = ob->x; + new->y = ob->y; + NewState (new,&s_pooffrom1); + ob->temp3 = 0; + } +} + + +/* +====================== += += ChangeFromFlower += +====================== +*/ + +void ChangeFromFlower (objtype *ob) +{ + objtype *flower; + statetype *state; + unsigned oldbottom; + + SD_PlaySound (UNFLOWERPOWERSND); + flower = (objtype *)ob->temp1; + + oldbottom = flower->bottom; + ChangeState (flower,(statetype *)flower->temp2); + flower->y += oldbottom - flower->bottom; + + flower->obclass = flower->temp1; + flower->active = yes; // allow it to unspawn now if off screen +} + + +/* +============================================================================= + + BONUS + +temp1 = bonus type +temp2 = base shape number +temp3 = last animated shape number +1 + +============================================================================= +*/ + +void BonusThink (objtype *ob); + +extern statetype s_bonus1; + +#pragma warn -sus + +statetype s_bonus = {NULL,NULL,step,false, + false,20, 0,0, BonusThink, NULL, DrawReact2, &s_bonus}; + +statetype s_bonusrise = {NULL,NULL,slide,false, + false,40, 0,8, NULL, NULL, DrawReact3, NULL}; + +#pragma warn +sus + +/* +==================== += += SpawnBonus += +==================== +*/ + +int bonusshape[12] = {PEPPERMINT1SPR,COOKIE1SPR,CANDYCANE1SPR,CANDYBAR1SPR, + LOLLIPOP1SPR,COTTONCANDY1SPR,EXTRAKEEN1SPR,SUPERBONUS1SPR,FLOWERPOWER1SPR, + FLOWERPOWERUP1SPR,BOOBUSBOMB1SPR,MAGICKEY1SPR}; + +void SpawnBonus (int tilex, int tiley, int type) +{ + GetNewObj (false); + + new->needtoclip = false; + new->obclass = bonusobj; + new->x = tilex<y = tiley<y -= 8*PIXGLOBAL; // flower power up one block + + new->ydir = -1; // bonus stuff flies up when touched + + new->temp1 = type; + new->temp2 = new->shapenum = bonusshape[type]; + if (type != 7) + new->temp3 = new->temp2 + 2; + else + new->temp3 = new->temp2 + 4; // super bonus is 4 stage animation + + NewState (new,&s_bonus); +} + +/* +==================== += += BonusThink += +==================== +*/ + +void BonusThink (objtype *ob) +{ + if (++ob->shapenum == ob->temp3) + ob->shapenum = ob->temp2; +} + + + +/* +============================================================================= + + BROCCOLASH + +============================================================================= +*/ + +void BroccoThink (objtype *ob); +void BroccoGetUp (objtype *ob); + +extern statetype s_broccowalk1; +extern statetype s_broccowalk2; +extern statetype s_broccowalk3; +extern statetype s_broccowalk4; + +extern statetype s_broccosmash1; +extern statetype s_broccosmash2; +extern statetype s_broccosmash3; +extern statetype s_broccosmash4; +extern statetype s_broccosmash5; +extern statetype s_broccosmash6; +extern statetype s_broccosmash7; +extern statetype s_broccosmash8; +extern statetype s_broccosmash9; + +#pragma warn -sus + +statetype s_broccowalk1 = {BROCCOLASHRUNL1SPR,BROCCOLASHRUNR1SPR,step,false, + true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk2}; +statetype s_broccowalk2 = {BROCCOLASHRUNL2SPR,BROCCOLASHRUNR2SPR,step,false, + true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk3}; +statetype s_broccowalk3 = {BROCCOLASHRUNL3SPR,BROCCOLASHRUNR3SPR,step,false, + true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk4}; +statetype s_broccowalk4 = {BROCCOLASHRUNL4SPR,BROCCOLASHRUNR4SPR,step,false, + true,7, 128,0, BroccoThink, NULL, WalkReact, &s_broccowalk1}; + +statetype s_broccosmash1 = {BROCCOLASHSMASHL1SPR,BROCCOLASHSMASHR1SPR,step,true, + false,3, 0,0, NULL, NULL, DrawReact, &s_broccosmash2}; +statetype s_broccosmash2 = {BROCCOLASHSMASHL2SPR,BROCCOLASHSMASHR2SPR,step,true, + false,3, 0,0, NULL, NULL, DrawReact, &s_broccosmash3}; +statetype s_broccosmash3 = {BROCCOLASHSMASHL3SPR,BROCCOLASHSMASHR3SPR,step,true, + false,3, 0,0, NULL, NULL, DrawReact, &s_broccosmash4}; +statetype s_broccosmash4 = {BROCCOLASHSMASHL4SPR,BROCCOLASHSMASHR4SPR,step,false, + false,7, 0,0, NULL, NULL, DrawReact, &s_broccosmash5}; +statetype s_broccosmash5 = {BROCCOLASHSMASHL3SPR,BROCCOLASHSMASHR3SPR,step,true, + false,6, 0,0, NULL, NULL, DrawReact, &s_broccosmash6}; +statetype s_broccosmash6 = {BROCCOLASHSMASHL2SPR,BROCCOLASHSMASHR2SPR,step,true, + false,6, 0,0, NULL, NULL, DrawReact, &s_broccosmash7}; +statetype s_broccosmash7 = {BROCCOLASHSMASHL1SPR,BROCCOLASHSMASHR1SPR,step,true, + false,6, 0,0, NULL, NULL, DrawReact, &s_broccosmash8}; +statetype s_broccosmash8 = {BROCCOLASHRUNL1SPR,BROCCOLASHRUNR1SPR,step,true, + false,6, 0,0, BroccoGetUp, NULL, DrawReact, &s_broccosmash9}; +statetype s_broccosmash9 = {BROCCOLASHRUNL1SPR,BROCCOLASHRUNR1SPR,step,true, + false,6, 128,0, NULL, NULL, WalkReact, &s_broccowalk1}; + +#pragma warn +sus + +/* +==================== += += SpawnBrocco += +==================== +*/ + +void SpawnBrocco (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = broccoobj; + new->x = tilex<y = (tiley<xdir = 1; + NewState (new,&s_broccowalk1); +} + +/* +==================== += += BroccoGetUp += +==================== +*/ + +void BroccoGetUp (objtype *ob) +{ + ob->needtoclip = true; +} + + +/* +==================== += += BroccoThink += +==================== +*/ + +void BroccoThink (objtype *ob) +{ + int delta; + + if (ob->top > player->bottom || ob->bottom < player->top) + return; + + delta = player->x - ob->x; + + if ( ob->xdir == -1 ) + { + if (delta < -3*TILEGLOBAL) + return; + if (delta > TILEGLOBAL/2) + { + ob->xdir = 1; + return; + } + ob->state = &s_broccosmash1; + ob->needtoclip = false; + ob->xmove = 0; + return; + } + else + { + delta = player->left - ob->right; + if (delta > 3*TILEGLOBAL) + return; + if (delta < -TILEGLOBAL/2) + { + ob->xdir = -1; + return; + } + ob->state = &s_broccosmash1; + ob->needtoclip = false; + ob->xmove = 0; + return; + } +} + + +/* +============================================================================= + + TOMATOOTH + +ob->temp1 = jumptime + +============================================================================= +*/ + +#define SPDTOMATBOUNCE 30 +#define TICTOMATJUMP 10 +#define SPDTOMAT 16 + +void TomatBounceThink (objtype *ob); +void TomatReact (objtype *ob); + +extern statetype s_tomatbounce; +extern statetype s_tomatbounce2; + +#pragma warn -sus + +statetype s_tomatbounce = {TOMATOOTHL1SPR,TOMATOOTHR1SPR,stepthink,false, + false,20, 0,0, TomatBounceThink, NULL, TomatReact, &s_tomatbounce2}; +statetype s_tomatbounce2 = {TOMATOOTHL2SPR,TOMATOOTHR2SPR,stepthink,false, + false,20, 0,0, TomatBounceThink, NULL, TomatReact, &s_tomatbounce}; + +#pragma warn +sus + +/* +==================== += += SpawnTomat += +==================== +*/ + +void SpawnTomat (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = tomatobj; + new->x = tilex<y = (tiley<xdir = 1; + NewState (new,&s_tomatbounce); +} + +/* +==================== += += TomatBounceThink += +==================== +*/ + +void TomatBounceThink (objtype *ob) +{ + AccelerateX (ob,ob->x > player->x ? -1 : 1,SPDTOMAT); + if (ob->xspeed > 0) + ob->xdir = 1; + else + ob->xdir = -1; + + if (ob->temp1) + { + if (ob->temp1ymove = ob->yspeed*ob->temp1; + ob->temp1 = 0; + } + else + { + ob->ymove = ob->yspeed*tics; + ob->temp1-=tics; + } + } + else + DoGravity(ob); + +} + + +/* +==================== += += TomatReactThink += +==================== +*/ + +void TomatReact (objtype *ob) +{ + if (ob->hiteast || ob->hitwest) + { + ob->xdir = -ob->xdir; + ob->xspeed = -ob->xspeed; + } + + if (ob->hitsouth) + { + if (ob->tileright >= originxtile + && ob->tileleft <= originxtilemax + && ob->tiletop >= originytile + && ob->tilebottom <= originytilemax) + SD_PlaySound (BOUNCESND); + ob->yspeed = -ob->yspeed; + } + + if (ob->hitnorth) + { + if (ob->tileright >= originxtile + && ob->tileleft <= originxtilemax + && ob->tiletop >= originytile + && ob->tilebottom <= originytilemax) + SD_PlaySound (BOUNCESND); + ob->yspeed = -SPDTOMATBOUNCE-(US_RndT()>>4); + ob->temp1 = TICTOMATJUMP; + } + + PLACESPRITE; +} + + +/* +============================================================================= + + CARROT COURIER + +============================================================================= +*/ + +#define SPDCARROTLEAPX 32 +#define SPDCARROTLEAPY 40 + +void CarrotThink (objtype *ob); +void CarrotReact (objtype *ob); +void CarrotAirReact (objtype *ob); + +extern statetype s_carrotwalk1; +extern statetype s_carrotwalk2; +extern statetype s_carrotwalk3; +extern statetype s_carrotwalk4; + +extern statetype s_carrotleap; + +#pragma warn -sus + +statetype s_carrotwalk1 = {CARROTRUNL1SPR,CARROTRUNR1SPR,step,false, + true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk2}; +statetype s_carrotwalk2 = {CARROTRUNL2SPR,CARROTRUNR2SPR,step,false, + true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk3}; +statetype s_carrotwalk3 = {CARROTRUNL3SPR,CARROTRUNR3SPR,step,false, + true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk4}; +statetype s_carrotwalk4 = {CARROTRUNL4SPR,CARROTRUNR4SPR,step,false, + true,5, 128,0, NULL, NULL, CarrotReact, &s_carrotwalk1}; + +statetype s_carrotleap = {CARROTLEAPL1SPR,CARROTLEAPR1SPR,think,false, + false,0, 0,0, ProjectileThink, NULL, CarrotAirReact, NULL}; + +#pragma warn +sus + +/* +==================== += += SpawnCarrot += +==================== +*/ + +void SpawnCarrot (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = carrotobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_carrotwalk1); + new->hitnorth = 1; +} + + +/* +==================== += += CarrotReact += +==================== +*/ + +void CarrotReact (objtype *ob) +{ + unsigned x, width, bot, far *map; + + if (ob->xdir == 1 && ob->hitwest) + { + ob->xdir = -1; + } + else if (ob->xdir == -1 && ob->hiteast) + { + ob->xdir = 1; + } + else if (!ob->hitnorth) + { + ob->x -= ob->xmove; + ob->y -= ob->ymove; + + ob->yspeed = -SPDCARROTLEAPY; + ob->xspeed = SPDCARROTLEAPX*ob->xdir; + ChangeState (ob,&s_carrotleap); + } + + PLACESPRITE; +} + + +/* +==================== += += CarrotAirReact += +==================== +*/ + +void CarrotAirReact (objtype *ob) +{ + if (ob->hitsouth) + ob->yspeed = 0; + + if (ob->hitnorth) + ChangeState (ob,&s_carrotwalk1); + + PLACESPRITE; +} + + + +/* +============================================================================= + + ASPARAGUSTO + +============================================================================= +*/ + +void AsparThink (objtype *ob); + +extern statetype s_asparwalk1; +extern statetype s_asparwalk2; +extern statetype s_asparwalk3; +extern statetype s_asparwalk4; + +#pragma warn -sus + +statetype s_asparwalk1 = {ASPARAGUSRUNL1SPR,ASPARAGUSRUNR1SPR,step,true, + true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk2}; +statetype s_asparwalk2 = {ASPARAGUSRUNL2SPR,ASPARAGUSRUNR2SPR,step,false, + true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk3}; +statetype s_asparwalk3 = {ASPARAGUSRUNL3SPR,ASPARAGUSRUNR3SPR,step,true, + true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk4}; +statetype s_asparwalk4 = {ASPARAGUSRUNL4SPR,ASPARAGUSRUNR4SPR,step,false, + true,3, 100,0, NULL, NULL, WalkReact, &s_asparwalk1}; + +#pragma warn +sus + +/* +==================== += += SpawnAspar += +==================== +*/ + +void SpawnAspar (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = asparobj; + new->x = tilex<y = tiley<xdir = 1; + NewState (new,&s_asparwalk1); +} + + +/* +============================================================================= + + SOUR GRAPE + +============================================================================= +*/ + +void GrapeThink (objtype *ob); +void GrapeRiseReact (objtype *ob); +void GrapeFallReact (objtype *ob); + +extern statetype s_grapewait; +extern statetype s_grapefall; +extern statetype s_grapesit; +extern statetype s_graperise; + +#pragma warn -sus + +statetype s_grapewait = {GRAPEONVINESPR,GRAPEONVINESPR,think,false, + false,0, 0,0, GrapeThink, NULL, DrawReact, NULL}; + +statetype s_grapefall = {GRAPEFALLINGSPR,GRAPEFALLINGSPR,think,false, + false,0, 0,0, ProjectileThink, NULL, GrapeFallReact, NULL}; + +statetype s_grapesit = {GRAPEONVINESPR,GRAPEONVINESPR,step,false, + false,30, 0,0, NULL, NULL, DrawReact, &s_graperise}; +statetype s_graperise = {GRAPEONVINESPR,GRAPEONVINESPR,slide,false, + false,0, 0,-16, NULL, NULL, GrapeRiseReact, NULL}; + +#pragma warn +sus + +/* +==================== += += SpawnGrape += +==================== +*/ + +void SpawnGrape (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = grapeobj; + new->x = tilex<y = tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_grapewait); +} + + +/* +==================== += += GrapeThink += +==================== +*/ + +void GrapeThink (objtype *ob) +{ + unsigned y,starty,endy, far *map; + + if (player->left > ob->right + || player->right < ob->left + || player->y < ob->y ) + return; + +// +// see if there are any walls between grape and player +// + starty = ob->tilebottom; + endy = player->tiletop; + + map = mapsegs[1] + mapbwidthtable[starty]/2 + ob->tilemidx; + for (y = starty ; ystate = &s_grapefall; + SD_PlaySound (GRAPESCREAMSND); + +} + + +/* +==================== += += GrapeRiseReact += +==================== +*/ + +void GrapeRiseReact (objtype *ob) +{ + if (ob->hitsouth) + ChangeState(ob,&s_grapewait); + PLACESPRITE; +} + + +/* +==================== += += GrapeFallReact += +==================== +*/ + +void GrapeFallReact (objtype *ob) +{ + if (ob->hitnorth) + { + SD_PlaySound (BOUNCESND); + ob->yspeed = -(ob->yspeed<<1)/3; + if (ob->yspeed > -32) + ChangeState(ob,&s_grapesit); + } + PLACESPRITE; +} + diff --git a/src/lib/hb/kd_act2.c b/src/lib/hb/kd_act2.c new file mode 100755 index 00000000..e1865404 --- /dev/null +++ b/src/lib/hb/kd_act2.c @@ -0,0 +1,1509 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_ACT1.C +#include "KD_DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define PLACESPRITE RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum, \ + spritedraw,0); + + +void ProjectileReact (objtype *ob); + +/* +============================================================================= + + TATER TROOPER + +temp1 = chasing player +temp2 = nothink time + +============================================================================= +*/ + +void TaterThink (objtype *ob); +void BackupReact (objtype *ob); + +extern statetype s_taterwalk1; +extern statetype s_taterwalk2; +extern statetype s_taterwalk3; +extern statetype s_taterwalk4; + + +extern statetype s_taterattack1; +extern statetype s_taterattack2; +extern statetype s_taterattack3; + +#pragma warn -sus + +statetype s_taterwalk1 = {TATERTROOPWALKL1SPR,TATERTROOPWALKR1SPR,step,false, + true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk2}; +statetype s_taterwalk2 = {TATERTROOPWALKL2SPR,TATERTROOPWALKR2SPR,step,false, + true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk3}; +statetype s_taterwalk3 = {TATERTROOPWALKL3SPR,TATERTROOPWALKR3SPR,step,false, + true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk4}; +statetype s_taterwalk4 = {TATERTROOPWALKL4SPR,TATERTROOPWALKR4SPR,step,false, + true,10, 128,0, TaterThink, NULL, WalkReact, &s_taterwalk1}; + +statetype s_taterattack1= {TATERTROOPLUNGEL1SPR,TATERTROOPLUNGER1SPR,step,false, + false,12, 0,0, NULL, NULL, BackupReact, &s_taterattack2}; +statetype s_taterattack2= {TATERTROOPLUNGEL2SPR,TATERTROOPLUNGER2SPR,step,false, + false,20, 0,0, NULL, NULL, DrawReact, &s_taterattack3}; +statetype s_taterattack3= {TATERTROOPLUNGEL1SPR,TATERTROOPLUNGER1SPR,step,false, + false,8, 0,0, NULL, NULL, DrawReact, &s_taterwalk1}; + +#pragma warn +sus + +/* +==================== += += SpawnTater += +==================== +*/ + +void SpawnTater (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = taterobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_taterwalk1); + new->hitnorth = 1; +} + +/* +==================== += += TaterThink += +==================== +*/ + +void TaterThink (objtype *ob) +{ + int delta; + + if (ob->top > player->bottom || ob->bottom < player->top) + return; + + if ( ob->xdir == -1 ) + { + delta = ob->left - player->right; + if (delta > TILEGLOBAL) + return; + if (delta < -8*PIXGLOBAL) + { + ob->xdir = 1; + return; + } + SD_PlaySound (TATERSWINGSND); + ob->state = &s_taterattack1; + return; + } + else + { + delta = player->left - ob->right; + if (delta > TILEGLOBAL) + return; + if (delta < -8*PIXGLOBAL) + { + ob->xdir = -1; + return; + } + SD_PlaySound (TATERSWINGSND); + ob->state = &s_taterattack1; + return; + } +} + + +/* +==================== += += BackupReact += +==================== +*/ + +void BackupReact (objtype *ob) +{ + if (!ob->hitnorth) + { + ob->x-=ob->xmove; + ob->y-=ob->ymove; + } + + PLACESPRITE; +} + +/* +============================================================================= + + CANTELOUPE CART + +============================================================================= +*/ + +extern statetype s_cartroll1; +extern statetype s_cartroll2; + +void CartReact (objtype *ob); + +#pragma warn -sus + +statetype s_cartroll1 = {CANTCARTL1SPR,CANTCARTL1SPR,slide,true, + false,5, 32,0, NULL, NULL, CartReact, &s_cartroll2}; +statetype s_cartroll2 = {CANTCARTL2SPR,CANTCARTL2SPR,slide,true, + false,5, 32,0, NULL, NULL, CartReact, &s_cartroll1}; + +#pragma warn +sus + +/* +==================== += += SpawnCart += +==================== +*/ + +void SpawnCart (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = cartobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + new->active = allways; + NewState (new,&s_cartroll1); +} + + + +/* +==================== += += CartReact += +==================== +*/ + +void CartReact (objtype *ob) +{ + unsigned far *map; + + if (ob->xdir == 1 && ob->hitwest) + { + ob->xdir = -1; + } + else if (ob->xdir == -1 && ob->hiteast) + { + ob->xdir = 1; + } + + map = mapsegs[1] + mapbwidthtable[ob->tilebottom+1]/2; + if (ob->xdir == 1) + map += ob->tileright; + else + map += ob->tileleft; + + if ( !tinf[NORTHWALL + *map] ) + ob->xdir = -ob->xdir; + + PLACESPRITE; +} + + +/* +============================================================================= + + FRENCHY + +============================================================================= +*/ + +#define FRYXSPEED 40 +#define FRYYSPEED -20 + + +void FrenchyThink (objtype *ob); +void FrenchyRunThink (objtype *ob); +void FrenchyThrow (objtype *ob); + +extern statetype s_frenchywalk1; +extern statetype s_frenchywalk2; +extern statetype s_frenchywalk3; +extern statetype s_frenchywalk4; + +extern statetype s_frenchyrun1; +extern statetype s_frenchyrun2; +extern statetype s_frenchyrun3; +extern statetype s_frenchyrun4; + +extern statetype s_frenchythrow1; +extern statetype s_frenchythrow2; +extern statetype s_frenchythrow3; + +extern statetype s_fry1; +extern statetype s_fry2; + +#pragma warn -sus + +statetype s_frenchywalk1 = {FRENCHYRUNL1SPR,FRENCHYRUNR1SPR,step,false, + true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk2}; +statetype s_frenchywalk2 = {FRENCHYRUNL2SPR,FRENCHYRUNR2SPR,step,false, + true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk3}; +statetype s_frenchywalk3 = {FRENCHYRUNL3SPR,FRENCHYRUNR3SPR,step,false, + true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk4}; +statetype s_frenchywalk4 = {FRENCHYRUNL4SPR,FRENCHYRUNR4SPR,step,false, + true,10, 128,0, FrenchyThink, NULL, WalkReact, &s_frenchywalk1}; + +statetype s_frenchyrun1 = {FRENCHYRUNL1SPR,FRENCHYRUNR1SPR,step,true, + true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun2}; +statetype s_frenchyrun2 = {FRENCHYRUNL2SPR,FRENCHYRUNR2SPR,step,true, + true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun3}; +statetype s_frenchyrun3 = {FRENCHYRUNL3SPR,FRENCHYRUNR3SPR,step,true, + true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun4}; +statetype s_frenchyrun4 = {FRENCHYRUNL4SPR,FRENCHYRUNR4SPR,step,true, + true,5, 128,0, FrenchyRunThink, NULL, WalkReact, &s_frenchyrun1}; + +statetype s_frenchythrow1 = {FRENCHYTHROWL1SPR,FRENCHYTHROWR1SPR,step,false, + false,10, 0,0, NULL, NULL, DrawReact, &s_frenchythrow2}; +statetype s_frenchythrow2 = {FRENCHYTHROWL2SPR,FRENCHYTHROWR2SPR,step,false, + false,1, 0,0, FrenchyThrow, NULL, DrawReact, &s_frenchythrow3}; +statetype s_frenchythrow3 = {FRENCHYTHROWL2SPR,FRENCHYTHROWR2SPR,step,false, + false,10, -128,0, NULL, NULL, DrawReact, &s_frenchywalk1}; + +statetype s_fry1 = {FRENCHFRY1SPR,FRENCHFRY1SPR,stepthink,false, + false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_fry2}; +statetype s_fry2 = {FRENCHFRY2SPR,FRENCHFRY2SPR,stepthink,false, + false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_fry1}; + + +#pragma warn +sus + + +/* +==================== += += SpawnFrenchy += +==================== +*/ + +void SpawnFrenchy (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = frenchyobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_frenchywalk1); +} + + +/* +==================== += += FrenchyRunThink += +==================== +*/ + +void FrenchyRunThink (objtype *ob) +{ + ob->state = &s_frenchywalk1; +} + + +/* +==================== += += FrenchyThrow += +==================== +*/ + +void FrenchyThrow (objtype *ob) +{ + GetNewObj (true); + new->obclass = shotobj; + if (ob->xdir == 1) + { + new->x = ob->x+24*16; + new->y = ob->y+8*16; + } + else + { + new->x = ob->x; + new->y = ob->y+8*16; + } + new->xdir = ob->xdir; + new->ydir = 1; + new->xspeed = ob->xdir * FRYXSPEED-(US_RndT()>>4); + new->yspeed = FRYYSPEED; + new->active = removable; + NewState (new,&s_fry1); + + ob->nothink = 2; +} + + +/* +==================== += += FrenchyThink += +==================== +*/ + +void FrenchyThink (objtype *ob) +{ + int delta; + + if ( abs(ob->y - player->y) > 3*TILEGLOBAL ) + { + if (US_RndT()<8) + ob->xdir = -ob->xdir; // turn randomly + return; + } + + delta = player->x - ob->x; + + if (delta < -8*TILEGLOBAL) + { + // walk closer + ob->xdir = -1; + } + if (delta < -4*TILEGLOBAL) + { + // throw + ob->xdir = -1; + ob->state = &s_frenchythrow1; + } + else if (delta < 0) + { + // run away + ob->xdir = 1; + ob->state = &s_frenchyrun1; + ob->nothink = 8; + } + else if (delta < 4*TILEGLOBAL) + { + // run away + ob->xdir = -1; + ob->state = &s_frenchyrun1; + ob->nothink = 8; + } + else if (delta < 8*TILEGLOBAL) + { + // throw and walk closer + ob->xdir = 1; + ob->state = &s_frenchythrow1; + } + else + { + // walk closer + ob->xdir = 1; + } +} + + +/* +============================================================================= + + MELON LIPS + +ob->temp1 = direction : 0 = left, 1 = right, 2 = down + +============================================================================= +*/ + +#define SPITXSPEED 48 +#define SPITYSPEED -20 + +void MelonSpitThink (objtype *ob); +void ProjectileReact (objtype *ob); + +extern statetype s_melonside; +extern statetype s_melonsidespit; +extern statetype s_melonsidespit2; + +extern statetype s_melondown; +extern statetype s_melondownspit; +extern statetype s_melondownspit2; + +extern statetype s_melonseed1; +extern statetype s_melonseed2; + +extern statetype s_melonseedd1; +extern statetype s_melonseedd2; + +#pragma warn -sus + +statetype s_melonside = {MELONLIPSL1SPR,MELONLIPSR1SPR,step,false, + false,200, 0,0, NULL, NULL, DrawReact, &s_melonsidespit}; +statetype s_melonsidespit= {MELONLIPSL2SPR,MELONLIPSR2SPR,step,false, + false,6, 0,0, MelonSpitThink, NULL, DrawReact, &s_melonsidespit2}; +statetype s_melonsidespit2= {MELONLIPSL2SPR,MELONLIPSR2SPR,step,false, + false,6, 0,0, NULL, NULL, DrawReact, &s_melonside}; + +statetype s_melondown = {MELONLIPSD1SPR,MELONLIPSD1SPR,step,false, + false,200, 0,0, NULL, NULL, DrawReact, &s_melondownspit}; +statetype s_melondownspit = {MELONLIPSD2SPR,MELONLIPSD2SPR,step,false, + false,6, 0,0, MelonSpitThink, NULL, DrawReact, &s_melondownspit2}; +statetype s_melondownspit2 = {MELONLIPSD2SPR,MELONLIPSD2SPR,step,false, + false,6, 0,0, NULL, NULL, DrawReact, &s_melondown}; + +statetype s_melonseed1 = {MELONSEEDL1SPR,MELONSEEDR1SPR,think,false, + false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseed2}; +statetype s_melonseed2 = {MELONSEEDL2SPR,MELONSEEDR2SPR,think,false, + false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseed1}; + +statetype s_melonseedd1 = {MELONSEEDD1SPR,MELONSEEDD1SPR,stepthink,false, + false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseedd2}; +statetype s_melonseedd2 = {MELONSEEDD2SPR,MELONSEEDD2SPR,stepthink,false, + false,4, 0,0, ProjectileThink, NULL, ProjectileReact, &s_melonseedd1}; + +#pragma warn +sus + +/* +==================== += += SpawnMelon += +==================== +*/ + +void SpawnMelon (int tilex, int tiley,int dir) +{ + GetNewObj (false); + + new->obclass = melonobj; + new->x = tilex<y = tiley<xdir = 1; + else + new->xdir = -1; + if (dir <2) + NewState (new,&s_melonside); + else + NewState (new,&s_melondown); + + new->ticcount = US_RndT()>>1; + new->temp1 = dir; +} + + +/* +==================== += += MelonSpitThink += +==================== +*/ + +void MelonSpitThink (objtype *ob) +{ + GetNewObj (false); + new->obclass = shotobj; + switch (ob->temp1) + { + case 0: + new->x = ob->x+24*16; + new->y = ob->y+8*16; + new->xdir = ob->xdir; + new->ydir = 1; + new->xspeed = -SPITXSPEED-(US_RndT()>>4); + new->yspeed = SPITYSPEED; + NewState (new,&s_melonseed1); + break; + case 1: + new->x = ob->x; + new->y = ob->y+8*16; + new->xdir = ob->xdir; + new->ydir = 1; + new->xspeed = SPITXSPEED+(US_RndT()>>4); + new->yspeed = SPITYSPEED; + NewState (new,&s_melonseed1); + break; + case 2: + new->x = ob->x+8*16; + new->y = ob->y+24*16; + new->ydir = 1; + new->yspeed = -SPITYSPEED; + NewState (new,&s_melonseedd1); + break; + } + + new->active = removable; +} + +/* +============================ += += ProjectileReact += +============================ +*/ + +void ProjectileReact (objtype *ob) +{ + unsigned wall,absx,absy,angle,newangle; + unsigned long speed; + + PLACESPRITE; + if (ob->hiteast || ob->hitwest) + ob->xspeed= -ob->xspeed/2; + + if (ob->hitsouth) + ob->yspeed= -ob->yspeed/2; + + wall = ob->hitnorth; + if (wall) + { + if (ob->yspeed < 0) + ob->yspeed = 0; + + absx = abs(ob->xspeed); + absy = ob->yspeed; + if (absx>absy) + { + if (absx>absy*2) // 22 degrees + { + angle = 0; + speed = absx*286; // x*sqrt(5)/2 + } + else // 45 degrees + { + angle = 1; + speed = absx*362; // x*sqrt(2) + } + } + else + { + if (absy>absx*2) // 90 degrees + { + angle = 3; + speed = absy*256; + } + else + { + angle = 2; // 67 degrees + speed = absy*286; // y*sqrt(5)/2 + } + } + if (ob->xspeed > 0) + angle = 7-angle; + + speed >>= 1; + newangle = bounceangle[ob->hitnorth][angle]; + switch (newangle) + { + case 0: + ob->xspeed = speed / 286; + ob->yspeed = -ob->xspeed / 2; + break; + case 1: + ob->xspeed = speed / 362; + ob->yspeed = -ob->xspeed; + break; + case 2: + ob->yspeed = -(speed / 286); + ob->xspeed = -ob->yspeed / 2; + break; + case 3: + + case 4: + ob->xspeed = 0; + ob->yspeed = -(speed / 256); + break; + case 5: + ob->yspeed = -(speed / 286); + ob->xspeed = ob->yspeed / 2; + break; + case 6: + ob->xspeed = ob->yspeed = -(speed / 362); + break; + case 7: + ob->xspeed = -(speed / 286); + ob->yspeed = ob->xspeed / 2; + break; + + case 8: + ob->xspeed = -(speed / 286); + ob->yspeed = -ob->xspeed / 2; + break; + case 9: + ob->xspeed = -(speed / 362); + ob->yspeed = -ob->xspeed; + break; + case 10: + ob->yspeed = speed / 286; + ob->xspeed = -ob->yspeed / 2; + break; + case 11: + + case 12: + ob->xspeed = 0; + ob->yspeed = -(speed / 256); + break; + case 13: + ob->yspeed = speed / 286; + ob->xspeed = ob->yspeed / 2; + break; + case 14: + ob->xspeed = speed / 362; + ob->yspeed = speed / 362; + break; + case 15: + ob->xspeed = speed / 286; + ob->yspeed = ob->xspeed / 2; + break; + } + + if (speed < 256*16) + RemoveObj (ob); + } +} + +/* +============================================================================= + + SQUASHER + +============================================================================= +*/ + +#define SPDSQUASHLEAPY 50 + + +void SquasherThink (objtype *ob); +void SquasherJumpReact (objtype *ob); + +extern statetype s_squasherwalk1; +extern statetype s_squasherwalk2; + +extern statetype s_squasherjump1; +extern statetype s_squasherjump2; + +extern statetype s_squasherwait; + +#pragma warn -sus + +statetype s_squasherwalk1 = {SQUASHERWALKL1SPR,SQUASHERWALKR1SPR,step,false, + true,10, 128,0, SquasherThink, NULL, WalkReact, &s_squasherwalk2}; +statetype s_squasherwalk2 = {SQUASHERWALKL2SPR,SQUASHERWALKR2SPR,step,false, + true,10, 128,0, SquasherThink, NULL, WalkReact, &s_squasherwalk1}; + +statetype s_squasherjump1 = {SQUASHERJUMPL1SPR,SQUASHERJUMPR1SPR,stepthink,false, + false,20, 0,0, ProjectileThink, NULL, SquasherJumpReact, &s_squasherjump2}; +statetype s_squasherjump2 = {SQUASHERJUMPL2SPR,SQUASHERJUMPR2SPR,think,false, + false,0, 0,0, ProjectileThink, NULL, SquasherJumpReact, NULL}; + +statetype s_squasherwait = {SQUASHERJUMPL2SPR,SQUASHERJUMPR2SPR,step,false, + false,10, 0,0, ProjectileThink, NULL, DrawReact, &s_squasherwalk1}; + +#pragma warn +sus + +/* +==================== += += SpawnSquasher += +==================== +*/ + +void SpawnSquasher (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = squashobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_squasherwalk1); +} + +/* +==================== += += SquasherThink += +==================== +*/ + +void SquasherThink (objtype *ob) +{ + int delta; + + if ( abs(ob->y - player->y) > 3*TILEGLOBAL ) + { + if (US_RndT()<8) + ob->xdir = -ob->xdir; // turn randomly + return; + } + + + delta = player->x - ob->x; + + if ( ob->xdir == -1 ) + { + if (delta < -6*TILEGLOBAL) + return; + if (delta > 8*PIXGLOBAL) + { + ob->xdir = 1; + return; + } + } + else + { + if (delta > 6*TILEGLOBAL) + return; + if (delta < -8*PIXGLOBAL) + { + ob->xdir = 1; + return; + } + } + + ob->yspeed = -SPDSQUASHLEAPY; + ob->xspeed = delta/60; + ob->state = &s_squasherjump1; +} + +/* +==================== += += SquasherJumpReact += +==================== +*/ + +void SquasherJumpReact (objtype *ob) +{ + if (ob->hitsouth) + ob->yspeed = 0; + + if (ob->hitnorth) + ChangeState (ob,&s_squasherwait); + + PLACESPRITE; +} + + +/* +============================================================================= + + APEL + +temp4 = pole x coordinate + +============================================================================= +*/ + +void ApelThink (objtype *ob); +void ApelClimbThink (objtype *ob); +void ApelSlideThink (objtype *ob); +void ApelFallThink (objtype *ob); + +void ApelClimbReact (objtype *ob); +void ApelSlideReact (objtype *ob); +void ApelFallReact (objtype *ob); + +extern statetype s_apelwalk1; +extern statetype s_apelwalk2; +extern statetype s_apelwalk3; + +extern statetype s_apelclimb1; +extern statetype s_apelclimb2; + +extern statetype s_apelslide1; +extern statetype s_apelslide2; +extern statetype s_apelslide3; +extern statetype s_apelslide4; + +extern statetype s_apelfall; + +#pragma warn -sus + +statetype s_apelwalk1 = {APELWALKL1SPR,APELWALKR1SPR,step,false, + true,10, 128,0, ApelThink, NULL, WalkReact, &s_apelwalk2}; +statetype s_apelwalk2 = {APELWALKL2SPR,APELWALKR2SPR,step,false, + true,10, 128,0, ApelThink, NULL, WalkReact, &s_apelwalk3}; +statetype s_apelwalk3 = {APELWALKL3SPR,APELWALKR3SPR,step,false, + true,10, 128,0, ApelThink, NULL, WalkReact, &s_apelwalk1}; + +statetype s_apelclimb1 = {APELSHINNY1SPR,APELSHINNY1SPR,slide,false, + false,6, 0,-16, ApelClimbThink, NULL, ApelClimbReact, &s_apelclimb2}; +statetype s_apelclimb2 = {APELSHINNY2SPR,APELSHINNY2SPR,slide,false, + false,6, 0,-16, ApelClimbThink, NULL, ApelClimbReact, &s_apelclimb1}; + +statetype s_apelslide1 = {APELSLIDE1SPR,APELSLIDE1SPR,slide,false, + false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide2}; +statetype s_apelslide2 = {APELSLIDE2SPR,APELSLIDE2SPR,slide,false, + false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide3}; +statetype s_apelslide3 = {APELSLIDE3SPR,APELSLIDE3SPR,slide,false, + false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide4}; +statetype s_apelslide4 = {APELSLIDE4SPR,APELSLIDE4SPR,slide,false, + false,6, 0,16, ApelSlideThink, NULL, ApelFallReact, &s_apelslide1}; + +statetype s_apelfall = {APELWALKL1SPR,APELWALKR1SPR,think,false, + false,0, 0,0, ProjectileThink, NULL, ApelFallReact, NULL}; + +#pragma warn +sus + + +/* +==================== += += SpawnApel += +==================== +*/ + +void SpawnApel (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = apelobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_apelwalk1); +} + + +/* +==================== += += ApelThink += +==================== +*/ + +void ApelThink (objtype *ob) +{ + int x,y; + unsigned far *map; + + if (ob->top > player->bottom || ob->bottom < player->top) + { + // + // try to climb a pole to reach player + // + if (ob->y < player->y) + y = ob->tilebottom; + else + y = ob->tiletop; + + map = (unsigned _seg *)mapsegs[1]+ + mapbwidthtable[y]/2 + ob->tilemidx; + + if ((tinf[INTILE+*map]&0x7f) == 1) + { + ob->xmove = (ob->tilemidx<x; + ob->ymove = 0; + ob->temp4 = ob->tilemidx; // for future reference + ob->needtoclip = false; // can climb through pole holes + if (ob->y < player->y) + ob->state = &s_apelslide1; + else + ob->state = &s_apelclimb1; + return; + } + } + + if (US_RndT()>32) // don't turn around all the time + return; + + if (ob->x < player->x) + ob->xdir = 1; + else + ob->xdir = -1; + +} + + +/* +==================== += += ApelClimbThink += +==================== +*/ + +void ApelClimbThink (objtype *ob) +{ + unsigned far *map; + + map = (unsigned _seg *)mapsegs[1]+ + mapbwidthtable[ob->tiletop]/2 + ob->temp4; + + if ((tinf[INTILE+*map]&0x7f) != 1) + { + ob->needtoclip = true; + ob->state = &s_apelfall; + } +} + + +/* +==================== += += ApelSlideThink += +==================== +*/ + +void ApelSlideThink (objtype *ob) +{ + unsigned far *map; + + map = (unsigned _seg *)mapsegs[1]+ + mapbwidthtable[ob->tilebottom]/2 + ob->temp4; + + if ((tinf[INTILE+*map]&0x7f) != 1) + { + ob->needtoclip = true; + ob->state = &s_apelfall; + } +} + + +/* +==================== += += ApelClimbReact += +==================== +*/ + +void ApelClimbReact (objtype *ob) +{ + if (ob->hitsouth) + ChangeState (ob,&s_apelfall); + PLACESPRITE; +} + + +/* +==================== += += ApelFallReact += +==================== +*/ + +void ApelFallReact (objtype *ob) +{ + if (ob->hitnorth) + ChangeState (ob,&s_apelwalk1); + PLACESPRITE; +} + + +/* +============================================================================= + + PEA BRAIN + +============================================================================= +*/ + +void PeaBrainThink (objtype *ob); +void PeaFlyReact (objtype *ob); + +extern statetype s_peabrainfly; + +extern statetype s_peabrainwalk1; +extern statetype s_peabrainwalk2; +extern statetype s_peabrainwalk3; +extern statetype s_peabrainwalk4; + +#pragma warn -sus + +statetype s_peabrainfly = {PEABRAINWALKL1SPR,PEABRAINWALKR1SPR,think,false, + false,0, 0,0, ProjectileThink, NULL, PeaFlyReact, NULL}; + +statetype s_peabrainwalk1 = {PEABRAINWALKL1SPR,PEABRAINWALKR1SPR,step,false, + true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk2}; +statetype s_peabrainwalk2 = {PEABRAINWALKL2SPR,PEABRAINWALKR2SPR,step,false, + true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk3}; +statetype s_peabrainwalk3 = {PEABRAINWALKL3SPR,PEABRAINWALKR3SPR,step,false, + true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk4}; +statetype s_peabrainwalk4 = {PEABRAINWALKL4SPR,PEABRAINWALKR4SPR,step,false, + true,10, 128,0, PeaBrainThink, NULL, WalkReact, &s_peabrainwalk1}; + +#pragma warn +sus + +/* +==================== += += SpawnPeaBrain += +==================== +*/ + +void SpawnPeaBrain (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = peabrainobj; + new->x = tilex<y = tiley<hitnorth) + ChangeState (ob,&s_peabrainwalk1); + + PLACESPRITE; +} + + +/* +============================================================================= + + PEA POD + +temp1 = number of peas spit + +============================================================================= +*/ + +#define MAXPEASPIT 4 + +#define PEAXSPEED 48 +#define PEAYSPEED -20 + + +void PeaPodThink (objtype *ob); +void SpitPeaBrain (objtype *ob); + +extern statetype s_peapodwalk1; +extern statetype s_peapodwalk2; +extern statetype s_peapodwalk3; +extern statetype s_peapodwalk4; + +extern statetype s_peapodspit1; +extern statetype s_peapodspit2; + +#pragma warn -sus + +statetype s_peapodwalk1 = {PEAPODRUNL1SPR,PEAPODRUNR1SPR,step,false, + true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk2}; +statetype s_peapodwalk2 = {PEAPODRUNL2SPR,PEAPODRUNR2SPR,step,false, + true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk3}; +statetype s_peapodwalk3 = {PEAPODRUNL3SPR,PEAPODRUNR3SPR,step,false, + true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk4}; +statetype s_peapodwalk4 = {PEAPODRUNL4SPR,PEAPODRUNR4SPR,step,false, + true,10, 128,0, PeaPodThink, NULL, WalkReact, &s_peapodwalk1}; + +statetype s_peapodspit1 = {PEAPODSPITLSPR,PEAPODSPITRSPR,step,false, + true,30, 0,0, SpitPeaBrain, NULL, DrawReact, &s_peapodspit2}; +statetype s_peapodspit2 = {PEAPODSPITLSPR,PEAPODSPITRSPR,step,false, + true,30, 0,0, NULL, NULL, DrawReact, &s_peapodwalk1}; + +#pragma warn +sus + +/* +==================== += += SpawnPeaPod += +==================== +*/ + +void SpawnPeaPod (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = peapodobj; + new->x = tilex<y = (tiley<xdir = 1; + new->ydir = 1; + NewState (new,&s_peapodwalk1); +} + +/* +==================== += += SpitPeaBrain += +==================== +*/ + +void SpitPeaBrain (objtype *ob) +{ + GetNewObj (true); + new->obclass = peabrainobj; + if (ob->xdir == 1) + { + new->x = ob->x+8*16; + new->y = ob->y+8*16; + } + else + { + new->x = ob->x; + new->y = ob->y+8*16; + } + new->xdir = ob->xdir; + new->ydir = 1; + new->xspeed = ob->xdir * PEAXSPEED-(US_RndT()>>4); + new->yspeed = PEAYSPEED; + NewState (new,&s_peabrainfly); +} + + + +/* +==================== += += PeaPodThink += +==================== +*/ + +void PeaPodThink (objtype *ob) +{ + int delta; + + if ( abs(ob->y - player->y) > 3*TILEGLOBAL ) + return; + + if (player->x < ob->x && ob->xdir == 1) + return; + + if (player->x > ob->x && ob->xdir == -1) + return; + + if (US_RndT()<8 && ob->temp1 < MAXPEASPIT) + { + ob->temp1 ++; + ob->state = &s_peapodspit1; + ob->xmove = 0; + } +} + + +/* +============================================================================= + + BOOBUS TUBOR + +temp4 = hit points + +============================================================================= +*/ + +#define PREFRAGTHINK 60 +#define POSTFRAGTHINK 60 + +#define SPDBOOBUSJUMP 60 +#define SPDBOOBUSRUNJUMP 24 + +void BoobusThink (objtype *ob); +void FinishThink (objtype *ob); +void FragThink (objtype *ob); +void BoobusGroundReact (objtype *ob); +void BoobusAirReact (objtype *ob); + +extern statetype s_boobuswalk1; +extern statetype s_boobuswalk2; +extern statetype s_boobuswalk3; +extern statetype s_boobuswalk4; + +extern statetype s_boobusjump; + +extern statetype s_deathwait1; +extern statetype s_deathwait2; +extern statetype s_deathwait3; +extern statetype s_deathboom1; +extern statetype s_deathboom2; +extern statetype s_deathboom3; +extern statetype s_deathboom4; +extern statetype s_deathboom5; +extern statetype s_deathboom6; + +#pragma warn -sus + +statetype s_boobuswalk1 = {BOOBUSWALKL1SPR,BOOBUSWALKR1SPR,step,false, + true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk2}; +statetype s_boobuswalk2 = {BOOBUSWALKL2SPR,BOOBUSWALKR2SPR,step,false, + true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk3}; +statetype s_boobuswalk3 = {BOOBUSWALKL3SPR,BOOBUSWALKR3SPR,step,false, + true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk4}; +statetype s_boobuswalk4 = {BOOBUSWALKL4SPR,BOOBUSWALKR4SPR,step,false, + true,10, 128,0, BoobusThink, NULL, BoobusGroundReact, &s_boobuswalk1}; + +statetype s_boobusjump = {BOOBUSJUMPSPR,BOOBUSJUMPSPR,think,false, + false,0, 0,0, ProjectileThink, NULL, BoobusAirReact, NULL}; + +statetype s_boobusdie = {BOOBUSJUMPSPR,BOOBUSJUMPSPR,step,false, + false,4, 0,0, FragThink, NULL, DrawReact, &s_boobusdie}; +statetype s_boobusdie2 = {NULL,NULL,step,false, + false,4, 0,0, FragThink, NULL, NULL, &s_boobusdie2}; +statetype s_boobusdie3 = {NULL,NULL,step,false, + false,250, 0,0, FinishThink, NULL, NULL, NULL}; + +statetype s_deathboom1 = {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false, + false,20, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom2}; +statetype s_deathboom2 = {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false, + false,20, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom3}; +statetype s_deathboom3 = {POOF1SPR,POOF1SPR,step,false, + false,40, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom4}; +statetype s_deathboom4 = {POOF2SPR,POOF2SPR,step,false, + false,30, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom5}; +statetype s_deathboom5 = {POOF3SPR,POOF3SPR,step,false, + false,30, 0,0, ProjectileThink, NULL, DrawReact3, &s_deathboom6}; +statetype s_deathboom6 = {POOF4SPR,POOF4SPR,step,false, + false,30, 0,0, ProjectileThink, NULL, DrawReact3, NULL}; + + +#pragma warn +sus + +/* +==================== += += SpawnBoobus += +==================== +*/ + +void SpawnBoobus (int tilex, int tiley) +{ + GetNewObj (false); + + new->obclass = boobusobj; + new->x = tilex<y = (tiley<xdir = -1; + NewState (new,&s_boobuswalk1); + new->temp4 = 12; // hit points +} + + +/* +=================== += += FragThink += +=================== +*/ + +void FragThink (objtype *ob) +{ + if (++ob->temp1 == PREFRAGTHINK) + ob->state = &s_boobusdie2; + if (++ob->temp1 == POSTFRAGTHINK) + { + RF_RemoveSprite (&ob->sprite); + ob->state = &s_boobusdie3; + } + + SD_PlaySound (BOMBBOOMSND); + GetNewObj (true); + new->x = ob->x-BLOCKSIZE + 5*US_RndT(); + new->y = ob->y-BLOCKSIZE + 5*US_RndT(); + new->xspeed = 0; //US_RndT()/4-32; + new->yspeed = 0; //US_RndT()/4-32; + US_RndT(); // keep rnd from even wrapping + ChangeState (new,&s_deathboom1); +} + + +/* +=================== += += FinishThink += +=================== +*/ + +void FinishThink (objtype *ob) +{ + playstate = victorious; + ob++; + SD_PlaySound (BOOBUSGONESND); +} + + +/* +==================== += += BoobusThink += +==================== +*/ + +void BoobusThink (objtype *ob) +{ + unsigned move; + boolean inline = false; + + if (ob->left > player->right) + ob->xdir = -1; + else if (ob->right < player->left) + ob->xdir = 1; + else + inline = true; + + if (player->top < ob->bottom && player->bottom > ob->top) + { + // on same level as player, so charge! + + } + else + { + // above or below player, so get directly in line and jump + if (inline) + { + if (ob->y < player->y) + { + // drop down + move = PIXGLOBAL*8; + ob->tilebottom++; + ob->bottom += move; + ob->y += move; + ob->state = &s_boobusjump; + ob->yspeed = ob->xmove = ob->xspeed = ob->ymove = 0; + } + else + { + // jump up + ob->state = &s_boobusjump; + ob->yspeed = -SPDBOOBUSJUMP; + ob->xspeed = 0; + } + + } + } +} + +/* +==================== += += BoobusGroundReact += +==================== +*/ + +void BoobusGroundReact (objtype *ob) +{ + if (ob->xdir == 1 && ob->hitwest) + { + ob->x -= ob->xmove; + ob->xdir = -1; + ob->nothink = US_RndT()>>5; + ChangeState (ob,ob->state); + } + else if (ob->xdir == -1 && ob->hiteast) + { + ob->x -= ob->xmove; + ob->xdir = 1; + ob->nothink = US_RndT()>>5; + ChangeState (ob,ob->state); + } + else if (!ob->hitnorth) + { + if (ob->bottom >= player->bottom) + { + // jump over + ob->x -= ob->xmove; + ob->y -= ob->ymove; + ob->yspeed = -SPDBOOBUSJUMP; + ob->xspeed = ob->xdir * SPDBOOBUSRUNJUMP; + } + else + { + // drop down + ob->xspeed = ob->yspeed = 0; + } + ChangeState (ob,&s_boobusjump); + } + + PLACESPRITE; +} + + + +/* +==================== += += BoobusAirReact += +==================== +*/ + +void BoobusAirReact (objtype *ob) +{ + if (ob->hitsouth) + ob->yspeed = 0; + + if (ob->hitnorth) + ChangeState (ob,&s_boobuswalk1); + + PLACESPRITE; +} diff --git a/src/lib/hb/kd_def.h b/src/lib/hb/kd_def.h new file mode 100755 index 00000000..c2728a68 --- /dev/null +++ b/src/lib/hb/kd_def.h @@ -0,0 +1,366 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_DEF.H + +#include "ID_HEADS.H" +#include +#include "SOFT.H" +#include "SL_FILE.H" + +#define FRILLS 0 // Cut out frills for 360K - MIKE MAYNARD + + +/* +============================================================================= + + GLOBAL CONSTANTS + +============================================================================= +*/ + +#define CREDITS 0 + +#define MAXACTORS MAXSPRITES + +#define ACCGRAVITY 3 +#define SPDMAXY 80 + +#define BLOCKSIZE (8*PIXGLOBAL) // for positioning starting actors + +// +// movement scrolling edges +// +#define SCROLLEAST (TILEGLOBAL*11) +#define SCROLLWEST (TILEGLOBAL*9) +#define SCROLLSOUTH (TILEGLOBAL*8) +#define SCROLLNORTH (TILEGLOBAL*4) + +#define CLIPMOVE 24 // speed to push out of a solid wall + +#define GAMELEVELS 17 + +/* +============================================================================= + + TYPES + +============================================================================= +*/ + +typedef enum {notdone,resetgame,levelcomplete,warptolevel,died,victorious} + exittype; + +typedef enum {nothing,keenobj,powerobj,doorobj, + bonusobj,broccoobj,tomatobj,carrotobj,celeryobj,asparobj,grapeobj, + taterobj,cartobj,frenchyobj,melonobj,turnipobj,cauliobj,brusselobj, + mushroomobj,squashobj,apelobj,peapodobj,peabrainobj,boobusobj, + shotobj,inertobj} classtype; + +typedef struct +{ + int leftshapenum,rightshapenum; + enum {step,slide,think,stepthink,slidethink} progress; + boolean skippable; + + boolean pushtofloor; + int tictime; + int xmove; + int ymove; + void (*think) (); + void (*contact) (); + void (*react) (); + void *nextstate; +} statetype; + + +typedef struct +{ + unsigned worldx,worldy; + boolean leveldone[GAMELEVELS]; + long score,nextextra; + int flowerpowers; + int boobusbombs,bombsthislevel; + int keys; + int mapon; + int lives; + int difficulty; +} gametype; + + +typedef struct objstruct +{ + classtype obclass; + enum {no,yes,allways,removable} active; + boolean needtoreact,needtoclip; + unsigned nothink; + unsigned x,y; + + int xdir,ydir; + int xmove,ymove; + int xspeed,yspeed; + + int ticcount,ticadjust; + statetype *state; + + unsigned shapenum; + + unsigned left,top,right,bottom; // hit rectangle + unsigned midx; + unsigned tileleft,tiletop,tileright,tilebottom; // hit rect in tiles + unsigned tilemidx; + + int hitnorth,hiteast,hitsouth,hitwest; // wall numbers contacted + + int temp1,temp2,temp3,temp4; + + void *sprite; + + struct objstruct *next,*prev; +} objtype; + + +struct BitMapHeader { + unsigned int w,h,x,y; + unsigned char d,trans,comp,pad; +}; + +struct BitMap { + unsigned int Width; + unsigned int Height; + unsigned int Depth; + unsigned int BytesPerRow; + char far *Planes[8]; +}; + +struct Shape { + memptr Data; + long size; + unsigned int BPR; + struct BitMapHeader bmHdr; +}; + +typedef struct { + int handle; // handle of file + memptr buffer; // pointer to buffer + word offset; // offset into buffer + word status; // read/write status +} BufferedIO; + + + +/* +============================================================================= + + KD_MAIN.C DEFINITIONS + +============================================================================= +*/ + +extern char str[80],str2[20]; +extern boolean singlestep,jumpcheat,godmode,tedlevel; +extern unsigned tedlevelnum; + +void DebugMemory (void); +void TestSprites(void); +int DebugKeys (void); +void StartupId (void); +void ShutdownId (void); +void Quit (char *error); +void InitGame (void); +void main (void); + + +/* +============================================================================= + + KD_DEMO.C DEFINITIONS + +============================================================================= +*/ + +void Finale (void); +void GameOver (void); +void DemoLoop (void); +void StatusWindow (void); +void NewGame (void); +void TEDDeath (void); + +boolean LoadGame (int file); +boolean SaveGame (int file); +void ResetGame (void); + +/* +============================================================================= + + KD_PLAY.C DEFINITIONS + +============================================================================= +*/ + +extern gametype gamestate; +extern exittype playstate; +extern boolean button0held,button1held; +extern unsigned originxtilemax,originytilemax; +extern objtype *new,*check,*player,*scoreobj; + +extern ControlInfo c; + +extern objtype dummyobj; + +extern char *levelnames[21]; + +void CheckKeys (void); +void CalcInactivate (void); +void InitObjArray (void); +void GetNewObj (boolean usedummy); +void RemoveObj (objtype *gone); +void ScanInfoPlane (void); +void PatchWorldMap (void); +void MarkTileGraphics (void); +void FadeAndUnhook (void); +void SetupGameLevel (boolean loadnow); +void ScrollScreen (void); +void MoveObjVert (objtype *ob, int ymove); +void MoveObjHoriz (objtype *ob, int xmove); +void GivePoints (unsigned points); +void ClipToEnds (objtype *ob); +void ClipToEastWalls (objtype *ob); +void ClipToWestWalls (objtype *ob); +void ClipToWalls (objtype *ob); +void ClipToSprite (objtype *push, objtype *solid, boolean squish); +void ClipToSpriteSide (objtype *push, objtype *solid); +int DoActor (objtype *ob,int tics); +void StateMachine (objtype *ob); +void NewState (objtype *ob,statetype *state); +void PlayLoop (void); +void GameLoop (void); + +/* +============================================================================= + + KD_KEEN.C DEFINITIONS + +============================================================================= +*/ + +void CalcSingleGravity (void); + +void ProjectileThink (objtype *ob); +void VelocityThink (objtype *ob); +void DrawReact (objtype *ob); + +void SpawnScore (void); +void FixScoreBox (void); +void SpawnWorldKeen (int tilex, int tiley); +void SpawnKeen (int tilex, int tiley, int dir); + +void KillKeen (void); + +extern int singlegravity; +extern unsigned bounceangle[8][8]; + +extern statetype s_keendie1; + +/* +============================================================================= + + KD_ACT1.C DEFINITIONS + +============================================================================= +*/ + +void WalkReact (objtype *ob); + +void DoGravity (objtype *ob); +void AccelerateX (objtype *ob,int dir,int max); +void FrictionX (objtype *ob); + +void ProjectileThink (objtype *ob); +void VelocityThink (objtype *ob); +void DrawReact (objtype *ob); +void DrawReact2 (objtype *ob); +void DrawReact3 (objtype *ob); +void ChangeState (objtype *ob, statetype *state); + +void ChangeToFlower (objtype *ob); + +void SpawnBonus (int tilex, int tiley, int type); +void SpawnDoor (int tilex, int tiley); +void SpawnBrocco (int tilex, int tiley); +void SpawnTomat (int tilex, int tiley); +void SpawnCarrot (int tilex, int tiley); +void SpawnAspar (int tilex, int tiley); +void SpawnGrape (int tilex, int tiley); + +extern statetype s_doorraise; + +extern statetype s_bonus; +extern statetype s_bonusrise; + +extern statetype s_broccosmash3; +extern statetype s_broccosmash4; + +extern statetype s_grapefall; + +/* +============================================================================= + + KD_ACT2.C DEFINITIONS + +============================================================================= +*/ + +void SpawnTater (int tilex, int tiley); +void SpawnCart (int tilex, int tiley); +void SpawnFrenchy (int tilex, int tiley); +void SpawnMelon (int tilex, int tiley,int dir); +void SpawnSquasher (int tilex, int tiley); +void SpawnApel (int tilex, int tiley); +void SpawnPeaPod (int tilex, int tiley); +void SpawnPeaBrain (int tilex, int tiley); +void SpawnBoobus (int tilex, int tiley); + +extern statetype s_taterattack2; +extern statetype s_squasherjump2; +extern statetype s_boobusdie; + +extern statetype s_deathwait1; +extern statetype s_deathwait2; +extern statetype s_deathwait3; +extern statetype s_deathboom1; +extern statetype s_deathboom2; + + +///////////////////////////////////////////////////////////////////////////// +// +// GELIB.C DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////// + +void FreeShape(struct Shape *shape); +int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty); + +long Verify(char *filename); +memptr InitBufferedIO(int handle, BufferedIO *bio); +void FreeBufferedIO(BufferedIO *bio); +byte bio_readch(BufferedIO *bio); +void bio_fillbuffer(BufferedIO *bio); +void SwapLong(long far *Var); +void SwapWord(unsigned int far *Var); +void MoveGfxDst(short x, short y); \ No newline at end of file diff --git a/src/lib/hb/kd_demo.c b/src/lib/hb/kd_demo.c new file mode 100755 index 00000000..4469a924 --- /dev/null +++ b/src/lib/hb/kd_demo.c @@ -0,0 +1,545 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_DEMO.C + +#include +#include "KD_DEF.H" + +#pragma hdrstop + +#define RLETAG 0xABCD + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +void NewGame (void) +{ + word i; + + gamestate.worldx = 0; // spawn keen at starting spot + + gamestate.mapon = 0; + gamestate.score = 0; + gamestate.nextextra = 20000; + gamestate.lives = 3; + gamestate.flowerpowers = gamestate.boobusbombs = 0; + for (i = 0;i < GAMELEVELS;i++) + gamestate.leveldone[i] = false; +} + +//=========================================================================== + +/* +===================== += += WaitOrKey += +===================== +*/ + +int WaitOrKey (int vbls) +{ + while (vbls--) + { + IN_ReadControl(0,&c); // get player input + if (LastScan || c.button0 || c.button1) + { + IN_ClearKeysDown (); + return 1; + } + VW_WaitVBL(1); + } + return 0; +} + +//=========================================================================== + +/* +===================== += += GameOver += +===================== +*/ + +void +GameOver (void) +{ + VW_InitDoubleBuffer (); + US_CenterWindow (16,3); + + US_PrintCentered("Game Over!"); + + VW_UpdateScreen (); + IN_ClearKeysDown (); + IN_Ack (); + +} + + +//=========================================================================== + +/* +================== += += StatusWindow += +================== +*/ + +void StatusWindow (void) +{ + word x; + + // DEBUG - make this look better + + US_CenterWindow(22,7); + US_CPrint("Status Window"); + + WindowX += 8; + WindowW -= 8; + WindowY += 20; + WindowH -= 20; + PrintX = WindowX; + PrintY = WindowY; + + VWB_DrawTile8(PrintX,PrintY,26); + VWB_DrawTile8(PrintX + 8,PrintY,27); + PrintX += 24; + US_PrintUnsigned(gamestate.lives); + US_Print("\n"); + + VWB_DrawTile8(PrintX,PrintY,32); + VWB_DrawTile8(PrintX + 8,PrintY,33); + VWB_DrawTile8(PrintX,PrintY + 8,34); + VWB_DrawTile8(PrintX + 8,PrintY + 8,35); + PrintX += 24; + US_PrintUnsigned(gamestate.boobusbombs); + US_Print("\n"); + + WindowX += 50; + WindowW -= 50; + PrintX = WindowX; + PrintY = WindowY; + + fontcolor = F_FIRSTCOLOR; + US_Print("Next "); + fontcolor = F_BLACK; + x = PrintX; + VWB_DrawTile8(PrintX,PrintY,26); + VWB_DrawTile8(PrintX + 8,PrintY,27); + PrintX += 24; + US_PrintUnsigned(gamestate.nextextra); + US_Print("\n"); + + PrintX = x; + VWB_DrawTile8(PrintX,PrintY,24); + VWB_DrawTile8(PrintX + 8,PrintY,25); + PrintX += 24; + US_PrintUnsigned(gamestate.keys); + US_Print("\n"); + + // DEBUG - add flower powers (#36) + + VW_UpdateScreen(); + IN_Ack(); +} + +boolean +SaveGame(int file) +{ + word i,size,compressed,expanded; + objtype *o; + memptr bigbuffer; + + if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i++) // Write all three planes of the map + { +// +// leave a word at start of compressed data for compressed length +// + compressed = CA_RLEWCompress ((unsigned huge *)mapsegs[i] + ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG); + + *(unsigned huge *)bigbuffer = compressed; + + if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + } + + for (o = player;o;o = o->next) + if (!CA_FarWrite(file,(void far *)o,sizeof(objtype))) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + MM_FreePtr (&bigbuffer); + return(true); +} + + +boolean +LoadGame(int file) +{ + word i,j,size; + objtype *o; + int orgx,orgy; + objtype *prev,*next,*followed; + unsigned compressed,expanded; + memptr bigbuffer; + + if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate))) + return(false); + +// drop down a cache level and mark everything, so when the option screen +// is exited it will be cached + + ca_levelbit >>= 1; + ca_levelnum--; + + SetupGameLevel (false); // load in and cache the base old level + titleptr[ca_levelnum] = levelnames[mapon]; + + ca_levelbit <<= 1; + ca_levelnum ++; + + expanded = mapwidth * mapheight * 2; + MM_GetPtr (&bigbuffer,expanded); + + for (i = 0;i < 3;i++) // Read all three planes of the map + { + if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + if (!CA_FarRead(file,(void far *)bigbuffer,compressed) ) + { + MM_FreePtr (&bigbuffer); + return(false); + } + + CA_RLEWexpand ((unsigned huge *)bigbuffer, + (unsigned huge *)mapsegs[i],compressed,RLETAG); + } + + MM_FreePtr (&bigbuffer); + + // Read the object list back in - assumes at least one object in list + + InitObjArray (); + new = player; + prev = new->prev; + next = new->next; + if (!CA_FarRead(file,(void far *)new,sizeof(objtype))) + return(false); + new->prev = prev; + new->next = next; + new->needtoreact = true; + new->sprite = NULL; + new = scoreobj; + while (true) + { + prev = new->prev; + next = new->next; + if (!CA_FarRead(file,(void far *)new,sizeof(objtype))) + return(false); + followed = new->next; + new->prev = prev; + new->next = next; + new->needtoreact = true; + new->sprite = NULL; + + if (followed) + GetNewObj (false); + else + break; + } + + *((long *)&(scoreobj->temp1)) = -1; // force score to be updated + scoreobj->temp3 = -1; // and flower power + scoreobj->temp4 = -1; // and lives + + return(true); +} + +void +ResetGame(void) +{ + NewGame (); + + ca_levelnum--; + CA_ClearMarks(); + titleptr[ca_levelnum] = NULL; // don't reload old level + ca_levelnum++; +} + +#if FRILLS +void +TEDDeath(void) +{ + ShutdownId(); + execlp("TED5.EXE","TED5.EXE","/LAUNCH","KDREAMS",NULL); +} +#endif + +static boolean +MoveTitleTo(int offset) +{ + boolean done; + int dir, + chunk, + move; + longword lasttime,delay; + + if (offset < originxglobal) + dir = -1; + else + dir = +1; + + chunk = dir * PIXGLOBAL; + + done = false; + delay = 1; + while (!done) + { + lasttime = TimeCount; + move = delay * chunk; + if (chunk < 0) + done = originxglobal + move <= offset; + else + done = originxglobal + move >= offset; + if (!done) + { + RF_Scroll(move,0); + RF_Refresh(); + } + if (IN_IsUserInput()) + return(true); + delay = TimeCount - lasttime; + } + if (originxglobal != offset) + { + RF_Scroll(offset - originxglobal,0); + RF_Refresh(); + } + return(false); +} + +static boolean +Wait(longword time) +{ + time += TimeCount; + while ((TimeCount < time) && (!IN_IsUserInput())) + { + if (!(TimeCount % MINTICS)) + RF_Refresh(); + } + return(IN_IsUserInput()); +} + +static boolean +ShowText(int offset,WindowRec *wr,char *s) +{ + if (MoveTitleTo(offset)) + return(true); + + US_RestoreWindow(wr); + US_CPrint(s); + VW_UpdateScreen(); + + if (Wait(TickBase * 5)) + return(true); + + US_RestoreWindow(wr); + US_CPrint(s); + VW_UpdateScreen(); + return(false); +} + +/* +===================== += += DemoLoop += +===================== +*/ + +void +DemoLoop (void) +{ + char *s; + word move; + longword lasttime; + char *FileName1; + struct Shape FileShape1; +#if CREDITS + char *FileName2; + struct Shape FileShape2; +#endif + struct ffblk ffblk; + WindowRec mywin; + int bufsave = bufferofs; + int dissave = displayofs; + + +#if FRILLS +// +// check for launch from ted +// + if (tedlevel) + { + NewGame(); + gamestate.mapon = tedlevelnum; + GameLoop(); + TEDDeath(); + } +#endif + +// +// demo loop +// + US_SetLoadSaveHooks(LoadGame,SaveGame,ResetGame); + restartgame = gd_Continue; + + if (findfirst("KDREAMS.CMP", &ffblk, 0) == -1) + Quit("Couldn't find KDREAMS.CMP"); + + while (true) + { + + loadedgame = false; + + FileName1 = "TITLESCR.LBM"; + if (LoadLIBShape("KDREAMS.CMP", FileName1, &FileShape1)) + Quit("Can't load TITLE SCREEN"); +#if CREDITS + FileName2 = "CREDITS.LBM"; + if (LoadLIBShape("KDREAMS.CMP", FileName2, &FileShape2)) + Quit("Can't load CREDITS SCREEN"); +#endif + + while (!restartgame && !loadedgame) + { + + VW_InitDoubleBuffer(); + IN_ClearKeysDown(); + + while (true) + { + + VW_SetScreen(0, 0); + MoveGfxDst(0, 200); + UnpackEGAShapeToScreen(&FileShape1, 0, 0); + VW_ScreenToScreen (64*200,0,40,200); + +#if CREDITS + if (IN_UserInput(TickBase * 8, false)) + break; +#else + if (IN_UserInput(TickBase * 4, false)) + break; +#endif + +#if CREDITS + MoveGfxDst(0, 200); + UnpackEGAShapeToScreen(&FileShape2, 0, 0); + VW_ScreenToScreen (64*200,0,40,200); + + if (IN_UserInput(TickBase * 7, false)) + break; +#else + MoveGfxDst(0, 200); + UnpackEGAShapeToScreen(&FileShape1, 0, 0); + VW_ScreenToScreen (64*200,0,40,200); + + if (IN_UserInput(TickBase * 3, false)) + break; +#endif + + displayofs = 0; + VWB_Bar(0,0,320,200,FIRSTCOLOR); + US_DisplayHighScores(-1); + + if (IN_UserInput(TickBase * 6, false)) + break; + + } + + bufferofs = bufsave; + displayofs = dissave; + + VW_FixRefreshBuffer(); + US_ControlPanel (); + } + + if (!loadedgame) + NewGame(); + + FreeShape(&FileShape1); +#if CREDITS + FreeShape(&FileShape2); +#endif + GameLoop(); + } +} diff --git a/src/lib/hb/kd_keen.c b/src/lib/hb/kd_keen.c new file mode 100755 index 00000000..a1349317 --- /dev/null +++ b/src/lib/hb/kd_keen.c @@ -0,0 +1,2528 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_KEEN.C + +#include "KD_DEF.H" +#pragma hdrstop + +/* + +player->temp1 = pausetime / pointer to zees when sleeping +player->temp2 = pausecount / stagecount +player->temp3 = +player->temp4 = + + +*/ + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define KEENPRIORITY 1 + +#define PLACESPRITE RF_PlaceSprite (&ob->sprite,ob->x,ob->y,ob->shapenum, \ + spritedraw,KEENPRIORITY); + +#define MINPOLEJUMPTICS 19 // wait tics before allowing a pole regram + +#define SPDRUNJUMP 16 +#define SPDPOLESIDEJUMP 8 +#define WALKAIRSPEED 8 +#define DIVESPEED 32 +#define JUMPTIME 16 +#define POLEJUMPTIME 10 +#define SPDJUMP 40 +#define SPDDIVEUP 16 +#define SPDPOLEJUMP 20 + +#define SPDPOWERUP -64 +#define SPDPOWER 64 +#define SPDPOWERY -20 +#define POWERCOUNT 50 + +#define MAXXSPEED 24 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +int singlegravity; +unsigned bounceangle[8][8] = +{ +{0,0,0,0,0,0,0,0}, +{7,6,5,4,3,2,1,0}, +{5,4,3,2,1,0,15,14}, +{5,4,3,2,1,0,15,14}, +{3,2,1,0,15,14,13,12}, +{9,8,7,6,5,4,3,2}, +{9,8,7,6,5,4,3,2}, +{11,10,9,8,7,6,5,4} +}; + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +int jumptime; +long leavepoletime; // TimeCount when jumped off pole + +/* +============================================================================= + + SCORE BOX ROUTINES + +============================================================================= +*/ + +void SpawnScore (void); +void ScoreThink (objtype *ob); +void ScoreReact (objtype *ob); + +void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize); + +statetype s_score = {NULL,NULL,think,false, + false,0, 0,0, ScoreThink , NULL, ScoreReact, NULL}; + + +/* +====================== += += SpawnScore += +====================== +*/ + +void SpawnScore (void) +{ + scoreobj->obclass = inertobj; + scoreobj->active = allways; + scoreobj->needtoclip = false; + *((long *)&(scoreobj->temp1)) = -1; // force score to be updated + scoreobj->temp3 = -1; // and flower power + scoreobj->temp4 = -1; // and lives + NewState (scoreobj,&s_score); +} + + +void FixScoreBox (void) +{ + unsigned width, planesize; + unsigned smallplane,bigplane; + spritetype _seg *block; + byte far *dest; + +// draw boobus bomb if on level 15, else flower power + block = (spritetype _seg *)grsegs[SCOREBOXSPR]; + width = block->width[0]; + planesize = block->planesize[0]; + dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0] + + planesize + width*16 + 4*CHARWIDTH; + if (mapon == 15) + { + MemDrawChar (20,dest,width,planesize); + MemDrawChar (21,dest+CHARWIDTH,width,planesize); + MemDrawChar (22,dest+width*8,width,planesize); + MemDrawChar (23,dest+width*8+CHARWIDTH,width,planesize); + } + else + { + MemDrawChar (28,dest,width,planesize); + MemDrawChar (29,dest+CHARWIDTH,width,planesize); + MemDrawChar (30,dest+width*8,width,planesize); + MemDrawChar (31,dest+width*8+CHARWIDTH,width,planesize); + } + +} + + +/* +====================== += += MemDrawChar += +====================== +*/ + +#if GRMODE == EGAGR + +void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize) +{ +asm mov si,[char8] +asm shl si,1 +asm shl si,1 +asm shl si,1 +asm shl si,1 +asm shl si,1 // index into char 8 segment + +asm mov ds,[WORD PTR grsegs+STARTTILE8*2] +asm mov es,[WORD PTR dest+2] + +asm mov cx,4 // draw four planes +asm mov bx,[width] +asm dec bx + +planeloop: + +asm mov di,[WORD PTR dest] + +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb +asm add di,bx +asm movsb + +asm mov ax,[planesize] +asm add [WORD PTR dest],ax + +asm loop planeloop + +asm mov ax,ss +asm mov ds,ax + +} +#endif + +#if GRMODE == CGAGR +void MemDrawChar (int char8,byte far *dest,unsigned width,unsigned planesize) +{ +asm mov si,[char8] +asm shl si,1 +asm shl si,1 +asm shl si,1 +asm shl si,1 // index into char 8 segment + +asm mov ds,[WORD PTR grsegs+STARTTILE8*2] +asm mov es,[WORD PTR dest+2] + +asm mov bx,[width] +asm sub bx,2 + +asm mov di,[WORD PTR dest] + +asm movsw +asm add di,bx +asm movsw +asm add di,bx +asm movsw +asm add di,bx +asm movsw +asm add di,bx +asm movsw +asm add di,bx +asm movsw +asm add di,bx +asm movsw +asm add di,bx +asm movsw + +asm mov ax,ss +asm mov ds,ax + + planesize++; // shut the compiler up +} +#endif + + +/* +==================== += += ShiftScore += +==================== +*/ +#if GRMODE == EGAGR +void ShiftScore (void) +{ + spritetabletype far *spr; + spritetype _seg *dest; + + spr = &spritetable[SCOREBOXSPR-STARTSPRITES]; + dest = (spritetype _seg *)grsegs[SCOREBOXSPR]; + + CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0], + dest->sourceoffset[1],spr->width,spr->height,2); + + CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0], + dest->sourceoffset[2],spr->width,spr->height,4); + + CAL_ShiftSprite (FP_SEG(dest),dest->sourceoffset[0], + dest->sourceoffset[3],spr->width,spr->height,6); +} +#endif + +/* +=============== += += ScoreThink += +=============== +*/ + +void ScoreThink (objtype *ob) +{ + char str[10],*ch; + spritetype _seg *block; + byte far *dest; + unsigned i, length, width, planesize, number; + +// +// score changed +// + if ((gamestate.score>>16) != ob->temp1 + || (unsigned)gamestate.score != ob->temp2 ) + { + block = (spritetype _seg *)grsegs[SCOREBOXSPR]; + width = block->width[0]; + planesize = block->planesize[0]; + dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0] + + planesize + width*4 + 1*CHARWIDTH; + + ltoa (gamestate.score,str,10); + + // erase leading spaces + length = strlen(str); + for (i=6;i>length;i--) + MemDrawChar (0,dest+=CHARWIDTH,width,planesize); + + // draw digits + ch = str; + while (*ch) + MemDrawChar (*ch++ - '0'+1,dest+=CHARWIDTH,width,planesize); + +#if GRMODE == EGAGR + ShiftScore (); +#endif + ob->needtoreact = true; + ob->temp1 = gamestate.score>>16; + ob->temp2 = gamestate.score; + } + +// +// flower power changed +// + if (mapon == 15) + number = gamestate.boobusbombs; + else + number = gamestate.flowerpowers; + if (number != ob->temp3) + { + block = (spritetype _seg *)grsegs[SCOREBOXSPR]; + width = block->width[0]; + planesize = block->planesize[0]; + dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0] + + planesize + width*20 + 5*CHARWIDTH; + + if (number > 99) + strcpy (str,"99"); + else + ltoa (number,str,10); + + // erase leading spaces + length = strlen(str); + for (i=2;i>length;i--) + MemDrawChar (0,dest+=CHARWIDTH,width,planesize); + + // draw digits + ch = str; + while (*ch) + MemDrawChar (*ch++ - '0'+1,dest+=CHARWIDTH,width,planesize); + +#if GRMODE == EGAGR + ShiftScore (); +#endif + ob->needtoreact = true; + ob->temp3 = gamestate.flowerpowers; + } + +// +// lives changed +// + if (gamestate.lives != ob->temp4) + { + block = (spritetype _seg *)grsegs[SCOREBOXSPR]; + width = block->width[0]; + planesize = block->planesize[0]; + dest = (byte far *)grsegs[SCOREBOXSPR]+block->sourceoffset[0] + + planesize + width*20 + 2*CHARWIDTH; + + if (gamestate.lives>9) + MemDrawChar ('9' - '0'+1,dest,width,planesize); + else + MemDrawChar (gamestate.lives +1,dest,width,planesize); + +#if GRMODE == EGAGR + ShiftScore (); +#endif + ob->needtoreact = true; + ob->temp4 = gamestate.lives; + } + + if (originxglobal != ob->x || originyglobal != ob->y) + ob->needtoreact = true; +} + + +/* +=============== += += ScoreReact += +=============== +*/ + +void ScoreReact (objtype *ob) +{ + ob->x = originxglobal; + ob->y = originyglobal; + +#if GRMODE == EGAGR + RF_PlaceSprite (&ob->sprite + ,ob->x+4*PIXGLOBAL + ,ob->y+4*PIXGLOBAL + ,SCOREBOXSPR + ,spritedraw + ,PRIORITIES-1); +#endif +#if GRMODE == CGAGR + RF_PlaceSprite (&ob->sprite + ,ob->x+8*PIXGLOBAL + ,ob->y+8*PIXGLOBAL + ,SCOREBOXSPR + ,spritedraw + ,PRIORITIES-1); +#endif +} + + +/* +============================================================================= + + FLOWER POWER ROUTINES + +temp1 = 8, same as power bonus +temp2 = initial direction + +============================================================================= +*/ + +void PowerCount (objtype *ob); +void PowerContact (objtype *ob, objtype *hit); +void PowerReact (objtype *ob); + +extern statetype s_flowerpower1; +extern statetype s_flowerpower2; + +extern statetype s_boobusbomb1; +extern statetype s_boobusbomb2; + +extern statetype s_bombexplode; +extern statetype s_bombexplode2; +extern statetype s_bombexplode3; +extern statetype s_bombexplode4; +extern statetype s_bombexplode5; +extern statetype s_bombexplode6; + +extern statetype s_powerblink1; +extern statetype s_powerblink2; + +statetype s_flowerpower1 = {FLOWERPOWER1SPR,FLOWERPOWER1SPR,stepthink,false, + false,10, 0,0, ProjectileThink, PowerContact, PowerReact, &s_flowerpower2}; +statetype s_flowerpower2 = {FLOWERPOWER2SPR,FLOWERPOWER2SPR,stepthink,false, + false,10, 0,0, ProjectileThink, PowerContact, PowerReact, &s_flowerpower1}; + +statetype s_boobusbomb1 = {BOOBUSBOMB1SPR,BOOBUSBOMB1SPR,stepthink,false, + false,5, 0,0, ProjectileThink, PowerContact, PowerReact, &s_boobusbomb2}; +statetype s_boobusbomb2 = {BOOBUSBOMB3SPR,BOOBUSBOMB3SPR,stepthink,false, + false,5, 0,0, ProjectileThink, PowerContact, PowerReact, &s_boobusbomb1}; + +statetype s_bombexplode = {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false, + false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode2}; +statetype s_bombexplode2= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false, + false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode3}; +statetype s_bombexplode3= {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false, + false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode4}; +statetype s_bombexplode4= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false, + false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode5}; +statetype s_bombexplode5= {BOOBUSBOOM1SPR,BOOBUSBOOM1SPR,step,false, + false,5, 0,0, NULL, NULL, DrawReact, &s_bombexplode6}; +statetype s_bombexplode6= {BOOBUSBOOM2SPR,BOOBUSBOOM2SPR,step,false, + false,5, 0,0, NULL, NULL, DrawReact, NULL}; + +statetype s_powerblink1 = {FLOWERPOWER1SPR,FLOWERPOWER1SPR,step,false, + false,5, 0,0, PowerCount, NULL, DrawReact, &s_powerblink2}; +statetype s_powerblink2 = {-1,-1,step,false, + false,5, 0,0, PowerCount, NULL, DrawReact, &s_powerblink1}; + + +/* +=============== += += ThrowPower += +=============== +*/ + +void ThrowPower (unsigned x, unsigned y, int dir) +{ + statetype *startstate; + + if (mapon == 15) + { + if (!gamestate.boobusbombs) + { + SD_PlaySound (NOWAYSND); + return; // no bombs to throw + } + SD_PlaySound (THROWBOMBSND); + gamestate.bombsthislevel--; + gamestate.boobusbombs--; + } + else + { + if (!gamestate.flowerpowers) + { + SD_PlaySound (NOWAYSND); + return; // no flower power to throw + } + SD_PlaySound (THROWSND); + gamestate.flowerpowers--; + } + + + + GetNewObj (true); + new->obclass = powerobj; + new->temp2 = dir; + new->x = x; + new->y = y; + new->tileleft = new->tileright = x>>G_T_SHIFT; + new->tiletop = new->tilebottom = y>>G_T_SHIFT; + new->ydir = -1; + + switch (dir) + { + case dir_North: + new->xspeed = 0; + new->yspeed = SPDPOWERUP; + break; + case dir_East: + new->xspeed = SPDPOWER; + new->yspeed = SPDPOWERY; + break; + case dir_South: + new->xspeed = 0; + new->yspeed = SPDPOWER; + break; + case dir_West: + new->xspeed = -SPDPOWER; + new->yspeed = SPDPOWERY; + break; + default: + Quit ("ThrowPower: Bad dir!"); + } + + if (mapon != 15) + { + new->temp1 = 8; // flower power bonus + startstate = &s_flowerpower1; + } + else + { + new->temp1 = 10; // boobus bomb bonus + startstate = &s_boobusbomb1; + } + new->active = removable; + +#if 0 + new->x -= 5*new->xspeed; // make sure they hit nearby walls + new->y -= 5*new->yspeed; +#endif + NewState (new,startstate); +#if 0 + new->xmove = 5*new->xspeed; + new->ymove = 5*new->yspeed; + ClipToWalls (new); +#endif +} + +/* +============================ += += PowerCount += +============================ +*/ + +void PowerCount (objtype *ob) +{ + ob->temp2+=tics; + + ob->shapenum = 0; + + if (ob->temp2 > POWERCOUNT) + RemoveObj(ob); +} + + +/* +============================ += += CalcSingleGravity += +============================ +*/ + +void CalcSingleGravity (void) +{ + unsigned speed; + long i; +// +// only accelerate on odd tics, because of limited precision +// + speed = 0; + singlegravity = 0; + for (i=lasttimecount-tics;iSPDMAXY) + speed=SPDMAXY; + } + singlegravity+=speed; + } + + singlegravity/=2; +} + + +/* +============================ += += PowerContact += +============================ +*/ + +void PowerContact (objtype *ob, objtype *hit) +{ + unsigned x,y,yspot,xspot; + + switch (hit->obclass) + { + case broccoobj: + case tomatobj: + case carrotobj: + case celeryobj: + case asparobj: + case taterobj: + case frenchyobj: + case squashobj: + case apelobj: + case peapodobj: + case peabrainobj: + ChangeToFlower (hit); + RemoveObj (ob); + break; + case boobusobj: + if (!--hit->temp4) + { + // killed boobus + GivePoints (50000); + GivePoints (50000); + hit->obclass = inertobj; + ChangeState (hit,&s_boobusdie); + hit->temp1 = 0; // death count + } + SD_PlaySound (BOMBBOOMSND); + ChangeState (ob,&s_bombexplode); + break; + } +} + + +/* +============================ += += PowerReact += +============================ +*/ + +void PowerReact (objtype *ob) +{ + unsigned wall,absx,absy,angle,newangle; + unsigned long speed; + + PLACESPRITE; + if (ob->hiteast || ob->hitwest) + { + ob->xspeed= -ob->xspeed/2; + ob->obclass = bonusobj; + } + + if (ob->hitsouth) + { + if ( ob->hitsouth == 17) // go through pole holes + { + if (ob->temp2 != dir_North) + ob->obclass = bonusobj; + ob->top-=32; + ob->y-=32; + ob->xspeed = 0; + ob->x = ob->tilemidx*TILEGLOBAL + 4*PIXGLOBAL; + } + else + { + ob->yspeed= -ob->yspeed/2; + } + return; + } + + wall = ob->hitnorth; + if ( wall == 17) // go through pole holes + { + if (ob->temp2 != dir_South) + ob->obclass = bonusobj; + ob->bottom+=32; + ob->y+=32; + ob->xspeed = 0; + ob->x = ob->tilemidx*TILEGLOBAL + 4*PIXGLOBAL; + } + else if (wall) + { + ob->obclass = bonusobj; + if (ob->yspeed < 0) + ob->yspeed = 0; + + absx = abs(ob->xspeed); + absy = ob->yspeed; + if (absx>absy) + { + if (absx>absy*2) // 22 degrees + { + angle = 0; + speed = absx*286; // x*sqrt(5)/2 + } + else // 45 degrees + { + angle = 1; + speed = absx*362; // x*sqrt(2) + } + } + else + { + if (absy>absx*2) // 90 degrees + { + angle = 3; + speed = absy*256; + } + else + { + angle = 2; // 67 degrees + speed = absy*286; // y*sqrt(5)/2 + } + } + if (ob->xspeed > 0) + angle = 7-angle; + + speed >>= 1; + newangle = bounceangle[ob->hitnorth][angle]; + switch (newangle) + { + case 0: + ob->xspeed = speed / 286; + ob->yspeed = -ob->xspeed / 2; + break; + case 1: + ob->xspeed = speed / 362; + ob->yspeed = -ob->xspeed; + break; + case 2: + ob->yspeed = -(speed / 286); + ob->xspeed = -ob->yspeed / 2; + break; + case 3: + + case 4: + ob->xspeed = 0; + ob->yspeed = -(speed / 256); + break; + case 5: + ob->yspeed = -(speed / 286); + ob->xspeed = ob->yspeed / 2; + break; + case 6: + ob->xspeed = ob->yspeed = -(speed / 362); + break; + case 7: + ob->xspeed = -(speed / 286); + ob->yspeed = ob->xspeed / 2; + break; + + case 8: + ob->xspeed = -(speed / 286); + ob->yspeed = -ob->xspeed / 2; + break; + case 9: + ob->xspeed = -(speed / 362); + ob->yspeed = -ob->xspeed; + break; + case 10: + ob->yspeed = speed / 286; + ob->xspeed = -ob->yspeed / 2; + break; + case 11: + + case 12: + ob->xspeed = 0; + ob->yspeed = -(speed / 256); + break; + case 13: + ob->yspeed = speed / 286; + ob->xspeed = ob->yspeed / 2; + break; + case 14: + ob->xspeed = speed / 362; + ob->yspeed = speed / 362; + break; + case 15: + ob->xspeed = speed / 286; + ob->yspeed = ob->xspeed / 2; + break; + } + + if (speed < 256*16) + { + if (mapon == 15) + { + ob->ydir = -1; // bonus stuff flies up when touched + ob->temp2 = ob->shapenum = BOOBUSBOMB1SPR; + ob->temp3 = ob->temp2 + 2; + ob->needtoclip = 0; + ChangeState (ob,&s_bonus); + } + else + { + ChangeState (ob,&s_powerblink1); + ob->needtoclip = 0; + ob->temp2 = 0; // blink time + } + } + } +} + + +/* +============================================================================= + + MINI KEEN + +player->temp1 = dir +player->temp2 = animation stage + +#define WORLDKEENSLEEP1SPR 238 +#define WORLDKEENSLEEP2SPR 239 +#define WORLDKEENWAVE1SPR 240 +#define WORLDKEENWAVE2SPR 241 +============================================================================= +*/ + +void SpawnWorldKeen (int tilex, int tiley); +void KeenWorldThink (objtype *ob); +void KeenWorldWalk (objtype *ob); + +extern statetype s_worldkeen; + +extern statetype s_worldkeenwave1; +extern statetype s_worldkeenwave2; +extern statetype s_worldkeenwave3; +extern statetype s_worldkeenwave4; +extern statetype s_worldkeenwave5; + +extern statetype s_worldkeenwait; + +extern statetype s_worldkeensleep1; +extern statetype s_worldkeensleep2; + +extern statetype s_worldwalk; + +#pragma warn -sus + +statetype s_worldkeen = {NULL,NULL,stepthink,false, + false,360, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave1}; + +statetype s_worldkeenwave1= {WORLDKEENWAVE1SPR,WORLDKEENWAVE1SPR,stepthink,false, + false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave2}; +statetype s_worldkeenwave2= {WORLDKEENWAVE2SPR,WORLDKEENWAVE2SPR,stepthink,false, + false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave3}; +statetype s_worldkeenwave3= {WORLDKEENWAVE1SPR,WORLDKEENWAVE1SPR,stepthink,false, + false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave4}; +statetype s_worldkeenwave4= {WORLDKEENWAVE2SPR,WORLDKEENWAVE2SPR,stepthink,false, + false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwave5}; +statetype s_worldkeenwave5= {WORLDKEENWAVE1SPR,WORLDKEENWAVE1SPR,stepthink,false, + false,20, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeenwait}; + +statetype s_worldkeenwait = {WORLDKEEND3SPR,WORLDKEEND3SPR,stepthink,false, + false,400, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeensleep1}; + +statetype s_worldkeensleep1 = {WORLDKEENSLEEP1SPR,WORLDKEENSLEEP1SPR,stepthink,false, + false,30, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeensleep2}; +statetype s_worldkeensleep2 = {WORLDKEENSLEEP2SPR,WORLDKEENSLEEP2SPR,stepthink,false, + false,90, 0,0, KeenWorldThink, NULL, DrawReact, &s_worldkeensleep2}; + +statetype s_worldwalk = {NULL,NULL,slide,false, + false,4, 16,16, KeenWorldWalk, NULL, DrawReact, &s_worldwalk}; + +#pragma warn +sus + +/* +====================== += += SpawnWorldKeen += +====================== +*/ + +void SpawnWorldKeen (int tilex, int tiley) +{ + player->obclass = keenobj; + if (!gamestate.worldx) + { + player->x = tilex<y = tiley<x = gamestate.worldx; + player->y = gamestate.worldy; + } + + player->active = yes; + player->needtoclip = true; + player->xdir = 0; + player->ydir = 0; + player->temp1 = dir_West; + player->temp2 = 3; + player->shapenum = 3+WORLDKEENL1SPR-1; // feet together + NewState (player,&s_worldkeen); +} + + +/* +====================== += += CheckEnterLevel += +====================== +*/ + +void CheckEnterLevel (objtype *ob) +{ + int x,y,tile; + + for (y=ob->tiletop;y<=ob->tilebottom;y++) + for (x=ob->tileleft;x<=ob->tileright;x++) + { + tile = *((unsigned _seg *)mapsegs[2]+mapbwidthtable[y]/2+x); + if (tile >= 3 && tile <=18) + { + // + // enter level! + // + if (tile == 17) + { + if (gamestate.boobusbombs < 12) + { + VW_FixRefreshBuffer (); + US_CenterWindow (26,6); + PrintY+= 8; + US_CPrint ("You can't possibly\n" + "defeat King Boobus Tuber\n" + "with less than 12 bombs!"); + VW_UpdateScreen (); + IN_Ack (); + RF_ForceRefresh (); + return; + } + } + gamestate.worldx = ob->x; + gamestate.worldy = ob->y; + gamestate.mapon = tile-2; + playstate = levelcomplete; + SD_PlaySound (ENTERLEVELSND); + } + } +} + + +/* +====================== += += KeenWorldThink += +====================== +*/ + +void KeenWorldThink (objtype *ob) +{ + if (c.dir!=dir_None) + { + ob->state = &s_worldwalk; + ob->temp2 = 0; + KeenWorldWalk (ob); + } + + if (c.button0 || c.button1) + CheckEnterLevel(ob); +} + + +/* +====================== += += KeenWorldWalk += +====================== +*/ + +int worldshapes[8] = {WORLDKEENU1SPR-1,WORLDKEENUR1SPR-1,WORLDKEENR1SPR-1, + WORLDKEENDR1SPR-1,WORLDKEEND1SPR-1,WORLDKEENDL1SPR-1,WORLDKEENL1SPR-1, + WORLDKEENUL1SPR-1}; + +int worldanims[4] ={2,3,1,3}; + +void KeenWorldWalk (objtype *ob) +{ + ob->xdir = c.xaxis; + ob->ydir = c.yaxis; + + if (++ob->temp2==4) + ob->temp2 = 0; + + if (c.button0 || c.button1) + CheckEnterLevel(ob); + + if (c.dir == dir_None) + { + ob->state = &s_worldkeen; + ob->shapenum = worldshapes[ob->temp1]+3; + return; + } + + ob->temp1 = c.dir; + ob->shapenum = worldshapes[ob->temp1]+worldanims[ob->temp2]; +} + + +/* +============================================================================= + + KEEN + +player->temp1 = pausetime / pointer to zees when sleeping +player->temp2 = pausecount / stagecount +player->temp3 = +player->temp4 = pole x location + +============================================================================= +*/ + +void KeenStandThink (objtype *ob); +void KeenPauseThink (objtype *ob); +void KeenGoSleepThink (objtype *ob); +void KeenSleepThink (objtype *ob); +void KeenDieThink (objtype *ob); +void KeenDuckThink (objtype *ob); +void KeenDropDownThink (objtype *ob); +void KeenWalkThink (objtype *ob); +void KeenAirThink (objtype *ob); +void KeenPoleThink (objtype *ob); +void KeenClimbThink (objtype *ob); +void KeenDropThink (objtype *ob); +void KeenDive (objtype *ob); +void KeenThrow (objtype *ob); + +void KeenContact (objtype *ob, objtype *hit); +void KeenSimpleReact (objtype *ob); +void KeenStandReact (objtype *ob); +void KeenWalkReact (objtype *ob); +void KeenPoleReact (objtype *ob); +void KeenAirReact (objtype *ob); +void KeenDiveReact (objtype *ob); +void KeenSlideReact (objtype *ob); + +//------------------- + +extern statetype s_keenzee1; +extern statetype s_keenzee2; +extern statetype s_keenzee3; +extern statetype s_keenzee4; +extern statetype s_keenzee5; + +//------------------- + +extern statetype s_keenstand; +extern statetype s_keenpauselook; +extern statetype s_keenyawn1; +extern statetype s_keenyawn2; +extern statetype s_keenyawn3; +extern statetype s_keenyawn4; + +extern statetype s_keenwait1; +extern statetype s_keenwait2; +extern statetype s_keenwait3; +extern statetype s_keenwait4; +extern statetype s_keenwait5; +extern statetype s_keenwait6; + +extern statetype s_keenmoon1; +extern statetype s_keenmoon2; +extern statetype s_keenmoon3; + +extern statetype s_keengosleep1; +extern statetype s_keengosleep2; +extern statetype s_keensleep1; +extern statetype s_keensleep2; + +extern statetype s_keendie1; +extern statetype s_keendie2; +extern statetype s_keendie3; +extern statetype s_keendie4; + +extern statetype s_keenlookup; +extern statetype s_keenduck; +extern statetype s_keendrop; + +//------------------- + +extern statetype s_keenreach; + +extern statetype s_keenpole; + +extern statetype s_keenclimb1; +extern statetype s_keenclimb2; +extern statetype s_keenclimb3; + +extern statetype s_keenslide1; +extern statetype s_keenslide2; +extern statetype s_keenslide3; +extern statetype s_keenslide4; + +extern statetype s_keenpolethrow1; +extern statetype s_keenpolethrow2; +extern statetype s_keenpolethrow3; + +extern statetype s_keenpolethrowup1; +extern statetype s_keenpolethrowup2; +extern statetype s_keenpolethrowup3; + +extern statetype s_keenpolethrowdown1; +extern statetype s_keenpolethrowdown2; +extern statetype s_keenpolethrowdown3; + +//------------------- + +extern statetype s_keenwalk1; +extern statetype s_keenwalk2; +extern statetype s_keenwalk3; +extern statetype s_keenwalk4; + +extern statetype s_keenthrow1; +extern statetype s_keenthrow2; +extern statetype s_keenthrow3; +extern statetype s_keenthrow4; + +extern statetype s_keenthrowup1; +extern statetype s_keenthrowup2; +extern statetype s_keenthrowup3; + +extern statetype s_keendive1; +extern statetype s_keendive2; +extern statetype s_keendive3; + +//------------------- + +extern statetype s_keenjumpup1; +extern statetype s_keenjumpup2; +extern statetype s_keenjumpup3; + +extern statetype s_keenjump1; +extern statetype s_keenjump2; +extern statetype s_keenjump3; + +extern statetype s_keenairthrow1; +extern statetype s_keenairthrow2; +extern statetype s_keenairthrow3; + +extern statetype s_keenairthrowup1; +extern statetype s_keenairthrowup2; +extern statetype s_keenairthrowup3; + +extern statetype s_keenairthrowdown1; +extern statetype s_keenairthrowdown2; +extern statetype s_keenairthrowdown3; + +#pragma warn -sus + +//------------------- + +statetype s_keenzee1 = {KEENZEES1SPR,KEENZEES1SPR,step,false, + false,30, 0,0, NULL, NULL, DrawReact, &s_keenzee2}; +statetype s_keenzee2 = {KEENZEES2SPR,KEENZEES2SPR,step,false, + false,30, 0,0, NULL, NULL, DrawReact, &s_keenzee3}; +statetype s_keenzee3 = {KEENZEES3SPR,KEENZEES3SPR,step,false, + false,30, 0,0, NULL, NULL, DrawReact, &s_keenzee1}; + +//------------------- + +statetype s_keenstand = {KEENSTANDLSPR,KEENSTANDRSPR,stepthink,false, + true,4, 0,16, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand}; +statetype s_keenpauselook= {KEENLOOKULSPR,KEENLOOKURSPR,stepthink,false, + true,60, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand}; +statetype s_keenyawn1 = {KEENYAWN1SPR,KEENYAWN1SPR,stepthink,false, + true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenyawn2}; +statetype s_keenyawn2 = {KEENYAWN2SPR,KEENYAWN2SPR,stepthink,false, + true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenyawn3}; +statetype s_keenyawn3 = {KEENYAWN3SPR,KEENYAWN3SPR,stepthink,false, + true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenyawn4}; +statetype s_keenyawn4 = {KEENYAWN4SPR,KEENYAWN4SPR,stepthink,false, + true,40, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand}; + +statetype s_keenwait1 = {KEENWAITL2SPR,KEENWAITR2SPR,stepthink,false, + true,90, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait2}; +statetype s_keenwait2 = {KEENWAITL1SPR,KEENWAITR1SPR,stepthink,false, + true,10, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait3}; +statetype s_keenwait3 = {KEENWAITL2SPR,KEENWAITR2SPR,stepthink,false, + true,90, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait4}; +statetype s_keenwait4 = {KEENWAITL1SPR,KEENWAITR1SPR,stepthink,false, + true,10, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait5}; +statetype s_keenwait5 = {KEENWAITL2SPR,KEENWAITR2SPR,stepthink,false, + true,90, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenwait6}; +statetype s_keenwait6 = {KEENWAITL3SPR,KEENWAITR3SPR,stepthink,false, + true,70, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keenstand}; + +statetype s_keengosleep1= {KEENSLEEP1SPR,KEENSLEEP1SPR,stepthink,false, + true,20, 0,0, KeenPauseThink, KeenContact, KeenStandReact, &s_keengosleep2}; +statetype s_keengosleep2= {KEENSLEEP2SPR,KEENSLEEP2SPR,step,false, + true,20, 0,0, KeenGoSleepThink, KeenContact, KeenStandReact, &s_keensleep1}; +statetype s_keensleep1= {KEENSLEEP3SPR,KEENSLEEP3SPR,stepthink,false, + true,120, 0,0, KeenSleepThink, KeenContact, KeenStandReact, &s_keensleep2}; +statetype s_keensleep2= {KEENSLEEP4SPR,KEENSLEEP4SPR,stepthink,false, + true,120, 0,0, KeenSleepThink, KeenContact, KeenStandReact, &s_keensleep1}; + +statetype s_keengetup = {KEENGETUPLSPR,KEENGETUPRSPR,step,false, + true,15, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenstand}; + +statetype s_keendie1 = {KEENDREAM1SPR,KEENDREAM1SPR,step,false, + false,20, 0,0, NULL, NULL, KeenSimpleReact, &s_keendie2}; +statetype s_keendie2 = {KEENDREAM2SPR,KEENDREAM2SPR,step,false, + false,20, 0,0, NULL, NULL, KeenSimpleReact, &s_keendie3}; +statetype s_keendie3 = {KEENDREAM3SPR,KEENDREAM3SPR,step,false, + false,120, 0,0, KeenDieThink, NULL, KeenSimpleReact, &s_keendie3}; + +statetype s_keenlookup = {KEENLOOKULSPR,KEENLOOKURSPR,think,false, + true,0, 0,0, KeenStandThink, KeenContact, KeenStandReact, &s_keenlookup}; + +statetype s_keenduck = {KEENDUCKLSPR,KEENDUCKRSPR,think,false, + true,0, 0,0, KeenDuckThink, KeenContact, KeenStandReact, &s_keenduck}; + +statetype s_keendrop = {KEENDUCKLSPR,KEENDUCKRSPR,step,false, + false,0, 0,0, KeenDropDownThink, KeenContact, KeenSimpleReact, &s_keenjumpup3}; + +//------------------- + +statetype s_keenpole = {KEENSHINNYL1SPR,KEENSHINNYR1SPR,think,false, + false,0, 0,0, KeenPoleThink, KeenContact, KeenSimpleReact, &s_keenpole}; + +statetype s_keenclimb1 = {KEENSHINNYL1SPR,KEENSHINNYR1SPR,slidethink,false, + false,8, 0,8, KeenClimbThink, KeenContact, KeenSimpleReact, &s_keenclimb2}; +statetype s_keenclimb2 = {KEENSHINNYL2SPR,KEENSHINNYR2SPR,slidethink,false, + false,8, 0,8, KeenClimbThink, KeenContact, KeenSimpleReact, &s_keenclimb3}; +statetype s_keenclimb3 = {KEENSHINNYL3SPR,KEENSHINNYR3SPR,slidethink,false, + false,8, 0,8, KeenClimbThink, KeenContact, KeenSimpleReact, &s_keenclimb1}; + +statetype s_keenslide1 = {KEENSLIDED1SPR,KEENSLIDED1SPR,slide,false, + false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide2}; +statetype s_keenslide2 = {KEENSLIDED2SPR,KEENSLIDED2SPR,slide,false, + false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide3}; +statetype s_keenslide3 = {KEENSLIDED3SPR,KEENSLIDED3SPR,slide,false, + false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide4}; +statetype s_keenslide4 = {KEENSLIDED4SPR,KEENSLIDED4SPR,slide,false, + false,8, 0,24, KeenDropThink, KeenContact, KeenSimpleReact, &s_keenslide1}; + +statetype s_keenpolethrow1 = {KEENPTHROWL1SPR,KEENPTHROWR1SPR,step,false, + false,8, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpolethrow2}; +statetype s_keenpolethrow2 = {KEENPTHROWL2SPR,KEENPTHROWR2SPR,step,true, + false,1, 0,0, KeenThrow, KeenContact, KeenSimpleReact, &s_keenpolethrow3}; +statetype s_keenpolethrow3 = {KEENPTHROWL2SPR,KEENPTHROWR2SPR,step,false, + false,10, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpole}; + +statetype s_keenpolethrowup1 = {KEENPLTHROWU1SPR,KEENPRTHROWU1SPR,step,false, + false,8, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpolethrowup2}; +statetype s_keenpolethrowup2 = {KEENPLTHROWU2SPR,KEENPRTHROWU2SPR,step,true, + false,1, 0,0, KeenThrow, KeenContact, KeenSimpleReact, &s_keenpolethrowup3}; +statetype s_keenpolethrowup3 = {KEENPLTHROWU2SPR,KEENPRTHROWU2SPR,step,false, + false,10, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpole}; + +statetype s_keenpolethrowdown1 = {KEENPLTHROWD1SPR,KEENPRTHROWD1SPR,step,false, + false,8, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpolethrowdown2}; +statetype s_keenpolethrowdown2 = {KEENPLTHROWD2SPR,KEENPRTHROWD2SPR,step,true, + false,1, 0,0, KeenThrow, KeenContact, KeenSimpleReact, &s_keenpolethrowdown3}; +statetype s_keenpolethrowdown3 = {KEENPLTHROWD2SPR,KEENPRTHROWD2SPR,step,false, + false,10, 0,0, NULL, KeenContact, KeenSimpleReact, &s_keenpole}; + + +//------------------- + +statetype s_keenwalk1 = {KEENRUNL1SPR,KEENRUNR1SPR,slidethink,true, + true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk2}; +statetype s_keenwalk2 = {KEENRUNL2SPR,KEENRUNR2SPR,slidethink,true, + true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk3}; +statetype s_keenwalk3 = {KEENRUNL3SPR,KEENRUNR3SPR,slidethink,true, + true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk4}; +statetype s_keenwalk4 = {KEENRUNL4SPR,KEENRUNR4SPR,slidethink,true, + true,6, 24,0, KeenWalkThink, KeenContact, KeenWalkReact, &s_keenwalk1}; + +statetype s_keenthrow1 = {KEENTHROWL1SPR,KEENTHROWR1SPR,step,true, + true,4, 0,0, NULL, KeenContact, KeenStandReact, &s_keenthrow2}; +statetype s_keenthrow2 = {KEENTHROWL2SPR,KEENTHROWR2SPR,step,false, + true,4, 0,0, NULL, KeenContact, KeenStandReact, &s_keenthrow3}; +statetype s_keenthrow3 = {KEENTHROWL3SPR,KEENTHROWR3SPR,step,true, + true,1, 0,0, KeenThrow, KeenContact, KeenStandReact, &s_keenthrow4}; +statetype s_keenthrow4 = {KEENTHROWL3SPR,KEENTHROWR3SPR,step,false, + true,10, 0,0, NULL, KeenContact, KeenStandReact, &s_keenstand}; + +statetype s_keenthrowup1 = {KEENTHROWU1SPR,KEENTHROWU1SPR,step,false, + true,8, 0,0, NULL, KeenContact, KeenStandReact, &s_keenthrowup2}; +statetype s_keenthrowup2 = {KEENTHROWU2SPR,KEENTHROWU2SPR,step,true, + true,1, 0,0, KeenThrow, KeenContact, KeenStandReact, &s_keenthrowup3}; +statetype s_keenthrowup3 = {KEENTHROWU2SPR,KEENTHROWU2SPR,step,false, + true,10, 0,0, NULL, KeenContact, KeenStandReact, &s_keenstand}; + +//------------------- + +statetype s_keenjumpup1 = {KEENJUMPUL1SPR,KEENJUMPUR1SPR,think,false, + false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjumpup2}; +statetype s_keenjumpup2 = {KEENJUMPUL2SPR,KEENJUMPUR2SPR,think,false, + false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjumpup3}; +statetype s_keenjumpup3 = {KEENJUMPUL1SPR,KEENJUMPUR1SPR,think,false, + false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenstand}; + +statetype s_keenjump1 = {KEENJUMPL1SPR,KEENJUMPR1SPR,think,false, + false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump2}; +statetype s_keenjump2 = {KEENJUMPL2SPR,KEENJUMPR2SPR,think,false, + false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenjump3}; +statetype s_keenjump3 = {KEENJUMPL3SPR,KEENJUMPR3SPR,think,false, + false,0, 0,0, KeenAirThink, KeenContact, KeenAirReact, &s_keenstand}; + +statetype s_keenairthrow1= {KEENJLTHROWL1SPR,KEENJRTHROWR1SPR,stepthink,false, + false,8, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenairthrow2}; +statetype s_keenairthrow2= {KEENJLTHROWL2SPR,KEENJRTHROWR2SPR,step,true, + false,1, 0,0, KeenThrow, KeenContact, KeenAirReact, &s_keenairthrow3}; +statetype s_keenairthrow3= {KEENJLTHROWL2SPR,KEENJRTHROWR2SPR,stepthink,false, + false,10, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenjumpup3}; + +statetype s_keenairthrowup1= {KEENJLTHROWU1SPR,KEENJRTHROWU1SPR,stepthink,false, + false,8, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenairthrowup2}; +statetype s_keenairthrowup2= {KEENJLTHROWU2SPR,KEENJRTHROWU2SPR,step,true, + false,1, 0,0, KeenThrow, KeenContact, KeenAirReact, &s_keenairthrowup3}; +statetype s_keenairthrowup3= {KEENJLTHROWU2SPR,KEENJRTHROWU2SPR,stepthink,false, + false,10, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenjumpup3}; + +statetype s_keenairthrowdown1= {KEENJLTHROWD1SPR,KEENJRTHROWD1SPR,stepthink,false, + false,8, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenairthrowdown2}; +statetype s_keenairthrowdown2= {KEENJLTHROWD2SPR,KEENJRTHROWD2SPR,step,true, + false,1, 0,0, KeenThrow, KeenContact, KeenAirReact, &s_keenairthrowdown3}; +statetype s_keenairthrowdown3= {KEENJLTHROWD2SPR,KEENJRTHROWD2SPR,stepthink,false, + false,10, 0,0, ProjectileThink, KeenContact, KeenAirReact, &s_keenjumpup3}; + + +//=========================================================================== + +#pragma warn +sus + + +/* +=============== += += SpawnKeen += +=============== +*/ + +void SpawnKeen (int tilex, int tiley, int dir) +{ + player->obclass = keenobj; + player->active = yes; + player->needtoclip = true; + player->x = tilex<y = (tiley<xdir = dir; + player->ydir = 1; + NewState (player,&s_keenstand); +} + +//========================================================================== + +/* +====================== += += CheckGrabPole += +====================== +*/ + +boolean CheckGrabPole (objtype *ob) +{ + int x; + unsigned far *map; + +// +// kludgy bit to not let you grab a pole the instant you jump off it +// + if (TimeCount < leavepoletime) + leavepoletime = 0; + else if (TimeCount - leavepoletime < MINPOLEJUMPTICS) + return false; + + if (c.yaxis == -1) + map = (unsigned _seg *)mapsegs[1]+ + mapbwidthtable[(ob->top+6*PIXGLOBAL)/TILEGLOBAL]/2; + else + map = (unsigned _seg *)mapsegs[1]+ + mapbwidthtable[ob->tilebottom]/2; + + x = (ob->left + (ob->right - ob->left)/2) >>G_T_SHIFT; + + map += x; + + if ((tinf[INTILE+*map]&0x7f) == 1) + { + ob->xmove = ((x<x; + ob->ymove = c.yaxis*32; + ob->temp4 = x; // for future reference + ob->needtoclip = false; // can climb through pole holes + ob->state = &s_keenpole; + return true; + } + + return false; +} + +//========================================================================== + + +/* +============================ += += KeenStandThink += +============================ +*/ + +void KeenStandThink (objtype *ob) +{ + if (c.xaxis) + { + // started walking + ob->xdir = c.xaxis; + ob->state = &s_keenwalk1; + ob->xmove = ob->xdir*s_keenwalk1.xmove*tics; + KeenWalkThink(ob); + return; + } + + if (c.button0 && !button0held) + { + // jump straight up + SD_PlaySound (JUMPSND); + ob->xspeed = 0; + ob->yspeed = -SPDJUMP; + ob->xmove = 0; + ob->ymove = 0; // ob->yspeed*tics; // DEBUG + jumptime = JUMPTIME; // -tics; + ob->state = &s_keenjumpup1; + button0held = true; + return; + } + + if (c.button1 && !button1held) + { + // throw current xdir + if (c.yaxis == -1) + ob->state = &s_keenthrowup1; + else + ob->state = &s_keenthrow1; + button1held = true; + return; + } + + switch (c.yaxis) + { + case -1: + if (!CheckGrabPole(ob)) // try to go up/down pole first + ob->state = &s_keenlookup; + return; + case 0: + ob->state = &s_keenstand; + return; + case 1: + if (!CheckGrabPole(ob)) // try to go up/down pole first + ob->state = &s_keenduck; + return; + } +} + +//=========================================================================== + +/* +======================= += += KeenPauseThink += += Do special animations in time += +======================= +*/ + +void KeenPauseThink (objtype *ob) +{ + if (c.dir != dir_None || c.button0 || c.button1) + { + ob->temp1 = ob->temp2 = 0; // not paused any more + ob->state = &s_keenstand; + KeenStandThink(ob); + return; + } + + if ((ob->hitnorth & ~7) != 24) // if it is 0 now, keen is standing on a sprite + ob->temp1 += tics; + + switch (ob->temp2) + { + case 0: + if (ob->temp1 > 200) + { + ob->temp2++; + ob->state = &s_keenpauselook; + ob->temp1 = 0; + } + break; + case 1: + if (ob->temp1 > 300) + { + ob->temp2++; + ob->state = &s_keenwait1; + ob->temp1 = 0; + } + break; + case 2: + if (ob->temp1 > 700) + { + ob->temp2++; + ob->state = &s_keenyawn1; + ob->temp1 = 0; + } + break; + case 3: + if (ob->temp1 > 400) + { + ob->temp2++; + ob->state = &s_keenpauselook; + ob->temp1 = 0; + } + break; + case 4: + if (ob->temp1 > 700) + { + ob->temp2++; + ob->state = &s_keenyawn1; + ob->temp1 = 0; + } + break; + case 5: + if (ob->temp1 > 400) + { + ob->temp2++; + ob->state = &s_keengosleep1; + } + break; + } +} + + +//=========================================================================== + +/* +======================= += += KeenGoSleepThink += +======================= +*/ + +void KeenGoSleepThink (objtype *ob) +{ +// +// spawn the zees above keens head +// + GetNewObj (true); + + new->obclass = inertobj; + new->x = player->x; + new->y = player->y-4*PIXGLOBAL; + new->xdir = 1; + new->ydir = -1; + NewState (new,&s_keenzee1); + + ob->temp1 = (int)new; // so they can be removed later +} + + +//=========================================================================== + +/* +======================= += += KeenSleepThink += +======================= +*/ + +void KeenSleepThink (objtype *ob) +{ + if (c.dir != dir_None || c.button0 || c.button1) + { + if (ob->temp1 != (unsigned)&dummyobj) + RemoveObj ((objtype *)ob->temp1); // remove the zees + ob->temp1 = ob->temp2 = 0; // not paused any more + ob->state = &s_keengetup; + } +} + + +//=========================================================================== + +/* +======================= += += KeenDieThink += +======================= +*/ + +void KeenDieThink (objtype *ob) +{ + ob++; // shut up compiler + playstate = died; +} + + +//=========================================================================== + +/* +======================= += += KeenDuckThink += +======================= +*/ + +void KeenDuckThink (objtype *ob) +{ + unsigned far *map, tile; + int midtile,bottomtile,move; + + if (c.yaxis != 1) + { + ob->state = &s_keenstand; + KeenStandThink(ob); + return; + } + + if (c.xaxis) + ob->xdir = c.xaxis; + + if (c.button0 && !button0held) + { + // + // drop down a level + // + button0held = true; + + midtile = (ob->tileleft + ob->tileright) >> 1; + bottomtile = ob->tilebottom; + map = (unsigned far *)mapsegs[1] + mapbwidthtable[bottomtile]/2 + + midtile; + tile = *map; + if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] + || tinf[SOUTHWALL+tile]) + return; // wall prevents drop down + + map += mapwidth; + tile = *map; + if (tinf[WESTWALL+tile] || tinf[EASTWALL+tile] + || tinf[SOUTHWALL+tile]) + return; // wall prevents drop down + + move = PIXGLOBAL*(tics<4 ? 4: tics); + ob->bottom += move; + ob->y += move; + ob->xmove = ob->ymove = 0; + ob->state = &s_keenjumpup3; + ob->temp2 = 1; // allready in last stage + ob->xspeed = ob->yspeed = 0; + SD_PlaySound (PLUMMETSND); + } +} + + +//=========================================================================== + +/* +======================= += += KeenDropDownThink += +======================= +*/ + +void KeenDropDownThink (objtype *ob) +{ + ob->needtoclip = true; +} + + +//=========================================================================== + +/* +======================= += += KeenWalkThink += +======================= +*/ + +unsigned slopespeed[8] = {0,0,4,4,8,-4,-4,-8}; + +void KeenWalkThink (objtype *ob) +{ + int move; + + if (!c.xaxis) + { + // + // stopped running + // + KeenStandThink (ob); + return; + } + + ob->xdir = c.xaxis; + + if (c.yaxis && CheckGrabPole(ob)) // try to go up/down pole + return; + + if (c.button0 && !button0held) + { + // + // running jump + // + SD_PlaySound (JUMPSND); + ob->xspeed = ob->xdir * SPDRUNJUMP; + ob->yspeed = -SPDJUMP; + ob->xmove = 0; + ob->ymove = 0; // ob->yspeed*tics; + button0held = true; + jumptime = JUMPTIME; //-tics; // DEBUG + ob->state = &s_keenjump1; + } + + if (c.button1 && !button1held) + { + // + // throw + // + ob->state = &s_keenthrow1; + button1held = true; + return; + } + + // + // give speed for slopes + // + move = tics*slopespeed[ob->hitnorth&7]; + + ob->xmove += move; +} + +//=========================================================================== + +/* +======================= += += KeenAirThink += +======================= +*/ + +void KeenAirThink (objtype *ob) +{ + if (jumptime) + { + if (jumptimeymove = ob->yspeed*jumptime; + jumptime = 0; + } + else + { + ob->ymove = ob->yspeed*tics; + if (!jumpcheat) + jumptime-=tics; + } + if (!c.button0) + jumptime = 0; + + if (!jumptime) + { + ob->temp2 = 0; + ob->state = ob->state->nextstate; // switch to second jump stage + } + } + else + { + DoGravity(ob); + + if (ob->yspeed>0 && !ob->temp2) + { + ob->state = ob->state->nextstate; // switch to third jump stage + ob->temp2 = 1; + } + } + +//------------- + + if (c.xaxis) + { + ob->xdir = c.xaxis; + AccelerateX(ob,c.xaxis*2,MAXXSPEED); + } + else + FrictionX(ob); + + if (c.button1 && !button1held) + { + button1held = true; + // + // throw + // + switch (c.yaxis) + { + case -1: + ob->state = &s_keenairthrowup1; + return; + case 0: + ob->state = &s_keenairthrow1; + return; + case 1: + ob->state = &s_keenairthrowdown1; + return; + } + } + + if (ob->hitsouth==17) // going through a pole hole + { + ob->xspeed = ob->xmove = 0; + } + + + if (c.yaxis == -1) + CheckGrabPole (ob); + +} + +//=========================================================================== + +/* +======================= += += KeenPoleThink += +======================= +*/ + +int polexspeed[3] = {-SPDPOLESIDEJUMP,0,SPDPOLESIDEJUMP}; + +void PoleActions (objtype *ob) +{ + if (c.xaxis) + ob->xdir = c.xaxis; + + if (c.button0 && !button0held) // jump off the pole + { + SD_PlaySound (JUMPSND); + ob->xspeed = polexspeed[c.xaxis+1]; + ob->yspeed = -SPDPOLEJUMP; + ob->needtoclip = true; + jumptime = POLEJUMPTIME; + ob->state = &s_keenjump1; + ob->ydir = 1; + button0held = true; + leavepoletime = TimeCount; + return; + } + + if (c.button1 && !button1held) + { + button1held = true; + + switch (c.yaxis) + { + case -1: + ob->state = &s_keenpolethrowup1; + break; + case 0: + ob->state = &s_keenpolethrow1; + break; + case 1: + ob->state = &s_keenpolethrowdown1; + break; + } + } +} + + +void KeenPoleThink (objtype *ob) +{ + unsigned far *map, tile; + + switch (c.yaxis) + { + case -1: + ob->state = &s_keenclimb1; + ob->ydir = -1; + return; + + case 1: + ob->state = &s_keenslide1; + ob->ydir = 1; + KeenDropThink (ob); + return; + } + + if (c.xaxis) + { + // + // walk off pole if right next to ground + // + map = mapsegs[1] + (mapbwidthtable[ob->tilebottom+1]/2 + ob->tilemidx); + tile = *map; + if (tinf[NORTHWALL+tile]) + { + ob->xspeed = 0; + ob->yspeed = 0; + ob->needtoclip = true; + jumptime = 0; + ob->state = &s_keenjump3; + ob->ydir = 1; + SD_PlaySound (PLUMMETSND); + return; + } + } + + PoleActions (ob); +} + + +/* +======================= += += KeenClimbThink += +======================= +*/ + +void KeenClimbThink (objtype *ob) +{ + unsigned far *map; + + map = (unsigned _seg *)mapsegs[1]+mapbwidthtable[ob->tiletop]/2+ob->temp4; + + if ((tinf[INTILE+*map]&0x7f) != 1) + { + ob->ymove=0; + ob->state = &s_keenpole; // ran out of pole + return; + } + + switch (c.yaxis) + { + case 0: + ob->state = &s_keenpole; + ob->ydir = 0; + break; + + case 1: + ob->state = &s_keenslide1; + ob->ydir = 1; + KeenDropThink (ob); + break; + } + + PoleActions (ob); +} + + +/* +======================= += += KeenDropThink += +======================= +*/ + +void KeenDropThink (objtype *ob) +{ + unsigned far *map; + + map = (unsigned _seg *)mapsegs[1]+mapbwidthtable[ob->tilebottom]/2+ob->temp4; + + if ((tinf[INTILE+*map]&0x7f) != 1) + { + SD_PlaySound (PLUMMETSND); + ob->state = &s_keenjump3; // ran out of pole + jumptime = 0; + ob->temp2 = 1; + ob->xspeed = polexspeed[c.xaxis+1]; + ob->yspeed = 0; + ob->needtoclip = true; + ob->tilebottom--; + return; + } + + switch (c.yaxis) + { + case -1: + ob->state = &s_keenclimb1; + ob->ydir = -1; + break; + + case 0: + ob->state = &s_keenpole; + ob->ydir = 0; + break; + } + + PoleActions (ob); +} + +//=========================================================================== + +/* +======================= += += KeenThrow += +======================= +*/ + +void KeenThrow (objtype *ob) +{ +// can't use & in a switch statement... + + if (ob->state == &s_keenthrow3) + { + if (ob->xdir > 0) + ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_East); + else + ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_West); + return; + } + + if (ob->state == &s_keenpolethrow2) + { + if (ob->xdir > 0) + ThrowPower (ob->x+24*PIXGLOBAL,ob->y,dir_East); + else + ThrowPower (ob->x-8*PIXGLOBAL,ob->y,dir_West); + return; + } + + if (ob->state == &s_keenthrowup2) + { + ThrowPower (ob->x+4*PIXGLOBAL,ob->y-8*PIXGLOBAL,dir_North); + return; + } + + if (ob->state == &s_keenpolethrowup2) + { + ThrowPower (ob->x+8*PIXGLOBAL,ob->y-8*PIXGLOBAL,dir_North); + return; + } + + if (ob->state == &s_keenpolethrowdown2) + { + ThrowPower (ob->x+8*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_South); + return; + } + + if (ob->state == &s_keenairthrow2) + { + if (ob->xdir > 0) + ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_East); + else + ThrowPower (ob->midx-4*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_West); +#if 0 + if (ob->xdir > 0) + ThrowPower (ob->x+32*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_East); + else + ThrowPower (ob->x-16*PIXGLOBAL,ob->y+8*PIXGLOBAL,dir_West); +#endif + + new->xspeed += ob->xspeed/2; + new->yspeed += ob->yspeed/2; + return; + } + + if (ob->state == &s_keenairthrowup2) + { + if (ob->xdir > 0) + ThrowPower (ob->x+16*PIXGLOBAL,ob->y,dir_North); + else + ThrowPower (ob->x+4*PIXGLOBAL,ob->y,dir_North); + new->xspeed += ob->xspeed/2; + new->yspeed += ob->yspeed/2; + return; + } + + if (ob->state == &s_keenairthrowdown2) + { + if (ob->xdir > 0) + ThrowPower (ob->x+3*PIXGLOBAL,ob->y+16*PIXGLOBAL,dir_South); + else + ThrowPower (ob->x+12*PIXGLOBAL,ob->y+16*PIXGLOBAL,dir_South); + new->xspeed += ob->xspeed/2; + new->yspeed += ob->yspeed/2; + return; + } + + Quit ("KeenThrow: Bad state!"); +} + + +/* +============================================================================= + + CONTACT ROUTINES + +============================================================================= +*/ + +/* +============================ += += KillKeen += +============================ +*/ + +void KillKeen (void) +{ + if (!godmode) + { + SD_PlaySound (WAKEUPSND); + player->needtoclip = false; + ChangeState (player,&s_keendie1); + } +} + + + +/* +============================ += += KeenContact += +============================ +*/ + +unsigned bonuspoints[6] = {100,200,500,1000,2000,5000}; + +void KeenContact (objtype *ob, objtype *hit) +{ + switch (hit->obclass) + { + case bonusobj: + hit->obclass = inertobj; + switch (hit->temp1) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + SD_PlaySound (GETPOINTSSND); + hit->shapenum = BONUS100SPR+hit->temp1; + GivePoints (bonuspoints[hit->temp1]); + ChangeState (hit,&s_bonusrise); + break; + case 6: + SD_PlaySound (EXTRAKEENSND); + hit->shapenum = BONUS1UPSPR; + gamestate.lives++; + ChangeState (hit,&s_bonusrise); + break; + case 7: + SD_PlaySound (EXTRAKEENSND); + hit->shapenum = BONUSSUPERSPR; + gamestate.lives+=3; + gamestate.flowerpowers+=8; + GivePoints (10000); + ChangeState (hit,&s_bonusrise); + break; + case 8: + SD_PlaySound (GETPOWERSND); + hit->shapenum = BONUSFLOWERSPR; + gamestate.flowerpowers++; + ChangeState (hit,&s_bonusrise); + break; + case 9: + SD_PlaySound (GETPOWERSND); + hit->shapenum = BONUSFLOWERUPSPR; + gamestate.flowerpowers+=5; + ChangeState (hit,&s_bonusrise); + break; + case 10: + SD_PlaySound (GETBOMBSND); + hit->shapenum = BONUSBOMBSPR; + gamestate.boobusbombs++; + gamestate.bombsthislevel++; + ChangeState (hit,&s_bonusrise); + break; + case 11: + SD_PlaySound (GETKEYSND); + hit->shapenum = BONUSKEYSPR; + gamestate.keys++; + ChangeState (hit,&s_bonusrise); + break; + } + break; + + case doorobj: + if (gamestate.keys) + { + if (hit->state != &s_doorraise) + { + SD_PlaySound (OPENDOORSND); + gamestate.keys--; + ChangeState (hit,&s_doorraise); + } + } + else + { + SD_PlaySound (NOWAYSND); + ClipToSpriteSide (ob,hit); + } + break; + case taterobj: + if (hit->state == &s_taterattack2) + KillKeen (); + break; + case carrotobj: + ClipToSpriteSide (ob,hit); + if (!ob->needtoclip) // got pushed off a pole + { + SD_PlaySound (PLUMMETSND); + ob->needtoclip = true; + ob->xspeed = ob->yspeed = 0; + ChangeState(ob,&s_keenjump3); + ob->temp2 = 1; + jumptime = 0; + } + break; + case cartobj: + ClipToSprite (ob,hit,true); + break; + case broccoobj: + if (hit->state == &s_broccosmash3 || hit->state == &s_broccosmash4) + KillKeen (); + break; + case squashobj: + if (hit->state == &s_squasherjump2) + KillKeen (); + else + { + ClipToSpriteSide (ob,hit); + if (!ob->needtoclip) // got pushed off a pole + { + SD_PlaySound (PLUMMETSND); + ob->needtoclip = true; + ob->xspeed = ob->yspeed = 0; + ChangeState(ob,&s_keenjump3); + ob->temp2 = 1; + jumptime = 0; + } + } + break; + case grapeobj: + if (hit->state == &s_grapefall) + KillKeen (); + break; + case tomatobj: + case celeryobj: + case asparobj: + case turnipobj: + case cauliobj: + case brusselobj: + case mushroomobj: + case apelobj: + case peabrainobj: + case boobusobj: + case shotobj: + KillKeen (); + break; + + } +} + + +/* +============================================================================= + + REACTION ROUTINES + +============================================================================= +*/ + + +/* +============================ += += KeenSimpleReact += +============================ +*/ + +void KeenSimpleReact (objtype *ob) +{ + PLACESPRITE; +} + + +/* +============================ += += KeenStandReact += +============================ +*/ + +void KeenStandReact (objtype *ob) +{ + if (!ob->hitnorth) + { + // + // walked off an edge + // + SD_PlaySound (PLUMMETSND); + ob->xspeed = ob->xdir*WALKAIRSPEED; + ChangeState (ob,&s_keenjump3); + ob->temp2 = 1; + jumptime = 0; + } + else if ( (ob->hitnorth & ~7) == 8) // deadly floor! + { + KillKeen (); + } + + PLACESPRITE; +} + +/* +============================ += += KeenWalkReact += +============================ +*/ + +void KeenWalkReact (objtype *ob) +{ + if (!ob->hitnorth) + { + // + // walked off an edge + // + ob->xspeed = ob->xdir*WALKAIRSPEED; + ob->yspeed = 0; + ChangeState (ob,&s_keenjump3); + ob->temp2 = 1; + jumptime = 0; + } + else if ( (ob->hitnorth & ~7) == 8) // deadly floor! + { + KillKeen (); + goto placeit; + } + + if (ob->hiteast == 2) // doors + { + + } + else if (ob->hitwest == 2) // doors + { + + } + else if (ob->hiteast || ob->hitwest) + { + // + // ran into a wall + // + ob->ticcount = 0; + ob->state = &s_keenstand; + ob->shapenum = ob->xdir == 1 ? s_keenstand.rightshapenum : + s_keenstand.leftshapenum; + } +placeit: + + PLACESPRITE; +} + + +/* +============================ += += KeenAirReact += +============================ +*/ + +void KeenAirReact (objtype *ob) +{ + int x,y; + unsigned far *map,mapextra; + + if (ob->hiteast || ob->hitwest) + ob->xspeed = 0; + + map = mapsegs[1] + (mapbwidthtable[ob->tiletop]/2) + ob->tileleft; + mapextra = mapwidth - (ob->tileright - ob->tileleft+1); + for (y=ob->tiletop;y<=ob->tilebottom;y++,map+=mapextra) + for (x=ob->tileleft;x<=ob->tileright;x++,map++) + if (tinf[SOUTHWALL+*map] == 17) // jumping up through a pole hole + { + ob->xspeed = 0; + ob->x = ob->tilemidx*TILEGLOBAL - 2*PIXGLOBAL; + goto checknorth; + } + + if (ob->hitsouth) + { + if (ob->hitsouth == 17) // jumping up through a pole hole + { + ob->y -= 32; + ob->top -= 32; + ob->xspeed = 0; + ob->x = ob->tilemidx*TILEGLOBAL - 2*PIXGLOBAL; + } + else + { + SD_PlaySound (HITHEADSND); + + if (ob->hitsouth > 1) + { + ob->yspeed += 16; + if (ob->yspeed<0) // push away from slopes + ob->yspeed = 0; + } + else + ob->yspeed = 0; + jumptime = 0; + } + } + +checknorth: + if (ob->hitnorth) + { + if (!(ob->hitnorth == 25 && jumptime)) // KLUDGE to allow jumping off + { // sprites + ob->temp1 = ob->temp2 = 0; + ChangeState (ob,&s_keenstand); + SD_PlaySound (LANDSND); + } + } + + PLACESPRITE; +} + +/* +============================ += += KeenSlideReact += +============================ +*/ + +void KeenSlideReact (objtype *ob) +{ + unsigned far *map; + + if (ob->hitnorth) // friction slow down + { + map = mapsegs[2] + (mapbwidthtable[ob->tiletop]/2 + ob->tileleft); + if (!tinf[SOUTHWALL+*map] && !tinf[SOUTHWALL+*(map+1)]) + FrictionX(ob); + } + + + if (ob->hitwest || ob->hiteast || !ob->xspeed) + ChangeState (ob,&s_keengetup); + + PLACESPRITE; +} + diff --git a/src/lib/hb/kd_main.c b/src/lib/hb/kd_main.c new file mode 100755 index 00000000..69a6d3e9 --- /dev/null +++ b/src/lib/hb/kd_main.c @@ -0,0 +1,533 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_MAIN.C +/* +============================================================================= + + KEEN DREAMS + + An Id Software production + +============================================================================= +*/ + +#include "mem.h" +#include "string.h" + +#include "KD_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +char str[80],str2[20]; +boolean singlestep,jumpcheat,godmode,tedlevel; +unsigned tedlevelnum; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void DebugMemory (void); +void TestSprites(void); +int DebugKeys (void); +void ShutdownId (void); +void Quit (char *error); +void InitGame (void); +void main (void); + +//=========================================================================== + +#if FRILLS + +/* +================== += += DebugMemory += +================== +*/ + +void DebugMemory (void) +{ + VW_FixRefreshBuffer (); + US_CenterWindow (16,7); + + US_CPrint ("Memory Usage"); + US_CPrint ("------------"); + US_Print ("Total :"); + US_PrintUnsigned (mminfo.mainmem/1024); + US_Print ("k\nFree :"); + US_PrintUnsigned (MM_UnusedMemory()/1024); + US_Print ("k\nWith purge:"); + US_PrintUnsigned (MM_TotalFree()/1024); + US_Print ("k\n"); + VW_UpdateScreen(); + IN_Ack (); +#if GRMODE == EGAGR + MM_ShowMemory (); +#endif +} + +/* +=================== += += TestSprites += +=================== +*/ + +#define DISPWIDTH 110 +#define TEXTWIDTH 40 +void TestSprites(void) +{ + int hx,hy,sprite,oldsprite,bottomy,topx,shift; + spritetabletype far *spr; + spritetype _seg *block; + unsigned mem,scan; + + + VW_FixRefreshBuffer (); + US_CenterWindow (30,17); + + US_CPrint ("Sprite Test"); + US_CPrint ("-----------"); + + hy=PrintY; + hx=(PrintX+56)&(~7); + topx = hx+TEXTWIDTH; + + US_Print ("Chunk:\nWidth:\nHeight:\nOrgx:\nOrgy:\nXl:\nYl:\nXh:\nYh:\n" + "Shifts:\nMem:\n"); + + bottomy = PrintY; + + sprite = STARTSPRITES; + shift = 0; + + do + { + if (sprite>=STARTTILE8) + sprite = STARTTILE8-1; + else if (spritewidth);US_Print ("\n");PrintX=hx; + US_PrintUnsigned (spr->height);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->orgx);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->orgy);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->xl);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->yl);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->xh);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->yh);US_Print ("\n");PrintX=hx; + US_PrintSigned (spr->shifts);US_Print ("\n");PrintX=hx; + if (!block) + { + US_Print ("-----"); + } + else + { + mem = block->sourceoffset[3]+5*block->planesize[3]; + mem = (mem+15)&(~15); // round to paragraphs + US_PrintUnsigned (mem); + } + + oldsprite = sprite; + do + { + // + // draw the current shift, then wait for key + // + VWB_Bar(topx,hy,DISPWIDTH,bottomy-hy,WHITE); + if (block) + { + PrintX = topx; + PrintY = hy; + US_Print ("Shift:"); + US_PrintUnsigned (shift); + US_Print ("\n"); + VWB_DrawSprite (topx+16+shift*2,PrintY,sprite); + } + + VW_UpdateScreen(); + + scan = IN_WaitForKey (); + + switch (scan) + { + case sc_UpArrow: + sprite++; + break; + case sc_DownArrow: + sprite--; + break; + case sc_LeftArrow: + if (--shift == -1) + shift = 3; + break; + case sc_RightArrow: + if (++shift == 4) + shift = 0; + break; + case sc_Escape: + return; + } + + } while (sprite == oldsprite); + + } while (1); + + +} + +#endif + + +/* +================ += += DebugKeys += +================ +*/ +int DebugKeys (void) +{ + boolean esc; + int level; + +#if FRILLS + if (Keyboard[0x12] && ingame) // DEBUG: end + 'E' to quit level + { + if (tedlevel) + TEDDeath(); + playstate = levelcomplete; + } +#endif + + if (Keyboard[0x22] && ingame) // G = god mode + { + VW_FixRefreshBuffer (); + US_CenterWindow (12,2); + if (godmode) + US_PrintCentered ("God mode OFF"); + else + US_PrintCentered ("God mode ON"); + VW_UpdateScreen(); + IN_Ack(); + godmode ^= 1; + return 1; + } + else if (Keyboard[0x17]) // I = item cheat + { + VW_FixRefreshBuffer (); + US_CenterWindow (12,3); + US_PrintCentered ("Free items!"); + gamestate.boobusbombs=99; + gamestate.flowerpowers=99; + gamestate.keys=99; + VW_UpdateScreen(); + IN_Ack (); + return 1; + } + else if (Keyboard[0x24]) // J = jump cheat + { + jumpcheat^=1; + VW_FixRefreshBuffer (); + US_CenterWindow (18,3); + if (jumpcheat) + US_PrintCentered ("Jump cheat ON"); + else + US_PrintCentered ("Jump cheat OFF"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } +#if FRILLS + else if (Keyboard[0x32]) // M = memory info + { + DebugMemory(); + return 1; + } +#endif + else if (Keyboard[0x19]) // P = pause with no screen disruptioon + { + IN_Ack(); + } + else if (Keyboard[0x1f] && ingame) // S = slow motion + { + singlestep^=1; + VW_FixRefreshBuffer (); + US_CenterWindow (18,3); + if (singlestep) + US_PrintCentered ("Slow motion ON"); + else + US_PrintCentered ("Slow motion OFF"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } +#if FRILLS + else if (Keyboard[0x14]) // T = sprite test + { + TestSprites(); + return 1; + } +#endif + else if (Keyboard[0x11] && ingame) // W = warp to level + { + VW_FixRefreshBuffer (); + US_CenterWindow(26,3); + PrintY+=6; + US_Print(" Warp to which level(0-16):"); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>=0 && level<=16) + { + gamestate.mapon = level; + playstate = warptolevel; + } + } + return 1; + } + return 0; +} + +//=========================================================================== + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); + SD_Shutdown (); + IN_Shutdown (); + RF_Shutdown (); + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + +//=========================================================================== + +/* +========================== += += Quit += +========================== +*/ + +void Quit (char *error) +{ + ShutdownId (); + if (error && *error) + { + clrscr(); + puts(error); + puts("\n"); + exit(1); + } + exit (0); +} + +//=========================================================================== + +/* +========================== += += InitGame += += Load a few things right away += +========================== +*/ + +#if 0 +#include "piracy.h" +#endif + +void InitGame (void) +{ + int i; + + MM_Startup (); + + +#if 0 + // Handle piracy screen... + // + movedata(FP_SEG(PIRACY),(unsigned)PIRACY,0xb800,displayofs,4000); + while ((bioskey(0)>>8) != sc_Return); +#endif + + +#if GRMODE == EGAGR + if (mminfo.mainmem < 335l*1024) + { +#pragma warn -pro +#pragma warn -nod + clrscr(); // we can't include CONIO because of a name conflict +#pragma warn +nod +#pragma warn +pro + puts ("There is not enough memory available to play the game reliably. You can"); + puts ("play anyway, but an out of memory condition will eventually pop up. The"); + puts ("correct solution is to unload some TSRs or rename your CONFIG.SYS and"); + puts ("AUTOEXEC.BAT to free up more memory.\n"); + puts ("Do you want to (Q)uit, or (C)ontinue?"); + i = bioskey (0); + if ( (i>>8) != sc_C) + Quit (""); + } +#endif + + US_TextScreen(); + + VW_Startup (); + RF_Startup (); + IN_Startup (); + SD_Startup (); + US_Startup (); + +// US_UpdateTextScreen(); + + CA_Startup (); + US_Setup (); + +// +// load in and lock down some basic chunks +// + + CA_ClearMarks (); + + CA_MarkGrChunk(STARTFONT); + CA_MarkGrChunk(STARTFONTM); + CA_MarkGrChunk(STARTTILE8); + CA_MarkGrChunk(STARTTILE8M); + for (i=KEEN_LUMP_START;i<=KEEN_LUMP_END;i++) + CA_MarkGrChunk(i); + + CA_CacheMarks (NULL, 0); + + MM_SetLock (&grsegs[STARTFONT],true); + MM_SetLock (&grsegs[STARTFONTM],true); + MM_SetLock (&grsegs[STARTTILE8],true); + MM_SetLock (&grsegs[STARTTILE8M],true); + for (i=KEEN_LUMP_START;i<=KEEN_LUMP_END;i++) + MM_SetLock (&grsegs[i],true); + + CA_LoadAllSounds (); + + fontcolor = WHITE; + + US_FinishTextScreen(); + + VW_SetScreenMode (GRMODE); + VW_ClearVideo (BLACK); +} + + + +//=========================================================================== + +/* +========================== += += main += +========================== +*/ + +void main (void) +{ + short i; + + if (stricmp(_argv[1], "/VER") == 0) + { + printf("\nKeen Dreams version 1.93 (Rev 1)\n"); + printf("developed for use with 100%% IBM compatibles\n"); + printf("that have 640K memory, DOS version 3.3 or later,\n"); + printf("and an EGA or VGA display adapter.\n"); + printf("Copyright 1991-1993 Softdisk Publishing.\n"); + printf("Commander Keen is a trademark of Id Software.\n"); + exit(0); + } + + if (stricmp(_argv[1], "/?") == 0) + { + printf("\nKeen Dreams version 1.93\n"); + printf("Copyright 1991-1993 Softdisk Publishing.\n\n"); + printf("Commander Keen is a trademark of Id Software.\n"); + printf("Type KDREAMS from the DOS prompt to run.\n\n"); + printf("KDREAMS /COMP for SVGA compatibility mode\n"); + printf("KDREAMS /NODR stops program hang with the drive still on\n"); + printf("KDREAMS /NOAL disables AdLib and Sound Blaster detection\n"); + printf("KDREAMS /NOSB disables Sound Blaster detection\n"); + printf("KDREAMS /NOJOYS ignores joystick\n"); + printf("KDREAMS /NOMOUSE ignores mouse\n"); + printf("KDREAMS /HIDDENCARD overrides video card detection\n"); + printf("KDREAMS /VER for version and compatibility information\n"); + printf("KDREAMS /? for this help information\n"); + exit(0); + } + + textcolor(7); + textbackground(0); + + InitGame(); + + DemoLoop(); // DemoLoop calls Quit when everything is done + Quit("Demo loop exited???"); +} + diff --git a/src/lib/hb/kd_play.c b/src/lib/hb/kd_play.c new file mode 100755 index 00000000..02bdccdb --- /dev/null +++ b/src/lib/hb/kd_play.c @@ -0,0 +1,1928 @@ +/* Keen Dreams Source Code + * Copyright (C) 2014 Javier M. Chavez + * + * 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. + */ + +// KD_PLAY.C + +#include "KD_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define INACTIVATEDIST 10 + +#define MAXMOVE (TILEGLOBAL-17) + +#define NUMLUMPS 22 + + +#define CONTROLSLUMP 0 +#define KEENLUMP 1 +#define WORLDKEENLUMP 2 +#define BROCCOLUMP 3 +#define TOMATLUMP 4 +#define CARROTLUMP 5 +#define ASPARLUMP 6 +#define GRAPELUMP 7 +#define TATERLUMP 8 +#define CARTLUMP 9 +#define FRENCHYLUMP 10 +#define MELONLUMP 11 +#define SQUASHLUMP 12 +#define APELLUMP 13 +#define PEALUMP 14 +#define BOOBUSLUMP 15 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +exittype playstate; +gametype gamestate; + +boolean button0held,button1held; +objtype *new,*check,*player,*scoreobj; + +unsigned originxtilemax,originytilemax; + +ControlInfo c; + +objtype dummyobj; + +char *levelnames[21] = +{ +"The Land of Tuberia", +"Horseradish Hill", +"The Melon Mines", +"Bridge Bottoms", +"Rhubarb Rapids", +"Parsnip Pass", +"Level 6", +"Spud City", +"Level 8", +"Apple Acres", +"Grape Grove", +"Level 11", +"Brussels Sprout Bay", +"Level 13", +"Squash Swamp", +"Boobus' Chamber", +"Castle Tuberia", +"", +"Title Page" +}; + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +// for asm scaning of map planes +unsigned mapx,mapy,mapxcount,mapycount,maptile,mapspot; + +int plummet; + +int objectcount; + +objtype objarray[MAXACTORS],*lastobj,*objfreelist; + +int oldtileleft,oldtiletop,oldtileright,oldtilebottom,oldtilemidx; +int oldleft,oldtop,oldright,oldbottom,oldmidx; +int leftmoved,topmoved,rightmoved,bottommoved,midxmoved; + +int topmove,bottommove,midxmove; + +int inactivateleft,inactivateright,inactivatetop,inactivatebottom; + +int fadecount; + +boolean bombspresent; + +boolean lumpneeded[NUMLUMPS]; +int lumpstart[NUMLUMPS] = +{ +CONTROLS_LUMP_START, +KEEN_LUMP_START, +WORLDKEEN_LUMP_START, +BROCCOLASH_LUMP_START, +TOMATO_LUMP_START, +CARROT_LUMP_START, +ASPAR_LUMP_START, +GRAPE_LUMP_START, +TATER_LUMP_START, +CANTA_LUMP_START, +FRENCHY_LUMP_START, +MELONLIPS_LUMP_START, +SQUASHER_LUMP_START, +APEL_LUMP_START, +PEAS_LUMP_START, +BOOBUS_LUMP_START, +}; + +int lumpend[NUMLUMPS] = +{ +CONTROLS_LUMP_END, +KEEN_LUMP_END, +WORLDKEEN_LUMP_END, +BROCCOLASH_LUMP_END, +TOMATO_LUMP_END, +CARROT_LUMP_END, +ASPAR_LUMP_END, +GRAPE_LUMP_END, +TATER_LUMP_END, +CANTA_LUMP_END, +FRENCHY_LUMP_END, +MELONLIPS_LUMP_END, +SQUASHER_LUMP_END, +APEL_LUMP_END, +PEAS_LUMP_END, +BOOBUS_LUMP_END, +}; + + +void CheckKeys (void); +void CalcInactivate (void); +void InitObjArray (void); +void GetNewObj (boolean usedummy); +void RemoveObj (objtype *gone); +void ScanInfoPlane (void); +void PatchWorldMap (void); +void MarkTileGraphics (void); +void FadeAndUnhook (void); +void SetupGameLevel (boolean loadnow); +void ScrollScreen (void); +void MoveObjVert (objtype *ob, int ymove); +void MoveObjHoriz (objtype *ob, int xmove); +void GivePoints (unsigned points); +void ClipToEnds (objtype *ob); +void ClipToEastWalls (objtype *ob); +void ClipToWestWalls (objtype *ob); +void ClipToWalls (objtype *ob); +void ClipToSpriteSide (objtype *push, objtype *solid); +void ClipToSprite (objtype *push, objtype *solid, boolean squish); +int DoActor (objtype *ob,int tics); +void StateMachine (objtype *ob); +void NewState (objtype *ob,statetype *state); +void PlayLoop (void); +void GameLoop (void); + +//=========================================================================== + +/* +===================== += += CheckKeys += +===================== +*/ + +void CheckKeys (void) +{ + if (screenfaded) // don't do anything with a faded screen + return; + +// +// space for status screen +// + if (Keyboard[sc_Space]) + { + StatusWindow (); + IN_ClearKeysDown(); + RF_ForceRefresh(); + lasttimecount = TimeCount; + } + +// +// pause key wierdness can't be checked as a scan code +// + if (Paused) + { + VW_FixRefreshBuffer (); + US_CenterWindow (8,3); + US_PrintCentered ("PAUSED"); + VW_UpdateScreen (); + IN_Ack(); + RF_ForceRefresh (); + Paused = false; + } + +// +// F1-F7/ESC to enter control panel +// + if ( (LastScan >= sc_F1 && LastScan <= sc_F7) || LastScan == sc_Escape) + { + VW_FixRefreshBuffer (); + US_CenterWindow (20,8); + US_CPrint ("Loading"); + VW_UpdateScreen (); + US_ControlPanel(); + IN_ClearKeysDown(); + if (restartgame) + playstate = resetgame; + else if (!loadedgame) + RF_ForceRefresh(); // don't refresh if loading a new game + + lasttimecount = TimeCount; + } + +// +// F10-? debug keys +// + if (Keyboard[sc_F10] && DebugKeys() ) + { + RF_ForceRefresh(); + lasttimecount = TimeCount; + } + +} + +//=========================================================================== + + +/* +======================= += += CalcInactivate += +======================= +*/ + +void CalcInactivate (void) +{ + originxtilemax = originxtile+PORTTILESWIDE-1; + originytilemax = originytile+PORTTILESHIGH-1; + + inactivateleft = originxtile-INACTIVATEDIST; + if (inactivateleft < 0) + inactivateleft = 0; + inactivateright = originxtilemax+INACTIVATEDIST; + inactivatetop = originytile-INACTIVATEDIST; + if (inactivatetop < 0) + inactivatetop = 0; + inactivatebottom = originytilemax+INACTIVATEDIST; +} + + +//=========================================================================== + + +/* +############################################################################# + + The objarray data structure + +############################################################################# + +Objarray containt structures for every actor currently playing. The structure +is accessed as a linked list starting at *player, ending when ob->next == +NULL. GetNewObj inserts a new object at the end of the list, meaning that +if an actor spawn another actor, the new one WILL get to think and react the +same frame. RemoveObj unlinks the given object and returns it to the free +list, but does not damage the objects ->next pointer, so if the current object +removes itself, a linked list following loop can still safely get to the +next element. + + + +############################################################################# +*/ + + +/* +========================= += += InitObjArray += += Call to clear out the entire object list, returning them all to the free += list. Allocates a special spot for the player. += +========================= +*/ + +void InitObjArray (void) +{ + int i; + + for (i=0;iprev; + memset (new,0,sizeof(*new)); + + if (lastobj) + lastobj->next = new; + new->prev = lastobj; // new->next is allready NULL from memset + + new->active = yes; + new->needtoclip = true; + lastobj = new; + + objectcount++; +} + +//=========================================================================== + +/* +========================= += += RemoveObj += += Add the given object back into the free list, and unlink it from it's += neighbors += +========================= +*/ + +void RemoveObj (objtype *gone) +{ + if (gone == player) + Quit ("RemoveObj: Tried to remove the player!"); + +// +// erase it from the refresh manager +// + RF_RemoveSprite (&gone->sprite); + +// +// fix the next object's back link +// + if (gone == lastobj) + lastobj = (objtype *)gone->prev; + else + gone->next->prev = gone->prev; + +// +// fix the previous object's forward link +// + gone->prev->next = gone->next; + +// +// add it back in to the free list +// + gone->prev = objfreelist; + objfreelist = gone; +} + +//=========================================================================== + + +void near HandleInfo (void) +{ + switch (maptile) + { + case 1: + SpawnKeen(mapx,mapy,1); + break; + case 2: + SpawnKeen(mapx,mapy,-1); + break; + case 19: + SpawnWorldKeen(mapx,mapy); + lumpneeded[WORLDKEENLUMP] = true; + break; + + case 31: + bombspresent = true; + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 32: + SpawnBonus(mapx,mapy,maptile-21); + new->active = false; + break; + case 33: + SpawnDoor(mapx,mapy); + new->active = false; + break; + case 41: + SpawnBrocco(mapx,mapy); + new->active = false; + lumpneeded[BROCCOLUMP] = true; + break; + case 42: + SpawnTomat(mapx,mapy); + new->active = false; + lumpneeded[TOMATLUMP] = true; + break; + case 43: + SpawnCarrot(mapx,mapy); + new->active = false; + lumpneeded[CARROTLUMP] = true; + break; + case 45: + SpawnAspar(mapx,mapy); + new->active = false; + lumpneeded[ASPARLUMP] = true; + break; + case 46: + SpawnGrape(mapx,mapy); + new->active = false; + lumpneeded[GRAPELUMP] = true; + break; + case 47: + SpawnTater(mapx,mapy); + new->active = false; + lumpneeded[TATERLUMP] = true; + break; + case 48: + SpawnCart(mapx,mapy); + lumpneeded[CARTLUMP] = true; + break; + case 49: + SpawnFrenchy(mapx,mapy); + new->active = false; + lumpneeded[FRENCHYLUMP] = true; + break; + case 50: + case 51: + case 52: + SpawnMelon(mapx,mapy,maptile-50); + new->active = false; + lumpneeded[MELONLUMP] = true; + break; + case 57: + SpawnSquasher(mapx,mapy); + new->active = false; + lumpneeded[SQUASHLUMP] = true; + break; + case 58: + SpawnApel(mapx,mapy); + new->active = false; + lumpneeded[APELLUMP] = true; + break; + case 59: + SpawnPeaPod(mapx,mapy); + new->active = false; + lumpneeded[PEALUMP] = true; + break; + case 60: + SpawnPeaBrain(mapx,mapy); + new->active = false; + lumpneeded[PEALUMP] = true; + break; + case 61: + SpawnBoobus(mapx,mapy); + lumpneeded[BOOBUSLUMP] = true; + break; + } + + if (new->active != allways) + new->active = false; +} + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +void ScanInfoPlane (void) +{ + unsigned x,y,i,j; + int tile; + unsigned far *start; + + InitObjArray(); // start spawning things with a clean slate + + memset (lumpneeded,0,sizeof(lumpneeded)); + +#if 0 + start = mapsegs[2]; + for (y=0;y=3 && info<=18 && gamestate.leveldone[info-2]) + { + *(mapsegs[2] + spot) = 0; + foreground = *(mapsegs[1] + spot); + if (foreground == 130) + *(mapsegs[1]+spot) = 0; // not blocking now + else if (foreground == 90) + { + // plant done flag + *(mapsegs[1]+spot) = 133; + *(mapsegs[1]+(spot-mapwidth-1)) = 131; + *(mapsegs[1]+(spot-mapwidth)) = 132; + } + } + spot++; + } while (spotx - (150<y-(84<left < originxmin + || player->right > originxmax+20*TILEGLOBAL) + { + playstate = levelcomplete; + return; + } + +// +// fallen off bottom of world? +// + if (!plummet && player->bottom > originymax+13*TILEGLOBAL) + { + godmode = 0; + plummet = 1; + KillKeen (); + return; + } + + if (player->x < originxglobal+SCROLLWEST) + xscroll = player->x - (originxglobal+SCROLLWEST); + else if (player->x > originxglobal+SCROLLEAST) + xscroll = player->x - (originxglobal+SCROLLEAST); + else + xscroll = 0; + + if (player->y < originyglobal+SCROLLNORTH) + yscroll = player->y - (originyglobal+SCROLLNORTH); + else if (player->y > originyglobal+SCROLLSOUTH) + yscroll = player->y - (originyglobal+SCROLLSOUTH); + else yscroll = 0; + + if (xscroll || yscroll) + { + RF_Scroll (xscroll,yscroll); + CalcInactivate (); + scoreobj->needtoreact = true; + } +} + +//========================================================================== + +/* +==================== += += GivePoints += += Grants extra men at 20k,40k,80k,160k,320k += +==================== +*/ + +void GivePoints (unsigned points) +{ + gamestate.score += points; + if (gamestate.score >= gamestate.nextextra) + { + SD_PlaySound (EXTRAKEENSND); + gamestate.lives++; + gamestate.nextextra*=2; + } +} + + +//========================================================================== + +/* +==================== += += MoveObjVert += +==================== +*/ + +void MoveObjVert (objtype *ob, int ymove) +{ + ob->y += ymove; + ob->top += ymove; + ob->bottom += ymove; + ob->tiletop = ob->top >> G_T_SHIFT; + ob->tilebottom = ob->bottom >> G_T_SHIFT; +} + + +/* +==================== += += MoveObjHoriz += +==================== +*/ + +void MoveObjHoriz (objtype *ob, int xmove) +{ + ob->x += xmove; + ob->left += xmove; + ob->right += xmove; + ob->tileleft = ob->left >> G_T_SHIFT; + ob->tileright = ob->right >> G_T_SHIFT; +} + + +/* +============================================================================= + + Actor to tile clipping rouitnes + +============================================================================= +*/ + +// walltype / x coordinate (0-15) + +int wallclip[8][16] = { // the height of a given point in a tile +{ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256}, +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78}, +{0x80,0x88,0x90,0x98,0xa0,0xa8,0xb0,0xb8,0xc0,0xc8,0xd0,0xd8,0xe0,0xe8,0xf0,0xf8}, +{ 0,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,0xa0,0xb0,0xc0,0xd0,0xe0,0xf0}, +{0x78,0x70,0x68,0x60,0x58,0x50,0x48,0x40,0x38,0x30,0x28,0x20,0x18,0x10,0x08, 0}, +{0xf8,0xf0,0xe8,0xe0,0xd8,0xd0,0xc8,0xc0,0xb8,0xb0,0xa8,0xa0,0x98,0x90,0x88,0x80}, +{0xf0,0xe0,0xd0,0xc0,0xb0,0xa0,0x90,0x80,0x70,0x60,0x50,0x40,0x30,0x20,0x10, 0} +}; + +// assignment within ifs are used heavily here, so turn off the warning +#pragma warn -pia + +/* +=========================== += += ClipToEnds += +=========================== +*/ + +void ClipToEnds (objtype *ob) +{ + unsigned far *map,tile,facetile,info,wall; + int leftpix,rightpix,midtiles,toppix,bottompix; + int x,y,clip,move,totalmove,maxmove,midxpix; + + midxpix = (ob->midx&0xf0) >> 4; + + maxmove = -abs(midxmoved) - bottommoved - 16; + map = (unsigned far *)mapsegs[1] + + mapbwidthtable[oldtilebottom-1]/2 + ob->tilemidx; + for (y=oldtilebottom-1 ; y<=ob->tilebottom ; y++,map+=mapwidth) + { + if (wall = tinf[NORTHWALL+*map]) + { + clip = wallclip[wall&7][midxpix]; + move = ( (y<bottom; + if (move<0 && move>=maxmove) + { + ob->hitnorth = wall; + MoveObjVert (ob,move); + return; + } + } + } + + maxmove = abs(midxmoved) - topmoved + 16; + map = (unsigned far *)mapsegs[1] + + mapbwidthtable[oldtiletop+1]/2 + ob->tilemidx; + for (y=oldtiletop+1 ; y>=ob->tiletop ; y--,map-=mapwidth) + { + if (wall = tinf[SOUTHWALL+*map]) + { + clip = wallclip[wall&7][midxpix]; + move = ( ((y+1)<top; + if (move > 0 && move<=maxmove) + { + totalmove = ob->ymove + move; + if (totalmove < TILEGLOBAL && totalmove > -TILEGLOBAL) + { + ob->hitsouth = wall; + MoveObjVert (ob,move); + } + } + } + } +} + + +/* +=========================== += += ClipToEastWalls / ClipToWestWalls += +=========================== +*/ + +void ClipToEastWalls (objtype *ob) +{ + int y,move,top,bottom; + unsigned far *map,tile,info,wall; + + // clip to east walls if moving west + + top = ob->tiletop; + if (ob->hitsouth>1) + top++; // on a slope inside a tile + bottom = ob->tilebottom; + if (ob->hitnorth>1) + bottom--; // on a slope inside a tile + + for (y=top;y<=bottom;y++) + { + map = (unsigned far *)mapsegs[1] + + mapbwidthtable[y]/2 + ob->tileleft; + + if (ob->hiteast = tinf[EASTWALL+*map]) + { + move = ( (ob->tileleft+1)<left; + MoveObjHoriz (ob,move); + return; + } + } +} + + +void ClipToWestWalls (objtype *ob) +{ + int y,move,top,bottom; + unsigned far *map,tile,info,wall; + + // check west walls if moving east + + top = ob->tiletop; + if (ob->hitsouth>1) + top++; // on a slope inside a tile + bottom = ob->tilebottom; + if (ob->hitnorth>1) + bottom--; // on a slope inside a tile + + for (y=top;y<=bottom;y++) + { + map = (unsigned far *)mapsegs[1] + + mapbwidthtable[y]/2 + ob->tileright; + + if (ob->hitwest = tinf[WESTWALL+*map]) + { + move = ( (ob->tileright<right; + MoveObjHoriz (ob,move); + return; + } + } +} + +// turn 'possibly incorrect assignment' warnings back on +#pragma warn +pia + + +//========================================================================== + +/* +================ += += ClipToWalls += += Moves the current object xmove/ymove units, clipping to walls += +================ +*/ + +void ClipToWalls (objtype *ob) +{ + unsigned x,y,tile; + spritetabletype far *shape; + boolean endfirst; + +// +// make sure it stays in contact with a 45 degree slope +// + if (ob->state->pushtofloor) + { + if (ob->xmove > 0) + ob->ymove = ob->xmove + 16; + else + ob->ymove = -ob->xmove + 16; + } + +// +// move the shape +// + if (ob->xmove > MAXMOVE) + ob->xmove = MAXMOVE; + else if (ob->xmove < -MAXMOVE) + ob->xmove = -MAXMOVE; + + if (ob->ymove > MAXMOVE+16) // +16 for push to floor + ob->ymove = MAXMOVE+16; + else if (ob->ymove < -MAXMOVE) + ob->ymove = -MAXMOVE; + + ob->x += ob->xmove; + ob->y += ob->ymove; + + ob->needtoreact = true; + + if (!ob->shapenum) // can't get a hit rect with no shape! + return; + + shape = &spritetable[ob->shapenum-STARTSPRITES]; + + oldtileright = ob->tileright; + oldtiletop = ob->tiletop; + oldtileleft = ob->tileleft; + oldtilebottom = ob->tilebottom; + oldtilemidx = ob->tilemidx; + + oldright = ob->right; + oldtop = ob->top; + oldleft = ob->left; + oldbottom = ob->bottom; + oldmidx = ob->midx; + + ob->left = ob->x + shape->xl; + ob->right = ob->x + shape->xh; + ob->top = ob->y + shape->yl; + ob->bottom = ob->y + shape->yh; + ob->midx = ob->left + (ob->right - ob->left)/2; + + ob->tileleft = ob->left >> G_T_SHIFT; + ob->tileright = ob->right >> G_T_SHIFT; + ob->tiletop = ob->top >> G_T_SHIFT; + ob->tilebottom = ob->bottom >> G_T_SHIFT; + ob->tilemidx = ob->midx >> G_T_SHIFT; + + ob->hitnorth = ob->hiteast = ob->hitsouth = ob->hitwest = 0; + + if (!ob->needtoclip) + return; + + leftmoved = ob->left - oldleft; + rightmoved = ob->right - oldright; + topmoved = ob->top - oldtop; + bottommoved = ob->bottom - oldbottom; + midxmoved = ob->midx - oldmidx; + +// +// clip it +// + + ClipToEnds(ob); + + if (leftmoved < 0 || ob == player) // make sure player gets cliped + ClipToEastWalls (ob); + if (rightmoved > 0 || ob == player) + ClipToWestWalls (ob); +} + +//========================================================================== + + +/* +================== += += ClipToSpriteSide += += Clips push to solid += +================== +*/ + +void ClipToSpriteSide (objtype *push, objtype *solid) +{ + int xmove,leftinto,rightinto; + + // + // amount the push shape can be pushed + // + xmove = solid->xmove - push->xmove; + + // + // amount it is inside + // + leftinto = solid->right - push->left; + rightinto = push->right - solid->left; + + if (leftinto>0 && leftinto<= xmove) + { + push->xmove = leftinto; + if (push->state->pushtofloor) + push->ymove = leftinto+16; + ClipToWalls (push); + push->hiteast = 1; + return; + } + + if (rightinto>0 && rightinto<= -xmove) + { + push->xmove = -rightinto; + if (push->state->pushtofloor) + push->ymove = rightinto+16; + ClipToWalls (push); + push->hitwest = 1; + return; + } + +} + +//========================================================================== + + +/* +================== += += ClipToSprite += += Clips push to solid += +================== +*/ + +void ClipToSprite (objtype *push, objtype *solid, boolean squish) +{ + boolean temp; + int walltemp,xmove,leftinto,rightinto,topinto,bottominto; + + xmove = solid->xmove - push->xmove; + + push->xmove = push->ymove = 0; + + // + // left / right + // + leftinto = solid->right - push->left; + rightinto = push->right - solid->left; + + if (leftinto>0 && leftinto<=xmove) + { + push->xmove = leftinto; + walltemp = push->hitnorth; + ClipToWalls (push); + if (!push->hitnorth) + push->hitnorth = walltemp; + if (squish && push->hitwest) + KillKeen (); + push->hiteast = 1; + return; + } + else if (rightinto>0 && rightinto<=-xmove) + { + push->xmove = -rightinto; + walltemp = push->hitnorth; + ClipToWalls (push); + if (!push->hitnorth) + push->hitnorth = walltemp; + if (squish && push->hiteast) + KillKeen (); + push->hitwest = 1; + return; + } + + // + // top / bottom + // + topinto = solid->bottom - push->top; + bottominto = push->bottom - solid->top; + + if (bottominto>0) + { + push->ymove = -bottominto+16; + push->xmove = solid->xmove; + temp = push->state->pushtofloor; + push->state->pushtofloor = false; + walltemp = push->hitnorth; + ClipToWalls (push); + if (!push->hitnorth) + push->hitnorth = walltemp; + push->state->pushtofloor = temp; + push->hitnorth = 25; + } + else if (topinto>0) + { + push->ymove = topinto; + ClipToWalls (push); + push->hitsouth = 25; + } +} + +//========================================================================== + + +/* +================== += += DoActor += += Moves an actor in its current state by a given number of tics. += If that time takes it into the next state, it changes the state += and returns the number of excess tics after the state change += +================== +*/ + +int DoActor (objtype *ob,int tics) +{ + int newtics,movetics,excesstics; + statetype *state; + + state = ob->state; + + if (state->progress == think) + { + if (state->think) + { + if (ob->nothink) + ob->nothink--; + else +#pragma warn -pro + state->think(ob); +#pragma warn +pro + } + return 0; + } + + newtics = ob->ticcount+tics; + + if (newtics < state->tictime || state->tictime == 0) + { + ob->ticcount = newtics; + if (state->progress == slide || state->progress == slidethink) + { + if (ob->xdir) + ob->xmove += ob->xdir == 1 ? tics*state->xmove + : -tics*state->xmove; + if (ob->ydir) + ob->ymove += ob->ydir == 1 ? tics*state->ymove + : -tics*state->ymove; + } + if (state->progress == slidethink || state->progress == stepthink) + { + if (state->think) + { + if (ob->nothink) + ob->nothink--; + else +#pragma warn -pro + state->think(ob); +#pragma warn +pro + } + } + return 0; + } + else + { + movetics = state->tictime - ob->ticcount; + excesstics = newtics - state->tictime; + ob->ticcount = 0; + if (state->progress == slide || state->progress == slidethink) + { + if (ob->xdir) + ob->xmove += ob->xdir == 1 ? movetics*state->xmove + : -movetics*state->xmove; + if (ob->ydir) + ob->ymove += ob->ydir == 1 ? movetics*state->ymove + : -movetics*state->ymove; + } + else + { + if (ob->xdir) + ob->xmove += ob->xdir == 1 ? state->xmove : -state->xmove; + if (ob->ydir) + ob->ymove += ob->ydir == 1 ? state->ymove : -state->ymove; + } + + if (state->think) + { + if (ob->nothink) + ob->nothink--; + else +#pragma warn -pro + state->think(ob); +#pragma warn +pro + } + + if (ob->state == state) + ob->state = state->nextstate; // go to next state + else if (!ob->state) + return 0; // object removed itself + return excesstics; + } +} + +//========================================================================== + + +/* +==================== += += StateMachine += += Change state and give directions += +==================== +*/ + +void StateMachine (objtype *ob) +{ + int excesstics,oldshapenum; + statetype *state; + + ob->xmove = ob->ymove = 0; + oldshapenum = ob->shapenum; + + state = ob->state; + + excesstics = DoActor(ob,tics); + if (ob->state != state) + { + ob->ticcount = 0; // start the new state at 0, then use excess + state = ob->state; + } + + while (excesstics) + { + // + // passed through to next state + // + if (!state->skippable && excesstics >= state->tictime) + excesstics = DoActor(ob,state->tictime-1); + else + excesstics = DoActor(ob,excesstics); + if (ob->state != state) + { + ob->ticcount = 0; // start the new state at 0, then use excess + state = ob->state; + } + } + + if (!state) // object removed itself + { + RemoveObj (ob); + return; + } + + + // + // if state->rightshapenum == NULL, the state does not have a standard + // shape (the think routine should have set it) + // + if (state->rightshapenum) + { + if (ob->xdir>0) + ob->shapenum = state->rightshapenum; + else + ob->shapenum = state->leftshapenum; + } + if (ob->shapenum == (unsigned)-1) + ob->shapenum = 0; // make it invisable this time + + if (ob->xmove || ob->ymove || ob->shapenum != oldshapenum) + { + // + // actor moved or changed shape + // make sure the movement is within limits (one tile) + // + ClipToWalls (ob); + } +} + +//========================================================================== + + +/* +==================== += += NewState += +==================== +*/ + +void NewState (objtype *ob,statetype *state) +{ + boolean temp; + + ob->state = state; + + if (state->rightshapenum) + { + if (ob->xdir>0) + ob->shapenum = state->rightshapenum; + else + ob->shapenum = state->leftshapenum; + } + + temp = ob->needtoclip; + + ob->needtoclip = false; + + ClipToWalls (ob); // just calculate values + + ob->needtoclip = temp; + + if (ob->needtoclip) + ClipToWalls (ob); + +} + +//========================================================================== + +/* +============================ += += PlayLoop += +============================ +*/ + +void PlayLoop (void) +{ + objtype *obj, *check; + long newtime; + + button0held = button1held = false; + + ingame = true; + playstate = 0; + plummet = 0; + + FixScoreBox (); // draw bomb/flower + + do + { + CalcSingleGravity (); + IN_ReadControl(0,&c); // get player input + if (!c.button0) + button0held = 0; + if (!c.button1) + button1held = 0; + +// +// go through state changes and propose movements +// + obj = player; + do + { + if (!obj->active + && obj->tileright >= originxtile + && obj->tileleft <= originxtilemax + && obj->tiletop <= originytilemax + && obj->tilebottom >= originytile) + { + obj->needtoreact = true; + obj->active = yes; + } + + if (obj->active) + StateMachine(obj); + + if ( (obj->active == true || obj->active == removable) && + ( obj->tileright < inactivateleft + || obj->tileleft > inactivateright + || obj->tiletop > inactivatebottom + || obj->tilebottom < inactivatetop) ) + { + if (obj->active == removable) + RemoveObj (obj); // temp thing (shots, etc) + else + { + if (US_RndT()sprite); + obj->active = no; + } + } + } + + obj = (objtype *)obj->next; + } while (obj); + +// +// check for and handle collisions between objects +// + obj = player; + do + { + if (obj->active) + { + check = (objtype *)obj->next; + while (check) + { + if ( check->active + && obj->right > check->left + && obj->left < check->right + && obj->top < check->bottom + && obj->bottom > check->top) + { +#pragma warn -pro + if (obj->state->contact) + obj->state->contact(obj,check); + if (check->state->contact) + check->state->contact(check,obj); +#pragma warn +pro + if (!obj->obclass) + break; // contact removed object + } + check = (objtype *)check->next; + } + } + obj = (objtype *)obj->next; + } while (obj); + + + ScrollScreen(); + +// +// react to whatever happened, and post sprites to the refresh manager +// + obj = player; + do + { + if (obj->needtoreact && obj->state->react) + { + obj->needtoreact = false; +#pragma warn -pro + obj->state->react(obj); +#pragma warn +pro + } + obj = (objtype *)obj->next; + } while (obj); + + +// +// update the screen and calculate the number of tics it took to execute +// this cycle of events (for adaptive timing of next cycle) +// + RF_Refresh(); + +// +// single step debug mode +// + if (singlestep) + { + VW_WaitVBL(14); + lasttimecount = TimeCount; + } + + CheckKeys(); + } while (!loadedgame && !playstate); + + ingame = false; +} + + +//========================================================================== + +/* +========================== += += GameFinale += +========================== +*/ + +void GameFinale (void) +{ +struct date d; + + VW_FixRefreshBuffer (); + +/* screen 1 of finale text (16 lines) */ + US_CenterWindow (30,21); + PrintY += 4; + US_CPrint ( +"Yes! Boobus Tuber's hash-brown-\n" +"like remains rained down from\n" +"the skies as Commander Keen\n" +"walked up to the Dream Machine.\n" +"He analyzed all the complex\n" +"controls and readouts on it, then\n" +"pulled down a huge red lever\n" +"marked \"On/Off Switch.\" The\n" +"machine clanked and rattled,\n" +"then went silent. He had freed\n" +"all the children from their\n" +"vegetable-enforced slavery!\n" +"Everything around Keen wobbled\n" +"in a disconcerting manner, his\n" +"eyelids grew heavy, and he\n" +"fell asleep....\n" + ); + VW_UpdateScreen(); + VW_WaitVBL(60); + SD_WaitSoundDone (); + IN_ClearKeysDown (); + IN_Ack(); + +/* screen 2 of finale (15 lines) */ + US_CenterWindow (30,21); + PrintY += 9; + US_CPrint ( +"Billy woke up, looking around the\n" +"room, the early morning sun\n" +"shining in his face. Nothing.\n" +"No vegetables to be seen. Was it\n" +"all just a dream?\n\n" +"Billy's mom entered the room.\n\n" +"\"Good morning, dear. I heard some\n" +"news on TV that you'd be\n" +"interested in,\" she said, sitting\n" +"by him on the bed.\n\n" +"\"What news?\" Billy asked,\n" +"still groggy.\n\n" + ); + VW_UpdateScreen(); + VW_WaitVBL(60); + IN_ClearKeysDown (); + IN_Ack(); + +/* screen 3 of finale (12 lines)*/ + US_CenterWindow (30,21); + PrintY += 23; + US_CPrint ( +"\"The President declared today\n" +"National 'I Hate Broccoli' Day.\n" +"He said kids are allowed to pick\n" +"one vegetable today, and they\n" +"don't have to eat it.\"\n\n" +"\"Aw, mom, I'm not afraid of any\n" +"stupid vegetables,\" Billy said.\n" +"\"But if it's okay with you, I'd\n" +"rather not have any french fries\n" +"for awhile.\"\n\n" +"THE END" + ); + VW_UpdateScreen(); + VW_WaitVBL(60); + IN_ClearKeysDown (); + IN_Ack(); + +} + +//========================================================================== + +/* +========================== += += HandleDeath += +========================== +*/ + +void HandleDeath (void) +{ + unsigned top,bottom,selection,y,color; + + gamestate.keys = 0; + gamestate.boobusbombs -= gamestate.bombsthislevel; + gamestate.lives--; + if (gamestate.lives < 0) + return; + + VW_FixRefreshBuffer (); + US_CenterWindow (20,8); + PrintY += 4; + US_CPrint ("You didn't make it past"); + US_CPrint (levelnames[mapon]); + PrintY += 8; + top = PrintY-2; + US_CPrint ("Try Again"); + PrintY += 4; + bottom = PrintY-2; + US_CPrint ("Exit to Tuberia"); + + selection = 0; + do + { + if (selection) + y = bottom; + else + y = top; + +// draw select bar + if ( (TimeCount / 16)&1 ) + color = SECONDCOLOR; + else + color = FIRSTCOLOR; + + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y,color); + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+1,color); + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+12,color); + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+13,color); + VWB_Vlin (y+1,y+11, WindowX+4,color); + VWB_Vlin (y+1,y+11, WindowX+5,color); + VWB_Vlin (y+1,y+11, WindowX+WindowW-4,color); + VWB_Vlin (y+1,y+11, WindowX+WindowW-5,color); + + VW_UpdateScreen (); + +// erase select bar + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y,WHITE); + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+1,WHITE); + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+12,WHITE); + VWB_Hlin (WindowX+4,WindowX+WindowW-4,y+13,WHITE); + VWB_Vlin (y+1,y+11, WindowX+4,WHITE); + VWB_Vlin (y+1,y+11, WindowX+5,WHITE); + VWB_Vlin (y+1,y+11, WindowX+WindowW-4,WHITE); + VWB_Vlin (y+1,y+11, WindowX+WindowW-5,WHITE); + + if (LastScan == sc_Escape) + { + gamestate.mapon = 0; // exit to tuberia + IN_ClearKeysDown (); + return; + } + + IN_ReadControl(0,&c); // get player input + if (c.button0 || c.button1 || LastScan == sc_Return + || LastScan == sc_Space) + { + if (selection) + gamestate.mapon = 0; // exit to tuberia + return; + } + if (c.yaxis == -1 || LastScan == sc_UpArrow) + selection = 0; + else if (c.yaxis == 1 || LastScan == sc_DownArrow) + selection = 1; + } while (1); + +} + +//========================================================================== + +/* +============================ += += GameLoop += += A game has just started (after the cinematic or load game) += +============================ +*/ + +void GameLoop (void) +{ + unsigned cities,i; + long orgx,orgy; + + gamestate.difficulty = restartgame; + restartgame = gd_Continue; + + do + { +startlevel: + if (loadedgame) + { + loadedgame = false; + // + // start the initial view position to center the player + // + orgx = (long)player->x - (150<y-(84<-1 && playstate!=victorious); + + GameOver (); + +done: + cities = 0; + for (i= 1; i<=16; i++) + if (gamestate.leveldone[i]) + cities++; + US_CheckHighScore (gamestate.score,cities); + VW_ClearVideo (FIRSTCOLOR); +} + diff --git a/src/lib/hb/wl_act1.c b/src/lib/hb/wl_act1.c new file mode 100755 index 00000000..10d84e4e --- /dev/null +++ b/src/lib/hb/wl_act1.c @@ -0,0 +1,900 @@ +// WL_ACT1.C + +#include "WL_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + STATICS + +============================================================================= +*/ + + +statobj_t statobjlist[MAXSTATS],*laststatobj; + + +struct +{ + int picnum; + stat_t type; +} statinfo[] = +{ +{SPR_STAT_0}, // puddle spr1v +{SPR_STAT_1,block}, // Green Barrel " +{SPR_STAT_2,block}, // Table/chairs " +{SPR_STAT_3,block}, // Floor lamp " +{SPR_STAT_4}, // Chandelier " +{SPR_STAT_5,block}, // Hanged man " +{SPR_STAT_6,bo_alpo}, // Bad food " +{SPR_STAT_7,block}, // Red pillar " +// +// NEW PAGE +// +{SPR_STAT_8,block}, // Tree spr2v +{SPR_STAT_9}, // Skeleton flat " +{SPR_STAT_10,block}, // Sink " (SOD:gibs) +{SPR_STAT_11,block}, // Potted plant " +{SPR_STAT_12,block}, // Urn " +{SPR_STAT_13,block}, // Bare table " +{SPR_STAT_14}, // Ceiling light " +#ifndef SPEAR +{SPR_STAT_15}, // Kitchen stuff " +#else +{SPR_STAT_15,block}, // Gibs! +#endif +// +// NEW PAGE +// +{SPR_STAT_16,block}, // suit of armor spr3v +{SPR_STAT_17,block}, // Hanging cage " +{SPR_STAT_18,block}, // SkeletoninCage " +{SPR_STAT_19}, // Skeleton relax " +{SPR_STAT_20,bo_key1}, // Key 1 " +{SPR_STAT_21,bo_key2}, // Key 2 " +{SPR_STAT_22,block}, // stuff (SOD:gibs) +{SPR_STAT_23}, // stuff +// +// NEW PAGE +// +{SPR_STAT_24,bo_food}, // Good food spr4v +{SPR_STAT_25,bo_firstaid}, // First aid " +{SPR_STAT_26,bo_clip}, // Clip " +{SPR_STAT_27,bo_machinegun}, // Machine gun " +{SPR_STAT_28,bo_chaingun}, // Gatling gun " +{SPR_STAT_29,bo_cross}, // Cross " +{SPR_STAT_30,bo_chalice}, // Chalice " +{SPR_STAT_31,bo_bible}, // Bible " +// +// NEW PAGE +// +{SPR_STAT_32,bo_crown}, // crown spr5v +{SPR_STAT_33,bo_fullheal}, // one up " +{SPR_STAT_34,bo_gibs}, // gibs " +{SPR_STAT_35,block}, // barrel " +{SPR_STAT_36,block}, // well " +{SPR_STAT_37,block}, // Empty well " +{SPR_STAT_38,bo_gibs}, // Gibs 2 " +{SPR_STAT_39,block}, // flag " +// +// NEW PAGE +// +#ifndef SPEAR +{SPR_STAT_40,block}, // Call Apogee spr7v +#else +{SPR_STAT_40}, // Red light +#endif +// +// NEW PAGE +// +{SPR_STAT_41}, // junk " +{SPR_STAT_42}, // junk " +{SPR_STAT_43}, // junk " +#ifndef SPEAR +{SPR_STAT_44}, // pots " +#else +{SPR_STAT_44,block}, // Gibs! +#endif +{SPR_STAT_45,block}, // stove " (SOD:gibs) +{SPR_STAT_46,block}, // spears " (SOD:gibs) +{SPR_STAT_47}, // vines " +// +// NEW PAGE +// +#ifdef SPEAR +{SPR_STAT_48,block}, // marble pillar +{SPR_STAT_49,bo_25clip}, // bonus 25 clip +{SPR_STAT_50,block}, // truck +{SPR_STAT_51,bo_spear}, // SPEAR OF DESTINY! +#endif + +{SPR_STAT_26,bo_clip2}, // Clip " +{-1} // terminator +}; + +/* +=============== += += InitStaticList += +=============== +*/ + +void InitStaticList (void) +{ + laststatobj = &statobjlist[0]; +} + + + +/* +=============== += += SpawnStatic += +=============== +*/ + +void SpawnStatic (int tilex, int tiley, int type) +{ + laststatobj->shapenum = statinfo[type].picnum; + laststatobj->tilex = tilex; + laststatobj->tiley = tiley; + laststatobj->visspot = &spotvis[tilex][tiley]; + + switch (statinfo[type].type) + { + case block: + (unsigned)actorat[tilex][tiley] = 1; // consider it a blocking tile + case dressing: + laststatobj->flags = 0; + break; + + case bo_cross: + case bo_chalice: + case bo_bible: + case bo_crown: + case bo_fullheal: + if (!loadedgame) + gamestate.treasuretotal++; + + case bo_firstaid: + case bo_key1: + case bo_key2: + case bo_key3: + case bo_key4: + case bo_clip: + case bo_25clip: + case bo_machinegun: + case bo_chaingun: + case bo_food: + case bo_alpo: + case bo_gibs: + case bo_spear: + laststatobj->flags = FL_BONUS; + laststatobj->itemnumber = statinfo[type].type; + break; + } + + laststatobj++; + + if (laststatobj == &statobjlist[MAXSTATS]) + Quit ("Too many static objects!\n"); +} + + +/* +=============== += += PlaceItemType += += Called during game play to drop actors' items. It finds the proper += item number based on the item type (bo_???). If there are no free item += spots, nothing is done. += +=============== +*/ + +void PlaceItemType (int itemtype, int tilex, int tiley) +{ + int type; + statobj_t *spot; + +// +// find the item number +// + for (type=0 ; ; type++) + { + if (statinfo[type].picnum == -1) // end of list + Quit ("PlaceItemType: couldn't find type!"); + if (statinfo[type].type == itemtype) + break; + } + +// +// find a spot in statobjlist to put it in +// + for (spot=&statobjlist[0] ; ; spot++) + { + if (spot==laststatobj) + { + if (spot == &statobjlist[MAXSTATS]) + return; // no free spots + laststatobj++; // space at end + break; + } + + if (spot->shapenum == -1) // -1 is a free spot + break; + } +// +// place it +// + spot->shapenum = statinfo[type].picnum; + spot->tilex = tilex; + spot->tiley = tiley; + spot->visspot = &spotvis[tilex][tiley]; + spot->flags = FL_BONUS; + spot->itemnumber = statinfo[type].type; +} + + + +/* +============================================================================= + + DOORS + +doorobjlist[] holds most of the information for the doors + +doorposition[] holds the amount the door is open, ranging from 0 to 0xffff + this is directly accessed by AsmRefresh during rendering + +The number of doors is limited to 64 because a spot in tilemap holds the + door number in the low 6 bits, with the high bit meaning a door center + and bit 6 meaning a door side tile + +Open doors conect two areas, so sounds will travel between them and sight + will be checked when the player is in a connected area. + +Areaconnect is incremented/decremented by each door. If >0 they connect + +Every time a door opens or closes the areabyplayer matrix gets recalculated. + An area is true if it connects with the player's current spor. + +============================================================================= +*/ + +#define DOORWIDTH 0x7800 +#define OPENTICS 300 + +doorobj_t doorobjlist[MAXDOORS],*lastdoorobj; +int doornum; + +unsigned doorposition[MAXDOORS]; // leading edge of door 0=closed + // 0xffff = fully open + +byte far areaconnect[NUMAREAS][NUMAREAS]; + +boolean areabyplayer[NUMAREAS]; + + +/* +============== += += ConnectAreas += += Scans outward from playerarea, marking all connected areas += +============== +*/ + +void RecursiveConnect (int areanumber) +{ + int i; + + for (i=0;iareanumber] = true; + RecursiveConnect (player->areanumber); +} + + +void InitAreas (void) +{ + memset (areabyplayer,0,sizeof(areabyplayer)); + areabyplayer[player->areanumber] = true; +} + + + +/* +=============== += += InitDoorList += +=============== +*/ + +void InitDoorList (void) +{ + memset (areabyplayer,0,sizeof(areabyplayer)); + _fmemset (areaconnect,0,sizeof(areaconnect)); + + lastdoorobj = &doorobjlist[0]; + doornum = 0; +} + + +/* +=============== += += SpawnDoor += +=============== +*/ + +void SpawnDoor (int tilex, int tiley, boolean vertical, int lock) +{ + int areanumber; + unsigned far *map; + + if (doornum==64) + Quit ("64+ doors on level!"); + + doorposition[doornum] = 0; // doors start out fully closed + lastdoorobj->tilex = tilex; + lastdoorobj->tiley = tiley; + lastdoorobj->vertical = vertical; + lastdoorobj->lock = lock; + lastdoorobj->action = dr_closed; + + (unsigned)actorat[tilex][tiley] = doornum | 0x80; // consider it a solid wall + +// +// make the door tile a special tile, and mark the adjacent tiles +// for door sides +// + tilemap[tilex][tiley] = doornum | 0x80; + map = mapsegs[0] + farmapylookup[tiley]+tilex; + if (vertical) + { + *map = *(map-1); // set area number + tilemap[tilex][tiley-1] |= 0x40; + tilemap[tilex][tiley+1] |= 0x40; + } + else + { + *map = *(map-mapwidth); // set area number + tilemap[tilex-1][tiley] |= 0x40; + tilemap[tilex+1][tiley] |= 0x40; + } + + doornum++; + lastdoorobj++; +} + +//=========================================================================== + +/* +===================== += += OpenDoor += +===================== +*/ + +void OpenDoor (int door) +{ + if (doorobjlist[door].action == dr_open) + doorobjlist[door].ticcount = 0; // reset open time + else + doorobjlist[door].action = dr_opening; // start it opening +} + + +/* +===================== += += CloseDoor += +===================== +*/ + +void CloseDoor (int door) +{ + int tilex,tiley,area; + objtype *check; + +// +// don't close on anything solid +// + tilex = doorobjlist[door].tilex; + tiley = doorobjlist[door].tiley; + + if (actorat[tilex][tiley]) + return; + + if (player->tilex == tilex && player->tiley == tiley) + return; + + if (doorobjlist[door].vertical) + { + if ( player->tiley == tiley ) + { + if ( ((player->x+MINDIST) >>TILESHIFT) == tilex ) + return; + if ( ((player->x-MINDIST) >>TILESHIFT) == tilex ) + return; + } + check = actorat[tilex-1][tiley]; + if (check && ((check->x+MINDIST) >> TILESHIFT) == tilex ) + return; + check = actorat[tilex+1][tiley]; + if (check && ((check->x-MINDIST) >> TILESHIFT) == tilex ) + return; + } + else if (!doorobjlist[door].vertical) + { + if (player->tilex == tilex) + { + if ( ((player->y+MINDIST) >>TILESHIFT) == tiley ) + return; + if ( ((player->y-MINDIST) >>TILESHIFT) == tiley ) + return; + } + check = actorat[tilex][tiley-1]; + if (check && ((check->y+MINDIST) >> TILESHIFT) == tiley ) + return; + check = actorat[tilex][tiley+1]; + if (check && ((check->y-MINDIST) >> TILESHIFT) == tiley ) + return; + } + + +// +// play door sound if in a connected area +// + area = *(mapsegs[0] + farmapylookup[doorobjlist[door].tiley] + +doorobjlist[door].tilex)-AREATILE; + if (areabyplayer[area]) + { + PlaySoundLocTile(CLOSEDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley); // JAB + } + + doorobjlist[door].action = dr_closing; +// +// make the door space solid +// + (unsigned)actorat[tilex][tiley] + = door | 0x80; +} + + + +/* +===================== += += OperateDoor += += The player wants to change the door's direction += +===================== +*/ + +void OperateDoor (int door) +{ + int lock; + + lock = doorobjlist[door].lock; + if (lock >= dr_lock1 && lock <= dr_lock4) + { + if ( ! (gamestate.keys & (1 << (lock-dr_lock1) ) ) ) + { + SD_PlaySound (NOWAYSND); // locked + return; + } + } + + switch (doorobjlist[door].action) + { + case dr_closed: + case dr_closing: + OpenDoor (door); + break; + case dr_open: + case dr_opening: + CloseDoor (door); + break; + } +} + + +//=========================================================================== + +/* +=============== += += DoorOpen += += Close the door after three seconds += +=============== +*/ + +void DoorOpen (int door) +{ + if ( (doorobjlist[door].ticcount += tics) >= OPENTICS) + CloseDoor (door); +} + + + +/* +=============== += += DoorOpening += +=============== +*/ + +void DoorOpening (int door) +{ + int area1,area2; + unsigned far *map; + long position; + + position = doorposition[door]; + if (!position) + { + // + // door is just starting to open, so connect the areas + // + map = mapsegs[0] + farmapylookup[doorobjlist[door].tiley] + +doorobjlist[door].tilex; + + if (doorobjlist[door].vertical) + { + area1 = *(map+1); + area2 = *(map-1); + } + else + { + area1 = *(map-mapwidth); + area2 = *(map+mapwidth); + } + area1 -= AREATILE; + area2 -= AREATILE; + areaconnect[area1][area2]++; + areaconnect[area2][area1]++; + ConnectAreas (); + if (areabyplayer[area1]) + { + PlaySoundLocTile(OPENDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley); // JAB + } + } + +// +// slide the door by an adaptive amount +// + position += tics<<10; + if (position >= 0xffff) + { + // + // door is all the way open + // + position = 0xffff; + doorobjlist[door].ticcount = 0; + doorobjlist[door].action = dr_open; + actorat[doorobjlist[door].tilex][doorobjlist[door].tiley] = 0; + } + + doorposition[door] = position; +} + + +/* +=============== += += DoorClosing += +=============== +*/ + +void DoorClosing (int door) +{ + int area1,area2,move; + unsigned far *map; + long position; + int tilex,tiley; + + tilex = doorobjlist[door].tilex; + tiley = doorobjlist[door].tiley; + + if ( ((unsigned)actorat[tilex][tiley] != (door | 0x80)) + || (player->tilex == tilex && player->tiley == tiley) ) + { // something got inside the door + OpenDoor (door); + return; + }; + + position = doorposition[door]; + +// +// slide the door by an adaptive amount +// + position -= tics<<10; + if (position <= 0) + { + // + // door is closed all the way, so disconnect the areas + // + position = 0; + + doorobjlist[door].action = dr_closed; + + map = mapsegs[0] + farmapylookup[doorobjlist[door].tiley] + +doorobjlist[door].tilex; + + if (doorobjlist[door].vertical) + { + area1 = *(map+1); + area2 = *(map-1); + } + else + { + area1 = *(map-mapwidth); + area2 = *(map+mapwidth); + } + area1 -= AREATILE; + area2 -= AREATILE; + areaconnect[area1][area2]--; + areaconnect[area2][area1]--; + + ConnectAreas (); + } + + doorposition[door] = position; +} + + + + +/* +===================== += += MoveDoors += += Called from PlayLoop += +===================== +*/ + +void MoveDoors (void) +{ + int door; + + if (gamestate.victoryflag) // don't move door during victory sequence + return; + + for (door = 0 ; door < doornum ; door++) + switch (doorobjlist[door].action) + { + case dr_open: + DoorOpen (door); + break; + + case dr_opening: + DoorOpening(door); + break; + + case dr_closing: + DoorClosing(door); + break; + } +} + + +/* +============================================================================= + + PUSHABLE WALLS + +============================================================================= +*/ + +unsigned pwallstate; +unsigned pwallpos; // amount a pushable wall has been moved (0-63) +unsigned pwallx,pwally; +int pwalldir; + +/* +=============== += += PushWall += +=============== +*/ + +void PushWall (int checkx, int checky, int dir) +{ + int oldtile; + + if (pwallstate) + return; + + + oldtile = tilemap[checkx][checky]; + if (!oldtile) + return; + + switch (dir) + { + case di_north: + if (actorat[checkx][checky-1]) + { + SD_PlaySound (NOWAYSND); + return; + } + (unsigned)actorat[checkx][checky-1] = + tilemap[checkx][checky-1] = oldtile; + break; + + case di_east: + if (actorat[checkx+1][checky]) + { + SD_PlaySound (NOWAYSND); + return; + } + (unsigned)actorat[checkx+1][checky] = + tilemap[checkx+1][checky] = oldtile; + break; + + case di_south: + if (actorat[checkx][checky+1]) + { + SD_PlaySound (NOWAYSND); + return; + } + (unsigned)actorat[checkx][checky+1] = + tilemap[checkx][checky+1] = oldtile; + break; + + case di_west: + if (actorat[checkx-1][checky]) + { + SD_PlaySound (NOWAYSND); + return; + } + (unsigned)actorat[checkx-1][checky] = + tilemap[checkx-1][checky] = oldtile; + break; + } + + gamestate.secretcount++; + pwallx = checkx; + pwally = checky; + pwalldir = dir; + pwallstate = 1; + pwallpos = 0; + tilemap[pwallx][pwally] |= 0xc0; + *(mapsegs[1]+farmapylookup[pwally]+pwallx) = 0; // remove P tile info + + SD_PlaySound (PUSHWALLSND); +} + + + +/* +================= += += MovePWalls += +================= +*/ + +void MovePWalls (void) +{ + int oldblock,oldtile; + + if (!pwallstate) + return; + + oldblock = pwallstate/128; + + pwallstate += tics; + + if (pwallstate/128 != oldblock) + { + // block crossed into a new block + oldtile = tilemap[pwallx][pwally] & 63; + + // + // the tile can now be walked into + // + tilemap[pwallx][pwally] = 0; + (unsigned)actorat[pwallx][pwally] = 0; + *(mapsegs[0]+farmapylookup[pwally]+pwallx) = player->areanumber+AREATILE; + + // + // see if it should be pushed farther + // + if (pwallstate>256) + { + // + // the block has been pushed two tiles + // + pwallstate = 0; + return; + } + else + { + switch (pwalldir) + { + case di_north: + pwally--; + if (actorat[pwallx][pwally-1]) + { + pwallstate = 0; + return; + } + (unsigned)actorat[pwallx][pwally-1] = + tilemap[pwallx][pwally-1] = oldtile; + break; + + case di_east: + pwallx++; + if (actorat[pwallx+1][pwally]) + { + pwallstate = 0; + return; + } + (unsigned)actorat[pwallx+1][pwally] = + tilemap[pwallx+1][pwally] = oldtile; + break; + + case di_south: + pwally++; + if (actorat[pwallx][pwally+1]) + { + pwallstate = 0; + return; + } + (unsigned)actorat[pwallx][pwally+1] = + tilemap[pwallx][pwally+1] = oldtile; + break; + + case di_west: + pwallx--; + if (actorat[pwallx-1][pwally]) + { + pwallstate = 0; + return; + } + (unsigned)actorat[pwallx-1][pwally] = + tilemap[pwallx-1][pwally] = oldtile; + break; + } + + tilemap[pwallx][pwally] = oldtile | 0xc0; + } + } + + + pwallpos = (pwallstate/2)&63; + +} + diff --git a/src/lib/hb/wl_act2.c b/src/lib/hb/wl_act2.c new file mode 100755 index 00000000..d9d99a0b --- /dev/null +++ b/src/lib/hb/wl_act2.c @@ -0,0 +1,3872 @@ +// WL_ACT2.C + +#include "WL_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define PROJECTILESIZE 0xc000l + +#define BJRUNSPEED 2048 +#define BJJUMPSPEED 680 + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east, + southwest,south,southeast}; + +int starthitpoints[4][NUMENEMIES] = + // + // BABY MODE + // + { + {25, // guards + 50, // officer + 100, // SS + 1, // dogs + 850, // Hans + 850, // Schabbs + 200, // fake hitler + 800, // mecha hitler + 45, // mutants + 25, // ghosts + 25, // ghosts + 25, // ghosts + 25, // ghosts + + 850, // Gretel + 850, // Gift + 850, // Fat + 5, // en_spectre, + 1450, // en_angel, + 850, // en_trans, + 1050, // en_uber, + 950, // en_will, + 1250 // en_death + }, + // + // DON'T HURT ME MODE + // + {25, // guards + 50, // officer + 100, // SS + 1, // dogs + 950, // Hans + 950, // Schabbs + 300, // fake hitler + 950, // mecha hitler + 55, // mutants + 25, // ghosts + 25, // ghosts + 25, // ghosts + 25, // ghosts + + 950, // Gretel + 950, // Gift + 950, // Fat + 10, // en_spectre, + 1550, // en_angel, + 950, // en_trans, + 1150, // en_uber, + 1050, // en_will, + 1350 // en_death + }, + // + // BRING 'EM ON MODE + // + {25, // guards + 50, // officer + 100, // SS + 1, // dogs + + 1050, // Hans + 1550, // Schabbs + 400, // fake hitler + 1050, // mecha hitler + + 55, // mutants + 25, // ghosts + 25, // ghosts + 25, // ghosts + 25, // ghosts + + 1050, // Gretel + 1050, // Gift + 1050, // Fat + 15, // en_spectre, + 1650, // en_angel, + 1050, // en_trans, + 1250, // en_uber, + 1150, // en_will, + 1450 // en_death + }, + // + // DEATH INCARNATE MODE + // + {25, // guards + 50, // officer + 100, // SS + 1, // dogs + + 1200, // Hans + 2400, // Schabbs + 500, // fake hitler + 1200, // mecha hitler + + 65, // mutants + 25, // ghosts + 25, // ghosts + 25, // ghosts + 25, // ghosts + + 1200, // Gretel + 1200, // Gift + 1200, // Fat + 25, // en_spectre, + 2000, // en_angel, + 1200, // en_trans, + 1400, // en_uber, + 1300, // en_will, + 1600 // en_death + }} + ; + +void A_StartDeathCam (objtype *ob); + + +void T_Path (objtype *ob); +void T_Shoot (objtype *ob); +void T_Bite (objtype *ob); +void T_DogChase (objtype *ob); +void T_Chase (objtype *ob); +void T_Projectile (objtype *ob); +void T_Stand (objtype *ob); + +void A_DeathScream (objtype *ob); + +extern statetype s_rocket; +extern statetype s_smoke1; +extern statetype s_smoke2; +extern statetype s_smoke3; +extern statetype s_smoke4; +extern statetype s_boom2; +extern statetype s_boom3; + +void A_Smoke (objtype *ob); + +statetype s_rocket = {true,SPR_ROCKET_1,3,T_Projectile,A_Smoke,&s_rocket}; +statetype s_smoke1 = {false,SPR_SMOKE_1,3,NULL,NULL,&s_smoke2}; +statetype s_smoke2 = {false,SPR_SMOKE_2,3,NULL,NULL,&s_smoke3}; +statetype s_smoke3 = {false,SPR_SMOKE_3,3,NULL,NULL,&s_smoke4}; +statetype s_smoke4 = {false,SPR_SMOKE_4,3,NULL,NULL,NULL}; + +statetype s_boom1 = {false,SPR_BOOM_1,6,NULL,NULL,&s_boom2}; +statetype s_boom2 = {false,SPR_BOOM_2,6,NULL,NULL,&s_boom3}; +statetype s_boom3 = {false,SPR_BOOM_3,6,NULL,NULL,NULL}; + +#ifdef SPEAR + +extern statetype s_hrocket; +extern statetype s_hsmoke1; +extern statetype s_hsmoke2; +extern statetype s_hsmoke3; +extern statetype s_hsmoke4; +extern statetype s_hboom2; +extern statetype s_hboom3; + +void A_Smoke (objtype *ob); + +statetype s_hrocket = {true,SPR_HROCKET_1,3,T_Projectile,A_Smoke,&s_hrocket}; +statetype s_hsmoke1 = {false,SPR_HSMOKE_1,3,NULL,NULL,&s_hsmoke2}; +statetype s_hsmoke2 = {false,SPR_HSMOKE_2,3,NULL,NULL,&s_hsmoke3}; +statetype s_hsmoke3 = {false,SPR_HSMOKE_3,3,NULL,NULL,&s_hsmoke4}; +statetype s_hsmoke4 = {false,SPR_HSMOKE_4,3,NULL,NULL,NULL}; + +statetype s_hboom1 = {false,SPR_HBOOM_1,6,NULL,NULL,&s_hboom2}; +statetype s_hboom2 = {false,SPR_HBOOM_2,6,NULL,NULL,&s_hboom3}; +statetype s_hboom3 = {false,SPR_HBOOM_3,6,NULL,NULL,NULL}; + +#endif + +void T_Schabb (objtype *ob); +void T_SchabbThrow (objtype *ob); +void T_Fake (objtype *ob); +void T_FakeFire (objtype *ob); +void T_Ghosts (objtype *ob); + +void A_Slurpie (objtype *ob); +void A_HitlerMorph (objtype *ob); +void A_MechaSound (objtype *ob); + +/* +================= += += A_Smoke += +================= +*/ + +void A_Smoke (objtype *ob) +{ + GetNewActor (); +#ifdef SPEAR + if (ob->obclass == hrocketobj) + new->state = &s_hsmoke1; + else +#endif + new->state = &s_smoke1; + new->ticcount = 6; + + new->tilex = ob->tilex; + new->tiley = ob->tiley; + new->x = ob->x; + new->y = ob->y; + new->obclass = inertobj; + new->active = true; + + new->flags = FL_NEVERMARK; +} + + +/* +=================== += += ProjectileTryMove += += returns true if move ok +=================== +*/ + +#define PROJSIZE 0x2000 + +boolean ProjectileTryMove (objtype *ob) +{ + int xl,yl,xh,yh,x,y; + objtype *check; + long deltax,deltay; + + xl = (ob->x-PROJSIZE) >>TILESHIFT; + yl = (ob->y-PROJSIZE) >>TILESHIFT; + + xh = (ob->x+PROJSIZE) >>TILESHIFT; + yh = (ob->y+PROJSIZE) >>TILESHIFT; + +// +// check for solid walls +// + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + if (check && checkspeed*tics; + + deltax = FixedByFrac(speed,costable[ob->angle]); + deltay = -FixedByFrac(speed,sintable[ob->angle]); + + if (deltax>0x10000l) + deltax = 0x10000l; + if (deltay>0x10000l) + deltay = 0x10000l; + + ob->x += deltax; + ob->y += deltay; + + deltax = LABS(ob->x - player->x); + deltay = LABS(ob->y - player->y); + + if (!ProjectileTryMove (ob)) + { + if (ob->obclass == rocketobj) + { + PlaySoundLocActor(MISSILEHITSND,ob); + ob->state = &s_boom1; + } +#ifdef SPEAR + else if (ob->obclass == hrocketobj) + { + PlaySoundLocActor(MISSILEHITSND,ob); + ob->state = &s_hboom1; + } +#endif + else + ob->state = NULL; // mark for removal + + return; + } + + if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE) + { // hit the player + switch (ob->obclass) + { + case needleobj: + damage = (US_RndT() >>3) + 20; + break; + case rocketobj: + case hrocketobj: + case sparkobj: + damage = (US_RndT() >>3) + 30; + break; + case fireobj: + damage = (US_RndT() >>3); + break; + } + + TakeDamage (damage,ob); + ob->state = NULL; // mark for removal + return; + } + + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; + +} + + + + +/* +============================================================================= + + GUARD + +============================================================================= +*/ + +// +// guards +// + +extern statetype s_grdstand; + +extern statetype s_grdpath1; +extern statetype s_grdpath1s; +extern statetype s_grdpath2; +extern statetype s_grdpath3; +extern statetype s_grdpath3s; +extern statetype s_grdpath4; + +extern statetype s_grdpain; +extern statetype s_grdpain1; + +extern statetype s_grdgiveup; + +extern statetype s_grdshoot1; +extern statetype s_grdshoot2; +extern statetype s_grdshoot3; +extern statetype s_grdshoot4; + +extern statetype s_grdchase1; +extern statetype s_grdchase1s; +extern statetype s_grdchase2; +extern statetype s_grdchase3; +extern statetype s_grdchase3s; +extern statetype s_grdchase4; + +extern statetype s_grddie1; +extern statetype s_grddie1d; +extern statetype s_grddie2; +extern statetype s_grddie3; +extern statetype s_grddie4; + +statetype s_grdstand = {true,SPR_GRD_S_1,0,T_Stand,NULL,&s_grdstand}; + +statetype s_grdpath1 = {true,SPR_GRD_W1_1,20,T_Path,NULL,&s_grdpath1s}; +statetype s_grdpath1s = {true,SPR_GRD_W1_1,5,NULL,NULL,&s_grdpath2}; +statetype s_grdpath2 = {true,SPR_GRD_W2_1,15,T_Path,NULL,&s_grdpath3}; +statetype s_grdpath3 = {true,SPR_GRD_W3_1,20,T_Path,NULL,&s_grdpath3s}; +statetype s_grdpath3s = {true,SPR_GRD_W3_1,5,NULL,NULL,&s_grdpath4}; +statetype s_grdpath4 = {true,SPR_GRD_W4_1,15,T_Path,NULL,&s_grdpath1}; + +statetype s_grdpain = {2,SPR_GRD_PAIN_1,10,NULL,NULL,&s_grdchase1}; +statetype s_grdpain1 = {2,SPR_GRD_PAIN_2,10,NULL,NULL,&s_grdchase1}; + +statetype s_grdshoot1 = {false,SPR_GRD_SHOOT1,20,NULL,NULL,&s_grdshoot2}; +statetype s_grdshoot2 = {false,SPR_GRD_SHOOT2,20,NULL,T_Shoot,&s_grdshoot3}; +statetype s_grdshoot3 = {false,SPR_GRD_SHOOT3,20,NULL,NULL,&s_grdchase1}; + +statetype s_grdchase1 = {true,SPR_GRD_W1_1,10,T_Chase,NULL,&s_grdchase1s}; +statetype s_grdchase1s = {true,SPR_GRD_W1_1,3,NULL,NULL,&s_grdchase2}; +statetype s_grdchase2 = {true,SPR_GRD_W2_1,8,T_Chase,NULL,&s_grdchase3}; +statetype s_grdchase3 = {true,SPR_GRD_W3_1,10,T_Chase,NULL,&s_grdchase3s}; +statetype s_grdchase3s = {true,SPR_GRD_W3_1,3,NULL,NULL,&s_grdchase4}; +statetype s_grdchase4 = {true,SPR_GRD_W4_1,8,T_Chase,NULL,&s_grdchase1}; + +statetype s_grddie1 = {false,SPR_GRD_DIE_1,15,NULL,A_DeathScream,&s_grddie2}; +statetype s_grddie2 = {false,SPR_GRD_DIE_2,15,NULL,NULL,&s_grddie3}; +statetype s_grddie3 = {false,SPR_GRD_DIE_3,15,NULL,NULL,&s_grddie4}; +statetype s_grddie4 = {false,SPR_GRD_DEAD,0,NULL,NULL,&s_grddie4}; + + +#ifndef SPEAR +// +// ghosts +// +extern statetype s_blinkychase1; +extern statetype s_blinkychase2; +extern statetype s_inkychase1; +extern statetype s_inkychase2; +extern statetype s_pinkychase1; +extern statetype s_pinkychase2; +extern statetype s_clydechase1; +extern statetype s_clydechase2; + +statetype s_blinkychase1 = {false,SPR_BLINKY_W1,10,T_Ghosts,NULL,&s_blinkychase2}; +statetype s_blinkychase2 = {false,SPR_BLINKY_W2,10,T_Ghosts,NULL,&s_blinkychase1}; + +statetype s_inkychase1 = {false,SPR_INKY_W1,10,T_Ghosts,NULL,&s_inkychase2}; +statetype s_inkychase2 = {false,SPR_INKY_W2,10,T_Ghosts,NULL,&s_inkychase1}; + +statetype s_pinkychase1 = {false,SPR_PINKY_W1,10,T_Ghosts,NULL,&s_pinkychase2}; +statetype s_pinkychase2 = {false,SPR_PINKY_W2,10,T_Ghosts,NULL,&s_pinkychase1}; + +statetype s_clydechase1 = {false,SPR_CLYDE_W1,10,T_Ghosts,NULL,&s_clydechase2}; +statetype s_clydechase2 = {false,SPR_CLYDE_W2,10,T_Ghosts,NULL,&s_clydechase1}; +#endif + +// +// dogs +// + +extern statetype s_dogpath1; +extern statetype s_dogpath1s; +extern statetype s_dogpath2; +extern statetype s_dogpath3; +extern statetype s_dogpath3s; +extern statetype s_dogpath4; + +extern statetype s_dogjump1; +extern statetype s_dogjump2; +extern statetype s_dogjump3; +extern statetype s_dogjump4; +extern statetype s_dogjump5; + +extern statetype s_dogchase1; +extern statetype s_dogchase1s; +extern statetype s_dogchase2; +extern statetype s_dogchase3; +extern statetype s_dogchase3s; +extern statetype s_dogchase4; + +extern statetype s_dogdie1; +extern statetype s_dogdie1d; +extern statetype s_dogdie2; +extern statetype s_dogdie3; +extern statetype s_dogdead; + +statetype s_dogpath1 = {true,SPR_DOG_W1_1,20,T_Path,NULL,&s_dogpath1s}; +statetype s_dogpath1s = {true,SPR_DOG_W1_1,5,NULL,NULL,&s_dogpath2}; +statetype s_dogpath2 = {true,SPR_DOG_W2_1,15,T_Path,NULL,&s_dogpath3}; +statetype s_dogpath3 = {true,SPR_DOG_W3_1,20,T_Path,NULL,&s_dogpath3s}; +statetype s_dogpath3s = {true,SPR_DOG_W3_1,5,NULL,NULL,&s_dogpath4}; +statetype s_dogpath4 = {true,SPR_DOG_W4_1,15,T_Path,NULL,&s_dogpath1}; + +statetype s_dogjump1 = {false,SPR_DOG_JUMP1,10,NULL,NULL,&s_dogjump2}; +statetype s_dogjump2 = {false,SPR_DOG_JUMP2,10,NULL,T_Bite,&s_dogjump3}; +statetype s_dogjump3 = {false,SPR_DOG_JUMP3,10,NULL,NULL,&s_dogjump4}; +statetype s_dogjump4 = {false,SPR_DOG_JUMP1,10,NULL,NULL,&s_dogjump5}; +statetype s_dogjump5 = {false,SPR_DOG_W1_1,10,NULL,NULL,&s_dogchase1}; + +statetype s_dogchase1 = {true,SPR_DOG_W1_1,10,T_DogChase,NULL,&s_dogchase1s}; +statetype s_dogchase1s = {true,SPR_DOG_W1_1,3,NULL,NULL,&s_dogchase2}; +statetype s_dogchase2 = {true,SPR_DOG_W2_1,8,T_DogChase,NULL,&s_dogchase3}; +statetype s_dogchase3 = {true,SPR_DOG_W3_1,10,T_DogChase,NULL,&s_dogchase3s}; +statetype s_dogchase3s = {true,SPR_DOG_W3_1,3,NULL,NULL,&s_dogchase4}; +statetype s_dogchase4 = {true,SPR_DOG_W4_1,8,T_DogChase,NULL,&s_dogchase1}; + +statetype s_dogdie1 = {false,SPR_DOG_DIE_1,15,NULL,A_DeathScream,&s_dogdie2}; +statetype s_dogdie2 = {false,SPR_DOG_DIE_2,15,NULL,NULL,&s_dogdie3}; +statetype s_dogdie3 = {false,SPR_DOG_DIE_3,15,NULL,NULL,&s_dogdead}; +statetype s_dogdead = {false,SPR_DOG_DEAD,15,NULL,NULL,&s_dogdead}; + + +// +// officers +// + +extern statetype s_ofcstand; + +extern statetype s_ofcpath1; +extern statetype s_ofcpath1s; +extern statetype s_ofcpath2; +extern statetype s_ofcpath3; +extern statetype s_ofcpath3s; +extern statetype s_ofcpath4; + +extern statetype s_ofcpain; +extern statetype s_ofcpain1; + +extern statetype s_ofcgiveup; + +extern statetype s_ofcshoot1; +extern statetype s_ofcshoot2; +extern statetype s_ofcshoot3; +extern statetype s_ofcshoot4; + +extern statetype s_ofcchase1; +extern statetype s_ofcchase1s; +extern statetype s_ofcchase2; +extern statetype s_ofcchase3; +extern statetype s_ofcchase3s; +extern statetype s_ofcchase4; + +extern statetype s_ofcdie1; +extern statetype s_ofcdie2; +extern statetype s_ofcdie3; +extern statetype s_ofcdie4; +extern statetype s_ofcdie5; + +statetype s_ofcstand = {true,SPR_OFC_S_1,0,T_Stand,NULL,&s_ofcstand}; + +statetype s_ofcpath1 = {true,SPR_OFC_W1_1,20,T_Path,NULL,&s_ofcpath1s}; +statetype s_ofcpath1s = {true,SPR_OFC_W1_1,5,NULL,NULL,&s_ofcpath2}; +statetype s_ofcpath2 = {true,SPR_OFC_W2_1,15,T_Path,NULL,&s_ofcpath3}; +statetype s_ofcpath3 = {true,SPR_OFC_W3_1,20,T_Path,NULL,&s_ofcpath3s}; +statetype s_ofcpath3s = {true,SPR_OFC_W3_1,5,NULL,NULL,&s_ofcpath4}; +statetype s_ofcpath4 = {true,SPR_OFC_W4_1,15,T_Path,NULL,&s_ofcpath1}; + +statetype s_ofcpain = {2,SPR_OFC_PAIN_1,10,NULL,NULL,&s_ofcchase1}; +statetype s_ofcpain1 = {2,SPR_OFC_PAIN_2,10,NULL,NULL,&s_ofcchase1}; + +statetype s_ofcshoot1 = {false,SPR_OFC_SHOOT1,6,NULL,NULL,&s_ofcshoot2}; +statetype s_ofcshoot2 = {false,SPR_OFC_SHOOT2,20,NULL,T_Shoot,&s_ofcshoot3}; +statetype s_ofcshoot3 = {false,SPR_OFC_SHOOT3,10,NULL,NULL,&s_ofcchase1}; + +statetype s_ofcchase1 = {true,SPR_OFC_W1_1,10,T_Chase,NULL,&s_ofcchase1s}; +statetype s_ofcchase1s = {true,SPR_OFC_W1_1,3,NULL,NULL,&s_ofcchase2}; +statetype s_ofcchase2 = {true,SPR_OFC_W2_1,8,T_Chase,NULL,&s_ofcchase3}; +statetype s_ofcchase3 = {true,SPR_OFC_W3_1,10,T_Chase,NULL,&s_ofcchase3s}; +statetype s_ofcchase3s = {true,SPR_OFC_W3_1,3,NULL,NULL,&s_ofcchase4}; +statetype s_ofcchase4 = {true,SPR_OFC_W4_1,8,T_Chase,NULL,&s_ofcchase1}; + +statetype s_ofcdie1 = {false,SPR_OFC_DIE_1,11,NULL,A_DeathScream,&s_ofcdie2}; +statetype s_ofcdie2 = {false,SPR_OFC_DIE_2,11,NULL,NULL,&s_ofcdie3}; +statetype s_ofcdie3 = {false,SPR_OFC_DIE_3,11,NULL,NULL,&s_ofcdie4}; +statetype s_ofcdie4 = {false,SPR_OFC_DIE_4,11,NULL,NULL,&s_ofcdie5}; +statetype s_ofcdie5 = {false,SPR_OFC_DEAD,0,NULL,NULL,&s_ofcdie5}; + + +// +// mutant +// + +extern statetype s_mutstand; + +extern statetype s_mutpath1; +extern statetype s_mutpath1s; +extern statetype s_mutpath2; +extern statetype s_mutpath3; +extern statetype s_mutpath3s; +extern statetype s_mutpath4; + +extern statetype s_mutpain; +extern statetype s_mutpain1; + +extern statetype s_mutgiveup; + +extern statetype s_mutshoot1; +extern statetype s_mutshoot2; +extern statetype s_mutshoot3; +extern statetype s_mutshoot4; + +extern statetype s_mutchase1; +extern statetype s_mutchase1s; +extern statetype s_mutchase2; +extern statetype s_mutchase3; +extern statetype s_mutchase3s; +extern statetype s_mutchase4; + +extern statetype s_mutdie1; +extern statetype s_mutdie2; +extern statetype s_mutdie3; +extern statetype s_mutdie4; +extern statetype s_mutdie5; + +statetype s_mutstand = {true,SPR_MUT_S_1,0,T_Stand,NULL,&s_mutstand}; + +statetype s_mutpath1 = {true,SPR_MUT_W1_1,20,T_Path,NULL,&s_mutpath1s}; +statetype s_mutpath1s = {true,SPR_MUT_W1_1,5,NULL,NULL,&s_mutpath2}; +statetype s_mutpath2 = {true,SPR_MUT_W2_1,15,T_Path,NULL,&s_mutpath3}; +statetype s_mutpath3 = {true,SPR_MUT_W3_1,20,T_Path,NULL,&s_mutpath3s}; +statetype s_mutpath3s = {true,SPR_MUT_W3_1,5,NULL,NULL,&s_mutpath4}; +statetype s_mutpath4 = {true,SPR_MUT_W4_1,15,T_Path,NULL,&s_mutpath1}; + +statetype s_mutpain = {2,SPR_MUT_PAIN_1,10,NULL,NULL,&s_mutchase1}; +statetype s_mutpain1 = {2,SPR_MUT_PAIN_2,10,NULL,NULL,&s_mutchase1}; + +statetype s_mutshoot1 = {false,SPR_MUT_SHOOT1,6,NULL,T_Shoot,&s_mutshoot2}; +statetype s_mutshoot2 = {false,SPR_MUT_SHOOT2,20,NULL,NULL,&s_mutshoot3}; +statetype s_mutshoot3 = {false,SPR_MUT_SHOOT3,10,NULL,T_Shoot,&s_mutshoot4}; +statetype s_mutshoot4 = {false,SPR_MUT_SHOOT4,20,NULL,NULL,&s_mutchase1}; + +statetype s_mutchase1 = {true,SPR_MUT_W1_1,10,T_Chase,NULL,&s_mutchase1s}; +statetype s_mutchase1s = {true,SPR_MUT_W1_1,3,NULL,NULL,&s_mutchase2}; +statetype s_mutchase2 = {true,SPR_MUT_W2_1,8,T_Chase,NULL,&s_mutchase3}; +statetype s_mutchase3 = {true,SPR_MUT_W3_1,10,T_Chase,NULL,&s_mutchase3s}; +statetype s_mutchase3s = {true,SPR_MUT_W3_1,3,NULL,NULL,&s_mutchase4}; +statetype s_mutchase4 = {true,SPR_MUT_W4_1,8,T_Chase,NULL,&s_mutchase1}; + +statetype s_mutdie1 = {false,SPR_MUT_DIE_1,7,NULL,A_DeathScream,&s_mutdie2}; +statetype s_mutdie2 = {false,SPR_MUT_DIE_2,7,NULL,NULL,&s_mutdie3}; +statetype s_mutdie3 = {false,SPR_MUT_DIE_3,7,NULL,NULL,&s_mutdie4}; +statetype s_mutdie4 = {false,SPR_MUT_DIE_4,7,NULL,NULL,&s_mutdie5}; +statetype s_mutdie5 = {false,SPR_MUT_DEAD,0,NULL,NULL,&s_mutdie5}; + + +// +// SS +// + +extern statetype s_ssstand; + +extern statetype s_sspath1; +extern statetype s_sspath1s; +extern statetype s_sspath2; +extern statetype s_sspath3; +extern statetype s_sspath3s; +extern statetype s_sspath4; + +extern statetype s_sspain; +extern statetype s_sspain1; + +extern statetype s_ssshoot1; +extern statetype s_ssshoot2; +extern statetype s_ssshoot3; +extern statetype s_ssshoot4; +extern statetype s_ssshoot5; +extern statetype s_ssshoot6; +extern statetype s_ssshoot7; +extern statetype s_ssshoot8; +extern statetype s_ssshoot9; + +extern statetype s_sschase1; +extern statetype s_sschase1s; +extern statetype s_sschase2; +extern statetype s_sschase3; +extern statetype s_sschase3s; +extern statetype s_sschase4; + +extern statetype s_ssdie1; +extern statetype s_ssdie2; +extern statetype s_ssdie3; +extern statetype s_ssdie4; + +statetype s_ssstand = {true,SPR_SS_S_1,0,T_Stand,NULL,&s_ssstand}; + +statetype s_sspath1 = {true,SPR_SS_W1_1,20,T_Path,NULL,&s_sspath1s}; +statetype s_sspath1s = {true,SPR_SS_W1_1,5,NULL,NULL,&s_sspath2}; +statetype s_sspath2 = {true,SPR_SS_W2_1,15,T_Path,NULL,&s_sspath3}; +statetype s_sspath3 = {true,SPR_SS_W3_1,20,T_Path,NULL,&s_sspath3s}; +statetype s_sspath3s = {true,SPR_SS_W3_1,5,NULL,NULL,&s_sspath4}; +statetype s_sspath4 = {true,SPR_SS_W4_1,15,T_Path,NULL,&s_sspath1}; + +statetype s_sspain = {2,SPR_SS_PAIN_1,10,NULL,NULL,&s_sschase1}; +statetype s_sspain1 = {2,SPR_SS_PAIN_2,10,NULL,NULL,&s_sschase1}; + +statetype s_ssshoot1 = {false,SPR_SS_SHOOT1,20,NULL,NULL,&s_ssshoot2}; +statetype s_ssshoot2 = {false,SPR_SS_SHOOT2,20,NULL,T_Shoot,&s_ssshoot3}; +statetype s_ssshoot3 = {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_ssshoot4}; +statetype s_ssshoot4 = {false,SPR_SS_SHOOT2,10,NULL,T_Shoot,&s_ssshoot5}; +statetype s_ssshoot5 = {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_ssshoot6}; +statetype s_ssshoot6 = {false,SPR_SS_SHOOT2,10,NULL,T_Shoot,&s_ssshoot7}; +statetype s_ssshoot7 = {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_ssshoot8}; +statetype s_ssshoot8 = {false,SPR_SS_SHOOT2,10,NULL,T_Shoot,&s_ssshoot9}; +statetype s_ssshoot9 = {false,SPR_SS_SHOOT3,10,NULL,NULL,&s_sschase1}; + +statetype s_sschase1 = {true,SPR_SS_W1_1,10,T_Chase,NULL,&s_sschase1s}; +statetype s_sschase1s = {true,SPR_SS_W1_1,3,NULL,NULL,&s_sschase2}; +statetype s_sschase2 = {true,SPR_SS_W2_1,8,T_Chase,NULL,&s_sschase3}; +statetype s_sschase3 = {true,SPR_SS_W3_1,10,T_Chase,NULL,&s_sschase3s}; +statetype s_sschase3s = {true,SPR_SS_W3_1,3,NULL,NULL,&s_sschase4}; +statetype s_sschase4 = {true,SPR_SS_W4_1,8,T_Chase,NULL,&s_sschase1}; + +statetype s_ssdie1 = {false,SPR_SS_DIE_1,15,NULL,A_DeathScream,&s_ssdie2}; +statetype s_ssdie2 = {false,SPR_SS_DIE_2,15,NULL,NULL,&s_ssdie3}; +statetype s_ssdie3 = {false,SPR_SS_DIE_3,15,NULL,NULL,&s_ssdie4}; +statetype s_ssdie4 = {false,SPR_SS_DEAD,0,NULL,NULL,&s_ssdie4}; + + +#ifndef SPEAR +// +// hans +// +extern statetype s_bossstand; + +extern statetype s_bosschase1; +extern statetype s_bosschase1s; +extern statetype s_bosschase2; +extern statetype s_bosschase3; +extern statetype s_bosschase3s; +extern statetype s_bosschase4; + +extern statetype s_bossdie1; +extern statetype s_bossdie2; +extern statetype s_bossdie3; +extern statetype s_bossdie4; + +extern statetype s_bossshoot1; +extern statetype s_bossshoot2; +extern statetype s_bossshoot3; +extern statetype s_bossshoot4; +extern statetype s_bossshoot5; +extern statetype s_bossshoot6; +extern statetype s_bossshoot7; +extern statetype s_bossshoot8; + + +statetype s_bossstand = {false,SPR_BOSS_W1,0,T_Stand,NULL,&s_bossstand}; + +statetype s_bosschase1 = {false,SPR_BOSS_W1,10,T_Chase,NULL,&s_bosschase1s}; +statetype s_bosschase1s = {false,SPR_BOSS_W1,3,NULL,NULL,&s_bosschase2}; +statetype s_bosschase2 = {false,SPR_BOSS_W2,8,T_Chase,NULL,&s_bosschase3}; +statetype s_bosschase3 = {false,SPR_BOSS_W3,10,T_Chase,NULL,&s_bosschase3s}; +statetype s_bosschase3s = {false,SPR_BOSS_W3,3,NULL,NULL,&s_bosschase4}; +statetype s_bosschase4 = {false,SPR_BOSS_W4,8,T_Chase,NULL,&s_bosschase1}; + +statetype s_bossdie1 = {false,SPR_BOSS_DIE1,15,NULL,A_DeathScream,&s_bossdie2}; +statetype s_bossdie2 = {false,SPR_BOSS_DIE2,15,NULL,NULL,&s_bossdie3}; +statetype s_bossdie3 = {false,SPR_BOSS_DIE3,15,NULL,NULL,&s_bossdie4}; +statetype s_bossdie4 = {false,SPR_BOSS_DEAD,0,NULL,NULL,&s_bossdie4}; + +statetype s_bossshoot1 = {false,SPR_BOSS_SHOOT1,30,NULL,NULL,&s_bossshoot2}; +statetype s_bossshoot2 = {false,SPR_BOSS_SHOOT2,10,NULL,T_Shoot,&s_bossshoot3}; +statetype s_bossshoot3 = {false,SPR_BOSS_SHOOT3,10,NULL,T_Shoot,&s_bossshoot4}; +statetype s_bossshoot4 = {false,SPR_BOSS_SHOOT2,10,NULL,T_Shoot,&s_bossshoot5}; +statetype s_bossshoot5 = {false,SPR_BOSS_SHOOT3,10,NULL,T_Shoot,&s_bossshoot6}; +statetype s_bossshoot6 = {false,SPR_BOSS_SHOOT2,10,NULL,T_Shoot,&s_bossshoot7}; +statetype s_bossshoot7 = {false,SPR_BOSS_SHOOT3,10,NULL,T_Shoot,&s_bossshoot8}; +statetype s_bossshoot8 = {false,SPR_BOSS_SHOOT1,10,NULL,NULL,&s_bosschase1}; + + +// +// gretel +// +extern statetype s_gretelstand; + +extern statetype s_gretelchase1; +extern statetype s_gretelchase1s; +extern statetype s_gretelchase2; +extern statetype s_gretelchase3; +extern statetype s_gretelchase3s; +extern statetype s_gretelchase4; + +extern statetype s_greteldie1; +extern statetype s_greteldie2; +extern statetype s_greteldie3; +extern statetype s_greteldie4; + +extern statetype s_gretelshoot1; +extern statetype s_gretelshoot2; +extern statetype s_gretelshoot3; +extern statetype s_gretelshoot4; +extern statetype s_gretelshoot5; +extern statetype s_gretelshoot6; +extern statetype s_gretelshoot7; +extern statetype s_gretelshoot8; + + +statetype s_gretelstand = {false,SPR_GRETEL_W1,0,T_Stand,NULL,&s_gretelstand}; + +statetype s_gretelchase1 = {false,SPR_GRETEL_W1,10,T_Chase,NULL,&s_gretelchase1s}; +statetype s_gretelchase1s = {false,SPR_GRETEL_W1,3,NULL,NULL,&s_gretelchase2}; +statetype s_gretelchase2 = {false,SPR_GRETEL_W2,8,T_Chase,NULL,&s_gretelchase3}; +statetype s_gretelchase3 = {false,SPR_GRETEL_W3,10,T_Chase,NULL,&s_gretelchase3s}; +statetype s_gretelchase3s = {false,SPR_GRETEL_W3,3,NULL,NULL,&s_gretelchase4}; +statetype s_gretelchase4 = {false,SPR_GRETEL_W4,8,T_Chase,NULL,&s_gretelchase1}; + +statetype s_greteldie1 = {false,SPR_GRETEL_DIE1,15,NULL,A_DeathScream,&s_greteldie2}; +statetype s_greteldie2 = {false,SPR_GRETEL_DIE2,15,NULL,NULL,&s_greteldie3}; +statetype s_greteldie3 = {false,SPR_GRETEL_DIE3,15,NULL,NULL,&s_greteldie4}; +statetype s_greteldie4 = {false,SPR_GRETEL_DEAD,0,NULL,NULL,&s_greteldie4}; + +statetype s_gretelshoot1 = {false,SPR_GRETEL_SHOOT1,30,NULL,NULL,&s_gretelshoot2}; +statetype s_gretelshoot2 = {false,SPR_GRETEL_SHOOT2,10,NULL,T_Shoot,&s_gretelshoot3}; +statetype s_gretelshoot3 = {false,SPR_GRETEL_SHOOT3,10,NULL,T_Shoot,&s_gretelshoot4}; +statetype s_gretelshoot4 = {false,SPR_GRETEL_SHOOT2,10,NULL,T_Shoot,&s_gretelshoot5}; +statetype s_gretelshoot5 = {false,SPR_GRETEL_SHOOT3,10,NULL,T_Shoot,&s_gretelshoot6}; +statetype s_gretelshoot6 = {false,SPR_GRETEL_SHOOT2,10,NULL,T_Shoot,&s_gretelshoot7}; +statetype s_gretelshoot7 = {false,SPR_GRETEL_SHOOT3,10,NULL,T_Shoot,&s_gretelshoot8}; +statetype s_gretelshoot8 = {false,SPR_GRETEL_SHOOT1,10,NULL,NULL,&s_gretelchase1}; +#endif + + +/* +=============== += += SpawnStand += +=============== +*/ + +void SpawnStand (enemy_t which, int tilex, int tiley, int dir) +{ + unsigned far *map,tile; + + switch (which) + { + case en_guard: + SpawnNewObj (tilex,tiley,&s_grdstand); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_officer: + SpawnNewObj (tilex,tiley,&s_ofcstand); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_mutant: + SpawnNewObj (tilex,tiley,&s_mutstand); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_ss: + SpawnNewObj (tilex,tiley,&s_ssstand); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + } + + + map = mapsegs[0]+farmapylookup[tiley]+tilex; + if (*map == AMBUSHTILE) + { + tilemap[tilex][tiley] = 0; + + if (*(map+1) >= AREATILE) + tile = *(map+1); + if (*(map-mapwidth) >= AREATILE) + tile = *(map-mapwidth); + if (*(map+mapwidth) >= AREATILE) + tile = *(map+mapwidth); + if ( *(map-1) >= AREATILE) + tile = *(map-1); + + *map = tile; + new->areanumber = tile-AREATILE; + + new->flags |= FL_AMBUSH; + } + + new->obclass = guardobj+which; + new->hitpoints = starthitpoints[gamestate.difficulty][which]; + new->dir = dir*2; + new->flags |= FL_SHOOTABLE; +} + + + +/* +=============== += += SpawnDeadGuard += +=============== +*/ + +void SpawnDeadGuard (int tilex, int tiley) +{ + SpawnNewObj (tilex,tiley,&s_grddie4); + new->obclass = inertobj; +} + + + +#ifndef SPEAR +/* +=============== += += SpawnBoss += +=============== +*/ + +void SpawnBoss (int tilex, int tiley) +{ + unsigned far *map,tile; + + SpawnNewObj (tilex,tiley,&s_bossstand); + new->speed = SPDPATROL; + + new->obclass = bossobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_boss]; + new->dir = south; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + +/* +=============== += += SpawnGretel += +=============== +*/ + +void SpawnGretel (int tilex, int tiley) +{ + unsigned far *map,tile; + + SpawnNewObj (tilex,tiley,&s_gretelstand); + new->speed = SPDPATROL; + + new->obclass = gretelobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_gretel]; + new->dir = north; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} +#endif + +/* +=============== += += SpawnPatrol += +=============== +*/ + +void SpawnPatrol (enemy_t which, int tilex, int tiley, int dir) +{ + switch (which) + { + case en_guard: + SpawnNewObj (tilex,tiley,&s_grdpath1); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_officer: + SpawnNewObj (tilex,tiley,&s_ofcpath1); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_ss: + SpawnNewObj (tilex,tiley,&s_sspath1); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_mutant: + SpawnNewObj (tilex,tiley,&s_mutpath1); + new->speed = SPDPATROL; + if (!loadedgame) + gamestate.killtotal++; + break; + + case en_dog: + SpawnNewObj (tilex,tiley,&s_dogpath1); + new->speed = SPDDOG; + if (!loadedgame) + gamestate.killtotal++; + break; + } + + new->obclass = guardobj+which; + new->dir = dir*2; + new->hitpoints = starthitpoints[gamestate.difficulty][which]; + new->distance = tileglobal; + new->flags |= FL_SHOOTABLE; + new->active = true; + + actorat[new->tilex][new->tiley] = NULL; // don't use original spot + + switch (dir) + { + case 0: + new->tilex++; + break; + case 1: + new->tiley--; + break; + case 2: + new->tilex--; + break; + case 3: + new->tiley++; + break; + } + + actorat[new->tilex][new->tiley] = new; +} + + + +/* +================== += += A_DeathScream += +================== +*/ + +void A_DeathScream (objtype *ob) +{ +#ifndef UPLOAD +#ifndef SPEAR + if (mapon==9 && !US_RndT()) +#else + if ((mapon==18 || mapon==19) && !US_RndT()) +#endif + { + switch(ob->obclass) + { + case mutantobj: + case guardobj: + case officerobj: + case ssobj: + case dogobj: + PlaySoundLocActor(DEATHSCREAM6SND,ob); + return; + } + } +#endif + + switch (ob->obclass) + { + case mutantobj: + PlaySoundLocActor(AHHHGSND,ob); + break; + + case guardobj: + { + int sounds[9]={ DEATHSCREAM1SND, + DEATHSCREAM2SND, + DEATHSCREAM3SND, + DEATHSCREAM4SND, + DEATHSCREAM5SND, + DEATHSCREAM7SND, + DEATHSCREAM8SND, + DEATHSCREAM9SND + }; + + #ifndef UPLOAD + PlaySoundLocActor(sounds[US_RndT()%8],ob); + #else + PlaySoundLocActor(sounds[US_RndT()%2],ob); + #endif + } + break; + case officerobj: + PlaySoundLocActor(NEINSOVASSND,ob); + break; + case ssobj: + PlaySoundLocActor(LEBENSND,ob); // JAB + break; + case dogobj: + PlaySoundLocActor(DOGDEATHSND,ob); // JAB + break; +#ifndef SPEAR + case bossobj: + SD_PlaySound(MUTTISND); // JAB + break; + case schabbobj: + SD_PlaySound(MEINGOTTSND); + break; + case fakeobj: + SD_PlaySound(HITLERHASND); + break; + case mechahitlerobj: + SD_PlaySound(SCHEISTSND); + break; + case realhitlerobj: + SD_PlaySound(EVASND); + break; + case gretelobj: + SD_PlaySound(MEINSND); + break; + case giftobj: + SD_PlaySound(DONNERSND); + break; + case fatobj: + SD_PlaySound(ROSESND); + break; +#else + case spectreobj: + SD_PlaySound(GHOSTFADESND); + break; + case angelobj: + SD_PlaySound(ANGELDEATHSND); + break; + case transobj: + SD_PlaySound(TRANSDEATHSND); + break; + case uberobj: + SD_PlaySound(UBERDEATHSND); + break; + case willobj: + SD_PlaySound(WILHELMDEATHSND); + break; + case deathobj: + SD_PlaySound(KNIGHTDEATHSND); + break; +#endif + } +} + + +/* +============================================================================= + + SPEAR ACTORS + +============================================================================= +*/ + +#ifdef SPEAR + +void T_Launch (objtype *ob); +void T_Will (objtype *ob); + +extern statetype s_angelshoot1; +extern statetype s_deathshoot1; +extern statetype s_spark1; + +// +// trans +// +extern statetype s_transstand; + +extern statetype s_transchase1; +extern statetype s_transchase1s; +extern statetype s_transchase2; +extern statetype s_transchase3; +extern statetype s_transchase3s; +extern statetype s_transchase4; + +extern statetype s_transdie0; +extern statetype s_transdie01; +extern statetype s_transdie1; +extern statetype s_transdie2; +extern statetype s_transdie3; +extern statetype s_transdie4; + +extern statetype s_transshoot1; +extern statetype s_transshoot2; +extern statetype s_transshoot3; +extern statetype s_transshoot4; +extern statetype s_transshoot5; +extern statetype s_transshoot6; +extern statetype s_transshoot7; +extern statetype s_transshoot8; + + +statetype s_transstand = {false,SPR_TRANS_W1,0,T_Stand,NULL,&s_transstand}; + +statetype s_transchase1 = {false,SPR_TRANS_W1,10,T_Chase,NULL,&s_transchase1s}; +statetype s_transchase1s = {false,SPR_TRANS_W1,3,NULL,NULL,&s_transchase2}; +statetype s_transchase2 = {false,SPR_TRANS_W2,8,T_Chase,NULL,&s_transchase3}; +statetype s_transchase3 = {false,SPR_TRANS_W3,10,T_Chase,NULL,&s_transchase3s}; +statetype s_transchase3s = {false,SPR_TRANS_W3,3,NULL,NULL,&s_transchase4}; +statetype s_transchase4 = {false,SPR_TRANS_W4,8,T_Chase,NULL,&s_transchase1}; + +statetype s_transdie0 = {false,SPR_TRANS_W1,1,NULL,A_DeathScream,&s_transdie01}; +statetype s_transdie01 = {false,SPR_TRANS_W1,1,NULL,NULL,&s_transdie1}; +statetype s_transdie1 = {false,SPR_TRANS_DIE1,15,NULL,NULL,&s_transdie2}; +statetype s_transdie2 = {false,SPR_TRANS_DIE2,15,NULL,NULL,&s_transdie3}; +statetype s_transdie3 = {false,SPR_TRANS_DIE3,15,NULL,NULL,&s_transdie4}; +statetype s_transdie4 = {false,SPR_TRANS_DEAD,0,NULL,NULL,&s_transdie4}; + +statetype s_transshoot1 = {false,SPR_TRANS_SHOOT1,30,NULL,NULL,&s_transshoot2}; +statetype s_transshoot2 = {false,SPR_TRANS_SHOOT2,10,NULL,T_Shoot,&s_transshoot3}; +statetype s_transshoot3 = {false,SPR_TRANS_SHOOT3,10,NULL,T_Shoot,&s_transshoot4}; +statetype s_transshoot4 = {false,SPR_TRANS_SHOOT2,10,NULL,T_Shoot,&s_transshoot5}; +statetype s_transshoot5 = {false,SPR_TRANS_SHOOT3,10,NULL,T_Shoot,&s_transshoot6}; +statetype s_transshoot6 = {false,SPR_TRANS_SHOOT2,10,NULL,T_Shoot,&s_transshoot7}; +statetype s_transshoot7 = {false,SPR_TRANS_SHOOT3,10,NULL,T_Shoot,&s_transshoot8}; +statetype s_transshoot8 = {false,SPR_TRANS_SHOOT1,10,NULL,NULL,&s_transchase1}; + + +/* +=============== += += SpawnTrans += +=============== +*/ + +void SpawnTrans (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (SoundBlasterPresent && DigiMode != sds_Off) + s_transdie01.tictime = 105; + + SpawnNewObj (tilex,tiley,&s_transstand); + new->obclass = transobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_trans]; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +// +// uber +// +void T_UShoot (objtype *ob); + +extern statetype s_uberstand; + +extern statetype s_uberchase1; +extern statetype s_uberchase1s; +extern statetype s_uberchase2; +extern statetype s_uberchase3; +extern statetype s_uberchase3s; +extern statetype s_uberchase4; + +extern statetype s_uberdie0; +extern statetype s_uberdie01; +extern statetype s_uberdie1; +extern statetype s_uberdie2; +extern statetype s_uberdie3; +extern statetype s_uberdie4; +extern statetype s_uberdie5; + +extern statetype s_ubershoot1; +extern statetype s_ubershoot2; +extern statetype s_ubershoot3; +extern statetype s_ubershoot4; +extern statetype s_ubershoot5; +extern statetype s_ubershoot6; +extern statetype s_ubershoot7; + + +statetype s_uberstand = {false,SPR_UBER_W1,0,T_Stand,NULL,&s_uberstand}; + +statetype s_uberchase1 = {false,SPR_UBER_W1,10,T_Chase,NULL,&s_uberchase1s}; +statetype s_uberchase1s = {false,SPR_UBER_W1,3,NULL,NULL,&s_uberchase2}; +statetype s_uberchase2 = {false,SPR_UBER_W2,8,T_Chase,NULL,&s_uberchase3}; +statetype s_uberchase3 = {false,SPR_UBER_W3,10,T_Chase,NULL,&s_uberchase3s}; +statetype s_uberchase3s = {false,SPR_UBER_W3,3,NULL,NULL,&s_uberchase4}; +statetype s_uberchase4 = {false,SPR_UBER_W4,8,T_Chase,NULL,&s_uberchase1}; + +statetype s_uberdie0 = {false,SPR_UBER_W1,1,NULL,A_DeathScream,&s_uberdie01}; +statetype s_uberdie01 = {false,SPR_UBER_W1,1,NULL,NULL,&s_uberdie1}; +statetype s_uberdie1 = {false,SPR_UBER_DIE1,15,NULL,NULL,&s_uberdie2}; +statetype s_uberdie2 = {false,SPR_UBER_DIE2,15,NULL,NULL,&s_uberdie3}; +statetype s_uberdie3 = {false,SPR_UBER_DIE3,15,NULL,NULL,&s_uberdie4}; +statetype s_uberdie4 = {false,SPR_UBER_DIE4,15,NULL,NULL,&s_uberdie5}; +statetype s_uberdie5 = {false,SPR_UBER_DEAD,0,NULL,NULL,&s_uberdie5}; + +statetype s_ubershoot1 = {false,SPR_UBER_SHOOT1,30,NULL,NULL,&s_ubershoot2}; +statetype s_ubershoot2 = {false,SPR_UBER_SHOOT2,12,NULL,T_UShoot,&s_ubershoot3}; +statetype s_ubershoot3 = {false,SPR_UBER_SHOOT3,12,NULL,T_UShoot,&s_ubershoot4}; +statetype s_ubershoot4 = {false,SPR_UBER_SHOOT4,12,NULL,T_UShoot,&s_ubershoot5}; +statetype s_ubershoot5 = {false,SPR_UBER_SHOOT3,12,NULL,T_UShoot,&s_ubershoot6}; +statetype s_ubershoot6 = {false,SPR_UBER_SHOOT2,12,NULL,T_UShoot,&s_ubershoot7}; +statetype s_ubershoot7 = {false,SPR_UBER_SHOOT1,12,NULL,NULL,&s_uberchase1}; + + +/* +=============== += += SpawnUber += +=============== +*/ + +void SpawnUber (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (SoundBlasterPresent && DigiMode != sds_Off) + s_uberdie01.tictime = 70; + + SpawnNewObj (tilex,tiley,&s_uberstand); + new->obclass = uberobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_uber]; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +=============== += += T_UShoot += +=============== +*/ + +void T_UShoot (objtype *ob) +{ + int dx,dy,dist; + + T_Shoot (ob); + + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + if (dist <= 1) + TakeDamage (10,ob); +} + + +// +// will +// +extern statetype s_willstand; + +extern statetype s_willchase1; +extern statetype s_willchase1s; +extern statetype s_willchase2; +extern statetype s_willchase3; +extern statetype s_willchase3s; +extern statetype s_willchase4; + +extern statetype s_willdie1; +extern statetype s_willdie2; +extern statetype s_willdie3; +extern statetype s_willdie4; +extern statetype s_willdie5; +extern statetype s_willdie6; + +extern statetype s_willshoot1; +extern statetype s_willshoot2; +extern statetype s_willshoot3; +extern statetype s_willshoot4; +extern statetype s_willshoot5; +extern statetype s_willshoot6; + + +statetype s_willstand = {false,SPR_WILL_W1,0,T_Stand,NULL,&s_willstand}; + +statetype s_willchase1 = {false,SPR_WILL_W1,10,T_Will,NULL,&s_willchase1s}; +statetype s_willchase1s = {false,SPR_WILL_W1,3,NULL,NULL,&s_willchase2}; +statetype s_willchase2 = {false,SPR_WILL_W2,8,T_Will,NULL,&s_willchase3}; +statetype s_willchase3 = {false,SPR_WILL_W3,10,T_Will,NULL,&s_willchase3s}; +statetype s_willchase3s = {false,SPR_WILL_W3,3,NULL,NULL,&s_willchase4}; +statetype s_willchase4 = {false,SPR_WILL_W4,8,T_Will,NULL,&s_willchase1}; + +statetype s_willdeathcam = {false,SPR_WILL_W1,1,NULL,NULL,&s_willdie1}; + +statetype s_willdie1 = {false,SPR_WILL_W1,1,NULL,A_DeathScream,&s_willdie2}; +statetype s_willdie2 = {false,SPR_WILL_W1,10,NULL,NULL,&s_willdie3}; +statetype s_willdie3 = {false,SPR_WILL_DIE1,10,NULL,NULL,&s_willdie4}; +statetype s_willdie4 = {false,SPR_WILL_DIE2,10,NULL,NULL,&s_willdie5}; +statetype s_willdie5 = {false,SPR_WILL_DIE3,10,NULL,NULL,&s_willdie6}; +statetype s_willdie6 = {false,SPR_WILL_DEAD,20,NULL,NULL,&s_willdie6}; + +statetype s_willshoot1 = {false,SPR_WILL_SHOOT1,30,NULL,NULL,&s_willshoot2}; +statetype s_willshoot2 = {false,SPR_WILL_SHOOT2,10,NULL,T_Launch,&s_willshoot3}; +statetype s_willshoot3 = {false,SPR_WILL_SHOOT3,10,NULL,T_Shoot,&s_willshoot4}; +statetype s_willshoot4 = {false,SPR_WILL_SHOOT4,10,NULL,T_Shoot,&s_willshoot5}; +statetype s_willshoot5 = {false,SPR_WILL_SHOOT3,10,NULL,T_Shoot,&s_willshoot6}; +statetype s_willshoot6 = {false,SPR_WILL_SHOOT4,10,NULL,T_Shoot,&s_willchase1}; + + +/* +=============== += += SpawnWill += +=============== +*/ + +void SpawnWill (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (SoundBlasterPresent && DigiMode != sds_Off) + s_willdie2.tictime = 70; + + SpawnNewObj (tilex,tiley,&s_willstand); + new->obclass = willobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_will]; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +================ += += T_Will += +================ +*/ + +void T_Will (objtype *ob) +{ + long move; + int dx,dy,dist; + boolean dodge; + + dodge = false; + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + + if (CheckLine(ob)) // got a shot at player? + { + if ( US_RndT() < (tics<<3) ) + { + // + // go into attack frame + // + if (ob->obclass == willobj) + NewState (ob,&s_willshoot1); + else if (ob->obclass == angelobj) + NewState (ob,&s_angelshoot1); + else + NewState (ob,&s_deathshoot1); + return; + } + dodge = true; + } + + if (ob->dir == nodir) + { + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (ob->distance < 0) + { + // + // waiting for a door to open + // + OpenDoor (-ob->distance-1); + if (doorobjlist[-ob->distance-1].action != dr_open) + return; + ob->distance = TILEGLOBAL; // go ahead, the door is now opoen + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (dist <4) + SelectRunDir (ob); + else if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + + +// +// death +// +extern statetype s_deathstand; + +extern statetype s_deathchase1; +extern statetype s_deathchase1s; +extern statetype s_deathchase2; +extern statetype s_deathchase3; +extern statetype s_deathchase3s; +extern statetype s_deathchase4; + +extern statetype s_deathdie1; +extern statetype s_deathdie2; +extern statetype s_deathdie3; +extern statetype s_deathdie4; +extern statetype s_deathdie5; +extern statetype s_deathdie6; +extern statetype s_deathdie7; +extern statetype s_deathdie8; +extern statetype s_deathdie9; + +extern statetype s_deathshoot1; +extern statetype s_deathshoot2; +extern statetype s_deathshoot3; +extern statetype s_deathshoot4; +extern statetype s_deathshoot5; + + +statetype s_deathstand = {false,SPR_DEATH_W1,0,T_Stand,NULL,&s_deathstand}; + +statetype s_deathchase1 = {false,SPR_DEATH_W1,10,T_Will,NULL,&s_deathchase1s}; +statetype s_deathchase1s = {false,SPR_DEATH_W1,3,NULL,NULL,&s_deathchase2}; +statetype s_deathchase2 = {false,SPR_DEATH_W2,8,T_Will,NULL,&s_deathchase3}; +statetype s_deathchase3 = {false,SPR_DEATH_W3,10,T_Will,NULL,&s_deathchase3s}; +statetype s_deathchase3s = {false,SPR_DEATH_W3,3,NULL,NULL,&s_deathchase4}; +statetype s_deathchase4 = {false,SPR_DEATH_W4,8,T_Will,NULL,&s_deathchase1}; + +statetype s_deathdeathcam = {false,SPR_DEATH_W1,1,NULL,NULL,&s_deathdie1}; + +statetype s_deathdie1 = {false,SPR_DEATH_W1,1,NULL,A_DeathScream,&s_deathdie2}; +statetype s_deathdie2 = {false,SPR_DEATH_W1,10,NULL,NULL,&s_deathdie3}; +statetype s_deathdie3 = {false,SPR_DEATH_DIE1,10,NULL,NULL,&s_deathdie4}; +statetype s_deathdie4 = {false,SPR_DEATH_DIE2,10,NULL,NULL,&s_deathdie5}; +statetype s_deathdie5 = {false,SPR_DEATH_DIE3,10,NULL,NULL,&s_deathdie6}; +statetype s_deathdie6 = {false,SPR_DEATH_DIE4,10,NULL,NULL,&s_deathdie7}; +statetype s_deathdie7 = {false,SPR_DEATH_DIE5,10,NULL,NULL,&s_deathdie8}; +statetype s_deathdie8 = {false,SPR_DEATH_DIE6,10,NULL,NULL,&s_deathdie9}; +statetype s_deathdie9 = {false,SPR_DEATH_DEAD,0,NULL,NULL,&s_deathdie9}; + +statetype s_deathshoot1 = {false,SPR_DEATH_SHOOT1,30,NULL,NULL,&s_deathshoot2}; +statetype s_deathshoot2 = {false,SPR_DEATH_SHOOT2,10,NULL,T_Launch,&s_deathshoot3}; +statetype s_deathshoot3 = {false,SPR_DEATH_SHOOT4,10,NULL,T_Shoot,&s_deathshoot4}; +statetype s_deathshoot4 = {false,SPR_DEATH_SHOOT3,10,NULL,T_Launch,&s_deathshoot5}; +statetype s_deathshoot5 = {false,SPR_DEATH_SHOOT4,10,NULL,T_Shoot,&s_deathchase1}; + + +/* +=============== += += SpawnDeath += +=============== +*/ + +void SpawnDeath (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (SoundBlasterPresent && DigiMode != sds_Off) + s_deathdie2.tictime = 105; + + SpawnNewObj (tilex,tiley,&s_deathstand); + new->obclass = deathobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_death]; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + +/* +=============== += += T_Launch += +=============== +*/ + +void T_Launch (objtype *ob) +{ + long deltax,deltay; + float angle; + int iangle; + + deltax = player->x - ob->x; + deltay = ob->y - player->y; + angle = atan2 (deltay,deltax); + if (angle<0) + angle = M_PI*2+angle; + iangle = angle/(M_PI*2)*ANGLES; + if (ob->obclass == deathobj) + { + T_Shoot (ob); + if (ob->state == &s_deathshoot2) + { + iangle-=4; + if (iangle<0) + iangle+=ANGLES; + } + else + { + iangle+=4; + if (iangle>=ANGLES) + iangle-=ANGLES; + } + } + + GetNewActor (); + new->state = &s_rocket; + new->ticcount = 1; + + new->tilex = ob->tilex; + new->tiley = ob->tiley; + new->x = ob->x; + new->y = ob->y; + new->obclass = rocketobj; + switch(ob->obclass) + { + case deathobj: + new->state = &s_hrocket; + new->obclass = hrocketobj; + PlaySoundLocActor (KNIGHTMISSILESND,new); + break; + case angelobj: + new->state = &s_spark1; + new->obclass = sparkobj; + PlaySoundLocActor (ANGELFIRESND,new); + break; + default: + PlaySoundLocActor (MISSILEFIRESND,new); + } + + new->dir = nodir; + new->angle = iangle; + new->speed = 0x2000l; + new->flags = FL_NONMARK; + new->active = true; +} + + + +// +// angel +// +void A_Relaunch (objtype *ob); +void A_Victory (objtype *ob); +void A_StartAttack (objtype *ob); +void A_Breathing (objtype *ob); + +extern statetype s_angelstand; + +extern statetype s_angelchase1; +extern statetype s_angelchase1s; +extern statetype s_angelchase2; +extern statetype s_angelchase3; +extern statetype s_angelchase3s; +extern statetype s_angelchase4; + +extern statetype s_angeldie1; +extern statetype s_angeldie11; +extern statetype s_angeldie2; +extern statetype s_angeldie3; +extern statetype s_angeldie4; +extern statetype s_angeldie5; +extern statetype s_angeldie6; +extern statetype s_angeldie7; +extern statetype s_angeldie8; +extern statetype s_angeldie9; + +extern statetype s_angelshoot1; +extern statetype s_angelshoot2; +extern statetype s_angelshoot3; +extern statetype s_angelshoot4; +extern statetype s_angelshoot5; +extern statetype s_angelshoot6; + +extern statetype s_angeltired; +extern statetype s_angeltired2; +extern statetype s_angeltired3; +extern statetype s_angeltired4; +extern statetype s_angeltired5; +extern statetype s_angeltired6; +extern statetype s_angeltired7; + +extern statetype s_spark1; +extern statetype s_spark2; +extern statetype s_spark3; +extern statetype s_spark4; + + +statetype s_angelstand = {false,SPR_ANGEL_W1,0,T_Stand,NULL,&s_angelstand}; + +statetype s_angelchase1 = {false,SPR_ANGEL_W1,10,T_Will,NULL,&s_angelchase1s}; +statetype s_angelchase1s = {false,SPR_ANGEL_W1,3,NULL,NULL,&s_angelchase2}; +statetype s_angelchase2 = {false,SPR_ANGEL_W2,8,T_Will,NULL,&s_angelchase3}; +statetype s_angelchase3 = {false,SPR_ANGEL_W3,10,T_Will,NULL,&s_angelchase3s}; +statetype s_angelchase3s = {false,SPR_ANGEL_W3,3,NULL,NULL,&s_angelchase4}; +statetype s_angelchase4 = {false,SPR_ANGEL_W4,8,T_Will,NULL,&s_angelchase1}; + +statetype s_angeldie1 = {false,SPR_ANGEL_W1,1,NULL,A_DeathScream,&s_angeldie11}; +statetype s_angeldie11 = {false,SPR_ANGEL_W1,1,NULL,NULL,&s_angeldie2}; +statetype s_angeldie2 = {false,SPR_ANGEL_DIE1,10,NULL,A_Slurpie,&s_angeldie3}; +statetype s_angeldie3 = {false,SPR_ANGEL_DIE2,10,NULL,NULL,&s_angeldie4}; +statetype s_angeldie4 = {false,SPR_ANGEL_DIE3,10,NULL,NULL,&s_angeldie5}; +statetype s_angeldie5 = {false,SPR_ANGEL_DIE4,10,NULL,NULL,&s_angeldie6}; +statetype s_angeldie6 = {false,SPR_ANGEL_DIE5,10,NULL,NULL,&s_angeldie7}; +statetype s_angeldie7 = {false,SPR_ANGEL_DIE6,10,NULL,NULL,&s_angeldie8}; +statetype s_angeldie8 = {false,SPR_ANGEL_DIE7,10,NULL,NULL,&s_angeldie9}; +statetype s_angeldie9 = {false,SPR_ANGEL_DEAD,130,NULL,A_Victory,&s_angeldie9}; + +statetype s_angelshoot1 = {false,SPR_ANGEL_SHOOT1,10,NULL,A_StartAttack,&s_angelshoot2}; +statetype s_angelshoot2 = {false,SPR_ANGEL_SHOOT2,20,NULL,T_Launch,&s_angelshoot3}; +statetype s_angelshoot3 = {false,SPR_ANGEL_SHOOT1,10,NULL,A_Relaunch,&s_angelshoot2}; + +statetype s_angeltired = {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angeltired2}; +statetype s_angeltired2 = {false,SPR_ANGEL_TIRED2,40,NULL,NULL,&s_angeltired3}; +statetype s_angeltired3 = {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angeltired4}; +statetype s_angeltired4 = {false,SPR_ANGEL_TIRED2,40,NULL,NULL,&s_angeltired5}; +statetype s_angeltired5 = {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angeltired6}; +statetype s_angeltired6 = {false,SPR_ANGEL_TIRED2,40,NULL,NULL,&s_angeltired7}; +statetype s_angeltired7 = {false,SPR_ANGEL_TIRED1,40,NULL,A_Breathing,&s_angelchase1}; + +statetype s_spark1 = {false,SPR_SPARK1,6,T_Projectile,NULL,&s_spark2}; +statetype s_spark2 = {false,SPR_SPARK2,6,T_Projectile,NULL,&s_spark3}; +statetype s_spark3 = {false,SPR_SPARK3,6,T_Projectile,NULL,&s_spark4}; +statetype s_spark4 = {false,SPR_SPARK4,6,T_Projectile,NULL,&s_spark1}; + + +#pragma argsused +void A_Slurpie (objtype *ob) +{ + SD_PlaySound(SLURPIESND); +} + +#pragma argsused +void A_Breathing (objtype *ob) +{ + SD_PlaySound(ANGELTIREDSND); +} + +/* +=============== += += SpawnAngel += +=============== +*/ + +void SpawnAngel (int tilex, int tiley) +{ + unsigned far *map,tile; + + + if (SoundBlasterPresent && DigiMode != sds_Off) + s_angeldie11.tictime = 105; + + SpawnNewObj (tilex,tiley,&s_angelstand); + new->obclass = angelobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_angel]; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +================= += += A_Victory += +================= +*/ + +#pragma argsused +void A_Victory (objtype *ob) +{ + playstate = ex_victorious; +} + + +/* +================= += += A_StartAttack += +================= +*/ + +void A_StartAttack (objtype *ob) +{ + ob->temp1 = 0; +} + + +/* +================= += += A_Relaunch += +================= +*/ + +void A_Relaunch (objtype *ob) +{ + if (++ob->temp1 == 3) + { + NewState (ob,&s_angeltired); + return; + } + + if (US_RndT()&1) + { + NewState (ob,&s_angelchase1); + return; + } +} + + + + +// +// spectre +// +void T_SpectreWait (objtype *ob); +void A_Dormant (objtype *ob); + +extern statetype s_spectrewait1; +extern statetype s_spectrewait2; +extern statetype s_spectrewait3; +extern statetype s_spectrewait4; + +extern statetype s_spectrechase1; +extern statetype s_spectrechase2; +extern statetype s_spectrechase3; +extern statetype s_spectrechase4; + +extern statetype s_spectredie1; +extern statetype s_spectredie2; +extern statetype s_spectredie3; +extern statetype s_spectredie4; + +extern statetype s_spectrewake; + +statetype s_spectrewait1 = {false,SPR_SPECTRE_W1,10,T_Stand,NULL,&s_spectrewait2}; +statetype s_spectrewait2 = {false,SPR_SPECTRE_W2,10,T_Stand,NULL,&s_spectrewait3}; +statetype s_spectrewait3 = {false,SPR_SPECTRE_W3,10,T_Stand,NULL,&s_spectrewait4}; +statetype s_spectrewait4 = {false,SPR_SPECTRE_W4,10,T_Stand,NULL,&s_spectrewait1}; + +statetype s_spectrechase1 = {false,SPR_SPECTRE_W1,10,T_Ghosts,NULL,&s_spectrechase2}; +statetype s_spectrechase2 = {false,SPR_SPECTRE_W2,10,T_Ghosts,NULL,&s_spectrechase3}; +statetype s_spectrechase3 = {false,SPR_SPECTRE_W3,10,T_Ghosts,NULL,&s_spectrechase4}; +statetype s_spectrechase4 = {false,SPR_SPECTRE_W4,10,T_Ghosts,NULL,&s_spectrechase1}; + +statetype s_spectredie1 = {false,SPR_SPECTRE_F1,10,NULL,NULL,&s_spectredie2}; +statetype s_spectredie2 = {false,SPR_SPECTRE_F2,10,NULL,NULL,&s_spectredie3}; +statetype s_spectredie3 = {false,SPR_SPECTRE_F3,10,NULL,NULL,&s_spectredie4}; +statetype s_spectredie4 = {false,SPR_SPECTRE_F4,300,NULL,NULL,&s_spectrewake}; +statetype s_spectrewake = {false,SPR_SPECTRE_F4,10,NULL,A_Dormant,&s_spectrewake}; + +/* +=============== += += SpawnSpectre += +=============== +*/ + +void SpawnSpectre (int tilex, int tiley) +{ + unsigned far *map,tile; + + SpawnNewObj (tilex,tiley,&s_spectrewait1); + new->obclass = spectreobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_spectre]; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; // |FL_NEVERMARK|FL_NONMARK; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +=============== += += A_Dormant += +=============== +*/ + +void A_Dormant (objtype *ob) +{ + long deltax,deltay; + int xl,xh,yl,yh; + int x,y; + unsigned tile; + + deltax = ob->x - player->x; + if (deltax < -MINACTORDIST || deltax > MINACTORDIST) + goto moveok; + deltay = ob->y - player->y; + if (deltay < -MINACTORDIST || deltay > MINACTORDIST) + goto moveok; + + return; +moveok: + + xl = (ob->x-MINDIST) >> TILESHIFT; + xh = (ob->x+MINDIST) >> TILESHIFT; + yl = (ob->y-MINDIST) >> TILESHIFT; + yh = (ob->y+MINDIST) >> TILESHIFT; + + for (y=yl ; y<=yh ; y++) + for (x=xl ; x<=xh ; x++) + { + tile = actorat[x][y]; + if (!tile) + continue; + if (tile<256) + return; + if (((objtype *)tile)->flags&FL_SHOOTABLE) + return; + } + + ob->flags |= FL_AMBUSH | FL_SHOOTABLE; + ob->flags &= ~FL_ATTACKMODE; + ob->dir = nodir; + NewState (ob,&s_spectrewait1); +} + + +#endif + +/* +============================================================================= + + SCHABBS / GIFT / FAT + +============================================================================= +*/ + +#ifndef SPEAR +/* +=============== += += SpawnGhosts += +=============== +*/ + +void SpawnGhosts (int which, int tilex, int tiley) +{ + unsigned far *map,tile; + + switch(which) + { + case en_blinky: + SpawnNewObj (tilex,tiley,&s_blinkychase1); + break; + case en_clyde: + SpawnNewObj (tilex,tiley,&s_clydechase1); + break; + case en_pinky: + SpawnNewObj (tilex,tiley,&s_pinkychase1); + break; + case en_inky: + SpawnNewObj (tilex,tiley,&s_inkychase1); + break; + } + + new->obclass = ghostobj; + new->speed = SPDDOG; + + new->dir = east; + new->flags |= FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + + +void T_Gift (objtype *ob); +void T_GiftThrow (objtype *ob); + +void T_Fat (objtype *ob); +void T_FatThrow (objtype *ob); + +// +// schabb +// +extern statetype s_schabbstand; + +extern statetype s_schabbchase1; +extern statetype s_schabbchase1s; +extern statetype s_schabbchase2; +extern statetype s_schabbchase3; +extern statetype s_schabbchase3s; +extern statetype s_schabbchase4; + +extern statetype s_schabbdie1; +extern statetype s_schabbdie2; +extern statetype s_schabbdie3; +extern statetype s_schabbdie4; +extern statetype s_schabbdie5; +extern statetype s_schabbdie6; + +extern statetype s_schabbshoot1; +extern statetype s_schabbshoot2; + +extern statetype s_needle1; +extern statetype s_needle2; +extern statetype s_needle3; +extern statetype s_needle4; + +extern statetype s_schabbdeathcam; + + +statetype s_schabbstand = {false,SPR_SCHABB_W1,0,T_Stand,NULL,&s_schabbstand}; + +statetype s_schabbchase1 = {false,SPR_SCHABB_W1,10,T_Schabb,NULL,&s_schabbchase1s}; +statetype s_schabbchase1s = {false,SPR_SCHABB_W1,3,NULL,NULL,&s_schabbchase2}; +statetype s_schabbchase2 = {false,SPR_SCHABB_W2,8,T_Schabb,NULL,&s_schabbchase3}; +statetype s_schabbchase3 = {false,SPR_SCHABB_W3,10,T_Schabb,NULL,&s_schabbchase3s}; +statetype s_schabbchase3s = {false,SPR_SCHABB_W3,3,NULL,NULL,&s_schabbchase4}; +statetype s_schabbchase4 = {false,SPR_SCHABB_W4,8,T_Schabb,NULL,&s_schabbchase1}; + +statetype s_schabbdeathcam = {false,SPR_SCHABB_W1,1,NULL,NULL,&s_schabbdie1}; + +statetype s_schabbdie1 = {false,SPR_SCHABB_W1,10,NULL,A_DeathScream,&s_schabbdie2}; +statetype s_schabbdie2 = {false,SPR_SCHABB_W1,10,NULL,NULL,&s_schabbdie3}; +statetype s_schabbdie3 = {false,SPR_SCHABB_DIE1,10,NULL,NULL,&s_schabbdie4}; +statetype s_schabbdie4 = {false,SPR_SCHABB_DIE2,10,NULL,NULL,&s_schabbdie5}; +statetype s_schabbdie5 = {false,SPR_SCHABB_DIE3,10,NULL,NULL,&s_schabbdie6}; +statetype s_schabbdie6 = {false,SPR_SCHABB_DEAD,20,NULL,A_StartDeathCam,&s_schabbdie6}; + +statetype s_schabbshoot1 = {false,SPR_SCHABB_SHOOT1,30,NULL,NULL,&s_schabbshoot2}; +statetype s_schabbshoot2 = {false,SPR_SCHABB_SHOOT2,10,NULL,T_SchabbThrow,&s_schabbchase1}; + +statetype s_needle1 = {false,SPR_HYPO1,6,T_Projectile,NULL,&s_needle2}; +statetype s_needle2 = {false,SPR_HYPO2,6,T_Projectile,NULL,&s_needle3}; +statetype s_needle3 = {false,SPR_HYPO3,6,T_Projectile,NULL,&s_needle4}; +statetype s_needle4 = {false,SPR_HYPO4,6,T_Projectile,NULL,&s_needle1}; + + +// +// gift +// +extern statetype s_giftstand; + +extern statetype s_giftchase1; +extern statetype s_giftchase1s; +extern statetype s_giftchase2; +extern statetype s_giftchase3; +extern statetype s_giftchase3s; +extern statetype s_giftchase4; + +extern statetype s_giftdie1; +extern statetype s_giftdie2; +extern statetype s_giftdie3; +extern statetype s_giftdie4; +extern statetype s_giftdie5; +extern statetype s_giftdie6; + +extern statetype s_giftshoot1; +extern statetype s_giftshoot2; + +extern statetype s_needle1; +extern statetype s_needle2; +extern statetype s_needle3; +extern statetype s_needle4; + +extern statetype s_giftdeathcam; + +extern statetype s_boom1; +extern statetype s_boom2; +extern statetype s_boom3; + + +statetype s_giftstand = {false,SPR_GIFT_W1,0,T_Stand,NULL,&s_giftstand}; + +statetype s_giftchase1 = {false,SPR_GIFT_W1,10,T_Gift,NULL,&s_giftchase1s}; +statetype s_giftchase1s = {false,SPR_GIFT_W1,3,NULL,NULL,&s_giftchase2}; +statetype s_giftchase2 = {false,SPR_GIFT_W2,8,T_Gift,NULL,&s_giftchase3}; +statetype s_giftchase3 = {false,SPR_GIFT_W3,10,T_Gift,NULL,&s_giftchase3s}; +statetype s_giftchase3s = {false,SPR_GIFT_W3,3,NULL,NULL,&s_giftchase4}; +statetype s_giftchase4 = {false,SPR_GIFT_W4,8,T_Gift,NULL,&s_giftchase1}; + +statetype s_giftdeathcam = {false,SPR_GIFT_W1,1,NULL,NULL,&s_giftdie1}; + +statetype s_giftdie1 = {false,SPR_GIFT_W1,1,NULL,A_DeathScream,&s_giftdie2}; +statetype s_giftdie2 = {false,SPR_GIFT_W1,10,NULL,NULL,&s_giftdie3}; +statetype s_giftdie3 = {false,SPR_GIFT_DIE1,10,NULL,NULL,&s_giftdie4}; +statetype s_giftdie4 = {false,SPR_GIFT_DIE2,10,NULL,NULL,&s_giftdie5}; +statetype s_giftdie5 = {false,SPR_GIFT_DIE3,10,NULL,NULL,&s_giftdie6}; +statetype s_giftdie6 = {false,SPR_GIFT_DEAD,20,NULL,A_StartDeathCam,&s_giftdie6}; + +statetype s_giftshoot1 = {false,SPR_GIFT_SHOOT1,30,NULL,NULL,&s_giftshoot2}; +statetype s_giftshoot2 = {false,SPR_GIFT_SHOOT2,10,NULL,T_GiftThrow,&s_giftchase1}; + + +// +// fat +// +extern statetype s_fatstand; + +extern statetype s_fatchase1; +extern statetype s_fatchase1s; +extern statetype s_fatchase2; +extern statetype s_fatchase3; +extern statetype s_fatchase3s; +extern statetype s_fatchase4; + +extern statetype s_fatdie1; +extern statetype s_fatdie2; +extern statetype s_fatdie3; +extern statetype s_fatdie4; +extern statetype s_fatdie5; +extern statetype s_fatdie6; + +extern statetype s_fatshoot1; +extern statetype s_fatshoot2; +extern statetype s_fatshoot3; +extern statetype s_fatshoot4; +extern statetype s_fatshoot5; +extern statetype s_fatshoot6; + +extern statetype s_needle1; +extern statetype s_needle2; +extern statetype s_needle3; +extern statetype s_needle4; + +extern statetype s_fatdeathcam; + + +statetype s_fatstand = {false,SPR_FAT_W1,0,T_Stand,NULL,&s_fatstand}; + +statetype s_fatchase1 = {false,SPR_FAT_W1,10,T_Fat,NULL,&s_fatchase1s}; +statetype s_fatchase1s = {false,SPR_FAT_W1,3,NULL,NULL,&s_fatchase2}; +statetype s_fatchase2 = {false,SPR_FAT_W2,8,T_Fat,NULL,&s_fatchase3}; +statetype s_fatchase3 = {false,SPR_FAT_W3,10,T_Fat,NULL,&s_fatchase3s}; +statetype s_fatchase3s = {false,SPR_FAT_W3,3,NULL,NULL,&s_fatchase4}; +statetype s_fatchase4 = {false,SPR_FAT_W4,8,T_Fat,NULL,&s_fatchase1}; + +statetype s_fatdeathcam = {false,SPR_FAT_W1,1,NULL,NULL,&s_fatdie1}; + +statetype s_fatdie1 = {false,SPR_FAT_W1,1,NULL,A_DeathScream,&s_fatdie2}; +statetype s_fatdie2 = {false,SPR_FAT_W1,10,NULL,NULL,&s_fatdie3}; +statetype s_fatdie3 = {false,SPR_FAT_DIE1,10,NULL,NULL,&s_fatdie4}; +statetype s_fatdie4 = {false,SPR_FAT_DIE2,10,NULL,NULL,&s_fatdie5}; +statetype s_fatdie5 = {false,SPR_FAT_DIE3,10,NULL,NULL,&s_fatdie6}; +statetype s_fatdie6 = {false,SPR_FAT_DEAD,20,NULL,A_StartDeathCam,&s_fatdie6}; + +statetype s_fatshoot1 = {false,SPR_FAT_SHOOT1,30,NULL,NULL,&s_fatshoot2}; +statetype s_fatshoot2 = {false,SPR_FAT_SHOOT2,10,NULL,T_GiftThrow,&s_fatshoot3}; +statetype s_fatshoot3 = {false,SPR_FAT_SHOOT3,10,NULL,T_Shoot,&s_fatshoot4}; +statetype s_fatshoot4 = {false,SPR_FAT_SHOOT4,10,NULL,T_Shoot,&s_fatshoot5}; +statetype s_fatshoot5 = {false,SPR_FAT_SHOOT3,10,NULL,T_Shoot,&s_fatshoot6}; +statetype s_fatshoot6 = {false,SPR_FAT_SHOOT4,10,NULL,T_Shoot,&s_fatchase1}; + + +/* +=============== += += SpawnSchabbs += +=============== +*/ + +void SpawnSchabbs (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (DigiMode != sds_Off) + s_schabbdie2.tictime = 140; + else + s_schabbdie2.tictime = 5; + + SpawnNewObj (tilex,tiley,&s_schabbstand); + new->speed = SPDPATROL; + + new->obclass = schabbobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_schabbs]; + new->dir = south; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +=============== += += SpawnGift += +=============== +*/ + +void SpawnGift (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (DigiMode != sds_Off) + s_giftdie2.tictime = 140; + else + s_giftdie2.tictime = 5; + + SpawnNewObj (tilex,tiley,&s_giftstand); + new->speed = SPDPATROL; + + new->obclass = giftobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_gift]; + new->dir = north; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +=============== += += SpawnFat += +=============== +*/ + +void SpawnFat (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (DigiMode != sds_Off) + s_fatdie2.tictime = 140; + else + s_fatdie2.tictime = 5; + + SpawnNewObj (tilex,tiley,&s_fatstand); + new->speed = SPDPATROL; + + new->obclass = fatobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_fat]; + new->dir = south; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +================= += += T_SchabbThrow += +================= +*/ + +void T_SchabbThrow (objtype *ob) +{ + long deltax,deltay; + float angle; + int iangle; + + deltax = player->x - ob->x; + deltay = ob->y - player->y; + angle = atan2 (deltay,deltax); + if (angle<0) + angle = M_PI*2+angle; + iangle = angle/(M_PI*2)*ANGLES; + + GetNewActor (); + new->state = &s_needle1; + new->ticcount = 1; + + new->tilex = ob->tilex; + new->tiley = ob->tiley; + new->x = ob->x; + new->y = ob->y; + new->obclass = needleobj; + new->dir = nodir; + new->angle = iangle; + new->speed = 0x2000l; + + new->flags = FL_NONMARK; + new->active = true; + + PlaySoundLocActor (SCHABBSTHROWSND,new); +} + +/* +================= += += T_GiftThrow += +================= +*/ + +void T_GiftThrow (objtype *ob) +{ + long deltax,deltay; + float angle; + int iangle; + + deltax = player->x - ob->x; + deltay = ob->y - player->y; + angle = atan2 (deltay,deltax); + if (angle<0) + angle = M_PI*2+angle; + iangle = angle/(M_PI*2)*ANGLES; + + GetNewActor (); + new->state = &s_rocket; + new->ticcount = 1; + + new->tilex = ob->tilex; + new->tiley = ob->tiley; + new->x = ob->x; + new->y = ob->y; + new->obclass = rocketobj; + new->dir = nodir; + new->angle = iangle; + new->speed = 0x2000l; + new->flags = FL_NONMARK; + new->active = true; + + PlaySoundLocActor (MISSILEFIRESND,new); +} + + + +/* +================= += += T_Schabb += +================= +*/ + +void T_Schabb (objtype *ob) +{ + long move; + int dx,dy,dist; + boolean dodge; + + dodge = false; + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + + if (CheckLine(ob)) // got a shot at player? + { + + if ( US_RndT() < (tics<<3) ) + { + // + // go into attack frame + // + NewState (ob,&s_schabbshoot1); + return; + } + dodge = true; + } + + if (ob->dir == nodir) + { + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (ob->distance < 0) + { + // + // waiting for a door to open + // + OpenDoor (-ob->distance-1); + if (doorobjlist[-ob->distance-1].action != dr_open) + return; + ob->distance = TILEGLOBAL; // go ahead, the door is now opoen + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (dist <4) + SelectRunDir (ob); + else if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + + + + +/* +================= += += T_Gift += +================= +*/ + +void T_Gift (objtype *ob) +{ + long move; + int dx,dy,dist; + boolean dodge; + + dodge = false; + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + + if (CheckLine(ob)) // got a shot at player? + { + + if ( US_RndT() < (tics<<3) ) + { + // + // go into attack frame + // + NewState (ob,&s_giftshoot1); + return; + } + dodge = true; + } + + if (ob->dir == nodir) + { + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (ob->distance < 0) + { + // + // waiting for a door to open + // + OpenDoor (-ob->distance-1); + if (doorobjlist[-ob->distance-1].action != dr_open) + return; + ob->distance = TILEGLOBAL; // go ahead, the door is now opoen + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (dist <4) + SelectRunDir (ob); + else if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + + + + +/* +================= += += T_Fat += +================= +*/ + +void T_Fat (objtype *ob) +{ + long move; + int dx,dy,dist; + boolean dodge; + + dodge = false; + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + + if (CheckLine(ob)) // got a shot at player? + { + + if ( US_RndT() < (tics<<3) ) + { + // + // go into attack frame + // + NewState (ob,&s_fatshoot1); + return; + } + dodge = true; + } + + if (ob->dir == nodir) + { + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (ob->distance < 0) + { + // + // waiting for a door to open + // + OpenDoor (-ob->distance-1); + if (doorobjlist[-ob->distance-1].action != dr_open) + return; + ob->distance = TILEGLOBAL; // go ahead, the door is now opoen + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (dist <4) + SelectRunDir (ob); + else if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + + + +/* +============================================================================= + + HITLERS + +============================================================================= +*/ + + +// +// fake +// +extern statetype s_fakestand; + +extern statetype s_fakechase1; +extern statetype s_fakechase1s; +extern statetype s_fakechase2; +extern statetype s_fakechase3; +extern statetype s_fakechase3s; +extern statetype s_fakechase4; + +extern statetype s_fakedie1; +extern statetype s_fakedie2; +extern statetype s_fakedie3; +extern statetype s_fakedie4; +extern statetype s_fakedie5; +extern statetype s_fakedie6; + +extern statetype s_fakeshoot1; +extern statetype s_fakeshoot2; +extern statetype s_fakeshoot3; +extern statetype s_fakeshoot4; +extern statetype s_fakeshoot5; +extern statetype s_fakeshoot6; +extern statetype s_fakeshoot7; +extern statetype s_fakeshoot8; +extern statetype s_fakeshoot9; + +extern statetype s_fire1; +extern statetype s_fire2; + +statetype s_fakestand = {false,SPR_FAKE_W1,0,T_Stand,NULL,&s_fakestand}; + +statetype s_fakechase1 = {false,SPR_FAKE_W1,10,T_Fake,NULL,&s_fakechase1s}; +statetype s_fakechase1s = {false,SPR_FAKE_W1,3,NULL,NULL,&s_fakechase2}; +statetype s_fakechase2 = {false,SPR_FAKE_W2,8,T_Fake,NULL,&s_fakechase3}; +statetype s_fakechase3 = {false,SPR_FAKE_W3,10,T_Fake,NULL,&s_fakechase3s}; +statetype s_fakechase3s = {false,SPR_FAKE_W3,3,NULL,NULL,&s_fakechase4}; +statetype s_fakechase4 = {false,SPR_FAKE_W4,8,T_Fake,NULL,&s_fakechase1}; + +statetype s_fakedie1 = {false,SPR_FAKE_DIE1,10,NULL,A_DeathScream,&s_fakedie2}; +statetype s_fakedie2 = {false,SPR_FAKE_DIE2,10,NULL,NULL,&s_fakedie3}; +statetype s_fakedie3 = {false,SPR_FAKE_DIE3,10,NULL,NULL,&s_fakedie4}; +statetype s_fakedie4 = {false,SPR_FAKE_DIE4,10,NULL,NULL,&s_fakedie5}; +statetype s_fakedie5 = {false,SPR_FAKE_DIE5,10,NULL,NULL,&s_fakedie6}; +statetype s_fakedie6 = {false,SPR_FAKE_DEAD,0,NULL,NULL,&s_fakedie6}; + +statetype s_fakeshoot1 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot2}; +statetype s_fakeshoot2 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot3}; +statetype s_fakeshoot3 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot4}; +statetype s_fakeshoot4 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot5}; +statetype s_fakeshoot5 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot6}; +statetype s_fakeshoot6 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot7}; +statetype s_fakeshoot7 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot8}; +statetype s_fakeshoot8 = {false,SPR_FAKE_SHOOT,8,NULL,T_FakeFire,&s_fakeshoot9}; +statetype s_fakeshoot9 = {false,SPR_FAKE_SHOOT,8,NULL,NULL,&s_fakechase1}; + +statetype s_fire1 = {false,SPR_FIRE1,6,NULL,T_Projectile,&s_fire2}; +statetype s_fire2 = {false,SPR_FIRE2,6,NULL,T_Projectile,&s_fire1}; + +// +// hitler +// +extern statetype s_mechachase1; +extern statetype s_mechachase1s; +extern statetype s_mechachase2; +extern statetype s_mechachase3; +extern statetype s_mechachase3s; +extern statetype s_mechachase4; + +extern statetype s_mechadie1; +extern statetype s_mechadie2; +extern statetype s_mechadie3; +extern statetype s_mechadie4; + +extern statetype s_mechashoot1; +extern statetype s_mechashoot2; +extern statetype s_mechashoot3; +extern statetype s_mechashoot4; +extern statetype s_mechashoot5; +extern statetype s_mechashoot6; + + +extern statetype s_hitlerchase1; +extern statetype s_hitlerchase1s; +extern statetype s_hitlerchase2; +extern statetype s_hitlerchase3; +extern statetype s_hitlerchase3s; +extern statetype s_hitlerchase4; + +extern statetype s_hitlerdie1; +extern statetype s_hitlerdie2; +extern statetype s_hitlerdie3; +extern statetype s_hitlerdie4; +extern statetype s_hitlerdie5; +extern statetype s_hitlerdie6; +extern statetype s_hitlerdie7; +extern statetype s_hitlerdie8; +extern statetype s_hitlerdie9; +extern statetype s_hitlerdie10; + +extern statetype s_hitlershoot1; +extern statetype s_hitlershoot2; +extern statetype s_hitlershoot3; +extern statetype s_hitlershoot4; +extern statetype s_hitlershoot5; +extern statetype s_hitlershoot6; + +extern statetype s_hitlerdeathcam; + +statetype s_mechastand = {false,SPR_MECHA_W1,0,T_Stand,NULL,&s_mechastand}; + +statetype s_mechachase1 = {false,SPR_MECHA_W1,10,T_Chase,A_MechaSound,&s_mechachase1s}; +statetype s_mechachase1s = {false,SPR_MECHA_W1,6,NULL,NULL,&s_mechachase2}; +statetype s_mechachase2 = {false,SPR_MECHA_W2,8,T_Chase,NULL,&s_mechachase3}; +statetype s_mechachase3 = {false,SPR_MECHA_W3,10,T_Chase,A_MechaSound,&s_mechachase3s}; +statetype s_mechachase3s = {false,SPR_MECHA_W3,6,NULL,NULL,&s_mechachase4}; +statetype s_mechachase4 = {false,SPR_MECHA_W4,8,T_Chase,NULL,&s_mechachase1}; + +statetype s_mechadie1 = {false,SPR_MECHA_DIE1,10,NULL,A_DeathScream,&s_mechadie2}; +statetype s_mechadie2 = {false,SPR_MECHA_DIE2,10,NULL,NULL,&s_mechadie3}; +statetype s_mechadie3 = {false,SPR_MECHA_DIE3,10,NULL,A_HitlerMorph,&s_mechadie4}; +statetype s_mechadie4 = {false,SPR_MECHA_DEAD,0,NULL,NULL,&s_mechadie4}; + +statetype s_mechashoot1 = {false,SPR_MECHA_SHOOT1,30,NULL,NULL,&s_mechashoot2}; +statetype s_mechashoot2 = {false,SPR_MECHA_SHOOT2,10,NULL,T_Shoot,&s_mechashoot3}; +statetype s_mechashoot3 = {false,SPR_MECHA_SHOOT3,10,NULL,T_Shoot,&s_mechashoot4}; +statetype s_mechashoot4 = {false,SPR_MECHA_SHOOT2,10,NULL,T_Shoot,&s_mechashoot5}; +statetype s_mechashoot5 = {false,SPR_MECHA_SHOOT3,10,NULL,T_Shoot,&s_mechashoot6}; +statetype s_mechashoot6 = {false,SPR_MECHA_SHOOT2,10,NULL,T_Shoot,&s_mechachase1}; + + +statetype s_hitlerchase1 = {false,SPR_HITLER_W1,6,T_Chase,NULL,&s_hitlerchase1s}; +statetype s_hitlerchase1s = {false,SPR_HITLER_W1,4,NULL,NULL,&s_hitlerchase2}; +statetype s_hitlerchase2 = {false,SPR_HITLER_W2,2,T_Chase,NULL,&s_hitlerchase3}; +statetype s_hitlerchase3 = {false,SPR_HITLER_W3,6,T_Chase,NULL,&s_hitlerchase3s}; +statetype s_hitlerchase3s = {false,SPR_HITLER_W3,4,NULL,NULL,&s_hitlerchase4}; +statetype s_hitlerchase4 = {false,SPR_HITLER_W4,2,T_Chase,NULL,&s_hitlerchase1}; + +statetype s_hitlerdeathcam = {false,SPR_HITLER_W1,10,NULL,NULL,&s_hitlerdie1}; + +statetype s_hitlerdie1 = {false,SPR_HITLER_W1,1,NULL,A_DeathScream,&s_hitlerdie2}; +statetype s_hitlerdie2 = {false,SPR_HITLER_W1,10,NULL,NULL,&s_hitlerdie3}; +statetype s_hitlerdie3 = {false,SPR_HITLER_DIE1,10,NULL,A_Slurpie,&s_hitlerdie4}; +statetype s_hitlerdie4 = {false,SPR_HITLER_DIE2,10,NULL,NULL,&s_hitlerdie5}; +statetype s_hitlerdie5 = {false,SPR_HITLER_DIE3,10,NULL,NULL,&s_hitlerdie6}; +statetype s_hitlerdie6 = {false,SPR_HITLER_DIE4,10,NULL,NULL,&s_hitlerdie7}; +statetype s_hitlerdie7 = {false,SPR_HITLER_DIE5,10,NULL,NULL,&s_hitlerdie8}; +statetype s_hitlerdie8 = {false,SPR_HITLER_DIE6,10,NULL,NULL,&s_hitlerdie9}; +statetype s_hitlerdie9 = {false,SPR_HITLER_DIE7,10,NULL,NULL,&s_hitlerdie10}; +statetype s_hitlerdie10 = {false,SPR_HITLER_DEAD,20,NULL,A_StartDeathCam,&s_hitlerdie10}; + +statetype s_hitlershoot1 = {false,SPR_HITLER_SHOOT1,30,NULL,NULL,&s_hitlershoot2}; +statetype s_hitlershoot2 = {false,SPR_HITLER_SHOOT2,10,NULL,T_Shoot,&s_hitlershoot3}; +statetype s_hitlershoot3 = {false,SPR_HITLER_SHOOT3,10,NULL,T_Shoot,&s_hitlershoot4}; +statetype s_hitlershoot4 = {false,SPR_HITLER_SHOOT2,10,NULL,T_Shoot,&s_hitlershoot5}; +statetype s_hitlershoot5 = {false,SPR_HITLER_SHOOT3,10,NULL,T_Shoot,&s_hitlershoot6}; +statetype s_hitlershoot6 = {false,SPR_HITLER_SHOOT2,10,NULL,T_Shoot,&s_hitlerchase1}; + + + +/* +=============== += += SpawnFakeHitler += +=============== +*/ + +void SpawnFakeHitler (int tilex, int tiley) +{ + unsigned far *map,tile; + + + if (DigiMode != sds_Off) + s_hitlerdie2.tictime = 140; + else + s_hitlerdie2.tictime = 5; + + SpawnNewObj (tilex,tiley,&s_fakestand); + new->speed = SPDPATROL; + + new->obclass = fakeobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_fake]; + new->dir = north; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +=============== += += SpawnHitler += +=============== +*/ + +void SpawnHitler (int tilex, int tiley) +{ + unsigned far *map,tile; + + if (DigiMode != sds_Off) + s_hitlerdie2.tictime = 140; + else + s_hitlerdie2.tictime = 5; + + + SpawnNewObj (tilex,tiley,&s_mechastand); + new->speed = SPDPATROL; + + new->obclass = mechahitlerobj; + new->hitpoints = starthitpoints[gamestate.difficulty][en_hitler]; + new->dir = south; + new->flags |= FL_SHOOTABLE|FL_AMBUSH; + if (!loadedgame) + gamestate.killtotal++; +} + + +/* +=============== += += A_HitlerMorph += +=============== +*/ + +void A_HitlerMorph (objtype *ob) +{ + unsigned far *map,tile,hitpoints[4]={500,700,800,900}; + + + SpawnNewObj (ob->tilex,ob->tiley,&s_hitlerchase1); + new->speed = SPDPATROL*5; + + new->x = ob->x; + new->y = ob->y; + + new->distance = ob->distance; + new->dir = ob->dir; + new->flags = ob->flags | FL_SHOOTABLE; + + new->obclass = realhitlerobj; + new->hitpoints = hitpoints[gamestate.difficulty]; +} + + +//////////////////////////////////////////////////////// +// +// A_MechaSound +// A_Slurpie +// +//////////////////////////////////////////////////////// +void A_MechaSound (objtype *ob) +{ + if (areabyplayer[ob->areanumber]) + PlaySoundLocActor (MECHSTEPSND,ob); +} + + +#pragma argsused +void A_Slurpie (objtype *ob) +{ + SD_PlaySound(SLURPIESND); +} + +/* +================= += += T_FakeFire += +================= +*/ + +void T_FakeFire (objtype *ob) +{ + long deltax,deltay; + float angle; + int iangle; + + deltax = player->x - ob->x; + deltay = ob->y - player->y; + angle = atan2 (deltay,deltax); + if (angle<0) + angle = M_PI*2+angle; + iangle = angle/(M_PI*2)*ANGLES; + + GetNewActor (); + new->state = &s_fire1; + new->ticcount = 1; + + new->tilex = ob->tilex; + new->tiley = ob->tiley; + new->x = ob->x; + new->y = ob->y; + new->dir = nodir; + new->angle = iangle; + new->obclass = fireobj; + new->speed = 0x1200l; + new->flags = FL_NEVERMARK; + new->active = true; + + PlaySoundLocActor (FLAMETHROWERSND,new); +} + + + +/* +================= += += T_Fake += +================= +*/ + +void T_Fake (objtype *ob) +{ + long move; + int dx,dy,dist; + boolean dodge; + + if (CheckLine(ob)) // got a shot at player? + { + if ( US_RndT() < (tics<<1) ) + { + // + // go into attack frame + // + NewState (ob,&s_fakeshoot1); + return; + } + } + + if (ob->dir == nodir) + { + SelectDodgeDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + SelectDodgeDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + +#endif +/* +============================================================================ + + STAND + +============================================================================ +*/ + + +/* +=============== += += T_Stand += +=============== +*/ + +void T_Stand (objtype *ob) +{ + SightPlayer (ob); +} + + +/* +============================================================================ + + CHASE + +============================================================================ +*/ + +/* +================= += += T_Chase += +================= +*/ + +void T_Chase (objtype *ob) +{ + long move; + int dx,dy,dist,chance; + boolean dodge; + + if (gamestate.victoryflag) + return; + + dodge = false; + if (CheckLine(ob)) // got a shot at player? + { + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + if (!dist || (dist==1 && ob->distance<0x4000) ) + chance = 300; + else + chance = (tics<<4)/dist; + + if ( US_RndT()obclass) + { + case guardobj: + NewState (ob,&s_grdshoot1); + break; + case officerobj: + NewState (ob,&s_ofcshoot1); + break; + case mutantobj: + NewState (ob,&s_mutshoot1); + break; + case ssobj: + NewState (ob,&s_ssshoot1); + break; +#ifndef SPEAR + case bossobj: + NewState (ob,&s_bossshoot1); + break; + case gretelobj: + NewState (ob,&s_gretelshoot1); + break; + case mechahitlerobj: + NewState (ob,&s_mechashoot1); + break; + case realhitlerobj: + NewState (ob,&s_hitlershoot1); + break; +#else + case angelobj: + NewState (ob,&s_angelshoot1); + break; + case transobj: + NewState (ob,&s_transshoot1); + break; + case uberobj: + NewState (ob,&s_ubershoot1); + break; + case willobj: + NewState (ob,&s_willshoot1); + break; + case deathobj: + NewState (ob,&s_deathshoot1); + break; +#endif + } + return; + } + dodge = true; + } + + if (ob->dir == nodir) + { + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (ob->distance < 0) + { + // + // waiting for a door to open + // + OpenDoor (-ob->distance-1); + if (doorobjlist[-ob->distance-1].action != dr_open) + return; + ob->distance = TILEGLOBAL; // go ahead, the door is now opoen + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + + +/* +================= += += T_Ghosts += +================= +*/ + +void T_Ghosts (objtype *ob) +{ + long move; + + + if (ob->dir == nodir) + { + SelectChaseDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + SelectChaseDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + +/* +================= += += T_DogChase += +================= +*/ + +void T_DogChase (objtype *ob) +{ + long move; + int dist,chance; + long dx,dy; + + + if (ob->dir == nodir) + { + SelectDodgeDir (ob); + if (ob->dir == nodir) + return; // object is blocked in + } + + move = ob->speed*tics; + + while (move) + { + // + // check for byte range + // + dx = player->x - ob->x; + if (dx<0) + dx = -dx; + dx -= move; + if (dx <= MINACTORDIST) + { + dy = player->y - ob->y; + if (dy<0) + dy = -dy; + dy -= move; + if (dy <= MINACTORDIST) + { + NewState (ob,&s_dogjump1); + return; + } + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + // + // reached goal tile, so select another one + // + + // + // fix position to account for round off during moving + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + SelectDodgeDir (ob); + + if (ob->dir == nodir) + return; // object is blocked in + } + +} + + + +/* +============================================================================ + + PATH + +============================================================================ +*/ + + +/* +=============== += += SelectPathDir += +=============== +*/ + +void SelectPathDir (objtype *ob) +{ + unsigned spot; + + spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS; + + if (spot<8) + { + // new direction + ob->dir = spot; + } + + ob->distance = TILEGLOBAL; + + if (!TryWalk (ob)) + ob->dir = nodir; +} + + +/* +=============== += += T_Path += +=============== +*/ + +void T_Path (objtype *ob) +{ + long move; + long deltax,deltay,size; + + if (SightPlayer (ob)) + return; + + if (ob->dir == nodir) + { + SelectPathDir (ob); + if (ob->dir == nodir) + return; // all movement is blocked + } + + + move = ob->speed*tics; + + while (move) + { + if (ob->distance < 0) + { + // + // waiting for a door to open + // + OpenDoor (-ob->distance-1); + if (doorobjlist[-ob->distance-1].action != dr_open) + return; + ob->distance = TILEGLOBAL; // go ahead, the door is now opoen + } + + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + if (ob->tilex>MAPSIZE || ob->tiley>MAPSIZE) + { + sprintf (str,"T_Path hit a wall at %u,%u, dir %u" + ,ob->tilex,ob->tiley,ob->dir); + Quit (str); + } + + + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + SelectPathDir (ob); + + if (ob->dir == nodir) + return; // all movement is blocked + } +} + + +/* +============================================================================= + + FIGHT + +============================================================================= +*/ + + +/* +=============== += += T_Shoot += += Try to damage the player, based on skill level and player's speed += +=============== +*/ + +void T_Shoot (objtype *ob) +{ + int dx,dy,dist; + int hitchance,damage; + + hitchance = 128; + + if (!areabyplayer[ob->areanumber]) + return; + + if (!CheckLine (ob)) // player is behind a wall + return; + + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx:dy; + + if (ob->obclass == ssobj || ob->obclass == bossobj) + dist = dist*2/3; // ss are better shots + + if (thrustspeed >= RUNSPEED) + { + if (ob->flags&FL_VISABLE) + hitchance = 160-dist*16; // player can see to dodge + else + hitchance = 160-dist*8; + } + else + { + if (ob->flags&FL_VISABLE) + hitchance = 256-dist*16; // player can see to dodge + else + hitchance = 256-dist*8; + } + +// see if the shot was a hit + + if (US_RndT()>2; + else if (dist<4) + damage = US_RndT()>>3; + else + damage = US_RndT()>>4; + + TakeDamage (damage,ob); + } + + switch(ob->obclass) + { + case ssobj: + PlaySoundLocActor(SSFIRESND,ob); + break; +#ifndef SPEAR + case giftobj: + case fatobj: + PlaySoundLocActor(MISSILEFIRESND,ob); + break; + case mechahitlerobj: + case realhitlerobj: + case bossobj: + PlaySoundLocActor(BOSSFIRESND,ob); + break; + case schabbobj: + PlaySoundLocActor(SCHABBSTHROWSND,ob); + break; + case fakeobj: + PlaySoundLocActor(FLAMETHROWERSND,ob); + break; +#endif + default: + PlaySoundLocActor(NAZIFIRESND,ob); + } + +} + + +/* +=============== += += T_Bite += +=============== +*/ + +void T_Bite (objtype *ob) +{ + long dx,dy; + int hitchance,damage; + + + PlaySoundLocActor(DOGATTACKSND,ob); // JAB + + dx = player->x - ob->x; + if (dx<0) + dx = -dx; + dx -= TILEGLOBAL; + if (dx <= MINACTORDIST) + { + dy = player->y - ob->y; + if (dy<0) + dy = -dy; + dy -= TILEGLOBAL; + if (dy <= MINACTORDIST) + { + if (US_RndT()<180) + { + TakeDamage (US_RndT()>>4,ob); + return; + } + } + } + + return; +} + + +#ifndef SPEAR +/* +============================================================================ + + BJ VICTORY + +============================================================================ +*/ + + +// +// BJ victory +// + +void T_BJRun (objtype *ob); +void T_BJJump (objtype *ob); +void T_BJDone (objtype *ob); +void T_BJYell (objtype *ob); + +void T_DeathCam (objtype *ob); + +extern statetype s_bjrun1; +extern statetype s_bjrun1s; +extern statetype s_bjrun2; +extern statetype s_bjrun3; +extern statetype s_bjrun3s; +extern statetype s_bjrun4; + +extern statetype s_bjjump1; +extern statetype s_bjjump2; +extern statetype s_bjjump3; +extern statetype s_bjjump4; + + +statetype s_bjrun1 = {false,SPR_BJ_W1,12,T_BJRun,NULL,&s_bjrun1s}; +statetype s_bjrun1s = {false,SPR_BJ_W1,3, NULL,NULL,&s_bjrun2}; +statetype s_bjrun2 = {false,SPR_BJ_W2,8,T_BJRun,NULL,&s_bjrun3}; +statetype s_bjrun3 = {false,SPR_BJ_W3,12,T_BJRun,NULL,&s_bjrun3s}; +statetype s_bjrun3s = {false,SPR_BJ_W3,3, NULL,NULL,&s_bjrun4}; +statetype s_bjrun4 = {false,SPR_BJ_W4,8,T_BJRun,NULL,&s_bjrun1}; + + +statetype s_bjjump1 = {false,SPR_BJ_JUMP1,14,T_BJJump,NULL,&s_bjjump2}; +statetype s_bjjump2 = {false,SPR_BJ_JUMP2,14,T_BJJump,T_BJYell,&s_bjjump3}; +statetype s_bjjump3 = {false,SPR_BJ_JUMP3,14,T_BJJump,NULL,&s_bjjump4}; +statetype s_bjjump4 = {false,SPR_BJ_JUMP4,300,NULL,T_BJDone,&s_bjjump4}; + + +statetype s_deathcam = {false,0,0,NULL,NULL,NULL}; + + +/* +=============== += += SpawnBJVictory += +=============== +*/ + +void SpawnBJVictory (void) +{ + unsigned far *map,tile; + + SpawnNewObj (player->tilex,player->tiley+1,&s_bjrun1); + new->x = player->x; + new->y = player->y; + new->obclass = bjobj; + new->dir = north; + new->temp1 = 6; // tiles to run forward +} + + + +/* +=============== += += T_BJRun += +=============== +*/ + +void T_BJRun (objtype *ob) +{ + long move; + + move = BJRUNSPEED*tics; + + while (move) + { + if (move < ob->distance) + { + MoveObj (ob,move); + break; + } + + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + SelectPathDir (ob); + + if ( !(--ob->temp1) ) + { + NewState (ob,&s_bjjump1); + return; + } + } +} + + +/* +=============== += += T_BJJump += +=============== +*/ + +void T_BJJump (objtype *ob) +{ + long move; + + move = BJJUMPSPEED*tics; + MoveObj (ob,move); +} + + +/* +=============== += += T_BJYell += +=============== +*/ + +void T_BJYell (objtype *ob) +{ + PlaySoundLocActor(YEAHSND,ob); // JAB +} + + +/* +=============== += += T_BJDone += +=============== +*/ + +#pragma argsused +void T_BJDone (objtype *ob) +{ + playstate = ex_victorious; // exit castle tile +} + + + +//=========================================================================== + + +/* +=============== += += CheckPosition += +=============== +*/ + +boolean CheckPosition (objtype *ob) +{ + int x,y,xl,yl,xh,yh; + objtype *check; + + xl = (ob->x-PLAYERSIZE) >>TILESHIFT; + yl = (ob->y-PLAYERSIZE) >>TILESHIFT; + + xh = (ob->x+PLAYERSIZE) >>TILESHIFT; + yh = (ob->y+PLAYERSIZE) >>TILESHIFT; + + // + // check for solid walls + // + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + if (check && checkx = gamestate.killx; + player->y = gamestate.killy; + + dx = ob->x - player->x; + dy = player->y - ob->y; + + fangle = atan2(dy,dx); // returns -pi to pi + if (fangle<0) + fangle = M_PI*2+fangle; + + player->angle = fangle/(M_PI*2)*ANGLES; + +// +// try to position as close as possible without being in a wall +// + dist = 0x14000l; + do + { + xmove = FixedByFrac(dist,costable[player->angle]); + ymove = -FixedByFrac(dist,sintable[player->angle]); + + player->x = ob->x - xmove; + player->y = ob->y - ymove; + dist += 0x1000; + + } while (!CheckPosition (player)); + plux = player->x >> UNSIGNEDSHIFT; // scale to fit in unsigned + pluy = player->y >> UNSIGNEDSHIFT; + player->tilex = player->x >> TILESHIFT; // scale to tile values + player->tiley = player->y >> TILESHIFT; + +// +// go back to the game +// + temp = bufferofs; + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + DrawPlayBorder (); + } + bufferofs = temp; + + fizzlein = true; + switch (ob->obclass) + { +#ifndef SPEAR + case schabbobj: + NewState (ob,&s_schabbdeathcam); + break; + case realhitlerobj: + NewState (ob,&s_hitlerdeathcam); + break; + case giftobj: + NewState (ob,&s_giftdeathcam); + break; + case fatobj: + NewState (ob,&s_fatdeathcam); + break; +#endif + } + +} + +#endif diff --git a/src/lib/hb/wl_agent.c b/src/lib/hb/wl_agent.c new file mode 100755 index 00000000..631478d0 --- /dev/null +++ b/src/lib/hb/wl_agent.c @@ -0,0 +1,1421 @@ +// WL_AGENT.C + +#include "WL_DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define MAXMOUSETURN 10 + + +#define MOVESCALE 150l +#define BACKMOVESCALE 100l +#define ANGLESCALE 20 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + + +// +// player state info +// +boolean running; +long thrustspeed; + +unsigned plux,pluy; // player coordinates scaled to unsigned + +int anglefrac; +int gotgatgun; // JR + +objtype *LastAttacker; + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +void T_Player (objtype *ob); +void T_Attack (objtype *ob); + +statetype s_player = {false,0,0,T_Player,NULL,NULL}; +statetype s_attack = {false,0,0,T_Attack,NULL,NULL}; + + +long playerxmove,playerymove; + +struct atkinf +{ + char tics,attack,frame; // attack is 1 for gun, 2 for knife +} attackinfo[4][14] = + +{ +{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} }, +{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} }, +{ {6,0,1},{6,1,2},{6,3,3},{6,-1,4} }, +{ {6,0,1},{6,1,2},{6,4,3},{6,-1,4} }, +}; + + +int strafeangle[9] = {0,90,180,270,45,135,225,315,0}; + +void DrawWeapon (void); +void GiveWeapon (int weapon); +void GiveAmmo (int ammo); + +//=========================================================================== + +//---------- + +void Attack (void); +void Use (void); +void Search (objtype *ob); +void SelectWeapon (void); +void SelectItem (void); + +//---------- + +boolean TryMove (objtype *ob); +void T_Player (objtype *ob); + +void ClipMove (objtype *ob, long xmove, long ymove); + +/* +============================================================================= + + CONTROL STUFF + +============================================================================= +*/ + +/* +====================== += += CheckWeaponChange += += Keys 1-4 change weapons += +====================== +*/ + +void CheckWeaponChange (void) +{ + int i,buttons; + + if (!gamestate.ammo) // must use knife with no ammo + return; + + for (i=wp_knife ; i<=gamestate.bestweapon ; i++) + if (buttonstate[bt_readyknife+i-wp_knife]) + { + gamestate.weapon = gamestate.chosenweapon = i; + DrawWeapon (); + return; + } +} + + +/* +======================= += += ControlMovement += += Takes controlx,controly, and buttonstate[bt_strafe] += += Changes the player's angle and position += += There is an angle hack because when going 70 fps, the roundoff becomes += significant += +======================= +*/ + +void ControlMovement (objtype *ob) +{ + long oldx,oldy; + int angle,maxxmove; + int angleunits; + long speed; + + thrustspeed = 0; + + oldx = player->x; + oldy = player->y; + +// +// side to side move +// + if (buttonstate[bt_strafe]) + { + // + // strafing + // + // + if (controlx > 0) + { + angle = ob->angle - ANGLES/4; + if (angle < 0) + angle += ANGLES; + Thrust (angle,controlx*MOVESCALE); // move to left + } + else if (controlx < 0) + { + angle = ob->angle + ANGLES/4; + if (angle >= ANGLES) + angle -= ANGLES; + Thrust (angle,-controlx*MOVESCALE); // move to right + } + } + else + { + // + // not strafing + // + anglefrac += controlx; + angleunits = anglefrac/ANGLESCALE; + anglefrac -= angleunits*ANGLESCALE; + ob->angle -= angleunits; + + if (ob->angle >= ANGLES) + ob->angle -= ANGLES; + if (ob->angle < 0) + ob->angle += ANGLES; + + } + +// +// forward/backwards move +// + if (controly < 0) + { + Thrust (ob->angle,-controly*MOVESCALE); // move forwards + } + else if (controly > 0) + { + angle = ob->angle + ANGLES/2; + if (angle >= ANGLES) + angle -= ANGLES; + Thrust (angle,controly*BACKMOVESCALE); // move backwards + } + + if (gamestate.victoryflag) // watching the BJ actor + return; + +// +// calculate total move +// + playerxmove = player->x - oldx; + playerymove = player->y - oldy; +} + +/* +============================================================================= + + STATUS WINDOW STUFF + +============================================================================= +*/ + + +/* +================== += += StatusDrawPic += +================== +*/ + +void StatusDrawPic (unsigned x, unsigned y, unsigned picnum) +{ + unsigned temp; + + temp = bufferofs; + bufferofs = 0; + + bufferofs = PAGE1START+(200-STATUSLINES)*SCREENWIDTH; + LatchDrawPic (x,y,picnum); + bufferofs = PAGE2START+(200-STATUSLINES)*SCREENWIDTH; + LatchDrawPic (x,y,picnum); + bufferofs = PAGE3START+(200-STATUSLINES)*SCREENWIDTH; + LatchDrawPic (x,y,picnum); + + bufferofs = temp; +} + + +/* +================== += += DrawFace += +================== +*/ + +void DrawFace (void) +{ + if (gamestate.health) + { + #ifdef SPEAR + if (godmode) + StatusDrawPic (17,4,GODMODEFACE1PIC+gamestate.faceframe); + else + #endif + StatusDrawPic (17,4,FACE1APIC+3*((100-gamestate.health)/16)+gamestate.faceframe); + } + else + { +#ifndef SPEAR + if (LastAttacker->obclass == needleobj) + StatusDrawPic (17,4,MUTANTBJPIC); + else +#endif + StatusDrawPic (17,4,FACE8APIC); + } +} + + +/* +=============== += += UpdateFace += += Calls draw face if time to change += +=============== +*/ + +#define FACETICS 70 + +int facecount; + +void UpdateFace (void) +{ + + if (SD_SoundPlaying() == GETGATLINGSND) + return; + + facecount += tics; + if (facecount > US_RndT()) + { + gamestate.faceframe = (US_RndT()>>6); + if (gamestate.faceframe==3) + gamestate.faceframe = 1; + + facecount = 0; + DrawFace (); + } +} + + + +/* +=============== += += LatchNumber += += right justifies and pads with blanks += +=============== +*/ + +void LatchNumber (int x, int y, int width, long number) +{ + unsigned length,c; + char str[20]; + + ltoa (number,str,10); + + length = strlen (str); + + while (length>=2; + + if (!godmode) + gamestate.health -= points; + + if (gamestate.health<=0) + { + gamestate.health = 0; + playstate = ex_died; + killerobj = attacker; + } + + StartDamageFlash (points); + + gotgatgun=0; + + DrawHealth (); + DrawFace (); + + // + // MAKE BJ'S EYES BUG IF MAJOR DAMAGE! + // + #ifdef SPEAR + if (points > 30 && gamestate.health!=0 && !godmode) + { + StatusDrawPic (17,4,BJOUCHPIC); + facecount = 0; + } + #endif + +} + + +/* +=============== += += HealSelf += +=============== +*/ + +void HealSelf (int points) +{ + gamestate.health += points; + if (gamestate.health>100) + gamestate.health = 100; + + DrawHealth (); + gotgatgun = 0; // JR + DrawFace (); +} + + +//=========================================================================== + + +/* +=============== += += DrawLevel += +=============== +*/ + +void DrawLevel (void) +{ +#ifdef SPEAR + if (gamestate.mapon == 20) + LatchNumber (2,16,2,18); + else +#endif + LatchNumber (2,16,2,gamestate.mapon+1); +} + +//=========================================================================== + + +/* +=============== += += DrawLives += +=============== +*/ + +void DrawLives (void) +{ + LatchNumber (14,16,1,gamestate.lives); +} + + +/* +=============== += += GiveExtraMan += +=============== +*/ + +void GiveExtraMan (void) +{ + if (gamestate.lives<9) + gamestate.lives++; + DrawLives (); + SD_PlaySound (BONUS1UPSND); +} + +//=========================================================================== + +/* +=============== += += DrawScore += +=============== +*/ + +void DrawScore (void) +{ + LatchNumber (6,16,6,gamestate.score); +} + +/* +=============== += += GivePoints += +=============== +*/ + +void GivePoints (long points) +{ + gamestate.score += points; + while (gamestate.score >= gamestate.nextextra) + { + gamestate.nextextra += EXTRAPOINTS; + GiveExtraMan (); + } + DrawScore (); +} + +//=========================================================================== + +/* +================== += += DrawWeapon += +================== +*/ + +void DrawWeapon (void) +{ + StatusDrawPic (32,8,KNIFEPIC+gamestate.weapon); +} + + +/* +================== += += DrawKeys += +================== +*/ + +void DrawKeys (void) +{ + if (gamestate.keys & 1) + StatusDrawPic (30,4,GOLDKEYPIC); + else + StatusDrawPic (30,4,NOKEYPIC); + + if (gamestate.keys & 2) + StatusDrawPic (30,20,SILVERKEYPIC); + else + StatusDrawPic (30,20,NOKEYPIC); +} + + + +/* +================== += += GiveWeapon += +================== +*/ + +void GiveWeapon (int weapon) +{ + GiveAmmo (6); + + if (gamestate.bestweapon 99) + gamestate.ammo = 99; + DrawAmmo (); +} + +//=========================================================================== + +/* +================== += += GiveKey += +================== +*/ + +void GiveKey (int key) +{ + gamestate.keys |= (1<itemnumber) + { + case bo_firstaid: + if (gamestate.health == 100) + return; + + SD_PlaySound (HEALTH2SND); + HealSelf (25); + break; + + case bo_key1: + case bo_key2: + case bo_key3: + case bo_key4: + GiveKey (check->itemnumber - bo_key1); + SD_PlaySound (GETKEYSND); + break; + + case bo_cross: + SD_PlaySound (BONUS1SND); + GivePoints (100); + gamestate.treasurecount++; + break; + case bo_chalice: + SD_PlaySound (BONUS2SND); + GivePoints (500); + gamestate.treasurecount++; + break; + case bo_bible: + SD_PlaySound (BONUS3SND); + GivePoints (1000); + gamestate.treasurecount++; + break; + case bo_crown: + SD_PlaySound (BONUS4SND); + GivePoints (5000); + gamestate.treasurecount++; + break; + + case bo_clip: + if (gamestate.ammo == 99) + return; + + SD_PlaySound (GETAMMOSND); + GiveAmmo (8); + break; + case bo_clip2: + if (gamestate.ammo == 99) + return; + + SD_PlaySound (GETAMMOSND); + GiveAmmo (4); + break; + +#ifdef SPEAR + case bo_25clip: + if (gamestate.ammo == 99) + return; + + SD_PlaySound (GETAMMOBOXSND); + GiveAmmo (25); + break; +#endif + + case bo_machinegun: + SD_PlaySound (GETMACHINESND); + GiveWeapon (wp_machinegun); + break; + case bo_chaingun: + SD_PlaySound (GETGATLINGSND); + GiveWeapon (wp_chaingun); + + StatusDrawPic (17,4,GOTGATLINGPIC); + facecount = 0; + gotgatgun = 1; + break; + + case bo_fullheal: + SD_PlaySound (BONUS1UPSND); + HealSelf (99); + GiveAmmo (25); + GiveExtraMan (); + gamestate.treasurecount++; + break; + + case bo_food: + if (gamestate.health == 100) + return; + + SD_PlaySound (HEALTH1SND); + HealSelf (10); + break; + + case bo_alpo: + if (gamestate.health == 100) + return; + + SD_PlaySound (HEALTH1SND); + HealSelf (4); + break; + + case bo_gibs: + if (gamestate.health >10) + return; + + SD_PlaySound (SLURPIESND); + HealSelf (1); + break; + + case bo_spear: + spearflag = true; + spearx = player->x; + speary = player->y; + spearangle = player->angle; + playstate = ex_completed; + } + + StartBonusFlash (); + check->shapenum = -1; // remove from list +} + + +/* +=================== += += TryMove += += returns true if move ok += debug: use pointers to optimize +=================== +*/ + +boolean TryMove (objtype *ob) +{ + int xl,yl,xh,yh,x,y; + objtype *check; + long deltax,deltay; + + xl = (ob->x-PLAYERSIZE) >>TILESHIFT; + yl = (ob->y-PLAYERSIZE) >>TILESHIFT; + + xh = (ob->x+PLAYERSIZE) >>TILESHIFT; + yh = (ob->y+PLAYERSIZE) >>TILESHIFT; + +// +// check for solid walls +// + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + check = actorat[x][y]; + if (check && check0) + yl--; + if (yh0) + xl--; + if (xh objlist + && (check->flags & FL_SHOOTABLE) ) + { + deltax = ob->x - check->x; + if (deltax < -MINACTORDIST || deltax > MINACTORDIST) + continue; + deltay = ob->y - check->y; + if (deltay < -MINACTORDIST || deltay > MINACTORDIST) + continue; + + return false; + } + } + + return true; +} + + +/* +=================== += += ClipMove += +=================== +*/ + +void ClipMove (objtype *ob, long xmove, long ymove) +{ + long basex,basey; + + basex = ob->x; + basey = ob->y; + + ob->x = basex+xmove; + ob->y = basey+ymove; + if (TryMove (ob)) + return; + + if (noclip && ob->x > 2*TILEGLOBAL && ob->y > 2*TILEGLOBAL && + ob->x < (((long)(mapwidth-1))<y < (((long)(mapheight-1))<x = basex+xmove; + ob->y = basey; + if (TryMove (ob)) + return; + + ob->x = basex; + ob->y = basey+ymove; + if (TryMove (ob)) + return; + + ob->x = basex; + ob->y = basey; +} + +//========================================================================== + +/* +=================== += += VictoryTile += +=================== +*/ + +void VictoryTile (void) +{ +#ifndef SPEAR + SpawnBJVictory (); +#endif + + gamestate.victoryflag = true; +} + + +/* +=================== += += Thrust += +=================== +*/ + +void Thrust (int angle, long speed) +{ + long xmove,ymove; + long slowmax; + unsigned offset; + + + // + // ZERO FUNNY COUNTER IF MOVED! + // + #ifdef SPEAR + if (speed) + funnyticount = 0; + #endif + + thrustspeed += speed; +// +// moving bounds speed +// + if (speed >= MINDIST*2) + speed = MINDIST*2-1; + + xmove = FixedByFrac(speed,costable[angle]); + ymove = -FixedByFrac(speed,sintable[angle]); + + ClipMove(player,xmove,ymove); + + player->tilex = player->x >> TILESHIFT; // scale to tile values + player->tiley = player->y >> TILESHIFT; + + offset = farmapylookup[player->tiley]+player->tilex; + player->areanumber = *(mapsegs[0] + offset) -AREATILE; + + if (*(mapsegs[1] + offset) == EXITTILE) + VictoryTile (); +} + + +/* +============================================================================= + + ACTIONS + +============================================================================= +*/ + + +/* +=============== += += Cmd_Fire += +=============== +*/ + +void Cmd_Fire (void) +{ + buttonheld[bt_attack] = true; + + gamestate.weaponframe = 0; + + player->state = &s_attack; + + gamestate.attackframe = 0; + gamestate.attackcount = + attackinfo[gamestate.weapon][gamestate.attackframe].tics; + gamestate.weaponframe = + attackinfo[gamestate.weapon][gamestate.attackframe].frame; +} + +//=========================================================================== + +/* +=============== += += Cmd_Use += +=============== +*/ + +void Cmd_Use (void) +{ + objtype *check; + int checkx,checky,doornum,dir; + boolean elevatorok; + + +// +// find which cardinal direction the player is facing +// + if (player->angle < ANGLES/8 || player->angle > 7*ANGLES/8) + { + checkx = player->tilex + 1; + checky = player->tiley; + dir = di_east; + elevatorok = true; + } + else if (player->angle < 3*ANGLES/8) + { + checkx = player->tilex; + checky = player->tiley-1; + dir = di_north; + elevatorok = false; + } + else if (player->angle < 5*ANGLES/8) + { + checkx = player->tilex - 1; + checky = player->tiley; + dir = di_west; + elevatorok = true; + } + else + { + checkx = player->tilex; + checky = player->tiley + 1; + dir = di_south; + elevatorok = false; + } + + doornum = tilemap[checkx][checky]; + if (*(mapsegs[1]+farmapylookup[checky]+checkx) == PUSHABLETILE) + { + // + // pushable wall + // + + PushWall (checkx,checky,dir); + return; + } + if (!buttonheld[bt_use] && doornum == ELEVATORTILE && elevatorok) + { + // + // use elevator + // + buttonheld[bt_use] = true; + + tilemap[checkx][checky]++; // flip switch + if (*(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) == ALTELEVATORTILE) + playstate = ex_secretlevel; + else + playstate = ex_completed; + SD_PlaySound (LEVELDONESND); + SD_WaitSoundDone(); + } + else if (!buttonheld[bt_use] && doornum & 0x80) + { + buttonheld[bt_use] = true; + OperateDoor (doornum & ~0x80); + } + else + SD_PlaySound (DONOTHINGSND); + +} + +/* +============================================================================= + + PLAYER CONTROL + +============================================================================= +*/ + + + +/* +=============== += += SpawnPlayer += +=============== +*/ + +void SpawnPlayer (int tilex, int tiley, int dir) +{ + player->obclass = playerobj; + player->active = true; + player->tilex = tilex; + player->tiley = tiley; + player->areanumber = + *(mapsegs[0] + farmapylookup[player->tiley]+player->tilex); + player->x = ((long)tilex<y = ((long)tiley<state = &s_player; + player->angle = (1-dir)*90; + if (player->angle<0) + player->angle += ANGLES; + player->flags = FL_NEVERMARK; + Thrust (0,0); // set some variables + + InitAreas (); +} + + +//=========================================================================== + +/* +=============== += += T_KnifeAttack += += Update player hands, and try to do damage when the proper frame is reached += +=============== +*/ + +void KnifeAttack (objtype *ob) +{ + objtype *check,*closest; + long dist; + + SD_PlaySound (ATKKNIFESND); +// actually fire + dist = 0x7fffffff; + closest = NULL; + for (check=ob->next ; check ; check=check->next) + if ( (check->flags & FL_SHOOTABLE) + && (check->flags & FL_VISABLE) + && abs (check->viewx-centerx) < shootdelta + ) + { + if (check->transx < dist) + { + dist = check->transx; + closest = check; + } + } + + if (!closest || dist> 0x18000l) + { + // missed + + return; + } + +// hit something + DamageActor (closest,US_RndT() >> 4); +} + + + +void GunAttack (objtype *ob) +{ + objtype *check,*closest,*oldclosest; + int damage; + int dx,dy,dist; + long viewdist; + + switch (gamestate.weapon) + { + case wp_pistol: + SD_PlaySound (ATKPISTOLSND); + break; + case wp_machinegun: + SD_PlaySound (ATKMACHINEGUNSND); + break; + case wp_chaingun: + SD_PlaySound (ATKGATLINGSND); + break; + } + + madenoise = true; + +// +// find potential targets +// + viewdist = 0x7fffffffl; + closest = NULL; + + while (1) + { + oldclosest = closest; + + for (check=ob->next ; check ; check=check->next) + if ( (check->flags & FL_SHOOTABLE) + && (check->flags & FL_VISABLE) + && abs (check->viewx-centerx) < shootdelta + ) + { + if (check->transx < viewdist) + { + viewdist = check->transx; + closest = check; + } + } + + if (closest == oldclosest) + return; // no more targets, all missed + + // + // trace a line from player to enemey + // + if (CheckLine(closest)) + break; + + } + +// +// hit something +// + dx = abs(closest->tilex - player->tilex); + dy = abs(closest->tiley - player->tiley); + dist = dx>dy ? dx:dy; + + if (dist<2) + damage = US_RndT() / 4; + else if (dist<4) + damage = US_RndT() / 6; + else + { + if ( (US_RndT() / 12) < dist) // missed + return; + damage = US_RndT() / 6; + } + + DamageActor (closest,damage); +} + +//=========================================================================== + +/* +=============== += += VictorySpin += +=============== +*/ + +void VictorySpin (void) +{ + long desty; + + if (player->angle > 270) + { + player->angle -= tics * 3; + if (player->angle < 270) + player->angle = 270; + } + else if (player->angle < 270) + { + player->angle += tics * 3; + if (player->angle > 270) + player->angle = 270; + } + + desty = (((long)player->tiley-5)<y > desty) + { + player->y -= tics*4096; + if (player->y < desty) + player->y = desty; + } +} + + +//=========================================================================== + +/* +=============== += += T_Attack += +=============== +*/ + +void T_Attack (objtype *ob) +{ + struct atkinf *cur; + + UpdateFace (); + + if (gamestate.victoryflag) // watching the BJ actor + { + VictorySpin (); + return; + } + + if ( buttonstate[bt_use] && !buttonheld[bt_use] ) + buttonstate[bt_use] = false; + + if ( buttonstate[bt_attack] && !buttonheld[bt_attack]) + buttonstate[bt_attack] = false; + + ControlMovement (ob); + if (gamestate.victoryflag) // watching the BJ actor + return; + + plux = player->x >> UNSIGNEDSHIFT; // scale to fit in unsigned + pluy = player->y >> UNSIGNEDSHIFT; + player->tilex = player->x >> TILESHIFT; // scale to tile values + player->tiley = player->y >> TILESHIFT; + +// +// change frame and fire +// + gamestate.attackcount -= tics; + while (gamestate.attackcount <= 0) + { + cur = &attackinfo[gamestate.weapon][gamestate.attackframe]; + switch (cur->attack) + { + case -1: + ob->state = &s_player; + if (!gamestate.ammo) + { + gamestate.weapon = wp_knife; + DrawWeapon (); + } + else + { + if (gamestate.weapon != gamestate.chosenweapon) + { + gamestate.weapon = gamestate.chosenweapon; + DrawWeapon (); + } + }; + gamestate.attackframe = gamestate.weaponframe = 0; + return; + + case 4: + if (!gamestate.ammo) + break; + if (buttonstate[bt_attack]) + gamestate.attackframe -= 2; + case 1: + if (!gamestate.ammo) + { // can only happen with chain gun + gamestate.attackframe++; + break; + } + GunAttack (ob); + gamestate.ammo--; + DrawAmmo (); + break; + + case 2: + KnifeAttack (ob); + break; + + case 3: + if (gamestate.ammo && buttonstate[bt_attack]) + gamestate.attackframe -= 2; + break; + } + + gamestate.attackcount += cur->tics; + gamestate.attackframe++; + gamestate.weaponframe = + attackinfo[gamestate.weapon][gamestate.attackframe].frame; + } + +} + + + +//=========================================================================== + +/* +=============== += += T_Player += +=============== +*/ + +void T_Player (objtype *ob) +{ + if (gamestate.victoryflag) // watching the BJ actor + { + VictorySpin (); + return; + } + + UpdateFace (); + CheckWeaponChange (); + + if ( buttonstate[bt_use] ) + Cmd_Use (); + + if ( buttonstate[bt_attack] && !buttonheld[bt_attack]) + Cmd_Fire (); + + ControlMovement (ob); + if (gamestate.victoryflag) // watching the BJ actor + return; + + + plux = player->x >> UNSIGNEDSHIFT; // scale to fit in unsigned + pluy = player->y >> UNSIGNEDSHIFT; + player->tilex = player->x >> TILESHIFT; // scale to tile values + player->tiley = player->y >> TILESHIFT; +} + + diff --git a/src/lib/hb/wl_asm.asm b/src/lib/hb/wl_asm.asm new file mode 100755 index 00000000..cca5d690 --- /dev/null +++ b/src/lib/hb/wl_asm.asm @@ -0,0 +1,69 @@ +; JABHACK.ASM + +.386C +IDEAL +MODEL MEDIUM + +EXTRN LDIV@:far + +;============================================================================ + +DATASEG + +;============================================================================ + +CODESEG + +; Hacked up Juan Jimenez's code a bit to just return 386/not 386 +PROC _CheckIs386 +PUBLIC _CheckIs386 + +;hack to never look for a 386, for benchmark comparisons of same code on all CPUs +; pushf ; Save flag registers, we use them here +; xor ax,ax ; Clear AX and... +; push ax ; ...push it onto the stack +; popf ; Pop 0 into flag registers (all bits to 0), +; pushf ; attempting to set bits 12-15 of flags to 0's +; pop ax ; Recover the save flags +; and ax,08000h ; If bits 12-15 of flags are set to +; cmp ax,08000h ; zero then it's 8088/86 or 80188/186 +; jz not386 +; +; mov ax,07000h ; Try to set flag bits 12-14 to 1's +; push ax ; Push the test value onto the stack +; popf ; Pop it into the flag register +; pushf ; Push it back onto the stack +; pop ax ; Pop it into AX for check +; and ax,07000h ; if bits 12-14 are cleared then +; jz not386 ; the chip is an 80286 +; +; mov ax,1 ; We now assume it's a 80386 or better +; popf +; retf +;end benchmark hack + +not386: + xor ax,ax + popf + retf + + ENDP + + +PROC _jabhack2 +PUBLIC _jabhack2 + + push es + + mov ax,seg LDIV@ + mov es,ax + mov ax,9090h ;Two NOP's + mov [WORD FAR es:LDIV@],ax ;Patch over XOR AX,AX + mov [WORD FAR es:LDIV@+2],ax ;and over JMP SHORT COMMON + + pop es + retf + + ENDP + + END diff --git a/src/lib/wl_debug.c b/src/lib/hb/wl_debug.c similarity index 100% rename from src/lib/wl_debug.c rename to src/lib/hb/wl_debug.c diff --git a/src/lib/hb/wl_def.h b/src/lib/hb/wl_def.h new file mode 100755 index 00000000..d1bc0802 --- /dev/null +++ b/src/lib/hb/wl_def.h @@ -0,0 +1,1276 @@ +//#define BETA +#define YEAR 1992 +#define MONTH 9 +#define DAY 30 + +#include "ID_HEADS.H" +#include +#include + +#include "WL_MENU.H" + +#ifdef SPANISH +#include "SPANISH.H" +#else +#include "FOREIGN.H" +#endif + +#ifdef SPEAR +#include "F_SPEAR.H" +#endif + +/* +============================================================================= + + MACROS + +============================================================================= +*/ + + +#define COLORBORDER(color) asm{mov dx,STATUS_REGISTER_1;in al,dx;\ + mov dx,ATR_INDEX;mov al,ATR_OVERSCAN;out dx,al;mov al,color;out dx,al;\ + mov al,32;out dx,al}; + +#define MAPSPOT(x,y,plane) (*(mapsegs[plane]+farmapylookup[y]+x)) + +#define SIGN(x) ((x)>0?1:-1) +#define ABS(x) ((int)(x)>0?(x):-(x)) +#define LABS(x) ((long)(x)>0?(x):-(x)) + +/* +============================================================================= + + GLOBAL CONSTANTS + +============================================================================= +*/ + +#define MAXACTORS 150 // max number of nazis, etc / map +#define MAXSTATS 400 // max number of lamps, bonus, etc +#define MAXDOORS 64 // max number of sliding doors +#define MAXWALLTILES 64 // max number of wall tiles + +// +// tile constants +// + +#define ICONARROWS 90 +#define PUSHABLETILE 98 +#define EXITTILE 99 // at end of castle +#define AREATILE 107 // first of NUMAREAS floor tiles +#define NUMAREAS 37 +#define ELEVATORTILE 21 +#define AMBUSHTILE 106 +#define ALTELEVATORTILE 107 + +#define NUMBERCHARS 9 + + +//---------------- + +#define EXTRAPOINTS 40000 + +#define PLAYERSPEED 3000 +#define RUNSPEED 6000 + +#define SCREENSEG 0xa000 + +#define SCREENBWIDE 80 + +#define HEIGHTRATIO 0.50 // also defined in id_mm.c + +#define BORDERCOLOR 3 +#define FLASHCOLOR 5 +#define FLASHTICS 4 + + +#define PLAYERSIZE MINDIST // player radius +#define MINACTORDIST 0x10000l // minimum dist from player center + // to any actor center + +#define NUMLATCHPICS 100 + + +#define PI 3.141592657 + +#define GLOBAL1 (1l<<16) +#define TILEGLOBAL GLOBAL1 +#define PIXGLOBAL (GLOBAL1/64) +#define TILESHIFT 16l +#define UNSIGNEDSHIFT 8 + +#define ANGLES 360 // must be divisable by 4 +#define ANGLEQUAD (ANGLES/4) +#define FINEANGLES 3600 +#define ANG90 (FINEANGLES/4) +#define ANG180 (ANG90*2) +#define ANG270 (ANG90*3) +#define ANG360 (ANG90*4) +#define VANG90 (ANGLES/4) +#define VANG180 (VANG90*2) +#define VANG270 (VANG90*3) +#define VANG360 (VANG90*4) + +#define MINDIST (0x5800l) + + +#define MAXSCALEHEIGHT 256 // largest scale on largest view + +#define MAXVIEWWIDTH 320 + +#define MAPSIZE 64 // maps are 64*64 max +#define NORTH 0 +#define EAST 1 +#define SOUTH 2 +#define WEST 3 + + +#define STATUSLINES 40 + +#define SCREENSIZE (SCREENBWIDE*208) +#define PAGE1START 0 +#define PAGE2START (SCREENSIZE) +#define PAGE3START (SCREENSIZE*2u) +#define FREESTART (SCREENSIZE*3u) + + +#define PIXRADIUS 512 + +#define STARTAMMO 8 + + +// object flag values + +#define FL_SHOOTABLE 1 +#define FL_BONUS 2 +#define FL_NEVERMARK 4 +#define FL_VISABLE 8 +#define FL_ATTACKMODE 16 +#define FL_FIRSTATTACK 32 +#define FL_AMBUSH 64 +#define FL_NONMARK 128 + + +// +// sprite constants +// + +enum { + SPR_DEMO, + SPR_DEATHCAM, +// +// static sprites +// + SPR_STAT_0,SPR_STAT_1,SPR_STAT_2,SPR_STAT_3, + SPR_STAT_4,SPR_STAT_5,SPR_STAT_6,SPR_STAT_7, + + SPR_STAT_8,SPR_STAT_9,SPR_STAT_10,SPR_STAT_11, + SPR_STAT_12,SPR_STAT_13,SPR_STAT_14,SPR_STAT_15, + + SPR_STAT_16,SPR_STAT_17,SPR_STAT_18,SPR_STAT_19, + SPR_STAT_20,SPR_STAT_21,SPR_STAT_22,SPR_STAT_23, + + SPR_STAT_24,SPR_STAT_25,SPR_STAT_26,SPR_STAT_27, + SPR_STAT_28,SPR_STAT_29,SPR_STAT_30,SPR_STAT_31, + + SPR_STAT_32,SPR_STAT_33,SPR_STAT_34,SPR_STAT_35, + SPR_STAT_36,SPR_STAT_37,SPR_STAT_38,SPR_STAT_39, + + SPR_STAT_40,SPR_STAT_41,SPR_STAT_42,SPR_STAT_43, + SPR_STAT_44,SPR_STAT_45,SPR_STAT_46,SPR_STAT_47, + +#ifdef SPEAR + SPR_STAT_48,SPR_STAT_49,SPR_STAT_50,SPR_STAT_51, +#endif + +// +// guard +// + SPR_GRD_S_1,SPR_GRD_S_2,SPR_GRD_S_3,SPR_GRD_S_4, + SPR_GRD_S_5,SPR_GRD_S_6,SPR_GRD_S_7,SPR_GRD_S_8, + + SPR_GRD_W1_1,SPR_GRD_W1_2,SPR_GRD_W1_3,SPR_GRD_W1_4, + SPR_GRD_W1_5,SPR_GRD_W1_6,SPR_GRD_W1_7,SPR_GRD_W1_8, + + SPR_GRD_W2_1,SPR_GRD_W2_2,SPR_GRD_W2_3,SPR_GRD_W2_4, + SPR_GRD_W2_5,SPR_GRD_W2_6,SPR_GRD_W2_7,SPR_GRD_W2_8, + + SPR_GRD_W3_1,SPR_GRD_W3_2,SPR_GRD_W3_3,SPR_GRD_W3_4, + SPR_GRD_W3_5,SPR_GRD_W3_6,SPR_GRD_W3_7,SPR_GRD_W3_8, + + SPR_GRD_W4_1,SPR_GRD_W4_2,SPR_GRD_W4_3,SPR_GRD_W4_4, + SPR_GRD_W4_5,SPR_GRD_W4_6,SPR_GRD_W4_7,SPR_GRD_W4_8, + + SPR_GRD_PAIN_1,SPR_GRD_DIE_1,SPR_GRD_DIE_2,SPR_GRD_DIE_3, + SPR_GRD_PAIN_2,SPR_GRD_DEAD, + + SPR_GRD_SHOOT1,SPR_GRD_SHOOT2,SPR_GRD_SHOOT3, + +// +// dogs +// + SPR_DOG_W1_1,SPR_DOG_W1_2,SPR_DOG_W1_3,SPR_DOG_W1_4, + SPR_DOG_W1_5,SPR_DOG_W1_6,SPR_DOG_W1_7,SPR_DOG_W1_8, + + SPR_DOG_W2_1,SPR_DOG_W2_2,SPR_DOG_W2_3,SPR_DOG_W2_4, + SPR_DOG_W2_5,SPR_DOG_W2_6,SPR_DOG_W2_7,SPR_DOG_W2_8, + + SPR_DOG_W3_1,SPR_DOG_W3_2,SPR_DOG_W3_3,SPR_DOG_W3_4, + SPR_DOG_W3_5,SPR_DOG_W3_6,SPR_DOG_W3_7,SPR_DOG_W3_8, + + SPR_DOG_W4_1,SPR_DOG_W4_2,SPR_DOG_W4_3,SPR_DOG_W4_4, + SPR_DOG_W4_5,SPR_DOG_W4_6,SPR_DOG_W4_7,SPR_DOG_W4_8, + + SPR_DOG_DIE_1,SPR_DOG_DIE_2,SPR_DOG_DIE_3,SPR_DOG_DEAD, + SPR_DOG_JUMP1,SPR_DOG_JUMP2,SPR_DOG_JUMP3, + + + +// +// ss +// + SPR_SS_S_1,SPR_SS_S_2,SPR_SS_S_3,SPR_SS_S_4, + SPR_SS_S_5,SPR_SS_S_6,SPR_SS_S_7,SPR_SS_S_8, + + SPR_SS_W1_1,SPR_SS_W1_2,SPR_SS_W1_3,SPR_SS_W1_4, + SPR_SS_W1_5,SPR_SS_W1_6,SPR_SS_W1_7,SPR_SS_W1_8, + + SPR_SS_W2_1,SPR_SS_W2_2,SPR_SS_W2_3,SPR_SS_W2_4, + SPR_SS_W2_5,SPR_SS_W2_6,SPR_SS_W2_7,SPR_SS_W2_8, + + SPR_SS_W3_1,SPR_SS_W3_2,SPR_SS_W3_3,SPR_SS_W3_4, + SPR_SS_W3_5,SPR_SS_W3_6,SPR_SS_W3_7,SPR_SS_W3_8, + + SPR_SS_W4_1,SPR_SS_W4_2,SPR_SS_W4_3,SPR_SS_W4_4, + SPR_SS_W4_5,SPR_SS_W4_6,SPR_SS_W4_7,SPR_SS_W4_8, + + SPR_SS_PAIN_1,SPR_SS_DIE_1,SPR_SS_DIE_2,SPR_SS_DIE_3, + SPR_SS_PAIN_2,SPR_SS_DEAD, + + SPR_SS_SHOOT1,SPR_SS_SHOOT2,SPR_SS_SHOOT3, + +// +// mutant +// + SPR_MUT_S_1,SPR_MUT_S_2,SPR_MUT_S_3,SPR_MUT_S_4, + SPR_MUT_S_5,SPR_MUT_S_6,SPR_MUT_S_7,SPR_MUT_S_8, + + SPR_MUT_W1_1,SPR_MUT_W1_2,SPR_MUT_W1_3,SPR_MUT_W1_4, + SPR_MUT_W1_5,SPR_MUT_W1_6,SPR_MUT_W1_7,SPR_MUT_W1_8, + + SPR_MUT_W2_1,SPR_MUT_W2_2,SPR_MUT_W2_3,SPR_MUT_W2_4, + SPR_MUT_W2_5,SPR_MUT_W2_6,SPR_MUT_W2_7,SPR_MUT_W2_8, + + SPR_MUT_W3_1,SPR_MUT_W3_2,SPR_MUT_W3_3,SPR_MUT_W3_4, + SPR_MUT_W3_5,SPR_MUT_W3_6,SPR_MUT_W3_7,SPR_MUT_W3_8, + + SPR_MUT_W4_1,SPR_MUT_W4_2,SPR_MUT_W4_3,SPR_MUT_W4_4, + SPR_MUT_W4_5,SPR_MUT_W4_6,SPR_MUT_W4_7,SPR_MUT_W4_8, + + SPR_MUT_PAIN_1,SPR_MUT_DIE_1,SPR_MUT_DIE_2,SPR_MUT_DIE_3, + SPR_MUT_PAIN_2,SPR_MUT_DIE_4,SPR_MUT_DEAD, + + SPR_MUT_SHOOT1,SPR_MUT_SHOOT2,SPR_MUT_SHOOT3,SPR_MUT_SHOOT4, + +// +// officer +// + SPR_OFC_S_1,SPR_OFC_S_2,SPR_OFC_S_3,SPR_OFC_S_4, + SPR_OFC_S_5,SPR_OFC_S_6,SPR_OFC_S_7,SPR_OFC_S_8, + + SPR_OFC_W1_1,SPR_OFC_W1_2,SPR_OFC_W1_3,SPR_OFC_W1_4, + SPR_OFC_W1_5,SPR_OFC_W1_6,SPR_OFC_W1_7,SPR_OFC_W1_8, + + SPR_OFC_W2_1,SPR_OFC_W2_2,SPR_OFC_W2_3,SPR_OFC_W2_4, + SPR_OFC_W2_5,SPR_OFC_W2_6,SPR_OFC_W2_7,SPR_OFC_W2_8, + + SPR_OFC_W3_1,SPR_OFC_W3_2,SPR_OFC_W3_3,SPR_OFC_W3_4, + SPR_OFC_W3_5,SPR_OFC_W3_6,SPR_OFC_W3_7,SPR_OFC_W3_8, + + SPR_OFC_W4_1,SPR_OFC_W4_2,SPR_OFC_W4_3,SPR_OFC_W4_4, + SPR_OFC_W4_5,SPR_OFC_W4_6,SPR_OFC_W4_7,SPR_OFC_W4_8, + + SPR_OFC_PAIN_1,SPR_OFC_DIE_1,SPR_OFC_DIE_2,SPR_OFC_DIE_3, + SPR_OFC_PAIN_2,SPR_OFC_DIE_4,SPR_OFC_DEAD, + + SPR_OFC_SHOOT1,SPR_OFC_SHOOT2,SPR_OFC_SHOOT3, + +#ifndef SPEAR +// +// ghosts +// + SPR_BLINKY_W1,SPR_BLINKY_W2,SPR_PINKY_W1,SPR_PINKY_W2, + SPR_CLYDE_W1,SPR_CLYDE_W2,SPR_INKY_W1,SPR_INKY_W2, + +// +// hans +// + SPR_BOSS_W1,SPR_BOSS_W2,SPR_BOSS_W3,SPR_BOSS_W4, + SPR_BOSS_SHOOT1,SPR_BOSS_SHOOT2,SPR_BOSS_SHOOT3,SPR_BOSS_DEAD, + + SPR_BOSS_DIE1,SPR_BOSS_DIE2,SPR_BOSS_DIE3, + +// +// schabbs +// + SPR_SCHABB_W1,SPR_SCHABB_W2,SPR_SCHABB_W3,SPR_SCHABB_W4, + SPR_SCHABB_SHOOT1,SPR_SCHABB_SHOOT2, + + SPR_SCHABB_DIE1,SPR_SCHABB_DIE2,SPR_SCHABB_DIE3,SPR_SCHABB_DEAD, + SPR_HYPO1,SPR_HYPO2,SPR_HYPO3,SPR_HYPO4, + +// +// fake +// + SPR_FAKE_W1,SPR_FAKE_W2,SPR_FAKE_W3,SPR_FAKE_W4, + SPR_FAKE_SHOOT,SPR_FIRE1,SPR_FIRE2, + + SPR_FAKE_DIE1,SPR_FAKE_DIE2,SPR_FAKE_DIE3,SPR_FAKE_DIE4, + SPR_FAKE_DIE5,SPR_FAKE_DEAD, + +// +// hitler +// + SPR_MECHA_W1,SPR_MECHA_W2,SPR_MECHA_W3,SPR_MECHA_W4, + SPR_MECHA_SHOOT1,SPR_MECHA_SHOOT2,SPR_MECHA_SHOOT3,SPR_MECHA_DEAD, + + SPR_MECHA_DIE1,SPR_MECHA_DIE2,SPR_MECHA_DIE3, + + SPR_HITLER_W1,SPR_HITLER_W2,SPR_HITLER_W3,SPR_HITLER_W4, + SPR_HITLER_SHOOT1,SPR_HITLER_SHOOT2,SPR_HITLER_SHOOT3,SPR_HITLER_DEAD, + + SPR_HITLER_DIE1,SPR_HITLER_DIE2,SPR_HITLER_DIE3,SPR_HITLER_DIE4, + SPR_HITLER_DIE5,SPR_HITLER_DIE6,SPR_HITLER_DIE7, + +// +// giftmacher +// + SPR_GIFT_W1,SPR_GIFT_W2,SPR_GIFT_W3,SPR_GIFT_W4, + SPR_GIFT_SHOOT1,SPR_GIFT_SHOOT2, + + SPR_GIFT_DIE1,SPR_GIFT_DIE2,SPR_GIFT_DIE3,SPR_GIFT_DEAD, +#endif +// +// Rocket, smoke and small explosion +// + SPR_ROCKET_1,SPR_ROCKET_2,SPR_ROCKET_3,SPR_ROCKET_4, + SPR_ROCKET_5,SPR_ROCKET_6,SPR_ROCKET_7,SPR_ROCKET_8, + + SPR_SMOKE_1,SPR_SMOKE_2,SPR_SMOKE_3,SPR_SMOKE_4, + SPR_BOOM_1,SPR_BOOM_2,SPR_BOOM_3, + +// +// Angel of Death's DeathSparks(tm) +// +#ifdef SPEAR + SPR_HROCKET_1,SPR_HROCKET_2,SPR_HROCKET_3,SPR_HROCKET_4, + SPR_HROCKET_5,SPR_HROCKET_6,SPR_HROCKET_7,SPR_HROCKET_8, + + SPR_HSMOKE_1,SPR_HSMOKE_2,SPR_HSMOKE_3,SPR_HSMOKE_4, + SPR_HBOOM_1,SPR_HBOOM_2,SPR_HBOOM_3, + + SPR_SPARK1,SPR_SPARK2,SPR_SPARK3,SPR_SPARK4, +#endif + +#ifndef SPEAR +// +// gretel +// + SPR_GRETEL_W1,SPR_GRETEL_W2,SPR_GRETEL_W3,SPR_GRETEL_W4, + SPR_GRETEL_SHOOT1,SPR_GRETEL_SHOOT2,SPR_GRETEL_SHOOT3,SPR_GRETEL_DEAD, + + SPR_GRETEL_DIE1,SPR_GRETEL_DIE2,SPR_GRETEL_DIE3, + +// +// fat face +// + SPR_FAT_W1,SPR_FAT_W2,SPR_FAT_W3,SPR_FAT_W4, + SPR_FAT_SHOOT1,SPR_FAT_SHOOT2,SPR_FAT_SHOOT3,SPR_FAT_SHOOT4, + + SPR_FAT_DIE1,SPR_FAT_DIE2,SPR_FAT_DIE3,SPR_FAT_DEAD, + +// +// bj +// + SPR_BJ_W1,SPR_BJ_W2,SPR_BJ_W3,SPR_BJ_W4, + SPR_BJ_JUMP1,SPR_BJ_JUMP2,SPR_BJ_JUMP3,SPR_BJ_JUMP4, +#else +// +// THESE ARE FOR 'SPEAR OF DESTINY' +// + +// +// Trans Grosse +// + SPR_TRANS_W1,SPR_TRANS_W2,SPR_TRANS_W3,SPR_TRANS_W4, + SPR_TRANS_SHOOT1,SPR_TRANS_SHOOT2,SPR_TRANS_SHOOT3,SPR_TRANS_DEAD, + + SPR_TRANS_DIE1,SPR_TRANS_DIE2,SPR_TRANS_DIE3, + +// +// Wilhelm +// + SPR_WILL_W1,SPR_WILL_W2,SPR_WILL_W3,SPR_WILL_W4, + SPR_WILL_SHOOT1,SPR_WILL_SHOOT2,SPR_WILL_SHOOT3,SPR_WILL_SHOOT4, + + SPR_WILL_DIE1,SPR_WILL_DIE2,SPR_WILL_DIE3,SPR_WILL_DEAD, + +// +// UberMutant +// + SPR_UBER_W1,SPR_UBER_W2,SPR_UBER_W3,SPR_UBER_W4, + SPR_UBER_SHOOT1,SPR_UBER_SHOOT2,SPR_UBER_SHOOT3,SPR_UBER_SHOOT4, + + SPR_UBER_DIE1,SPR_UBER_DIE2,SPR_UBER_DIE3,SPR_UBER_DIE4, + SPR_UBER_DEAD, + +// +// Death Knight +// + SPR_DEATH_W1,SPR_DEATH_W2,SPR_DEATH_W3,SPR_DEATH_W4, + SPR_DEATH_SHOOT1,SPR_DEATH_SHOOT2,SPR_DEATH_SHOOT3,SPR_DEATH_SHOOT4, + + SPR_DEATH_DIE1,SPR_DEATH_DIE2,SPR_DEATH_DIE3,SPR_DEATH_DIE4, + SPR_DEATH_DIE5,SPR_DEATH_DIE6,SPR_DEATH_DEAD, + +// +// Ghost +// + SPR_SPECTRE_W1,SPR_SPECTRE_W2,SPR_SPECTRE_W3,SPR_SPECTRE_W4, + SPR_SPECTRE_F1,SPR_SPECTRE_F2,SPR_SPECTRE_F3,SPR_SPECTRE_F4, + +// +// Angel of Death +// + SPR_ANGEL_W1,SPR_ANGEL_W2,SPR_ANGEL_W3,SPR_ANGEL_W4, + SPR_ANGEL_SHOOT1,SPR_ANGEL_SHOOT2,SPR_ANGEL_TIRED1,SPR_ANGEL_TIRED2, + + SPR_ANGEL_DIE1,SPR_ANGEL_DIE2,SPR_ANGEL_DIE3,SPR_ANGEL_DIE4, + SPR_ANGEL_DIE5,SPR_ANGEL_DIE6,SPR_ANGEL_DIE7,SPR_ANGEL_DEAD, + +#endif + +// +// player attack frames +// + SPR_KNIFEREADY,SPR_KNIFEATK1,SPR_KNIFEATK2,SPR_KNIFEATK3, + SPR_KNIFEATK4, + + SPR_PISTOLREADY,SPR_PISTOLATK1,SPR_PISTOLATK2,SPR_PISTOLATK3, + SPR_PISTOLATK4, + + SPR_MACHINEGUNREADY,SPR_MACHINEGUNATK1,SPR_MACHINEGUNATK2,MACHINEGUNATK3, + SPR_MACHINEGUNATK4, + + SPR_CHAINREADY,SPR_CHAINATK1,SPR_CHAINATK2,SPR_CHAINATK3, + SPR_CHAINATK4, + + }; + + +/* +============================================================================= + + GLOBAL TYPES + +============================================================================= +*/ + +typedef long fixed; + +typedef enum { + di_north, + di_east, + di_south, + di_west +} controldir_t; + +typedef enum { + dr_normal, + dr_lock1, + dr_lock2, + dr_lock3, + dr_lock4, + dr_elevator +} door_t; + +typedef enum { + ac_badobject = -1, + ac_no, + ac_yes, + ac_allways +} activetype; + +typedef enum { + nothing, + playerobj, + inertobj, + guardobj, + officerobj, + ssobj, + dogobj, + bossobj, + schabbobj, + fakeobj, + mechahitlerobj, + mutantobj, + needleobj, + fireobj, + bjobj, + ghostobj, + realhitlerobj, + gretelobj, + giftobj, + fatobj, + rocketobj, + + spectreobj, + angelobj, + transobj, + uberobj, + willobj, + deathobj, + hrocketobj, + sparkobj +} classtype; + +typedef enum { + dressing, + block, + bo_gibs, + bo_alpo, + bo_firstaid, + bo_key1, + bo_key2, + bo_key3, + bo_key4, + bo_cross, + bo_chalice, + bo_bible, + bo_crown, + bo_clip, + bo_clip2, + bo_machinegun, + bo_chaingun, + bo_food, + bo_fullheal, + bo_25clip, + bo_spear +} stat_t; + +typedef enum { + east, + northeast, + north, + northwest, + west, + southwest, + south, + southeast, + nodir +} dirtype; + + +#define NUMENEMIES 22 +typedef enum { + en_guard, + en_officer, + en_ss, + en_dog, + en_boss, + en_schabbs, + en_fake, + en_hitler, + en_mutant, + en_blinky, + en_clyde, + en_pinky, + en_inky, + en_gretel, + en_gift, + en_fat, + en_spectre, + en_angel, + en_trans, + en_uber, + en_will, + en_death +} enemy_t; + + +typedef struct statestruct +{ + boolean rotate; + int shapenum; // a shapenum of -1 means get from ob->temp1 + int tictime; + void (*think) (),(*action) (); + struct statestruct *next; +} statetype; + + +//--------------------- +// +// trivial actor structure +// +//--------------------- + +typedef struct statstruct +{ + byte tilex,tiley; + byte *visspot; + int shapenum; // if shapenum == -1 the obj has been removed + byte flags; + byte itemnumber; +} statobj_t; + + +//--------------------- +// +// door actor structure +// +//--------------------- + +typedef struct doorstruct +{ + byte tilex,tiley; + boolean vertical; + byte lock; + enum {dr_open,dr_closed,dr_opening,dr_closing} action; + int ticcount; +} doorobj_t; + + +//-------------------- +// +// thinking actor structure +// +//-------------------- + +typedef struct objstruct +{ + activetype active; + int ticcount; + classtype obclass; + statetype *state; + + byte flags; // FL_SHOOTABLE, etc + + long distance; // if negative, wait for that door to open + dirtype dir; + + fixed x,y; + unsigned tilex,tiley; + byte areanumber; + + int viewx; + unsigned viewheight; + fixed transx,transy; // in global coord + + int angle; + int hitpoints; + long speed; + + int temp1,temp2,temp3; + struct objstruct *next,*prev; +} objtype; + + +#define NUMBUTTONS 8 +enum { + bt_nobutton=-1, + bt_attack=0, + bt_strafe, + bt_run, + bt_use, + bt_readyknife, + bt_readypistol, + bt_readymachinegun, + bt_readychaingun +}; + + +#define NUMWEAPONS 5 +typedef enum { + wp_knife, + wp_pistol, + wp_machinegun, + wp_chaingun +} weapontype; + + +typedef enum { + gd_baby, + gd_easy, + gd_medium, + gd_hard +}; + +//--------------- +// +// gamestate structure +// +//--------------- + +typedef struct +{ + int difficulty; + int mapon; + long oldscore,score,nextextra; + int lives; + int health; + int ammo; + int keys; + weapontype bestweapon,weapon,chosenweapon; + + int faceframe; + int attackframe,attackcount,weaponframe; + + int episode,secretcount,treasurecount,killcount, + secrettotal,treasuretotal,killtotal; + long TimeCount; + long killx,killy; + boolean victoryflag; // set during victory animations +} gametype; + + +typedef enum { + ex_stillplaying, + ex_completed, + ex_died, + ex_warped, + ex_resetgame, + ex_loadedgame, + ex_victorious, + ex_abort, + ex_demodone, + ex_secretlevel +} exit_t; + + +/* +============================================================================= + + WL_MAIN DEFINITIONS + +============================================================================= +*/ + +extern boolean MS_CheckParm (char far *string); + +extern char str[80],str2[20]; +extern int tedlevelnum; +extern boolean tedlevel; +extern boolean nospr; +extern boolean IsA386; + +extern byte far *scalermemory; + +extern fixed focallength; +extern unsigned viewangles; +extern unsigned screenofs; +extern int viewwidth; +extern int viewheight; +extern int centerx; +extern int shootdelta; + +extern int dirangle[9]; + +extern boolean startgame,loadedgame,virtualreality; +extern int mouseadjustment; +// +// math tables +// +extern int pixelangle[MAXVIEWWIDTH]; +extern long far finetangent[FINEANGLES/4]; +extern fixed far sintable[],far *costable; + +// +// derived constants +// +extern fixed scale,maxslope; +extern long heightnumerator; +extern int minheightdiv; + +extern char configname[13]; + + + +void HelpScreens (void); +void OrderingInfo (void); +void TEDDeath(void); +void Quit (char *error); +void CalcProjection (long focal); +boolean SetViewSize (unsigned width, unsigned height); +void NewGame (int difficulty,int episode); +void NewViewSize (int width); +boolean LoadTheGame(int file,int x,int y); +boolean SaveTheGame(int file,int x,int y); +void ShowViewSize (int width); +void ShutdownId (void); + + +/* +============================================================================= + + WL_GAME DEFINITIONS + +============================================================================= +*/ + + +extern boolean ingame,fizzlein; +extern unsigned latchpics[NUMLATCHPICS]; +extern gametype gamestate; +extern int doornum; + +extern char demoname[13]; + +extern long spearx,speary; +extern unsigned spearangle; +extern boolean spearflag; + + +void DrawPlayBorder (void); +void ScanInfoPlane (void); +void SetupGameLevel (void); +void NormalScreen (void); +void DrawPlayScreen (void); +void FizzleOut (void); +void GameLoop (void); +void ClearMemory (void); +void PlayDemo (int demonumber); +void RecordDemo (void); +void DrawAllPlayBorder (void); +void DrawHighScores(void); +void DrawAllPlayBorderSides (void); + + +// JAB +#define PlaySoundLocTile(s,tx,ty) PlaySoundLocGlobal(s,(((long)(tx) << TILESHIFT) + (1L << (TILESHIFT - 1))),(((long)ty << TILESHIFT) + (1L << (TILESHIFT - 1)))) +#define PlaySoundLocActor(s,ob) PlaySoundLocGlobal(s,(ob)->x,(ob)->y) +void PlaySoundLocGlobal(word s,fixed gx,fixed gy); +void UpdateSoundLoc(void); + + +/* +============================================================================= + + WL_PLAY DEFINITIONS + +============================================================================= +*/ + +#ifdef SPEAR +extern long funnyticount; // FOR FUNNY BJ FACE +#endif + +extern exit_t playstate; + +extern boolean madenoise; + +extern objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj, + *objfreelist,*killerobj; +extern statobj_t statobjlist[MAXSTATS],*laststatobj; +extern doorobj_t doorobjlist[MAXDOORS],*lastdoorobj; + +extern unsigned farmapylookup[MAPSIZE]; +extern byte *nearmapylookup[MAPSIZE]; + +extern byte tilemap[MAPSIZE][MAPSIZE]; // wall values only +extern byte spotvis[MAPSIZE][MAPSIZE]; +extern objtype *actorat[MAPSIZE][MAPSIZE]; + +#define UPDATESIZE (UPDATEWIDE*UPDATEHIGH) +extern byte update[UPDATESIZE]; + +extern boolean singlestep,godmode,noclip; +extern int extravbls; + +// +// control info +// +extern boolean mouseenabled,joystickenabled,joypadenabled,joystickprogressive; +extern int joystickport; +extern int dirscan[4]; +extern int buttonscan[NUMBUTTONS]; +extern int buttonmouse[4]; +extern int buttonjoy[4]; + +extern boolean buttonheld[NUMBUTTONS]; + +extern int viewsize; + +// +// curent user input +// +extern int controlx,controly; // range from -100 to 100 +extern boolean buttonstate[NUMBUTTONS]; + +extern boolean demorecord,demoplayback; +extern char far *demoptr, far *lastdemoptr; +extern memptr demobuffer; + + + +void InitRedShifts (void); +void FinishPaletteShifts (void); + +void CenterWindow(word w,word h); +void InitActorList (void); +void GetNewActor (void); +void RemoveObj (objtype *gone); +void PollControls (void); +void StopMusic(void); +void StartMusic(void); +void PlayLoop (void); +void StartDamageFlash (int damage); +void StartBonusFlash (void); + +/* +============================================================================= + + WL_INTER + +============================================================================= +*/ + +void IntroScreen (void); +void PreloadGraphics(void); +void LevelCompleted (void); +void CheckHighScore (long score,word other); +void Victory (void); +void ClearSplitVWB (void); + + +/* +============================================================================= + + WL_DEBUG + +============================================================================= +*/ + +int DebugKeys (void); +void PicturePause (void); + + +/* +============================================================================= + + WL_DRAW DEFINITIONS + +============================================================================= +*/ + +extern unsigned screenloc[3]; +extern unsigned freelatch; + +extern long lasttimecount; +extern long frameon; +extern boolean fizzlein; + +extern unsigned wallheight[MAXVIEWWIDTH]; + +extern fixed tileglobal; +extern fixed focallength; +extern fixed mindist; + +// +// math tables +// +extern int pixelangle[MAXVIEWWIDTH]; +extern long far finetangent[FINEANGLES/4]; +extern fixed far sintable[],far *costable; + +// +// derived constants +// +extern fixed scale; +extern long heightnumerator,mindist; + +// +// refresh variables +// +extern fixed viewx,viewy; // the focal point +extern int viewangle; +extern fixed viewsin,viewcos; + +extern long postsource; +extern unsigned postx; +extern unsigned postwidth; + + +extern int horizwall[],vertwall[]; + +extern unsigned pwallpos; + + +fixed FixedByFrac (fixed a, fixed b); +void TransformActor (objtype *ob); +void BuildTables (void); +void ClearScreen (void); +int CalcRotate (objtype *ob); +void DrawScaleds (void); +void CalcTics (void); +void FixOfs (void); +void ThreeDRefresh (void); +void FarScalePost (void); + +/* +============================================================================= + + WL_STATE DEFINITIONS + +============================================================================= +*/ +#define TURNTICS 10 +#define SPDPATROL 512 +#define SPDDOG 1500 + + +extern dirtype opposite[9]; +extern dirtype diagonal[9][9]; + + +void InitHitRect (objtype *ob, unsigned radius); +void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state); +void NewState (objtype *ob, statetype *state); + +boolean TryWalk (objtype *ob); +void SelectChaseDir (objtype *ob); +void SelectDodgeDir (objtype *ob); +void SelectRunDir (objtype *ob); +void MoveObj (objtype *ob, long move); +boolean SightPlayer (objtype *ob); + +void KillActor (objtype *ob); +void DamageActor (objtype *ob, unsigned damage); + +boolean CheckLine (objtype *ob); +boolean CheckSight (objtype *ob); + + +/* +============================================================================= + + WL_SCALE DEFINITIONS + +============================================================================= +*/ + + +#define COMPSCALECODESTART (65*4) // offset to start of code in comp scaler + +typedef struct +{ + unsigned codeofs[65]; + unsigned width[65]; + byte code[]; +} t_compscale; + +typedef struct +{ + unsigned leftpix,rightpix; + unsigned dataofs[64]; +// table data after dataofs[rightpix-leftpix+1] +} t_compshape; + + +extern t_compscale _seg *scaledirectory[MAXSCALEHEIGHT+1]; +extern long fullscalefarcall[MAXSCALEHEIGHT+1]; + +extern byte bitmasks1[8][8]; +extern byte bitmasks2[8][8]; +extern unsigned wordmasks[8][8]; + +extern byte mapmasks1[4][8]; +extern byte mapmasks2[4][8]; +extern byte mapmasks3[4][8]; + +extern int maxscale,maxscaleshl2; + +extern boolean insetupscaling; + +void SetupScaling (int maxscaleheight); +void ScaleShape (int xcenter, int shapenum, unsigned height); +void SimpleScaleShape (int xcenter, int shapenum, unsigned height); + +/* +============================================================================= + + WL_AGENT DEFINITIONS + +============================================================================= +*/ + +// +// player state info +// +extern boolean running; +extern long thrustspeed; +extern unsigned plux,pluy; // player coordinates scaled to unsigned + +extern int anglefrac; +extern int facecount; + +void SpawnPlayer (int tilex, int tiley, int dir); +void DrawFace (void); +void DrawHealth (void); +void TakeDamage (int points,objtype *attacker); +void HealSelf (int points); +void DrawLevel (void); +void DrawLives (void); +void GiveExtraMan (void); +void DrawScore (void); +void GivePoints (long points); +void DrawWeapon (void); +void DrawKeys (void); +void GiveWeapon (int weapon); +void DrawAmmo (void); +void GiveAmmo (int ammo); +void GiveKey (int key); +void GetBonus (statobj_t *check); + +void Thrust (int angle, long speed); + +/* +============================================================================= + + WL_ACT1 DEFINITIONS + +============================================================================= +*/ + +extern doorobj_t doorobjlist[MAXDOORS],*lastdoorobj; +extern int doornum; + +extern unsigned doorposition[MAXDOORS],pwallstate; + +extern byte far areaconnect[NUMAREAS][NUMAREAS]; + +extern boolean areabyplayer[NUMAREAS]; + +extern unsigned pwallstate; +extern unsigned pwallpos; // amount a pushable wall has been moved (0-63) +extern unsigned pwallx,pwally; +extern int pwalldir; + + +void InitDoorList (void); +void InitStaticList (void); +void SpawnStatic (int tilex, int tiley, int type); +void SpawnDoor (int tilex, int tiley, boolean vertical, int lock); +void MoveDoors (void); +void MovePWalls (void); +void OpenDoor (int door); +void PlaceItemType (int itemtype, int tilex, int tiley); +void PushWall (int checkx, int checky, int dir); +void OperateDoor (int door); +void InitAreas (void); + +/* +============================================================================= + + WL_ACT2 DEFINITIONS + +============================================================================= +*/ + +#define s_nakedbody s_static10 + +extern statetype s_grddie1; +extern statetype s_dogdie1; +extern statetype s_ofcdie1; +extern statetype s_mutdie1; +extern statetype s_ssdie1; +extern statetype s_bossdie1; +extern statetype s_schabbdie1; +extern statetype s_fakedie1; +extern statetype s_mechadie1; +extern statetype s_hitlerdie1; +extern statetype s_greteldie1; +extern statetype s_giftdie1; +extern statetype s_fatdie1; + +extern statetype s_spectredie1; +extern statetype s_angeldie1; +extern statetype s_transdie0; +extern statetype s_uberdie0; +extern statetype s_willdie1; +extern statetype s_deathdie1; + + +extern statetype s_grdchase1; +extern statetype s_dogchase1; +extern statetype s_ofcchase1; +extern statetype s_sschase1; +extern statetype s_mutchase1; +extern statetype s_bosschase1; +extern statetype s_schabbchase1; +extern statetype s_fakechase1; +extern statetype s_mechachase1; +extern statetype s_gretelchase1; +extern statetype s_giftchase1; +extern statetype s_fatchase1; + +extern statetype s_spectrechase1; +extern statetype s_angelchase1; +extern statetype s_transchase1; +extern statetype s_uberchase1; +extern statetype s_willchase1; +extern statetype s_deathchase1; + +extern statetype s_blinkychase1; +extern statetype s_hitlerchase1; + +extern statetype s_grdpain; +extern statetype s_grdpain1; +extern statetype s_ofcpain; +extern statetype s_ofcpain1; +extern statetype s_sspain; +extern statetype s_sspain1; +extern statetype s_mutpain; +extern statetype s_mutpain1; + +extern statetype s_deathcam; + +extern statetype s_schabbdeathcam2; +extern statetype s_hitlerdeathcam2; +extern statetype s_giftdeathcam2; +extern statetype s_fatdeathcam2; + +void SpawnStand (enemy_t which, int tilex, int tiley, int dir); +void SpawnPatrol (enemy_t which, int tilex, int tiley, int dir); +void KillActor (objtype *ob); + +void US_ControlPanel(byte); + +void SpawnDeadGuard (int tilex, int tiley); +void SpawnBoss (int tilex, int tiley); +void SpawnGretel (int tilex, int tiley); +void SpawnTrans (int tilex, int tiley); +void SpawnUber (int tilex, int tiley); +void SpawnWill (int tilex, int tiley); +void SpawnDeath (int tilex, int tiley); +void SpawnAngel (int tilex, int tiley); +void SpawnSpectre (int tilex, int tiley); +void SpawnGhosts (int which, int tilex, int tiley); +void SpawnSchabbs (int tilex, int tiley); +void SpawnGift (int tilex, int tiley); +void SpawnFat (int tilex, int tiley); +void SpawnFakeHitler (int tilex, int tiley); +void SpawnHitler (int tilex, int tiley); + +/* +============================================================================= + + WL_TEXT DEFINITIONS + +============================================================================= +*/ + +extern char helpfilename[],endfilename[]; + +extern void HelpScreens(void); +extern void EndText(void); diff --git a/src/lib/hb/wl_dr_a.asm b/src/lib/hb/wl_dr_a.asm new file mode 100755 index 00000000..aec139a1 --- /dev/null +++ b/src/lib/hb/wl_dr_a.asm @@ -0,0 +1,795 @@ + IDEAL + MODEL MEDIUM,C + P286 + +SCREENSEG = 0a000h + +FINEANGLES = 3600 +DEG90 = 900 +DEG180 = 1800 +DEG270 = 2700 +DEG360 = 3600 + +OP_JLE = 07eh +OP_JGE = 07dh + +EXTRN finetangent:DWORD ; far array, starts at offset 0 + +EXTRN HitHorizWall:FAR +EXTRN HitVertWall:FAR +EXTRN HitHorizDoor:FAR +EXTRN HitVertDoor:FAR +EXTRN HitHorizPWall:FAR +EXTRN HitVertPWall:FAR + + +DATASEG + +EXTRN viewwidth:WORD + +EXTRN tilemap:BYTE +EXTRN spotvis:BYTE +EXTRN pixelangle:WORD + + +EXTRN midangle:WORD +EXTRN angle:WORD + +EXTRN focaltx:WORD +EXTRN focalty:WORD +EXTRN viewtx:WORD +EXTRN viewty:WORD +EXTRN viewx:DWORD +EXTRN viewy:DWORD + +EXTRN xpartialup:WORD +EXTRN ypartialup:WORD +EXTRN xpartialdown:WORD +EXTRN ypartialdown:WORD + +EXTRN tilehit:WORD +EXTRN pixx:WORD +EXTRN wallheight:WORD ; array of VIEWWIDTH entries + +EXTRN xtile:WORD +EXTRN ytile:WORD +EXTRN xtilestep:WORD +EXTRN ytilestep:WORD +EXTRN xintercept:DWORD +EXTRN yintercept:DWORD +EXTRN xstep:DWORD +EXTRN ystep:DWORD + +EXTRN doorposition:WORD ; table of door position values + + +EXTRN pwallpos:WORD ; amound a pushable wall has been moved + +CODESEG + +;------------------- +; +; xpartialbyystep +; +; multiplies long [ystep] (possibly negative), by word [xpartial] (in BX) +; +; returns dx:ax +; trashes bx,cx,di +; +;------------------- + +PROC xpartialbyystep NEAR +; +; setup +; + mov ax,[WORD ystep] + mov cx,[WORD ystep+2] + or cx,cx ; is ystep negatice? + jns @@multpos +; +; multiply negative cx:ax by bx +; + neg cx + neg ax + sbb cx,0 + + mul bx ; fraction*fraction + mov di,dx ; di is low word of result + mov ax,cx ; + mul bx ; units*fraction + add ax,di + adc dx,0 + + neg dx + neg ax + sbb dx,0 + ret +; +; multiply positive cx:ax by bx +; +EVEN +@@multpos: + mul bx ; fraction*fraction + mov di,dx ; di is low word of result + mov ax,cx ; + mul bx ; units*fraction + add ax,di + adc dx,0 + + ret + +ENDP + + + +;------------------- +; +; ypartialbyxstep +; +; multiplies long [xstep] (possibly negative), by word [ypartial] (in BP) +; +; returns dx:ax +; trashes cx,di,bp +; +;------------------- + +PROC ypartialbyxstep NEAR +; +; setup +; + mov ax,[WORD xstep] + mov cx,[WORD xstep+2] + or cx,cx ; is ystep negatice? + jns @@multpos +; +; multiply negative cx:ax by bx +; + neg cx + neg ax + sbb cx,0 + + mul bp ; fraction*fraction + mov di,dx ; di is low word of result + mov ax,cx ; + mul bp ; units*fraction + add ax,di + adc dx,0 + + neg dx + neg ax + sbb dx,0 + ret +; +; multiply positive cx:ax by bx +; +EVEN +@@multpos: + mul bp ; fraction*fraction + mov di,dx ; di is low word of result + mov ax,cx ; + mul bp ; units*fraction + add ax,di + adc dx,0 + ret + +ENDP + + +;============================ +; +; AsmRefresh +; +; +;============================ + +PROC AsmRefresh +PUBLIC AsmRefresh + + push si + push di + push bp + + mov [pixx],0 +;--------------------------------------------------------------------------- +; +; Setup to trace a ray through pixx view pixel +; +; CX : angle of the ray through pixx +; ES : points to segment of finetangent array for this block of code +; +; Upon entrance to initialize block +; +; BX : xpartial +; BP : ypartial +; +;--------------------------------------------------------------------------- + EVEN +pixxloop: + mov ax,SEG finetangent + mov es,ax + mov cx,[midangle] ; center of view area + mov bx,[pixx] + shl bx,1 + add cx,[pixelangle+bx] ; delta for this pixel + cmp cx,0 + jge not0 +;---------- +; +; -90 - -1 degree arc +; +;---------- + add cx,FINEANGLES ; -90 is the same as 270 + jmp entry360 + +not0: + cmp cx,DEG90 + jge not90 +;---------- +; +; 0-89 degree arc +; +;---------- +entry90: + mov [xtilestep],1 ; xtilestep = 1 + mov [ytilestep],-1 ; ytilestep = -1 + mov [BYTE cs:horizop],OP_JGE ; patch a jge in + mov [BYTE cs:vertop],OP_JLE ; patch a jle in + mov bx,DEG90-1 + sub bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx] + mov dx,[es:bx+2] + mov [WORD xstep],ax + mov [WORD xstep+2],dx ; xstep = finetangent[DEG90-1-angle] + mov bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx] + mov dx,[es:bx+2] + neg dx + neg ax + sbb dx,0 + mov [WORD ystep],ax + mov [WORD ystep+2],dx ; ystep = -finetangent[angle] + + mov bx,[xpartialup] ; xpartial = xpartialup + mov bp,[ypartialdown] ; ypartial = ypartialdown + jmp initvars + +not90: + cmp cx,DEG180 + jge not180 +;---------- +; +; 90-179 degree arc +; +;---------- + mov ax,-1 + mov [xtilestep],ax ; xtilestep = -1 + mov [ytilestep],ax ; ytilestep = -1 + mov [BYTE cs:horizop],OP_JLE ; patch a jle in + mov [BYTE cs:vertop],OP_JLE ; patch a jle in + + mov bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx-DEG90*4] + mov dx,[es:bx+2-DEG90*4] + neg dx + neg ax + sbb dx,0 + mov [WORD xstep],ax + mov [WORD xstep+2],dx ; xstep = -finetangent[angle-DEG90] + mov bx,DEG180-1 + sub bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx] + mov dx,[es:bx+2] + neg dx + neg ax + sbb dx,0 + mov [WORD ystep],ax + mov [WORD ystep+2],dx ; ystep = -finetangent[DEG180-1-angle] + + mov bx,[xpartialdown] ; xpartial = xpartialdown + mov bp,[ypartialdown] ; ypartial = ypartialdown + jmp initvars + +not180: + cmp cx,DEG270 + jge not270 +;---------- +; +; 180-269 degree arc +; +;---------- + mov [xtilestep],-1 ; xtilestep = -1 + mov [ytilestep],1 ; ytilestep = 1 + mov [BYTE cs:horizop],OP_JLE ; patch a jle in + mov [BYTE cs:vertop],OP_JGE ; patch a jge in + + mov bx,DEG270-1 + sub bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx] + mov dx,[es:bx+2] + neg dx + neg ax + sbb dx,0 + mov [WORD xstep],ax + mov [WORD xstep+2],dx ; xstep = -finetangent[DEG270-1-angle] + mov bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx-DEG180*4] + mov dx,[es:bx+2-DEG180*4] + mov [WORD ystep],ax + mov [WORD ystep+2],dx ; ystep = finetangent[angle-DEG180] + + mov bx,[xpartialdown] ; xpartial = xpartialdown + mov bp,[ypartialup] ; ypartial = ypartialup + jmp initvars + + +not270: + cmp cx,DEG360 + jge not360 +;---------- +; +; 270-359 degree arc +; +;---------- +entry360: + mov ax,1 + mov [xtilestep],ax ; xtilestep = 1 + mov [ytilestep],ax ; ytilestep = 1 + mov [BYTE cs:horizop],OP_JGE ; patch a jge in + mov [BYTE cs:vertop],OP_JGE ; patch a jge in + + mov bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx-DEG270*4] + mov dx,[es:bx+2-DEG270*4] + mov [WORD xstep],ax + mov [WORD xstep+2],dx ; xstep = finetangent[angle-DEG270] + mov bx,DEG360-1 + sub bx,cx + ;begin 8086 hack + ;shl bx,2 + shl bx,1 + shl bx,1 + ;end 8086 hack + mov ax,[es:bx] + mov dx,[es:bx+2] + mov [WORD ystep],ax + mov [WORD ystep+2],dx ; ystep = finetangent[DEG360-1-angle] + + mov bx,[xpartialup] ; xpartial = xpartialup + mov bp,[ypartialup] ; ypartial = ypartialup + jmp initvars + + +not360: +;---------- +; +; 360-449 degree arc +; +;---------- + sub cx,FINEANGLES ; -449 is the same as 89 + jmp entry90 + +;--------------------------------------------------------------------------- +; +; initialise variables for intersection testing +; +;--------------------------------------------------------------------------- +initvars: + call NEAR xpartialbyystep ; xpartial is in BX + add ax,[WORD viewy] + adc dx,[WORD viewy+2] + mov [WORD yintercept],ax + mov [WORD yintercept+2],dx + + mov si,[focaltx] + add si,[xtilestep] + mov [xtile],si ; xtile = focaltx+xtilestep + ;begin 8086 hack + ;shl si,6 + push cx + mov cl,6 + shl si,cl + pop cx + ;end 8086 hack + add si,dx ; xspot = (xtile<<6) + yinttile + + + call NEAR ypartialbyxstep ; ypartial is in BP + add ax,[WORD viewx] + adc dx,[WORD viewx+2] + mov [WORD xintercept],ax + mov cx,dx + + mov bx,[focalty] + add bx,[ytilestep] + mov bp,bx ; ytile = focalty+ytilestep + mov di,dx + ;begin 8086 hack + ;shl di,6 + push cx + mov cl,6 + shl di,cl + pop cx + ;end 8086 hack + add di,bx ; yspot = (xinttile<<6) + ytile + + mov bx,[xtile] + mov dx,[WORD yintercept+2] + mov ax,SCREENSEG + mov es,ax ; faster than mov es,[screenseg] + + +;--------------------------------------------------------------------------- +; +; trace along this angle until we hit a wall +; +; CORE LOOP! +; +; All variables are killed when a wall is hit +; +; AX : scratch +; BX : xtile +; CX : high word of xintercept +; DX : high word of yintercept +; SI : xspot (yinttile<<6)+xtile (index into tilemap and spotvis) +; DI : yspot (xinttile<<6)+ytile (index into tilemap and spotvis) +; BP : ytile +; ES : screenseg +; +;--------------------------------------------------------------------------- + +;----------- +; +; check intersections with vertical walls +; +;----------- + + EVEN +vertcheck: + cmp dx,bp +vertop: ; 0x7e = jle (ytilestep==-1) + jle horizentry ; 0x7d = jge (ytilestep==1) +vertentry: + test [BYTE tilemap+si],0ffh ; tilehit = *((byte *)tilemap+xspot); + jnz hitvert +passvert: + mov [BYTE spotvis+si],1 ; *((byte *)spotvis+xspot) = true; + add bx,[xtilestep] ; xtile+=xtilestep + mov ax,[WORD ystep] + add [WORD yintercept],ax ; yintercept += ystep + adc dx,[WORD ystep+2] + mov si,bx + ;begin 8086 hack + ;shl si,6 + push cx + mov cl,6 + shl si,cl + pop cx + ;end 8086 hack + add si,dx ; xspot = (xtile<<6)+yinttile + jmp vertcheck + + EVEN +hitvert: + mov al,[BYTE tilemap+si] ; tilehit = *((byte *)tilemap+xspot); + mov [BYTE tilehit],al + or al,al ; set flags + jns notvertdoor + jmp vertdoor +notvertdoor: + mov [WORD xintercept],0 + mov [WORD xintercept+2],bx + mov [xtile],bx + mov [WORD yintercept+2],dx + mov [ytile],dx + call FAR HitVertWall + jmp nextpix + + +;----------- +; +; check intersections with horizontal walls +; +;----------- + EVEN +horizcheck: + cmp cx,bx +horizop: ; 0x7e = jle (xtilestep==-1) + jle vertentry ; 0x7d = jge (xtilestep==1) +horizentry: + test [BYTE tilemap+di],0ffh ; tilehit = *((byte *)tilemap+yspot); + jnz hithoriz +passhoriz: + mov [BYTE spotvis+di],1 ; *((byte *)spotvis+yspot) = true; + add bp,[ytilestep] ; ytile+=ytilestep + mov ax,[WORD xstep] + add [WORD xintercept],ax ; xintercept += xstep + adc cx,[WORD xstep+2] + mov di,cx + ;begin 8086 hack + ;shl di,6 + push cx + mov cl,6 + shl di,cl + pop cx + ;end 8086 hack + add di,bp ; yspot = (xinttile<<6)+ytile + jmp horizcheck + + EVEN +hithoriz: + mov al,[BYTE tilemap+di] ; tilehit = *((byte *)tilemap+yspot); + mov [BYTE tilehit],al + or al,al ; set flags + js horizdoor + mov [WORD xintercept+2],cx + mov [xtile],cx + mov [WORD yintercept],0 + mov [WORD yintercept+2],bp + mov [ytile],bp + call FAR HitHorizWall + jmp nextpix + +;--------------------------------------------------------------------------- +; +; next pixel over +; +;--------------------------------------------------------------------------- + +nextpix: + mov ax,[pixx] + inc ax + mov [pixx],ax + cmp ax,[viewwidth] + jge done + jmp pixxloop +done: + pop bp + pop di + pop si + retf + +;=========================================================================== + +;============= +; +; hit a special horizontal wall, so find which coordinate a door would be +; intersected at, and check to see if the door is open past that point +; +;============= +horizdoor: + mov [xtile],bx ; save off live register variables + mov [WORD yintercept+2],dx + + test al,040h ; both high bits set == pushable wall + jnz horizpushwall + + mov bx,ax + and bx,7fh ; strip high bit + shl bx,1 ; index into word width door table + + mov ax,[WORD xstep] + mov dx,[WORD xstep+2] + sar dx,1 + rcr ax,1 ; half a step gets to door position + + add ax,[WORD xintercept] ; add half step to current intercept pos + adc dx,cx ; CX hold high word of xintercept + + cmp cx,dx ; is it still in the same tile? + je hithmid +; +; midpoint is outside tile, so it hit the side of the wall before a door +; +continuehoriz: + mov bx,[xtile] ; reload register variables + mov dx,[WORD yintercept+2] + jmp passhoriz ; continue tracing +; +; the trace hit the door plane at pixel position AX, see if the door is +; closed that much +; +hithmid: + cmp ax,[doorposition+bx] ; position of leading edge of door + jb continuehoriz +; +; draw the door +; + mov [WORD xintercept],ax ; save pixel intercept position + mov [WORD xintercept+2],cx + + mov [WORD yintercept],8000h ; intercept in middle of tile + mov [WORD yintercept+2],bp + + call FAR HitHorizDoor + jmp nextpix + +;============ +; +; hit a sliding horizontal wall +; +;============ + +horizpushwall: + mov ax,[WORD xstep+2] ; multiply xstep by pwallmove (0-63) + mul [pwallpos] + mov bx,ax + mov ax,[WORD xstep] + mul [pwallpos] + add dx,bx + + sar dx,1 ; then divide by 64 to accomplish a + rcr ax,1 ; fixed point multiplication + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + + add ax,[WORD xintercept] ; add partial step to current intercept + adc dx,cx ; CX hold high word of xintercept + + cmp cx,dx ; is it still in the same tile? + jne continuehoriz ; no, it hit the side + +; +; draw the pushable wall at the new height +; + mov [WORD xintercept],ax ; save pixel intercept position + mov [WORD xintercept+2],dx + + mov [WORD yintercept+2],bp + mov [WORD yintercept],0 + + call FAR HitHorizPWall + jmp nextpix + + + +;=========================================================================== + +;============= +; +; hit a special vertical wall, so find which coordinate a door would be +; intersected at, and check to see if the door is open past that point +; +;============= +vertdoor: + mov [xtile],bx ; save off live register variables + mov [WORD yintercept+2],dx + + test al,040h ; both high bits set == pushable wall + jnz vertpushwall + + mov bx,ax + and bx,7fh ; strip high bit + shl bx,1 ; index into word width doorposition + + mov ax,[WORD ystep] + mov dx,[WORD ystep+2] + sar dx,1 + rcr ax,1 ; half a step gets to door position + + add ax,[WORD yintercept] ; add half step to current intercept pos + adc dx,[WORD yintercept+2] + + cmp [WORD yintercept+2],dx ; is it still in the same tile? + je hitvmid +; +; midpoint is outside tile, so it hit the side of the wall before a door +; +continuevert: + mov bx,[xtile] ; reload register variables + mov dx,[WORD yintercept+2] + jmp passvert ; continue tracing +; +; the trace hit the door plane at pixel position AX, see if the door is +; closed that much +; +hitvmid: + cmp ax,[doorposition+bx] ; position of leading edge of door + jb continuevert +; +; draw the door +; + mov [WORD yintercept],ax ; save pixel intercept position + mov [WORD xintercept],8000h ; intercept in middle of tile + mov ax,[xtile] + mov [WORD xintercept+2],ax + + call FAR HitVertDoor + jmp nextpix + +;============ +; +; hit a sliding vertical wall +; +;============ + +vertpushwall: + mov ax,[WORD ystep+2] ; multiply ystep by pwallmove (0-63) + mul [pwallpos] + mov bx,ax + mov ax,[WORD ystep] + mul [pwallpos] + add dx,bx + + sar dx,1 ; then divide by 64 to accomplish a + rcr ax,1 ; fixed point multiplication + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + sar dx,1 + rcr ax,1 + + add ax,[WORD yintercept] ; add partial step to current intercept + adc dx,[WORD yintercept+2] + + cmp [WORD yintercept+2],dx ; is it still in the same tile? + jne continuevert ; no, it hit the side + +; +; draw the pushable wall at the new height +; + mov [WORD yintercept],ax ; save pixel intercept position + mov [WORD yintercept+2],dx + + mov bx,[xtile] + mov [WORD xintercept+2],bx + mov [WORD xintercept],0 + + call FAR HitVertPWall + jmp nextpix + + + +ENDP + + +END + + diff --git a/src/lib/hb/wl_draw.c b/src/lib/hb/wl_draw.c new file mode 100755 index 00000000..3c32e39e --- /dev/null +++ b/src/lib/hb/wl_draw.c @@ -0,0 +1,1419 @@ +// WL_DRAW.C + +#include "WL_DEF.H" +#include +#pragma hdrstop + +//#define DEBUGWALLS +//#define DEBUGTICS + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +// the door is the last picture before the sprites +#define DOORWALL (PMSpriteStart-8) + +#define ACTORSIZE 0x4000 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +#ifdef DEBUGWALLS +unsigned screenloc[3]= {0,0,0}; +#else +unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START}; +#endif +unsigned freelatch = FREESTART; + +long lasttimecount; +long frameon; + +unsigned wallheight[MAXVIEWWIDTH]; + +fixed tileglobal = TILEGLOBAL; +fixed mindist = MINDIST; + + +// +// math tables +// +int pixelangle[MAXVIEWWIDTH]; +long far finetangent[FINEANGLES/4]; +fixed far sintable[ANGLES+ANGLES/4],far *costable = sintable+(ANGLES/4); + +// +// refresh variables +// +fixed viewx,viewy; // the focal point +int viewangle; +fixed viewsin,viewcos; + + + +fixed FixedByFrac (fixed a, fixed b); +void TransformActor (objtype *ob); +void BuildTables (void); +void ClearScreen (void); +int CalcRotate (objtype *ob); +void DrawScaleds (void); +void CalcTics (void); +void FixOfs (void); +void ThreeDRefresh (void); + + + +// +// wall optimization variables +// +int lastside; // true for vertical +long lastintercept; +int lasttilehit; + + +// +// ray tracing variables +// +int focaltx,focalty,viewtx,viewty; + +int midangle,angle; +unsigned xpartial,ypartial; +unsigned xpartialup,xpartialdown,ypartialup,ypartialdown; +unsigned xinttile,yinttile; + +unsigned tilehit; +unsigned pixx; + +int xtile,ytile; +int xtilestep,ytilestep; +long xintercept,yintercept; +long xstep,ystep; + +int horizwall[MAXWALLTILES],vertwall[MAXWALLTILES]; + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +void AsmRefresh (void); // in WL_DR_A.ASM + +/* +============================================================================ + + 3 - D DEFINITIONS + +============================================================================ +*/ + + +//========================================================================== + + +/* +======================== += += FixedByFrac += += multiply a 16/16 bit, 2's complement fixed point number by a 16 bit += fraction, passed as a signed magnitude 32 bit number += +======================== +*/ + +#pragma warn -rvl // I stick the return value in with ASMs + +fixed FixedByFrac (fixed a, fixed b) +{ +// +// setup +// +asm mov si,[WORD PTR b+2] // sign of result = sign of fraction + +asm mov ax,[WORD PTR a] +asm mov cx,[WORD PTR a+2] + +asm or cx,cx +asm jns aok: // negative? +asm neg cx +asm neg ax +asm sbb cx,0 +asm xor si,0x8000 // toggle sign of result +aok: + +// +// multiply cx:ax by bx +// +asm mov bx,[WORD PTR b] +asm mul bx // fraction*fraction +asm mov di,dx // di is low word of result +asm mov ax,cx // +asm mul bx // units*fraction +asm add ax,di +asm adc dx,0 + +// +// put result dx:ax in 2's complement +// +asm test si,0x8000 // is the result negative? +asm jz ansok: +asm neg dx +asm neg ax +asm sbb dx,0 + +ansok:; + +} + +#pragma warn +rvl + +//========================================================================== + +/* +======================== += += TransformActor += += Takes paramaters: += gx,gy : globalx/globaly of point += += globals: += viewx,viewy : point of view += viewcos,viewsin : sin/cos of viewangle += scale : conversion from global value to screen value += += sets: += screenx,transx,transy,screenheight: projected edge location and size += +======================== +*/ + + +// +// transform actor +// +void TransformActor (objtype *ob) +{ + int ratio; + fixed gx,gy,gxt,gyt,nx,ny; + long temp; + +// +// translate point to view centered coordinates +// + gx = ob->x-viewx; + gy = ob->y-viewy; + +// +// calculate newx +// + gxt = FixedByFrac(gx,viewcos); + gyt = FixedByFrac(gy,viewsin); + nx = gxt-gyt-ACTORSIZE; // fudge the shape forward a bit, because + // the midpoint could put parts of the shape + // into an adjacent wall + +// +// calculate newy +// + gxt = FixedByFrac(gx,viewsin); + gyt = FixedByFrac(gy,viewcos); + ny = gyt+gxt; + +// +// calculate perspective ratio +// + ob->transx = nx; + ob->transy = ny; + + if (nxviewheight = 0; + return; + } + + ob->viewx = centerx + ny*scale/nx; // DEBUG: use assembly divide + +// +// calculate height (heightnumerator/(nx>>8)) +// + asm mov ax,[WORD PTR heightnumerator] + asm mov dx,[WORD PTR heightnumerator+2] + asm idiv [WORD PTR nx+1] // nx>>8 + asm mov [WORD PTR temp],ax + asm mov [WORD PTR temp+2],dx + + ob->viewheight = temp; +} + +//========================================================================== + +/* +======================== += += TransformTile += += Takes paramaters: += tx,ty : tile the object is centered in += += globals: += viewx,viewy : point of view += viewcos,viewsin : sin/cos of viewangle += scale : conversion from global value to screen value += += sets: += screenx,transx,transy,screenheight: projected edge location and size += += Returns true if the tile is withing getting distance += +======================== +*/ + +boolean TransformTile (int tx, int ty, int *dispx, int *dispheight) +{ + int ratio; + fixed gx,gy,gxt,gyt,nx,ny; + long temp; + +// +// translate point to view centered coordinates +// + gx = ((long)tx<>8)) +// + asm mov ax,[WORD PTR heightnumerator] + asm mov dx,[WORD PTR heightnumerator+2] + asm idiv [WORD PTR nx+1] // nx>>8 + asm mov [WORD PTR temp],ax + asm mov [WORD PTR temp+2],dx + + *dispheight = temp; + +// +// see if it should be grabbed +// + if (nx-TILEGLOBAL/2 && ny>8)) + // + if (nx>8 +} + + +//========================================================================== + +/* +=================== += += ScalePost += +=================== +*/ + +long postsource; +unsigned postx; +unsigned postwidth; + +void near ScalePost (void) // VGA version +{ + asm mov ax,SCREENSEG + asm mov es,ax + + asm mov bx,[postx] + asm shl bx,1 + asm mov bp,WORD PTR [wallheight+bx] // fractional height (low 3 bits frac) + asm and bp,0xfff8 // bp = heightscaler*4 + asm shr bp,1 + asm cmp bp,[maxscaleshl2] + asm jle heightok + asm mov bp,[maxscaleshl2] +heightok: + asm add bp,OFFSET fullscalefarcall + // + // scale a byte wide strip of wall + // + asm mov bx,[postx] + asm mov di,bx + asm shr di,1 // X in bytes + asm shr di,1 + asm add di,[bufferofs] + + asm and bx,3 + /* begin 8086 hack + asm shl bx,3 + */ + asm push cx + asm mov cl,3 + asm shl bx,cl + asm pop cx + /* end 8086 hack */ + asm add bx,[postwidth] + + asm mov al,BYTE PTR [mapmasks1-1+bx] // -1 because no widths of 0 + asm mov dx,SC_INDEX+1 + asm out dx,al // set bit mask register + asm lds si,DWORD PTR [postsource] + asm call DWORD PTR [bp] // scale the line of pixels + + asm mov al,BYTE PTR [ss:mapmasks2-1+bx] // -1 because no widths of 0 + asm or al,al + asm jz nomore + + // + // draw a second byte for vertical strips that cross two bytes + // + asm inc di + asm out dx,al // set bit mask register + asm call DWORD PTR [bp] // scale the line of pixels + + asm mov al,BYTE PTR [ss:mapmasks3-1+bx] // -1 because no widths of 0 + asm or al,al + asm jz nomore + // + // draw a third byte for vertical strips that cross three bytes + // + asm inc di + asm out dx,al // set bit mask register + asm call DWORD PTR [bp] // scale the line of pixels + + +nomore: + asm mov ax,ss + asm mov ds,ax +} + +void FarScalePost (void) // just so other files can call +{ + ScalePost (); +} + + +/* +==================== += += HitVertWall += += tilehit bit 7 is 0, because it's not a door tile += if bit 6 is 1 and the adjacent tile is a door tile, use door side pic += +==================== +*/ + +void HitVertWall (void) +{ + int wallpic; + unsigned texture; + + texture = (yintercept>>4)&0xfc0; + if (xtilestep == -1) + { + texture = 0xfc0-texture; + xintercept += TILEGLOBAL; + } + wallheight[pixx] = CalcHeight(); + + if (lastside==1 && lastintercept == xtile && lasttilehit == tilehit) + { + // in the same wall type as last time, so check for optimized draw + if (texture == (unsigned)postsource) + { + // wide scale + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { + ScalePost (); + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + // new wall + if (lastside != -1) // if not the first scaled post + ScalePost (); + + lastside = true; + lastintercept = xtile; + + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + if (tilehit & 0x40) + { // check for adjacent doors + ytile = yintercept>>TILESHIFT; + if ( tilemap[xtile-xtilestep][ytile]&0x80 ) + wallpic = DOORWALL+3; + else + wallpic = vertwall[tilehit & ~0x40]; + } + else + wallpic = vertwall[tilehit]; + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic); + (unsigned)postsource = texture; + + } +} + + +/* +==================== += += HitHorizWall += += tilehit bit 7 is 0, because it's not a door tile += if bit 6 is 1 and the adjacent tile is a door tile, use door side pic += +==================== +*/ + +void HitHorizWall (void) +{ + int wallpic; + unsigned texture; + + texture = (xintercept>>4)&0xfc0; + if (ytilestep == -1) + yintercept += TILEGLOBAL; + else + texture = 0xfc0-texture; + wallheight[pixx] = CalcHeight(); + + if (lastside==0 && lastintercept == ytile && lasttilehit == tilehit) + { + // in the same wall type as last time, so check for optimized draw + if (texture == (unsigned)postsource) + { + // wide scale + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { + ScalePost (); + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + // new wall + if (lastside != -1) // if not the first scaled post + ScalePost (); + + lastside = 0; + lastintercept = ytile; + + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + if (tilehit & 0x40) + { // check for adjacent doors + xtile = xintercept>>TILESHIFT; + if ( tilemap[xtile][ytile-ytilestep]&0x80 ) + wallpic = DOORWALL+2; + else + wallpic = horizwall[tilehit & ~0x40]; + } + else + wallpic = horizwall[tilehit]; + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic); + (unsigned)postsource = texture; + } + +} + +//========================================================================== + +/* +==================== += += HitHorizDoor += +==================== +*/ + +void HitHorizDoor (void) +{ + unsigned texture,doorpage,doornum; + + doornum = tilehit&0x7f; + texture = ( (xintercept-doorposition[doornum]) >> 4) &0xfc0; + + wallheight[pixx] = CalcHeight(); + + if (lasttilehit == tilehit) + { + // in the same door as last time, so check for optimized draw + if (texture == (unsigned)postsource) + { + // wide scale + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { + ScalePost (); + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + if (lastside != -1) // if not the first scaled post + ScalePost (); // draw last post + // first pixel in this door + lastside = 2; + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + switch (doorobjlist[doornum].lock) + { + case dr_normal: + doorpage = DOORWALL; + break; + case dr_lock1: + case dr_lock2: + case dr_lock3: + case dr_lock4: + doorpage = DOORWALL+6; + break; + case dr_elevator: + doorpage = DOORWALL+4; + break; + } + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage); + (unsigned)postsource = texture; + } +} + +//========================================================================== + +/* +==================== += += HitVertDoor += +==================== +*/ + +void HitVertDoor (void) +{ + unsigned texture,doorpage,doornum; + + doornum = tilehit&0x7f; + texture = ( (yintercept-doorposition[doornum]) >> 4) &0xfc0; + + wallheight[pixx] = CalcHeight(); + + if (lasttilehit == tilehit) + { + // in the same door as last time, so check for optimized draw + if (texture == (unsigned)postsource) + { + // wide scale + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { + ScalePost (); + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + if (lastside != -1) // if not the first scaled post + ScalePost (); // draw last post + // first pixel in this door + lastside = 2; + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + switch (doorobjlist[doornum].lock) + { + case dr_normal: + doorpage = DOORWALL; + break; + case dr_lock1: + case dr_lock2: + case dr_lock3: + case dr_lock4: + doorpage = DOORWALL+6; + break; + case dr_elevator: + doorpage = DOORWALL+4; + break; + } + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage+1); + (unsigned)postsource = texture; + } +} + +//========================================================================== + + +/* +==================== += += HitHorizPWall += += A pushable wall in action has been hit += +==================== +*/ + +void HitHorizPWall (void) +{ + int wallpic; + unsigned texture,offset; + + texture = (xintercept>>4)&0xfc0; + offset = pwallpos<<10; + if (ytilestep == -1) + yintercept += TILEGLOBAL-offset; + else + { + texture = 0xfc0-texture; + yintercept += offset; + } + + wallheight[pixx] = CalcHeight(); + + if (lasttilehit == tilehit) + { + // in the same wall type as last time, so check for optimized draw + if (texture == (unsigned)postsource) + { + // wide scale + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { + ScalePost (); + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + // new wall + if (lastside != -1) // if not the first scaled post + ScalePost (); + + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + wallpic = horizwall[tilehit&63]; + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic); + (unsigned)postsource = texture; + } + +} + + +/* +==================== += += HitVertPWall += += A pushable wall in action has been hit += +==================== +*/ + +void HitVertPWall (void) +{ + int wallpic; + unsigned texture,offset; + + texture = (yintercept>>4)&0xfc0; + offset = pwallpos<<10; + if (xtilestep == -1) + { + xintercept += TILEGLOBAL-offset; + texture = 0xfc0-texture; + } + else + xintercept += offset; + + wallheight[pixx] = CalcHeight(); + + if (lasttilehit == tilehit) + { + // in the same wall type as last time, so check for optimized draw + if (texture == (unsigned)postsource) + { + // wide scale + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { + ScalePost (); + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + // new wall + if (lastside != -1) // if not the first scaled post + ScalePost (); + + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + wallpic = vertwall[tilehit&63]; + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(wallpic); + (unsigned)postsource = texture; + } + +} + +//========================================================================== + +//========================================================================== + +#if 0 +/* +===================== += += ClearScreen += +===================== +*/ + +void ClearScreen (void) +{ + unsigned floor=egaFloor[gamestate.episode*10+mapon], + ceiling=egaCeiling[gamestate.episode*10+mapon]; + + // + // clear the screen + // +asm mov dx,GC_INDEX +asm mov ax,GC_MODE + 256*2 // read mode 0, write mode 2 +asm out dx,ax +asm mov ax,GC_BITMASK + 255*256 +asm out dx,ax + +asm mov dx,40 +asm mov ax,[viewwidth] +asm shr ax,1 +asm shr ax,1 +asm shr ax,1 +asm sub dx,ax // dx = 40-viewwidth/8 + +asm mov bx,[viewwidth] +asm shr bx,1 // bl = viewwidth/16 +asm shr bx,1 +asm shr bx,1 +asm shr bx,1 +asm mov bh,BYTE PTR [viewheight] +asm shr bh,1 // half height + +asm mov ax,[ceiling] +asm mov es,[screenseg] +asm mov di,[bufferofs] + +toploop: +asm mov cl,bl +asm rep stosw +asm add di,dx +asm dec bh +asm jnz toploop + +asm mov bh,BYTE PTR [viewheight] +asm shr bh,1 // half height +asm mov ax,[floor] + +bottomloop: +asm mov cl,bl +asm rep stosw +asm add di,dx +asm dec bh +asm jnz bottomloop + + +asm mov dx,GC_INDEX +asm mov ax,GC_MODE + 256*10 // read mode 1, write mode 2 +asm out dx,ax +asm mov al,GC_BITMASK +asm out dx,al + +} +#endif +//========================================================================== + +unsigned vgaCeiling[]= +{ +#ifndef SPEAR + 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0xbfbf, + 0x4e4e,0x4e4e,0x4e4e,0x1d1d,0x8d8d,0x4e4e,0x1d1d,0x2d2d,0x1d1d,0x8d8d, + 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x1d1d,0x2d2d,0xdddd,0x1d1d,0x1d1d,0x9898, + + 0x1d1d,0x9d9d,0x2d2d,0xdddd,0xdddd,0x9d9d,0x2d2d,0x4d4d,0x1d1d,0xdddd, + 0x7d7d,0x1d1d,0x2d2d,0x2d2d,0xdddd,0xd7d7,0x1d1d,0x1d1d,0x1d1d,0x2d2d, + 0x1d1d,0x1d1d,0x1d1d,0x1d1d,0xdddd,0xdddd,0x7d7d,0xdddd,0xdddd,0xdddd +#else + 0x6f6f,0x4f4f,0x1d1d,0xdede,0xdfdf,0x2e2e,0x7f7f,0x9e9e,0xaeae,0x7f7f, + 0x1d1d,0xdede,0xdfdf,0xdede,0xdfdf,0xdede,0xe1e1,0xdcdc,0x2e2e,0x1d1d,0xdcdc +#endif +}; + +/* +===================== += += VGAClearScreen += +===================== +*/ + +void VGAClearScreen (void) +{ + unsigned ceiling=vgaCeiling[gamestate.episode*10+mapon]; + + // + // clear the screen + // +asm mov dx,SC_INDEX +asm mov ax,SC_MAPMASK+15*256 // write through all planes +asm out dx,ax + +asm mov dx,80 +asm mov ax,[viewwidth] +asm shr ax,1 +asm shr ax,1 +asm sub dx,ax // dx = 40-viewwidth/2 + +asm mov bx,[viewwidth] +asm shr bx,1 // bl = viewwidth/8 +asm shr bx,1 +asm shr bx,1 +asm mov bh,BYTE PTR [viewheight] +asm shr bh,1 // half height + +asm mov es,[screenseg] +asm mov di,[bufferofs] +asm mov ax,[ceiling] + +toploop: +asm mov cl,bl +asm rep stosw +asm add di,dx +asm dec bh +asm jnz toploop + +asm mov bh,BYTE PTR [viewheight] +asm shr bh,1 // half height +asm mov ax,0x1919 + +bottomloop: +asm mov cl,bl +asm rep stosw +asm add di,dx +asm dec bh +asm jnz bottomloop +} + +//========================================================================== + +/* +===================== += += CalcRotate += +===================== +*/ + +int CalcRotate (objtype *ob) +{ + int angle,viewangle; + + // this isn't exactly correct, as it should vary by a trig value, + // but it is close enough with only eight rotations + + viewangle = player->angle + (centerx - ob->viewx)/8; + + if (ob->obclass == rocketobj || ob->obclass == hrocketobj) + angle = (viewangle-180)- ob->angle; + else + angle = (viewangle-180)- dirangle[ob->dir]; + + angle+=ANGLES/16; + while (angle>=ANGLES) + angle-=ANGLES; + while (angle<0) + angle+=ANGLES; + + if (ob->state->rotate == 2) // 2 rotation pain frame + return 4*(angle/(ANGLES/2)); // seperated by 3 (art layout...) + + return angle/(ANGLES/8); +} + + +/* +===================== += += DrawScaleds += += Draws all objects that are visable += +===================== +*/ + +#define MAXVISABLE 50 + +typedef struct +{ + int viewx, + viewheight, + shapenum; +} visobj_t; + +visobj_t vislist[MAXVISABLE],*visptr,*visstep,*farthest; + +void DrawScaleds (void) +{ + int i,j,least,numvisable,height; + memptr shape; + byte *tilespot,*visspot; + int shapenum; + unsigned spotloc; + + statobj_t *statptr; + objtype *obj; + + visptr = &vislist[0]; + +// +// place static objects +// + for (statptr = &statobjlist[0] ; statptr !=laststatobj ; statptr++) + { + if ((visptr->shapenum = statptr->shapenum) == -1) + continue; // object has been deleted + + if (!*statptr->visspot) + continue; // not visable + + if (TransformTile (statptr->tilex,statptr->tiley + ,&visptr->viewx,&visptr->viewheight) && statptr->flags & FL_BONUS) + { + GetBonus (statptr); + continue; + } + + if (!visptr->viewheight) + continue; // to close to the object + + if (visptr < &vislist[MAXVISABLE-1]) // don't let it overflow + visptr++; + } + +// +// place active objects +// + for (obj = player->next;obj;obj=obj->next) + { + if (!(visptr->shapenum = obj->state->shapenum)) + continue; // no shape + + spotloc = (obj->tilex<<6)+obj->tiley; // optimize: keep in struct? + visspot = &spotvis[0][0]+spotloc; + tilespot = &tilemap[0][0]+spotloc; + + // + // could be in any of the nine surrounding tiles + // + if (*visspot + || ( *(visspot-1) && !*(tilespot-1) ) + || ( *(visspot+1) && !*(tilespot+1) ) + || ( *(visspot-65) && !*(tilespot-65) ) + || ( *(visspot-64) && !*(tilespot-64) ) + || ( *(visspot-63) && !*(tilespot-63) ) + || ( *(visspot+65) && !*(tilespot+65) ) + || ( *(visspot+64) && !*(tilespot+64) ) + || ( *(visspot+63) && !*(tilespot+63) ) ) + { + obj->active = true; + TransformActor (obj); + if (!obj->viewheight) + continue; // too close or far away + + visptr->viewx = obj->viewx; + visptr->viewheight = obj->viewheight; + if (visptr->shapenum == -1) + visptr->shapenum = obj->temp1; // special shape + + if (obj->state->rotate) + visptr->shapenum += CalcRotate (obj); + + if (visptr < &vislist[MAXVISABLE-1]) // don't let it overflow + visptr++; + obj->flags |= FL_VISABLE; + } + else + obj->flags &= ~FL_VISABLE; + } + +// +// draw from back to front +// + numvisable = visptr-&vislist[0]; + + if (!numvisable) + return; // no visable objects + + for (i = 0; iviewheight; + if (height < least) + { + least = height; + farthest = visstep; + } + } + // + // draw farthest + // + ScaleShape(farthest->viewx,farthest->shapenum,farthest->viewheight); + + farthest->viewheight = 32000; + } + +} + +//========================================================================== + +/* +============== += += DrawPlayerWeapon += += Draw the player's hands += +============== +*/ + +int weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY + ,SPR_MACHINEGUNREADY,SPR_CHAINREADY}; + +void DrawPlayerWeapon (void) +{ + int shapenum; + +#ifndef SPEAR + if (gamestate.victoryflag) + { + if (player->state == &s_deathcam && (TimeCount&32) ) + SimpleScaleShape(viewwidth/2,SPR_DEATHCAM,viewheight+1); + return; + } +#endif + + if (gamestate.weapon != -1) + { + shapenum = weaponscale[gamestate.weapon]+gamestate.weaponframe; + SimpleScaleShape(viewwidth/2,shapenum,viewheight+1); + } + + if (demorecord || demoplayback) + SimpleScaleShape(viewwidth/2,SPR_DEMO,viewheight+1); +} + + +//========================================================================== + + +/* +===================== += += CalcTics += +===================== +*/ + +void CalcTics (void) +{ + long newtime,oldtimecount; + +// +// calculate tics since last refresh for adaptive timing +// + if (lasttimecount > TimeCount) + TimeCount = lasttimecount; // if the game was paused a LONG time + + do + { + newtime = TimeCount; + tics = newtime-lasttimecount; + } while (!tics); // make sure at least one tic passes + + lasttimecount = newtime; + +#ifdef FILEPROFILE + strcpy (scratch,"\tTics:"); + itoa (tics,str,10); + strcat (scratch,str); + strcat (scratch,"\n"); + write (profilehandle,scratch,strlen(scratch)); +#endif + + if (tics>MAXTICS) + { + TimeCount -= (tics-MAXTICS); + tics = MAXTICS; + } +} + + +//========================================================================== + + +/* +======================== += += FixOfs += +======================== +*/ + +void FixOfs (void) +{ + VW_ScreenToScreen (displayofs,bufferofs,viewwidth/8,viewheight); +} + + +//========================================================================== + + +/* +==================== += += WallRefresh += +==================== +*/ + +void WallRefresh (void) +{ +// +// set up variables for this view +// + viewangle = player->angle; + midangle = viewangle*(FINEANGLES/ANGLES); + viewsin = sintable[viewangle]; + viewcos = costable[viewangle]; + viewx = player->x - FixedByFrac(focallength,viewcos); + viewy = player->y + FixedByFrac(focallength,viewsin); + + focaltx = viewx>>TILESHIFT; + focalty = viewy>>TILESHIFT; + + viewtx = player->x >> TILESHIFT; + viewty = player->y >> TILESHIFT; + + xpartialdown = viewx&(TILEGLOBAL-1); + xpartialup = TILEGLOBAL-xpartialdown; + ypartialdown = viewy&(TILEGLOBAL-1); + ypartialup = TILEGLOBAL-ypartialdown; + + lastside = -1; // the first pixel is on a new wall + AsmRefresh (); + ScalePost (); // no more optimization on last post +} + +//========================================================================== + +/* +======================== += += ThreeDRefresh += +======================== +*/ + +void ThreeDRefresh (void) +{ + int tracedir; + +// this wouldn't need to be done except for my debugger/video wierdness + outportb (SC_INDEX,SC_MAPMASK); + +// +// clear out the traced array +// +asm mov ax,ds +asm mov es,ax +asm mov di,OFFSET spotvis +asm xor ax,ax +asm mov cx,2048 // 64*64 / 2 +asm rep stosw + + bufferofs += screenofs; + +// +// follow the walls from there to the right, drawwing as we go +// + VGAClearScreen (); + + WallRefresh (); + +// +// draw all the scaled images +// + DrawScaleds(); // draw scaled stuff + DrawPlayerWeapon (); // draw player's hands + +// +// show screen and time last cycle +// + if (fizzlein) + { + FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,20,false); + fizzlein = false; + + lasttimecount = TimeCount = 0; // don't make a big tic count + + } + + bufferofs -= screenofs; + displayofs = bufferofs; + + asm cli + asm mov cx,[displayofs] + asm mov dx,3d4h // CRTC address register + asm mov al,0ch // start address high register + asm out dx,al + asm inc dx + asm mov al,ch + asm out dx,al // set the high byte + asm sti + + bufferofs += SCREENSIZE; + if (bufferofs > PAGE3START) + bufferofs = PAGE1START; + + frameon++; + PM_NextFrame(); +} + + +//=========================================================================== + diff --git a/src/lib/hb/wl_game.c b/src/lib/hb/wl_game.c new file mode 100755 index 00000000..9f63d9fe --- /dev/null +++ b/src/lib/hb/wl_game.c @@ -0,0 +1,1484 @@ +// WL_GAME.C + +#include "WL_DEF.H" +#pragma hdrstop + +#ifdef MYPROFILE +#include +#endif + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean ingame,fizzlein; +unsigned latchpics[NUMLATCHPICS]; +gametype gamestate; + +long spearx,speary; +unsigned spearangle; +boolean spearflag; + +// +// ELEVATOR BACK MAPS - REMEMBER (-1)!! +// +int ElevatorBackTo[]={1,1,7,3,5,3}; + +void ScanInfoPlane (void); +void SetupGameLevel (void); +void DrawPlayScreen (void); +void LoadLatchMem (void); +void GameLoop (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +//=========================================================================== +//=========================================================================== + + +/* +========================== += += SetSoundLoc - Given the location of an object (in terms of global += coordinates, held in globalsoundx and globalsoundy), munges the values += for an approximate distance from the left and right ear, and puts += those values into leftchannel and rightchannel. += += JAB += +========================== +*/ + + fixed globalsoundx,globalsoundy; + int leftchannel,rightchannel; +#define ATABLEMAX 15 +byte righttable[ATABLEMAX][ATABLEMAX * 2] = { +{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 0, 0, 0, 0, 0, 1, 3, 5, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, 4, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 4, 1, 0, 0, 0, 1, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 6, 5, 4, 2, 1, 0, 1, 2, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 5, 4, 3, 2, 2, 3, 3, 5, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 6, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8} +}; +byte lefttable[ATABLEMAX][ATABLEMAX * 2] = { +{ 8, 8, 8, 8, 8, 8, 8, 8, 5, 3, 1, 0, 0, 0, 0, 0, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 0, 0, 0, 0, 0, 4, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 6, 4, 2, 1, 0, 0, 0, 1, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 3, 2, 1, 0, 1, 2, 4, 5, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 5, 3, 3, 2, 2, 3, 4, 5, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 5, 4, 4, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 6, 6, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8} +}; + +void +SetSoundLoc(fixed gx,fixed gy) +{ + fixed xt,yt; + int x,y; + +// +// translate point to view centered coordinates +// + gx -= viewx; + gy -= viewy; + +// +// calculate newx +// + xt = FixedByFrac(gx,viewcos); + yt = FixedByFrac(gy,viewsin); + x = (xt - yt) >> TILESHIFT; + +// +// calculate newy +// + xt = FixedByFrac(gx,viewsin); + yt = FixedByFrac(gy,viewcos); + y = (yt + xt) >> TILESHIFT; + + if (y >= ATABLEMAX) + y = ATABLEMAX - 1; + else if (y <= -ATABLEMAX) + y = -ATABLEMAX; + if (x < 0) + x = -x; + if (x >= ATABLEMAX) + x = ATABLEMAX - 1; + leftchannel = lefttable[x][y + ATABLEMAX]; + rightchannel = righttable[x][y + ATABLEMAX]; + +#if 0 + CenterWindow(8,1); + US_PrintSigned(leftchannel); + US_Print(","); + US_PrintSigned(rightchannel); + VW_UpdateScreen(); +#endif +} + +/* +========================== += += SetSoundLocGlobal - Sets up globalsoundx & globalsoundy and then calls += UpdateSoundLoc() to transform that into relative channel volumes. Those += values are then passed to the Sound Manager so that they'll be used for += the next sound played (if possible). += += JAB += +========================== +*/ +void PlaySoundLocGlobal(word s,fixed gx,fixed gy) +{ + SetSoundLoc(gx,gy); + SD_PositionSound(leftchannel,rightchannel); + if (SD_PlaySound(s)) + { + globalsoundx = gx; + globalsoundy = gy; + } +} + +void UpdateSoundLoc(void) +{ + if (SoundPositioned) + { + SetSoundLoc(globalsoundx,globalsoundy); + SD_SetPosition(leftchannel,rightchannel); + } +} + +/* +** JAB End +*/ + + +/* +========================== += += ClearMemory += +========================== +*/ + +void ClearMemory (void) +{ + PM_UnlockMainMem(); + SD_StopDigitized(); + MM_SortMem (); +} + + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +void ScanInfoPlane (void) +{ + unsigned x,y,i,j; + int tile; + unsigned far *start; + + start = mapsegs[1]; + for (y=0;ywidth; + mapheight = mapheaderseg[mapon]->height; + + if (mapwidth != 64 || mapheight != 64) + Quit ("Map not 64*64!"); + + +// +// copy the wall data to a data segment array +// + memset (tilemap,0,sizeof(tilemap)); + memset (actorat,0,sizeof(actorat)); + map = mapsegs[0]; + for (y=0;y= 90 && tile <= 101) + { + // door + switch (tile) + { + case 90: + case 92: + case 94: + case 96: + case 98: + case 100: + SpawnDoor (x,y,1,(tile-90)/2); + break; + case 91: + case 93: + case 95: + case 97: + case 99: + case 101: + SpawnDoor (x,y,0,(tile-91)/2); + break; + } + } + } + +// +// spawn actors +// + ScanInfoPlane (); + +// +// take out the ambush markers +// + map = mapsegs[0]; + for (y=0;y= AREATILE) + tile = *map; + if (*(map-1-mapwidth) >= AREATILE) + tile = *(map-1-mapwidth); + if (*(map-1+mapwidth) >= AREATILE) + tile = *(map-1+mapwidth); + if ( *(map-2) >= AREATILE) + tile = *(map-2); + + *(map-1) = tile; + } + } + + + +// +// have the caching manager load and purge stuff to make sure all marks +// are in memory +// + CA_LoadAllSounds (); + +} + + +//========================================================================== + + +/* +=================== += += DrawPlayBorderSides += += To fix window overwrites += +=================== +*/ + +void DrawPlayBorderSides (void) +{ + int xl,yl; + + xl = 160-viewwidth/2; + yl = (200-STATUSLINES-viewheight)/2; + + VWB_Bar (0,0,xl-1,200-STATUSLINES,127); + VWB_Bar (xl+viewwidth+1,0,xl-2,200-STATUSLINES,127); + + VWB_Vlin (yl-1,yl+viewheight,xl-1,0); + VWB_Vlin (yl-1,yl+viewheight,xl+viewwidth,125); +} + + +/* +=================== += += DrawAllPlayBorderSides += +=================== +*/ + +void DrawAllPlayBorderSides (void) +{ + unsigned i,temp; + + temp = bufferofs; + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + DrawPlayBorderSides (); + } + bufferofs = temp; +} + +/* +=================== += += DrawPlayBorder += +=================== +*/ +void DrawAllPlayBorder (void) +{ + unsigned i,temp; + + temp = bufferofs; + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + DrawPlayBorder (); + } + bufferofs = temp; +} + +/* +=================== += += DrawPlayBorder += +=================== +*/ + +void DrawPlayBorder (void) +{ + int xl,yl; + + VWB_Bar (0,0,320,200-STATUSLINES,127); + + xl = 160-viewwidth/2; + yl = (200-STATUSLINES-viewheight)/2; + VWB_Bar (xl,yl,viewwidth,viewheight,0); + + VWB_Hlin (xl-1,xl+viewwidth,yl-1,0); + VWB_Hlin (xl-1,xl+viewwidth,yl+viewheight,125); + VWB_Vlin (yl-1,yl+viewheight,xl-1,0); + VWB_Vlin (yl-1,yl+viewheight,xl+viewwidth,125); + VWB_Plot (xl-1,yl+viewheight,124); +} + + + +/* +=================== += += DrawPlayScreen += +=================== +*/ + +void DrawPlayScreen (void) +{ + int i,j,p,m; + unsigned temp; + + VW_FadeOut (); + + temp = bufferofs; + + CA_CacheGrChunk (STATUSBARPIC); + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + DrawPlayBorder (); + VWB_DrawPic (0,200-STATUSLINES,STATUSBARPIC); + } + + bufferofs = temp; + + UNCACHEGRCHUNK (STATUSBARPIC); + + DrawFace (); + DrawHealth (); + DrawLives (); + DrawLevel (); + DrawAmmo (); + DrawKeys (); + DrawWeapon (); + DrawScore (); +} + + + +//========================================================================== + +/* +================== += += StartDemoRecord += +================== +*/ + +#define MAXDEMOSIZE 8192 + +void StartDemoRecord (int levelnumber) +{ + MM_GetPtr (&demobuffer,MAXDEMOSIZE); + MM_SetLock (&demobuffer,true); + demoptr = (char far *)demobuffer; + lastdemoptr = demoptr+MAXDEMOSIZE; + + *demoptr = levelnumber; + demoptr += 4; // leave space for length + demorecord = true; +} + + +/* +================== += += FinishDemoRecord += +================== +*/ + +char demoname[13] = "DEMO?."; + +void FinishDemoRecord (void) +{ + long length,level; + + demorecord = false; + + length = demoptr - (char far *)demobuffer; + + demoptr = ((char far *)demobuffer)+1; + *(unsigned far *)demoptr = length; + + CenterWindow(24,3); + PrintY+=6; + US_Print(" Demo number (0-9):"); + VW_UpdateScreen(); + + if (US_LineInput (px,py,str,NULL,true,2,0)) + { + level = atoi (str); + if (level>=0 && level<=9) + { + demoname[4] = '0'+level; + CA_WriteFile (demoname,(void far *)demobuffer,length); + } + } + + + MM_FreePtr (&demobuffer); +} + +//========================================================================== + +/* +================== += += RecordDemo += += Fades the screen out, then starts a demo. Exits with the screen faded += +================== +*/ + +void RecordDemo (void) +{ + int level,esc; + + CenterWindow(26,3); + PrintY+=6; + CA_CacheGrChunk(STARTFONT); + fontnumber=0; + US_Print(" Demo which level(1-10):"); + VW_UpdateScreen(); + VW_FadeIn (); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (esc) + return; + + level = atoi (str); + level--; + + SETFONTCOLOR(0,15); + VW_FadeOut (); + +#ifndef SPEAR + NewGame (gd_hard,level/10); + gamestate.mapon = level%10; +#else + NewGame (gd_hard,0); + gamestate.mapon = level; +#endif + + StartDemoRecord (level); + + DrawPlayScreen (); + VW_FadeIn (); + + startgame = false; + demorecord = true; + + SetupGameLevel (); + StartMusic (); + PM_CheckMainMem (); + fizzlein = true; + + PlayLoop (); + + demoplayback = false; + + StopMusic (); + VW_FadeOut (); + ClearMemory (); + + FinishDemoRecord (); +} + +//========================================================================== + +/* +================== += += PlayDemo += += Fades the screen out, then starts a demo. Exits with the screen faded += +================== +*/ + +void PlayDemo (int demonumber) +{ + int length; + +#ifdef DEMOSEXTERN +// debug: load chunk +#ifndef SPEARDEMO + int dems[4]={T_DEMO0,T_DEMO1,T_DEMO2,T_DEMO3}; +#else + int dems[1]={T_DEMO0}; +#endif + + CA_CacheGrChunk(dems[demonumber]); + demoptr = grsegs[dems[demonumber]]; + MM_SetLock (&grsegs[dems[demonumber]],true); +#else + demoname[4] = '0'+demonumber; + CA_LoadFile (demoname,&demobuffer); + MM_SetLock (&demobuffer,true); + demoptr = (char far *)demobuffer; +#endif + + NewGame (1,0); + gamestate.mapon = *demoptr++; + gamestate.difficulty = gd_hard; + length = *((unsigned far *)demoptr)++; + demoptr++; + lastdemoptr = demoptr-4+length; + + VW_FadeOut (); + + SETFONTCOLOR(0,15); + DrawPlayScreen (); + VW_FadeIn (); + + startgame = false; + demoplayback = true; + + SetupGameLevel (); + StartMusic (); + PM_CheckMainMem (); + fizzlein = true; + + PlayLoop (); + +#ifdef DEMOSEXTERN + UNCACHEGRCHUNK(dems[demonumber]); +#else + MM_FreePtr (&demobuffer); +#endif + + demoplayback = false; + + StopMusic (); + VW_FadeOut (); + ClearMemory (); +} + +//========================================================================== + +/* +================== += += Died += +================== +*/ + +#define DEATHROTATE 2 + +void Died (void) +{ + float fangle; + long dx,dy; + int iangle,curangle,clockwise,counter,change; + + gamestate.weapon = -1; // take away weapon + SD_PlaySound (PLAYERDEATHSND); +// +// swing around to face attacker +// + dx = killerobj->x - player->x; + dy = player->y - killerobj->y; + + fangle = atan2(dy,dx); // returns -pi to pi + if (fangle<0) + fangle = M_PI*2+fangle; + + iangle = fangle/(M_PI*2)*ANGLES; + + if (player->angle > iangle) + { + counter = player->angle - iangle; + clockwise = ANGLES-player->angle + iangle; + } + else + { + clockwise = iangle - player->angle; + counter = player->angle + ANGLES-iangle; + } + + curangle = player->angle; + + if (clockwiseiangle) + curangle -= ANGLES; + do + { + change = tics*DEATHROTATE; + if (curangle + change > iangle) + change = iangle-curangle; + + curangle += change; + player->angle += change; + if (player->angle >= ANGLES) + player->angle -= ANGLES; + + ThreeDRefresh (); + CalcTics (); + } while (curangle != iangle); + } + else + { + // + // rotate counterclockwise + // + if (curangleangle += change; + if (player->angle < 0) + player->angle += ANGLES; + + ThreeDRefresh (); + CalcTics (); + } while (curangle != iangle); + } + +// +// fade to red +// + FinishPaletteShifts (); + + bufferofs += screenofs; + VW_Bar (0,0,viewwidth,viewheight,4); + IN_ClearKeysDown (); + FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,70,false); + bufferofs -= screenofs; + IN_UserInput(100); + SD_WaitSoundDone (); + + if (tedlevel == false) // SO'S YA DON'T GET KILLED WHILE LAUNCHING! + gamestate.lives--; + + if (gamestate.lives > -1) + { + gamestate.health = 100; + gamestate.weapon = gamestate.bestweapon + = gamestate.chosenweapon = wp_pistol; + gamestate.ammo = STARTAMMO; + gamestate.keys = 0; + gamestate.attackframe = gamestate.attackcount = + gamestate.weaponframe = 0; + + DrawKeys (); + DrawWeapon (); + DrawAmmo (); + DrawHealth (); + DrawFace (); + DrawLives (); + } + +} + +//========================================================================== + +/* +=================== += += GameLoop += +=================== +*/ + +void GameLoop (void) +{ + int i,xl,yl,xh,yh; + char num[20]; + boolean died; +#ifdef MYPROFILE + clock_t start,end; +#endif + +restartgame: + ClearMemory (); + SETFONTCOLOR(0,15); + DrawPlayScreen (); + died = false; +restart: + do + { + if (!loadedgame) + gamestate.score = gamestate.oldscore; + DrawScore(); + + startgame = false; + if (loadedgame) + loadedgame = false; + else + SetupGameLevel (); + +#ifdef SPEAR + if (gamestate.mapon == 20) // give them the key allways + { + gamestate.keys |= 1; + DrawKeys (); + } +#endif + + ingame = true; + StartMusic (); + PM_CheckMainMem (); + if (!died) + PreloadGraphics (); + else + died = false; + + fizzlein = true; + DrawLevel (); + +startplayloop: + PlayLoop (); + +#ifdef SPEAR + if (spearflag) + { + SD_StopSound(); + SD_PlaySound(GETSPEARSND); + if (DigiMode != sds_Off) + { + long lasttimecount = TimeCount; + + while(TimeCount < lasttimecount+150) + //while(DigiPlaying!=false) + SD_Poll(); + } + else + SD_WaitSoundDone(); + + ClearMemory (); + gamestate.oldscore = gamestate.score; + gamestate.mapon = 20; + SetupGameLevel (); + StartMusic (); + PM_CheckMainMem (); + player->x = spearx; + player->y = speary; + player->angle = spearangle; + spearflag = false; + Thrust (0,0); + goto startplayloop; + } +#endif + + StopMusic (); + ingame = false; + + if (demorecord && playstate != ex_warped) + FinishDemoRecord (); + + if (startgame || loadedgame) + goto restartgame; + + switch (playstate) + { + case ex_completed: + case ex_secretlevel: + gamestate.keys = 0; + DrawKeys (); + VW_FadeOut (); + + ClearMemory (); + + LevelCompleted (); // do the intermission +#ifdef SPEARDEMO + if (gamestate.mapon == 1) + { + died = true; // don't "get psyched!" + + VW_FadeOut (); + + ClearMemory (); + + CheckHighScore (gamestate.score,gamestate.mapon+1); + + #pragma warn -sus + #ifndef JAPAN + _fstrcpy(MainMenu[viewscores].string,STR_VS); + #endif + MainMenu[viewscores].routine = CP_ViewScores; + #pragma warn +sus + + return; + } +#endif + +#ifdef JAPDEMO + if (gamestate.mapon == 3) + { + died = true; // don't "get psyched!" + + VW_FadeOut (); + + ClearMemory (); + + CheckHighScore (gamestate.score,gamestate.mapon+1); + + #pragma warn -sus + #ifndef JAPAN + _fstrcpy(MainMenu[viewscores].string,STR_VS); + #endif + MainMenu[viewscores].routine = CP_ViewScores; + #pragma warn +sus + + return; + } +#endif + + gamestate.oldscore = gamestate.score; + +#ifndef SPEAR + // + // COMING BACK FROM SECRET LEVEL + // + if (gamestate.mapon == 9) + gamestate.mapon = ElevatorBackTo[gamestate.episode]; // back from secret + else + // + // GOING TO SECRET LEVEL + // + if (playstate == ex_secretlevel) + gamestate.mapon = 9; +#else + +#define FROMSECRET1 3 +#define FROMSECRET2 11 + + // + // GOING TO SECRET LEVEL + // + if (playstate == ex_secretlevel) + switch(gamestate.mapon) + { + case FROMSECRET1: gamestate.mapon = 18; break; + case FROMSECRET2: gamestate.mapon = 19; break; + } + else + // + // COMING BACK FROM SECRET LEVEL + // + if (gamestate.mapon == 18 || gamestate.mapon == 19) + switch(gamestate.mapon) + { + case 18: gamestate.mapon = FROMSECRET1+1; break; + case 19: gamestate.mapon = FROMSECRET2+1; break; + } +#endif + else + // + // GOING TO NEXT LEVEL + // + gamestate.mapon++; + + + break; + + case ex_died: + Died (); + died = true; // don't "get psyched!" + + if (gamestate.lives > -1) + break; // more lives left + + VW_FadeOut (); + + ClearMemory (); + + CheckHighScore (gamestate.score,gamestate.mapon+1); + + #pragma warn -sus + #ifndef JAPAN + _fstrcpy(MainMenu[viewscores].string,STR_VS); + #endif + MainMenu[viewscores].routine = CP_ViewScores; + #pragma warn +sus + + return; + + case ex_victorious: + +#ifndef SPEAR + VW_FadeOut (); +#else + VL_FadeOut (0,255,0,17,17,300); +#endif + ClearMemory (); + + Victory (); + + ClearMemory (); + + CheckHighScore (gamestate.score,gamestate.mapon+1); + + #pragma warn -sus + #ifndef JAPAN + _fstrcpy(MainMenu[viewscores].string,STR_VS); + #endif + MainMenu[viewscores].routine = CP_ViewScores; + #pragma warn +sus + + return; + + default: + ClearMemory (); + break; + } + + } while (1); + +} + diff --git a/src/lib/hb/wl_inter.c b/src/lib/hb/wl_inter.c new file mode 100755 index 00000000..c74cc9e6 --- /dev/null +++ b/src/lib/hb/wl_inter.c @@ -0,0 +1,1718 @@ +// WL_INTER.C + +#include "WL_DEF.H" +#pragma hdrstop + + +//========================================================================== + +/* +================== += += CLearSplitVWB += +================== +*/ + +void ClearSplitVWB (void) +{ + memset (update,0,sizeof(update)); + WindowX = 0; + WindowY = 0; + WindowW = 320; + WindowH = 160; +} + + +//========================================================================== + +#ifdef SPEAR +#ifndef SPEARDEMO +//////////////////////////////////////////////////////// +// +// End of Spear of Destiny +// +//////////////////////////////////////////////////////// + +void EndScreen (int palette, int screen) +{ + CA_CacheScreen (screen); + VW_UpdateScreen (); + CA_CacheGrChunk (palette); + VL_FadeIn(0,255,grsegs[palette],30); + UNCACHEGRCHUNK (palette); + IN_ClearKeysDown (); + IN_Ack (); + VW_FadeOut (); +} + + +void EndSpear(void) +{ + EndScreen (END1PALETTE, ENDSCREEN11PIC); + + CA_CacheScreen (ENDSCREEN3PIC); + VW_UpdateScreen (); + CA_CacheGrChunk (END3PALETTE); + VL_FadeIn(0,255,grsegs[END3PALETTE],30); + UNCACHEGRCHUNK (END3PALETTE); + fontnumber = 0; + fontcolor = 0xd0; + WindowX = 0; + WindowW = 320; + PrintX = 0; + PrintY = 180; + US_CPrint (STR_ENDGAME1"\n"); + US_CPrint (STR_ENDGAME2); + VW_UpdateScreen (); + IN_StartAck (); + TimeCount = 0; + while (!IN_CheckAck () && TimeCount < 700); + + PrintX = 0; + PrintY = 180; + VWB_Bar(0,180,320,20,0); + US_CPrint (STR_ENDGAME3"\n"); + US_CPrint (STR_ENDGAME4); + VW_UpdateScreen (); + IN_StartAck (); + TimeCount = 0; + while (!IN_CheckAck () && TimeCount < 700); + + VW_FadeOut (); + + EndScreen (END4PALETTE, ENDSCREEN4PIC); + EndScreen (END5PALETTE, ENDSCREEN5PIC); + EndScreen (END6PALETTE, ENDSCREEN6PIC); + EndScreen (END7PALETTE, ENDSCREEN7PIC); + EndScreen (END8PALETTE, ENDSCREEN8PIC); + EndScreen (END9PALETTE, ENDSCREEN9PIC); + + EndScreen (END2PALETTE, ENDSCREEN12PIC); + + MainMenu[savegame].active = 0; +} +#endif +#endif + +//========================================================================== + +/* +================== += += Victory += +================== +*/ + +void Victory (void) +{ +#ifndef SPEARDEMO + long sec; + int i,min,kr,sr,tr,x; + char tempstr[8]; + +#define RATIOX 6 +#define RATIOY 14 +#define TIMEX 14 +#define TIMEY 8 + + +#ifdef SPEAR + StartCPMusic (XTHEEND_MUS); + + CA_CacheGrChunk(BJCOLLAPSE1PIC); + CA_CacheGrChunk(BJCOLLAPSE2PIC); + CA_CacheGrChunk(BJCOLLAPSE3PIC); + CA_CacheGrChunk(BJCOLLAPSE4PIC); + + VWB_Bar(0,0,320,200,VIEWCOLOR); + VWB_DrawPic (124,44,BJCOLLAPSE1PIC); + VW_UpdateScreen (); + VW_FadeIn (); + VW_WaitVBL(2*70); + VWB_DrawPic (124,44,BJCOLLAPSE2PIC); + VW_UpdateScreen (); + VW_WaitVBL(105); + VWB_DrawPic (124,44,BJCOLLAPSE3PIC); + VW_UpdateScreen (); + VW_WaitVBL(105); + VWB_DrawPic (124,44,BJCOLLAPSE4PIC); + VW_UpdateScreen (); + VW_WaitVBL(3*70); + + UNCACHEGRCHUNK(BJCOLLAPSE1PIC); + UNCACHEGRCHUNK(BJCOLLAPSE2PIC); + UNCACHEGRCHUNK(BJCOLLAPSE3PIC); + UNCACHEGRCHUNK(BJCOLLAPSE4PIC); + VL_FadeOut (0,255,0,17,17,5); +#endif + + StartCPMusic (URAHERO_MUS); + ClearSplitVWB (); + CacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END); + CA_CacheGrChunk(STARTFONT); + +#ifndef SPEAR + CA_CacheGrChunk(C_TIMECODEPIC); +#endif + + + VWB_Bar (0,0,320,200-STATUSLINES,127); +#ifdef JAPAN +#ifndef JAPDEMO + CA_CacheGrChunk(C_ENDRATIOSPIC); + VWB_DrawPic(0,0,C_ENDRATIOSPIC); + UNCACHEGRCHUNK(C_ENDRATIOSPIC); +#endif +#else + Write(18,2,STR_YOUWIN); + + Write(TIMEX,TIMEY-2,STR_TOTALTIME); + + Write(12,RATIOY-2,"averages"); + + #ifdef SPANISH + Write(RATIOX+2, RATIOY, STR_RATKILL); + Write(RATIOX+2, RATIOY+2, STR_RATSECRET); + Write(RATIOX+2, RATIOY+4,STR_RATTREASURE); + #else + Write(RATIOX+8,RATIOY, STR_RATKILL); + Write(RATIOX+4,RATIOY+2, STR_RATSECRET); + Write(RATIOX, RATIOY+4,STR_RATTREASURE); + #endif + +#endif + +#ifndef JAPDEMO + VWB_DrawPic (8,4,L_BJWINSPIC); +#endif + + +#ifndef SPEAR + for (kr = sr = tr = sec = i = 0;i < 8;i++) +#else + for (kr = sr = tr = sec = i = 0;i < 20;i++) +#endif + { + sec += LevelRatios[i].time; + kr += LevelRatios[i].kill; + sr += LevelRatios[i].secret; + tr += LevelRatios[i].treasure; + } + +#ifndef SPEAR + kr /= 8; + sr /= 8; + tr /= 8; +#else + kr /= 14; + sr /= 14; + tr /= 14; +#endif + + min = sec/60; + sec %= 60; + + if (min > 99) + min = sec = 99; + + i = TIMEX*8+1; + VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(min/10)); + i += 2*8; + VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(min%10)); + i += 2*8; + Write(i/8,TIMEY,":"); + i += 1*8; + VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(sec/10)); + i += 2*8; + VWB_DrawPic(i,TIMEY*8,L_NUM0PIC+(sec%10)); + VW_UpdateScreen (); + + itoa(kr,tempstr,10); + x=RATIOX+24-strlen(tempstr)*2; + Write(x,RATIOY,tempstr); + + itoa(sr,tempstr,10); + x=RATIOX+24-strlen(tempstr)*2; + Write(x,RATIOY+2,tempstr); + + itoa(tr,tempstr,10); + x=RATIOX+24-strlen(tempstr)*2; + Write(x,RATIOY+4,tempstr); + + +#ifndef SPANISH +#ifndef UPLOAD +#ifndef SPEAR + // + // TOTAL TIME VERIFICATION CODE + // + if (gamestate.difficulty>=gd_medium) + { + VWB_DrawPic (30*8,TIMEY*8,C_TIMECODEPIC); + fontnumber = 0; + fontcolor = READHCOLOR; + PrintX = 30*8-3; + PrintY = TIMEY*8+8; + PrintX+=4; + tempstr[0] = (((min/10)^(min%10))^0xa)+'A'; + tempstr[1] = (((sec/10)^(sec%10))^0xa)+'A'; + tempstr[2] = (tempstr[0]^tempstr[1])+'A'; + tempstr[3] = 0; + US_Print(tempstr); + } +#endif +#endif +#endif + + + fontnumber = 1; + + VW_UpdateScreen (); + VW_FadeIn (); + + IN_Ack(); + + #ifndef SPEAR + if (Keyboard[sc_P] && MS_CheckParm("goobers")) + PicturePause(); + #endif + + VW_FadeOut (); + +#ifndef SPEAR + UNCACHEGRCHUNK(C_TIMECODEPIC); +#endif + UnCacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END); + +#ifndef SPEAR + EndText(); +#else + EndSpear(); +#endif + +#endif // SPEARDEMO +} + + +//========================================================================== + +#ifndef JAPAN +/* +================== += += PG13 += +================== +*/ + +void PG13 (void) +{ + VW_FadeOut(); + VWB_Bar(0,0,320,200,0x82); // background + + CA_CacheGrChunk (PG13PIC); + VWB_DrawPic (216,110,PG13PIC); + VW_UpdateScreen (); + + UNCACHEGRCHUNK (PG13PIC); + + VW_FadeIn(); + IN_UserInput(TickBase*7); + + VW_FadeOut (); +} +#endif + + +//========================================================================== + +void Write(int x,int y,char *string) +{ + int alpha[]={L_NUM0PIC,L_NUM1PIC,L_NUM2PIC,L_NUM3PIC,L_NUM4PIC,L_NUM5PIC, + L_NUM6PIC,L_NUM7PIC,L_NUM8PIC,L_NUM9PIC,L_COLONPIC,0,0,0,0,0,0,L_APIC,L_BPIC, + L_CPIC,L_DPIC,L_EPIC,L_FPIC,L_GPIC,L_HPIC,L_IPIC,L_JPIC,L_KPIC, + L_LPIC,L_MPIC,L_NPIC,L_OPIC,L_PPIC,L_QPIC,L_RPIC,L_SPIC,L_TPIC, + L_UPIC,L_VPIC,L_WPIC,L_XPIC,L_YPIC,L_ZPIC}; + + int i,ox,nx,ny; + char ch; + + + ox=nx=x*8; + ny=y*8; + for (i=0;i='a') + ch-=('a'-'A'); + ch-='0'; + + switch(string[i]) + { + case '!': + VWB_DrawPic(nx,ny,L_EXPOINTPIC); + nx+=8; + continue; + + case '\'': + VWB_DrawPic(nx,ny,L_APOSTROPHEPIC); + nx+=8; + continue; + + case ' ': break; + case 0x3a: // ':' + + VWB_DrawPic(nx,ny,L_COLONPIC); + nx+=8; + continue; + + case '%': + VWB_DrawPic(nx,ny,L_PERCENTPIC); + break; + + default: + VWB_DrawPic(nx,ny,alpha[ch]); + } + nx+=16; + } +} + + +// +// Breathe Mr. BJ!!! +// +void BJ_Breathe(void) +{ + static int which=0,max=10; + int pics[2]={L_GUYPIC,L_GUY2PIC}; + + + if (TimeCount>max) + { + which^=1; + VWB_DrawPic(0,16,pics[which]); + VW_UpdateScreen(); + TimeCount=0; + max=35; + } +} + + + +/* +================== += += LevelCompleted += += Entered with the screen faded out += Still in split screen mode with the status bar += += Exit with the screen faded out += +================== +*/ + +#ifndef SPEAR +LRstruct LevelRatios[8]; +#else +LRstruct LevelRatios[20]; +#endif + +void LevelCompleted (void) +{ + #define VBLWAIT 30 + #define PAR_AMOUNT 500 + #define PERCENT100AMT 10000 + typedef struct { + float time; + char timestr[6]; + } times; + + int x,i,min,sec,ratio,kr,sr,tr; + unsigned temp; + char tempstr[10]; + long bonus,timeleft=0; + times parTimes[]= + { +#ifndef SPEAR + // + // Episode One Par Times + // + {1.5, "01:30"}, + {2, "02:00"}, + {2, "02:00"}, + {3.5, "03:30"}, + {3, "03:00"}, + {3, "03:00"}, + {2.5, "02:30"}, + {2.5, "02:30"}, + {0, "??:??"}, // Boss level + {0, "??:??"}, // Secret level + + // + // Episode Two Par Times + // + {1.5, "01:30"}, + {3.5, "03:30"}, + {3, "03:00"}, + {2, "02:00"}, + {4, "04:00"}, + {6, "06:00"}, + {1, "01:00"}, + {3, "03:00"}, + {0, "??:??"}, + {0, "??:??"}, + + // + // Episode Three Par Times + // + {1.5, "01:30"}, + {1.5, "01:30"}, + {2.5, "02:30"}, + {2.5, "02:30"}, + {3.5, "03:30"}, + {2.5, "02:30"}, + {2, "02:00"}, + {6, "06:00"}, + {0, "??:??"}, + {0, "??:??"}, + + // + // Episode Four Par Times + // + {2, "02:00"}, + {2, "02:00"}, + {1.5, "01:30"}, + {1, "01:00"}, + {4.5, "04:30"}, + {3.5, "03:30"}, + {2, "02:00"}, + {4.5, "04:30"}, + {0, "??:??"}, + {0, "??:??"}, + + // + // Episode Five Par Times + // + {2.5, "02:30"}, + {1.5, "01:30"}, + {2.5, "02:30"}, + {2.5, "02:30"}, + {4, "04:00"}, + {3, "03:00"}, + {4.5, "04:30"}, + {3.5, "03:30"}, + {0, "??:??"}, + {0, "??:??"}, + + // + // Episode Six Par Times + // + {6.5, "06:30"}, + {4, "04:00"}, + {4.5, "04:30"}, + {6, "06:00"}, + {5, "05:00"}, + {5.5, "05:30"}, + {5.5, "05:30"}, + {8.5, "08:30"}, + {0, "??:??"}, + {0, "??:??"} +#else + // + // SPEAR OF DESTINY TIMES + // + {1.5, "01:30"}, + {3.5, "03:30"}, + {2.75, "02:45"}, + {3.5, "03:30"}, + {0, "??:??"}, // Boss 1 + {4.5, "04:30"}, + {3.25, "03:15"}, + {2.75, "02:45"}, + {4.75, "04:45"}, + {0, "??:??"}, // Boss 2 + {6.5, "06:30"}, + {4.5, "04:30"}, + {2.75, "02:45"}, + {4.5, "04:30"}, + {6, "06:00"}, + {0, "??:??"}, // Boss 3 + {6, "06:00"}, + {0, "??:??"}, // Boss 4 + {0, "??:??"}, // Secret level 1 + {0, "??:??"}, // Secret level 2 +#endif + }; + + + + CacheLump(LEVELEND_LUMP_START,LEVELEND_LUMP_END); + ClearSplitVWB (); // set up for double buffering in split screen + VWB_Bar (0,0,320,200-STATUSLINES,127); + StartCPMusic(ENDLEVEL_MUS); + +// +// do the intermission +// + IN_ClearKeysDown(); + IN_StartAck(); + +#ifdef JAPAN + CA_CacheGrChunk(C_INTERMISSIONPIC); + VWB_DrawPic(0,0,C_INTERMISSIONPIC); + UNCACHEGRCHUNK(C_INTERMISSIONPIC); +#endif + VWB_DrawPic(0,16,L_GUYPIC); + +#ifndef SPEAR + if (mapon<8) +#else + if (mapon != 4 && + mapon != 9 && + mapon != 15 && + mapon < 17) +#endif + { +#ifndef JAPAN + #ifdef SPANISH + Write(14,2,"piso\ncompletado"); + #else + Write(14,2,"floor\ncompleted"); + #endif + + Write(14,7,STR_BONUS" 0"); + Write(16,10,STR_TIME); + Write(16,12,STR_PAR); + + #ifdef SPANISH + Write(11,14, STR_RAT2KILL); + Write(11,16, STR_RAT2SECRET); + Write(11,18,STR_RAT2TREASURE); + #else + Write(9,14, STR_RAT2KILL); + Write(5,16, STR_RAT2SECRET); + Write(1,18,STR_RAT2TREASURE); + #endif + + Write(26,2,itoa(gamestate.mapon+1,tempstr,10)); +#endif + + #ifdef SPANISH + Write(30,12,parTimes[gamestate.episode*10+mapon].timestr); + #else + Write(26,12,parTimes[gamestate.episode*10+mapon].timestr); + #endif + + // + // PRINT TIME + // + sec=gamestate.TimeCount/70; + + if (sec > 99*60) // 99 minutes max + sec = 99*60; + + if (gamestate.TimeCountname); + + // + // level + // + ultoa(s->completed,buffer,10); +#ifndef SPEAR + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (22 * 8)-w; +#else + USL_MeasureString(buffer,&w,&h); + PrintX = 194 - w; +#endif + +#ifndef UPLOAD +#ifndef SPEAR + PrintX -= 6; + itoa(s->episode+1,buffer1,10); + US_Print("E"); + US_Print(buffer1); + US_Print("/L"); +#endif +#endif + +#ifdef SPEAR + if (s->completed == 21) + VWB_DrawPic (PrintX+8,PrintY-1,C_WONSPEARPIC); + else +#endif + US_Print(buffer); + + // + // score + // + ultoa(s->score,buffer,10); +#ifndef SPEAR + for (str = buffer;*str;str++) + *str = *str + (129 - '0'); // Used fixed-width numbers (129...) + USL_MeasureString(buffer,&w,&h); + PrintX = (34 * 8) - 8 - w; +#else + USL_MeasureString(buffer,&w,&h); + PrintX = 292 - w; +#endif + US_Print(buffer); + + #if 0 +#ifndef UPLOAD +#ifndef SPEAR + // + // verification # + // + if (!i) + { + temp=(((s->score >> 28)& 0xf)^ + ((s->score >> 24)& 0xf))+'A'; + temp1=(((s->score >> 20)& 0xf)^ + ((s->score >> 16)& 0xf))+'A'; + temp2=(((s->score >> 12)& 0xf)^ + ((s->score >> 8)& 0xf))+'A'; + temp3=(((s->score >> 4)& 0xf)^ + ((s->score >> 0)& 0xf))+'A'; + + SETFONTCOLOR(0x49,0x29); + PrintX = 35*8; + buffer[0]=temp; + buffer[1]=temp1; + buffer[2]=temp2; + buffer[3]=temp3; + buffer[4]=0; + US_Print(buffer); + SETFONTCOLOR(15,0x29); + } +#endif +#endif + #endif + } + + VW_UpdateScreen (); + +#ifdef SPEAR + UnCacheLump (HIGHSCORES_LUMP_START,HIGHSCORES_LUMP_END); + fontnumber = 0; +#endif +} + +//=========================================================================== + + +/* +======================= += += CheckHighScore += +======================= +*/ + +void CheckHighScore (long score,word other) +{ + word i,j; + int n; + HighScore myscore; + + strcpy(myscore.name,""); + myscore.score = score; + myscore.episode = gamestate.episode; + myscore.completed = other; + + for (i = 0,n = -1;i < MaxScores;i++) + { + if + ( + (myscore.score > Scores[i].score) + || ( + (myscore.score == Scores[i].score) + && (myscore.completed > Scores[i].completed) + ) + ) + { + for (j = MaxScores;--j > i;) + Scores[j] = Scores[j - 1]; + Scores[i] = myscore; + n = i; + break; + } + } + +#ifdef SPEAR + StartCPMusic (XAWARD_MUS); +#else + StartCPMusic (ROSTER_MUS); +#endif + DrawHighScores (); + + VW_FadeIn (); + + if (n != -1) + { + // + // got a high score + // + PrintY = 76 + (16 * n); +#ifndef SPEAR + PrintX = 4*8; + backcolor = BORDCOLOR; + fontcolor = 15; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100); +#else + PrintX = 16; + fontnumber = 1; + VWB_Bar (PrintX-2,PrintY-2,145,15,0x9c); + VW_UpdateScreen (); + backcolor = 0x9c; + fontcolor = 15; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,130); +#endif + } + else + { + IN_ClearKeysDown (); + IN_UserInput(500); + } + +} + + +#ifndef UPLOAD +#ifndef SPEAR +#ifndef JAPAN +//////////////////////////////////////////////////////// +// +// NON-SHAREWARE NOTICE +// +//////////////////////////////////////////////////////// +void NonShareware(void) +{ + VW_FadeOut(); + + ClearMScreen(); + DrawStripes(10); + + CA_CacheGrChunk(STARTFONT+1); + fontnumber = 1; + + SETFONTCOLOR(READHCOLOR,BKGDCOLOR); + PrintX=110; + PrintY=15; + + #ifdef SPANISH + US_Print("Atencion"); + #else + US_Print("Attention"); + #endif + + SETFONTCOLOR(HIGHLIGHT,BKGDCOLOR); + WindowX=PrintX=40; + PrintY=60; + #ifdef SPANISH + US_Print("Este juego NO es gratis y\n"); + US_Print("NO es Shareware; favor de\n"); + US_Print("no distribuirlo.\n\n"); + #else + US_Print("This game is NOT shareware.\n"); + US_Print("Please do not distribute it.\n"); + US_Print("Thanks.\n\n"); + #endif + US_Print(" Id Software\n"); + + VW_UpdateScreen (); + VW_FadeIn(); + IN_Ack(); +} +#endif +#endif +#endif + +#ifdef SPEAR +#ifndef SPEARDEMO +//////////////////////////////////////////////////////// +// +// COPY PROTECTION FOR FormGen +// +//////////////////////////////////////////////////////// +char far CopyProFailedStrs[][100] = { + STR_COPY1, + STR_COPY2, + + STR_COPY3, + STR_COPY4, + + STR_COPY5, + STR_COPY6, + + STR_COPY7, + STR_COPY8, + + STR_COPY9, + "", + + STR_COPY10, + STR_COPY11, + + STR_COPY12, + "", + + STR_COPY13, + "", + + STR_COPY14, + "" + }, + + far BackDoorStrs[5][16] = { + "a spoon?", + "bite me!", + "joshua", + "pelt", +#ifdef BETA + "beta" +#else + "snoops" +#endif + }, + + far GoodBoyStrs[10][40] = { + "...is the CORRECT ANSWER!", + "", + + "Consider yourself bitten, sir.", + "", + + "Greetings Professor Falken, would you", + "like to play Spear of Destiny?", + + "Do you have any gold spray paint?", + "", + +#ifdef BETA + "Beta testing approved.", +#else + "I wish I had a 21\" monitor...", +#endif + "" + }, + + far bossstrs[4][24] = { + "DEATH KNIGHT", + "BARNACLE WILHELM", + "UBERMUTANTUBER MUTANT", + "TRANS GROSSE" + }, + + far WordStr[5][20] = { + "New Game", + "Sound...F4", + "Control...F6", + "Change View...F5", + "Quit...F10"}, + + far WordCorrect[5][2] = {"3","4","4","5","5"}, + + far MemberStr[10][40] = { + STR_COPY15, + "", + + STR_COPY16, + "", + + STR_COPY17, + STR_COPY18, + + STR_COPY19, + STR_COPY20, + + STR_COPY21, + STR_COPY22}, + + far MemberCorrect[5][24] = { + "adrian carmack", + "john carmackjohn romero", + "tom hall", + "jay wilbur", + "kevin cloud"}, + + far DosMessages[9][80] = { + STR_NOPE1, + STR_NOPE2, + STR_NOPE3, + STR_NOPE4, + STR_NOPE5, + STR_NOPE6, + STR_NOPE7, + STR_NOPE8, + STR_NOPE9}, + + far MiscTitle[4][20] = { + "BLOOD TEST", + "STRAIGHT-LACED", + "QUITE SHAPELY", + "I AM WHAT I AMMO" + }, + + far MiscStr[12][40] = { + STR_MISC1, + STR_MISC2, + "", + + STR_MISC3, + STR_MISC4, + "", + + STR_MISC5, + STR_MISC6, + "", + + STR_MISC7, + STR_MISC8, + STR_MISC9 + }, + + far MiscCorrect[4][5] = {"ss","8",STR_STAR,"45"}; + + +int BackDoor(char *s) +{ + int i; + + + strlwr(s); + + for (i=0;i<5;i++) + if (!_fstrcmp(s,BackDoorStrs[i])) + { + SETFONTCOLOR(14,15); + fontnumber = 0; + PrintY = 175; + VWB_DrawPic (0,20*8,COPYPROTBOXPIC); + US_CPrint(GoodBoyStrs[i*2]); + US_CPrint(GoodBoyStrs[i*2+1]); + VW_UpdateScreen(); + return 1; + } + + return 0; +} + + +void CopyProtection(void) +{ +#define TYPEBOX_Y 177 +#define TYPEBOX_BKGD 0x9c +#define PRINTCOLOR HIGHLIGHT + + int i,match,whichboss,bossnum,try,whichline,enemypicked[4]={0,0,0,0}, + bosses[4] = { BOSSPIC1PIC,BOSSPIC2PIC,BOSSPIC3PIC,BOSSPIC4PIC }, + whichone,whichpicked[4]={0,0,0,0},quiztype,whichmem, + memberpicked[5]={0,0,0,0,0},wordpicked[5]={0,0,0,0,0},whichword; + + char inputbuffer[20], + message[80]; + + enum + { + debriefing, + checkmanual, + staffquiz, + miscquiz, + + totaltypes + }; + + + + try = 0; + VW_FadeOut(); + CA_CacheGrChunk(C_BACKDROPPIC); + CacheLump(COPYPROT_LUMP_START,COPYPROT_LUMP_END); + CA_CacheGrChunk(STARTFONT+1); + CA_LoadAllSounds(); + StartCPMusic(COPYPRO_MUS); + US_InitRndT(true); + + while (try<3) + { + fontnumber = 1; + SETFONTCOLOR(PRINTCOLOR-2,15); + VWB_DrawPic (0,0,C_BACKDROPPIC); + VWB_DrawPic (0,0,COPYPROTTOPPIC); + VWB_DrawPic (0,20*8,COPYPROTBOXPIC); + WindowX = WindowY = 0; + WindowW = 320; + WindowH = 200; + PrintY = 65; + + quiztype = US_RndT()%totaltypes; + switch(quiztype) + { + // + // BOSSES QUIZ + // + case debriefing: + PrintX = 0; + US_Print(STR_DEBRIEF); + SETFONTCOLOR(PRINTCOLOR,15); + + while (enemypicked[whichboss = US_RndT()&3]); + enemypicked[whichboss] = 1; + bossnum = bosses[whichboss]; + VWB_DrawPic(128,60,bossnum); + fontnumber = 0; + PrintY = 130; + US_CPrint(STR_ENEMY1"\n"); + US_CPrint(STR_ENEMY2"\n\n"); + + VW_UpdateScreen(); + VW_FadeIn(); + + PrintX = 100; + fontcolor = 15; + backcolor = TYPEBOX_BKGD; + inputbuffer[0] = 0; + PrintY = TYPEBOX_Y; + fontnumber = 1; + US_LineInput(PrintX,PrintY,inputbuffer,nil,true,20,100); + + match = 0; + for (i=0;i<_fstrlen(bossstrs[whichboss]);i++) + if (!_fstrnicmp(inputbuffer,bossstrs[whichboss]+i,strlen(inputbuffer)) && + strlen(inputbuffer)>3) + match = 1; + + match += BackDoor(inputbuffer); + break; + + // + // MANUAL CHECK + // + case checkmanual: + while (wordpicked[whichword = US_RndT()%5]); + wordpicked[whichword] = 1; + US_CPrint(STR_CHECKMAN); + SETFONTCOLOR(PRINTCOLOR,15); + PrintY += 25; + US_CPrint(STR_MAN1); + US_CPrint(STR_MAN2); + _fstrcpy(message,STR_MAN3" \""); + _fstrcat(message,WordStr[whichword]); + _fstrcat(message,"\" "STR_MAN4); + US_CPrint(message); + VW_UpdateScreen(); + VW_FadeIn(); + + PrintX = 146; + fontcolor = 15; + backcolor = TYPEBOX_BKGD; + inputbuffer[0] = 0; + PrintY = TYPEBOX_Y; + US_LineInput(PrintX,PrintY,inputbuffer,nil,true,6,100); + + strlwr(inputbuffer); + match = 1-(_fstrcmp(inputbuffer,WordCorrect[whichword])!=0); + match += BackDoor(inputbuffer); + break; + + // + // STAFF QUIZ + // + case staffquiz: + while (memberpicked[whichmem = US_RndT()%5]); + memberpicked[whichmem] = 1; + US_CPrint(STR_ID1); + SETFONTCOLOR(PRINTCOLOR,15); + PrintY += 25; + US_CPrint(MemberStr[whichmem*2]); + US_CPrint(MemberStr[whichmem*2+1]); + VW_UpdateScreen(); + VW_FadeIn(); + + PrintX = 100; + fontcolor = 15; + backcolor = TYPEBOX_BKGD; + inputbuffer[0] = 0; + PrintY = TYPEBOX_Y; + US_LineInput(PrintX,PrintY,inputbuffer,nil,true,20,120); + + strlwr(inputbuffer); + match = 0; + for (i=0;i<_fstrlen(MemberCorrect[whichmem]);i++) + if (!_fstrnicmp(inputbuffer,MemberCorrect[whichmem]+i,strlen(inputbuffer)) && + strlen(inputbuffer)>2) + match = 1; + match += BackDoor(inputbuffer); + break; + + // + // MISCELLANEOUS QUESTIONS + // + case miscquiz: + while (whichpicked[whichone = US_RndT()&3]); + whichpicked[whichone] = 1; + US_CPrint(MiscTitle[whichone]); + SETFONTCOLOR(PRINTCOLOR,15); + PrintY += 25; + US_CPrint(MiscStr[whichone*3]); + US_CPrint(MiscStr[whichone*3+1]); + US_CPrint(MiscStr[whichone*3+2]); + VW_UpdateScreen(); + VW_FadeIn(); + + PrintX = 146; + fontcolor = 15; + backcolor = TYPEBOX_BKGD; + inputbuffer[0] = 0; + PrintY = TYPEBOX_Y; + US_LineInput(PrintX,PrintY,inputbuffer,nil,true,6,100); + + strlwr(inputbuffer); + match = 1-(_fstrcmp(inputbuffer,MiscCorrect[whichone])!=0); + match += BackDoor(inputbuffer); + break; + } + + // + // IF NO MATCH, WE'VE GOT A (MINOR) PROBLEM! + // + + if (!match) + { + whichline = 2*(US_RndT()%9); + SETFONTCOLOR(14,15); + fontnumber = 0; + PrintY = 175; + VWB_DrawPic (0,20*8,COPYPROTBOXPIC); + US_CPrint(CopyProFailedStrs[whichline]); + US_CPrint(CopyProFailedStrs[whichline+1]); + + VW_UpdateScreen(); + SD_PlaySound(NOWAYSND); + IN_UserInput(TickBase*3); + VW_FadeOut(); + try++; + } + else + { + int start; + + + SD_PlaySound(BONUS1UPSND); + SD_WaitSoundDone(); + UNCACHEGRCHUNK (STARTFONT+1); + UNCACHEGRCHUNK (C_BACKDROPPIC); + UnCacheLump (COPYPROT_LUMP_START,COPYPROT_LUMP_END); + + switch(SoundMode) + { + case sdm_Off: return; + case sdm_PC: start = STARTPCSOUNDS; break; + case sdm_AdLib: start = STARTADLIBSOUNDS; + } + + for (i=0;i +#include "WL_DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + WOLFENSTEIN 3-D + + An Id Software production + + by John Carmack + +============================================================================= +*/ + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +#define FOCALLENGTH (0x5700l) // in global coordinates +#define VIEWGLOBAL 0x10000 // globals visable flush to wall + +#define VIEWWIDTH 256 // size of view window +#define VIEWHEIGHT 144 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +char str[80],str2[20]; +int tedlevelnum; +boolean tedlevel; +boolean nospr; +boolean IsA386; +int dirangle[9] = {0,ANGLES/8,2*ANGLES/8,3*ANGLES/8,4*ANGLES/8, + 5*ANGLES/8,6*ANGLES/8,7*ANGLES/8,ANGLES}; + +// +// proejection variables +// +fixed focallength; +unsigned screenofs; +int viewwidth; +int viewheight; +int centerx; +int shootdelta; // pixels away from centerx a target can be +fixed scale,maxslope; +long heightnumerator; +int minheightdiv; + + +void Quit (char *error); + +boolean startgame,loadedgame,virtualreality; +int mouseadjustment; + +char configname[13]="CONFIG."; + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +/* +==================== += += ReadConfig += +==================== +*/ + +void ReadConfig(void) +{ + int file; + SDMode sd; + SMMode sm; + SDSMode sds; + + + if ( (file = open(configname,O_BINARY | O_RDONLY)) != -1) + { + // + // valid config file + // + read(file,Scores,sizeof(HighScore) * MaxScores); + + read(file,&sd,sizeof(sd)); + read(file,&sm,sizeof(sm)); + read(file,&sds,sizeof(sds)); + + read(file,&mouseenabled,sizeof(mouseenabled)); + read(file,&joystickenabled,sizeof(joystickenabled)); + read(file,&joypadenabled,sizeof(joypadenabled)); + read(file,&joystickprogressive,sizeof(joystickprogressive)); + read(file,&joystickport,sizeof(joystickport)); + + read(file,&dirscan,sizeof(dirscan)); + read(file,&buttonscan,sizeof(buttonscan)); + read(file,&buttonmouse,sizeof(buttonmouse)); + read(file,&buttonjoy,sizeof(buttonjoy)); + + read(file,&viewsize,sizeof(viewsize)); + read(file,&mouseadjustment,sizeof(mouseadjustment)); + + close(file); + + if (sd == sdm_AdLib && !AdLibPresent && !SoundBlasterPresent) + { + sd = sdm_PC; + sd = smm_Off; + } + + if ((sds == sds_SoundBlaster && !SoundBlasterPresent) || + (sds == sds_SoundSource && !SoundSourcePresent)) + sds = sds_Off; + + if (!MousePresent) + mouseenabled = false; + if (!JoysPresent[joystickport]) + joystickenabled = false; + + MainMenu[6].active=1; + MainItems.curpos=0; + } + else + { + // + // no config file, so select by hardware + // + if (SoundBlasterPresent || AdLibPresent) + { + sd = sdm_AdLib; + sm = smm_AdLib; + } + else + { + sd = sdm_PC; + sm = smm_Off; + } + + if (SoundBlasterPresent) + sds = sds_SoundBlaster; + else if (SoundSourcePresent) + sds = sds_SoundSource; + else + sds = sds_Off; + + if (MousePresent) + mouseenabled = true; + + joystickenabled = false; + joypadenabled = false; + joystickport = 0; + joystickprogressive = false; + + viewsize = 15; + mouseadjustment=5; + } + + SD_SetMusicMode (sm); + SD_SetSoundMode (sd); + SD_SetDigiDevice (sds); + +} + + +/* +==================== += += WriteConfig += +==================== +*/ + +void WriteConfig(void) +{ + int file; + + file = open(configname,O_CREAT | O_BINARY | O_WRONLY, + S_IREAD | S_IWRITE | S_IFREG); + + if (file != -1) + { + write(file,Scores,sizeof(HighScore) * MaxScores); + + write(file,&SoundMode,sizeof(SoundMode)); + write(file,&MusicMode,sizeof(MusicMode)); + write(file,&DigiMode,sizeof(DigiMode)); + + write(file,&mouseenabled,sizeof(mouseenabled)); + write(file,&joystickenabled,sizeof(joystickenabled)); + write(file,&joypadenabled,sizeof(joypadenabled)); + write(file,&joystickprogressive,sizeof(joystickprogressive)); + write(file,&joystickport,sizeof(joystickport)); + + write(file,&dirscan,sizeof(dirscan)); + write(file,&buttonscan,sizeof(buttonscan)); + write(file,&buttonmouse,sizeof(buttonmouse)); + write(file,&buttonjoy,sizeof(buttonjoy)); + + write(file,&viewsize,sizeof(viewsize)); + write(file,&mouseadjustment,sizeof(mouseadjustment)); + + close(file); + } +} + + +//=========================================================================== + + +/* +======================== += += Patch386 += += Patch ldiv to use 32 bit instructions += +======================== +*/ + +char *JHParmStrings[] = {"no386",nil}; +void Patch386 (void) +{ +extern void far jabhack2(void); +extern int far CheckIs386(void); + + int i; + + for (i = 1;i < _argc;i++) + if (US_CheckParm(_argv[i],JHParmStrings) == 0) + { + IsA386 = false; + return; + } + + if (CheckIs386()) + { + IsA386 = true; + jabhack2(); + } + else + IsA386 = false; +} + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +void NewGame (int difficulty,int episode) +{ + memset (&gamestate,0,sizeof(gamestate)); + gamestate.difficulty = difficulty; + gamestate.weapon = gamestate.bestweapon + = gamestate.chosenweapon = wp_pistol; + gamestate.health = 100; + gamestate.ammo = STARTAMMO; + gamestate.lives = 3; + gamestate.nextextra = EXTRAPOINTS; + gamestate.episode=episode; + + startgame = true; +} + +//=========================================================================== + +void DiskFlopAnim(int x,int y) +{ + static char which=0; + if (!x && !y) + return; + VWB_DrawPic(x,y,C_DISKLOADING1PIC+which); + VW_UpdateScreen(); + which^=1; +} + + +long DoChecksum(byte far *source,unsigned size,long checksum) +{ + unsigned i; + + for (i=0;inext) + size += sizeof(*ob); + size += sizeof(nullobj); + + size += sizeof(gamestate) + + sizeof(LRstruct)*8 + + sizeof(tilemap) + + sizeof(actorat) + + sizeof(laststatobj) + + sizeof(statobjlist) + + sizeof(doorposition) + + sizeof(pwallstate) + + sizeof(pwallx) + + sizeof(pwally) + + sizeof(pwalldir) + + sizeof(pwallpos); + + if (avail < size) + { + Message(STR_NOSPACE1"\n" + STR_NOSPACE2); + return false; + } + + checksum = 0; + + + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)&gamestate,sizeof(gamestate)); + checksum = DoChecksum((byte far *)&gamestate,sizeof(gamestate),checksum); + + DiskFlopAnim(x,y); +#ifdef SPEAR + CA_FarWrite (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*20); + checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*20,checksum); +#else + CA_FarWrite (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*8); + checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*8,checksum); +#endif + + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)tilemap,sizeof(tilemap)); + checksum = DoChecksum((byte far *)tilemap,sizeof(tilemap),checksum); + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)actorat,sizeof(actorat)); + checksum = DoChecksum((byte far *)actorat,sizeof(actorat),checksum); + + CA_FarWrite (file,(void far *)areaconnect,sizeof(areaconnect)); + CA_FarWrite (file,(void far *)areabyplayer,sizeof(areabyplayer)); + + for (ob = player ; ob ; ob=ob->next) + { + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)ob,sizeof(*ob)); + } + nullobj.active = ac_badobject; // end of file marker + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)&nullobj,sizeof(nullobj)); + + + + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)&laststatobj,sizeof(laststatobj)); + checksum = DoChecksum((byte far *)&laststatobj,sizeof(laststatobj),checksum); + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)statobjlist,sizeof(statobjlist)); + checksum = DoChecksum((byte far *)statobjlist,sizeof(statobjlist),checksum); + + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)doorposition,sizeof(doorposition)); + checksum = DoChecksum((byte far *)doorposition,sizeof(doorposition),checksum); + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)doorobjlist,sizeof(doorobjlist)); + checksum = DoChecksum((byte far *)doorobjlist,sizeof(doorobjlist),checksum); + + DiskFlopAnim(x,y); + CA_FarWrite (file,(void far *)&pwallstate,sizeof(pwallstate)); + checksum = DoChecksum((byte far *)&pwallstate,sizeof(pwallstate),checksum); + CA_FarWrite (file,(void far *)&pwallx,sizeof(pwallx)); + checksum = DoChecksum((byte far *)&pwallx,sizeof(pwallx),checksum); + CA_FarWrite (file,(void far *)&pwally,sizeof(pwally)); + checksum = DoChecksum((byte far *)&pwally,sizeof(pwally),checksum); + CA_FarWrite (file,(void far *)&pwalldir,sizeof(pwalldir)); + checksum = DoChecksum((byte far *)&pwalldir,sizeof(pwalldir),checksum); + CA_FarWrite (file,(void far *)&pwallpos,sizeof(pwallpos)); + checksum = DoChecksum((byte far *)&pwallpos,sizeof(pwallpos),checksum); + + // + // WRITE OUT CHECKSUM + // + CA_FarWrite (file,(void far *)&checksum,sizeof(checksum)); + + return(true); +} + +//=========================================================================== + +/* +================== += += LoadTheGame += +================== +*/ + +boolean LoadTheGame(int file,int x,int y) +{ + long checksum,oldchecksum; + objtype *ob,nullobj; + + + checksum = 0; + + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)&gamestate,sizeof(gamestate)); + checksum = DoChecksum((byte far *)&gamestate,sizeof(gamestate),checksum); + + DiskFlopAnim(x,y); +#ifdef SPEAR + CA_FarRead (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*20); + checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*20,checksum); +#else + CA_FarRead (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*8); + checksum = DoChecksum((byte far *)&LevelRatios[0],sizeof(LRstruct)*8,checksum); +#endif + + DiskFlopAnim(x,y); + SetupGameLevel (); + + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)tilemap,sizeof(tilemap)); + checksum = DoChecksum((byte far *)tilemap,sizeof(tilemap),checksum); + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)actorat,sizeof(actorat)); + checksum = DoChecksum((byte far *)actorat,sizeof(actorat),checksum); + + CA_FarRead (file,(void far *)areaconnect,sizeof(areaconnect)); + CA_FarRead (file,(void far *)areabyplayer,sizeof(areabyplayer)); + + + + InitActorList (); + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)player,sizeof(*player)); + + while (1) + { + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)&nullobj,sizeof(nullobj)); + if (nullobj.active == ac_badobject) + break; + GetNewActor (); + // don't copy over the links + memcpy (new,&nullobj,sizeof(nullobj)-4); + } + + + + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)&laststatobj,sizeof(laststatobj)); + checksum = DoChecksum((byte far *)&laststatobj,sizeof(laststatobj),checksum); + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)statobjlist,sizeof(statobjlist)); + checksum = DoChecksum((byte far *)statobjlist,sizeof(statobjlist),checksum); + + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)doorposition,sizeof(doorposition)); + checksum = DoChecksum((byte far *)doorposition,sizeof(doorposition),checksum); + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)doorobjlist,sizeof(doorobjlist)); + checksum = DoChecksum((byte far *)doorobjlist,sizeof(doorobjlist),checksum); + + DiskFlopAnim(x,y); + CA_FarRead (file,(void far *)&pwallstate,sizeof(pwallstate)); + checksum = DoChecksum((byte far *)&pwallstate,sizeof(pwallstate),checksum); + CA_FarRead (file,(void far *)&pwallx,sizeof(pwallx)); + checksum = DoChecksum((byte far *)&pwallx,sizeof(pwallx),checksum); + CA_FarRead (file,(void far *)&pwally,sizeof(pwally)); + checksum = DoChecksum((byte far *)&pwally,sizeof(pwally),checksum); + CA_FarRead (file,(void far *)&pwalldir,sizeof(pwalldir)); + checksum = DoChecksum((byte far *)&pwalldir,sizeof(pwalldir),checksum); + CA_FarRead (file,(void far *)&pwallpos,sizeof(pwallpos)); + checksum = DoChecksum((byte far *)&pwallpos,sizeof(pwallpos),checksum); + + CA_FarRead (file,(void far *)&oldchecksum,sizeof(oldchecksum)); + + if (oldchecksum != checksum) + { + Message(STR_SAVECHT1"\n" + STR_SAVECHT2"\n" + STR_SAVECHT3"\n" + STR_SAVECHT4); + + IN_ClearKeysDown(); + IN_Ack(); + + gamestate.score = 0; + gamestate.lives = 1; + gamestate.weapon = + gamestate.chosenweapon = + gamestate.bestweapon = wp_pistol; + gamestate.ammo = 8; + } + + return true; +} + +//=========================================================================== + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); + SD_Shutdown (); + PM_Shutdown (); + IN_Shutdown (); + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + + +//=========================================================================== + +/* +================== += += BuildTables += += Calculates: += += scale projection constant += sintable/costable overlapping fractional tables += +================== +*/ + +const float radtoint = (float)FINEANGLES/2/PI; + +void BuildTables (void) +{ + int i; + float angle,anglestep; + double tang; + fixed value; + + +// +// calculate fine tangents +// + + for (i=0;i>2 +// + heightnumerator = (TILEGLOBAL*scale)>>6; + minheightdiv = heightnumerator/0x7fff +1; + +// +// calculate the angle offset from view angle of each pixel's ray +// + + for (i=0;i>= 8; +} + + + +//=========================================================================== + +/* +=================== += += SetupWalls += += Map tile values to scaled pics += +=================== +*/ + +void SetupWalls (void) +{ + int i; + + for (i=1;i=0) + { + if (lastsong >= 0) + MusicMenu[start+lastsong].active = 1; + + StartCPMusic(songs[start + which]); + MusicMenu[start+which].active = 2; + DrawMenu (&MusicItems,&MusicMenu[start]); + VW_UpdateScreen(); + lastsong = which; + } + } while(which>=0); + + MenuFadeOut(); + IN_ClearKeysDown(); +#ifdef SPEAR + UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); +#else + UnCacheLump (CONTROLS_LUMP_START,CONTROLS_LUMP_END); +#endif +} +#endif + + +/* +========================== += += InitGame += += Load a few things right away += +========================== +*/ + +void InitGame (void) +{ + int i,x,y; + unsigned *blockstart; + + if (MS_CheckParm ("virtual")) + virtualreality = true; + else + virtualreality = false; + + MM_Startup (); // so the signon screen can be freed + + SignonScreen (); + + VW_Startup (); + IN_Startup (); + PM_Startup (); + PM_UnlockMainMem (); + SD_Startup (); + CA_Startup (); + US_Startup (); + + +#ifndef SPEAR + if (mminfo.mainmem < 235000L) +#else + if (mminfo.mainmem < 257000L && !MS_CheckParm("debugmode")) +#endif + { + memptr screen; + + CA_CacheGrChunk (ERRORSCREEN); + screen = grsegs[ERRORSCREEN]; + ShutdownId(); + movedata ((unsigned)screen,7+7*160,0xb800,0,17*160); + gotoxy (1,23); + exit(1); + } + + +// +// build some tables +// + InitDigiMap (); + + for (i=0;i YEAR || + (d.month >= MONTH && d.day >= DAY)) + { + printf("Sorry, BETA-TESTING is over. Thanks for you help.\n"); + exit(1); + } +#endif + + CheckForEpisodes(); + + Patch386 (); + + InitGame (); + + DemoLoop(); + + Quit("Demo loop exited???"); +} + diff --git a/src/lib/hb/wl_menu.c b/src/lib/hb/wl_menu.c new file mode 100755 index 00000000..7d1521cb --- /dev/null +++ b/src/lib/hb/wl_menu.c @@ -0,0 +1,3986 @@ +//////////////////////////////////////////////////////////////////// +// +// WL_MENU.C +// by John Romero (C) 1992 Id Software, Inc. +// +//////////////////////////////////////////////////////////////////// +#include "wl_def.h" +#pragma hdrstop + +// +// PRIVATE PROTOTYPES +// +void CP_ReadThis(void); + +#ifdef SPEAR +#define STARTITEM newgame + +#else +#ifdef GOODTIMES +#define STARTITEM newgame + +#else +#define STARTITEM readthis +#endif +#endif + +char far endStrings[9][80]= +{ +#ifndef SPEAR + {"Dost thou wish to\nleave with such hasty\nabandon?"}, + {"Chickening out...\nalready?"}, + {"Press N for more carnage.\nPress Y to be a weenie."}, + {"So, you think you can\nquit this easily, huh?"}, + {"Press N to save the world.\nPress Y to abandon it in\nits hour of need."}, + {"Press N if you are brave.\nPress Y to cower in shame."}, + {"Heroes, press N.\nWimps, press Y."}, + {"You are at an intersection.\nA sign says, 'Press Y to quit.'\n>"}, + {"For guns and glory, press N.\nFor work and worry, press Y."} +#else + ENDSTR1, + ENDSTR2, + ENDSTR3, + ENDSTR4, + ENDSTR5, + ENDSTR6, + ENDSTR7, + ENDSTR8, + ENDSTR9 +#endif +}; + +CP_iteminfo + MainItems={MENU_X,MENU_Y,10,STARTITEM,24}, + SndItems={SM_X,SM_Y1,12,0,52}, + LSItems={LSM_X,LSM_Y,10,0,24}, + CtlItems={CTL_X,CTL_Y,6,-1,56}, + CusItems={8,CST_Y+13*2,9,-1,0}, + NewEitems={NE_X,NE_Y,11,0,88}, + NewItems={NM_X,NM_Y,4,2,24}; + +#pragma warn -sus +CP_itemtype far +MainMenu[]= +{ +#ifdef JAPAN + {1,"",CP_NewGame}, + {1,"",CP_Sound}, + {1,"",CP_Control}, + {1,"",CP_LoadGame}, + {0,"",CP_SaveGame}, + {1,"",CP_ChangeView}, + {2,"",CP_ReadThis}, + {1,"",CP_ViewScores}, + {1,"",0}, + {1,"",0} +#else + + {1,STR_NG,CP_NewGame}, + {1,STR_SD,CP_Sound}, + {1,STR_CL,CP_Control}, + {1,STR_LG,CP_LoadGame}, + {0,STR_SG,CP_SaveGame}, + {1,STR_CV,CP_ChangeView}, + +#ifndef GOODTIMES +#ifndef SPEAR + + #ifdef SPANISH + {2,"Ve esto!",CP_ReadThis}, + #else + {2,"Read This!",CP_ReadThis}, + #endif + +#endif +#endif + + {1,STR_VS,CP_ViewScores}, + {1,STR_BD,0}, + {1,STR_QT,0} +#endif +}, + +far SndMenu[]= +{ +#ifdef JAPAN + {1,"",0}, + {1,"",0}, + {1,"",0}, + {0,"",0}, + {0,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {0,"",0}, + {0,"",0}, + {1,"",0}, + {1,"",0}, +#else + {1,STR_NONE,0}, + {1,STR_PC,0}, + {1,STR_ALSB,0}, + {0,"",0}, + {0,"",0}, + {1,STR_NONE,0}, + {1,STR_DISNEY,0}, + {1,STR_SB,0}, + {0,"",0}, + {0,"",0}, + {1,STR_NONE,0}, + {1,STR_ALSB,0} +#endif +}, + +far CtlMenu[]= +{ +#ifdef JAPAN + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",MouseSensitivity}, + {1,"",CustomControls} +#else + {0,STR_MOUSEEN,0}, + {0,STR_JOYEN,0}, + {0,STR_PORT2,0}, + {0,STR_GAMEPAD,0}, + {0,STR_SENS,MouseSensitivity}, + {1,STR_CUSTOM,CustomControls} +#endif +}, + +#pragma warn +sus + +#ifndef SPEAR +far NewEmenu[]= +{ +#ifdef JAPAN +#ifdef JAPDEMO + {1,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, + {0,"",0}, +#else + {1,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0} +#endif +#else + #ifdef SPANISH + {1,"Episodio 1\n" + "Fuga desde Wolfenstein",0}, + {0,"",0}, + {3,"Episodio 2\n" + "Operacion Eisenfaust",0}, + {0,"",0}, + {3,"Episodio 3\n" + "Muere, Fuhrer, Muere!",0}, + {0,"",0}, + {3,"Episodio 4\n" + "Un Negro Secreto",0}, + {0,"",0}, + {3,"Episodio 5\n" + "Huellas del Loco",0}, + {0,"",0}, + {3,"Episodio 6\n" + "Confrontacion",0} + #else + {1,"Episode 1\n" + "Escape from Wolfenstein",0}, + {0,"",0}, + {3,"Episode 2\n" + "Operation: Eisenfaust",0}, + {0,"",0}, + {3,"Episode 3\n" + "Die, Fuhrer, Die!",0}, + {0,"",0}, + {3,"Episode 4\n" + "A Dark Secret",0}, + {0,"",0}, + {3,"Episode 5\n" + "Trail of the Madman",0}, + {0,"",0}, + {3,"Episode 6\n" + "Confrontation",0} + #endif +#endif +}, +#endif + + +far NewMenu[]= +{ +#ifdef JAPAN + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0} +#else + {1,STR_DADDY,0}, + {1,STR_HURTME,0}, + {1,STR_BRINGEM,0}, + {1,STR_DEATH,0} +#endif +}, + +far LSMenu[]= +{ + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0}, + {1,"",0} +}, + +far CusMenu[]= +{ + {1,"",0}, + {0,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0}, + {0,"",0}, + {1,"",0}, + {0,"",0}, + {1,"",0} +} +; + + +int color_hlite[]={ + DEACTIVE, + HIGHLIGHT, + READHCOLOR, + 0x67 + }, + + color_norml[]={ + DEACTIVE, + TEXTCOLOR, + READCOLOR, + 0x6b + }; + +int EpisodeSelect[6]={1}; + + +int SaveGamesAvail[10],StartGame,SoundStatus=1,pickquick; +char SaveGameNames[10][32],SaveName[13]="SAVEGAM?."; + + +//////////////////////////////////////////////////////////////////// +// +// INPUT MANAGER SCANCODE TABLES +// +//////////////////////////////////////////////////////////////////// +static byte + *ScanNames[] = // Scan code names with single chars + { + "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?", + "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S", + "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V", + "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?", + "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?", + "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?" + }, // DEBUG - consolidate these + far ExtScanCodes[] = // Scan codes with >1 char names + { + 1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e, + 0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36, + 0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48, + 0x50,0x4b,0x4d,0x00 + }, + *ExtScanNames[] = // Names corresponding to ExtScanCodes + { + "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4", + "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft", + "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up", + "Down","Left","Right","" + }; + + +//////////////////////////////////////////////////////////////////// +// +// Wolfenstein Control Panel! Ta Da! +// +//////////////////////////////////////////////////////////////////// +void US_ControlPanel(byte scancode) +{ + int which,i,start; + + + if (ingame) + if (CP_CheckQuick(scancode)) + return; + + StartCPMusic(MENUSONG); + SetupControlPanel(); + + // + // F-KEYS FROM WITHIN GAME + // + switch(scancode) + { + case sc_F1: + #ifdef SPEAR + BossKey(); + #else + #ifdef GOODTIMES + BossKey(); + #else + HelpScreens(); + #endif + #endif + goto finishup; + + case sc_F2: + CP_SaveGame(0); + goto finishup; + + case sc_F3: + CP_LoadGame(0); + goto finishup; + + case sc_F4: + CP_Sound(); + goto finishup; + + case sc_F5: + CP_ChangeView(); + goto finishup; + + case sc_F6: + CP_Control(); + goto finishup; + + finishup: + CleanupControlPanel(); + #ifdef SPEAR + UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + #endif + return; + } + +#ifdef SPEAR + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif + + DrawMainMenu(); + MenuFadeIn(); + StartGame=0; + + // + // MAIN MENU LOOP + // + do + { + which=HandleMenu(&MainItems,&MainMenu[0],NULL); + + #ifdef SPEAR + #ifndef SPEARDEMO + // + // EASTER EGG FOR SPEAR OF DESTINY! + // + if (Keyboard[sc_I] && Keyboard[sc_D]) + { + VW_FadeOut(); + StartCPMusic (XJAZNAZI_MUS); + UnCacheLump(OPTIONS_LUMP_START,OPTIONS_LUMP_END); + UnCacheLump(BACKDROP_LUMP_START,BACKDROP_LUMP_END); + MM_SortMem (); + ClearMemory (); + + + CA_CacheGrChunk (IDGUYS1PIC); + VWB_DrawPic(0,0,IDGUYS1PIC); + UNCACHEGRCHUNK(IDGUYS1PIC); + + CA_CacheGrChunk (IDGUYS2PIC); + VWB_DrawPic(0,80,IDGUYS2PIC); + UNCACHEGRCHUNK(IDGUYS2PIC); + + VW_UpdateScreen(); + + CA_CacheGrChunk (IDGUYSPALETTE); + VL_FadeIn(0,255,grsegs[IDGUYSPALETTE],30); + UNCACHEGRCHUNK(IDGUYSPALETTE); + + while (Keyboard[sc_I] || Keyboard[sc_D]); + IN_ClearKeysDown(); + IN_Ack(); + + VW_FadeOut(); + + CacheLump(BACKDROP_LUMP_START,BACKDROP_LUMP_END); + CacheLump(OPTIONS_LUMP_START,OPTIONS_LUMP_END); + DrawMainMenu(); + StartCPMusic (MENUSONG); + MenuFadeIn(); + } + #endif + #endif + + switch(which) + { + case viewscores: + if (MainMenu[viewscores].routine == NULL) + if (CP_EndGame()) + StartGame=1; + + DrawMainMenu(); + MenuFadeIn(); + break; + + case backtodemo: + #ifdef SPEAR + if (!ingame) + { + // + // DEALLOCATE ALL SOUNDS! + // + switch (SoundMode) + { + case sdm_PC: + start = STARTPCSOUNDS; + break; + case sdm_AdLib: + start = STARTADLIBSOUNDS; + break; + } + + if (SoundMode != sdm_Off) + for (i=0;i"); + while (!Keyboard[sc_Escape]) + IN_ClearKeysDown(); + + SD_MusicOn(); + VL_SetVGAPlaneMode (); + VL_TestPaletteSet (); + VL_SetPalette (&gamepal); + LoadLatchMem(); +} +#endif +#endif + +//////////////////////////////////////////////////////////////////// +// +// CHECK QUICK-KEYS & QUIT (WHILE IN A GAME) +// +//////////////////////////////////////////////////////////////////// +int CP_CheckQuick(unsigned scancode) +{ + switch(scancode) + { + // + // END GAME + // + case sc_F7: + CA_CacheGrChunk(STARTFONT+1); + + WindowH=160; + #ifdef JAPAN + if (GetYorN(7,8,C_JAPQUITPIC)) + #else + if (Confirm(ENDGAMESTR)) + #endif + { + playstate = ex_died; + pickquick = gamestate.lives = 0; + } + + DrawAllPlayBorder(); + WindowH=200; + fontnumber=0; + MainMenu[savegame].active = 0; + return 1; + + // + // QUICKSAVE + // + case sc_F8: + if (SaveGamesAvail[LSItems.curpos] && pickquick) + { + CA_CacheGrChunk(STARTFONT+1); + fontnumber = 1; + Message(STR_SAVING"..."); + CP_SaveGame(1); + fontnumber=0; + } + else + { + #ifndef SPEAR + CA_CacheGrChunk(STARTFONT+1); + CA_CacheGrChunk(C_CURSOR1PIC); + CA_CacheGrChunk(C_CURSOR2PIC); + CA_CacheGrChunk(C_DISKLOADING1PIC); + CA_CacheGrChunk(C_DISKLOADING2PIC); + CA_CacheGrChunk(C_SAVEGAMEPIC); + CA_CacheGrChunk(C_MOUSELBACKPIC); + #else + CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); + CA_CacheGrChunk(C_CURSOR1PIC); + #endif + + VW_FadeOut (); + + StartCPMusic(MENUSONG); + pickquick=CP_SaveGame(0); + + SETFONTCOLOR(0,15); + IN_ClearKeysDown(); + DrawPlayScreen (); + + if (!startgame && !loadedgame) + { + VW_FadeIn (); + StartMusic (); + } + + if (loadedgame) + playstate = ex_abort; + lasttimecount = TimeCount; + + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + + PM_CheckMainMem (); + + #ifndef SPEAR + UNCACHEGRCHUNK(C_CURSOR1PIC); + UNCACHEGRCHUNK(C_CURSOR2PIC); + UNCACHEGRCHUNK(C_DISKLOADING1PIC); + UNCACHEGRCHUNK(C_DISKLOADING2PIC); + UNCACHEGRCHUNK(C_SAVEGAMEPIC); + UNCACHEGRCHUNK(C_MOUSELBACKPIC); + #else + UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); + #endif + } + return 1; + + // + // QUICKLOAD + // + case sc_F9: + if (SaveGamesAvail[LSItems.curpos] && pickquick) + { + char string[100]=STR_LGC; + + + CA_CacheGrChunk(STARTFONT+1); + fontnumber = 1; + + strcat(string,SaveGameNames[LSItems.curpos]); + strcat(string,"\"?"); + + if (Confirm(string)) + CP_LoadGame(1); + + DrawAllPlayBorder(); + fontnumber=0; + } + else + { + #ifndef SPEAR + CA_CacheGrChunk(STARTFONT+1); + CA_CacheGrChunk(C_CURSOR1PIC); + CA_CacheGrChunk(C_CURSOR2PIC); + CA_CacheGrChunk(C_DISKLOADING1PIC); + CA_CacheGrChunk(C_DISKLOADING2PIC); + CA_CacheGrChunk(C_LOADGAMEPIC); + CA_CacheGrChunk(C_MOUSELBACKPIC); + #else + CA_CacheGrChunk(C_CURSOR1PIC); + CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); + #endif + + VW_FadeOut (); + + StartCPMusic(MENUSONG); + pickquick=CP_LoadGame(0); + + SETFONTCOLOR(0,15); + IN_ClearKeysDown(); + DrawPlayScreen (); + + if (!startgame && !loadedgame) + { + VW_FadeIn (); + StartMusic (); + } + + if (loadedgame) + playstate = ex_abort; + + lasttimecount = TimeCount; + + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + PM_CheckMainMem (); + + #ifndef SPEAR + UNCACHEGRCHUNK(C_CURSOR1PIC); + UNCACHEGRCHUNK(C_CURSOR2PIC); + UNCACHEGRCHUNK(C_DISKLOADING1PIC); + UNCACHEGRCHUNK(C_DISKLOADING2PIC); + UNCACHEGRCHUNK(C_LOADGAMEPIC); + UNCACHEGRCHUNK(C_MOUSELBACKPIC); + #else + UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); + #endif + } + return 1; + + // + // QUIT + // + case sc_F10: + CA_CacheGrChunk(STARTFONT+1); + + WindowX=WindowY=0; + WindowW=320; + WindowH=160; + #ifdef JAPAN + if (GetYorN(7,8,C_QUITMSGPIC)) + #else + #ifdef SPANISH + if (Confirm(ENDGAMESTR)) + #else + if (Confirm(endStrings[US_RndT()&0x7+(US_RndT()&1)])) + #endif + #endif + { + int i; + + + VW_UpdateScreen(); + SD_MusicOff(); + SD_StopSound(); + MenuFadeOut(); + + // + // SHUT-UP THE ADLIB + // + for (i=1;i<=0xf5;i++) + alOut(i,0); + Quit(NULL); + } + + DrawAllPlayBorder(); + WindowH=200; + fontnumber=0; + return 1; + } + + return 0; +} + + +//////////////////////////////////////////////////////////////////// +// +// END THE CURRENT GAME +// +//////////////////////////////////////////////////////////////////// +int CP_EndGame(void) +{ +#ifdef JAPAN + if (!GetYorN(7,8,C_JAPQUITPIC)) +#else + if (!Confirm(ENDGAMESTR)) +#endif + return 0; + + pickquick = gamestate.lives = 0; + playstate = ex_died; + + #pragma warn -sus + MainMenu[savegame].active = 0; + MainMenu[viewscores].routine=CP_ViewScores; + #ifndef JAPAN + _fstrcpy(MainMenu[viewscores].string,STR_VS); + #endif + #pragma warn +sus + + return 1; +} + + +//////////////////////////////////////////////////////////////////// +// +// VIEW THE HIGH SCORES +// +//////////////////////////////////////////////////////////////////// +void CP_ViewScores(void) +{ + fontnumber=0; + +#ifdef SPEAR + UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + StartCPMusic (XAWARD_MUS); +#else + StartCPMusic (ROSTER_MUS); +#endif + + DrawHighScores (); + VW_UpdateScreen (); + MenuFadeIn(); + fontnumber=1; + + IN_Ack(); + + StartCPMusic(MENUSONG); + MenuFadeOut(); + +#ifdef SPEAR + CacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif +} + + +//////////////////////////////////////////////////////////////////// +// +// START A NEW GAME +// +//////////////////////////////////////////////////////////////////// +void CP_NewGame(void) +{ + int which,episode; + +#ifdef SPEAR + UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif + + +#ifndef SPEAR +firstpart: + + DrawNewEpisode(); + do + { + which=HandleMenu(&NewEitems,&NewEmenu[0],NULL); + switch(which) + { + case -1: + MenuFadeOut(); + return; + + default: + if (!EpisodeSelect[which/2]) + { + SD_PlaySound (NOWAYSND); + Message("Please select \"Read This!\"\n" + "from the Options menu to\n" + "find out how to order this\n" + "episode from Apogee."); + IN_ClearKeysDown(); + IN_Ack(); + DrawNewEpisode(); + which = 0; + } + else + { + episode = which/2; + which = 1; + } + break; + } + + } while (!which); + + ShootSnd(); + + // + // ALREADY IN A GAME? + // + if (ingame) + #ifdef JAPAN + if (!GetYorN(7,8,C_JAPNEWGAMEPIC)) + #else + if (!Confirm(CURGAME)) + #endif + { + MenuFadeOut(); + return; + } + + MenuFadeOut(); + +#else + episode = 0; + + // + // ALREADY IN A GAME? + // + CacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END); + DrawNewGame(); + if (ingame) + if (!Confirm(CURGAME)) + { + MenuFadeOut(); + UnCacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + return; + } + +#endif + + DrawNewGame(); + which=HandleMenu(&NewItems,&NewMenu[0],DrawNewGameDiff); + if (which<0) + { + MenuFadeOut(); + #ifndef SPEAR + goto firstpart; + #else + UnCacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + return; + #endif + } + + ShootSnd(); + NewGame(which,episode); + StartGame=1; + MenuFadeOut(); + + // + // CHANGE "READ THIS!" TO NORMAL COLOR + // + #ifndef SPEAR + #ifndef GOODTIMES + MainMenu[readthis].active=1; + #endif + #endif + + pickquick = 0; + +#ifdef SPEAR + UnCacheLump (NEWGAME_LUMP_START,NEWGAME_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif +} + + +#ifndef SPEAR +///////////////////// +// +// DRAW NEW EPISODE MENU +// +void DrawNewEpisode(void) +{ + int i; + +#ifdef JAPAN + CA_CacheScreen(S_EPISODEPIC); +#else + ClearMScreen(); + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + + DrawWindow(NE_X-4,NE_Y-4,NE_W+8,NE_H+8,BKGDCOLOR); + SETFONTCOLOR(READHCOLOR,BKGDCOLOR); + PrintY=2; + WindowX=0; + #ifdef SPANISH + US_CPrint("Cual episodio jugar?"); + #else + US_CPrint("Which episode to play?"); + #endif +#endif + + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + DrawMenu(&NewEitems,&NewEmenu[0]); + + for (i=0;i<6;i++) + VWB_DrawPic(NE_X+32,NE_Y+i*26,C_EPISODE1PIC+i); + + VW_UpdateScreen(); + MenuFadeIn(); + WaitKeyUp(); +} +#endif + +///////////////////// +// +// DRAW NEW GAME MENU +// +void DrawNewGame(void) +{ +#ifdef JAPAN + CA_CacheScreen(S_SKILLPIC); +#else + ClearMScreen(); + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + + SETFONTCOLOR(READHCOLOR,BKGDCOLOR); + PrintX=NM_X+20; + PrintY=NM_Y-32; + +#ifndef SPEAR + #ifdef SPANISH + US_Print("Eres macho?"); + #else + US_Print("How tough are you?"); + #endif +#else + VWB_DrawPic (PrintX,PrintY,C_HOWTOUGHPIC); +#endif + + DrawWindow(NM_X-5,NM_Y-10,NM_W,NM_H,BKGDCOLOR); +#endif + + DrawMenu(&NewItems,&NewMenu[0]); + DrawNewGameDiff(NewItems.curpos); + VW_UpdateScreen(); + MenuFadeIn(); + WaitKeyUp(); +} + + +//////////////////////// +// +// DRAW NEW GAME GRAPHIC +// +void DrawNewGameDiff(int w) +{ + VWB_DrawPic(NM_X+185,NM_Y+7,w+C_BABYMODEPIC); +} + + +//////////////////////////////////////////////////////////////////// +// +// HANDLE SOUND MENU +// +//////////////////////////////////////////////////////////////////// +void CP_Sound(void) +{ + int which,i; + + +#ifdef SPEAR + UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + CacheLump (SOUND_LUMP_START,SOUND_LUMP_END); +#endif + + DrawSoundMenu(); + MenuFadeIn(); + WaitKeyUp(); + + do + { + which=HandleMenu(&SndItems,&SndMenu[0],NULL); + // + // HANDLE MENU CHOICES + // + switch(which) + { + // + // SOUND EFFECTS + // + case 0: + if (SoundMode!=sdm_Off) + { + SD_WaitSoundDone(); + SD_SetSoundMode(sdm_Off); + DrawSoundMenu(); + } + break; + case 1: + if (SoundMode!=sdm_PC) + { + SD_WaitSoundDone(); + SD_SetSoundMode(sdm_PC); + CA_LoadAllSounds(); + DrawSoundMenu(); + ShootSnd(); + } + break; + case 2: + if (SoundMode!=sdm_AdLib) + { + SD_WaitSoundDone(); + SD_SetSoundMode(sdm_AdLib); + CA_LoadAllSounds(); + DrawSoundMenu(); + ShootSnd(); + } + break; + + // + // DIGITIZED SOUND + // + case 5: + if (DigiMode!=sds_Off) + { + SD_SetDigiDevice(sds_Off); + DrawSoundMenu(); + } + break; + case 6: + if (DigiMode!=sds_SoundSource) + { + SD_SetDigiDevice(sds_SoundSource); + DrawSoundMenu(); + ShootSnd(); + } + break; + case 7: + if (DigiMode!=sds_SoundBlaster) + { + SD_SetDigiDevice(sds_SoundBlaster); + DrawSoundMenu(); + ShootSnd(); + } + break; + + // + // MUSIC + // + case 10: + if (MusicMode!=smm_Off) + { + SD_SetMusicMode(smm_Off); + DrawSoundMenu(); + ShootSnd(); + } + break; + case 11: + if (MusicMode!=smm_AdLib) + { + SD_SetMusicMode(smm_AdLib); + DrawSoundMenu(); + ShootSnd(); + StartCPMusic(MENUSONG); + } + break; + } + } while(which>=0); + + MenuFadeOut(); + +#ifdef SPEAR + UnCacheLump (SOUND_LUMP_START,SOUND_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif +} + + +////////////////////// +// +// DRAW THE SOUND MENU +// +void DrawSoundMenu(void) +{ + int i,on; + + +#ifdef JAPAN + CA_CacheScreen(S_SOUNDPIC); +#else + // + // DRAW SOUND MENU + // + ClearMScreen(); + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + + DrawWindow(SM_X-8,SM_Y1-3,SM_W,SM_H1,BKGDCOLOR); + DrawWindow(SM_X-8,SM_Y2-3,SM_W,SM_H2,BKGDCOLOR); + DrawWindow(SM_X-8,SM_Y3-3,SM_W,SM_H3,BKGDCOLOR); +#endif + + // + // IF NO ADLIB, NON-CHOOSENESS! + // + if (!AdLibPresent && !SoundBlasterPresent) + { + SndMenu[2].active=SndMenu[10].active=SndMenu[11].active=0; + } + + if (!SoundSourcePresent) + SndMenu[6].active=0; + + if (!SoundBlasterPresent) + SndMenu[7].active=0; + + if (!SoundSourcePresent && !SoundBlasterPresent) + SndMenu[5].active=0; + + DrawMenu(&SndItems,&SndMenu[0]); +#ifndef JAPAN + VWB_DrawPic(100,SM_Y1-20,C_FXTITLEPIC); + VWB_DrawPic(100,SM_Y2-20,C_DIGITITLEPIC); + VWB_DrawPic(100,SM_Y3-20,C_MUSICTITLEPIC); +#endif + + for (i=0;i=0 && SaveGamesAvail[which]) + { + ShootSnd(); + name[7]=which+'0'; + + handle=open(name,O_BINARY); + lseek(handle,32,SEEK_SET); + + DrawLSAction(0); + loadedgame=true; + + LoadTheGame(handle,LSA_X+8,LSA_Y+5); + close(handle); + + StartGame=1; + ShootSnd(); + // + // CHANGE "READ THIS!" TO NORMAL COLOR + // + + #ifndef SPEAR + #ifndef GOODTIMES + MainMenu[readthis].active=1; + #endif + #endif + + exit=1; + break; + } + + } while(which>=0); + + MenuFadeOut(); + +#ifdef SPEAR + UnCacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif + + return exit; +} + + +/////////////////////////////////// +// +// HIGHLIGHT CURRENT SELECTED ENTRY +// +void TrackWhichGame(int w) +{ + static int lastgameon=0; + + PrintLSEntry(lastgameon,TEXTCOLOR); + PrintLSEntry(w,HIGHLIGHT); + + lastgameon=w; +} + + +//////////////////////////// +// +// DRAW THE LOAD/SAVE SCREEN +// +void DrawLoadSaveScreen(int loadsave) +{ + #define DISKX 100 + #define DISKY 0 + + int i; + + + ClearMScreen(); + fontnumber=1; + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + DrawWindow(LSM_X-10,LSM_Y-5,LSM_W,LSM_H,BKGDCOLOR); + DrawStripes(10); + + if (!loadsave) + VWB_DrawPic(60,0,C_LOADGAMEPIC); + else + VWB_DrawPic(60,0,C_SAVEGAMEPIC); + + for (i=0;i<10;i++) + PrintLSEntry(i,TEXTCOLOR); + + DrawMenu(&LSItems,&LSMenu[0]); + VW_UpdateScreen(); + MenuFadeIn(); + WaitKeyUp(); +} + + +/////////////////////////////////////////// +// +// PRINT LOAD/SAVE GAME ENTRY W/BOX OUTLINE +// +void PrintLSEntry(int w,int color) +{ + SETFONTCOLOR(color,BKGDCOLOR); + DrawOutline(LSM_X+LSItems.indent,LSM_Y+w*13,LSM_W-LSItems.indent-15,11,color,color); + PrintX=LSM_X+LSItems.indent+2; + PrintY=LSM_Y+w*13+1; + fontnumber=0; + + if (SaveGamesAvail[w]) + US_Print(SaveGameNames[w]); + else + US_Print(" - "STR_EMPTY" -"); + + fontnumber=1; +} + + +//////////////////////////////////////////////////////////////////// +// +// SAVE CURRENT GAME +// +//////////////////////////////////////////////////////////////////// +int CP_SaveGame(int quick) +{ + int handle,which,exit=0; + unsigned nwritten; + char name[13],input[32]; + + + strcpy(name,SaveName); + + // + // QUICKSAVE? + // + if (quick) + { + which=LSItems.curpos; + + if (SaveGamesAvail[which]) + { + name[7]=which+'0'; + unlink(name); + handle=creat(name,S_IREAD|S_IWRITE); + + strcpy(input,&SaveGameNames[which][0]); + + _dos_write(handle,(void far *)input,32,&nwritten); + lseek(handle,32,SEEK_SET); + SaveTheGame(handle,0,0); + close(handle); + + return 1; + } + } + + +#ifdef SPEAR + UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + CacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END); +#endif + + DrawLoadSaveScreen(1); + + do + { + which=HandleMenu(&LSItems,&LSMenu[0],TrackWhichGame); + if (which>=0) + { + // + // OVERWRITE EXISTING SAVEGAME? + // + if (SaveGamesAvail[which]) + #ifdef JAPAN + if (!GetYorN(7,8,C_JAPSAVEOVERPIC)) + #else + if (!Confirm(GAMESVD)) + #endif + { + DrawLoadSaveScreen(1); + continue; + } + else + { + DrawLoadSaveScreen(1); + PrintLSEntry(which,HIGHLIGHT); + VW_UpdateScreen(); + } + + ShootSnd(); + + strcpy(input,&SaveGameNames[which][0]); + name[7]=which+'0'; + + fontnumber=0; + if (!SaveGamesAvail[which]) + VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*13+1,LSM_W-LSItems.indent-16,10,BKGDCOLOR); + VW_UpdateScreen(); + + if (US_LineInput(LSM_X+LSItems.indent+2,LSM_Y+which*13+1,input,input,true,31,LSM_W-LSItems.indent-30)) + { + SaveGamesAvail[which]=1; + strcpy(&SaveGameNames[which][0],input); + + unlink(name); + handle=creat(name,S_IREAD|S_IWRITE); + _dos_write(handle,(void far *)input,32,&nwritten); + lseek(handle,32,SEEK_SET); + + DrawLSAction(1); + SaveTheGame(handle,LSA_X+8,LSA_Y+5); + + close(handle); + + ShootSnd(); + exit=1; + } + else + { + VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*13+1,LSM_W-LSItems.indent-16,10,BKGDCOLOR); + PrintLSEntry(which,HIGHLIGHT); + VW_UpdateScreen(); + SD_PlaySound(ESCPRESSEDSND); + continue; + } + + fontnumber=1; + break; + } + + } while(which>=0); + + MenuFadeOut(); + +#ifdef SPEAR + UnCacheLump (LOADSAVE_LUMP_START,LOADSAVE_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif + + return exit; +} + + +//////////////////////////////////////////////////////////////////// +// +// CALIBRATE JOYSTICK +// +//////////////////////////////////////////////////////////////////// +int CalibrateJoystick(void) +{ + #define CALX 85 + #define CALY 40 + #define CALW 158 + #define CALH 140 + + unsigned xmin,ymin,xmax,ymax,jb; + + + + #ifdef JAPAN + VWB_DrawPic(CALX,CALY,C_JOY0PIC); + #else + DrawWindow(CALX-5,CALY-5,CALW,CALH,TEXTCOLOR); + DrawOutline(CALX-5,CALY-5,CALW,CALH,0,HIGHLIGHT); + SETFONTCOLOR(0,TEXTCOLOR); + + WindowX = PrintX = CALX; + WindowW = CALW; + WindowH = CALH; + WindowY = PrintY = CALY; + US_Print(" "STR_CALIB"\n "STR_JOYST"\n"); + VWB_DrawPic(CALX+40,CALY+30,C_JOY1PIC); + PrintY = CALY+80; + US_Print(STR_MOVEJOY); + SETFONTCOLOR(BKGDCOLOR,TEXTCOLOR); + US_Print(" "STR_ESCEXIT); + #endif + VW_UpdateScreen(); + + do + { + jb=IN_JoyButtons(); + if (Keyboard[sc_Escape]) + return 0; + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + PicturePause(); + #endif + + } while(!(jb&1)); + + SD_PlaySound(SHOOTSND); + IN_GetJoyAbs(joystickport,&xmin,&ymin); + + + #ifdef JAPAN + VWB_DrawPic(CALX,CALY,C_JOY1PIC); + #else + DrawWindow(CALX-5,CALY-5,CALW,CALH,TEXTCOLOR); + DrawOutline(CALX-5,CALY-5,CALW,CALH,0,HIGHLIGHT); + SETFONTCOLOR(0,TEXTCOLOR); + + PrintX = CALX; + PrintY = CALY; + US_Print(" "STR_CALIB"\n "STR_JOYST"\n"); + VWB_DrawPic(CALX+40,CALY+30,C_JOY2PIC); + PrintY = CALY+80; + US_Print(STR_MOVEJOY2); + SETFONTCOLOR(BKGDCOLOR,TEXTCOLOR); + US_Print(" "STR_ESCEXIT); + #endif + VW_UpdateScreen(); + + do + { + jb=IN_JoyButtons(); + if (Keyboard[sc_Escape]) + return 0; + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + PicturePause(); + #endif + } while(!(jb&2)); + + IN_GetJoyAbs(joystickport,&xmax,&ymax); + SD_PlaySound(SHOOTSND); + + while (IN_JoyButtons()); + + // + // ASSIGN ACTUAL VALUES HERE + // + if ((xmin != xmax) && (ymin != ymax)) + IN_SetupJoy(joystickport,xmin,xmax,ymin,ymax); + else + return 0; + + return 1; +} + + +//////////////////////////////////////////////////////////////////// +// +// DEFINE CONTROLS +// +//////////////////////////////////////////////////////////////////// +void CP_Control(void) +{ + #define CTL_SPC 70 + enum {MOUSEENABLE,JOYENABLE,USEPORT2,PADENABLE,MOUSESENS,CUSTOMIZE}; + int i,which; + + +#ifdef SPEAR + UnCacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); + CacheLump (CONTROL_LUMP_START,CONTROL_LUMP_END); +#endif + + DrawCtlScreen(); + MenuFadeIn(); + WaitKeyUp(); + + do + { + which=HandleMenu(&CtlItems,&CtlMenu[0],NULL); + switch(which) + { + case MOUSEENABLE: + mouseenabled^=1; + _CX=_DX=CENTER; + Mouse(4); + DrawCtlScreen(); + CusItems.curpos=-1; + ShootSnd(); + break; + + case JOYENABLE: + joystickenabled^=1; + if (joystickenabled) + if (!CalibrateJoystick()) + joystickenabled = 0; + DrawCtlScreen(); + CusItems.curpos=-1; + ShootSnd(); + break; + + case USEPORT2: + joystickport^=1; + DrawCtlScreen(); + ShootSnd(); + break; + + case PADENABLE: + joypadenabled^=1; + DrawCtlScreen(); + ShootSnd(); + break; + + case MOUSESENS: + case CUSTOMIZE: + DrawCtlScreen(); + MenuFadeIn(); + WaitKeyUp(); + break; + } + } while(which>=0); + + MenuFadeOut(); + +#ifdef SPEAR + UnCacheLump (CONTROL_LUMP_START,CONTROL_LUMP_END); + CacheLump (OPTIONS_LUMP_START,OPTIONS_LUMP_END); +#endif +} + + +//////////////////////////////// +// +// DRAW MOUSE SENSITIVITY SCREEN +// +void DrawMouseSens(void) +{ +#ifdef JAPAN + CA_CacheScreen(S_MOUSESENSPIC); +#else + ClearMScreen(); + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + #ifdef SPANISH + DrawWindow(10,80,300,43,BKGDCOLOR); + #else + DrawWindow(10,80,300,30,BKGDCOLOR); + #endif + + WindowX=0; + WindowW=320; + PrintY=82; + SETFONTCOLOR(READCOLOR,BKGDCOLOR); + US_CPrint(STR_MOUSEADJ); + + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + #ifdef SPANISH + PrintX=14; + PrintY=95+13; + US_Print(STR_SLOW); + PrintX=252; + US_Print(STR_FAST); + #else + PrintX=14; + PrintY=95; + US_Print(STR_SLOW); + PrintX=269; + US_Print(STR_FAST); + #endif +#endif + + VWB_Bar(60,97,200,10,TEXTCOLOR); + DrawOutline(60,97,200,10,0,HIGHLIGHT); + DrawOutline(60+20*mouseadjustment,97,20,10,0,READCOLOR); + VWB_Bar(61+20*mouseadjustment,98,19,9,READHCOLOR); + + VW_UpdateScreen(); + MenuFadeIn(); +} + + +/////////////////////////// +// +// ADJUST MOUSE SENSITIVITY +// +void MouseSensitivity(void) +{ + ControlInfo ci; + int exit=0,oldMA; + + + oldMA=mouseadjustment; + DrawMouseSens(); + do + { + ReadAnyControl(&ci); + switch(ci.dir) + { + case dir_North: + case dir_West: + if (mouseadjustment) + { + mouseadjustment--; + VWB_Bar(60,97,200,10,TEXTCOLOR); + DrawOutline(60,97,200,10,0,HIGHLIGHT); + DrawOutline(60+20*mouseadjustment,97,20,10,0,READCOLOR); + VWB_Bar(61+20*mouseadjustment,98,19,9,READHCOLOR); + VW_UpdateScreen(); + SD_PlaySound(MOVEGUN1SND); + while(Keyboard[sc_LeftArrow]); + WaitKeyUp(); + } + break; + + case dir_South: + case dir_East: + if (mouseadjustment<9) + { + mouseadjustment++; + VWB_Bar(60,97,200,10,TEXTCOLOR); + DrawOutline(60,97,200,10,0,HIGHLIGHT); + DrawOutline(60+20*mouseadjustment,97,20,10,0,READCOLOR); + VWB_Bar(61+20*mouseadjustment,98,19,9,READHCOLOR); + VW_UpdateScreen(); + SD_PlaySound(MOVEGUN1SND); + while(Keyboard[sc_RightArrow]); + WaitKeyUp(); + } + break; + } + + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + #else + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("debugmode")) + #endif + PicturePause(); + + if (ci.button0 || Keyboard[sc_Space] || Keyboard[sc_Enter]) + exit=1; + else + if (ci.button1 || Keyboard[sc_Escape]) + exit=2; + + } while(!exit); + + if (exit==2) + { + mouseadjustment=oldMA; + SD_PlaySound(ESCPRESSEDSND); + } + else + SD_PlaySound(SHOOTSND); + + WaitKeyUp(); + MenuFadeOut(); +} + + +/////////////////////////// +// +// DRAW CONTROL MENU SCREEN +// +void DrawCtlScreen(void) +{ + int i,x,y; + + +#ifdef JAPAN + CA_CacheScreen(S_CONTROLPIC); +#else + ClearMScreen(); + DrawStripes(10); + VWB_DrawPic(80,0,C_CONTROLPIC); + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + DrawWindow(CTL_X-8,CTL_Y-5,CTL_W,CTL_H,BKGDCOLOR); +#endif + WindowX=0; + WindowW=320; + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + + if (JoysPresent[0]) + CtlMenu[1].active= + CtlMenu[2].active= + CtlMenu[3].active=1; + + CtlMenu[2].active=CtlMenu[3].active=joystickenabled; + + if (MousePresent) + { + CtlMenu[4].active= + CtlMenu[0].active=1; + } + + CtlMenu[4].active=mouseenabled; + + + DrawMenu(&CtlItems,&CtlMenu[0]); + + + x=CTL_X+CtlItems.indent-24; + y=CTL_Y+3; + if (mouseenabled) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + y=CTL_Y+16; + if (joystickenabled) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + y=CTL_Y+29; + if (joystickport) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + y=CTL_Y+42; + if (joypadenabled) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + // + // PICK FIRST AVAILABLE SPOT + // + if (CtlItems.curpos<0 || !CtlMenu[CtlItems.curpos].active) + for (i=0;i<6;i++) + if (CtlMenu[i].active) + { + CtlItems.curpos=i; + break; + } + + DrawMenuGun(&CtlItems); + VW_UpdateScreen(); +} + + +//////////////////////////////////////////////////////////////////// +// +// CUSTOMIZE CONTROLS +// +//////////////////////////////////////////////////////////////////// +enum {FIRE,STRAFE,RUN,OPEN}; +char mbarray[4][3]={"b0","b1","b2","b3"}, + order[4]={RUN,OPEN,FIRE,STRAFE}; + + +void CustomControls(void) +{ + int which; + + + DrawCustomScreen(); + do + { + which=HandleMenu(&CusItems,&CusMenu[0],FixupCustom); + switch(which) + { + case 0: + DefineMouseBtns(); + DrawCustMouse(1); + break; + case 3: + DefineJoyBtns(); + DrawCustJoy(0); + break; + case 6: + DefineKeyBtns(); + DrawCustKeybd(0); + break; + case 8: + DefineKeyMove(); + DrawCustKeys(0); + } + } while(which>=0); + + + + MenuFadeOut(); +} + + +//////////////////////// +// +// DEFINE THE MOUSE BUTTONS +// +void DefineMouseBtns(void) +{ + CustomCtrls mouseallowed={0,1,1,1}; + EnterCtrlData(2,&mouseallowed,DrawCustMouse,PrintCustMouse,MOUSE); +} + + +//////////////////////// +// +// DEFINE THE JOYSTICK BUTTONS +// +void DefineJoyBtns(void) +{ + CustomCtrls joyallowed={1,1,1,1}; + EnterCtrlData(5,&joyallowed,DrawCustJoy,PrintCustJoy,JOYSTICK); +} + + +//////////////////////// +// +// DEFINE THE KEYBOARD BUTTONS +// +void DefineKeyBtns(void) +{ + CustomCtrls keyallowed={1,1,1,1}; + EnterCtrlData(8,&keyallowed,DrawCustKeybd,PrintCustKeybd,KEYBOARDBTNS); +} + + +//////////////////////// +// +// DEFINE THE KEYBOARD BUTTONS +// +void DefineKeyMove(void) +{ + CustomCtrls keyallowed={1,1,1,1}; + EnterCtrlData(10,&keyallowed,DrawCustKeys,PrintCustKeys,KEYBOARDMOVE); +} + + +//////////////////////// +// +// ENTER CONTROL DATA FOR ANY TYPE OF CONTROL +// +enum {FWRD,RIGHT,BKWD,LEFT}; +int moveorder[4]={LEFT,RIGHT,FWRD,BKWD}; + +void EnterCtrlData(int index,CustomCtrls *cust,void (*DrawRtn)(int),void (*PrintRtn)(int),int type) +{ + int j,exit,tick,redraw,which,x,picked; + ControlInfo ci; + + + ShootSnd(); + PrintY=CST_Y+13*index; + IN_ClearKeysDown(); + exit=0; + redraw=1; + // + // FIND FIRST SPOT IN ALLOWED ARRAY + // + for (j=0;j<4;j++) + if (cust->allowed[j]) + { + which=j; + break; + } + + do + { + if (redraw) + { + x=CST_START+CST_SPC*which; + DrawWindow(5,PrintY-1,310,13,BKGDCOLOR); + + DrawRtn(1); + DrawWindow(x-2,PrintY,CST_SPC,11,TEXTCOLOR); + DrawOutline(x-2,PrintY,CST_SPC,11,0,HIGHLIGHT); + SETFONTCOLOR(0,TEXTCOLOR); + PrintRtn(which); + PrintX=x; + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + VW_UpdateScreen(); + WaitKeyUp(); + redraw=0; + } + + ReadAnyControl(&ci); + + if (type==MOUSE || type==JOYSTICK) + if (IN_KeyDown(sc_Enter)||IN_KeyDown(sc_Control)||IN_KeyDown(sc_Alt)) + { + IN_ClearKeysDown(); + ci.button0=ci.button1=false; + } + + // + // CHANGE BUTTON VALUE? + // + if ((ci.button0|ci.button1|ci.button2|ci.button3)|| + ((type==KEYBOARDBTNS||type==KEYBOARDMOVE) && LastScan==sc_Enter)) + { + tick=TimeCount=picked=0; + SETFONTCOLOR(0,TEXTCOLOR); + + do + { + int button,result=0; + + + if (type==KEYBOARDBTNS||type==KEYBOARDMOVE) + IN_ClearKeysDown(); + + // + // FLASH CURSOR + // + if (TimeCount>10) + { + switch(tick) + { + case 0: + VWB_Bar(x,PrintY+1,CST_SPC-2,10,TEXTCOLOR); + break; + case 1: + PrintX=x; + US_Print("?"); + SD_PlaySound(HITWALLSND); + } + tick^=1; + TimeCount=0; + VW_UpdateScreen(); + } + + // + // WHICH TYPE OF INPUT DO WE PROCESS? + // + switch(type) + { + case MOUSE: + Mouse(3); + button=_BX; + switch(button) + { + case 1: result=1; break; + case 2: result=2; break; + case 4: result=3; break; + } + + if (result) + { + int z; + + + for (z=0;z<4;z++) + if (order[which]==buttonmouse[z]) + { + buttonmouse[z]=bt_nobutton; + break; + } + + buttonmouse[result-1]=order[which]; + picked=1; + SD_PlaySound(SHOOTDOORSND); + } + break; + + case JOYSTICK: + if (ci.button0) result=1; + else + if (ci.button1) result=2; + else + if (ci.button2) result=3; + else + if (ci.button3) result=4; + + if (result) + { + int z; + + + for (z=0;z<4;z++) + if (order[which]==buttonjoy[z]) + { + buttonjoy[z]=bt_nobutton; + break; + } + + buttonjoy[result-1]=order[which]; + picked=1; + SD_PlaySound(SHOOTDOORSND); + } + break; + + case KEYBOARDBTNS: + if (LastScan) + { + buttonscan[order[which]]=LastScan; + picked=1; + ShootSnd(); + IN_ClearKeysDown(); + } + break; + + case KEYBOARDMOVE: + if (LastScan) + { + dirscan[moveorder[which]]=LastScan; + picked=1; + ShootSnd(); + IN_ClearKeysDown(); + } + break; + } + + // + // EXIT INPUT? + // + if (IN_KeyDown(sc_Escape)) + { + picked=1; + continue; + } + + } while(!picked); + + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + redraw=1; + WaitKeyUp(); + continue; + } + + if (ci.button1 || IN_KeyDown(sc_Escape)) + exit=1; + + // + // MOVE TO ANOTHER SPOT? + // + switch(ci.dir) + { + case dir_West: + do + { + which--; + if (which<0) + which=3; + } while(!cust->allowed[which]); + redraw=1; + SD_PlaySound(MOVEGUN1SND); + while(ReadAnyControl(&ci),ci.dir!=dir_None); + IN_ClearKeysDown(); + break; + + case dir_East: + do + { + which++; + if (which>3) + which=0; + } while(!cust->allowed[which]); + redraw=1; + SD_PlaySound(MOVEGUN1SND); + while(ReadAnyControl(&ci),ci.dir!=dir_None); + IN_ClearKeysDown(); + break; + case dir_North: + case dir_South: + exit=1; + } + } while(!exit); + + SD_PlaySound(ESCPRESSEDSND); + WaitKeyUp(); + DrawWindow(5,PrintY-1,310,13,BKGDCOLOR); +} + + +//////////////////////// +// +// FIXUP GUN CURSOR OVERDRAW SHIT +// +void FixupCustom(int w) +{ + static int lastwhich=-1; + int y=CST_Y+26+w*13; + + + VWB_Hlin(7,32,y-1,DEACTIVE); + VWB_Hlin(7,32,y+12,BORD2COLOR); +#ifndef SPEAR + VWB_Hlin(7,32,y-2,BORDCOLOR); + VWB_Hlin(7,32,y+13,BORDCOLOR); +#else + VWB_Hlin(7,32,y-2,BORD2COLOR); + VWB_Hlin(7,32,y+13,BORD2COLOR); +#endif + + switch(w) + { + case 0: DrawCustMouse(1); break; + case 3: DrawCustJoy(1); break; + case 6: DrawCustKeybd(1); break; + case 8: DrawCustKeys(1); + } + + + if (lastwhich>=0) + { + y=CST_Y+26+lastwhich*13; + VWB_Hlin(7,32,y-1,DEACTIVE); + VWB_Hlin(7,32,y+12,BORD2COLOR); +#ifndef SPEAR + VWB_Hlin(7,32,y-2,BORDCOLOR); + VWB_Hlin(7,32,y+13,BORDCOLOR); +#else + VWB_Hlin(7,32,y-2,BORD2COLOR); + VWB_Hlin(7,32,y+13,BORD2COLOR); +#endif + + if (lastwhich!=w) + switch(lastwhich) + { + case 0: DrawCustMouse(0); break; + case 3: DrawCustJoy(0); break; + case 6: DrawCustKeybd(0); break; + case 8: DrawCustKeys(0); + } + } + + lastwhich=w; +} + + +//////////////////////// +// +// DRAW CUSTOMIZE SCREEN +// +void DrawCustomScreen(void) +{ + int i; + + +#ifdef JAPAN + CA_CacheScreen(S_CUSTOMPIC); + fontnumber=1; + + PrintX=CST_START; + PrintY = CST_Y+26; + DrawCustMouse(0); + + PrintX=CST_START; + US_Print("\n\n\n"); + DrawCustJoy(0); + + PrintX=CST_START; + US_Print("\n\n\n"); + DrawCustKeybd(0); + + PrintX=CST_START; + US_Print("\n\n\n"); + DrawCustKeys(0); +#else + ClearMScreen(); + WindowX=0; + WindowW=320; + VWB_DrawPic(112,184,C_MOUSELBACKPIC); + DrawStripes(10); + VWB_DrawPic(80,0,C_CUSTOMIZEPIC); + + // + // MOUSE + // + SETFONTCOLOR(READCOLOR,BKGDCOLOR); + WindowX=0; + WindowW=320; + +#ifndef SPEAR + PrintY=CST_Y; + US_CPrint("Mouse\n"); +#else + PrintY = CST_Y+13; + VWB_DrawPic (128,48,C_MOUSEPIC); +#endif + + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + #ifdef SPANISH + PrintX=CST_START-16; + US_Print(STR_CRUN); + PrintX=CST_START-16+CST_SPC*1; + US_Print(STR_COPEN); + PrintX=CST_START-16+CST_SPC*2; + US_Print(STR_CFIRE); + PrintX=CST_START-16+CST_SPC*3; + US_Print(STR_CSTRAFE"\n"); + #else + PrintX=CST_START; + US_Print(STR_CRUN); + PrintX=CST_START+CST_SPC*1; + US_Print(STR_COPEN); + PrintX=CST_START+CST_SPC*2; + US_Print(STR_CFIRE); + PrintX=CST_START+CST_SPC*3; + US_Print(STR_CSTRAFE"\n"); + #endif + + DrawWindow(5,PrintY-1,310,13,BKGDCOLOR); + DrawCustMouse(0); + US_Print("\n"); + + + // + // JOYSTICK/PAD + // +#ifndef SPEAR + SETFONTCOLOR(READCOLOR,BKGDCOLOR); + US_CPrint("Joystick/Gravis GamePad\n"); +#else + PrintY += 13; + VWB_DrawPic (40,88,C_JOYSTICKPIC); +#endif + +#ifdef SPEAR + VWB_DrawPic (112,120,C_KEYBOARDPIC); +#endif + + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + #ifdef SPANISH + PrintX=CST_START-16; + US_Print(STR_CRUN); + PrintX=CST_START-16+CST_SPC*1; + US_Print(STR_COPEN); + PrintX=CST_START-16+CST_SPC*2; + US_Print(STR_CFIRE); + PrintX=CST_START-16+CST_SPC*3; + US_Print(STR_CSTRAFE"\n"); + #else + PrintX=CST_START; + US_Print(STR_CRUN); + PrintX=CST_START+CST_SPC*1; + US_Print(STR_COPEN); + PrintX=CST_START+CST_SPC*2; + US_Print(STR_CFIRE); + PrintX=CST_START+CST_SPC*3; + US_Print(STR_CSTRAFE"\n"); + #endif + DrawWindow(5,PrintY-1,310,13,BKGDCOLOR); + DrawCustJoy(0); + US_Print("\n"); + + + // + // KEYBOARD + // +#ifndef SPEAR + SETFONTCOLOR(READCOLOR,BKGDCOLOR); + US_CPrint("Keyboard\n"); +#else + PrintY += 13; +#endif + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + #ifdef SPANISH + PrintX=CST_START-16; + US_Print(STR_CRUN); + PrintX=CST_START-16+CST_SPC*1; + US_Print(STR_COPEN); + PrintX=CST_START-16+CST_SPC*2; + US_Print(STR_CFIRE); + PrintX=CST_START-16+CST_SPC*3; + US_Print(STR_CSTRAFE"\n"); + #else + PrintX=CST_START; + US_Print(STR_CRUN); + PrintX=CST_START+CST_SPC*1; + US_Print(STR_COPEN); + PrintX=CST_START+CST_SPC*2; + US_Print(STR_CFIRE); + PrintX=CST_START+CST_SPC*3; + US_Print(STR_CSTRAFE"\n"); + #endif + DrawWindow(5,PrintY-1,310,13,BKGDCOLOR); + DrawCustKeybd(0); + US_Print("\n"); + + + // + // KEYBOARD MOVE KEYS + // + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + #ifdef SPANISH + PrintX=4; + US_Print(STR_LEFT); + US_Print("/"); + US_Print(STR_RIGHT); + US_Print("/"); + US_Print(STR_FRWD); + US_Print("/"); + US_Print(STR_BKWD"\n"); + #else + PrintX=CST_START; + US_Print(STR_LEFT); + PrintX=CST_START+CST_SPC*1; + US_Print(STR_RIGHT); + PrintX=CST_START+CST_SPC*2; + US_Print(STR_FRWD); + PrintX=CST_START+CST_SPC*3; + US_Print(STR_BKWD"\n"); + #endif + DrawWindow(5,PrintY-1,310,13,BKGDCOLOR); + DrawCustKeys(0); +#endif + // + // PICK STARTING POINT IN MENU + // + if (CusItems.curpos<0) + for (i=0;i19) + newview=19; + ShowViewSize(newview); + VW_UpdateScreen(); + SD_PlaySound(HITWALLSND); + TicDelay(10); + break; + } + + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + #else + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("debugmode")) + #endif + PicturePause(); + + if (ci.button0 || Keyboard[sc_Enter]) + exit=1; + else + if (ci.button1 || Keyboard[sc_Escape]) + { + viewwidth=oldview*16; + SD_PlaySound(ESCPRESSEDSND); + MenuFadeOut(); + return; + } + + } while(!exit); + + + if (oldview!=newview) + { + SD_PlaySound (SHOOTSND); + Message(STR_THINK"..."); + NewViewSize(newview); + } + + ShootSnd(); + MenuFadeOut(); +} + + +///////////////////////////// +// +// DRAW THE CHANGEVIEW SCREEN +// +void DrawChangeView(int view) +{ +#ifdef JAPAN + CA_CacheScreen(S_CHANGEPIC); + + ShowViewSize(view); +#else + VWB_Bar(0,160,320,40,VIEWCOLOR); + ShowViewSize(view); + + PrintY=161; + WindowX=0; + WindowY=320; + SETFONTCOLOR(HIGHLIGHT,BKGDCOLOR); + + US_CPrint(STR_SIZE1"\n"); + US_CPrint(STR_SIZE2"\n"); + US_CPrint(STR_SIZE3); +#endif + VW_UpdateScreen(); + + MenuFadeIn(); +} + + +//////////////////////////////////////////////////////////////////// +// +// QUIT THIS INFERNAL GAME! +// +//////////////////////////////////////////////////////////////////// +void CP_Quit(void) +{ + int i; + + + #ifdef JAPAN + if (GetYorN(7,11,C_QUITMSGPIC)) + #else + + #ifdef SPANISH + if (Confirm(ENDGAMESTR)) + #else + if (Confirm(endStrings[US_RndT()&0x7+(US_RndT()&1)])) + #endif + + #endif + { + VW_UpdateScreen(); + SD_MusicOff(); + SD_StopSound(); + MenuFadeOut(); + // + // SHUT-UP THE ADLIB + // + for (i=1;i<=0xf5;i++) + alOut(i,0); + Quit(NULL); + } + + DrawMainMenu(); +} + + +//////////////////////////////////////////////////////////////////// +// +// HANDLE INTRO SCREEN (SYSTEM CONFIG) +// +//////////////////////////////////////////////////////////////////// +void IntroScreen(void) +{ +#ifdef SPEAR + +#define MAINCOLOR 0x4f +#define EMSCOLOR 0x4f +#define XMSCOLOR 0x4f + +#else + +#define MAINCOLOR 0x6c +#define EMSCOLOR 0x6c +#define XMSCOLOR 0x6c + +#endif +#define FILLCOLOR 14 + + long memory,emshere,xmshere; + int i,num,ems[10]={100,200,300,400,500,600,700,800,900,1000}, + xms[10]={100,200,300,400,500,600,700,800,900,1000}, + main[10]={32,64,96,128,160,192,224,256,288,320}; + + + // + // DRAW MAIN MEMORY + // + memory=(1023l+mminfo.nearheap+mminfo.farheap)/1024l; + for (i=0;i<10;i++) + if (memory>=main[i]) + VWB_Bar(49,163-8*i,6,5,MAINCOLOR-i); + + + // + // DRAW EMS MEMORY + // + if (EMSPresent) + { + emshere=4l*EMSPagesAvail; + for (i=0;i<10;i++) + if (emshere>=ems[i]) + VWB_Bar(89,163-8*i,6,5,EMSCOLOR-i); + } + + // + // DRAW XMS MEMORY + // + if (XMSPresent) + { + xmshere=4l*XMSPagesAvail; + for (i=0;i<10;i++) + if (xmshere>=xms[i]) + VWB_Bar(129,163-8*i,6,5,XMSCOLOR-i); + } + + // + // FILL BOXES + // + if (MousePresent) + VWB_Bar(164,82,12,2,FILLCOLOR); + + if (JoysPresent[0] || JoysPresent[1]) + VWB_Bar(164,105,12,2,FILLCOLOR); + + if (AdLibPresent && !SoundBlasterPresent) + VWB_Bar(164,128,12,2,FILLCOLOR); + + if (SoundBlasterPresent) + VWB_Bar(164,151,12,2,FILLCOLOR); + + if (SoundSourcePresent) + VWB_Bar(164,174,12,2,FILLCOLOR); +} + + +//////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// +// SUPPORT ROUTINES +// +//////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// +// Clear Menu screens to dark red +// +//////////////////////////////////////////////////////////////////// +void ClearMScreen(void) +{ +#ifndef SPEAR + VWB_Bar(0,0,320,200,BORDCOLOR); +#else + VWB_DrawPic(0,0,C_BACKDROPPIC); +#endif +} + + +//////////////////////////////////////////////////////////////////// +// +// Un/Cache a LUMP of graphics +// +//////////////////////////////////////////////////////////////////// +void CacheLump(int lumpstart,int lumpend) +{ + int i; + + for (i=lumpstart;i<=lumpend;i++) + CA_CacheGrChunk(i); +} + + +void UnCacheLump(int lumpstart,int lumpend) +{ + int i; + + for (i=lumpstart;i<=lumpend;i++) + if (grsegs[i]) + UNCACHEGRCHUNK(i); +} + + +//////////////////////////////////////////////////////////////////// +// +// Draw a window for a menu +// +//////////////////////////////////////////////////////////////////// +void DrawWindow(int x,int y,int w,int h,int wcolor) +{ + VWB_Bar(x,y,w,h,wcolor); + DrawOutline(x,y,w,h,BORD2COLOR,DEACTIVE); +} + + +void DrawOutline(int x,int y,int w,int h,int color1,int color2) +{ + VWB_Hlin(x,x+w,y,color2); + VWB_Vlin(y,y+h,x,color2); + VWB_Hlin(x,x+w,y+h,color1); + VWB_Vlin(y,y+h,x+w,color1); +} + + +//////////////////////////////////////////////////////////////////// +// +// Setup Control Panel stuff - graphics, etc. +// +//////////////////////////////////////////////////////////////////// +void SetupControlPanel(void) +{ + struct ffblk f; + char name[13]; + int which,i; + + + // + // CACHE GRAPHICS & SOUNDS + // + CA_CacheGrChunk(STARTFONT+1); +#ifndef SPEAR + CacheLump(CONTROLS_LUMP_START,CONTROLS_LUMP_END); +#else + CacheLump(BACKDROP_LUMP_START,BACKDROP_LUMP_END); +#endif + + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + fontnumber=1; + WindowH=200; + + if (!ingame) + CA_LoadAllSounds(); + else + MainMenu[savegame].active=1; + + // + // SEE WHICH SAVE GAME FILES ARE AVAILABLE & READ STRING IN + // + strcpy(name,SaveName); + if (!findfirst(name,&f,0)) + do + { + which=f.ff_name[7]-'0'; + if (which<10) + { + int handle; + char temp[32]; + + SaveGamesAvail[which]=1; + handle=open(f.ff_name,O_BINARY); + read(handle,temp,32); + close(handle); + strcpy(&SaveGameNames[which][0],temp); + } + } while(!findnext(&f)); + + // + // CENTER MOUSE + // + _CX=_DX=CENTER; + Mouse(4); +} + + +//////////////////////////////////////////////////////////////////// +// +// Clean up all the Control Panel stuff +// +//////////////////////////////////////////////////////////////////// +void CleanupControlPanel(void) +{ +#ifndef SPEAR + UnCacheLump(CONTROLS_LUMP_START,CONTROLS_LUMP_END); +#else + UnCacheLump (BACKDROP_LUMP_START,BACKDROP_LUMP_END); +#endif + + fontnumber = 0; +} + + +//////////////////////////////////////////////////////////////////// +// +// Handle moving gun around a menu +// +//////////////////////////////////////////////////////////////////// +int HandleMenu(CP_iteminfo *item_i,CP_itemtype far *items,void (*routine)(int w)) +{ + char key; + static int redrawitem=1,lastitem=-1; + int i,x,y,basey,exit,which,shape,timer; + ControlInfo ci; + + + which=item_i->curpos; + x=item_i->x&-8; + basey=item_i->y-2; + y=basey+which*13; + + VWB_DrawPic(x,y,C_CURSOR1PIC); + SetTextColor(items+which,1); + if (redrawitem) + { + PrintX=item_i->x+item_i->indent; + PrintY=item_i->y+which*13; + US_Print((items+which)->string); + } + // + // CALL CUSTOM ROUTINE IF IT IS NEEDED + // + if (routine) + routine(which); + VW_UpdateScreen(); + + shape=C_CURSOR1PIC; + timer=8; + exit=0; + TimeCount=0; + IN_ClearKeysDown(); + + + do + { + // + // CHANGE GUN SHAPE + // + if (TimeCount>timer) + { + TimeCount=0; + if (shape==C_CURSOR1PIC) + { + shape=C_CURSOR2PIC; + timer=8; + } + else + { + shape=C_CURSOR1PIC; + timer=70; + } + VWB_DrawPic(x,y,shape); + if (routine) + routine(which); + VW_UpdateScreen(); + } + + CheckPause(); + + // + // SEE IF ANY KEYS ARE PRESSED FOR INITIAL CHAR FINDING + // + key=LastASCII; + if (key) + { + int ok=0; + + // + // CHECK FOR SCREEN CAPTURE + // + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + #else + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("debugmode")) + #endif + PicturePause(); + + + if (key>='a') + key-='a'-'A'; + + for (i=which+1;iamount;i++) + if ((items+i)->active && (items+i)->string[0]==key) + { + EraseGun(item_i,items,x,y,which); + which=i; + DrawGun(item_i,items,x,&y,which,basey,routine); + ok=1; + IN_ClearKeysDown(); + break; + } + + // + // DIDN'T FIND A MATCH FIRST TIME THRU. CHECK AGAIN. + // + if (!ok) + { + for (i=0;iactive && (items+i)->string[0]==key) + { + EraseGun(item_i,items,x,y,which); + which=i; + DrawGun(item_i,items,x,&y,which,basey,routine); + IN_ClearKeysDown(); + break; + } + } + } + + // + // GET INPUT + // + ReadAnyControl(&ci); + switch(ci.dir) + { + //////////////////////////////////////////////// + // + // MOVE UP + // + case dir_North: + + EraseGun(item_i,items,x,y,which); + + // + // ANIMATE HALF-STEP + // + if (which && (items+which-1)->active) + { + y-=6; + DrawHalfStep(x,y); + } + + // + // MOVE TO NEXT AVAILABLE SPOT + // + do + { + if (!which) + which=item_i->amount-1; + else + which--; + } while(!(items+which)->active); + + DrawGun(item_i,items,x,&y,which,basey,routine); + // + // WAIT FOR BUTTON-UP OR DELAY NEXT MOVE + // + TicDelay(20); + break; + + //////////////////////////////////////////////// + // + // MOVE DOWN + // + case dir_South: + + EraseGun(item_i,items,x,y,which); + // + // ANIMATE HALF-STEP + // + if (which!=item_i->amount-1 && (items+which+1)->active) + { + y+=6; + DrawHalfStep(x,y); + } + + do + { + if (which==item_i->amount-1) + which=0; + else + which++; + } while(!(items+which)->active); + + DrawGun(item_i,items,x,&y,which,basey,routine); + + // + // WAIT FOR BUTTON-UP OR DELAY NEXT MOVE + // + TicDelay(20); + break; + } + + if (ci.button0 || + Keyboard[sc_Space] || + Keyboard[sc_Enter]) + exit=1; + + if (ci.button1 || + Keyboard[sc_Escape]) + exit=2; + + } while(!exit); + + + IN_ClearKeysDown(); + + // + // ERASE EVERYTHING + // + if (lastitem!=which) + { + VWB_Bar(x-1,y,25,16,BKGDCOLOR); + PrintX=item_i->x+item_i->indent; + PrintY=item_i->y+which*13; + US_Print((items+which)->string); + redrawitem=1; + } + else + redrawitem=0; + + if (routine) + routine(which); + VW_UpdateScreen(); + + item_i->curpos=which; + + lastitem=which; + switch(exit) + { + case 1: + // + // CALL THE ROUTINE + // + if ((items+which)->routine!=NULL) + { + ShootSnd(); + MenuFadeOut(); + (items+which)->routine(0); + } + return which; + + case 2: + SD_PlaySound(ESCPRESSEDSND); + return -1; + } + + return 0; // JUST TO SHUT UP THE ERROR MESSAGES! +} + + +// +// ERASE GUN & DE-HIGHLIGHT STRING +// +void EraseGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int y,int which) +{ + VWB_Bar(x-1,y,25,16,BKGDCOLOR); + SetTextColor(items+which,0); + + PrintX=item_i->x+item_i->indent; + PrintY=item_i->y+which*13; + US_Print((items+which)->string); + VW_UpdateScreen(); +} + + +// +// DRAW HALF STEP OF GUN TO NEXT POSITION +// +void DrawHalfStep(int x,int y) +{ + VWB_DrawPic(x,y,C_CURSOR1PIC); + VW_UpdateScreen(); + SD_PlaySound(MOVEGUN1SND); + TimeCount=0; + while(TimeCount<8); +} + + +// +// DRAW GUN AT NEW POSITION +// +void DrawGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int *y,int which,int basey,void (*routine)(int w)) +{ + VWB_Bar(x-1,*y,25,16,BKGDCOLOR); + *y=basey+which*13; + VWB_DrawPic(x,*y,C_CURSOR1PIC); + SetTextColor(items+which,1); + + PrintX=item_i->x+item_i->indent; + PrintY=item_i->y+which*13; + US_Print((items+which)->string); + + // + // CALL CUSTOM ROUTINE IF IT IS NEEDED + // + if (routine) + routine(which); + VW_UpdateScreen(); + SD_PlaySound(MOVEGUN2SND); +} + +//////////////////////////////////////////////////////////////////// +// +// DELAY FOR AN AMOUNT OF TICS OR UNTIL CONTROLS ARE INACTIVE +// +//////////////////////////////////////////////////////////////////// +void TicDelay(int count) +{ + ControlInfo ci; + + + TimeCount=0; + do + { + ReadAnyControl(&ci); + } while(TimeCountcurpos; + + + WindowX=PrintX=item_i->x+item_i->indent; + WindowY=PrintY=item_i->y; + WindowW=320; + WindowH=200; + + for (i=0;iamount;i++) + { + SetTextColor(items+i,which==i); + + PrintY=item_i->y+i*13; + if ((items+i)->active) + US_Print((items+i)->string); + else + { + SETFONTCOLOR(DEACTIVE,BKGDCOLOR); + US_Print((items+i)->string); + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + } + + US_Print("\n"); + } +} + + +//////////////////////////////////////////////////////////////////// +// +// SET TEXT COLOR (HIGHLIGHT OR NO) +// +//////////////////////////////////////////////////////////////////// +void SetTextColor(CP_itemtype far *items,int hlight) +{ + if (hlight) + {SETFONTCOLOR(color_hlite[items->active],BKGDCOLOR);} + else + {SETFONTCOLOR(color_norml[items->active],BKGDCOLOR);} +} + + +//////////////////////////////////////////////////////////////////// +// +// WAIT FOR CTRLKEY-UP OR BUTTON-UP +// +//////////////////////////////////////////////////////////////////// +void WaitKeyUp(void) +{ + ControlInfo ci; + while(ReadAnyControl(&ci), ci.button0| + ci.button1| + ci.button2| + ci.button3| + Keyboard[sc_Space]| + Keyboard[sc_Enter]| + Keyboard[sc_Escape]); +} + + +//////////////////////////////////////////////////////////////////// +// +// READ KEYBOARD, JOYSTICK AND MOUSE FOR INPUT +// +//////////////////////////////////////////////////////////////////// +void ReadAnyControl(ControlInfo *ci) +{ + int mouseactive=0; + + + IN_ReadControl(0,ci); + + if (mouseenabled) + { + int mousey,mousex; + + + // READ MOUSE MOTION COUNTERS + // RETURN DIRECTION + // HOME MOUSE + // CHECK MOUSE BUTTONS + + Mouse(3); + mousex=_CX; + mousey=_DX; + + if (mouseydir=dir_North; + _CX=_DX=CENTER; + Mouse(4); + mouseactive=1; + } + else + if (mousey>CENTER+SENSITIVE) + { + ci->dir=dir_South; + _CX=_DX=CENTER; + Mouse(4); + mouseactive=1; + } + + if (mousexdir=dir_West; + _CX=_DX=CENTER; + Mouse(4); + mouseactive=1; + } + else + if (mousex>CENTER+SENSITIVE) + { + ci->dir=dir_East; + _CX=_DX=CENTER; + Mouse(4); + mouseactive=1; + } + + if (IN_MouseButtons()) + { + ci->button0=IN_MouseButtons()&1; + ci->button1=IN_MouseButtons()&2; + ci->button2=IN_MouseButtons()&4; + ci->button3=false; + mouseactive=1; + } + } + + if (joystickenabled && !mouseactive) + { + int jx,jy,jb; + + + INL_GetJoyDelta(joystickport,&jx,&jy); + if (jy<-SENSITIVE) + ci->dir=dir_North; + else + if (jy>SENSITIVE) + ci->dir=dir_South; + + if (jx<-SENSITIVE) + ci->dir=dir_West; + else + if (jx>SENSITIVE) + ci->dir=dir_East; + + jb=IN_JoyButtons(); + if (jb) + { + ci->button0=jb&1; + ci->button1=jb&2; + if (joypadenabled) + { + ci->button2=jb&4; + ci->button3=jb&8; + } + else + ci->button2=ci->button3=false; + } + } +} + + +//////////////////////////////////////////////////////////////////// +// +// DRAW DIALOG AND CONFIRM YES OR NO TO QUESTION +// +//////////////////////////////////////////////////////////////////// +int Confirm(char far *string) +{ + int xit=0,i,x,y,tick=0,time,whichsnd[2]={ESCPRESSEDSND,SHOOTSND}; + + + Message(string); + IN_ClearKeysDown(); + + // + // BLINK CURSOR + // + x=PrintX; + y=PrintY; + TimeCount=0; + + do + { + if (TimeCount>=10) + { + switch(tick) + { + case 0: + VWB_Bar(x,y,8,13,TEXTCOLOR); + break; + case 1: + PrintX=x; + PrintY=y; + US_Print("_"); + } + VW_UpdateScreen(); + tick^=1; + TimeCount=0; + } + + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + PicturePause(); + #endif + + #ifdef SPANISH + } while(!Keyboard[sc_S] && !Keyboard[sc_N] && !Keyboard[sc_Escape]); + #else + } while(!Keyboard[sc_Y] && !Keyboard[sc_N] && !Keyboard[sc_Escape]); + #endif + + #ifdef SPANISH + if (Keyboard[sc_S]) + { + xit=1; + ShootSnd(); + } + + while(Keyboard[sc_S] || Keyboard[sc_N] || Keyboard[sc_Escape]); + + #else + + if (Keyboard[sc_Y]) + { + xit=1; + ShootSnd(); + } + + while(Keyboard[sc_Y] || Keyboard[sc_N] || Keyboard[sc_Escape]); + #endif + + IN_ClearKeysDown(); + SD_PlaySound(whichsnd[xit]); + return xit; +} + +#ifdef JAPAN +//////////////////////////////////////////////////////////////////// +// +// DRAW MESSAGE & GET Y OR N +// +//////////////////////////////////////////////////////////////////// +int GetYorN(int x,int y,int pic) +{ + int xit=0,whichsnd[2]={ESCPRESSEDSND,SHOOTSND}; + + + CA_CacheGrChunk(pic); + VWB_DrawPic(x * 8,y * 8,pic); + UNCACHEGRCHUNK(pic); + VW_UpdateScreen(); + IN_ClearKeysDown(); + + do + { + #ifndef SPEAR + if (Keyboard[sc_Tab] && Keyboard[sc_P] && MS_CheckParm("goobers")) + PicturePause(); + #endif + + #ifdef SPANISH + } while(!Keyboard[sc_S] && !Keyboard[sc_N] && !Keyboard[sc_Escape]); + #else + } while(!Keyboard[sc_Y] && !Keyboard[sc_N] && !Keyboard[sc_Escape]); + #endif + + #ifdef SPANISH + if (Keyboard[sc_S]) + { + xit=1; + ShootSnd(); + } + + while(Keyboard[sc_S] || Keyboard[sc_N] || Keyboard[sc_Escape]); + + #else + + if (Keyboard[sc_Y]) + { + xit=1; + ShootSnd(); + } + + while(Keyboard[sc_Y] || Keyboard[sc_N] || Keyboard[sc_Escape]); + #endif + + IN_ClearKeysDown(); + SD_PlaySound(whichsnd[xit]); + return xit; +} +#endif + + +//////////////////////////////////////////////////////////////////// +// +// PRINT A MESSAGE IN A WINDOW +// +//////////////////////////////////////////////////////////////////// +void Message(char far *string) +{ + int h=0,w=0,mw=0,i,x,y,time; + fontstruct _seg *font; + + + CA_CacheGrChunk (STARTFONT+1); + fontnumber=1; + font=grsegs[STARTFONT+fontnumber]; + h=font->height; + for (i=0;i<_fstrlen(string);i++) + if (string[i]=='\n') + { + if (w>mw) + mw=w; + w=0; + h+=font->height; + } + else + w+=font->width[string[i]]; + + if (w+10>mw) + mw=w+10; + + PrintY=(WindowH/2)-h/2; + PrintX=WindowX=160-mw/2; + + DrawWindow(WindowX-5,PrintY-5,mw+10,h+10,TEXTCOLOR); + DrawOutline(WindowX-5,PrintY-5,mw+10,h+10,0,HIGHLIGHT); + SETFONTCOLOR(0,TEXTCOLOR); + US_Print(string); + VW_UpdateScreen(); +} + + +//////////////////////////////////////////////////////////////////// +// +// THIS MAY BE FIXED A LITTLE LATER... +// +//////////////////////////////////////////////////////////////////// +static int lastmusic; + +void StartCPMusic(int song) +{ + musicnames chunk; + + if (audiosegs[STARTMUSIC + lastmusic]) // JDC + MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + lastmusic]); + lastmusic = song; + + SD_MusicOff(); + chunk = song; + + MM_BombOnError (false); + CA_CacheAudioChunk(STARTMUSIC + chunk); + MM_BombOnError (true); + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]); + } +} + +void FreeMusic (void) +{ + if (audiosegs[STARTMUSIC + lastmusic]) // JDC + MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + lastmusic]); +} + + +/////////////////////////////////////////////////////////////////////////// +// +// IN_GetScanName() - Returns a string containing the name of the +// specified scan code +// +/////////////////////////////////////////////////////////////////////////// +byte * +IN_GetScanName(ScanCode scan) +{ + byte **p; + ScanCode far *s; + + for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++) + if (*s == scan) + return(*p); + + return(ScanNames[scan]); +} + + +/////////////////////////////////////////////////////////////////////////// +// +// CHECK FOR PAUSE KEY (FOR MUSIC ONLY) +// +/////////////////////////////////////////////////////////////////////////// +void CheckPause(void) +{ + if (Paused) + { + switch(SoundStatus) + { + case 0: SD_MusicOn(); break; + case 1: SD_MusicOff(); break; + } + + SoundStatus^=1; + VW_WaitVBL(3); + IN_ClearKeysDown(); + Paused=false; + } +} + + +/////////////////////////////////////////////////////////////////////////// +// +// DRAW GUN CURSOR AT CORRECT POSITION IN MENU +// +/////////////////////////////////////////////////////////////////////////// +void DrawMenuGun(CP_iteminfo *iteminfo) +{ + int x,y; + + + x=iteminfo->x; + y=iteminfo->y+iteminfo->curpos*13-2; + VWB_DrawPic(x,y,C_CURSOR1PIC); +} + + +/////////////////////////////////////////////////////////////////////////// +// +// DRAW SCREEN TITLE STRIPES +// +/////////////////////////////////////////////////////////////////////////// +void DrawStripes(int y) +{ +#ifndef SPEAR + VWB_Bar(0,y,320,24,0); + VWB_Hlin(0,319,y+22,STRIPE); +#else + VWB_Bar(0,y,320,22,0); + VWB_Hlin(0,319,y+23,0); +#endif +} + +void ShootSnd(void) +{ + SD_PlaySound(SHOOTSND); +} + + +/////////////////////////////////////////////////////////////////////////// +// +// CHECK FOR EPISODES +// +/////////////////////////////////////////////////////////////////////////// +void CheckForEpisodes(void) +{ + struct ffblk f; + +// +// JAPANESE VERSION +// +#ifdef JAPAN +#ifdef JAPDEMO + if (!findfirst("*.WJ1",&f,FA_ARCH)) + { + strcpy(extension,"WJ1"); +#else + if (!findfirst("*.WJ6",&f,FA_ARCH)) + { + strcpy(extension,"WJ6"); +#endif + strcat(configname,extension); + strcat(SaveName,extension); + strcat(PageFileName,extension); + strcat(audioname,extension); + strcat(demoname,extension); + EpisodeSelect[1] = + EpisodeSelect[2] = + EpisodeSelect[3] = + EpisodeSelect[4] = + EpisodeSelect[5] = 1; + } + else + Quit("NO JAPANESE WOLFENSTEIN 3-D DATA FILES to be found!"); +#else + +// +// ENGLISH +// +#ifndef UPLOAD +#ifndef SPEAR + if (!findfirst("*.WL6",&f,FA_ARCH)) + { + strcpy(extension,"WL6"); + NewEmenu[2].active = + NewEmenu[4].active = + NewEmenu[6].active = + NewEmenu[8].active = + NewEmenu[10].active = + EpisodeSelect[1] = + EpisodeSelect[2] = + EpisodeSelect[3] = + EpisodeSelect[4] = + EpisodeSelect[5] = 1; + } + else + if (!findfirst("*.WL3",&f,FA_ARCH)) + { + strcpy(extension,"WL3"); + NewEmenu[2].active = + NewEmenu[4].active = + EpisodeSelect[1] = + EpisodeSelect[2] = 1; + } + else +#endif +#endif + + + +#ifdef SPEAR +#ifndef SPEARDEMO + if (!findfirst("*.SOD",&f,FA_ARCH)) + { + strcpy(extension,"SOD"); + } + else + Quit("NO SPEAR OF DESTINY DATA FILES TO BE FOUND!"); +#else + if (!findfirst("*.SDM",&f,FA_ARCH)) + { + strcpy(extension,"SDM"); + } + else + Quit("NO SPEAR OF DESTINY DEMO DATA FILES TO BE FOUND!"); +#endif + +#else + if (!findfirst("*.WL1",&f,FA_ARCH)) + { + strcpy(extension,"WL1"); + } + else + Quit("NO WOLFENSTEIN 3-D DATA FILES to be found!"); +#endif + + strcat(configname,extension); + strcat(SaveName,extension); + strcat(PageFileName,extension); + strcat(audioname,extension); + strcat(demoname,extension); +#ifndef SPEAR +#ifndef GOODTIMES + strcat(helpfilename,extension); +#endif + strcat(endfilename,extension); +#endif +#endif +} diff --git a/src/lib/hb/wl_menu.h b/src/lib/hb/wl_menu.h new file mode 100755 index 00000000..dc03400c --- /dev/null +++ b/src/lib/hb/wl_menu.h @@ -0,0 +1,234 @@ +// +// WL_MENU.H +// +#ifdef SPEAR + +#define BORDCOLOR 0x99 +#define BORD2COLOR 0x93 +#define DEACTIVE 0x9b +#define BKGDCOLOR 0x9d +//#define STRIPE 0x9c + +#define MenuFadeOut() VL_FadeOut(0,255,0,0,51,10) + +#else + +#define BORDCOLOR 0x29 +#define BORD2COLOR 0x23 +#define DEACTIVE 0x2b +#define BKGDCOLOR 0x2d +#define STRIPE 0x2c + +#define MenuFadeOut() VL_FadeOut(0,255,43,0,0,10) + +#endif + +#define READCOLOR 0x4a +#define READHCOLOR 0x47 +#define VIEWCOLOR 0x7f +#define TEXTCOLOR 0x17 +#define HIGHLIGHT 0x13 +#define MenuFadeIn() VL_FadeIn(0,255,&gamepal,10) + + +#define MENUSONG WONDERIN_MUS + +#ifndef SPEAR +#define INTROSONG NAZI_NOR_MUS +#else +#define INTROSONG XTOWER2_MUS +#endif + +#define SENSITIVE 60 +#define CENTER SENSITIVE*2 + +#define MENU_X 76 +#define MENU_Y 55 +#define MENU_W 178 +#ifndef SPEAR +#define MENU_H 13*10+6 +#else +#define MENU_H 13*9+6 +#endif + +#define SM_X 48 +#define SM_W 250 + +#define SM_Y1 20 +#define SM_H1 4*13-7 +#define SM_Y2 SM_Y1+5*13 +#define SM_H2 4*13-7 +#define SM_Y3 SM_Y2+5*13 +#define SM_H3 3*13-7 + +#define CTL_X 24 +#define CTL_Y 70 +#define CTL_W 284 +#define CTL_H 13*7-7 + +#define LSM_X 85 +#define LSM_Y 55 +#define LSM_W 175 +#define LSM_H 10*13+10 + +#define NM_X 50 +#define NM_Y 100 +#define NM_W 225 +#define NM_H 13*4+15 + +#define NE_X 10 +#define NE_Y 23 +#define NE_W 320-NE_X*2 +#define NE_H 200-NE_Y*2 + +#define CST_X 20 +#define CST_Y 48 +#define CST_START 60 +#define CST_SPC 60 + + +// +// TYPEDEFS +// +typedef struct { + int x,y,amount,curpos,indent; + } CP_iteminfo; + +typedef struct { + int active; + char string[36]; + void (* routine)(int temp1); + } CP_itemtype; + +typedef struct { + int allowed[4]; + } CustomCtrls; + +extern CP_itemtype far MainMenu[],far NewEMenu[]; +extern CP_iteminfo MainItems; + +// +// FUNCTION PROTOTYPES +// +void SetupControlPanel(void); +void CleanupControlPanel(void); + +void DrawMenu(CP_iteminfo *item_i,CP_itemtype far *items); +int HandleMenu(CP_iteminfo *item_i, + CP_itemtype far *items, + void (*routine)(int w)); +void ClearMScreen(void); +void DrawWindow(int x,int y,int w,int h,int wcolor); +void DrawOutline(int x,int y,int w,int h,int color1,int color2); +void WaitKeyUp(void); +void ReadAnyControl(ControlInfo *ci); +void TicDelay(int count); +void CacheLump(int lumpstart,int lumpend); +void UnCacheLump(int lumpstart,int lumpend); +void StartCPMusic(int song); +int Confirm(char far *string); +void Message(char far *string); +void CheckPause(void); +void ShootSnd(void); +void CheckSecretMissions(void); +void BossKey(void); + +void DrawGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int *y,int which,int basey,void (*routine)(int w)); +void DrawHalfStep(int x,int y); +void EraseGun(CP_iteminfo *item_i,CP_itemtype far *items,int x,int y,int which); +void SetTextColor(CP_itemtype far *items,int hlight); +void DrawMenuGun(CP_iteminfo *iteminfo); +void DrawStripes(int y); + +void DefineMouseBtns(void); +void DefineJoyBtns(void); +void DefineKeyBtns(void); +void DefineKeyMove(void); +void EnterCtrlData(int index,CustomCtrls *cust,void (*DrawRtn)(int),void (*PrintRtn)(int),int type); + +void DrawMainMenu(void); +void DrawSoundMenu(void); +void DrawLoadSaveScreen(int loadsave); +void DrawNewEpisode(void); +void DrawNewGame(void); +void DrawChangeView(int view); +void DrawMouseSens(void); +void DrawCtlScreen(void); +void DrawCustomScreen(void); +void DrawLSAction(int which); +void DrawCustMouse(int hilight); +void DrawCustJoy(int hilight); +void DrawCustKeybd(int hilight); +void DrawCustKeys(int hilight); +void PrintCustMouse(int i); +void PrintCustJoy(int i); +void PrintCustKeybd(int i); +void PrintCustKeys(int i); + +void PrintLSEntry(int w,int color); +void TrackWhichGame(int w); +void DrawNewGameDiff(int w); +void FixupCustom(int w); + +void CP_NewGame(void); +void CP_Sound(void); +int CP_LoadGame(int quick); +int CP_SaveGame(int quick); +void CP_Control(void); +void CP_ChangeView(void); +void CP_ExitOptions(void); +void CP_Quit(void); +void CP_ViewScores(void); +int CP_EndGame(void); +int CP_CheckQuick(unsigned scancode); +void CustomControls(void); +void MouseSensitivity(void); + +void CheckForEpisodes(void); + +// +// VARIABLES +// +extern int SaveGamesAvail[10],StartGame,SoundStatus; +extern char SaveGameNames[10][32],SaveName[13]; + +enum {MOUSE,JOYSTICK,KEYBOARDBTNS,KEYBOARDMOVE}; // FOR INPUT TYPES + +#ifndef USO_FIX1 +extern +#endif +enum +{ + newgame, + soundmenu, + control, + loadgame, + savegame, + changeview, + +#ifndef GOODTIMES +#ifndef SPEAR + readthis, +#endif +#endif + + viewscores, + backtodemo, + quit +} menuitems; + +// +// WL_INTER +// +typedef struct { + int kill,secret,treasure; + long time; + } LRstruct; + +extern LRstruct LevelRatios[]; + +void Write (int x,int y,char *string); +void NonShareware(void); +int GetYorN(int x,int y,int pic); + + diff --git a/src/lib/hb/wl_play.c b/src/lib/hb/wl_play.c new file mode 100755 index 00000000..447c8937 --- /dev/null +++ b/src/lib/hb/wl_play.c @@ -0,0 +1,1473 @@ +// WL_PLAY.C + +#include "WL_DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define sc_Question 0x35 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean madenoise; // true when shooting or screaming + +exit_t playstate; + +int DebugOk; + +objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj, + *objfreelist,*killerobj; + +unsigned farmapylookup[MAPSIZE]; +byte *nearmapylookup[MAPSIZE]; + +boolean singlestep,godmode,noclip; +int extravbls; + +byte tilemap[MAPSIZE][MAPSIZE]; // wall values only +byte spotvis[MAPSIZE][MAPSIZE]; +objtype *actorat[MAPSIZE][MAPSIZE]; + +// +// replacing refresh manager +// +unsigned mapwidth,mapheight,tics; +boolean compatability; +byte *updateptr; +unsigned mapwidthtable[64]; +unsigned uwidthtable[UPDATEHIGH]; +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +//uso: replace: byte update[UPDATESIZE]; +//uso: is needed? byte update[UPDATEHIGH][UPDATEWIDE]; + +// +// control info +// +boolean mouseenabled,joystickenabled,joypadenabled,joystickprogressive; +int joystickport; +int dirscan[4] = {sc_UpArrow,sc_RightArrow,sc_DownArrow,sc_LeftArrow}; +int buttonscan[NUMBUTTONS] = + {sc_Control,sc_Alt,sc_RShift,sc_Space,sc_1,sc_2,sc_3,sc_4}; +int buttonmouse[4]={bt_attack,bt_strafe,bt_use,bt_nobutton}; +int buttonjoy[4]={bt_attack,bt_strafe,bt_use,bt_run}; + +int viewsize; + +boolean buttonheld[NUMBUTTONS]; + +boolean demorecord,demoplayback; +char far *demoptr, far *lastdemoptr; +memptr demobuffer; + +// +// curent user input +// +int controlx,controly; // range from -100 to 100 per tic +boolean buttonstate[NUMBUTTONS]; + + + +//=========================================================================== + + +void CenterWindow(word w,word h); +void InitObjList (void); +void RemoveObj (objtype *gone); +void PollControls (void); +void StopMusic(void); +void StartMusic(void); +void PlayLoop (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +objtype dummyobj; + +// +// LIST OF SONGS FOR EACH VERSION +// +int songs[]= +{ +#ifndef SPEAR + // + // Episode One + // + GETTHEM_MUS, + SEARCHN_MUS, + POW_MUS, + SUSPENSE_MUS, + GETTHEM_MUS, + SEARCHN_MUS, + POW_MUS, + SUSPENSE_MUS, + + WARMARCH_MUS, // Boss level + CORNER_MUS, // Secret level + + // + // Episode Two + // + NAZI_OMI_MUS, + PREGNANT_MUS, + GOINGAFT_MUS, + HEADACHE_MUS, + NAZI_OMI_MUS, + PREGNANT_MUS, + HEADACHE_MUS, + GOINGAFT_MUS, + + WARMARCH_MUS, // Boss level + DUNGEON_MUS, // Secret level + + // + // Episode Three + // + INTROCW3_MUS, + NAZI_RAP_MUS, + TWELFTH_MUS, + ZEROHOUR_MUS, + INTROCW3_MUS, + NAZI_RAP_MUS, + TWELFTH_MUS, + ZEROHOUR_MUS, + + ULTIMATE_MUS, // Boss level + PACMAN_MUS, // Secret level + + // + // Episode Four + // + GETTHEM_MUS, + SEARCHN_MUS, + POW_MUS, + SUSPENSE_MUS, + GETTHEM_MUS, + SEARCHN_MUS, + POW_MUS, + SUSPENSE_MUS, + + WARMARCH_MUS, // Boss level + CORNER_MUS, // Secret level + + // + // Episode Five + // + NAZI_OMI_MUS, + PREGNANT_MUS, + GOINGAFT_MUS, + HEADACHE_MUS, + NAZI_OMI_MUS, + PREGNANT_MUS, + HEADACHE_MUS, + GOINGAFT_MUS, + + WARMARCH_MUS, // Boss level + DUNGEON_MUS, // Secret level + + // + // Episode Six + // + INTROCW3_MUS, + NAZI_RAP_MUS, + TWELFTH_MUS, + ZEROHOUR_MUS, + INTROCW3_MUS, + NAZI_RAP_MUS, + TWELFTH_MUS, + ZEROHOUR_MUS, + + ULTIMATE_MUS, // Boss level + FUNKYOU_MUS // Secret level +#else + + ////////////////////////////////////////////////////////////// + // + // SPEAR OF DESTINY TRACKS + // + ////////////////////////////////////////////////////////////// + XTIPTOE_MUS, + XFUNKIE_MUS, + XDEATH_MUS, + XGETYOU_MUS, // DON'T KNOW + ULTIMATE_MUS, // Trans Gr”sse + + DUNGEON_MUS, + GOINGAFT_MUS, + POW_MUS, + TWELFTH_MUS, + ULTIMATE_MUS, // Barnacle Wilhelm BOSS + + NAZI_OMI_MUS, + GETTHEM_MUS, + SUSPENSE_MUS, + SEARCHN_MUS, + ZEROHOUR_MUS, + ULTIMATE_MUS, // Super Mutant BOSS + + XPUTIT_MUS, + ULTIMATE_MUS, // Death Knight BOSS + + XJAZNAZI_MUS, // Secret level + XFUNKIE_MUS, // Secret level (DON'T KNOW) + + XEVIL_MUS // Angel of Death BOSS + +#endif +}; + + +/* +============================================================================= + + USER CONTROL + +============================================================================= +*/ + + +#define BASEMOVE 35 +#define RUNMOVE 70 +#define BASETURN 35 +#define RUNTURN 70 + +#define JOYSCALE 2 + +/* +=================== += += PollKeyboardButtons += +=================== +*/ + +void PollKeyboardButtons (void) +{ + int i; + + for (i=0;i 64) + controlx += (joyx-64)*JOYSCALE*tics; + else if (joyx < -64) + controlx -= (-joyx-64)*JOYSCALE*tics; + if (joyy > 64) + controlx += (joyy-64)*JOYSCALE*tics; + else if (joyy < -64) + controly -= (-joyy-64)*JOYSCALE*tics; + } + else if (buttonstate[bt_run]) + { + if (joyx > 64) + controlx += RUNMOVE*tics; + else if (joyx < -64) + controlx -= RUNMOVE*tics; + if (joyy > 64) + controly += RUNMOVE*tics; + else if (joyy < -64) + controly -= RUNMOVE*tics; + } + else + { + if (joyx > 64) + controlx += BASEMOVE*tics; + else if (joyx < -64) + controlx -= BASEMOVE*tics; + if (joyy > 64) + controly += BASEMOVE*tics; + else if (joyy < -64) + controly -= BASEMOVE*tics; + } +} + + +/* +=================== += += PollControls += += Gets user or demo input, call once each frame += += controlx set between -100 and 100 per tic += controly += buttonheld[] the state of the buttons LAST frame += buttonstate[] the state of the buttons THIS frame += +=================== +*/ + +void PollControls (void) +{ + int max,min,i; + byte buttonbits; + +// +// get timing info for last frame +// + if (demoplayback) + { + while (TimeCount>= 1; + } + + controlx = *demoptr++; + controly = *demoptr++; + + if (demoptr == lastdemoptr) + playstate = ex_completed; // demo is done + + controlx *= (int)tics; + controly *= (int)tics; + + return; + } + + +// +// get button states +// + PollKeyboardButtons (); + + if (mouseenabled) + PollMouseButtons (); + + if (joystickenabled) + PollJoystickButtons (); + +// +// get movements +// + PollKeyboardMove (); + + if (mouseenabled) + PollMouseMove (); + + if (joystickenabled) + PollJoystickMove (); + +// +// bound movement to a maximum +// + max = 100*tics; + min = -max; + if (controlx > max) + controlx = max; + else if (controlx < min) + controlx = min; + + if (controly > max) + controly = max; + else if (controly < min) + controly = min; + + if (demorecord) + { + // + // save info out to demo buffer + // + controlx /= (int)tics; + controly /= (int)tics; + + buttonbits = 0; + + for (i=NUMBUTTONS-1;i>=0;i--) + { + buttonbits <<= 1; + if (buttonstate[i]) + buttonbits |= 1; + } + + *demoptr++ = buttonbits; + *demoptr++ = controlx; + *demoptr++ = controly; + + if (demoptr >= lastdemoptr) + Quit ("Demo buffer overflowed!"); + + controlx *= (int)tics; + controly *= (int)tics; + } +} + + + +//========================================================================== + + + +/////////////////////////////////////////////////////////////////////////// +// +// CenterWindow() - Generates a window of a given width & height in the +// middle of the screen +// +/////////////////////////////////////////////////////////////////////////// + +#define MAXX 320 +#define MAXY 160 + +void CenterWindow(word w,word h) +{ + FixOfs (); + US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h); +} + +//=========================================================================== + + +/* +===================== += += CheckKeys += +===================== +*/ + +void CheckKeys (void) +{ + int i; + byte scan; + unsigned temp; + + + if (screenfaded || demoplayback) // don't do anything with a faded screen + return; + + scan = LastScan; + + + #ifdef SPEAR + // + // SECRET CHEAT CODE: TAB-G-F10 + // + if (Keyboard[sc_Tab] && + Keyboard[sc_G] && + Keyboard[sc_F10]) + { + WindowH = 160; + if (godmode) + { + Message ("God mode OFF"); + SD_PlaySound (NOBONUSSND); + } + else + { + Message ("God mode ON"); + SD_PlaySound (ENDBONUS2SND); + } + + IN_Ack(); + godmode ^= 1; + DrawAllPlayBorderSides (); + IN_ClearKeysDown(); + return; + } + #endif + + + // + // SECRET CHEAT CODE: 'MLI' + // + if (Keyboard[sc_M] && + Keyboard[sc_L] && + Keyboard[sc_I]) + { + gamestate.health = 100; + gamestate.ammo = 99; + gamestate.keys = 3; + gamestate.score = 0; + gamestate.TimeCount += 42000L; + GiveWeapon (wp_chaingun); + + DrawWeapon(); + DrawHealth(); + DrawKeys(); + DrawAmmo(); + DrawScore(); + + ClearMemory (); + CA_CacheGrChunk (STARTFONT+1); + ClearSplitVWB (); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + + Message(STR_CHEATER1"\n" + STR_CHEATER2"\n\n" + STR_CHEATER3"\n" + STR_CHEATER4"\n" + STR_CHEATER5); + + UNCACHEGRCHUNK(STARTFONT+1); + PM_CheckMainMem (); + IN_ClearKeysDown(); + IN_Ack(); + + DrawAllPlayBorder (); + } + + // + // OPEN UP DEBUG KEYS + // +#ifndef SPEAR + if (Keyboard[sc_BackSpace] && + Keyboard[sc_LShift] && + Keyboard[sc_Alt] && + MS_CheckParm("goobers")) +#else + if (Keyboard[sc_BackSpace] && + Keyboard[sc_LShift] && + Keyboard[sc_Alt] && + MS_CheckParm("debugmode")) +#endif + { + ClearMemory (); + CA_CacheGrChunk (STARTFONT+1); + ClearSplitVWB (); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + + Message("Debugging keys are\nnow available!"); + UNCACHEGRCHUNK(STARTFONT+1); + PM_CheckMainMem (); + IN_ClearKeysDown(); + IN_Ack(); + + DrawAllPlayBorderSides (); + DebugOk=1; + } + + // + // TRYING THE KEEN CHEAT CODE! + // + if (Keyboard[sc_B] && + Keyboard[sc_A] && + Keyboard[sc_T]) + { + ClearMemory (); + CA_CacheGrChunk (STARTFONT+1); + ClearSplitVWB (); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + + Message("Commander Keen is also\n" + "available from Apogee, but\n" + "then, you already know\n" + "that - right, Cheatmeister?!"); + + UNCACHEGRCHUNK(STARTFONT+1); + PM_CheckMainMem (); + IN_ClearKeysDown(); + IN_Ack(); + + DrawAllPlayBorder (); + } + +// +// pause key weirdness can't be checked as a scan code +// + if (Paused) + { + bufferofs = displayofs; + LatchDrawPic (20-4,80-2*8,PAUSEDPIC); + SD_MusicOff(); + IN_Ack(); + IN_ClearKeysDown (); + SD_MusicOn(); + Paused = false; + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + return; + } + + +// +// F1-F7/ESC to enter control panel +// + if ( +#ifndef DEBCHECK + scan == sc_F10 || +#endif + scan == sc_F9 || + scan == sc_F7 || + scan == sc_F8) // pop up quit dialog + { + ClearMemory (); + ClearSplitVWB (); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + US_ControlPanel(scan); + + DrawAllPlayBorderSides (); + + if (scan == sc_F9) + StartMusic (); + + PM_CheckMainMem (); + SETFONTCOLOR(0,15); + IN_ClearKeysDown(); + return; + } + + if ( (scan >= sc_F1 && scan <= sc_F9) || scan == sc_Escape) + { + StopMusic (); + ClearMemory (); + VW_FadeOut (); + + US_ControlPanel(scan); + + SETFONTCOLOR(0,15); + IN_ClearKeysDown(); + DrawPlayScreen (); + if (!startgame && !loadedgame) + { + VW_FadeIn (); + StartMusic (); + } + if (loadedgame) + playstate = ex_abort; + lasttimecount = TimeCount; + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + PM_CheckMainMem (); + return; + } + +// +// TAB-? debug keys +// + if (Keyboard[sc_Tab] && DebugOk) + { + CA_CacheGrChunk (STARTFONT); + fontnumber=0; + SETFONTCOLOR(0,15); + DebugKeys(); + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + lasttimecount = TimeCount; + return; + } + +} + + +//=========================================================================== + +/* +############################################################################# + + The objlist data structure + +############################################################################# + +objlist containt structures for every actor currently playing. The structure +is accessed as a linked list starting at *player, ending when ob->next == +NULL. GetNewObj inserts a new object at the end of the list, meaning that +if an actor spawn another actor, the new one WILL get to think and react the +same frame. RemoveObj unlinks the given object and returns it to the free +list, but does not damage the objects ->next pointer, so if the current object +removes itself, a linked list following loop can still safely get to the +next element. + + + +############################################################################# +*/ + + +/* +========================= += += InitActorList += += Call to clear out the actor object lists returning them all to the free += list. Allocates a special spot for the player. += +========================= +*/ + +int objcount; + +void InitActorList (void) +{ + int i; + +// +// init the actor lists +// + for (i=0;iprev; + memset (new,0,sizeof(*new)); + + if (lastobj) + lastobj->next = new; + new->prev = lastobj; // new->next is allready NULL from memset + + new->active = false; + lastobj = new; + + objcount++; +} + +//=========================================================================== + +/* +========================= += += RemoveObj += += Add the given object back into the free list, and unlink it from it's += neighbors += +========================= +*/ + +void RemoveObj (objtype *gone) +{ + objtype **spotat; + + if (gone == player) + Quit ("RemoveObj: Tried to remove the player!"); + + gone->state = NULL; + +// +// fix the next object's back link +// + if (gone == lastobj) + lastobj = (objtype *)gone->prev; + else + gone->next->prev = gone->prev; + +// +// fix the previous object's forward link +// + gone->prev->next = gone->next; + +// +// add it back in to the free list +// + gone->prev = objfreelist; + objfreelist = gone; + + objcount--; +} + +/* +============================================================================= + + MUSIC STUFF + +============================================================================= +*/ + + +/* +================= += += StopMusic += +================= +*/ + +void StopMusic(void) +{ + int i; + + SD_MusicOff(); + for (i = 0;i < LASTMUSIC;i++) + if (audiosegs[STARTMUSIC + i]) + { + MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3); + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false); + } +} + +//========================================================================== + + +/* +================= += += StartMusic += +================= +*/ + +void StartMusic(void) +{ + musicnames chunk; + + SD_MusicOff(); + chunk = songs[gamestate.mapon+gamestate.episode*10]; + +// if ((chunk == -1) || (MusicMode != smm_AdLib)) +//DEBUG control panel return; + + MM_BombOnError (false); + CA_CacheAudioChunk(STARTMUSIC + chunk); + MM_BombOnError (true); + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]); + } +} + + +/* +============================================================================= + + PALETTE SHIFTING STUFF + +============================================================================= +*/ + +#define NUMREDSHIFTS 6 +#define REDSTEPS 8 + +#define NUMWHITESHIFTS 3 +#define WHITESTEPS 20 +#define WHITETICS 6 + + +byte far redshifts[NUMREDSHIFTS][768]; +byte far whiteshifts[NUMREDSHIFTS][768]; + +int damagecount,bonuscount; +boolean palshifted; + +extern byte far gamepal; + +/* +===================== += += InitRedShifts += +===================== +*/ + +void InitRedShifts (void) +{ + byte far *workptr, far *baseptr; + int i,j,delta; + + +// +// fade through intermediate frames +// + for (i=1;i<=NUMREDSHIFTS;i++) + { + workptr = (byte far *)&redshifts[i-1][0]; + baseptr = &gamepal; + + for (j=0;j<=255;j++) + { + delta = 64-*baseptr; + *workptr++ = *baseptr++ + delta * i / REDSTEPS; + delta = -*baseptr; + *workptr++ = *baseptr++ + delta * i / REDSTEPS; + delta = -*baseptr; + *workptr++ = *baseptr++ + delta * i / REDSTEPS; + } + } + + for (i=1;i<=NUMWHITESHIFTS;i++) + { + workptr = (byte far *)&whiteshifts[i-1][0]; + baseptr = &gamepal; + + for (j=0;j<=255;j++) + { + delta = 64-*baseptr; + *workptr++ = *baseptr++ + delta * i / WHITESTEPS; + delta = 62-*baseptr; + *workptr++ = *baseptr++ + delta * i / WHITESTEPS; + delta = 0-*baseptr; + *workptr++ = *baseptr++ + delta * i / WHITESTEPS; + } + } +} + + +/* +===================== += += ClearPaletteShifts += +===================== +*/ + +void ClearPaletteShifts (void) +{ + bonuscount = damagecount = 0; +} + + +/* +===================== += += StartBonusFlash += +===================== +*/ + +void StartBonusFlash (void) +{ + bonuscount = NUMWHITESHIFTS*WHITETICS; // white shift palette +} + + +/* +===================== += += StartDamageFlash += +===================== +*/ + +void StartDamageFlash (int damage) +{ + damagecount += damage; +} + + +/* +===================== += += UpdatePaletteShifts += +===================== +*/ + +void UpdatePaletteShifts (void) +{ + int red,white; + + if (bonuscount) + { + white = bonuscount/WHITETICS +1; + if (white>NUMWHITESHIFTS) + white = NUMWHITESHIFTS; + bonuscount -= tics; + if (bonuscount < 0) + bonuscount = 0; + } + else + white = 0; + + + if (damagecount) + { + red = damagecount/10 +1; + if (red>NUMREDSHIFTS) + red = NUMREDSHIFTS; + + damagecount -= tics; + if (damagecount < 0) + damagecount = 0; + } + else + red = 0; + + if (red) + { + VW_WaitVBL(1); + VL_SetPalette (redshifts[red-1]); + palshifted = true; + } + else if (white) + { + VW_WaitVBL(1); + VL_SetPalette (whiteshifts[white-1]); + palshifted = true; + } + else if (palshifted) + { + VW_WaitVBL(1); + VL_SetPalette (&gamepal); // back to normal + palshifted = false; + } +} + + +/* +===================== += += FinishPaletteShifts += += Resets palette to normal if needed += +===================== +*/ + +void FinishPaletteShifts (void) +{ + if (palshifted) + { + palshifted = 0; + VW_WaitVBL(1); + VL_SetPalette (&gamepal); + } +} + + +/* +============================================================================= + + CORE PLAYLOOP + +============================================================================= +*/ + + +/* +===================== += += DoActor += +===================== +*/ + +void DoActor (objtype *ob) +{ + void (*think)(objtype *); + + if (!ob->active && !areabyplayer[ob->areanumber]) + return; + + if (!(ob->flags&(FL_NONMARK|FL_NEVERMARK)) ) + actorat[ob->tilex][ob->tiley] = NULL; + +// +// non transitional object +// + + if (!ob->ticcount) + { + think = ob->state->think; + if (think) + { + think (ob); + if (!ob->state) + { + RemoveObj (ob); + return; + } + } + + if (ob->flags&FL_NEVERMARK) + return; + + if ( (ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley]) + return; + + actorat[ob->tilex][ob->tiley] = ob; + return; + } + +// +// transitional object +// + ob->ticcount-=tics; + while ( ob->ticcount <= 0) + { + think = ob->state->action; // end of state action + if (think) + { + think (ob); + if (!ob->state) + { + RemoveObj (ob); + return; + } + } + + ob->state = ob->state->next; + + if (!ob->state) + { + RemoveObj (ob); + return; + } + + if (!ob->state->tictime) + { + ob->ticcount = 0; + goto think; + } + + ob->ticcount += ob->state->tictime; + } + +think: + // + // think + // + think = ob->state->think; + if (think) + { + think (ob); + if (!ob->state) + { + RemoveObj (ob); + return; + } + } + + if (ob->flags&FL_NEVERMARK) + return; + + if ( (ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley]) + return; + + actorat[ob->tilex][ob->tiley] = ob; +} + +//========================================================================== + + +/* +=================== += += PlayLoop += +=================== +*/ +long funnyticount; + + +void PlayLoop (void) +{ + int give; + int helmetangle; + + playstate = TimeCount = lasttimecount = 0; + frameon = 0; + running = false; + anglefrac = 0; + facecount = 0; + funnyticount = 0; + memset (buttonstate,0,sizeof(buttonstate)); + ClearPaletteShifts (); + + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + + if (demoplayback) + IN_StartAck (); + + do + { + if (virtualreality) + { + helmetangle = peek (0x40,0xf0); + player->angle += helmetangle; + if (player->angle >= ANGLES) + player->angle -= ANGLES; + } + + + PollControls(); + +// +// actor thinking +// + madenoise = false; + + MoveDoors (); + MovePWalls (); + + for (obj = player;obj;obj = obj->next) + DoActor (obj); + + UpdatePaletteShifts (); + + ThreeDRefresh (); + + // + // MAKE FUNNY FACE IF BJ DOESN'T MOVE FOR AWHILE + // + #ifdef SPEAR + funnyticount += tics; + if (funnyticount > 30l*70) + { + funnyticount = 0; + StatusDrawPic (17,4,BJWAITING1PIC+(US_RndT()&1)); + facecount = 0; + } + #endif + + gamestate.TimeCount+=tics; + + SD_Poll (); + UpdateSoundLoc(); // JAB + + if (screenfaded) + VW_FadeIn (); + + CheckKeys(); + +// +// debug aids +// + if (singlestep) + { + VW_WaitVBL(14); + lasttimecount = TimeCount; + } + if (extravbls) + VW_WaitVBL(extravbls); + + if (demoplayback) + { + if (IN_CheckAck ()) + { + IN_ClearKeysDown (); + playstate = ex_abort; + } + } + + + if (virtualreality) + { + player->angle -= helmetangle; + if (player->angle < 0) + player->angle += ANGLES; + } + + }while (!playstate && !startgame); + + if (playstate != ex_died) + FinishPaletteShifts (); +} + diff --git a/src/lib/hb/wl_scale.c b/src/lib/hb/wl_scale.c new file mode 100755 index 00000000..9c1e102f --- /dev/null +++ b/src/lib/hb/wl_scale.c @@ -0,0 +1,741 @@ +// WL_SCALE.C + +#include "WL_DEF.H" +#pragma hdrstop + +#define OP_RETF 0xcb + +/* +============================================================================= + + GLOBALS + +============================================================================= +*/ + +t_compscale _seg *scaledirectory[MAXSCALEHEIGHT+1]; +long fullscalefarcall[MAXSCALEHEIGHT+1]; + +int maxscale,maxscaleshl2; + +boolean insetupscaling; + +/* +============================================================================= + + LOCALS + +============================================================================= +*/ + +t_compscale _seg *work; +unsigned BuildCompScale (int height, memptr *finalspot); + +int stepbytwo; + +//=========================================================================== + +/* +============== += += BadScale += +============== +*/ + +void far BadScale (void) +{ + Quit ("BadScale called!"); +} + + +/* +========================== += += SetupScaling += +========================== +*/ + +void SetupScaling (int maxscaleheight) +{ + int i,x,y; + byte far *dest; + + insetupscaling = true; + + maxscaleheight/=2; // one scaler every two pixels + + maxscale = maxscaleheight-1; + maxscaleshl2 = maxscale<<2; + +// +// free up old scalers +// + for (i=1;i=stepbytwo) + i += 2; + } + memset (scaledirectory,0,sizeof(scaledirectory)); + + MM_SortMem (); + +// +// build the compiled scalers +// + stepbytwo = viewheight/2; // save space by double stepping + MM_GetPtr (&(memptr)work,20000); + + for (i=1;i<=maxscaleheight;i++) + { + BuildCompScale (i*2,&(memptr)scaledirectory[i]); + if (i>=stepbytwo) + i+= 2; + } + MM_FreePtr (&(memptr)work); + +// +// compact memory and lock down scalers +// + MM_SortMem (); + for (i=1;i<=maxscaleheight;i++) + { + MM_SetLock (&(memptr)scaledirectory[i],true); + fullscalefarcall[i] = (unsigned)scaledirectory[i]; + fullscalefarcall[i] <<=16; + fullscalefarcall[i] += scaledirectory[i]->codeofs[0]; + if (i>=stepbytwo) + { + scaledirectory[i+1] = scaledirectory[i]; + fullscalefarcall[i+1] = fullscalefarcall[i]; + scaledirectory[i+2] = scaledirectory[i]; + fullscalefarcall[i+2] = fullscalefarcall[i]; + i+=2; + } + } + scaledirectory[0] = scaledirectory[1]; + fullscalefarcall[0] = fullscalefarcall[1]; + +// +// check for oversize wall drawing +// + for (i=maxscaleheight;icode[0]; + toppix = (viewheight-height)/2; + fix = 0; + + for (src=0;src<=64;src++) + { + startpix = fix>>16; + fix += step; + endpix = fix>>16; + + if (endpix>startpix) + work->width[src] = endpix-startpix; + else + work->width[src] = 0; + +// +// mark the start of the code +// + work->codeofs[src] = FP_OFF(code); + +// +// compile some code if the source pixel generates any screen pixels +// + startpix+=toppix; + endpix+=toppix; + + if (startpix == endpix || endpix < 0 || startpix >= viewheight || src == 64) + continue; + + // + // mov al,[si+src] + // + *code++ = 0x8a; + *code++ = 0x44; + *code++ = src; + + for (;startpix= viewheight) + break; // off the bottom of the view area + if (startpix < 0) + continue; // not into the view area + + // + // mov [es:di+heightofs],al + // + *code++ = 0x26; + *code++ = 0x88; + *code++ = 0x85; + *((unsigned far *)code)++ = startpix*SCREENBWIDE; + } + + } + +// +// retf +// + *code++ = 0xcb; + + totalsize = FP_OFF(code); + MM_GetPtr (finalspot,totalsize); + _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize); + + return totalsize; +} + + +/* +======================= += += ScaleLine += += linescale should have the high word set to the segment of the scaler += +======================= +*/ + +extern int slinex,slinewidth; +extern unsigned far *linecmds; +extern long linescale; +extern unsigned maskword; + +byte mask1,mask2,mask3; + + +void near ScaleLine (void) +{ +asm mov cx,WORD PTR [linescale+2] +asm mov es,cx // segment of scaler + +asm mov bp,WORD PTR [linecmds] +asm mov dx,SC_INDEX+1 // to set SC_MAPMASK + +asm mov bx,[slinex] +asm mov di,bx +asm shr di,1 // X in bytes +asm shr di,1 +asm add di,[bufferofs] +asm and bx,3 +/* begin 8086 hack +asm shl bx,3 +*/ +asm push cx +asm mov cl,3 +asm shl bx,cl +asm pop cx +/* end 8086 hack */ +asm add bx,[slinewidth] // bx = (pixel*8+pixwidth) +asm mov al,BYTE [mapmasks3-1+bx] // -1 because pixwidth of 1 is first +asm mov ds,WORD PTR [linecmds+2] +asm or al,al +asm jz notthreebyte // scale across three bytes +asm jmp threebyte +notthreebyte: +asm mov al,BYTE PTR ss:[mapmasks2-1+bx] // -1 because pixwidth of 1 is first +asm or al,al +asm jnz twobyte // scale across two bytes + +// +// one byte scaling +// +asm mov al,BYTE PTR ss:[mapmasks1-1+bx] // -1 because pixwidth of 1 is first +asm out dx,al // set map mask register + +scalesingle: + +asm mov bx,[ds:bp] // table location of rtl to patch +asm or bx,bx +asm jz linedone // 0 signals end of segment list +asm mov bx,[es:bx] +asm mov dl,[es:bx] // save old value +asm mov BYTE PTR es:[bx],OP_RETF // patch a RETF in +asm mov si,[ds:bp+4] // table location of entry spot +asm mov ax,[es:si] +asm mov WORD PTR ss:[linescale],ax // call here to start scaling +asm mov si,[ds:bp+2] // corrected top of shape for this segment +asm add bp,6 // next segment list + +asm mov ax,SCREENSEG +asm mov es,ax +asm call ss:[linescale] // scale the segment of pixels + +asm mov es,cx // segment of scaler +asm mov BYTE PTR es:[bx],dl // unpatch the RETF +asm jmp scalesingle // do the next segment + + +// +// done +// +linedone: +asm mov ax,ss +asm mov ds,ax +return; + +// +// two byte scaling +// +twobyte: +asm mov ss:[mask2],al +asm mov al,BYTE PTR ss:[mapmasks1-1+bx] // -1 because pixwidth of 1 is first +asm mov ss:[mask1],al + +scaledouble: + +asm mov bx,[ds:bp] // table location of rtl to patch +asm or bx,bx +asm jz linedone // 0 signals end of segment list +asm mov bx,[es:bx] +asm mov cl,[es:bx] // save old value +asm mov BYTE PTR es:[bx],OP_RETF // patch a RETF in +asm mov si,[ds:bp+4] // table location of entry spot +asm mov ax,[es:si] +asm mov WORD PTR ss:[linescale],ax // call here to start scaling +asm mov si,[ds:bp+2] // corrected top of shape for this segment +asm add bp,6 // next segment list + +asm mov ax,SCREENSEG +asm mov es,ax +asm mov al,ss:[mask1] +asm out dx,al // set map mask register +asm call ss:[linescale] // scale the segment of pixels +asm inc di +asm mov al,ss:[mask2] +asm out dx,al // set map mask register +asm call ss:[linescale] // scale the segment of pixels +asm dec di + +asm mov es,WORD PTR ss:[linescale+2] // segment of scaler +asm mov BYTE PTR es:[bx],cl // unpatch the RETF +asm jmp scaledouble // do the next segment + + +// +// three byte scaling +// +threebyte: +asm mov ss:[mask3],al +asm mov al,BYTE PTR ss:[mapmasks2-1+bx] // -1 because pixwidth of 1 is first +asm mov ss:[mask2],al +asm mov al,BYTE PTR ss:[mapmasks1-1+bx] // -1 because pixwidth of 1 is first +asm mov ss:[mask1],al + +scaletriple: + +asm mov bx,[ds:bp] // table location of rtl to patch +asm or bx,bx +asm jz linedone // 0 signals end of segment list +asm mov bx,[es:bx] +asm mov cl,[es:bx] // save old value +asm mov BYTE PTR es:[bx],OP_RETF // patch a RETF in +asm mov si,[ds:bp+4] // table location of entry spot +asm mov ax,[es:si] +asm mov WORD PTR ss:[linescale],ax // call here to start scaling +asm mov si,[ds:bp+2] // corrected top of shape for this segment +asm add bp,6 // next segment list + +asm mov ax,SCREENSEG +asm mov es,ax +asm mov al,ss:[mask1] +asm out dx,al // set map mask register +asm call ss:[linescale] // scale the segment of pixels +asm inc di +asm mov al,ss:[mask2] +asm out dx,al // set map mask register +asm call ss:[linescale] // scale the segment of pixels +asm inc di +asm mov al,ss:[mask3] +asm out dx,al // set map mask register +asm call ss:[linescale] // scale the segment of pixels +asm dec di +asm dec di + +asm mov es,WORD PTR ss:[linescale+2] // segment of scaler +asm mov BYTE PTR es:[bx],cl // unpatch the RETF +asm jmp scaletriple // do the next segment + + +} + + +/* +======================= += += ScaleShape += += Draws a compiled shape at [scale] pixels high += += each vertical line of the shape has a pointer to segment data: += end of segment pixel*2 (0 terminates line) used to patch rtl in scaler += top of virtual line with segment in proper place += start of segment pixel*2, used to jsl into compiled scaler += += += Setup for call += -------------- += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += GC_INDEX pointing at GC_BITMASK += +======================= +*/ + +static long longtemp; + +void ScaleShape (int xcenter, int shapenum, unsigned height) +{ + t_compshape _seg *shape; + t_compscale _seg *comptable; + unsigned scale,srcx,stopx,tempx; + int t; + unsigned far *cmdptr; + boolean leftvis,rightvis; + + + shape = PM_GetSpritePage (shapenum); + + scale = height>>3; // low three bits are fractional + if (!scale || scale>maxscale) + return; // too close or far away + comptable = scaledirectory[scale]; + + *(((unsigned *)&linescale)+1)=(unsigned)comptable; // seg of far call + *(((unsigned *)&linecmds)+1)=(unsigned)shape; // seg of shape + +// +// scale to the left (from pixel 31 to shape->leftpix) +// + srcx = 32; + slinex = xcenter; + stopx = shape->leftpix; + cmdptr = &shape->dataofs[31-stopx]; + + while ( --srcx >=stopx && slinex>0) + { + (unsigned)linecmds = *cmdptr--; + if ( !(slinewidth = comptable->width[srcx]) ) + continue; + + if (slinewidth == 1) + { + slinex--; + if (slinex= height) + continue; // obscured by closer wall + ScaleLine (); + } + continue; + } + + // + // handle multi pixel lines + // + if (slinex>viewwidth) + { + slinex -= slinewidth; + slinewidth = viewwidth-slinex; + if (slinewidth<1) + continue; // still off the right side + } + else + { + if (slinewidth>slinex) + slinewidth = slinex; + slinex -= slinewidth; + } + + + leftvis = (wallheight[slinex] < height); + rightvis = (wallheight[slinex+slinewidth-1] < height); + + if (leftvis) + { + if (rightvis) + ScaleLine (); + else + { + while (wallheight[slinex+slinewidth-1] >= height) + slinewidth--; + ScaleLine (); + } + } + else + { + if (!rightvis) + continue; // totally obscured + + while (wallheight[slinex] >= height) + { + slinex++; + slinewidth--; + } + ScaleLine (); + break; // the rest of the shape is gone + } + } + + +// +// scale to the right +// + slinex = xcenter; + stopx = shape->rightpix; + if (shape->leftpix<31) + { + srcx = 31; + cmdptr = &shape->dataofs[32-shape->leftpix]; + } + else + { + srcx = shape->leftpix-1; + cmdptr = &shape->dataofs[0]; + } + slinewidth = 0; + + while ( ++srcx <= stopx && (slinex+=slinewidth)width[srcx]) ) + continue; + + if (slinewidth == 1) + { + if (slinex>=0 && wallheight[slinex] < height) + { + ScaleLine (); + } + continue; + } + + // + // handle multi pixel lines + // + if (slinex<0) + { + if (slinewidth <= -slinex) + continue; // still off the left edge + + slinewidth += slinex; + slinex = 0; + } + else + { + if (slinex + slinewidth > viewwidth) + slinewidth = viewwidth-slinex; + } + + + leftvis = (wallheight[slinex] < height); + rightvis = (wallheight[slinex+slinewidth-1] < height); + + if (leftvis) + { + if (rightvis) + { + ScaleLine (); + } + else + { + while (wallheight[slinex+slinewidth-1] >= height) + slinewidth--; + ScaleLine (); + break; // the rest of the shape is gone + } + } + else + { + if (rightvis) + { + while (wallheight[slinex] >= height) + { + slinex++; + slinewidth--; + } + ScaleLine (); + } + else + continue; // totally obscured + } + } +} + + + +/* +======================= += += SimpleScaleShape += += NO CLIPPING, height in pixels += += Draws a compiled shape at [scale] pixels high += += each vertical line of the shape has a pointer to segment data: += end of segment pixel*2 (0 terminates line) used to patch rtl in scaler += top of virtual line with segment in proper place += start of segment pixel*2, used to jsl into compiled scaler += += += Setup for call += -------------- += GC_MODE read mode 1, write mode 2 += GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff += GC_INDEX pointing at GC_BITMASK += +======================= +*/ + +void SimpleScaleShape (int xcenter, int shapenum, unsigned height) +{ + t_compshape _seg *shape; + t_compscale _seg *comptable; + unsigned scale,srcx,stopx,tempx; + int t; + unsigned far *cmdptr; + boolean leftvis,rightvis; + + + shape = PM_GetSpritePage (shapenum); + + scale = height>>1; + comptable = scaledirectory[scale]; + + *(((unsigned *)&linescale)+1)=(unsigned)comptable; // seg of far call + *(((unsigned *)&linecmds)+1)=(unsigned)shape; // seg of shape + +// +// scale to the left (from pixel 31 to shape->leftpix) +// + srcx = 32; + slinex = xcenter; + stopx = shape->leftpix; + cmdptr = &shape->dataofs[31-stopx]; + + while ( --srcx >=stopx ) + { + (unsigned)linecmds = *cmdptr--; + if ( !(slinewidth = comptable->width[srcx]) ) + continue; + + slinex -= slinewidth; + ScaleLine (); + } + + +// +// scale to the right +// + slinex = xcenter; + stopx = shape->rightpix; + if (shape->leftpix<31) + { + srcx = 31; + cmdptr = &shape->dataofs[32-shape->leftpix]; + } + else + { + srcx = shape->leftpix-1; + cmdptr = &shape->dataofs[0]; + } + slinewidth = 0; + + while ( ++srcx <= stopx ) + { + (unsigned)linecmds = *cmdptr++; + if ( !(slinewidth = comptable->width[srcx]) ) + continue; + + ScaleLine (); + slinex+=slinewidth; + } +} + + + + +// +// bit mask tables for drawing scaled strips up to eight pixels wide +// +// down here so the STUPID inline assembler doesn't get confused! +// + + +byte mapmasks1[4][8] = { +{1 ,3 ,7 ,15,15,15,15,15}, +{2 ,6 ,14,14,14,14,14,14}, +{4 ,12,12,12,12,12,12,12}, +{8 ,8 ,8 ,8 ,8 ,8 ,8 ,8} }; + +byte mapmasks2[4][8] = { +{0 ,0 ,0 ,0 ,1 ,3 ,7 ,15}, +{0 ,0 ,0 ,1 ,3 ,7 ,15,15}, +{0 ,0 ,1 ,3 ,7 ,15,15,15}, +{0 ,1 ,3 ,7 ,15,15,15,15} }; + +byte mapmasks3[4][8] = { +{0 ,0 ,0 ,0 ,0 ,0 ,0 ,0}, +{0 ,0 ,0 ,0 ,0 ,0 ,0 ,1}, +{0 ,0 ,0 ,0 ,0 ,0 ,1 ,3}, +{0 ,0 ,0 ,0 ,0 ,1 ,3 ,7} }; + + +unsigned wordmasks[8][8] = { +{0x0080,0x00c0,0x00e0,0x00f0,0x00f8,0x00fc,0x00fe,0x00ff}, +{0x0040,0x0060,0x0070,0x0078,0x007c,0x007e,0x007f,0x807f}, +{0x0020,0x0030,0x0038,0x003c,0x003e,0x003f,0x803f,0xc03f}, +{0x0010,0x0018,0x001c,0x001e,0x001f,0x801f,0xc01f,0xe01f}, +{0x0008,0x000c,0x000e,0x000f,0x800f,0xc00f,0xe00f,0xf00f}, +{0x0004,0x0006,0x0007,0x8007,0xc007,0xe007,0xf007,0xf807}, +{0x0002,0x0003,0x8003,0xc003,0xe003,0xf003,0xf803,0xfc03}, +{0x0001,0x8001,0xc001,0xe001,0xf001,0xf801,0xfc01,0xfe01} }; + +int slinex,slinewidth; +unsigned far *linecmds; +long linescale; +unsigned maskword; + diff --git a/src/lib/hb/wl_state.c b/src/lib/hb/wl_state.c new file mode 100755 index 00000000..ad534ba9 --- /dev/null +++ b/src/lib/hb/wl_state.c @@ -0,0 +1,1480 @@ +// WL_STATE.C + +#include "WL_DEF.H" +#pragma hdrstop + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +dirtype opposite[9] = + {west,southwest,south,southeast,east,northeast,north,northwest,nodir}; + +dirtype diagonal[9][9] = +{ +/* east */ {nodir,nodir,northeast,nodir,nodir,nodir,southeast,nodir,nodir}, + {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, +/* north */ {northeast,nodir,nodir,nodir,northwest,nodir,nodir,nodir,nodir}, + {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, +/* west */ {nodir,nodir,northwest,nodir,nodir,nodir,southwest,nodir,nodir}, + {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, +/* south */ {southeast,nodir,nodir,nodir,southwest,nodir,nodir,nodir,nodir}, + {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}, + {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir} +}; + + + +void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state); +void NewState (objtype *ob, statetype *state); + +boolean TryWalk (objtype *ob); +void MoveObj (objtype *ob, long move); + +void KillActor (objtype *ob); +void DamageActor (objtype *ob, unsigned damage); + +boolean CheckLine (objtype *ob); +void FirstSighting (objtype *ob); +boolean CheckSight (objtype *ob); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +//=========================================================================== + + +/* +=================== += += SpawnNewObj += += Spaws a new actor at the given TILE coordinates, with the given state, and += the given size in GLOBAL units. += += new = a pointer to an initialized new actor += +=================== +*/ + +void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state) +{ + GetNewActor (); + new->state = state; + if (state->tictime) + new->ticcount = US_RndT () % state->tictime; + else + new->ticcount = 0; + + new->tilex = tilex; + new->tiley = tiley; + new->x = ((long)tilex<y = ((long)tiley<dir = nodir; + + actorat[tilex][tiley] = new; + new->areanumber = + *(mapsegs[0] + farmapylookup[new->tiley]+new->tilex) - AREATILE; +} + + + +/* +=================== += += NewState += += Changes ob to a new state, setting ticcount to the max for that state += +=================== +*/ + +void NewState (objtype *ob, statetype *state) +{ + ob->state = state; + ob->ticcount = state->tictime; +} + + + +/* +============================================================================= + + ENEMY TILE WORLD MOVEMENT CODE + +============================================================================= +*/ + + +/* +================================== += += TryWalk += += Attempts to move ob in its current (ob->dir) direction. += += If blocked by either a wall or an actor returns FALSE += += If move is either clear or blocked only by a door, returns TRUE and sets += += ob->tilex = new destination += ob->tiley += ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination += ob->distance = TILEGLOBAl, or -doornumber if a door is blocking the way += += If a door is in the way, an OpenDoor call is made to start it opening. += The actor code should wait until += doorobjlist[-ob->distance].action = dr_open, meaning the door has been += fully opened += +================================== +*/ + +#define CHECKDIAG(x,y) \ +{ \ + temp=(unsigned)actorat[x][y]; \ + if (temp) \ + { \ + if (temp<256) \ + return false; \ + if (((objtype *)temp)->flags&FL_SHOOTABLE) \ + return false; \ + } \ +} + +#define CHECKSIDE(x,y) \ +{ \ + temp=(unsigned)actorat[x][y]; \ + if (temp) \ + { \ + if (temp<128) \ + return false; \ + if (temp<256) \ + doornum = temp&63; \ + else if (((objtype *)temp)->flags&FL_SHOOTABLE)\ + return false; \ + } \ +} + + +boolean TryWalk (objtype *ob) +{ + int doornum; + unsigned temp; + + doornum = -1; + + if (ob->obclass == inertobj) + { + switch (ob->dir) + { + case north: + ob->tiley--; + break; + + case northeast: + ob->tilex++; + ob->tiley--; + break; + + case east: + ob->tilex++; + break; + + case southeast: + ob->tilex++; + ob->tiley++; + break; + + case south: + ob->tiley++; + break; + + case southwest: + ob->tilex--; + ob->tiley++; + break; + + case west: + ob->tilex--; + break; + + case northwest: + ob->tilex--; + ob->tiley--; + break; + } + } + else + switch (ob->dir) + { + case north: + if (ob->obclass == dogobj || ob->obclass == fakeobj) + { + CHECKDIAG(ob->tilex,ob->tiley-1); + } + else + { + CHECKSIDE(ob->tilex,ob->tiley-1); + } + ob->tiley--; + break; + + case northeast: + CHECKDIAG(ob->tilex+1,ob->tiley-1); + CHECKDIAG(ob->tilex+1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley-1); + ob->tilex++; + ob->tiley--; + break; + + case east: + if (ob->obclass == dogobj || ob->obclass == fakeobj) + { + CHECKDIAG(ob->tilex+1,ob->tiley); + } + else + { + CHECKSIDE(ob->tilex+1,ob->tiley); + } + ob->tilex++; + break; + + case southeast: + CHECKDIAG(ob->tilex+1,ob->tiley+1); + CHECKDIAG(ob->tilex+1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley+1); + ob->tilex++; + ob->tiley++; + break; + + case south: + if (ob->obclass == dogobj || ob->obclass == fakeobj) + { + CHECKDIAG(ob->tilex,ob->tiley+1); + } + else + { + CHECKSIDE(ob->tilex,ob->tiley+1); + } + ob->tiley++; + break; + + case southwest: + CHECKDIAG(ob->tilex-1,ob->tiley+1); + CHECKDIAG(ob->tilex-1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley+1); + ob->tilex--; + ob->tiley++; + break; + + case west: + if (ob->obclass == dogobj || ob->obclass == fakeobj) + { + CHECKDIAG(ob->tilex-1,ob->tiley); + } + else + { + CHECKSIDE(ob->tilex-1,ob->tiley); + } + ob->tilex--; + break; + + case northwest: + CHECKDIAG(ob->tilex-1,ob->tiley-1); + CHECKDIAG(ob->tilex-1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley-1); + ob->tilex--; + ob->tiley--; + break; + + case nodir: + return false; + + default: + Quit ("Walk: Bad dir"); + } + + if (doornum != -1) + { + OpenDoor (doornum); + ob->distance = -doornum-1; + return true; + } + + + ob->areanumber = + *(mapsegs[0] + farmapylookup[ob->tiley]+ob->tilex) - AREATILE; + + ob->distance = TILEGLOBAL; + return true; +} + + + +/* +================================== += += SelectDodgeDir += += Attempts to choose and initiate a movement for ob that sends it towards += the player while dodging += += If there is no possible move (ob is totally surrounded) += += ob->dir = nodir += += Otherwise += += ob->dir = new direction to follow += ob->distance = TILEGLOBAL or -doornumber += ob->tilex = new destination += ob->tiley += ob->areanumber = the floor tile number (0-(NUMAREAS-1)) of destination += +================================== +*/ + +void SelectDodgeDir (objtype *ob) +{ + int deltax,deltay,i; + unsigned absdx,absdy; + dirtype dirtry[5]; + dirtype turnaround,tdir; + + if (ob->flags & FL_FIRSTATTACK) + { + // + // turning around is only ok the very first time after noticing the + // player + // + turnaround = nodir; + ob->flags &= ~FL_FIRSTATTACK; + } + else + turnaround=opposite[ob->dir]; + + deltax = player->tilex - ob->tilex; + deltay = player->tiley - ob->tiley; + +// +// arange 5 direction choices in order of preference +// the four cardinal directions plus the diagonal straight towards +// the player +// + + if (deltax>0) + { + dirtry[1]= east; + dirtry[3]= west; + } + else + { + dirtry[1]= west; + dirtry[3]= east; + } + + if (deltay>0) + { + dirtry[2]= south; + dirtry[4]= north; + } + else + { + dirtry[2]= north; + dirtry[4]= south; + } + +// +// randomize a bit for dodging +// + absdx = abs(deltax); + absdy = abs(deltay); + + if (absdx > absdy) + { + tdir = dirtry[1]; + dirtry[1] = dirtry[2]; + dirtry[2] = tdir; + tdir = dirtry[3]; + dirtry[3] = dirtry[4]; + dirtry[4] = tdir; + } + + if (US_RndT() < 128) + { + tdir = dirtry[1]; + dirtry[1] = dirtry[2]; + dirtry[2] = tdir; + tdir = dirtry[3]; + dirtry[3] = dirtry[4]; + dirtry[4] = tdir; + } + + dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ]; + +// +// try the directions util one works +// + for (i=0;i<5;i++) + { + if ( dirtry[i] == nodir || dirtry[i] == turnaround) + continue; + + ob->dir = dirtry[i]; + if (TryWalk(ob)) + return; + } + +// +// turn around only as a last resort +// + if (turnaround != nodir) + { + ob->dir = turnaround; + + if (TryWalk(ob)) + return; + } + + ob->dir = nodir; +} + + +/* +============================ += += SelectChaseDir += += As SelectDodgeDir, but doesn't try to dodge += +============================ +*/ + +void SelectChaseDir (objtype *ob) +{ + int deltax,deltay,i; + dirtype d[3]; + dirtype tdir, olddir, turnaround; + + + olddir=ob->dir; + turnaround=opposite[olddir]; + + deltax=player->tilex - ob->tilex; + deltay=player->tiley - ob->tiley; + + d[1]=nodir; + d[2]=nodir; + + if (deltax>0) + d[1]= east; + else if (deltax<0) + d[1]= west; + if (deltay>0) + d[2]=south; + else if (deltay<0) + d[2]=north; + + if (abs(deltay)>abs(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) + { + ob->dir=d[1]; + if (TryWalk(ob)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=nodir) + { + ob->dir=d[2]; + if (TryWalk(ob)) + return; + } + +/* there is no direct path to the player, so pick another direction */ + + if (olddir!=nodir) + { + ob->dir=olddir; + if (TryWalk(ob)) + return; + } + + if (US_RndT()>128) /*randomly determine direction of search*/ + { + for (tdir=north;tdir<=west;tdir++) + { + if (tdir!=turnaround) + { + ob->dir=tdir; + if ( TryWalk(ob) ) + return; + } + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + if (tdir!=turnaround) + { + ob->dir=tdir; + if ( TryWalk(ob) ) + return; + } + } + } + + if (turnaround != nodir) + { + ob->dir=turnaround; + if (ob->dir != nodir) + { + if ( TryWalk(ob) ) + return; + } + } + + ob->dir = nodir; // can't move +} + + +/* +============================ += += SelectRunDir += += Run Away from player += +============================ +*/ + +void SelectRunDir (objtype *ob) +{ + int deltax,deltay,i; + dirtype d[3]; + dirtype tdir, olddir, turnaround; + + + deltax=player->tilex - ob->tilex; + deltay=player->tiley - ob->tiley; + + if (deltax<0) + d[1]= east; + else + d[1]= west; + if (deltay<0) + d[2]=south; + else + d[2]=north; + + if (abs(deltay)>abs(deltax)) + { + tdir=d[1]; + d[1]=d[2]; + d[2]=tdir; + } + + ob->dir=d[1]; + if (TryWalk(ob)) + return; /*either moved forward or attacked*/ + + ob->dir=d[2]; + if (TryWalk(ob)) + return; + +/* there is no direct path to the player, so pick another direction */ + + if (US_RndT()>128) /*randomly determine direction of search*/ + { + for (tdir=north;tdir<=west;tdir++) + { + ob->dir=tdir; + if ( TryWalk(ob) ) + return; + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + ob->dir=tdir; + if ( TryWalk(ob) ) + return; + } + } + + ob->dir = nodir; // can't move +} + + +/* +================= += += MoveObj += += Moves ob be move global units in ob->dir direction += Actors are not allowed to move inside the player += Does NOT check to see if the move is tile map valid += += ob->x = adjusted for new position += ob->y += +================= +*/ + +void MoveObj (objtype *ob, long move) +{ + long deltax,deltay; + + switch (ob->dir) + { + case north: + ob->y -= move; + break; + case northeast: + ob->x += move; + ob->y -= move; + break; + case east: + ob->x += move; + break; + case southeast: + ob->x += move; + ob->y += move; + break; + case south: + ob->y += move; + break; + case southwest: + ob->x -= move; + ob->y += move; + break; + case west: + ob->x -= move; + break; + case northwest: + ob->x -= move; + ob->y -= move; + break; + + case nodir: + return; + + default: + Quit ("MoveObj: bad dir!"); + } + +// +// check to make sure it's not on top of player +// + if (areabyplayer[ob->areanumber]) + { + deltax = ob->x - player->x; + if (deltax < -MINACTORDIST || deltax > MINACTORDIST) + goto moveok; + deltay = ob->y - player->y; + if (deltay < -MINACTORDIST || deltay > MINACTORDIST) + goto moveok; + + if (ob->obclass == ghostobj || ob->obclass == spectreobj) + TakeDamage (tics*2,ob); + + // + // back up + // + switch (ob->dir) + { + case north: + ob->y += move; + break; + case northeast: + ob->x -= move; + ob->y += move; + break; + case east: + ob->x -= move; + break; + case southeast: + ob->x -= move; + ob->y -= move; + break; + case south: + ob->y -= move; + break; + case southwest: + ob->x += move; + ob->y -= move; + break; + case west: + ob->x += move; + break; + case northwest: + ob->x += move; + ob->y += move; + break; + + case nodir: + return; + } + return; + } +moveok: + ob->distance -=move; +} + +/* +============================================================================= + + STUFF + +============================================================================= +*/ + +/* +=============== += += DropItem += += Tries to drop a bonus item somewhere in the tiles surrounding the += given tilex/tiley += +=============== +*/ + +void DropItem (stat_t itemtype, int tilex, int tiley) +{ + int x,y,xl,xh,yl,yh; + +// +// find a free spot to put it in +// + if (!actorat[tilex][tiley]) + { + PlaceItemType (itemtype, tilex,tiley); + return; + } + + xl = tilex-1; + xh = tilex+1; + yl = tiley-1; + yh = tiley+1; + + for (x=xl ; x<= xh ; x++) + for (y=yl ; y<= yh ; y++) + if (!actorat[x][y]) + { + PlaceItemType (itemtype, x,y); + return; + } +} + + + +/* +=============== += += KillActor += +=============== +*/ + +void KillActor (objtype *ob) +{ + int tilex,tiley; + + tilex = ob->tilex = ob->x >> TILESHIFT; // drop item on center + tiley = ob->tiley = ob->y >> TILESHIFT; + + switch (ob->obclass) + { + case guardobj: + GivePoints (100); + NewState (ob,&s_grddie1); + PlaceItemType (bo_clip2,tilex,tiley); + break; + + case officerobj: + GivePoints (400); + NewState (ob,&s_ofcdie1); + PlaceItemType (bo_clip2,tilex,tiley); + break; + + case mutantobj: + GivePoints (700); + NewState (ob,&s_mutdie1); + PlaceItemType (bo_clip2,tilex,tiley); + break; + + case ssobj: + GivePoints (500); + NewState (ob,&s_ssdie1); + if (gamestate.bestweapon < wp_machinegun) + PlaceItemType (bo_machinegun,tilex,tiley); + else + PlaceItemType (bo_clip2,tilex,tiley); + break; + + case dogobj: + GivePoints (200); + NewState (ob,&s_dogdie1); + break; + +#ifndef SPEAR + case bossobj: + GivePoints (5000); + NewState (ob,&s_bossdie1); + PlaceItemType (bo_key1,tilex,tiley); + break; + + case gretelobj: + GivePoints (5000); + NewState (ob,&s_greteldie1); + PlaceItemType (bo_key1,tilex,tiley); + break; + + case giftobj: + GivePoints (5000); + gamestate.killx = player->x; + gamestate.killy = player->y; + NewState (ob,&s_giftdie1); + break; + + case fatobj: + GivePoints (5000); + gamestate.killx = player->x; + gamestate.killy = player->y; + NewState (ob,&s_fatdie1); + break; + + case schabbobj: + GivePoints (5000); + gamestate.killx = player->x; + gamestate.killy = player->y; + NewState (ob,&s_schabbdie1); + A_DeathScream(ob); + break; + case fakeobj: + GivePoints (2000); + NewState (ob,&s_fakedie1); + break; + + case mechahitlerobj: + GivePoints (5000); + NewState (ob,&s_mechadie1); + break; + case realhitlerobj: + GivePoints (5000); + gamestate.killx = player->x; + gamestate.killy = player->y; + NewState (ob,&s_hitlerdie1); + A_DeathScream(ob); + break; +#else + case spectreobj: + GivePoints (200); + NewState (ob,&s_spectredie1); + break; + + case angelobj: + GivePoints (5000); + NewState (ob,&s_angeldie1); + break; + + case transobj: + GivePoints (5000); + NewState (ob,&s_transdie0); + PlaceItemType (bo_key1,tilex,tiley); + break; + + case uberobj: + GivePoints (5000); + NewState (ob,&s_uberdie0); + PlaceItemType (bo_key1,tilex,tiley); + break; + + case willobj: + GivePoints (5000); + NewState (ob,&s_willdie1); + PlaceItemType (bo_key1,tilex,tiley); + break; + + case deathobj: + GivePoints (5000); + NewState (ob,&s_deathdie1); + PlaceItemType (bo_key1,tilex,tiley); + break; +#endif + } + + gamestate.killcount++; + ob->flags &= ~FL_SHOOTABLE; + actorat[ob->tilex][ob->tiley] = NULL; + ob->flags |= FL_NONMARK; +} + + + +/* +=================== += += DamageActor += += Called when the player succesfully hits an enemy. += += Does damage points to enemy ob, either putting it into a stun frame or += killing it. += +=================== +*/ + +void DamageActor (objtype *ob, unsigned damage) +{ + madenoise = true; + +// +// do double damage if shooting a non attack mode actor +// + if ( !(ob->flags & FL_ATTACKMODE) ) + damage <<= 1; + + ob->hitpoints -= damage; + + if (ob->hitpoints<=0) + KillActor (ob); + else + { + if (! (ob->flags & FL_ATTACKMODE) ) + FirstSighting (ob); // put into combat mode + + switch (ob->obclass) // dogs only have one hit point + { + case guardobj: + if (ob->hitpoints&1) + NewState (ob,&s_grdpain); + else + NewState (ob,&s_grdpain1); + break; + + case officerobj: + if (ob->hitpoints&1) + NewState (ob,&s_ofcpain); + else + NewState (ob,&s_ofcpain1); + break; + + case mutantobj: + if (ob->hitpoints&1) + NewState (ob,&s_mutpain); + else + NewState (ob,&s_mutpain1); + break; + + case ssobj: + if (ob->hitpoints&1) + NewState (ob,&s_sspain); + else + NewState (ob,&s_sspain1); + + break; + + } + } +} + +/* +============================================================================= + + CHECKSIGHT + +============================================================================= +*/ + + +/* +===================== += += CheckLine += += Returns true if a straight line between the player and ob is unobstructed += +===================== +*/ + +boolean CheckLine (objtype *ob) +{ + int x1,y1,xt1,yt1,x2,y2,xt2,yt2; + int x,y; + int xdist,ydist,xstep,ystep; + int temp; + int partial,delta; + long ltemp; + int xfrac,yfrac,deltafrac; + unsigned value,intercept; + + x1 = ob->x >> UNSIGNEDSHIFT; // 1/256 tile precision + y1 = ob->y >> UNSIGNEDSHIFT; + xt1 = x1 >> 8; + yt1 = y1 >> 8; + + x2 = plux; + y2 = pluy; + xt2 = player->tilex; + yt2 = player->tiley; + + + xdist = abs(xt2-xt1); + + if (xdist > 0) + { + if (xt2 > xt1) + { + partial = 256-(x1&0xff); + xstep = 1; + } + else + { + partial = x1&0xff; + xstep = -1; + } + + deltafrac = abs(x2-x1); + delta = y2-y1; + ltemp = ((long)delta<<8)/deltafrac; + if (ltemp > 0x7fffl) + ystep = 0x7fff; + else if (ltemp < -0x7fffl) + ystep = -0x7fff; + else + ystep = ltemp; + yfrac = y1 + (((long)ystep*partial) >>8); + + x = xt1+xstep; + xt2 += xstep; + do + { + y = yfrac>>8; + yfrac += ystep; + + value = (unsigned)tilemap[x][y]; + x += xstep; + + if (!value) + continue; + + if (value<128 || value>256) + return false; + + // + // see if the door is open enough + // + value &= ~0x80; + intercept = yfrac-ystep/2; + + if (intercept>doorposition[value]) + return false; + + } while (x != xt2); + } + + ydist = abs(yt2-yt1); + + if (ydist > 0) + { + if (yt2 > yt1) + { + partial = 256-(y1&0xff); + ystep = 1; + } + else + { + partial = y1&0xff; + ystep = -1; + } + + deltafrac = abs(y2-y1); + delta = x2-x1; + ltemp = ((long)delta<<8)/deltafrac; + if (ltemp > 0x7fffl) + xstep = 0x7fff; + else if (ltemp < -0x7fffl) + xstep = -0x7fff; + else + xstep = ltemp; + xfrac = x1 + (((long)xstep*partial) >>8); + + y = yt1 + ystep; + yt2 += ystep; + do + { + x = xfrac>>8; + xfrac += xstep; + + value = (unsigned)tilemap[x][y]; + y += ystep; + + if (!value) + continue; + + if (value<128 || value>256) + return false; + + // + // see if the door is open enough + // + value &= ~0x80; + intercept = xfrac-xstep/2; + + if (intercept>doorposition[value]) + return false; + } while (y != yt2); + } + + return true; +} + + + +/* +================ += += CheckSight += += Checks a straight line between player and current object += += If the sight is ok, check alertness and angle to see if they notice += += returns true if the player has been spoted += +================ +*/ + +#define MINSIGHT 0x18000l + +boolean CheckSight (objtype *ob) +{ + long deltax,deltay; + +// +// don't bother tracing a line if the area isn't connected to the player's +// + if (!areabyplayer[ob->areanumber]) + return false; + +// +// if the player is real close, sight is automatic +// + deltax = player->x - ob->x; + deltay = player->y - ob->y; + + if (deltax > -MINSIGHT && deltax < MINSIGHT + && deltay > -MINSIGHT && deltay < MINSIGHT) + return true; + +// +// see if they are looking in the right direction +// + switch (ob->dir) + { + case north: + if (deltay > 0) + return false; + break; + + case east: + if (deltax < 0) + return false; + break; + + case south: + if (deltay < 0) + return false; + break; + + case west: + if (deltax > 0) + return false; + break; + } + +// +// trace a line to check for blocking tiles (corners) +// + return CheckLine (ob); + +} + + + +/* +=============== += += FirstSighting += += Puts an actor into attack mode and possibly reverses the direction += if the player is behind it += +=============== +*/ + +void FirstSighting (objtype *ob) +{ +// +// react to the player +// + switch (ob->obclass) + { + case guardobj: + PlaySoundLocActor(HALTSND,ob); + NewState (ob,&s_grdchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case officerobj: + PlaySoundLocActor(SPIONSND,ob); + NewState (ob,&s_ofcchase1); + ob->speed *= 5; // go faster when chasing player + break; + + case mutantobj: + NewState (ob,&s_mutchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case ssobj: + PlaySoundLocActor(SCHUTZADSND,ob); + NewState (ob,&s_sschase1); + ob->speed *= 4; // go faster when chasing player + break; + + case dogobj: + PlaySoundLocActor(DOGBARKSND,ob); + NewState (ob,&s_dogchase1); + ob->speed *= 2; // go faster when chasing player + break; + +#ifndef SPEAR + case bossobj: + SD_PlaySound(GUTENTAGSND); + NewState (ob,&s_bosschase1); + ob->speed = SPDPATROL*3; // go faster when chasing player + break; + + case gretelobj: + SD_PlaySound(KEINSND); + NewState (ob,&s_gretelchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case giftobj: + SD_PlaySound(EINESND); + NewState (ob,&s_giftchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case fatobj: + SD_PlaySound(ERLAUBENSND); + NewState (ob,&s_fatchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case schabbobj: + SD_PlaySound(SCHABBSHASND); + NewState (ob,&s_schabbchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case fakeobj: + SD_PlaySound(TOT_HUNDSND); + NewState (ob,&s_fakechase1); + ob->speed *= 3; // go faster when chasing player + break; + + case mechahitlerobj: + SD_PlaySound(DIESND); + NewState (ob,&s_mechachase1); + ob->speed *= 3; // go faster when chasing player + break; + + case realhitlerobj: + SD_PlaySound(DIESND); + NewState (ob,&s_hitlerchase1); + ob->speed *= 5; // go faster when chasing player + break; + + case ghostobj: + NewState (ob,&s_blinkychase1); + ob->speed *= 2; // go faster when chasing player + break; +#else + + case spectreobj: + SD_PlaySound(GHOSTSIGHTSND); + NewState (ob,&s_spectrechase1); + ob->speed = 800; // go faster when chasing player + break; + + case angelobj: + SD_PlaySound(ANGELSIGHTSND); + NewState (ob,&s_angelchase1); + ob->speed = 1536; // go faster when chasing player + break; + + case transobj: + SD_PlaySound(TRANSSIGHTSND); + NewState (ob,&s_transchase1); + ob->speed = 1536; // go faster when chasing player + break; + + case uberobj: + NewState (ob,&s_uberchase1); + ob->speed = 3000; // go faster when chasing player + break; + + case willobj: + SD_PlaySound(WILHELMSIGHTSND); + NewState (ob,&s_willchase1); + ob->speed = 2048; // go faster when chasing player + break; + + case deathobj: + SD_PlaySound(KNIGHTSIGHTSND); + NewState (ob,&s_deathchase1); + ob->speed = 2048; // go faster when chasing player + break; + +#endif + } + + if (ob->distance < 0) + ob->distance = 0; // ignore the door opening command + + ob->flags |= FL_ATTACKMODE|FL_FIRSTATTACK; +} + + + +/* +=============== += += SightPlayer += += Called by actors that ARE NOT chasing the player. If the player += is detected (by sight, noise, or proximity), the actor is put into += it's combat frame and true is returned. += += Incorporates a random reaction delay += +=============== +*/ + +boolean SightPlayer (objtype *ob) +{ + if (ob->flags & FL_ATTACKMODE) + Quit ("An actor in ATTACKMODE called SightPlayer!"); + + if (ob->temp2) + { + // + // count down reaction time + // + ob->temp2 -= tics; + if (ob->temp2 > 0) + return false; + ob->temp2 = 0; // time to react + } + else + { + if (!areabyplayer[ob->areanumber]) + return false; + + if (ob->flags & FL_AMBUSH) + { + if (!CheckSight (ob)) + return false; + ob->flags &= ~FL_AMBUSH; + } + else + { + if (!madenoise && !CheckSight (ob)) + return false; + } + + + switch (ob->obclass) + { + case guardobj: + ob->temp2 = 1+US_RndT()/4; + break; + case officerobj: + ob->temp2 = 2; + break; + case mutantobj: + ob->temp2 = 1+US_RndT()/6; + break; + case ssobj: + ob->temp2 = 1+US_RndT()/6; + break; + case dogobj: + ob->temp2 = 1+US_RndT()/8; + break; + + case bossobj: + case schabbobj: + case fakeobj: + case mechahitlerobj: + case realhitlerobj: + case gretelobj: + case giftobj: + case fatobj: + case spectreobj: + case angelobj: + case transobj: + case uberobj: + case willobj: + case deathobj: + ob->temp2 = 1; + break; + } + return false; + } + + FirstSighting (ob); + + return true; +} + + diff --git a/src/lib/hb/wl_text.c b/src/lib/hb/wl_text.c new file mode 100755 index 00000000..1df86b83 --- /dev/null +++ b/src/lib/hb/wl_text.c @@ -0,0 +1,859 @@ +// WL_TEXT.C + +#include "WL_DEF.H" +#pragma hdrstop + +/* +============================================================================= + +TEXT FORMATTING COMMANDS +------------------------ +^C Change text color +^E[enter] End of layout (all pages) +^G,,[enter] Draw a graphic and push margins +^P[enter] start new page, must be the first chars in a layout +^L,[ENTER] Locate to a specific spot, x in pixels, y in lines + +============================================================================= +*/ + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define BACKCOLOR 0x11 + + +#define WORDLIMIT 80 +#define FONTHEIGHT 10 +#define TOPMARGIN 16 +#define BOTTOMMARGIN 32 +#define LEFTMARGIN 16 +#define RIGHTMARGIN 16 +#define PICMARGIN 8 +#define TEXTROWS ((200-TOPMARGIN-BOTTOMMARGIN)/FONTHEIGHT) +#define SPACEWIDTH 7 +#define SCREENPIXWIDTH 320 +#define SCREENMID (SCREENPIXWIDTH/2) + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +int pagenum,numpages; + +unsigned leftmargin[TEXTROWS],rightmargin[TEXTROWS]; +char far *text; +unsigned rowon; + +int picx,picy,picnum,picdelay; +boolean layoutdone; + +//=========================================================================== + +#ifndef JAPAN +/* +===================== += += RipToEOL += +===================== +*/ + +void RipToEOL (void) +{ + while (*text++ != '\n') // scan to end of line + ; +} + + +/* +===================== += += ParseNumber += +===================== +*/ + +int ParseNumber (void) +{ + char ch; + char num[80],*numptr; + +// +// scan until a number is found +// + ch = *text; + while (ch < '0' || ch >'9') + ch = *++text; + +// +// copy the number out +// + numptr = num; + do + { + *numptr++ = ch; + ch = *++text; + } while (ch >= '0' && ch <= '9'); + *numptr = 0; + + return atoi (num); +} + + + +/* +===================== += += ParsePicCommand += += Call with text pointing just after a ^P += Upon exit text points to the start of next line += +===================== +*/ + +void ParsePicCommand (void) +{ + picy=ParseNumber(); + picx=ParseNumber(); + picnum=ParseNumber(); + RipToEOL (); +} + + +void ParseTimedCommand (void) +{ + picy=ParseNumber(); + picx=ParseNumber(); + picnum=ParseNumber(); + picdelay=ParseNumber(); + RipToEOL (); +} + + +/* +===================== += += TimedPicCommand += += Call with text pointing just after a ^P += Upon exit text points to the start of next line += +===================== +*/ + +void TimedPicCommand (void) +{ + ParseTimedCommand (); + +// +// update the screen, and wait for time delay +// + VW_UpdateScreen (); + +// +// wait for time +// + TimeCount = 0; + while (TimeCount < picdelay) + ; + +// +// draw pic +// + VWB_DrawPic (picx&~7,picy,picnum); +} + + +/* +===================== += += HandleCommand += +===================== +*/ + +void HandleCommand (void) +{ + int i,margin,top,bottom; + int picwidth,picheight,picmid; + + switch (toupper(*++text)) + { + case 'B': + picy=ParseNumber(); + picx=ParseNumber(); + picwidth=ParseNumber(); + picheight=ParseNumber(); + VWB_Bar(picx,picy,picwidth,picheight,BACKCOLOR); + RipToEOL(); + break; + case ';': // comment + RipToEOL(); + break; + case 'P': // ^P is start of next page, ^E is end of file + case 'E': + layoutdone = true; + text--; // back up to the '^' + break; + + case 'C': // ^c changes text color + i = toupper(*++text); + if (i>='0' && i<='9') + fontcolor = i-'0'; + else if (i>='A' && i<='F') + fontcolor = i-'A'+10; + + fontcolor *= 16; + i = toupper(*++text); + if (i>='0' && i<='9') + fontcolor += i-'0'; + else if (i>='A' && i<='F') + fontcolor += i-'A'+10; + text++; + break; + + case '>': + px = 160; + text++; + break; + + case 'L': + py=ParseNumber(); + rowon = (py-TOPMARGIN)/FONTHEIGHT; + py = TOPMARGIN+rowon*FONTHEIGHT; + px=ParseNumber(); + while (*text++ != '\n') // scan to end of line + ; + break; + + case 'T': // ^Tyyy,xxx,ppp,ttt waits ttt tics, then draws pic + TimedPicCommand (); + break; + + case 'G': // ^Gyyy,xxx,ppp draws graphic + ParsePicCommand (); + VWB_DrawPic (picx&~7,picy,picnum); + picwidth = pictable[picnum-STARTPICS].width; + picheight = pictable[picnum-STARTPICS].height; + // + // adjust margins + // + picmid = picx + picwidth/2; + if (picmid > SCREENMID) + margin = picx-PICMARGIN; // new right margin + else + margin = picx+picwidth+PICMARGIN; // new left margin + + top = (picy-TOPMARGIN)/FONTHEIGHT; + if (top<0) + top = 0; + bottom = (picy+picheight-TOPMARGIN)/FONTHEIGHT; + if (bottom>=TEXTROWS) + bottom = TEXTROWS-1; + + for (i=top;i<=bottom;i++) + if (picmid > SCREENMID) + rightmargin[i] = margin; + else + leftmargin[i] = margin; + + // + // adjust this line if needed + // + if (px < leftmargin[rowon]) + px = leftmargin[rowon]; + break; + } +} + + +/* +===================== += += NewLine += +===================== +*/ + +void NewLine (void) +{ + char ch; + + if (++rowon == TEXTROWS) + { + // + // overflowed the page, so skip until next page break + // + layoutdone = true; + do + { + if (*text == '^') + { + ch = toupper(*(text+1)); + if (ch == 'E' || ch == 'P') + { + layoutdone = true; + return; + } + } + text++; + + } while (1); + + } + px = leftmargin[rowon]; + py+= FONTHEIGHT; +} + + + +/* +===================== += += HandleCtrls += +===================== +*/ + +void HandleCtrls (void) +{ + char ch; + + ch = *text++; // get the character and advance + + if (ch == '\n') + { + NewLine (); + return; + } + +} + + +/* +===================== += += HandleWord += +===================== +*/ + +void HandleWord (void) +{ + char word[WORDLIMIT]; + int i,wordindex; + unsigned wwidth,wheight,newpos; + + + // + // copy the next word into [word] + // + word[0] = *text++; + wordindex = 1; + while (*text>32) + { + word[wordindex] = *text++; + if (++wordindex == WORDLIMIT) + Quit ("PageLayout: Word limit exceeded"); + } + word[wordindex] = 0; // stick a null at end for C + + // + // see if it fits on this line + // + VW_MeasurePropString (word,&wwidth,&wheight); + + while (px+wwidth > rightmargin[rowon]) + { + NewLine (); + if (layoutdone) + return; // overflowed page + } + + // + // print it + // + newpos = px+wwidth; + VWB_DrawPropString (word); + px = newpos; + + // + // suck up any extra spaces + // + while (*text == ' ') + { + px += SPACEWIDTH; + text++; + } +} + +/* +===================== += += PageLayout += += Clears the screen, draws the pics on the page, and word wraps the text. += Returns a pointer to the terminating command += +===================== +*/ + +void PageLayout (boolean shownumber) +{ + int i,oldfontcolor; + char ch; + + oldfontcolor = fontcolor; + + fontcolor = 0; + +// +// clear the screen +// + VWB_Bar (0,0,320,200,BACKCOLOR); + VWB_DrawPic (0,0,H_TOPWINDOWPIC); + VWB_DrawPic (0,8,H_LEFTWINDOWPIC); + VWB_DrawPic (312,8,H_RIGHTWINDOWPIC); + VWB_DrawPic (8,176,H_BOTTOMINFOPIC); + + + for (i=0;i1) + { + #ifndef JAPAN + BackPage (); + BackPage (); + #else + pagenum--; + #endif + newpage = true; + } + break; + + case sc_Enter: + case sc_DownArrow: + case sc_PgDn: + case sc_RightArrow: // the text allready points at next page + if (pagenum