commit 727cbb8327b1df7b4a9255e4db2d54b424abf044 Author: archive Date: Mon Jul 8 00:00:00 2013 +0000 as released 2013-07-08 diff --git a/3D_ACT1.C b/3D_ACT1.C new file mode 100644 index 0000000..3cc7ddc --- /dev/null +++ b/3D_ACT1.C @@ -0,0 +1,2309 @@ +// 3D_ACT1.C + +#include "3D_DEF.H" +#pragma hdrstop + + +//=========================================================================== +// +// PROTOTYPES +// +//=========================================================================== + +void OpenDoor (int door); +void CloseDoor (int door); +void PlaceItemNearTile(int itemtype, int tilex, int tiley); + + +//=========================================================================== +// +// LOCALS +// +//=========================================================================== + + +concession_t ConHintList={0}; + + +/* +============================================================================= + + STATICS + +============================================================================= +*/ +statobj_t statobjlist[MAXSTATS],*laststatobj; + +stattype far statinfo[] = + +{ +{SPR_STAT_0,bo_water_puddle}, // Water Puddle SPR1V +{SPR_STAT_1,block}, // Containment Canister +{SPR_STAT_2,block}, // Lunch Table +{SPR_STAT_3,block}, // Floor Lamp +{SPR_STAT_4,block}, // Lab Table +{SPR_STAT_5,block}, // Pillar +{SPR_STAT_6}, // Blood Puddle +{SPR_STAT_7}, // Piss Puddle + +{SPR_STAT_8,block}, // Ficus Tree SPR2V +{SPR_STAT_9}, // Half-Eaten Corpse +{SPR_STAT_10,block}, // Water Fountain +{SPR_STAT_11,block}, // Plant 1 +{SPR_STAT_12,block}, // Vase +{SPR_STAT_13,block}, // General Table +{SPR_STAT_14}, // Ceiling Light +{SPR_STAT_15,block}, // General Chair + +{SPR_STAT_16,block}, // Kitchen Trash SPR3V +{SPR_STAT_17}, // Office Trash +{SPR_STAT_18,block}, // Plant 2 +{SPR_STAT_19,block}, // Gurney No-Blood +{SPR_STAT_20}, // Indirect Half-Sphere +{SPR_STAT_21}, // Exit Sign +{SPR_STAT_22}, // Transporter +{SPR_STAT_23,block}, // Body Can + +{SPR_STAT_24,bo_pistol}, // PISTOL SPR4V +{SPR_STAT_25,block}, // Statue + +{SPR_STAT_31,bo_clip}, // Charge Unit + +{SPR_STAT_27,bo_burst_rifle}, // Auto-Burst Rifle +{SPR_STAT_28,bo_ion_cannon}, // Particle Charged ION +{SPR_STAT_29,bo_firstaid}, // First Aid +{SPR_VSPIKE8,block}, // Static VSPIKE + +{SPR_STAT_26,bo_clip2}, // Big Charge pack/clip + +{SPR_STAT_32,bo_red_key}, // Red Key SPR5V +{SPR_STAT_33,bo_yellow_key}, // Yellow Key +{SPR_STAT_34,bo_bfg_cannon}, // BFG Cannon +{SPR_STAT_35,bo_blue_key}, // Blue Key +{SPR_STAT_36}, // OPEN +{SPR_STAT_37,block}, // Office Desk +{SPR_STAT_38,block}, // Office Chair +{SPR_STAT_39,block}, // Security Desk + +{SPR_STAT_40,bo_water}, // Full Water Bowl SPR7V +{SPR_STAT_41}, // Empty Water Bowl +{SPR_STAT_42,bo_chicken}, // Chicken Leg +{SPR_STAT_43}, // Chicken Bone +{SPR_STAT_44,bo_ham}, // Ham +{SPR_STAT_45}, // Ham Bone +{SPR_STAT_46,bo_grenade}, // Grande Launcher +{SPR_STAT_47}, // Video Game Machine + +{SPR_VPOST8,block}, // Static VPOST + +// -- VARIOUS -- + +{SPR_GURNEY_MUT_READY,block}, // 49 Gurney Mutant +{SPR_LCAN_ALIEN_READY,block}, // 50 Large Alien Canister +{SPR_SCAN_ALIEN_READY,block}, // 51 Small Alien Canister + +{SPR_GURNEY_MUT_EMPTY,block}, // 52 Gurney Mutant +{SPR_LCAN_ALIEN_EMPTY,block}, // 53 Large Alien Canister +{SPR_SCAN_ALIEN_EMPTY,block}, // 54 Small Alien Canister + +{SPR_OFC_DEAD}, // 55 Dead Gen Sci. + +{0}, // 56 Spacer + +{SPR_AIR_VENT,bo_plainvent}, // 57 Plain air vent +{SPR_AIR_VENT,bo_bloodvent}, // 58 Blood air vent +{SPR_AIR_VENT,bo_watervent}, // 59 Water air vent +{SPR_GRATE}, // 60 Floor Grate +{SPR_STEAM_PIPE}, // 61 Steam Pipe + +{SPR_STAT_48,bo_money_bag}, // 62 money bag +{SPR_STAT_49,bo_loot}, // 63 loot +{SPR_STAT_50,bo_gold}, // 64 gold +{SPR_STAT_51,bo_bonus}, // 65 bonus + +{SPR_STAT_52, block}, // 66 Greek Post +{SPR_STAT_53, block}, // 67 Red/Blue post +{SPR_STAT_54, block}, // 68 Red HiTech Post +{SPR_STAT_55}, // 69 Ceiling Lamp #2 +{SPR_STAT_56}, // 70 Ceiling Lamp #3 +{SPR_STAT_57}, // 71 Body Parts +{SPR_STAT_58}, // 72 OR Lamp +{SPR_STAT_59,block}, // 73 Office Sink +{SPR_STAT_57,0}, // EMPTY - Copy of 71 - Body Parts... +{SPR_CANDY_BAR,bo_candybar}, // 75 candy bar +{SPR_SANDWICH,bo_sandwich}, // 76 sandwich +{SPR_CRATE_1,block}, // 77 Crate #1 +{SPR_CRATE_2,block}, // 78 Crate #2 +{SPR_CRATE_3,block}, // 79 Crate #3 +{SPR_STAT_61,block}, // 80 Table +{SPR_STAT_62,block}, // 81 Chair +{SPR_STAT_63,block}, // 82 Stool +{SPR_STAT_64}, // 83 Gore + +{SPR_STAT_65,bo_gold3}, // Gold 3 +{SPR_STAT_66,bo_gold2}, // Gold 2 +{SPR_STAT_67,bo_gold1}, // Gold 1 + +{SPR_STAT_68,block}, // +{SPR_STAT_69,block}, // +{SPR_STAT_70,block}, // +{SPR_STAT_71,block}, // +{SPR_STAT_72,block}, // +{SPR_STAT_73}, // +{SPR_STAT_74}, // +{SPR_STAT_75}, // +{SPR_STAT_76}, // + +{SPR_RENT_DEAD}, // +{SPR_PRO_DEAD}, // +{SPR_SWAT_DEAD}, // +{SPR_GSCOUT_DEAD}, // +{SPR_FSCOUT_DEAD}, // +{SPR_MUTHUM1_DEAD}, +{SPR_MUTHUM2_DEAD}, +{SPR_LCAN_ALIEN_DEAD}, +{SPR_SCAN_ALIEN_DEAD}, +{SPR_GURNEY_MUT_DEAD}, +{SPR_TERROT_DEAD}, +{SPR_POD_DIE3}, +{SPR_STAT_77,bo_coin}, // Concession Machine Money +{SPR_STAT_78,bo_coin5}, // Concession Machine Money +{SPR_STAT_79}, // Auto-Charge Pistol + +{SPR_DOORBOMB,bo_plasma_detonator}, // Plasma Detonator +{SPR_RUBBLE}, // Door Rubble +{SPR_AUTOMAPPER,bo_automapper1}, // Auto Mapper Bonus #1 +{SPR_BONZI_TREE,block}, // BonziTree +{SPR_POT_PLANT,block}, // Yellow Potted Plant +{SPR_TUBE_PLANT,block}, // Tube Plant +{SPR_HITECH_CHAIR,block}, // Hi Tech table and chair +{SPR_DEAD_RENT}, // Dead AOG: Rent A Cop +{SPR_DEAD_PRO}, // Dead AOG: Pro Guard +{SPR_DEAD_SWAT}, // Dead AOG: Swat Guad + + +{-1} // terminator + +}; + + +/* +=============== += += InitStaticList += +=============== +*/ + +void InitStaticList (void) +{ + laststatobj = &statobjlist[0]; +} + +//--------------------------------------------------------------------------- +// FindStatic() +// +// FUNCTION: Searches the stat obj list and returns ptr to a static obj +// at a particular tile x & tile y coords. +// +// RETURNS: Ptr == Pointer to static obj. +// NULL == No static found. +//--------------------------------------------------------------------------- +statobj_t *FindStatic(unsigned tilex, unsigned tiley) +{ + statobj_t *spot; + + for (spot=statobjlist;spot!=laststatobj;spot++) + if (spot->shapenum != -1 && spot->tilex == tilex && spot->tiley == tiley) + return(spot); + + return(NULL); +} + +//--------------------------------------------------------------------------- +// FindEmptyStatic() +// +// FUNCTION: Searches the stat obj list and returns ptr to an empty +// static object. +// +// RETURNS: Ptr == Pointer to empty static obj. +// NULL == static objlist full. +//--------------------------------------------------------------------------- +statobj_t *FindEmptyStatic(void) +{ + statobj_t *spot; + + for (spot=&statobjlist[0] ; ; spot++) + { + if (spot==laststatobj) + { + if (spot == &statobjlist[MAXSTATS]) + return(NULL); + laststatobj++; // space at end + break; + } + + if (spot->shapenum == -1) // -1 is a free spot + break; + } + + return(spot); +} + + +/* +=============== += += SpawnStatic += +=============== +*/ + +void SpawnStatic (int tilex, int tiley, int type) +{ + statobj_t *spot; + +#if 0 +#if IN_DEVELOPMENT + if (tilemap[tilex][tiley]) + Quit("Static spawned on wall. %d,%d",tilex,tiley); +// ACT1_ERROR(SPAWNSTATIC_ON_WALL); +#endif +#endif + + if (!(spot = FindEmptyStatic())) + return; + + spot->shapenum = statinfo[type].picnum; + spot->tilex = tilex; + spot->tiley = tiley; + spot->visspot = &spotvis[tilex][tiley]; + spot->flags = 0; + +#if IN_DEVELOPMENT +#if GAME_VERSION == SHAREWARE_VERSION + if (!spot->shapenum) + Quit("Invalid static: %d %d",tilex,tiley); +#endif +#endif + + + switch (spot->shapenum) + { +#if GAME_VERSION != SHAREWARE_VERSION + case SPR_STAT_3: // floor lamp +#endif + case SPR_STAT_14: // ceiling light +#if GAME_VERSION != SHAREWARE_VERSION + case SPR_STAT_20: // +#endif + case SPR_STAT_47: + case SPR_STAT_51: + case SPR_STAT_55: + case SPR_STAT_56: + spot->lighting = LAMP_ON_SHADING; + break; + + default: + spot->lighting = 0; + break; + } + + + switch (statinfo[type].type) + { + case block: + (unsigned)actorat[tilex][tiley] = 1; // consider it a blocking tile + break; + + case bo_red_key: + case bo_yellow_key: + case bo_blue_key: + case bo_plasma_detonator: + TravelTable[tilex][tiley] |= TT_KEYS; + + case bo_gold1: + case bo_gold2: + case bo_gold3: + case bo_gold: + case bo_bonus: + case bo_money_bag: + case bo_loot: + + case bo_fullheal: + case bo_firstaid: + case bo_clip: + case bo_clip2: + case bo_burst_rifle: + case bo_ion_cannon: + case bo_grenade: + case bo_bfg_cannon: + case bo_pistol: + case bo_chicken: + case bo_ham: + case bo_water: + case bo_water_puddle: + case bo_sandwich: + case bo_candybar: + case bo_coin: + case bo_coin5: + case bo_automapper1: + spot->flags = FL_BONUS; + spot->itemnumber = statinfo[type].type; + break; + } + + spot->areanumber=GetAreaNumber(spot->tilex,spot->tiley); + + spot++; + + if (spot == &statobjlist[MAXSTATS]) + ACT1_ERROR(SPAWNSTATIC_TOO_MANY); +} + + + +//--------------------------------------------------------------------------- +// ReserveStatic() +// +// Reserves a static object at location 0,0 (unseen). This function is +// used to gaurantee that a static will be available. +//--------------------------------------------------------------------------- +statobj_t *ReserveStatic(void) +{ + statobj_t *spot; + + if (!(spot = FindEmptyStatic())) + ACT1_ERROR(SPAWNSTATIC_TOO_MANY); + + // Mark as Used. + + spot->shapenum = 1; + spot->tilex = 0; + spot->tiley = 0; + spot->visspot = &spotvis[0][0]; + + return(spot); +} + + +//--------------------------------------------------------------------------- +// FindReservedStatic() +// +// Finds a Reserved static object at location 0,0 (unseen). This function is +// used to gaurantee that a static will be available. +//--------------------------------------------------------------------------- +statobj_t *FindReservedStatic(void) +{ + statobj_t *spot; + + for (spot=&statobjlist[0];spot < &statobjlist[MAXSTATS];spot++) + { + if (spot->shapenum == 1 && (!spot->tilex) && (!spot->tiley)) // -1 is a free spot + return(spot); + } + + return(NULL); +} + + + +//--------------------------------------------------------------------------- +// UseReservedStatic() +// +// Finds a Reserved static object and moves it to a new location with new +// attributes. +// +// This function acts like PlaceItemType - But REQUIRES a reserved +// static. Before using this function, make sure that you have already +// reserved a static to be used using ReserveStatic(); +//--------------------------------------------------------------------------- +statobj_t *UseReservedStatic(int itemtype, int tilex, int tiley) +{ + statobj_t *spot; + int type; + + if (!(spot = FindReservedStatic())) + ACT1_ERROR(CANT_FIND_RESERVE_STATIC); + + // + // find the item number + // + + for (type=0;;type++) + { + if (statinfo[type].picnum == -1) // End of Static List... + ACT1_ERROR(PLACEITEMTYPE_NO_TYPE); + + if (statinfo[type].type == itemtype) // Bingo, Found it! + break; + } + + // + // place it + // + + switch (type) + { + case bo_red_key: + case bo_yellow_key: + case bo_blue_key: + TravelTable[tilex][tiley] |= TT_KEYS; + break; + } + + 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; + + spot->areanumber=GetAreaNumber(spot->tilex,spot->tiley); + +#if IN_DEVELOPMENT + if (spot->areanumber >= NUMAREAS) + Quit("Static Spawned on a wall at %d %d",spot->tilex,spot->tiley); +#endif + + return(spot); +} + +//-------------------------------------------------------------------------- +// PlaceReservedItemNearTile() +//-------------------------------------------------------------------------- +char far pint_xy[8][2]={{-1,-1},{-1, 0},{-1, 1}, + { 0,-1}, { 0, 1}, + { 1,-1},{ 1, 0},{ 1, 1}}; + +void PlaceReservedItemNearTile(int itemtype, int tilex, int tiley) +{ + char loop; + + for (loop=0; loop<8; loop++) + { + char x=tilex+pint_xy[loop][1], y=tiley+pint_xy[loop][0]; + + if (!tilemap[x][y]) + { + if (actorat[x][y] == (objtype *)1) // Check for a SOLID static + continue; + + UseReservedStatic(itemtype,x,y); + return; + } + } + + UseReservedStatic(itemtype,tilex,tiley); +} + + + +/* +=============== += += 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 + ACT1_ERROR(PLACEITEMTYPE_NO_TYPE); + if (statinfo[type].type == itemtype) + break; + } + +// +// find a spot in statobjlist to put it in +// + if (!(spot = FindEmptyStatic())) + return; + +// +// 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; + + spot->areanumber=GetAreaNumber(spot->tilex,spot->tiley); + +#if IN_DEVELOPMENT + if (spot->areanumber >= NUMAREAS) + Quit("Item Spawned on a wall at %d %d",spot->tilex,spot->tiley); +#endif + +} + +//-------------------------------------------------------------------------- +// PlaceItemNearTile() +//-------------------------------------------------------------------------- +void PlaceItemNearTile(int itemtype, int tilex, int tiley) +{ +// [0] is the y offset +// [1] is the x offset +// + char loop; + + for (loop=0; loop<8; loop++) + { + char x=tilex+pint_xy[loop][1], y=tiley+pint_xy[loop][0]; + + if (!tilemap[x][y]) + { + if (actorat[x][y] == (objtype *)1) // Check for a SOLID static + continue; + + PlaceItemType(itemtype,x,y); + return; + } + } + + PlaceItemType(itemtype,tilex,tiley); +} + + + + +//-------------------------------------------------------------------------- +// +//-------------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// ExplodeStatics() +// +// NOTES: Explodes statics in a one tile radius from a given tile x and tile y +// +//-------------------------------------------------------------------------- +void ExplodeStatics(int tilex, int tiley) +{ + statobj_t *statobj, *spot; + objtype *obj; + int y_diff,x_diff; + boolean remove; + + for (spot=&statobjlist[0] ; spot != laststatobj ; spot++) + if (spot->shapenum != -1) + { + y_diff = spot->tiley - tiley; + y_diff = ABS(y_diff); + + x_diff = spot->tilex - tilex; + x_diff = ABS(x_diff); + + if (x_diff < 2 && y_diff <2) + { + remove = false; + + // + // Test for specific statics.. + // + + switch (spot->itemnumber) + { + // + // Check for Clips + // + case bo_clip: + case bo_clip2: + remove = true; + SpawnCusExplosion((((fixed)spot->tilex)<tiley)<shapenum = -1; + spot->itemnumber = bo_nothing; + } + } + } +} + + + +/* +============================================================================= + + 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, keytype lock, door_t type) +{ + int areanumber; + unsigned far *map[2]; + + map[0] = mapsegs[0] + farmapylookup[tiley]+tilex; + map[1] = mapsegs[1] + farmapylookup[tiley]+tilex; + + if (doornum==64) + ACT1_ERROR(SPAWNDOOR_TOO_MANY); + + doorposition[doornum] = 0; // doors start out fully closed + lastdoorobj->tilex = tilex; + lastdoorobj->tiley = tiley; + lastdoorobj->vertical = vertical; + lastdoorobj->lock = lock; + lastdoorobj->type = type; + lastdoorobj->action = dr_closed; + lastdoorobj->flags = DR_BLASTABLE; // JIM - Do something with this! jdebug + + if (vertical) + { + lastdoorobj->areanumber[0]=GetAreaNumber(tilex+1,tiley); + lastdoorobj->areanumber[1]=GetAreaNumber(tilex-1,tiley); + } + else + { + lastdoorobj->areanumber[0]=GetAreaNumber(tilex,tiley-1); + lastdoorobj->areanumber[1]=GetAreaNumber(tilex,tiley+1); + } + + + (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; + + if (vertical) + { + if (*(map[0]-mapwidth-1) == TRANSPORTERTILE) + *map[0] = GetAreaNumber(tilex+1,tiley); + else + *map[0] = GetAreaNumber(tilex-1,tiley); + tilemap[tilex][tiley-1] |= 0x40; + tilemap[tilex][tiley+1] |= 0x40; + } + else + { + *map[0] = GetAreaNumber(tilex,tiley-1); + tilemap[tilex-1][tiley] |= 0x40; + tilemap[tilex+1][tiley] |= 0x40; + } + + doornum++; + lastdoorobj++; +} + +//=========================================================================== + + + +//-------------------------------------------------------------------------- +// CheckLinkedDoors +//-------------------------------------------------------------------------- +void CheckLinkedDoors(short door, short door_dir) +{ + static short LinkCheck=0; + static short base_tilex; + static short base_tiley; + + short tilex=doorobjlist[door].tilex, + tiley=doorobjlist[door].tiley, + next_tilex=0, + next_tiley=0; + +// Find next door in link. +// +// if ((*(mapsegs[1]+(farmapylookup[tiley]+tilex+1)) & 0xff00) == 0xf900) + if (*(mapsegs[1]+(farmapylookup[tiley]+tilex))) + { +// unsigned value=*(mapsegs[1]+(farmapylookup[tiley]+tilex+2)); + unsigned value=*(mapsegs[1]+(farmapylookup[tiley]+tilex)); + + // Define the next door in the link. + // + next_tilex = (value & 0xff00)>>8; + next_tiley = value & 0xff; + + // Is this the head of the link? + // + if (!LinkCheck) + { + base_tilex=tilex; + base_tiley=tiley; + } + } + + LinkCheck++; + +// Recursively open/close linked doors. +// + if ((next_tilex) && + (next_tiley) && + ((next_tilex != base_tilex) || (next_tiley != base_tiley)) + ) + { + short door=tilemap[next_tilex][next_tiley] & ~0x80; + + switch (door_dir) + { + case dr_opening: + doorobjlist[door].lock = kt_none; + OpenDoor(door); + break; + + case dr_closing: + doorobjlist[door].lock = kt_none; + CloseDoor(door); + break; + } + } + + LinkCheck--; +} + + +/* +===================== += += OpenDoor += +===================== +*/ + +void OpenDoor (int door) +{ + + if (doorobjlist[door].action == dr_jammed) + return; + + if (doorobjlist[door].action == dr_open) + doorobjlist[door].ticcount = 0; // reset open time + else + doorobjlist[door].action = dr_opening; // start it opening + + CheckLinkedDoors(door,dr_opening); + +} + + +/* +===================== += += CloseDoor += +===================== +*/ + +void CloseDoor (int door) +{ + int tilex,tiley,area; + objtype *check; + + if (doorobjlist[door].action == dr_jammed) + return; + +// +// 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 = GetAreaNumber(doorobjlist[door].tilex,doorobjlist[door].tiley); + if (areabyplayer[area]) + { + switch(doorobjlist[door].type) + { + case dr_bio: + case dr_office: + case dr_space: + case dr_normal: + PlaySoundLocTile(HTECHDOORCLOSESND,doorobjlist[door].tilex,doorobjlist[door].tiley); + break; + + default: + PlaySoundLocTile(CLOSEDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley); + break; + } + } + + doorobjlist[door].action = dr_closing; +// +// make the door space solid +// + (unsigned)actorat[tilex][tiley] + = door | 0x80; + + CheckLinkedDoors(door,dr_closing); + +} + +/* +===================== += += OperateDoor += += The player wants to change the door's direction += +===================== +*/ + +char far od_oneway[]="\r\r DOOR LOCKED FROM\r THIS SIDE.\r^XX"; +char far od_locked[]="\r\r DOOR PERMANENTLY\r LOCKED.\r^XX"; +char far od_reddenied[]="\r\r RED LEVEL\r ACCESS DENIED!\r^XX"; +char far od_yellowdenied[]="\r\r YELLOW LEVEL\r ACCESS DENIED!\r^XX"; +char far od_bluedenied[]="\r\r BLUE LEVEL\r ACCESS DENIED!\r^XX"; +char far od_granted[]="\r\r ACCESS GRANTED\r DOOR UNLOCKED.\r^XX"; +char far od_operating[]="\r\r OPERATING DOOR.\r^XX"; + +void OperateDoor (int door) +{ + int lock; + boolean OperateMsg,oneway = false; + + + // + // Check for wrong way on a ONEWAY door. + // + + switch (doorobjlist[door].type) + { + case dr_oneway_left: + if (player->tilex < doorobjlist[door].tilex) + oneway = true; + break; + + case dr_oneway_right: + if (player->tilex > doorobjlist[door].tilex) + oneway = true; + break; + + case dr_oneway_up: + if (player->tiley < doorobjlist[door].tiley) + oneway = true; + break; + + case dr_oneway_down: + if (player->tiley > doorobjlist[door].tiley) + oneway = true; + break; + } + + if (oneway) + { + if (doorobjlist[door].action == dr_closed) + { + DISPLAY_TIMED_MSG(od_oneway,MP_DOOR_OPERATE,MT_GENERAL); + SD_PlaySound(NOWAYSND); + } + + return; + } + + // + // Check for possibly being locked + // + + lock = doorobjlist[door].lock; + if (lock != kt_none) + { + if (!(gamestate.numkeys[lock-kt_red])) + { + SD_PlaySound(NOWAYSND); + switch (lock) + { + case kt_red: + DISPLAY_TIMED_MSG(od_reddenied,MP_DOOR_OPERATE,MT_GENERAL); + break; + + case kt_yellow: + DISPLAY_TIMED_MSG(od_yellowdenied,MP_DOOR_OPERATE,MT_GENERAL); + break; + + case kt_blue: + DISPLAY_TIMED_MSG(od_bluedenied,MP_DOOR_OPERATE,MT_GENERAL); + break; + + default: + DISPLAY_TIMED_MSG(od_locked,MP_DOOR_OPERATE,MT_GENERAL); + break; + } + + return; + } + else + { + TakeKey(lock-kt_red); + DISPLAY_TIMED_MSG(od_granted,MP_DOOR_OPERATE,MT_GENERAL); + doorobjlist[door].lock = kt_none; // UnLock door + } + } + else + DISPLAY_TIMED_MSG(od_operating,MP_DOOR_OPERATE,MT_GENERAL); + + switch (doorobjlist[door].action) + { + case dr_closed: + case dr_closing: + OpenDoor (door); + break; + + case dr_open: + case dr_opening: + CloseDoor (door); + break; + } +} + + + +//-------------------------------------------------------------------------- +// BlockDoorOpen() +//-------------------------------------------------------------------------- +void BlockDoorOpen(int door) +{ + doorobjlist[door].action = dr_jammed; + doorobjlist[door].ticcount = 0; + doorposition[door] = 0xffff; + doorobjlist[door].lock = kt_none; + doorobjlist[door].flags &= ~DR_BLASTABLE; + + actorat[doorobjlist[door].tilex][doorobjlist[door].tiley] = 0; +// tilemap[doorobjlist[door].tilex][doorobjlist[door].tiley] = 0; + + TransformAreas(doorobjlist[door].tilex,doorobjlist[door].tiley,1); +// SpawnStatic(doorobjlist[door].tilex, doorobjlist[door].tiley, DOOR_RUBBLE); +} + +//-------------------------------------------------------------------------- +// TryBlastDoor() +// +//-------------------------------------------------------------------------- +void TryBlastDoor(char door) +{ + switch (doorobjlist[door].type) + { + case dr_oneway_left: + case dr_oneway_up: + case dr_oneway_right: + case dr_oneway_down: + break; + + default: + if (doorposition[door] < 0x7fff && + doorobjlist[door].action != dr_jammed && + doorobjlist[door].lock == kt_none) + { + BlockDoorOpen(door); + SpawnCusExplosion((((fixed)doorobjlist[door].tilex)<x,player->y)) + { + obj->flags |= FL_SHOOTABLE; + + DISPLAY_TIMED_MSG(pd_dropped,MP_DOOR_OPERATE,MT_GENERAL); + SD_PlaySound(ROBOT_SERVOSND); // jdebug-This sound will probly change. + TakePlasmaDetonator(1); + return; + } + +#pragma warn +pia + + ACT1_ERROR(NO_DOORBOMB_SPARES); +} + + + +//-------------------------------------------------------------------------- +// TryDropPlasmaDetonator() - Will check to see if player is close enough to +// drop a detonator. +//-------------------------------------------------------------------------- +void TryDropPlasmaDetonator(void) +{ + #define MAX_RANGE_DIST 2 + objtype *obj; + short distx,disty,distance; + + + if (!gamestuff.level[gamestate.mapon+1].locked) + { + DISPLAY_TIMED_MSG(pd_floornotlocked, MP_DETONATOR, MT_GENERAL); + return; + } + + if (gamestate.mapon > 19) + { + DISPLAY_TIMED_MSG(pd_no_computer, MP_DETONATOR, MT_GENERAL); + return; + } + + if (!gamestate.plasma_detonators) + { + DISPLAY_TIMED_MSG(pd_donthaveany, MP_DETONATOR, MT_GENERAL); + return; + } + + if (!(obj = FindObj(rotating_cubeobj,-1,-1))) + ACT1_ERROR(CANT_FIND_LEVELCOMPUTER); + + if (obj->areanumber != player->areanumber) + { + DISPLAY_TIMED_MSG(pd_notnear,MP_DETONATOR,MT_GENERAL); + return; + } + + distx = player->tilex - obj->tilex; + distx = ABS(distx); + disty = player->tiley - obj->tiley; + disty = ABS(disty); + distance = distx>disty ? distx:disty; + + if (distance > MAX_RANGE_DIST) + { + DISPLAY_TIMED_MSG(pd_getcloser,MP_DETONATOR,MT_GENERAL); + return; + } + else + DropPlasmaDetonator(); +} + + +//=========================================================================== + +/* +=============== += += DoorOpen += += Close the door after three seconds += +=============== +*/ + +void DoorOpen (int door) +{ + if ( (doorobjlist[door].ticcount += tics) >= OPENTICS) + CloseDoor (door); +} + +#define USE_TRANSFORMAREAS + +#ifdef USE_TRANSFORMAREAS + +//-------------------------------------------------------------------------- +// TransformAreas() +//-------------------------------------------------------------------------- +int TransformAreas(char tilex, char tiley, char xform) +{ + short xofs,yofs; + byte area1,area2; + unsigned far *map,offset; + +// Is this walkway: Horizontal? Vertical? Error? +// + if ((tilemap[tilex][tiley+1]) && (tilemap[tilex][tiley-1])) + { + xofs = 1; + yofs = 0; + } + else + if ((tilemap[tilex+1][tiley]) && (tilemap[tilex-1][tiley])) + { + xofs = 0; + yofs = 1; + } + else + ACT1_ERROR(LINKAREA_BAD_LINK); + +// Define the two areas... +// + area1=GetAreaNumber(tilex+xofs,tiley+yofs); + if (area1 >= NUMAREAS) + ACT1_ERROR(TRANSFORM_AREA1_OUT_OF_RANGE); + + area2=GetAreaNumber(tilex-xofs,tiley-yofs); + if (area2 >= NUMAREAS) + ACT1_ERROR(TRANSFORM_AREA2_OUT_OF_RANGE); + +// Connect these two areas. +// + areaconnect[area1][area2] += xform; + areaconnect[area2][area1] += xform; + ConnectAreas (); + + return(area1); +} + +#endif + +/* +=============== += += DoorOpening += +=============== +*/ + +void DoorOpening (int door) +{ + int area1,area2; + unsigned far *map; + long position; + + position = doorposition[door]; + if (!position) + { + area1=TransformAreas(doorobjlist[door].tilex,doorobjlist[door].tiley,1); + + if (areabyplayer[area1]) + { +// PlaySoundLocTile(OPENDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley); // JAB + switch(doorobjlist[door].type) + { + case dr_bio: + case dr_office: + case dr_space: + case dr_normal: + PlaySoundLocTile(HTECHDOOROPENSND,doorobjlist[door].tilex,doorobjlist[door].tiley); + break; + + default: + PlaySoundLocTile(OPENDOORSND,doorobjlist[door].tilex,doorobjlist[door].tiley); + break; + } + } + } + +// +// 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) + { +#ifdef USE_TRANSFORMAREAS + position = 0; + doorobjlist[door].action = dr_closed; + TransformAreas(doorobjlist[door].tilex,doorobjlist[door].tiley,-1); +#else + // + // 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; + if (area1 >= HIDDENAREATILE-AREATILE) + area1 -= HIDDENAREATILE-AREATILE; + area2 -= AREATILE; + if (area2 >= HIDDENAREATILE-AREATILE) + area2 -= HIDDENAREATILE-AREATILE; + areaconnect[area1][area2]--; + areaconnect[area2][area1]--; + ConnectAreas (); +#endif + } + + doorposition[door] = position; +} + +/* +===================== += += MoveDoors += += Called from PlayLoop += +===================== +*/ +void MoveDoors (void) +{ + int door; + + 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=0,pwally=0; +int pwalldir,pwalldist; + +/* +=============== += += PushWall += +=============== +*/ + +void PushWall (int checkx, int checky, int dir) +{ + int oldtile; + + if (pwallstate) + return; + + TransformAreas(checkx,checky,1); + + oldtile = tilemap[checkx][checky]; + if (!oldtile) + return; + + switch (dir) + { + case di_north: + if (actorat[checkx][checky-1]) + { + return; + } + (unsigned)actorat[checkx][checky-1] = + tilemap[checkx][checky-1] = oldtile; + break; + + case di_east: + if (actorat[checkx+1][checky]) + { + return; + } + (unsigned)actorat[checkx+1][checky] = + tilemap[checkx+1][checky] = oldtile; + break; + + case di_south: + if (actorat[checkx][checky+1]) + { + return; + } + (unsigned)actorat[checkx][checky+1] = + tilemap[checkx][checky+1] = oldtile; + break; + + case di_west: + if (actorat[checkx-1][checky]) + { + return; + } + (unsigned)actorat[checkx-1][checky] = + tilemap[checkx-1][checky] = oldtile; + break; + } + + pwalldist=2; + 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*4; + + if (pwallstate/128 != oldblock) + { + unsigned areanumber; + + pwalldist--; + + // 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; + areanumber=GetAreaNumber(player->tilex,player->tiley); + if (GAN_HiddenArea) + areanumber += HIDDENAREATILE; + else + areanumber += AREATILE; + *(mapsegs[0]+farmapylookup[pwally]+pwallx) = areanumber; + + // + // see if it should be pushed farther + // + if (!pwalldist) + { + // + // 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; +} + +//========================================================================== +// +// 'SPECIAL MESSAGE' CACHING SYSTEM +// +// When creating special 'types' of message caching structures, make sure +// all 'special data' is placed at the end of the BASIC message structures. +// In memory, BASIC INFO should appear first. ex: +// +// mCacheList +// ---> NumMsgs +// ---> mCacheInfo +// ---> local_val +// ---> global_val +// ---> mSeg +// +// ... all special data follows ... +// +//========================================================================== + +//-------------------------------------------------------------------------- +// InitMsgCache() +//-------------------------------------------------------------------------- +void InitMsgCache(mCacheList *mList,unsigned listSize, unsigned infoSize) +{ + FreeMsgCache(mList,infoSize); + memset(mList,0,listSize); +} + +//-------------------------------------------------------------------------- +// FreeMsgCache() +//-------------------------------------------------------------------------- +void FreeMsgCache(mCacheList *mList, unsigned infoSize) +{ + mCacheInfo *ci=mList->mInfo; + char *ch_ptr; + + while (mList->NumMsgs--) + { + if (ci->mSeg) + MM_FreePtr(&ci->mSeg); + + ch_ptr = (char far *)ci; + ch_ptr += infoSize; + ci = (mCacheInfo *)ch_ptr; + } +} + +extern char far int_xx[]; + +//--------------------------------------------------------------------------- +// CacheMsg() +// +// Caches the specific message in FROM a given 'grsegs' block TO the +// next available message segment pointer. +//--------------------------------------------------------------------------- +void CacheMsg(mCacheInfo *ci, unsigned SegNum, unsigned MsgNum) +{ +// char far *Message, far *EndOfMsg, far *hint_buffer; +// unsigned char pos=0; + +// Alloc memory for message and cache-in seg +// + MM_GetPtr(&ci->mSeg,MAX_CACHE_MSG_LEN); +// hint_buffer = ci->mSeg; + +// Load message into CachInfo Message Seg. +// + LoadMsg(ci->mSeg,SegNum,MsgNum,MAX_CACHE_MSG_LEN); +} + +//--------------------------------------------------------------------------- +// LoadMsg() +// +// Loads the specific message in FROM a given 'grsegs' block TO the +// the memory address provided. Memory allocation and handleing prior and +// after this function usage is responsiblity of the calling function(s). +// +// PARAMS: hint_buffer - Destination address to store message +// SegNum - GrSeg for messages in VGAGRAPH.BS? +// MsgNum - Message number to load +// MaxMsgLen - Max len of cache msg (Len of hint_buffer) +// +// RETURNS : Returns the length of the loaded message +//--------------------------------------------------------------------------- +short LoadMsg(char far *hint_buffer, unsigned SegNum, unsigned MsgNum, unsigned MaxMsgLen) +{ + char far *Message, far *EndOfMsg; + short pos=0; + + CA_CacheGrChunk(SegNum); + Message = grsegs[SegNum]; + +// Search for end of MsgNum-1 (Start of our message) +// +#pragma warn -pia + while (--MsgNum) + { + if (!(Message = _fstrstr(Message,int_xx))) + ACT1_ERROR(INVALID_CACHE_MSG_NUM); + + Message += 3; // Bump to start of next Message + } +#pragma warn +pia + +// Move past LFs and CRs that follow "^XX" +// + while ((*Message=='\n') || (*Message=='\r')) + Message++; + +// Find the end of the message +// + if (!(EndOfMsg = _fstrstr(Message,int_xx))) + ACT1_ERROR(INVALID_CACHE_MSG_NUM); + EndOfMsg += 3; + +// Copy to a temp buffer +// + while (Message != EndOfMsg) + { + if (*Message != '\n') + hint_buffer[pos++] = *Message; + + if (pos >= MaxMsgLen) + ACT1_ERROR(HINT_BUFFER_OVERFLOW); + + Message++; + } + + hint_buffer[pos] = 0; // Null Terminate + UNCACHEGRCHUNK(SegNum); + + return(pos); +} + + +#if 0 +//--------------------------------------------------------------------------- +// CacheMsg() +// +// Caches the specific message in FROM a given 'grsegs' block TO the +// next available message segment pointer. +//--------------------------------------------------------------------------- +void CacheMsg(mCacheInfo *ci, unsigned SegNum, unsigned MsgNum) +{ + char far *Message, far *EndOfMsg, far *hint_buffer; + unsigned char pos=0; + +// Alloc memory for message and cache-in seg +// + MM_GetPtr(&ci->mSeg,MAX_CACHE_MSG_LEN); + hint_buffer = ci->mSeg; + + CA_CacheGrChunk(SegNum); + Message = grsegs[SegNum]; + +// Search for end of MsgNum-1 (Start of our message) +// +#pragma warn -pia + while (--MsgNum) + { + if (!(Message = _fstrstr(Message,int_xx))) + ACT1_ERROR(INVALID_CACHE_MSG_NUM); + + Message += 3; // Bump to start of next Message + } +#pragma warn +pia + +// Move past LFs and CRs that follow "^XX" +// + while ((*Message=='\n') || (*Message=='\r')) + Message++; + +// Find the end of the message +// + if (!(EndOfMsg = _fstrstr(Message,int_xx))) + ACT1_ERROR(INVALID_CACHE_MSG_NUM); + EndOfMsg += 3; + +// Copy to a temp buffer +// + while (Message != EndOfMsg) + { + if (*Message != '\n') + hint_buffer[pos++] = *Message; + + if (pos >= MAX_CACHE_MSG_LEN) + ACT1_ERROR(HINT_BUFFER_OVERFLOW); + + Message++; + } + + hint_buffer[pos] = 0; // Null Terminate + UNCACHEGRCHUNK(SegNum); +} +#endif +// + + +/* +============================================================================= + + CONCESSION MACHINES + +============================================================================= +*/ + +//-------------------------------------------------------------------------- +// SpawnConcession() +// +// actorat[][] - Holds concession machine number (1 - MAXCONCESSIONS+1) +//-------------------------------------------------------------------------- +void SpawnConcession(int tilex, int tiley, unsigned credits,unsigned machinetype) +{ + con_mCacheInfo *ci=&ConHintList.cmInfo[ConHintList.NumMsgs]; + + if (ConHintList.NumMsgs >= MAXCONCESSIONS) + ACT1_ERROR(SPAWNCONCESSION_TOO_MANY); + + if (credits != PUSHABLETILE) + switch (credits & 0xff00) + { + case 0: +#ifdef CON_HINTS + case 0xFD00: // Hint Id + ci->mInfo.global_val = credits & 0xff; + ci->mInfo.local_val = 0xff; + ci->operate_cnt = 3+(US_RndT() & 0x03); + if ((credits != 0xFDFF) && (credits)) + CacheConcessionMsg(); + ci->type = CT_HINT; // Force to Hint Type + break; +#endif + + case 0xFC00: // Food Id + ci->mInfo.local_val = credits & 0xff; + ci->operate_cnt = 0; + ci->type = machinetype; + break; + } + +// Consider it a solid wall (val != 0) +// + if (++ConHintList.NumMsgs > MAX_CACHE_MSGS) + ACT1_ERROR(SPAWNCON_CACHE_MSG_OVERFLOW); + (unsigned)actorat[tilex][tiley] = ConHintList.NumMsgs; + +// +// BORLAND SCREWS UP WHEN COMPILING THE LINE BELOW, EVEN THOUGH +// IT SHOULD BE JUST THE SAME AS THE TWO LINES ABOVE... +// +// (unsigned)actorat[tilex][tiley] = ++ConHintList.NumMsgs; +// +} + +#ifdef CON_HINTS + +//-------------------------------------------------------------------------- +// CacheConcessionMsg() +//-------------------------------------------------------------------------- +void CacheConcessionMsg() +{ + mCacheInfo *ci=(mCacheInfo far *)&ConHintList.cmInfo[ConHintList.NumMsgs]; + +// Make sure we don't overflow list. +// + if (ConHintList.NumMsgs >= MAX_CACHE_MSGS) + ACT1_ERROR(CACHEMSG_TOO_MANY); + +// Either re-use a message, or cache-in a new one. +// + if (!ReuseMsg(ci,ConHintList.NumMsgs,sizeof(con_mCacheInfo))) + { + // Cache-in new message + // + + CacheMsg(ci,CONCESSION_HINTS,ci->global_val); + ci->local_val = ConHintList.NumMsgs; + } +} + +#endif + +//-------------------------------------------------------------------------- +// ReuseMsg() +//-------------------------------------------------------------------------- +boolean ReuseMsg(mCacheInfo *ci, short count, short struct_size) +{ + char *scan_ch=(char *)ci; + mCacheInfo *scan=(mCacheInfo *)(scan_ch-struct_size); + +// Scan through all loaded messages -- see if we're loading one already +// cached-in. +// + while (count--) + { + // Is this message already cached in? + // + if (scan->global_val == ci->global_val) + { + ci->local_val = scan->local_val; + return(true); + } + + // Funky structure decrement... (structures can be any size...) + // + scan_ch=(char *)scan; + scan_ch-=struct_size; + scan=(mCacheInfo *)scan_ch; + } + + return(false); +} + +//-------------------------------------------------------------------------- +// OperateConcession() +//-------------------------------------------------------------------------- + +extern char far food_msg1[]; +extern char far bevs_msg1[]; +extern void writeTokenStr(char far *str); + +char far OutOrder[] = {"\r\r FOOD UNIT MACHINE\r IS OUT OF ORDER.^XX"}; + +void OperateConcession(unsigned concession) +{ + con_mCacheInfo *ci; + char far *msgptr; + boolean ok=false; + + ci=&ConHintList.cmInfo[concession-1]; + + switch (ci->type) + { +#ifdef CON_HINTS + case CT_HINT: + if (ci->operate_cnt != 0xff) + if (!ci->operate_cnt--) + ok=true; + break; +#endif + + case CT_FOOD: + case CT_BEVS: + if (ci->mInfo.local_val) + { + if (gamestate.health == 100) + { + DISPLAY_TIMED_MSG(noeat_msg1,MP_CONCESSION_OPERATE,MT_GENERAL); + SD_PlaySound(NOWAYSND); + return; + } + else + ok=true; + } + break; + } + + if (ok) + { + // Whada' ya' want? + + switch (ci->type) + { +#ifdef CON_HINTS + case CT_HINT: + SD_PlaySound(CON_HINTSND); + if (ci->mInfo.local_val == 0xFF) + DISPLAY_TIMED_MSG(ConcessionGenHints[US_RndT() % NUM_GEN_HINTS],MP_CONCESSION_OPERATE,MT_GENERAL); + else + { + msgptr = ConHintList.cmInfo[ci->mInfo.local_val].mInfo.mSeg; + DISPLAY_TIMED_MSG(msgptr,MP_CONCESSION_HINT,MT_GENERAL); + } + ci->mInfo.local_val = 0; // Mark as Out Of Order + break; +#endif + case CT_FOOD: + case CT_BEVS: + // One token please... Thank you. + + if (!gamestate.tokens) + { + DISPLAY_TIMED_MSG(NoFoodTokens,MP_NO_MORE_TOKENS,MT_NO_MO_FOOD_TOKENS); + SD_PlaySound(NOWAYSND); + return; + } + else + gamestate.tokens--; + + ci->mInfo.local_val--; + SD_PlaySound(CONCESSIONSSND); + switch (ci->type) + { + case CT_FOOD: + writeTokenStr(food_msg1); + DISPLAY_TIMED_MSG(food_msg1,MP_CONCESSION_OPERATE,MT_GENERAL); + HealSelf(10); + break; + + case CT_BEVS: + writeTokenStr(bevs_msg1); + DISPLAY_TIMED_MSG(bevs_msg1,MP_CONCESSION_OPERATE,MT_GENERAL); + HealSelf(7); + break; + } + break; + } + } + else + { + DISPLAY_TIMED_MSG(OutOrder,MP_CONCESSION_OUT_ORDER,MT_GENERAL); + SD_PlaySound(NOWAYSND); + } +} + +char xy_offset[8][2]={{0,-1},{0,+1},{-1,0},{+1,0}, // vert / horz + {-1,-1},{+1,+1},{-1,+1},{+1,-1}}; // diagnals + +//-------------------------------------------------------------------------- +// CheckSpawnEA() +//-------------------------------------------------------------------------- +void CheckSpawnEA() +{ + objtype temp,*ob; + char loop,ofs,tx,ty,x_diff,y_diff; + + if (objcount > MAXACTORS-8) + return; + + for (loop=0; loop gamestate.difficulty) + continue; + + // Decrement 'spawn delay' for current outlet. + // + if (eaList[loop].delay > tics) + { + eaList[loop].delay -= tics; + continue; + } + + // Reset to 1 because it's possible that an alien won't be spawned... + // If NOT, we'll try again on the next refresh. + // If SO, the delay is set to a true value below. + // + eaList[loop].delay=1; + + // Does this wall touch the 'area' that the player is in? + // + for (ofs=0; ofs<4; ofs++) + { + char nx=eaList[loop].tilex+xy_offset[ofs][0]; + char ny=eaList[loop].tiley+xy_offset[ofs][1]; + char areanumber=GetAreaNumber(nx,ny); + + if ((nx < 0) || (nx > 63) || (ny < 0) || (ny > 63)) + continue; + + if (areanumber != 127 && areabyplayer[areanumber]) + break; + } + + // Wall doesn't touch player 'area'. + // + if (ofs==4) + continue; + + // Setup tile x,y in temp obj. + // + temp.tilex = eaList[loop].tilex+xy_offset[ofs][0]; + temp.tiley = eaList[loop].tiley+xy_offset[ofs][1]; + + // Is another actor already on this tile? + // If so, "continue" if it's alive... + // + ob = actorat[temp.tilex][temp.tiley]; + if (ob >= objlist) + if (!(ob->flags & FL_DEADGUY)) + continue; + + // Is player already on this tile? + // + x_diff = player->tilex-temp.tilex; + y_diff = player->tiley-temp.tiley; + if (ABS(x_diff)<2 && ABS(y_diff)<2) + continue; + + // Setup x,y in temp obj and see if obj is in player's view. + // Actor is released if it's in player's view OR + // a random chance to release whether it can be seen or not. + // + temp.x = ((fixed)temp.tilex<temp2=loop; + PlaySoundLocActor(ELECAPPEARSND,new); + } + + // Reset spawn delay. + // + if ((*map1 & 0xff00) == 0xfa00) + eaList[loop].delay=60*((*map1)&0xff); + else + eaList[loop].delay=60*8+Random(60*22); + + break; + } +} + +//-------------------------------------------------------------------------- +// CheckSpawnGoldstern() +//-------------------------------------------------------------------------- +void CheckSpawnGoldstern(void) +{ + if (GoldsternInfo.WaitTime > tics) + { + // + // Count down general timer before doing any Goldie Stuff.. + // + + GoldsternInfo.WaitTime -=tics; + } + else + { + // + // What Kind of Goldie Stuff needs to be done? + // + + if (GoldsternInfo.flags == GS_COORDFOUND) + { + unsigned tilex,tiley; + + // See if we can spawn Dr. Goldstern... + + tilex = GoldieList[GoldsternInfo.LastIndex].tilex; + tiley = GoldieList[GoldsternInfo.LastIndex].tiley; + + if ((!actorat[tilex][tiley]) && ABS(player->tilex-tilex)>1 && ABS(player->tiley-tiley)>1 ) + { + SpawnStand(en_goldstern, tilex, tiley, 0); + GoldsternInfo.GoldSpawned = true; + } + } + else + { + // Find a new coord to spawn Goldie (GS_NEEDCOORD or GS_FIRSTTIME) + + FindNewGoldieSpawnSite(); + } + } +} + +//--------------------------------------------------------------------------- +// FindNewGoldieSpawnSite() +//--------------------------------------------------------------------------- +void FindNewGoldieSpawnSite(void) +{ + objtype temp; + char loop; + + GoldsternInfo.WaitTime = 0; + + for (loop=0; loop1) && (loop == GoldsternInfo.LastIndex)) + continue; + + // Setup tile x,y in temp obj. + // + + temp.tilex = GoldieList[loop].tilex; + temp.tiley = GoldieList[loop].tiley; + + // Setup x,y in temp obj and see if obj is in player's view. + // + + temp.x = ((fixed)temp.tilex<flags |= FL_SHOOTABLE | FL_SOLID | FL_OFFSET_STATES; + new->obclass = rentacopobj+which; + + switch (which) + { + case en_final_boss2: + new->lighting = NO_SHADING; + case en_spider_mutant: + case en_breather_beast: + case en_cyborg_warrior: + case en_reptilian_warrior: + case en_acid_dragon: + case en_mech_guardian: + case en_final_boss1: + case en_final_boss3: + case en_final_boss4: + new->temp1 = BossShapes[which-en_spider_mutant]; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->flags |= FL_PROJ_TRANSPARENT; + new->flags2 = FL2_BFG_SHOOTABLE|FL2_BFGSHOT_SOLID; + break; + + case en_green_ooze: + InitSmartSpeedAnim(new,SPR_GREEN_OOZE1,US_RndT()%3,2,at_CYCLE,ad_FWD,5+(US_RndT()&2)); + new->flags &= ~(FL_SHOOTABLE|FL_SOLID); + break; + + case en_black_ooze: + InitSmartSpeedAnim(new,SPR_BLACK_OOZE1,US_RndT()%3,2,at_CYCLE,ad_FWD,5+(US_RndT()&2)); + new->flags &= ~(FL_SHOOTABLE|FL_SOLID); + break; + + case en_green2_ooze: + InitSmartSpeedAnim(new,SPR_GREEN2_OOZE1,US_RndT()%3,2,at_CYCLE,ad_FWD,5+(US_RndT()&2)); + new->flags &= ~(FL_SHOOTABLE|FL_SOLID); + break; + + case en_black2_ooze: + InitSmartSpeedAnim(new,SPR_BLACK2_OOZE1,US_RndT()%3,2,at_CYCLE,ad_FWD,5+(US_RndT()&2)); + new->flags &= ~(FL_SHOOTABLE|FL_SOLID); + break; + + + case en_crate1: + case en_crate2: + case en_crate3: + new->temp1 = SPR_CRATE_1+which-en_crate1; + new->flags |= FL_NO_SLIDE|FL_FAKE_STATIC; + + // NOTE : TEMP2 is modified in ScanInfoPlane to determine if + // this object is a "blastable" or not. + break; + + case en_rotating_cube: + InitSmartSpeedAnim(new,SPR_CUBE1,0,9,at_CYCLE,ad_FWD,5); + new->flags2 = FL2_BFGSHOT_SOLID; + new->lighting = LAMP_ON_SHADING; + break; + + case en_ventdrip: + if (dir_which == en_bloodvent) + new->temp1 = SPR_BLOOD_DRIP1; + else + new->temp1 = SPR_WATER_DRIP1; + new->temp2 = 5+(US_RndT()%10); + new->temp3 = 0; + NewState(new,&s_ofs_random); + new->flags &= ~(FL_SHOOTABLE|FL_SOLID); + break; + + case en_plasma_detonator: + case en_plasma_detonator_reserve: + NewState(new,&s_ofs_random); + new->temp1 = SPR_DOORBOMB; + new->temp3 = PLASMA_DETONATORS_DELAY; + new->flags &= ~(FL_SOLID|FL_SHOOTABLE); + if (detonators_spawned++) + ACT2_ERROR(TOO_MANY_DETONATORS); + break; + + case en_flickerlight: + new->temp1 = SPR_DECO_ARC_1; + new->temp2 = (2+(US_RndT()%3))*60; + NewState(new,&s_ofs_random); + new->flags |= FL_NONMARK|FL_NEVERMARK; + new->flags &= ~(FL_SOLID|FL_SHOOTABLE); + new->lighting = LAMP_ON_SHADING; + break; + + case en_security_light: + new->flags &= ~(FL_SOLID | FL_SHOOTABLE); // ick - this is stupid... + NewState(new,&s_security_light); + break; + + case en_electrosphere: + new->trydir = dir_which; + new->flags &= ~FL_SOLID; + new->temp1 = SPR_ELECTRO_SPHERE_ROAM1; + new->speed = 3096; + new->lighting = NO_SHADING; // NO shading + NewState(new,&s_ofs_bounce); + SphereStartDir(new); + break; + + case en_podegg: + new->temp1 = SPR_POD_EGG; + if (scan_value == 0xffff) + new->temp2 = 60*5+(60*(US_RndT()%20)); + else + new->temp2 = scan_value*60; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + if (new->temp2==0xff*60) + new->flags &= ~FL_SHOOTABLE; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE|FL_FAKE_STATIC; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_pod: + new->temp1 = SPR_POD_WALK1; + new->speed = SPDPATROL; + new->ammo = -1; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE; + new->flags2 = FL2_BFG_SHOOTABLE; + break; + + case en_genetic_guard: + new->temp1 = SPR_GENETIC_W1; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE; + new->flags2 = FL2_BFG_SHOOTABLE; + break; + + case en_mutant_human1: + new->temp1 = SPR_MUTHUM1_W1; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_mutant_human2: + new->temp1 = SPR_MUTHUM2_W1; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_scan_wait_alien: + new->temp1 = SPR_SCAN_ALIEN_READY; + new->flags |= FL_STATIONARY|FL_NO_SLIDE|FL_FAKE_STATIC; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_lcan_wait_alien: + new->temp1 = SPR_LCAN_ALIEN_READY; + new->flags |= FL_STATIONARY|FL_NO_SLIDE|FL_FAKE_STATIC; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_morphing_spider_mutant: + case en_morphing_reptilian_warrior: + case en_morphing_mutanthuman2: + if (scan_value == 0xffff) + new->temp2 = 0xffff; // set to max! // 60*5+(60*(US_RndT()%20)); + else + new->temp2 = scan_value*60; + + if (new->temp2==0xff*60) + new->flags &= ~FL_SHOOTABLE; + + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->temp1 = MorphShapes[which-en_morphing_spider_mutant]; + new->flags |= FL_FAKE_STATIC; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + NewState(new,&s_ofs_random); + break; + + case en_gurney_wait: + if (scan_value == 0xffff) + new->temp3 = 0; + else + new->temp3 = (scan_value & 0xff)*60; + new->temp1 = SPR_GURNEY_MUT_READY; + new->flags |= FL_STATIONARY|FL_PROJ_TRANSPARENT|FL_NO_SLIDE|FL_FAKE_STATIC; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_lcan_alien: // Large Canister Alien - Out of can. + new->temp1 = SPR_LCAN_ALIEN_W1; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE; + new->flags2 = FL2_BFG_SHOOTABLE; + break; + + case en_scan_alien: // Small Canister Alien - Out of can. + new->temp1 = SPR_SCAN_ALIEN_W1; + new->speed = ALIENSPEED; + new->ammo = ALIENAMMOINIT; + new->flags |= FL_PROJ_TRANSPARENT; + new->flags2 = FL2_BFG_SHOOTABLE; + break; + + case en_gurney: // Gurney Mutant - Off of gurney. + new->temp1 = SPR_GURNEY_MUT_W1; + new->speed = ALIENSPEED; + new->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE; + new->ammo = ALIENAMMOINIT; + new->flags2 = FL2_BFG_SHOOTABLE; + break; + } + + CheckForSpecialTile(new,tilex,tiley); + + if (which < SPACER1_OBJ) + new->hitpoints = starthitpoints[gamestate.difficulty][which]; +} + + + + + +//--------------------------------------------------------------------------- +// T_OfsThink() - Think for general Offset Objects +// +// NOTE: This think is used for NON-SmartAnim objects +//--------------------------------------------------------------------------- + +short far grenade_shapes[] = {SPR_GRENADE_FLY3,SPR_GRENADE_FLY3,SPR_GRENADE_FLY2, + SPR_GRENADE_FLY1,SPR_GRENADE_FLY2,SPR_GRENADE_FLY2, + SPR_GRENADE_FLY3,SPR_GRENADE_FLY4,0}; + +void T_OfsThink(objtype *obj) +{ + char dx,dy,dist; + char oldofs,ofs=0; + + switch (obj->obclass) + { + case plasma_detonator_reserveobj: + break; + + case plasma_detonatorobj: + if (obj->temp3 < tics) + { + BlastNearDoors(obj->tilex, obj->tiley); + + obj->lighting = NO_SHADING; // no shading + obj->flags &= ~FL_SHOOTABLE; + obj->obclass = pd_explosionobj; + A_DeathScream(obj); + InitSmartSpeedAnim(obj,SPR_DETONATOR_EXP1,0,7,at_ONCE,ad_FWD,3+(US_RndT()&3)); + return; + } + else + obj->temp3-=tics; + + if (obj->ammo >= tics) + obj->ammo -= tics; + else + { + obj->ammo = DETONATOR_FLASH_RATE; + if (obj->temp1 == SPR_DOORBOMB) + obj->temp1 = SPR_ALT_DOORBOMB; + else + obj->temp1 = SPR_DOORBOMB; + } + break; + + case grenadeobj: + { + T_Projectile(obj); + + // Check for range and update shape... + + if (obj->obclass == grenadeobj) + { + // + // Has not exploded yet... + // + + dx = obj->s_tilex - obj->tilex; + dx = ABS(dx); + dy = obj->s_tiley - obj->tiley; + dy = ABS(dy); + dist = dx>dy ? dx:dy; + + // Reached end of range? + + if (!(obj->temp1 = grenade_shapes[dist])) + { + obj->obclass = gr_explosionobj; + InitSmartSpeedAnim(obj,SPR_GRENADE_EXPLODE1,0,4,at_ONCE,ad_FWD,3+(US_RndT()&3)); + } + } + + if (obj->obclass != grenadeobj) + { + ExplodeRadius(obj,50+(US_RndT()&0x7f),false); + A_DeathScream(obj); + } + } + break; + + + case bfg_shotobj: + { + T_Projectile(obj); + + if (obj->temp1 != SPR_BFG_WEAPON_SHOT3 && obj->obclass == bfg_shotobj) + { + // + // Has not exploded yet... + // + + dx = obj->s_tilex - obj->tilex; + dx = ABS(dx); + dy = obj->s_tiley - obj->tiley; + dy = ABS(dy); + dist = dx>dy ? dx:dy; + + obj->temp1 = SPR_BFG_WEAPON_SHOT2 + (dist>>2); + } + + if (obj->obclass != bfg_shotobj) + { + ExplodeRadius(obj,50+(US_RndT()&0x7f),false); + A_DeathScream(obj); + } + } + break; + + + case ventdripobj: + // Decrement timer... + // + if (tics < obj->temp2) + { + obj->temp2 -= tics; + break; + } + + // Assign random delay and next shape. + // + obj->temp2 = 5+(US_RndT()%10); + + obj->temp1 -= obj->temp3; + obj->temp3 = (obj->temp3 + 1) % 4; + obj->temp1 += obj->temp3; + break; + + case flickerlightobj: + if (tics < obj->temp2) + { + obj->temp2 -= tics; + break; + } + + if ((obj->temp1 == SPR_DECO_ARC_1) || (US_RndT()&15)) + { + // Becomes darker.... + + char t = (US_RndT()&1); + + obj->lighting = LAMP_ON_SHADING+((t+3)<<2); + obj->temp1 = SPR_DECO_ARC_2+t; + obj->temp2 = 2+(US_RndT()%2); + } + else + { + obj->temp1 = SPR_DECO_ARC_1; + obj->temp2 = (4+US_RndT()%10)*60; + obj->lighting = LAMP_ON_SHADING; + } + break; + + case electrosphereobj: + oldofs = obj->temp1 - SPR_ELECTRO_SPHERE_ROAM1; + ofs = US_RndT()&3; + if (ofs == oldofs) + ofs = (ofs+1) & 3; + obj->temp1 = SPR_ELECTRO_SPHERE_ROAM1+ofs; + break; + + case podobj: + if (obj->flags & FL_VISABLE) + FirstSighting(obj); + break; + + case podeggobj: +#if IN_DEVELOPMENT || TECH_SUPPORT_VERSION + if (!((Keyboard[sc_6] || Keyboard[sc_7]) && Keyboard[sc_8] && DebugOk)) + if (!(obj->flags & FL_VISABLE)) + break; + +#else + if (!(obj->flags & FL_VISABLE)) + break; +#endif + + if (obj->temp2 == 0xff*60) + break; + +#if IN_DEVELOPMENT || TECH_SUPPORT_VERSION + if ((Keyboard[sc_6] || Keyboard[sc_7]) && Keyboard[sc_8] && DebugOk) + obj->temp2 = 0; +#endif + + if (obj->temp2 > tics) + { + obj->temp2 -= tics; + break; + } + PlaySoundLocActor(PODHATCHSND,obj); + InitSmartSpeedAnim(obj,SPR_POD_HATCH1,0,2,at_ONCE,ad_FWD,7); + break; + + + case morphing_spider_mutantobj: + case morphing_reptilian_warriorobj: + case morphing_mutanthuman2obj: +#if IN_DEVELOPMENT || TECH_SUPPORT_VERSION + if (!((Keyboard[sc_6] || Keyboard[sc_7]) && Keyboard[sc_8] && DebugOk)) + if (!(obj->flags & FL_VISABLE)) + break; + +#else + if (!(obj->flags & FL_VISABLE)) + break; +#endif + +#if IN_DEVELOPMENT || TECH_SUPPORT_VERSION + if ((Keyboard[sc_6] || Keyboard[sc_7]) && Keyboard[sc_8] && DebugOk) + obj->temp2 = 0; +#endif + + if (obj->temp2 > tics) + { + obj->temp2 -= tics; + break; + } + obj->flags &= ~FL_SHOOTABLE; + InitSmartSpeedAnim(obj,obj->temp1,0,8,at_ONCE,ad_FWD,2); + break; + + + case crate1obj: + case crate2obj: + case crate3obj: + case lcan_wait_alienobj: // These objs wait till they are destroyed + case scan_wait_alienobj: // before doing anything. + break; + + case gurney_waitobj: + if (obj->flags & FL_VISABLE) + SightPlayer(obj); + break; + + case spider_mutantshotobj: + case final_boss4shotobj: + case acid_dragonshotobj: + obj->temp1 = BossShotShapes[obj->obclass-spider_mutantshotobj]+(US_RndT()%3); + T_Projectile(obj); + break; + + default: + T_Stand(obj); + break; + } +} + +//--------------------------------------------------------------------------- +// RandomSphereDir() +//--------------------------------------------------------------------------- +int RandomSphereDir(enemy_t enemy) +{ + int dir; + + switch (enemy) + { + case en_vertsphere: + dir = ((US_RndT()%2) * 4) + 2; + break; + + case en_horzsphere: + dir = (US_RndT()%2) * 4; + break; + + case en_diagsphere: + dir = ((US_RndT()%4) * 2) + 1; + break; + } + + return(dir); +} + +//--------------------------------------------------------------------------- +// SphereStartDir() +//--------------------------------------------------------------------------- +void SphereStartDir(objtype *ob) +{ + char loop; + + actorat[ob->tilex][ob->tiley] = NULL; + for (loop=0; loop<3; loop++) + { + ob->dir=RandomSphereDir(en_vertsphere+((ob->trydir-en_vertsphere+loop)%3)); + if (!TryWalk(ob,true)) + { + ob->dir = opposite[ob->dir]; + if (!TryWalk(ob,true)) + ob->dir = nodir; + else + break; + } + else + break; + } + actorat[ob->tilex][ob->tiley] = ob; +} + +//--------------------------------------------------------------------------- +// CheckForcedMove() +//--------------------------------------------------------------------------- +void CheckForcedMove(objtype *ob) +{ + short olddir; + + olddir = ob->dir; + ob->dir = RandomSphereDir(ob->trydir); + if (!TryWalk(ob,false)) + { + ob->dir = opposite[ob->dir]; + if (!TryWalk(ob,false)) + ob->dir = olddir; + } +} + +//--------------------------------------------------------------------------- +// T_OfsBounce() +//--------------------------------------------------------------------------- +void T_OfsBounce(objtype *ob) +{ + short oldtx,oldty; + long move,dx,dy,dist; + +// Should Electro-Sphere decrease player's health? +// + + dx = player->x - ob->x; + dx = LABS(dx); + dy = player->y - ob->y; + dy = LABS(dy); + dist = dx>dy ? dx : dy; + + if (dist < TILEGLOBAL) + { + PlaySoundLocActor(ELECARCDAMAGESND,ob); + TakeDamage(4,ob); + } + + +// Safety net - restart! Try to get a 'nodir' actor moving again. +// + if (ob->dir == nodir) + { + SphereStartDir(ob); + if (ob->dir == nodir) + return; + } + +// Make actor bounce around the map! +// + move = ob->speed*tics; + + while (move) + { + // Can actor move without reaching destination tile? + // + if (move < ob->distance) + { + MoveObj (ob,move); // YES! + break; + } + + // Align actor on destination tile. + // + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + ob->distance = TILEGLOBAL; + + // Can actor continue moving in current direction? + // + oldtx=ob->tilex; + oldty=ob->tiley; + if (!TryWalk(ob,true)) + { + boolean check_opposite=false; + + // Restore tilex/tiley + // + ob->tilex=oldtx; + ob->tiley=oldty; + + // Direction change is based on current direction. + // + switch (ob->dir) + { + case northeast: + case northwest: + case southeast: + case southwest: + if (ob->trydir != en_diagsphere) + { + if (!MoveTrappedDiag(ob)) + SphereStartDir(ob); + continue; + } + + ob->dir = (ob->dir+2)%8; // Try 90 degrees to the left + if (TryWalk(ob,false)) + break; + + ob->dir = opposite[ob->dir]; // Try 90 degrees to the right + if (TryWalk(ob,false)) + break; + + ob->dir = (ob->dir+2)%8; // Back to original direction.. + // Must be in a corner... + + check_opposite = true; + break; + + case north: + case south: + if (ob->trydir != en_vertsphere) + { + if (!MoveTrappedDiag(ob)) + SphereStartDir(ob); + continue; + } + + check_opposite = true; + break; + + case east: + case west: + if (ob->trydir != en_horzsphere) + { + if (!MoveTrappedDiag(ob)) + SphereStartDir(ob); + continue; + } + + check_opposite = true; + break; + } + + // Check opposite direction? + // + if (check_opposite) + { + ob->dir = opposite[ob->dir]; + if (!TryWalk(ob,false)) + ob->dir = nodir; + } + + // TryWalk "true" to set new destination tile. + // + if (!TryWalk(ob,true)) + ob->dir = nodir; + } + else + { + char orgx=ob->tilex,orgy=ob->tiley; + + ob->tilex=oldtx; + ob->tiley=oldty; + if (!MoveTrappedDiag(ob)) + { + ob->tilex=orgx; + ob->tiley=orgy; + } + } + } +} + +//--------------------------------------------------------------------------- +// MoveTrappedDiag() +//--------------------------------------------------------------------------- +boolean MoveTrappedDiag(objtype *ob) +{ +// Don't mess with HORZs, VERTs, or normal DIAGs. +// + if ((ob->trydir != en_diagsphere) || + ((ob->dir>>1<<1) != ob->dir)) + return(false); + +// When a DIAG gets to an open area, change it to diagonal movement! +// + if (!CheckTrappedDiag(ob)) + return(true); + +// DIAGs have a 50/50 chance of changing direction. +// + if (US_RndT()&1) + return(false); + +// Try changing directions. +// + if (ob->dir==north || ob->dir==south) + ob->trydir = en_horzsphere; + else + ob->trydir = en_vertsphere; + + CheckForcedMove(ob); + + ob->trydir = en_diagsphere; + + return(TryWalk(ob,true)); +} + +//--------------------------------------------------------------------------- +// CheckTrappedDiag() +//--------------------------------------------------------------------------- +boolean CheckTrappedDiag(objtype *ob) +{ + dirtype orgdir=ob->dir; + + for (ob->dir=northeast; ob->dir<=southeast; ob->dir += 2) + if (TryWalk(ob,false)) + break; + + if (ob->dir > southeast) + { + ob->dir = orgdir; + return(true); + } + else + { + TryWalk(ob,true); + return(false); + } +} + + + + +//--------------------------------------------------------------------------- +// FindObj() - This function will search the objlist for an object +// of a given type at a given tilex & tiley coords. +// +// PARAMETERS: +// which - objtype of actor to look for. +// tilex - tile x coord of actor looking for (-1 == Dont care) +// tiley - tile y coord of actor looking for (-1 == Dont care) +// +//--------------------------------------------------------------------------- +objtype *FindObj(classtype which, short tilex, short tiley) +{ + objtype *obj; + + for (obj=player->next;obj;obj=obj->next) + { + if (obj->obclass == which) + { + if (tilex != -1) + if (tilex != obj->tilex) + continue; + + if (tiley != -1) + if (tiley != obj->tiley) + continue; + + return(obj); + } + } + + return(NULL); +} + + + +//--------------------------------------------------------------------------- +// SpawnHiddenOfs() - This function will spawn a given offset actor at a passed +// x & y coords and then move the actor to (0,0) for hidding +// +// PURPOSE: This function was created mainly for spawning reserve objects that +// would later be simply moved from their (0,0) coords to a new +// location. +// +// NOTE: When this actor is moved from it's (0,0) coords, you will need to +// update the area number that this actor is standing in. +// +//--------------------------------------------------------------------------- +void SpawnHiddenOfs(enemy_t which, int tilex, int tiley) +{ + nevermark = true; + SpawnOffsetObj(which, tilex, tiley); // Spawn a reserve + nevermark = false; + new->tilex = 0; + new->tiley = 0; + new->x = TILEGLOBAL/2; + new->y = TILEGLOBAL/2; +} + + +//--------------------------------------------------------------------------- +// FindHiddenOfs() - This function will find a hidden ofs actor which was +// hidden using SpawnHiddenOfs() and will return a pointer +// to the actor in the objlist. +// +// NOTE: When this actor is moved from it's (0,0) coords, you will need to +// update the area number that this actor is standing in or use +// MoveHiddenOfs(). +//--------------------------------------------------------------------------- +objtype *FindHiddenOfs(classtype which) +{ + objtype *obj; + + if (!(obj = FindObj(which, -1, -1))) + ACT2_ERROR(CANT_FIND_HIDDEN_OBJ); + + return(obj); +} + + +//--------------------------------------------------------------------------- +// MoveHiddenOfs() - This function will find a hidden ofs actor which was +// hidden using SpawnHiddenOfs() and will move the actor +// to a new passed coords and change the actors class. +// +// RETURNS: Success = Ptr to the object that was moved. +// Failure = NULL. +// +// NOTE: This function updates the actors 'areanumber' to the newly changed +// coords. +//--------------------------------------------------------------------------- +objtype *MoveHiddenOfs(classtype which_class, classtype new_class, fixed x, fixed y) +{ + objtype *obj; + +#pragma warn -pia + if (obj=FindHiddenOfs(which_class)) +#pragma warn +pia + { + obj->obclass = new_class; + obj->x = x; + obj->y = y; + obj->tilex = x>>TILESHIFT; + obj->tiley = y>>TILESHIFT; + obj->areanumber=GetAreaNumber(obj->tilex, obj->tiley); + + return(obj); + } + + return(NULL); +} + + + +//=========================================================================== +// +// SMART_ANIM ANIMATION ROUTINES +// +// +// * NOTES: OffsetObjects require the use of TEMP1 of the object structure! +// +// * NOTES: THINK function AnimateOfsObj() requires the use of TEMP3 of the +// object structure! +// +//=========================================================================== + + +//--------------------------------------------------------------------------- +// InitSmartAnim() - Sets up an object for a SmartAnimation +// +// PARAMETERS: Obj - Ptr to object structure +// ShapeNum - First shape number in anim +// StartOfs - Starting offset in the animation to start with. +// MaxOfs - Ending offset in the animation. +// AnimType - Type of animation (at_CYCLE,at_ONCE,or at_REBOUND) +// AnimDir - Direction of animation to start (ad_FWD, or ad_REV) +// +// ** This function should ALWAY be used to init/start SmartAnimations! ** +// +// NOTE : It is the programmers responsiblity to watch bit field ranges on +// the passed parameters. +// +// NOTES: THINK function AnimateOfsObj() requires the use of TEMP3 of the +// object structure! +// +//--------------------------------------------------------------------------- + +void InitSmartAnimStruct(objtype *obj, unsigned ShapeNum, unsigned char StartOfs, unsigned char MaxOfs, animtype_t AnimType, animdir_t AnimDir) +{ + ofs_anim_t AnimStruct; + + AnimStruct.curframe = StartOfs; + AnimStruct.maxframe = MaxOfs; + AnimStruct.animtype = AnimType; + AnimStruct.animdir = AnimDir; + + obj->temp1 = ShapeNum + AnimStruct.curframe; + + *(ofs_anim_t *)&obj->temp3 = AnimStruct; +} + +void InitAnim(objtype *obj, unsigned ShapeNum, unsigned char StartOfs, unsigned char MaxOfs, animtype_t AnimType, animdir_t AnimDir, unsigned Delay, unsigned WaitDelay) +{ + InitSmartAnimStruct(obj,ShapeNum,StartOfs,MaxOfs,AnimType,AnimDir); + obj->s_tilex = WaitDelay; + obj->s_tiley = Delay; + NewState(obj,&s_ofs_smart_anim); +} + +// +// "s_ofs_smart_anim" - The following structure is designed for "Smart +// Animations." +// +// OVERVIEW: This structure will allow the flexiblity to perform ANY type +// of animation, custom, etc. using one single structure. +// All of the animations are using "Offset" techniques, where +// the current shapenumber of the object is in TEMP1 of the +// object structure, Code is then written to handle changing +// the shapenumber of each object individually. +// +// FUNCTIONS: T_SmartThink() - Is a think that is call continusly and is +// special coded for an object/actor. +// +// T_SmartThought() - Is called only once allowing for other +// special codeing, (Ex. the spawning of another animation at +// anypoint in the current animation, death screams, sounds,etc) +// +// * NOTES: The T_SmartThink could be modified to handle generic animations +// like cycle, once, rebound, etc. using flags in the object struct. +// +// + +//statetype s_ofs_smart_anim = {false,0, 1, NULL,T_SmartThought,&s_ofs_smart_anim}; +//statetype s_ofs_smart_anim2 = {false,0, 5, NULL,T_SmartThought,&s_ofs_smart_anim2}; + +statetype s_ofs_smart_anim = {false, 0, 1, T_SmartThought, NULL, &s_ofs_smart_anim}; +statetype s_ofs_smart_anim2 = {false, 0, 1, T_SmartThought, NULL, &s_ofs_smart_anim2}; + +// +// Functions +// + +//--------------------------------------------- +// T_SmartThought() - A think for ofset objects +// that is called ONLY ONCE +// per state change. +//--------------------------------------------- +void T_SmartThought(objtype *obj) +{ + long dx,dy; + + switch (obj->obclass) + { + case green_oozeobj: + case black_oozeobj: + case green2_oozeobj: + case black2_oozeobj: + { + if (((US_RndT()&7) == 7) && ((ofs_anim_t *)&obj->temp3)->curframe == 2 && obj->tilex == player->tilex && obj->tiley == player->tiley) + TakeDamage(4,obj); + } + break; + + case arc_barrierobj: + if (BARRIER_STATE(obj) == bt_DISABLED) + return; + + if (US_RndT() < 0x10) + { + dx = player->x - obj->x; + dx = LABS(dx); + dy = player->y - obj->y; + dy = LABS(dy); + + if (dy <= 0x16000 && dx <= 0x16000) + { + PlaySoundLocActor(ELECARCDAMAGESND,obj); + TakeDamage(4,obj); + } + } + + case post_barrierobj: + // + // Check for Turn offs + // + if ((unsigned)obj->temp2 != 0xffff) + if (!gamestate.barrier_table[obj->temp2].on) + ToggleBarrier(obj); + break; + + case volatiletransportobj: + case floatingbombobj: + if (obj->lighting) + { + // Slowly inc back to + + obj->lighting += ANIM_INFO(obj)->curframe; + if (obj->lighting>0) + obj->lighting = 0; + } + break; + + } + + if (ANIM_INFO(obj)->animtype) + { + if (AnimateOfsObj(obj)) + { + switch (obj->obclass) + { + case morphing_spider_mutantobj: + case morphing_reptilian_warriorobj: + case morphing_mutanthuman2obj: + dx = obj->obclass - morphing_spider_mutantobj; + obj->temp1 = MorphEndShapes[dx]; + PlaySoundLocActor(MorphSounds[dx],obj); + obj->obclass = MorphClass[dx]; + obj->hitpoints = starthitpoints[gamestate.difficulty][MorphClass[dx]-rentacopobj]; + obj->flags &= ~FL_FAKE_STATIC; + obj->flags |= FL_PROJ_TRANSPARENT|FL_SHOOTABLE; + NewState (obj,&s_ofs_chase1); + break; + + case podeggobj: + obj->flags |= FL_SHOOTABLE; + obj->obclass = podobj; + obj->temp1 = SPR_POD_WALK1; + NewState(obj,&s_ofs_chase1); + obj->hitpoints = starthitpoints[gamestate.difficulty][en_pod]; + break; + + case rotating_cubeobj: + DISPLAY_TIMED_MSG(pd_floorunlocked, MP_FLOOR_UNLOCKED,MT_GENERAL); + SD_PlaySound(ROLL_SCORESND); + obj->lighting = 0; + break; + + case inertobj: + case fixup_inertobj: + case scan_wait_alienobj: + case lcan_wait_alienobj: + case gurney_waitobj: + break; + + case gold_morphobj: + // + // Game completed! + // + playstate = ex_victorious; + obj->state = NULL; // Mark to be removed. + break; + + + case volatiletransportobj: + case floatingbombobj: + NewState(obj,&s_scout_dead); + obj->lighting = 0; + return; + + default: + obj->state = NULL; // Mark to be removed. + break; + } + } + + + if (ANIM_INFO(obj)->curframe == 3) + { + switch (obj->obclass) + { + case doorexplodeobj: + if (!obj->temp2) + { + int avail,total,i; + + // Make sure that there are at least DR_MIN_STATICS + + avail = MAXSTATS; + total = laststatobj-&statobjlist[0]; + for (i=0;i DR_MIN_STATICS) && (US_RndT() & 1)) + SpawnStatic(obj->tilex, obj->tiley, DOOR_RUBBLE_STATNUM); + } + break; + + case explosionobj: + if (!obj->temp2) + { + ExplodeRadius(obj, 20, true); + MakeAlertNoise(obj); + obj->temp2 = 1; + } + break; + + case pd_explosionobj: + if (!obj->temp2) + { + ExplodeRadius(obj, PLASMA_DETONATOR_DAMAGE, true); + MakeAlertNoise(obj); + obj->temp2 = 1; + } + + case bfg_explosionobj: + if (!obj->temp2) + { + ExplodeRadius(obj, BFG_DAMAGE, true); + MakeAlertNoise(obj); + obj->temp2 = 1; + } + break; + + + case gurney_waitobj: +#ifdef OBJ_RESERV +#if IN_DEVELOPMENT + if (((unsigned)obj->temp2 < (unsigned)objlist) || ((unsigned)obj->temp2 > (unsigned)&objlist[MAXACTORS])) + Quit("Gurney->temp2 out of range!"); +#endif + if (obj->temp2) + RemoveObj((objtype *)obj->temp2); +#endif + SpawnOffsetObj(en_gurney,obj->tilex,obj->tiley); + NewState(obj,&s_ofs_static); +// obj->obclass = fixup_inertobj; + break; + + case scan_wait_alienobj: +#ifdef OBJ_RESERV +#if IN_DEVELOPMENT + if (((unsigned)obj->temp2 < (unsigned)objlist) || ((unsigned)obj->temp2 >= (unsigned)&objlist[MAXACTORS])) + Quit("Scan->temp2 out of range!"); +#endif + if (obj->temp2) + RemoveObj((objtype *)obj->temp2); +#endif + + SpawnOffsetObj(en_scan_alien,obj->tilex,obj->tiley); + NewState(obj,&s_ofs_static); +// obj->obclass = fixup_inertobj; + break; + + case lcan_wait_alienobj: +#ifdef OBJ_RESERV +#if IN_DEVELOPMENT + if (((unsigned)obj->temp2 < (unsigned)objlist) || ((unsigned)obj->temp2 >= (unsigned)&objlist[MAXACTORS])) + Quit("Scan->temp2 out of range!"); +#endif + if (obj->temp2) + RemoveObj((objtype *)obj->temp2); +#endif + SpawnOffsetObj(en_lcan_alien,obj->tilex,obj->tiley); + NewState(obj,&s_ofs_static); +// obj->obclass = fixup_inertobj; + break; + } + } + + if (ANIM_INFO(obj)->curframe == 2) + { + switch (obj->obclass) + { + case volatiletransportobj: + if (!(obj->flags & FL_INTERROGATED)) + { + if (US_RndT() < 0x7f) + { + usedummy = true; + SpawnOffsetObj (en_green_ooze, obj->tilex, obj->tiley); + new->x = obj->x+(US_RndT()<<7); + new->y = obj->y+(US_RndT()<<7); + new->tilex = new->x>>TILESHIFT; + new->tiley = new->y>>TILESHIFT; + usedummy = false; + } + } + + case floatingbombobj: + if (!(obj->flags & FL_INTERROGATED)) + { + T_ExplodeDamage(obj); + obj->flags |= FL_INTERROGATED; + } + break; + } + } + } +} + +//------------------------------------------------------------------ +// AnimateOfsObj() - Animation routine for OffsetObjects. +// +// IMPORTANT NOTE: This does NOT work on the same principle as the old +// """""""""""""" AnimateObj() function! +// +// +// obj->TEMP1 - Holds the current shape number. +// +// obj->TEMP3.maxframe - Frames in anim. (0 == 1 Frame, 1 == 2 Frames, etc)) +// +// obj->TEMP3.curframe - Holds the the shape offset which TEMP1 is off from +// the 1st shape in the anim. ALWAYS POSITIVE NUMBER! +// REVerse animations have the "curframe" on the last +// offset and TEMP1-CURFRAME should equ the first frame +// and MAXFRAME should equ the offset for the first +// frame. +// +// obj->TEMP3.animdir - Determines the direction of the animation. +// +// +// obj->S_TILEX - Current number of tics remaining before advancing +// to next frame. +// +// obj->S_TILEY - Delay Tic value per frame - This value is copied +// into S_TILEX upon reaching the end of a frame. +// +// IF you want to do a reverse animation then you would need to init +// TEMP1 to the shape number of the LAST Shape, "curframe" to the offset +// value from the FIRST FRAME in anim and set "animdir" to at_REV. +// +// * NOTES: This is an OffsetObject which requires the use of TEMP1 of the +// object structure! +// +// * NOTES: The use of a SmartAnim requires the use of TEMP3 of the object +// structure!!! Therefore, Any other THINKS (like LookForGoodies) +// CAN NOT be used while this routine is being called also. +// +// * NOTES: ALL SmartAnimations have the SAME animation delay rates! Sorry! +// +// * NOTES: The SmartAnimations use S_TILEX & S_TILEY for animation delay +// values - All SMART actors can not be used if they are using +// a "smart" think! +// +//------------------------------------------------------------------ +boolean AnimateOfsObj(objtype *obj) +{ + boolean Done= false; + +#if 0 // Anim existance test moved to the calling function. + + if (ANIM_INFO(obj)->animtype == at_NONE) // Animation finished? + return(true); // YEP! + +#endif + + + if (obj->s_tilex) // Check animation delay. + { + if (obj->s_tilex < tics) + obj->s_tilex = 0; + else + obj->s_tilex -= tics; + return(false); + } + + switch (ANIM_INFO(obj)->animtype) // Animate this puppy! + { + case at_ONCE: + case at_CYCLE: + switch (ANIM_INFO(obj)->animdir) + { + case ad_FWD: + if (ANIM_INFO(obj)->curframe < ANIM_INFO(obj)->maxframe) + AdvanceAnimFWD(obj); + else + if (ANIM_INFO(obj)->animtype == at_CYCLE) + { + // Pull shape number back to start... + + obj->temp1-= ANIM_INFO(obj)->curframe; + + // Reset Cycle Animation + + ANIM_INFO(obj)->curframe = 0; + + obj->s_tilex=obj->s_tiley; + } + else + { + // Terminate ONCE Anim type + + ANIM_INFO(obj)->animtype = at_NONE; + Done = true; + } + break; + +#if 0 + + case ad_REV: + if (ANIM_INFO(obj)->curframe > 0) + AdvanceAnimREV(obj); + else + if (ANIM_INFO(obj)->animtype == at_CYCLE) + { + // Pull shape number up to start... + + obj->temp1 += ANIM_INFO(obj)->maxframe; + + // Reset REV cycle animation + + ANIM_INFO(obj)->curframe = ANIM_INFO(obj)->maxframe; + ANIM_INFO(obj)->animdelay = obj->s_tiley; + } + else + { + // Stop this puppy! + + ANIM_INFO(obj)->animtype = at_NONE; + Done = true; + } + break; // REV + +#endif + + } + break; + + +#if 0 + + case at_REBOUND: + switch (ANIM_INFO(obj)->animdir) + { + case ad_FWD: + if (ANIM_INFO(obj)->curframe < ANIM_INFO(obj)->maxframe) + AdvanceAnimFWD(obj); + else + { + ANIM_INFO(obj)->animdir = ad_REV; +// ANIM_INFO(obj)->animdelay = 1; + } + break; + + case ad_REV: + if (ANIM_INFO(obj)->curframe > 0) + AdvanceAnimREV(obj); + else + { + ANIM_INFO(obj)->animdir = ad_FWD; +// ANIM_INFO(obj)->animdelay = 1; + Done = true; + } + break; + } + break; /* REBOUND */ + +#endif + + + } + + return(Done); +} + + +//-------------------------------- +// AdvanceAnimFWD() +//-------------------------------- +void AdvanceAnimFWD(objtype *obj) +{ + ANIM_INFO(obj)->curframe++; // INC frames + + obj->temp1++; + obj->s_tilex = obj->s_tiley; +} + + +#if 0 + +//-------------------------------- +// AdvanceAnimREV() +//-------------------------------- +void AdvanceAnimREV(objtype *obj) +{ + ANIM_INFO(obj)->curframe--; // Dec frames + obj->temp1--; + +// ANIM_INFO(obj)->animdelay = ANIM_INFO(obj)->maxdelay; +} + +#endif + + + +//========================================================================== +// +// WALL SWITCH ACTIVATION +// +//========================================================================== + +//-------------------------------------------------------------------------- +// ActivateWallSwitch() - Updates the Map, Actors, and Tables for wall switchs +//-------------------------------------------------------------------------- +void ActivateWallSwitch(unsigned iconnum, short x, short y) +{ + #define UPDATE_OTHER_SWITCHES 1 + + unsigned states[] = {OFF_SWITCH,ON_SWITCH}; + unsigned mapx,mapy,newwall; + unsigned icon,num; + unsigned char *tile; + unsigned *actor; + barrier_type *barrier; + + + if ((iconnum & 0xFF00) == 0xF800) + { + + // + // Update tile map & Display switch'd message + // + num = iconnum & 0xff; + + + barrier = &gamestate.barrier_table[num]; + barrier->on ^= 1; + newwall = tilemap[x][y] = states[barrier->on]; + + DisplaySwitchOperateMsg(num); + PlaySoundLocActor(SWITCHSND,player); + +#if UPDATE_OTHER_SWITCHES + + tile = (unsigned char *)tilemap; + actor = (unsigned *)actorat; + + for (mapx=0;mapxon) + { + DISPLAY_TIMED_MSG(OnSwitchMessage,MP_WALLSWITCH_OPERATE,MT_GENERAL); + } + else + { + DISPLAY_TIMED_MSG(OffSwitchMessage,MP_WALLSWITCH_OPERATE,MT_GENERAL); + } +} + + +//-------------------------------------------------------------------------- +// UpdateBarrierTable(x,y,level) - Finds/Inserts arc entry in arc list +// +// RETURNS: Offset into barrier_table[] for a particular arc +// +//-------------------------------------------------------------------------- +unsigned UpdateBarrierTable(unsigned char x, unsigned char y, boolean OnOff) +{ + barrier_type *Barrier; + short num; + + // + // Scan Table... + // + + Barrier = gamestate.barrier_table; + + for (num = 0;num < MAX_BARRIER_SWITCHES;num++,Barrier++) + { + if (Barrier->coord.tilex == x && Barrier->coord.tiley == y) + { + return(num); + } + else + if (Barrier->on == 0xff) // Empty? + { + // We have hit end of list - Add + + Barrier->coord.tilex = x; + Barrier->coord.tiley = y; + Barrier->on = OnOff; + return(num); + } + } + + AGENT_ERROR(SWITCH_TABLE_OVERFLOW); + return(0); +} + + + +//-------------------------------------------------------------------------- +// ScanBarrierTable(x,y) - Scans a switch table for a arc in this level +// +// RETURNS : +// 0xFFFF - Not found in table +// barrier - barrier_table of the barrier for [num] +// +//-------------------------------------------------------------------------- +unsigned ScanBarrierTable(unsigned char x, unsigned char y) +{ + barrier_type *Barrier; + unsigned num; + + Barrier = gamestate.barrier_table; + + for (num=0;numcoord.tilex == x && Barrier->coord.tiley == y) + { + // Found switch... + + return(num); + } + } + + return(0xffff); // Mark as EMPTY +} + + +//-------------------------------------------------------------------------- +// Checks to see if the Barrier obj is free +//-------------------------------------------------------------------------- +boolean CheckActor(objtype *actor,unsigned code) +{ + if ((unsigned)actor->temp2 == 0xffff) // Is this actor free? + { + // + // Connect actor to barrier switch (code is index into barrier table) + // + + actor->temp2 = code; // This actor is NO longer a cycle actor. + return(true); + } + + return(false); +} + + +//-------------------------------------------------------------------------- +// CheckAndConnect() - +//-------------------------------------------------------------------------- +int CheckAndConnect(char x,char y, unsigned code) +{ + objtype *ob; + char offsets[] = {-1,0,1,0}; + short loop; + + ob = objlist; + + do + { + switch (ob->obclass) + { + case arc_barrierobj: + case post_barrierobj: + case vpost_barrierobj: + case vspike_barrierobj: + { + for (loop = 0;loop<4;loop++) + { + if ((ob->tilex == x+offsets[loop]) && (ob->tiley == y+offsets[3-loop])) + { + bars_connected++; + + if (CheckActor(ob,code)) + CheckAndConnect(x+offsets[loop],y+offsets[3-loop],code); + } + } + } + break; + } +#pragma warn -pia + } while (ob = ob->next); +#pragma warn +pia + + return(bars_connected); +} + +//-------------------------------------------------------------------------- +// ConnectBarriers() - Scans the object list and finds the single barrier +// that is connected by switch and checks to see if +// there are any surrounding barriers that need to be +// connected also. +//-------------------------------------------------------------------------- +void ConnectBarriers(void) +{ + barrier_type *Barrier; + unsigned num; + + Barrier = gamestate.barrier_table; + + for (num=0;numon != 0xff) + { + bars_connected = 0; + + if (!CheckAndConnect(Barrier->coord.tilex, Barrier->coord.tiley, num)) + AGENT_ERROR(BARRIER_SWITCH_NOT_CONNECTED); + } + } +} + + + +/* +============================================================================= + + BARRIERS + +============================================================================= +*/ + + +extern statetype s_barrier_transition; +void T_BarrierTransition(objtype *obj); +void T_BarrierShutdown(objtype *obj); + +statetype s_barrier_transition = {0,0,15,T_BarrierTransition,NULL,&s_barrier_transition}; +statetype s_vpost_barrier = {0,SPR_VPOST1,15,T_BarrierTransition,NULL,&s_vpost_barrier}; +statetype s_spike_barrier = {0,SPR_VSPIKE1,15,T_BarrierTransition,NULL,&s_spike_barrier}; +statetype s_barrier_shutdown = {0,0,15,T_BarrierShutdown,NULL,&s_barrier_shutdown}; + + +//--------------------------------------------------------------------------- +// SpawnBarrier() +//--------------------------------------------------------------------------- +void SpawnBarrier (enemy_t which, int tilex, int tiley,boolean OnOff) +{ + enemy_t dir_which; + + nevermark = !OnOff; + SpawnNewObj (tilex,tiley,&s_ofs_stand); + nevermark = false; + + if (OnOff) + new->flags = FL_OFFSET_STATES|FL_BARRIER|FL_FAKE_STATIC|FL_SOLID; + else + new->flags = FL_OFFSET_STATES|FL_BARRIER; + + new->obclass = rentacopobj+which; + new->ammo = OnOff; + new->temp2 = ScanBarrierTable(tilex,tiley); + new->flags2 = FL2_BFGSHOT_SOLID; + + switch (which) + { + case en_arc_barrier: + new->flags2 |= FL2_BFG_SHOOTABLE; + if (OnOff) + { + InitSmartSpeedAnim(new,SPR_ELEC_ARC1,US_RndT()%3,2,at_CYCLE,ad_FWD,3+(US_RndT()&3)); + new->lighting = LAMP_ON_SHADING; +// new->flags |= FL_SHOOTABLE; + } + else + { + NewState(new,&s_barrier_transition); + new->temp3 = 0; + new->flags &= ~(FL_SOLID|FL_FAKE_STATIC); + new->flags |= (FL_NEVERMARK|FL_NONMARK); + new->lighting = 0; + BARRIER_STATE(new) = bt_OFF; + new->temp1 = SPR_ELEC_ARC4; + } + break; + + + case en_post_barrier: + if (OnOff) + { + InitSmartSpeedAnim(new,SPR_ELEC_POST1,US_RndT()%3,2,at_CYCLE,ad_FWD,3+(US_RndT()&3)); + new->lighting = LAMP_ON_SHADING; + } + else + { + NewState(new,&s_barrier_transition); + new->temp3 = 0; + new->flags &= ~(FL_SOLID|FL_FAKE_STATIC); + new->flags |= (FL_NEVERMARK|FL_NONMARK); + new->lighting = 0; + BARRIER_STATE(new) = bt_OFF; + new->temp1 = SPR_ELEC_POST4; + } + break; + + + case en_vpost_barrier: + NewState(new,&s_vpost_barrier); + if (OnOff) + new->temp1 = SPR_VPOST8-SPR_VPOST1; + break; + + + case en_vspike_barrier: + NewState(new,&s_spike_barrier); + if (OnOff) + new->temp1 = SPR_VSPIKE8-SPR_VSPIKE1; + break; + } + +} + +//--------------------------------------------------------------------------- +// ToggleBarrier() +// +// OBJECT STATES: +// +// bt_ON -> bt_TURNING_OFF and think is changed to T_BarrierTrans +// +//--------------------------------------------------------------------------- + +void TurnPostOff(objtype *obj) +{ + NewState(obj,&s_barrier_transition); + obj->temp3 = 0; + obj->flags &= ~(FL_SOLID|FL_FAKE_STATIC); + obj->flags |= (FL_NEVERMARK|FL_NONMARK); + obj->lighting = 0; + BARRIER_STATE(obj) = bt_OFF; + +} + + +void TurnPostOn(objtype *obj) +{ + obj->flags |= (FL_SOLID|FL_FAKE_STATIC); + obj->flags &= ~(FL_NEVERMARK|FL_NONMARK); + obj->lighting = LAMP_ON_SHADING; + BARRIER_STATE(obj) = bt_ON; +} + + +void ToggleBarrier(objtype *obj) +{ + switch (BARRIER_STATE(obj)) + { + case bt_ON: // Same as closed + case bt_CLOSING: + // + // Turn OFF/Open + // + + switch (obj->obclass) + { + case post_barrierobj: + obj->temp1 = SPR_ELEC_POST4; + TurnPostOff(obj); + break; + + case arc_barrierobj: + obj->temp1 = SPR_ELEC_ARC4; + TurnPostOff(obj); + break; + + case vpost_barrierobj: + case vspike_barrierobj: + BARRIER_STATE(obj) = bt_OPENING; + break; + } + + break; + + case bt_OFF: // Same as open + case bt_OPENING: + // + // Turn ON/Closed + // + + switch (obj->obclass) + { + case post_barrierobj: + InitSmartSpeedAnim(obj,SPR_ELEC_POST1,US_RndT()%3,2,at_CYCLE,ad_FWD,3+(US_RndT()&3)); + TurnPostOn(obj); + break; + + case arc_barrierobj: + InitSmartSpeedAnim(obj,SPR_ELEC_ARC1,US_RndT()%3,2,at_CYCLE,ad_FWD,3+(US_RndT()&3)); + TurnPostOn(obj); + break; + + case vpost_barrierobj: + case vspike_barrierobj: + BARRIER_STATE(obj) = bt_CLOSING; + break; + } + break; + } +} + + + + +//--------------------------------------------------------------------------- +// T_BarrierShutdown() - This is used ONLY for electric arc barriers +// which "flicker" out when disabled/destroyed. +// +//--------------------------------------------------------------------------- +void T_BarrierShutdown(objtype *obj) +{ + switch (BARRIER_STATE(obj)) + { + case bt_DISABLING: + if (obj->hitpoints) + { + if (obj->temp2 < tics) + { + if (obj->temp1 == SPR_ELEC_ARC4) + { + obj->temp1 = SPR_ELEC_ARC1+obj->temp3; + obj->temp3 = (obj->temp3+1) % 4; + + PlaySoundLocActor(ELECARCDAMAGESND,obj); + obj->flags |= (FL_SOLID|FL_FAKE_STATIC); + obj->flags &= ~(FL_NEVERMARK|FL_NONMARK); + obj->lighting = LAMP_ON_SHADING; + obj->temp2 = US_RndT()&0x7; + } + else + { + obj->temp1 = SPR_ELEC_ARC4; + obj->flags &= ~(FL_SOLID|FL_FAKE_STATIC); + obj->flags |= (FL_NEVERMARK|FL_NONMARK); + obj->lighting = 0; + obj->temp2 = 5+US_RndT()&0xf; + } + + obj->hitpoints--; + } + else + obj->temp2 -= tics; + } + else + { + BARRIER_STATE(obj) = bt_DISABLED; + } + break; + + case bt_DISABLED: + obj->flags |= (FL_NEVERMARK|FL_NONMARK); +// obj->flags &= ~(FL_SOLID); //|FL_FAKE_STATIC); + obj->flags &= ~(FL_SOLID|FL_FAKE_STATIC); + obj->lighting = 0; + if (obj->obclass == post_barrierobj) + obj->temp1 = SPR_ELEC_POST4; + else + obj->temp1 = SPR_ELEC_ARC4; + obj->temp3 = 0; + NewState(obj,&s_ofs_smart_anim); + break; + + } +} + +//--------------------------------------------------------------------------- +// T_BarrierTransition() - +// +//--------------------------------------------------------------------------- +void T_BarrierTransition(objtype *obj) +{ + fixed dx,dy; + + switch (BARRIER_STATE(obj)) + { + // + // ON/CLOSED POSITION + // + case bt_ON: + // + // Check for cycle barrier. + // + if ((unsigned)obj->temp2 == 0xFFFF) + { + if (obj->temp3 < tics) + ToggleBarrier(obj); + else + obj->temp3-=tics; + } + else + if (!gamestate.barrier_table[obj->temp2].on) + ToggleBarrier(obj); + break; + + + // + // OFF/OPEN POSITION + // + case bt_OFF: + // + // Check for cycle barrier. + // + if ((unsigned)obj->temp2 == 0xFFFF) + { + if (obj->temp3 < tics) + ToggleBarrier(obj); + else + obj->temp3-=tics; + } + else + if (gamestate.barrier_table[obj->temp2].on) + ToggleBarrier(obj); + break; + + + // + // CLOSING/TURNING ON + // + case bt_CLOSING: + // Check for damaging the player + + dx = player->x - obj->x; + dx = LABS(dx); + dy = player->y - obj->y; + dy = LABS(dy); + + if (dy <= 0x17FFF && dx <= 0x17FFF) + { + if (dy <= 0x7FFF && dx <= 0x7FFF) + TakeDamage(2,obj); + break; + } + + if (obj->temp3 < tics) + { + obj->temp3 = VPOST_BARRIER_SPEED; + obj->temp1++; + + if (obj->temp1 > 7) + obj->temp1 = 7; // Errors... + + switch (obj->temp1) + { + case 3: + // Closed enough to be solid + // + + obj->flags |= (FL_SOLID|FL_FAKE_STATIC); + obj->flags &= ~(FL_NEVERMARK|FL_NONMARK); + break; + + case 7: + // Fully closed dude! + // + + BARRIER_STATE(obj) = bt_ON; + // + // Check for cycle barrier. + // + if ((unsigned)obj->temp2 == 0xFFFF) + { + obj->temp3 = VPOST_WAIT_DELAY; + } + break; + } + } + else + obj->temp3 -= tics; + + // Check to see if to was toggled + + if ((unsigned)obj->temp2 != 0xFFFF) + if (!gamestate.barrier_table[obj->temp2].on) + ToggleBarrier(obj); + break; + + + // + // OPENING/TURNNING OFF + // + case bt_OPENING: + if (obj->temp3 < tics) + { + obj->temp3 = VPOST_BARRIER_SPEED; + obj->temp1--; + + if (obj->temp1 < 0) + obj->temp1 = 0; // Errors... + + + switch (obj->temp1) + { + case 2: + // + // Open enough not to be solid. + + obj->flags &= ~(FL_SOLID|FL_FAKE_STATIC); + obj->flags |= (FL_NEVERMARK|FL_NONMARK); + break; + + case 0: + // Fully Open dude! + // + + BARRIER_STATE(obj) = bt_OFF; + + // + // Check for cycle barrier. + // + if ((unsigned)obj->temp2 == 0xFFFF) + obj->temp3 = VPOST_WAIT_DELAY; + break; + } + } + else + obj->temp3 -= tics; + + // Check to see if to was toggled + + if ((unsigned)obj->temp2 != 0xFFFF) + if (gamestate.barrier_table[obj->temp2].on) + ToggleBarrier(obj); + break; + } +} + + + +/* +============================================================================= + + GUARD + +============================================================================= +*/ + + + +// +// Rent-A-Cop +// + +extern statetype s_rent_stand; + +extern statetype s_rent_path1; +extern statetype s_rent_path1s; +extern statetype s_rent_path2; +extern statetype s_rent_path3; +extern statetype s_rent_path3s; +extern statetype s_rent_path4; + +extern statetype s_rent_pain; + +extern statetype s_rent_giveup; + +extern statetype s_rent_shoot1; +extern statetype s_rent_shoot2; +extern statetype s_rent_shoot3; +extern statetype s_rent_shoot4; + +extern statetype s_rent_chase1; +extern statetype s_rent_chase1s; +extern statetype s_rent_chase2; +extern statetype s_rent_chase3; +extern statetype s_rent_chase3s; +extern statetype s_rent_chase4; + +extern statetype s_rent_die1; +extern statetype s_rent_die1d; +extern statetype s_rent_die2; +extern statetype s_rent_die3; +extern statetype s_rent_die3s; +extern statetype s_rent_die4; + +statetype s_rent_stand = {true,SPR_RENT_S_1,0,T_Stand,NULL,&s_rent_stand}; + +statetype s_rent_path1 = {true,SPR_RENT_W1_1,20,T_Path,NULL,&s_rent_path1s}; +statetype s_rent_path1s = {true,SPR_RENT_W1_1,5,NULL,NULL,&s_rent_path2}; +statetype s_rent_path2 = {true,SPR_RENT_W2_1,15,T_Path,NULL,&s_rent_path3}; +statetype s_rent_path3 = {true,SPR_RENT_W3_1,20,T_Path,NULL,&s_rent_path3s}; +statetype s_rent_path3s = {true,SPR_RENT_W3_1,5,NULL,NULL,&s_rent_path4}; +statetype s_rent_path4 = {true,SPR_RENT_W4_1,15,T_Path,NULL,&s_rent_path1}; + +statetype s_rent_pain = {2,SPR_RENT_PAIN_1,15,NULL,NULL,&s_rent_chase1}; + +statetype s_rent_shoot1 = {false,SPR_RENT_SHOOT1,20,NULL,NULL,&s_rent_shoot2}; +statetype s_rent_shoot2 = {false,SPR_RENT_SHOOT2,20,NULL,T_Shoot,&s_rent_shoot3}; +statetype s_rent_shoot3 = {false,SPR_RENT_SHOOT3,20,NULL,T_Shade,&s_rent_chase1}; + +statetype s_rent_chase1 = {true,SPR_RENT_W1_1,10,T_Chase,NULL,&s_rent_chase1s}; +statetype s_rent_chase1s = {true,SPR_RENT_W1_1,3,NULL,NULL,&s_rent_chase2}; +statetype s_rent_chase2 = {true,SPR_RENT_W2_1,8,T_Chase,NULL,&s_rent_chase3}; +statetype s_rent_chase3 = {true,SPR_RENT_W3_1,10,T_Chase,NULL,&s_rent_chase3s}; +statetype s_rent_chase3s = {true,SPR_RENT_W3_1,3,NULL,NULL,&s_rent_chase4}; +statetype s_rent_chase4 = {true,SPR_RENT_W4_1,8,T_Chase,NULL,&s_rent_chase1}; + +statetype s_rent_die1 = {false,SPR_RENT_DIE_1,17,T_BlowBack,A_DeathScream,&s_rent_die2}; +statetype s_rent_die2 = {false,SPR_RENT_DIE_2,21,T_BlowBack,NULL,&s_rent_die3}; +statetype s_rent_die3 = {false,SPR_RENT_DIE_3,16,T_BlowBack,NULL,&s_rent_die3s}; +statetype s_rent_die3s = {false,SPR_RENT_DIE_4,13,T_BlowBack,NULL,&s_rent_die4}; +statetype s_rent_die4 = {false,SPR_RENT_DEAD,0,NULL,NULL,&s_rent_die4}; + +// +// 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_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,15,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,T_Shade,&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,15,T_BlowBack,A_DeathScream,&s_ofcdie2}; +statetype s_ofcdie2 = {false,SPR_OFC_DIE_2,15,T_BlowBack,NULL,&s_ofcdie3}; +statetype s_ofcdie3 = {false,SPR_OFC_DIE_3,15,T_BlowBack,NULL,&s_ofcdie4}; +statetype s_ofcdie4 = {false,SPR_OFC_DIE_4,15,T_BlowBack,NULL,&s_ofcdie5}; +statetype s_ofcdie5 = {false,SPR_OFC_DEAD,0,NULL,NULL,&s_ofcdie5}; + + +// +// SWAT +// + +extern statetype s_swatstand; + +extern statetype s_swatpath1; +extern statetype s_swatpath1s; +extern statetype s_swatpath2; +extern statetype s_swatpath3; +extern statetype s_swatpath3s; +extern statetype s_swatpath4; + +extern statetype s_swatpain; + +extern statetype s_swatgiveup; + +extern statetype s_swatshoot1; +extern statetype s_swatshoot2; +extern statetype s_swatshoot3; +extern statetype s_swatshoot4; +extern statetype s_swatshoot5; +extern statetype s_swatshoot6; +extern statetype s_swatshoot7; + +extern statetype s_swatchase1; +extern statetype s_swatchase1s; +extern statetype s_swatchase2; +extern statetype s_swatchase3; +extern statetype s_swatchase3s; +extern statetype s_swatchase4; + +extern statetype s_swatwounded1; +extern statetype s_swatwounded2; +extern statetype s_swatwounded3; +extern statetype s_swatwounded4; + +extern statetype s_swatunwounded1; +extern statetype s_swatunwounded2; +extern statetype s_swatunwounded3; +extern statetype s_swatunwounded4; + +extern statetype s_swatdie1; +extern statetype s_swatdie2; +extern statetype s_swatdie3; +extern statetype s_swatdie4; +extern statetype s_swatdie5; + + +statetype s_swatstand = {true,SPR_SWAT_S_1,0,T_Stand,NULL,&s_swatstand}; + +statetype s_swatpath1 = {true,SPR_SWAT_W1_1,20,T_Path,NULL,&s_swatpath1s}; +statetype s_swatpath1s = {true,SPR_SWAT_W1_1,5,NULL,NULL,&s_swatpath2}; +statetype s_swatpath2 = {true,SPR_SWAT_W2_1,15,T_Path,NULL,&s_swatpath3}; +statetype s_swatpath3 = {true,SPR_SWAT_W3_1,20,T_Path,NULL,&s_swatpath3s}; +statetype s_swatpath3s = {true,SPR_SWAT_W3_1,5,NULL,NULL,&s_swatpath4}; +statetype s_swatpath4 = {true,SPR_SWAT_W4_1,15,T_Path,NULL,&s_swatpath1}; + +statetype s_swatpain = {2,SPR_SWAT_PAIN_1,15,NULL,NULL,&s_swatshoot1}; + +statetype s_swatshoot1 = {false,SPR_SWAT_SHOOT1,10,NULL,NULL,&s_swatshoot2}; +statetype s_swatshoot2 = {false,SPR_SWAT_SHOOT2,20,NULL,T_Shoot,&s_swatshoot3}; +statetype s_swatshoot3 = {false,SPR_SWAT_SHOOT3,10,NULL,T_Shade,&s_swatshoot4}; +statetype s_swatshoot4 = {false,SPR_SWAT_SHOOT2,10,NULL,T_Shoot,&s_swatshoot5}; +statetype s_swatshoot5 = {false,SPR_SWAT_SHOOT3,10,NULL,T_Shade,&s_swatshoot6}; +statetype s_swatshoot6 = {false,SPR_SWAT_SHOOT2,10,NULL,T_Shoot,&s_swatshoot7}; +statetype s_swatshoot7 = {false,SPR_SWAT_SHOOT3,10,NULL,T_Shade,&s_swatchase1}; + +statetype s_swatchase1 = {true,SPR_SWAT_W1_1,10,T_Chase,NULL,&s_swatchase1s}; +statetype s_swatchase1s = {true,SPR_SWAT_W1_1,3,NULL,NULL,&s_swatchase2}; +statetype s_swatchase2 = {true,SPR_SWAT_W2_1,8,T_Chase,NULL,&s_swatchase3}; +statetype s_swatchase3 = {true,SPR_SWAT_W3_1,10,T_Chase,NULL,&s_swatchase3s}; +statetype s_swatchase3s = {true,SPR_SWAT_W3_1,3,NULL,NULL,&s_swatchase4}; +statetype s_swatchase4 = {true,SPR_SWAT_W4_1,8,T_Chase,NULL,&s_swatchase1}; + +statetype s_swatwounded1 = {false,SPR_SWAT_WOUNDED1,10,NULL,NULL,&s_swatwounded2}; +statetype s_swatwounded2 = {false,SPR_SWAT_WOUNDED2,10,NULL,NULL,&s_swatwounded3}; +statetype s_swatwounded3 = {false,SPR_SWAT_WOUNDED3,10,NULL,NULL,&s_swatwounded4}; +statetype s_swatwounded4 = {false,SPR_SWAT_WOUNDED4,10,T_SwatWound,NULL,&s_swatwounded4}; + +statetype s_swatunwounded1 = {false,SPR_SWAT_WOUNDED4,10,NULL,NULL,&s_swatunwounded2}; +statetype s_swatunwounded2 = {false,SPR_SWAT_WOUNDED3,10,NULL,NULL,&s_swatunwounded3}; +statetype s_swatunwounded3 = {false,SPR_SWAT_WOUNDED2,10,NULL,NULL,&s_swatunwounded4}; +statetype s_swatunwounded4 = {false,SPR_SWAT_WOUNDED1,10,NULL,T_SwatWound,&s_swatchase1}; + +statetype s_swatdie1 = {false,SPR_SWAT_DIE_1,20,T_BlowBack,A_DeathScream,&s_swatdie2}; +statetype s_swatdie2 = {false,SPR_SWAT_DIE_2,20,T_BlowBack,NULL,&s_swatdie3}; +statetype s_swatdie3 = {false,SPR_SWAT_DIE_3,20,T_BlowBack,NULL,&s_swatdie4}; +statetype s_swatdie4 = {false,SPR_SWAT_DIE_4,20,T_BlowBack,NULL,&s_swatdie5}; +statetype s_swatdie5 = {false,SPR_SWAT_DEAD,0,NULL,NULL,&s_swatdie5}; + + +// +// PRO Guard +// + +extern statetype s_prostand; + +extern statetype s_propath1; +extern statetype s_propath1s; +extern statetype s_propath2; +extern statetype s_propath3; +extern statetype s_propath3s; +extern statetype s_propath4; + +extern statetype s_propain; + +extern statetype s_proshoot1; +extern statetype s_proshoot2; +extern statetype s_proshoot3; +extern statetype s_proshoot4; +extern statetype s_proshoot5; +extern statetype s_proshoot6; +extern statetype s_proshoot6a; +//extern statetype s_proshoot7; +//extern statetype s_proshoot8; +//extern statetype s_proshoot9; + +extern statetype s_prochase1; +extern statetype s_prochase1s; +extern statetype s_prochase2; +extern statetype s_prochase3; +extern statetype s_prochase3s; +extern statetype s_prochase4; + +extern statetype s_prodie1; +extern statetype s_prodie2; +extern statetype s_prodie3; +extern statetype s_prodie3a; +extern statetype s_prodie4; + +statetype s_prostand = {true,SPR_PRO_S_1,0,T_Stand,NULL,&s_prostand}; + +statetype s_propath1 = {true,SPR_PRO_W1_1,20,T_Path,NULL,&s_propath1s}; +statetype s_propath1s = {true,SPR_PRO_W1_1,5,NULL,NULL,&s_propath2}; +statetype s_propath2 = {true,SPR_PRO_W2_1,15,T_Path,NULL,&s_propath3}; +statetype s_propath3 = {true,SPR_PRO_W3_1,20,T_Path,NULL,&s_propath3s}; +statetype s_propath3s = {true,SPR_PRO_W3_1,5,NULL,NULL,&s_propath4}; +statetype s_propath4 = {true,SPR_PRO_W4_1,15,T_Path,NULL,&s_propath1}; + +statetype s_propain = {2,SPR_PRO_PAIN_1,15,NULL,NULL,&s_prochase1}; + +statetype s_proshoot1 = {false,SPR_PRO_SHOOT1,20,NULL,NULL,&s_proshoot2}; +statetype s_proshoot2 = {false,SPR_PRO_SHOOT2,20,NULL,T_Shoot,&s_proshoot3}; +statetype s_proshoot3 = {false,SPR_PRO_SHOOT3,10,NULL,T_Shade,&s_proshoot4}; +statetype s_proshoot4 = {false,SPR_PRO_SHOOT2,10,NULL,T_Shoot,&s_proshoot5}; +statetype s_proshoot5 = {false,SPR_PRO_SHOOT3,10,NULL,T_Shade,&s_proshoot6}; +statetype s_proshoot6 = {false,SPR_PRO_SHOOT2,10,NULL,T_Shoot,&s_proshoot6a}; +statetype s_proshoot6a = {false,SPR_PRO_SHOOT3,10,NULL,T_Shade,&s_prochase1}; + +//statetype s_proshoot7 = {false,SPR_PRO_SHOOT3,10,NULL,NULL,&s_proshoot8}; +//statetype s_proshoot8 = {false,SPR_PRO_SHOOT2,10,NULL,T_Shoot,&s_proshoot9}; +//statetype s_proshoot9 = {false,SPR_PRO_SHOOT3,10,NULL,T_Shade,&s_prochase1}; + +statetype s_prochase1 = {true,SPR_PRO_W1_1,10,T_Chase,NULL,&s_prochase1s}; +statetype s_prochase1s = {true,SPR_PRO_W1_1,3,NULL,NULL,&s_prochase2}; +statetype s_prochase2 = {true,SPR_PRO_W2_1,8,T_Chase,NULL,&s_prochase3}; +statetype s_prochase3 = {true,SPR_PRO_W3_1,10,T_Chase,NULL,&s_prochase3s}; +statetype s_prochase3s = {true,SPR_PRO_W3_1,3,NULL,NULL,&s_prochase4}; +statetype s_prochase4 = {true,SPR_PRO_W4_1,8,T_Chase,NULL,&s_prochase1}; + +statetype s_prodie1 = {false,SPR_PRO_DIE_1,20,T_BlowBack,A_DeathScream,&s_prodie2}; +statetype s_prodie2 = {false,SPR_PRO_DIE_2,20,T_BlowBack,NULL,&s_prodie3}; +statetype s_prodie3 = {false,SPR_PRO_DIE_3,20,T_BlowBack,NULL,&s_prodie3a}; +statetype s_prodie3a = {false,SPR_PRO_DIE_4,20,T_BlowBack,NULL,&s_prodie4}; +statetype s_prodie4 = {false,SPR_PRO_DEAD,0,NULL,NULL,&s_prodie4}; + +extern statetype s_electro_appear1; +extern statetype s_electro_appear2; +extern statetype s_electro_appear3; + +extern statetype s_electro_chase1; +extern statetype s_electro_chase2; +extern statetype s_electro_chase3; +extern statetype s_electro_chase4; + +extern statetype s_electro_ouch; + +extern statetype s_electro_shoot1; +extern statetype s_electro_shoot2; +extern statetype s_electro_shoot3; + +extern statetype s_electro_shot1; +extern statetype s_electro_shot2; + +extern statetype s_electro_shot_exp1; +extern statetype s_electro_shot_exp2; + +extern statetype s_ofs_shot1; +extern statetype s_ofs_shot2; + +extern statetype s_ofs_shot_exp1; +extern statetype s_ofs_shot_exp2; + +extern statetype s_electro_die1; +extern statetype s_electro_die2; +extern statetype s_electro_die3; + +statetype s_electro_appear1 = {0,SPR_ELEC_APPEAR1,14,NULL,NULL,&s_electro_appear2}; +statetype s_electro_appear2 = {0,SPR_ELEC_APPEAR2,14,NULL,NULL,&s_electro_appear3}; +statetype s_electro_appear3 = {0,SPR_ELEC_APPEAR3,14,NULL,NULL,&s_electro_chase1}; + +statetype s_electro_chase1 = {0,SPR_ELEC_WALK1,14,T_Chase,NULL,&s_electro_chase2}; +statetype s_electro_chase2 = {0,SPR_ELEC_WALK2,14,NULL,NULL,&s_electro_chase3}; +statetype s_electro_chase3 = {0,SPR_ELEC_WALK3,14,T_Chase,NULL,&s_electro_chase4}; +statetype s_electro_chase4 = {0,SPR_ELEC_WALK4,14,NULL,NULL,&s_electro_chase1}; + +statetype s_electro_ouch = {0,SPR_ELEC_OUCH,14,NULL,NULL,&s_electro_chase1}; + +statetype s_electro_shoot1 = {0,SPR_ELEC_SHOOT1,14,NULL,NULL,&s_electro_shoot2}; +statetype s_electro_shoot2 = {0,SPR_ELEC_SHOOT2,14,T_Shoot,NULL,&s_electro_shoot3}; +statetype s_electro_shoot3 = {0,SPR_ELEC_SHOOT3,14,NULL,NULL,&s_electro_chase1}; + +statetype s_electro_shot1 = {0,SPR_ELEC_SHOT1,1,T_Projectile,NULL,&s_electro_shot2}; +statetype s_electro_shot2 = {0,SPR_ELEC_SHOT2,1,T_Projectile,NULL,&s_electro_shot1}; + +statetype s_ofs_shot1 = {0,0,1,T_Projectile,NULL,&s_ofs_shot2}; +statetype s_ofs_shot2 = {0,1,1,T_Projectile,NULL,&s_ofs_shot1}; + + +statetype s_electro_die1 = {0,SPR_ELEC_DIE1,14,NULL,A_DeathScream,&s_electro_die2}; +statetype s_electro_die2 = {0,SPR_ELEC_DIE2,14,NULL,NULL,&s_electro_die3}; +statetype s_electro_die3 = {0,SPR_ELEC_DIE3,14,NULL,NULL,NULL}; + + +extern statetype s_liquid_wait; +extern statetype s_liquid_move; + +extern statetype s_liquid_rise1; +extern statetype s_liquid_rise2; +extern statetype s_liquid_rise3; +extern statetype s_liquid_rise4; + +extern statetype s_liquid_stand; + +extern statetype s_liquid_fall1; +extern statetype s_liquid_fall2; +extern statetype s_liquid_fall3; +extern statetype s_liquid_fall4; + +extern statetype s_liquid_shoot1; +extern statetype s_liquid_shoot2; +extern statetype s_liquid_shoot3; + +extern statetype s_liquid_ouch; + +extern statetype s_liquid_die1; +extern statetype s_liquid_die2; +extern statetype s_liquid_die3; +extern statetype s_liquid_die4; +extern statetype s_liquid_dead; + +extern statetype s_blake1; +extern statetype s_blake2; +extern statetype s_blake3; +extern statetype s_blake4; + +extern statetype s_liquid_shot; + +void T_LiquidStand_Check(objtype *obj); +void T_LiquidMove(objtype *obj); +void T_Solid(objtype *obj); + + +statetype s_liquid_wait = {0,SPR_LIQUID_M1,14,T_Stand,NULL,&s_liquid_wait}; + +statetype s_liquid_move = {0,SPR_LIQUID_M1,14,T_LiquidMove,T_ChangeShape,&s_liquid_move}; + +statetype s_liquid_rise1 = {0,SPR_LIQUID_R1,12,NULL,NULL,&s_liquid_rise2}; +statetype s_liquid_rise2 = {0,SPR_LIQUID_R2,12,NULL,NULL,&s_liquid_rise3}; +statetype s_liquid_rise3 = {0,SPR_LIQUID_R3,12,NULL,T_Solid,&s_liquid_shoot1}; +//statetype s_liquid_rise4 = {0,SPR_LIQUID_R4,12,NULL,NULL,&s_liquid_stand}; + +statetype s_liquid_stand = {0,SPR_LIQUID_R4,40,T_LiquidStand,NULL,&s_liquid_stand}; + +statetype s_liquid_fall1 = {0,SPR_LIQUID_R3,15,NULL,NULL,&s_liquid_fall2}; +statetype s_liquid_fall2 = {0,SPR_LIQUID_R2,15,NULL,NULL,&s_liquid_fall3}; +statetype s_liquid_fall3 = {0,SPR_LIQUID_R1,15,NULL,NULL,&s_liquid_move}; + +statetype s_liquid_shoot1 = {0,SPR_LIQUID_S1,12,NULL,NULL,&s_liquid_shoot2}; +statetype s_liquid_shoot2 = {0,SPR_LIQUID_S2,12,NULL,NULL,&s_liquid_shoot3}; +statetype s_liquid_shoot3 = {0,SPR_LIQUID_S3,12,NULL,T_Shoot,&s_liquid_stand}; + +statetype s_liquid_ouch = {0,SPR_LIQUID_OUCH,7,NULL,NULL,&s_liquid_shoot1}; + +statetype s_liquid_die1 = {0,SPR_LIQUID_DIE_1,20,NULL,A_DeathScream,&s_liquid_die2}; +statetype s_liquid_die2 = {0,SPR_LIQUID_DIE_2,20,NULL,NULL,&s_liquid_die3}; +statetype s_liquid_die3 = {0,SPR_LIQUID_DIE_3,20,NULL,NULL,&s_liquid_die4}; +statetype s_liquid_die4 = {0,SPR_LIQUID_DIE_4,20,NULL,NULL,&s_liquid_dead}; +statetype s_liquid_dead = {0,SPR_LIQUID_DEAD,20,NULL,NULL,NULL}; + +statetype s_liquid_shot = {0,0,10,T_Projectile,T_ChangeShape,&s_liquid_shot}; + +statetype s_blake1 = {0,SPR_BLAKE_W1,12,NULL,NULL,&s_blake2}; +statetype s_blake2 = {0,SPR_BLAKE_W2,12,NULL,NULL,&s_blake3}; +statetype s_blake3 = {0,SPR_BLAKE_W3,12,NULL,NULL,&s_blake4}; +statetype s_blake4 = {0,SPR_BLAKE_W4,12,NULL,NULL,&s_blake1}; + +//--------------------------------------------------------------------------- +// T_ChangeShape() +//--------------------------------------------------------------------------- +void T_ChangeShape(objtype *obj) +{ +// obj->temp1 = obj->temp2 + random(3); + obj->temp1 = obj->temp2 + (US_RndT()%3); +} + +//-------------------------------------------------------------------------- +// T_MakeOffset() +//-------------------------------------------------------------------------- +void T_MakeOffset(objtype *obj) +{ + obj->flags |= FL_OFFSET_STATES; + obj->flags &= ~(FL_SOLID | FL_SHOOTABLE); +} + +//-------------------------------------------------------------------------- +// T_Solid() +//-------------------------------------------------------------------------- +void T_Solid(objtype *obj) +{ + obj->flags |= (FL_SOLID | FL_SHOOTABLE); +} + +//--------------------------------------------------------------------------- +// T_LiquidMove() +//--------------------------------------------------------------------------- +void T_LiquidMove(objtype *obj) +{ + int dx,dy,dist; + + + // + // Check to see if the Liquid Obj is VERY Close - Then FORCE up and + // start firing... + // + + dx = abs(obj->tilex - player->tilex); + dy = abs(obj->tiley - player->tiley); + dist = dx>dy ? dx : dy; + + if (dist < 6 && dx > 1 && dy > 1) + { + obj->flags &= ~(FL_OFFSET_STATES); + obj->flags |= FL_SOLID; + NewState(obj,&s_liquid_rise1); + } + else + T_Chase(obj); +} + + + + +//--------------------------------------------------------------------------- +// T_LiquidStand() +//--------------------------------------------------------------------------- +void T_LiquidStand(objtype *obj) +{ + int dx,dy; + + obj->flags |= FL_SHOOTABLE | FL_SOLID; + + if (US_RndT() < 80 && obj->temp2 < 5) + { + obj->temp2++; + NewState(obj,&s_liquid_shoot1); + } + else + { + dx = abs(obj->tilex - player->tilex); + dy = abs(obj->tiley - player->tiley); + + if (dx > 1 || dy > 1) + { + if (!(obj->flags & FL_VISABLE) || (US_RndT() < 40) || (obj->temp2 == 5)) + { + NewState(obj,&s_liquid_fall1); + obj->flags |= FL_OFFSET_STATES; + obj->flags &= ~(FL_SOLID | FL_SHOOTABLE); + obj->temp2 = 0; + } + } + else + obj->temp2 = 0; + } +} + + + +//--------------------------------------------------------------------------- +// T_SwatWound() +//--------------------------------------------------------------------------- +void T_SwatWound(objtype *ob) +{ + long dx,dy; + + if (ob->state == &s_swatunwounded4) + { + ob->flags |= FL_SOLID|FL_SHOOTABLE; + } + else + { + if (ob->temp2) + if (tics < ob->temp2) + { + ob->temp2 -= tics; + return; + } + + ob->temp2 = 0; + + dx = player->x - ob->x; + dx = LABS(dx); + dy = player->y - ob->y; + dy = LABS(dy); + + if (dy > TILEGLOBAL || dx > TILEGLOBAL) + { + ob->flags |= FL_SOLID|FL_SHOOTABLE; + NewState(ob,&s_swatunwounded1); + } + + +// if ((ob->tilex != player->tilex) && (ob->tiley != player->tiley)) +// NewState(ob,&s_swatunwounded1); + } +} + + + +/* +=============== += += SpawnStand += +=============== +*/ +void SpawnStand (enemy_t which, int tilex, int tiley, int dir) +{ + unsigned far *map,tile,ammo=8; + + switch (which) + { + case en_goldstern: + SpawnNewObj (tilex,tiley,&s_goldwarp_in1); + new->flags = FL_SHOOTABLE | FL_SOLID; + new->flags2 = FL2_BFG_SHOOTABLE; + new->speed = SPDPATROL; + if (gamestate.mapon==9) + new->hitpoints = starthitpoints[gamestate.difficulty][which]*15; + ammo = 25; + break; + + case en_electro_alien: + SpawnNewObj (tilex,tiley,&s_electro_appear1); + new->flags = FL_SHOOTABLE | FL_SOLID | FL_PROJ_TRANSPARENT; + new->speed = SPDPATROL; + new->lighting = NO_SHADING; // no shading + break; + + case en_liquid: + SpawnNewObj(tilex,tiley,&s_liquid_wait); + new->flags = FL_OFFSET_STATES|FL_PROJ_TRANSPARENT; + new->flags2 = FL2_BFG_SHOOTABLE; + new->speed = SPDPATROL*3; + break; + + case en_rentacop: + SpawnNewObj (tilex,tiley,&s_rent_stand); + new->flags = FL_SHOOTABLE | FL_SOLID; + new->flags2 = FL2_BFG_SHOOTABLE; + new->speed = SPDPATROL; + break; + + case en_gen_scientist: + SpawnNewObj (tilex,tiley,&s_ofcstand); + new->flags = FL_SHOOTABLE | FL_SOLID | FL_FRIENDLY | FL_RANDOM_TURN; + new->flags2 = FL2_BFG_SHOOTABLE; + if (US_RndT()&1) + new->flags |= FL_INFORMANT; + new->speed = SPDPATROL; + break; + + case en_swat: + SpawnNewObj (tilex,tiley,&s_swatstand); + new->flags = FL_SHOOTABLE | FL_SOLID; + new->flags2 = FL2_BFG_SHOOTABLE; + new->speed = SPDPATROL; + ammo = 30; + if (scan_value == 0xffff) + new->temp1 = US_RndT()&1; + else + new->temp1 = scan_value; + break; + + case en_proguard: + SpawnNewObj (tilex,tiley,&s_prostand); + new->flags = FL_SHOOTABLE | FL_SOLID; + new->flags2 = FL2_BFG_SHOOTABLE; + new->speed = SPDPATROL; + ammo = 25; + break; + + case en_hang_terrot: + SpawnNewObj (tilex,tiley,&s_terrot_wait); + new->flags = FL_SHOOTABLE|FL_NONMARK|FL_NEVERMARK; + new->speed = SPDPATROL; + break; + + case en_floatingbomb: + SpawnNewObj (tilex,tiley,&s_scout_stand); + new->speed = SPDPATROL; + new->temp1 = SPR_FSCOUT_W1_1; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + new->flags = FL_SHOOTABLE | FL_SOLID | FL_OFFSET_STATES|FL_FAKE_STATIC; + break; + + case en_volatiletransport: + SpawnNewObj (tilex,tiley,&s_scout_stand); + new->speed = SPDPATROL; + new->temp1 = SPR_GSCOUT_W1_1; + new->flags = FL_SHOOTABLE | FL_SOLID | FL_OFFSET_STATES | FL_STATIONARY|FL_FAKE_STATIC; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_steamgrate: + SpawnNewObj(tilex,tiley,&s_steamgrate); + new->flags = FL_OFFSET_STATES; + new->temp1 = SPR_GRATE; + new->temp2 = 60*4; + break; + + case en_steampipe: + nevermark = true; + SpawnNewObj(tilex,tiley,&s_steamgrate); + nevermark = false; + new->flags = FL_OFFSET_STATES|FL_NONMARK|FL_NEVERMARK; + new->temp1 = SPR_STEAM_PIPE; + new->temp2 = 60*4; + break; + } + + CheckForSpecialTile(new,tilex,tiley); + + new->ammo = ammo; + new->obclass = rentacopobj+which; + new->hitpoints += starthitpoints[gamestate.difficulty][which]; + new->dir = dir<<1; + + if (new->flags & FL_INFORMANT) + { + new->hitpoints=1; + new->ammo=0; + new->flags |= FL_HAS_AMMO | FL_HAS_TOKENS; + new->s_tilex = new->s_tiley = 0xff; + } +} + + +//--------------------------------------------------------------------------- +// CheckForSpecialTile() - Adds special attributes to actor if standing on +// special tiles. +//--------------------------------------------------------------------------- +void CheckForSpecialTile(objtype *obj, unsigned tilex, unsigned tiley) +{ + unsigned far *map,far *map1; + objtype *old_new; + boolean getarea = false; + + // + // Only shootables can use special tiles... + // + // (This also tests to make sure that the plasma_detonatorobj & + // plasma_detonator_reservedobj will not enter this function.) + // + + if (!(obj->flags & FL_SHOOTABLE)) + return; + + // + // Check and handle special tiles... Only one per actor... now! + // + + map = mapsegs[0]+farmapylookup[tiley]+tilex; + + switch (*map) + { + case CLOAK_AMBUSH_TILE: + obj->flags2 |= FL2_CLOAKED; + + case AMBUSHTILE: + obj->flags |= FL_AMBUSH|FL_SHOOTABLE|FL_SOLID; + getarea = true; + break; + + case DETONATOR_TILE: + old_new =new; + SpawnHiddenOfs(en_plasma_detonator_reserve, tilex, tiley); + new = old_new; + obj->flags &= ~FL_INFORMANT; + case RKEY_TILE: + case YKEY_TILE: + case BKEY_TILE: + case BFG_TILE: + case ION_TILE: + ReserveStatic(); + obj->flags2 |= SpecialSpawnFlags[(*map)-RKEY_TILE]; + getarea = true; + break; + + case CLOAK_TILE: + obj->flags2 |= FL2_CLOAKED; + getarea = true; + break; + + case LINC_TILE: + obj->flags2 |= FL2_LINC; + obj->flags &= ~FL_INFORMANT; // Make sure informants dont have links + getarea = true; + map1 = mapsegs[1]+farmapylookup[tiley]+tilex+1; + obj->linc = *map1; + *map1 = 0; + break; + } + + // + // Init areanumbers and tilemaps... + // + + if (getarea) + { + tilemap[tilex][tiley] = 0; + *map = obj->areanumber = GetAreaNumber(tilex,tiley); + + #if IN_DEVELOPMENT + if (obj->areanumber >= NUMAREAS) + Quit("Actor Spawned on wall at %d %d",tilex,tiley); + #endif + } +} + +/* +=============== += += SpawnPatrol += +=============== +*/ + +void SpawnPatrol (enemy_t which, int tilex, int tiley, int dir) +{ + short ammo = 8; + +#if IN_DEVELOPMENT + unsigned oldx,oldy; +#endif + + switch (which) + { + case en_blake: + SpawnNewObj (tilex,tiley,&s_blake1); + new->speed = SPDPATROL*2; + break; + + case en_rentacop: + SpawnNewObj (tilex,tiley,&s_rent_path1); + new->flags2 = FL2_BFG_SHOOTABLE; + new->speed = SPDPATROL; + break; + + case en_gen_scientist: + SpawnNewObj (tilex,tiley,&s_ofcpath1); + new->flags = FL_FRIENDLY | FL_RANDOM_TURN; + new->flags2 = FL2_BFG_SHOOTABLE; + if (US_RndT()&1) + new->flags |= FL_INFORMANT; + new->speed = SPDPATROL; + break; + + case en_proguard: + SpawnNewObj (tilex,tiley,&s_propath1); + new->speed = SPDPATROL; + new->flags2 = FL2_BFG_SHOOTABLE; + ammo = 25; + break; + + case en_swat: + SpawnNewObj (tilex,tiley,&s_swatpath1); + new->speed = SPDPATROL; + ammo = 30; + if (scan_value == 0xffff) + new->temp1 = US_RndT()&1; + else + new->temp1 = scan_value; + new->flags2 = FL2_BFG_SHOOTABLE; + break; + + case en_floatingbomb: + SpawnNewObj (tilex,tiley,&s_scout_path1); + new->speed = SPDPATROL; + new->temp1 = SPR_FSCOUT_W1_1; + new->flags = FL_OFFSET_STATES; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + + case en_volatiletransport: + SpawnNewObj (tilex,tiley,&s_scout_path1); + new->speed = SPDPATROL; + new->temp1 = SPR_GSCOUT_W1_1; + new->flags = FL_OFFSET_STATES; + new->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + break; + } + + new->ammo = ammo; + new->obclass = rentacopobj+which; + new->dir = dir<<1; + new->hitpoints = starthitpoints[gamestate.difficulty][which]; + new->distance = 0; + if (new->obclass!=blakeobj) + new->flags |= FL_SHOOTABLE|FL_SOLID; + new->active = true; + if (new->flags & FL_INFORMANT) + { + new->hitpoints=1; + new->ammo = 0; + new->flags |= FL_HAS_AMMO | FL_HAS_TOKENS; + new->s_tilex = new->s_tiley = 0xff; + } + + CheckForSpecialTile(new,tilex,tiley); + + actorat[new->tilex][new->tiley] = NULL; // don't use original spot + +#if IN_DEVELOPMENT + oldx = new->tilex; + oldy = new->tiley; +#endif + +#if 1 + TryWalk(new,true); +#else + switch (dir) + { + case 0: + new->tilex++; + break; + + case 1: + new->tiley--; + break; + + case 2: + new->tilex--; + break; + + case 3: + new->tiley++; + break; + } +#endif + +#if IN_DEVELOPMENT + if (new->obclass!=blakeobj) + { + if ((unsigned)actorat[new->tilex][new->tiley] == 1) + Quit("Actor spawned toward a solid static at %d %d",oldx,oldy); + + if (GetAreaNumber(new->tilex,new->tiley) >= NUMAREAS) + Quit("Actor spawned toward wall at %d %d",oldx,oldy); + } +#endif + + actorat[new->tilex][new->tiley] = new; +} + + + + +/* +================== += += A_DeathScream += +================== +*/ + +// 3D_ACT2.C + +void A_DeathScream (objtype *ob) +{ + switch (ob->obclass) + { + + case swatobj: + { + #if (GAME_VERSION != SHAREWARE_VERSION) + int sounds[2]= + { + SWATDIESND, + SWATDEATH2SND + }; + PlaySoundLocActor(sounds[US_RndT()&1],ob); + #else + PlaySoundLocActor(SWATDIESND,ob); + #endif + } + break; + + + + case rentacopobj: + { +#if (GAME_VERSION != SHAREWARE_VERSION) + int sounds[2]= + { + RENTDEATH1SND, + RENTDEATH2SND, + }; + PlaySoundLocActor(sounds[US_RndT()&1],ob); +#else + PlaySoundLocActor(RENTDEATH1SND,ob); +#endif + } + break; + + case mutant_human1obj: + case hang_terrotobj: + case floatingbombobj: + case volatiletransportobj: + case explosionobj: + case gr_explosionobj: + case bfg_explosionobj: + case pd_explosionobj: + case doorexplodeobj: + { + +#if GAME_VERSION != SHAREWARE_VERSION + int sounds[] = {EXPLODE1SND, + EXPLODE2SND}; + PlaySoundLocActor(sounds[US_RndT()&1],ob); +#else + PlaySoundLocActor(EXPLODE1SND,ob); +#endif + + } + break; + + case rotating_cubeobj: + SD_PlaySound(VITAL_GONESND); + break; + +#if (GAME_VERSION != SHAREWARE_VERSION) + + case gen_scientistobj: + if (ob->flags & FL_INFORMANT) + { + int sounds[3]= + { + INFORMANTDEATHSND, + INFORMDEATH2SND, + INFORMDEATH3SND + }; + PlaySoundLocActor(sounds[US_RndT()%3],ob); + } + else + { + int sounds[3]= + { + SCIENTISTDEATHSND, + SCIDEATH2SND, + SCIDEATH3SND + }; + PlaySoundLocActor(sounds[US_RndT()%3],ob); + } + break; + +#else + + case gen_scientistobj: + if (ob->flags & FL_INFORMANT) + PlaySoundLocActor(INFORMANTDEATHSND,ob); + else + PlaySoundLocActor(SCIENTISTDEATHSND,ob); + break; + +#endif + + + case breather_beastobj: + case cyborg_warriorobj: + case genetic_guardobj: + case acid_dragonobj: + case podobj: + PlaySoundLocActor(PODDEATHSND,ob); + break; + + case liquidobj: + PlaySoundLocActor(LIQUIDDIESND,ob); + break; + + case proguardobj: + { +#if (GAME_VERSION != SHAREWARE_VERSION) + int sounds[3]= + { + PROGUARDDEATHSND, + PRODEATH2SND, + }; + PlaySoundLocActor(sounds[US_RndT()&1],ob); +#else + PlaySoundLocActor(PROGUARDDEATHSND,ob); +#endif + } + break; + + + case final_boss1obj: + case spider_mutantobj: + PlaySoundLocActor(BLUEBOYDEATHSND,ob); + break; + + case mech_guardianobj: + case final_boss3obj: + case mutant_human2obj: + PlaySoundLocActor(DOGBOYDEATHSND,ob); + break; + + case reptilian_warriorobj: + case scan_alienobj: + PlaySoundLocActor(SCANDEATHSND,ob); + break; + + case lcan_alienobj: + case final_boss4obj: + PlaySoundLocActor(LCANDEATHSND,ob); + break; + + case gurneyobj: + PlaySoundLocActor(GURNEYDEATHSND,ob); + break; + + case lcan_wait_alienobj: + PlaySoundLocActor(LCANBREAKSND,ob); + break; + + case scan_wait_alienobj: + PlaySoundLocActor(SCANBREAKSND,ob); + break; + + } +} + + + + +//============================================================================ +// +// DROP +// +//============================================================================ + + + +//--------------------------------------------------------------------------- +// DropCargo() +//--------------------------------------------------------------------------- +void DropCargo(objtype *obj) +{ + // + // Keep seperate... May later have MULTI "cargo's" + // + + if (obj->flags2 & FL2_DROP_RKEY) + PlaceReservedItemNearTile(bo_red_key, obj->tilex, obj->tiley); + + if (obj->flags2 & FL2_DROP_YKEY) + PlaceReservedItemNearTile(bo_yellow_key, obj->tilex, obj->tiley); + + if (obj->flags2 & FL2_DROP_BKEY) + PlaceReservedItemNearTile(bo_blue_key, obj->tilex, obj->tiley); + + if (obj->flags2 & FL2_DROP_BFG) + PlaceReservedItemNearTile(bo_bfg_cannon, obj->tilex, obj->tiley); + + if (obj->flags2 & FL2_DROP_ION) + PlaceReservedItemNearTile(bo_ion_cannon, obj->tilex, obj->tiley); + + if (obj->flags2 & FL2_DROP_DETONATOR) + PlaceReservedItemNearTile(bo_plasma_detonator, obj->tilex, obj->tiley); + + if ((obj->flags2 & FL2_LINC) && obj->linc) + { + OperateSmartSwitch(obj->linc>>8,obj->linc&255,ST_TURN_OFF,true); + } +} + + + +/* +============================================================================ + + STAND + +============================================================================ +*/ + + +/* +=============== += += T_Stand += +=============== +*/ + +void T_Stand (objtype *ob) +{ + SightPlayer (ob); +} + + +/* +============================================================================ + + CHASE + +============================================================================ +*/ + + +/* +================= += += T_Chase += +================= +*/ + +// #define DODGE_N_CHASE + +void T_Chase (objtype *ob) +{ + long move; + int dx,dy,dist,chance; +#ifdef DODGE_N_CHASE + boolean dodge; +#endif + boolean nearattack=false; + + ob->flags &= ~FL_LOCKED_STATE; + + if (ob->flags & (FL_STATIONARY|FL_BARRIER_DAMAGE)) + return; + +// if ((ob->flags & (FL_SOLID|FL_SHOOTABLE)) != (FL_SOLID|FL_SHOOTABLE)) +// ob->flags |= FL_SOLID|FL_SHOOTABLE; + + if (ob->ammo) + { +#ifdef DODGE_N_CHASE + dodge = false; +#endif + if (CheckLine(ob,player) && (!PlayerInvisable)) // 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; + + if (dist==1 && ob->distance<0x4000) + nearattack=true; + + // Time to toggle SHOOTMODE? + // + switch (ob->obclass) + { + case mutant_human1obj: + case genetic_guardobj: + case gurneyobj: + case podobj: + case mutant_human2obj: + case scan_alienobj: + case lcan_alienobj: + case spider_mutantobj: + case breather_beastobj: + case cyborg_warriorobj: + case reptilian_warriorobj: + case acid_dragonobj: + case mech_guardianobj: + case gold_morphobj: + case final_boss1obj: + case final_boss2obj: + case final_boss3obj: + case final_boss4obj: + + // Check for mode change + // + if (ob->ammo > tics) + ob->ammo -= tics; + else + { + ChangeShootMode(ob); + + // Move half as far when doing near attacks... + // + if (!(ob->flags & FL_SHOOTMODE)) + ob->ammo >>= 1; + } + break; + } + + if (nearattack) + { + // Always shoot when in SHOOTMODE -- Never shoot when not! + // + if (ob->flags & FL_SHOOTMODE) + chance=300; + else + chance=0; + } + else + { + switch (ob->obclass) + { + case mutant_human1obj: + case genetic_guardobj: + case gurneyobj: + case podobj: + case mutant_human2obj: + case scan_alienobj: + case lcan_alienobj: + case spider_mutantobj: + case breather_beastobj: + case cyborg_warriorobj: + case reptilian_warriorobj: + case acid_dragonobj: + case mech_guardianobj: + case gold_morphobj: + case final_boss1obj: + case final_boss2obj: + case final_boss3obj: + case final_boss4obj: + + // Always shoot when in SHOOTMODE -- Never shoot when not! + // + if (ob->flags & FL_SHOOTMODE) + chance=300; + else + chance=0; + break; + + default: + chance = (tics<<4)/dist; + break; + } + } + + if ((US_RndT()ammo) && (!(ob->flags&FL_INTERROGATED))) + { + DoAttack(ob); + return; + } + +#ifdef DODGE_N_CHASE + dodge = true; +#endif + } + else + ChangeShootMode(ob); + } + + if (ob->dir == nodir) + { +#ifdef DODGE_N_CHASE + if (dodge) + SelectDodgeDir(ob); + else + SelectChaseDir(ob); +#else + + switch (ob->obclass) + { + case floatingbombobj: + SelectChaseDir(ob); + break; + + default: + SelectDodgeDir(ob); + break; + } +#endif + + 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; + +#ifdef DODGE_N_CHASE + if (dodge) + SelectDodgeDir (ob); + else + SelectChaseDir (ob); +#else + + switch (ob->obclass) + { + case floatingbombobj: + SelectChaseDir(ob); + break; + + default: + SelectDodgeDir(ob); + break; + } + +#endif + + if (ob->dir == nodir) + return; // object is blocked in + } +} + +//-------------------------------------------------------------------------- +// ChangeShootMode() +//-------------------------------------------------------------------------- +void ChangeShootMode(objtype *ob) +{ + if (ob->flags & FL_SHOOTMODE) + { + ob->flags &= ~FL_SHOOTMODE; + ob->ammo = 60+(US_RndT()%60); + } + else + { + ob->flags |= FL_SHOOTMODE; + ob->ammo = 1+(US_RndT()%2); + if (ob->obclass == gold_morphobj) + ob->ammo += 3+(US_RndT()%5); + } +} + +//-------------------------------------------------------------------------- +// DoAttack() +//-------------------------------------------------------------------------- +void DoAttack(objtype *ob) +{ + int dx,dy,dist; + + dx = abs(ob->tilex - player->tilex); + dy = abs(ob->tiley - player->tiley); + dist = dx>dy ? dx : dy; + if (!dist) + dist = 1; + + switch (ob->obclass) + { + case floatingbombobj: + if (dist <= 1) + { + ob->flags &= ~(FL_SHOOTABLE | FL_SOLID); + ob->flags |= FL_NONMARK | FL_DEADGUY; + KillActor(ob); + return; + } + break; + + case goldsternobj: + NewState (ob,&s_goldshoot1); + break; + + case gold_morphobj: + NewState (ob,&s_mgold_shoot1); + break; + + case rentacopobj: + NewState (ob,&s_rent_shoot1); + break; + + case gen_scientistobj: + NewState (ob,&s_ofcshoot1); + break; + + case swatobj: + NewState (ob,&s_swatshoot1); + break; + + case liquidobj: + if ((dx > 2) || (dy > 2) && US_RndT() < 30) + { + ob->flags &= ~(FL_OFFSET_STATES); + ob->flags |= FL_SOLID; + NewState(ob,&s_liquid_rise1); + } + break; + + case proguardobj: + NewState (ob,&s_proshoot1); + break; + + case electroobj: + NewState(ob,&s_electro_shoot1); + break; + + case podobj: + if (dist > CLOSE_RANGE) + NewState(ob,&s_ofs_pod_spit1); + else + NewState(ob,&s_ofs_pod_attack1); + break; + + case spider_mutantobj: + case acid_dragonobj: + case mech_guardianobj: + case breather_beastobj: + case cyborg_warriorobj: + case reptilian_warriorobj: + case gurneyobj: + case mutant_human1obj: + case final_boss1obj: + case final_boss2obj: + case final_boss3obj: + case final_boss4obj: + NewState(ob,&s_ofs_shoot1); + break; + + + case genetic_guardobj: + case mutant_human2obj: + case lcan_alienobj: + case scan_alienobj: + if (dist > CLOSE_RANGE) + NewState(ob,&s_ofs_spit1); + else + NewState (ob,&s_ofs_attack1); + break; + + } +} + +/* +============================================================================ + + PATH + +============================================================================ +*/ + + +/* +=============== += += SelectPathDir += +=============== +*/ + + +dirtype SelectPathDir (objtype *ob) +{ + boolean CantWalk=false,RandomTurn=false; + unsigned spot; + +// Look for directional arrows! +// + spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS; + if (spot<8) + ob->dir = spot; + +// Reset move distance and try to walk/turn. +// + ob->distance = TILEGLOBAL; + if (ob->flags & FL_RANDOM_TURN) + RandomTurn = US_RndT() > 180; + else + RandomTurn = false; + CantWalk = !TryWalk(ob,false); + +// Handle random turns and hitting walls +// + if (RandomTurn || CantWalk) + { + // Directional arrows have priority! + // + if ((!CantWalk) && (spot<8)) + goto exit_func; + + // Either: path is blocked OR actor is randomly turning. + // + if (ob->trydir == nodir) + ob->trydir |= US_RndT()&128; + else + ob->dir = ob->trydir&127; + + // Turn this actor + // + if (ob->trydir & 128) + { + ob->dir--; // turn clockwise + if (ob->dir < east) + ob->dir = nodir-1; + } + else + { + ob->dir++; // turn counter-clockwise + if (ob->dir >= nodir) + ob->dir = east; + } + ob->trydir = (ob->trydir&128) | ob->dir; + + // Walk into new direction? + // + if (!TryWalk(ob,false)) + ob->dir = nodir; + } + +exit_func:; + if (ob->dir != nodir) + { + TryWalk(ob,true); + ob->trydir = nodir; + } + + return(ob->dir); +} + +/* +=============== += += T_Path += +=============== +*/ + + +void T_Path (objtype *ob) +{ + long move; + long deltax,deltay,size; + + if (ob->flags & FL_STATIONARY) + return; + + switch (ob->obclass) + { + case volatiletransportobj: + break; + +#if 0 + case gen_scientistobj: + if (ob->flags & FL_INFORMANT) // Only informants can get scared. + { + if (ob->flags2 & FL2_SCARED) + { + // + // Count down our timer of "Running Scared" + // + if (ob->temp3 > tics) + ob->temp3 -= tics; + else + ob->flags2 &= ~FL2_SCARED; + } + else + if (madenoise && areabyplayer[ob->areanumber]) + { + ob->flags2 |= FL2_SCARED; + ob->temp3 = 60*2; + } + } + break; +#endif + + default: + if ((!(ob->flags & FL_FRIENDLY)) || madenoise) + if (SightPlayer(ob)) + return; + +#if LOOK_FOR_DEAD_GUYS + if (LookForDeadGuys(ob)) + return; +#endif + break; + } + + if (ob->dir == nodir) + if (SelectPathDir(ob) == nodir) + return; + + move = ob->speed*tics; + while (move) + { + if (ob->distance < 0) + { + // Actor waiting for 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) + ACT2_ERROR(T_PATH_HIT_WALL); +// Quit("T_Path hit a wall at %u,%u, dir %u",ob->tilex,ob->tiley,ob->dir); + + ob->x = ((long)ob->tilex<y = ((long)ob->tiley<distance; + + if (SelectPathDir(ob) == nodir) + return; + } +} + +/* +============================================================================= + + FIGHT + +============================================================================= +*/ + +int morph_angle_adj=0; + +void T_Shoot (objtype *ob) +{ + int dx,dy,dist; + int hitchance,damage; + int chance; + + switch (ob->obclass) + { + case SMART_ACTORS: + if (!ob->ammo) + return; + } + + switch (ob->obclass) + { + case electroobj: + case mutant_human2obj: + SpawnProjectile(ob,electroshotobj); + break; + + case liquidobj: + SpawnProjectile(ob,liquidshotobj); + break; + + case lcan_alienobj: + SpawnProjectile(ob,lcanshotobj); + break; + + case podobj: + SpawnProjectile(ob,podshotobj); + break; + + case scan_alienobj: + SpawnProjectile(ob,scanshotobj); + break; + + case gold_morphobj: + SpawnProjectile(ob,goldmorphshotobj); + + if (ob->hitpoints < 500) + chance = 255 / 2; + else + chance = 255 / 4; + + if (US_RndT() < chance) + { + morph_angle_adj = 24; + SpawnProjectile(ob,goldmorphshotobj); + morph_angle_adj = -24; + SpawnProjectile(ob,goldmorphshotobj); + morph_angle_adj = 16; + SpawnProjectile(ob,goldmorphshotobj); + morph_angle_adj = -16; + SpawnProjectile(ob,goldmorphshotobj); + morph_angle_adj = 8; + SpawnProjectile(ob,goldmorphshotobj); + morph_angle_adj = -8; + SpawnProjectile(ob,goldmorphshotobj); + morph_angle_adj = 0; + } + break; + + case spider_mutantobj: + case acid_dragonobj: + SpawnProjectile(ob,spider_mutantshotobj+(ob->obclass-spider_mutantobj)); +// SpawnProjectile(ob,spider_mutantshotobj+(ob->obclass-spider_mutantobj)); + break; + + case final_boss2obj: + SpawnProjectile(ob,final_boss2shotobj); + break; + + case final_boss4obj: + SpawnProjectile(ob,final_boss4shotobj); + break; + + + default: + hitchance = 128; + + ob->lighting=-10; + + if (!areabyplayer[ob->areanumber]) + return; + + if (!CheckLine (ob,player)) // 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 == swatobj) + if (dist) + 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 proguardobj: + case swatobj: + PlaySoundLocActor(ATKBURSTRIFLESND,ob); + break; + + default: + PlaySoundLocActor(ATKCHARGEDSND,ob); + break; + } + +#ifdef LIMITED_AMMO + switch (ob->obclass) + { + case SMART_ACTORS: + ob->ammo--; + CheckRunChase(ob); + break; + } +#endif + + MakeAlertNoise(ob); + break; + } + + switch (ob->obclass) + { + case proguardobj: + case swatobj: + break; + + default: + ob->flags &= ~FL_LOCKED_STATE; + break; + } +} + + +//---------------------------------------------------------------------- +// T_Shade() +//---------------------------------------------------------------------- +void T_Shade(objtype *obj) +{ + + switch (obj->obclass) + { + case final_boss2obj: + break; + + default: + obj->lighting = 0; + break; + } +} + +//---------------------------------------------------------------------- +// T_Hit +//---------------------------------------------------------------------- +void T_Hit(objtype *ob) +{ + long dx,dy; + int hitchance,damage; + + + switch (ob->obclass) + { + case scan_alienobj: + case lcan_alienobj: + case podobj: + hitchance = 220; // Higher - Better Chance (255 max!) + damage = (US_RndT()>>3)|1; + PlaySoundLocActor(CLAWATTACKSND,ob); + break; + + case genetic_guardobj: + case mutant_human2obj: + hitchance = 220; // Higher - Better Chance (255 max!) + damage = (US_RndT()>>3)|1; + PlaySoundLocActor(PUNCHATTACKSND,ob); + break; + + default: + hitchance = 200; // Higher - Better Chance (255 max!) + damage = US_RndT()>>4; + break; + + } + + MakeAlertNoise(ob); + + 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()speed = ALIENSPEED<<2; + obj->ammo = ALIENAMMOINIT; + obj->flags |= FL_PROJ_TRANSPARENT|FL_NO_SLIDE|FL_SHOOTABLE|FL_SOLID; + obj->flags2 = FL2_BFGSHOT_SOLID|FL2_BFG_SHOOTABLE; + obj->hitpoints = starthitpoints[gamestate.difficulty][en_gold_morph]; + obj->obclass = gold_morphobj; + + noShots = false; +} + +//-------------------------------------------------------------------------- +// A_Laugh() - Plays a Goldstern Laugh Sound +//-------------------------------------------------------------------------- +void A_Laugh(objtype *obj) +{ + PlaySoundLocActor(GOLDSTERNLAUGHSND,obj); +} + +//-------------------------------------------------------------------------- +// A_WarpIn() - Plays a warp Sound +//-------------------------------------------------------------------------- +void A_WarpIn(objtype *obj) +{ + PlaySoundLocActor(WARPINSND,obj); +} +//-------------------------------------------------------------------------- +// A_WarpOut() - Plays a warp Sound +//-------------------------------------------------------------------------- +void A_WarpOut(objtype *obj) +{ + PlaySoundLocActor(WARPOUTSND,obj); +} + +//-------------------------------------------------------------------------- +// A_Beep() - Plays a Beep Sound +//-------------------------------------------------------------------------- +void A_Beep(objtype *obj) +{ + PlaySoundLocActor(ELEV_BUTTONSND,obj); +} + + +//-------------------------------------------------------------------------- +// InitGoldsternInfo() +//-------------------------------------------------------------------------- +void InitGoldsternInfo(void) +{ + _fmemset(&GoldsternInfo,0,sizeof(GoldsternInfo)); + GoldsternInfo.LastIndex = GOLDIE_MAX_SPAWNS; +} + + + + +//=========================================================================== +// +// +// RED FLASHING SECURITY LAMPS +// +// +//=========================================================================== + + + +void T_FlipShape (objtype *obj) +{ + if (obj->flags & FL_ALERTED) + { + if (obj->temp1 ^= 1) + obj->lighting = LAMP_ON_SHADING; + else + obj->lighting = 0; + } +} + +statetype s_security_light = {false,SPR_SECURITY_NORMAL,20,T_Security,T_FlipShape,&s_security_light}; + + +//--------------------------------------------------------------------------- +// T_Security() +//--------------------------------------------------------------------------- +void T_Security(objtype *obj) +{ + + if (!(obj->flags & FL_ALERTED)) + if(alerted && areabyplayer[obj->areanumber]) + obj->flags |= FL_ALERTED; +} + + + + + + +//========================================================================== +// +// +// GROUND & FLOATING SCOUT ROUTINES +// +// +//========================================================================== + +void A_Scout_Alert(objtype *obj); +void T_PainThink(objtype *obj); + +extern statetype s_scout_path1; +extern statetype s_scout_path1s; +extern statetype s_scout_path2; +extern statetype s_scout_path3; +extern statetype s_scout_path3s; +extern statetype s_scout_path4; + +extern statetype s_scout_pain; +extern statetype s_scout_pain2; + +extern statetype s_scout_run; +extern statetype s_scout_run1s; +extern statetype s_scout_run2; +extern statetype s_scout_run3; +extern statetype s_scout_run3s; +extern statetype s_scout_run4; + +extern statetype s_scout_dead; + +statetype s_scout_stand = {true,SPR_GSCOUT_W1_1-SPR_GSCOUT_W1_1,20,T_Stand,NULL,&s_scout_stand}; + +statetype s_scout_path1 = {true,SPR_GSCOUT_W1_1-SPR_GSCOUT_W1_1,15,T_Path,NULL,&s_scout_path1}; +statetype s_scout_path2 = {true,SPR_GSCOUT_W2_1-SPR_GSCOUT_W1_1,15,T_Path,NULL,&s_scout_path2}; +statetype s_scout_path3 = {true,SPR_GSCOUT_W3_1-SPR_GSCOUT_W1_1,15,T_Path,NULL,&s_scout_path3}; +statetype s_scout_path4 = {true,SPR_GSCOUT_W4_1-SPR_GSCOUT_W1_1,15,T_Path,NULL,&s_scout_path4}; + +statetype s_scout_run = {true,SPR_GSCOUT_W1_1-SPR_GSCOUT_W1_1,10,T_Chase,NULL,&s_scout_run}; +statetype s_scout_run2 = {true,SPR_GSCOUT_W2_1-SPR_GSCOUT_W1_1,10,T_Chase,NULL,&s_scout_run2}; +statetype s_scout_run3 = {true,SPR_GSCOUT_W3_1-SPR_GSCOUT_W1_1,10,T_Chase,NULL,&s_scout_run3}; +statetype s_scout_run4 = {true,SPR_GSCOUT_W4_1-SPR_GSCOUT_W1_1,10,T_Chase,NULL,&s_scout_run4}; + +statetype s_scout_dead = {false,SPR_GSCOUT_W1_1-SPR_GSCOUT_W1_1,20,NULL,NULL,&s_scout_dead}; + + +//--------------------------------------------------------------------------- +// T_Scout_Alert() +//--------------------------------------------------------------------------- +void A_Scout_Alert(objtype *obj) +{ + PlaySoundLocActor(SCOUT_ALERTSND,obj); + MakeAlertNoise(obj); +} + + +//--------------------------------------------------------------------------- +// T_ExplodeScout() +//--------------------------------------------------------------------------- +void T_ExplodeScout(objtype *obj) +{ + SpawnExplosion(obj->x+0x4000+(US_RndT()<<5),obj->y+0x4000+(US_RndT()<<5)); + SpawnExplosion(obj->x-0x4000-(US_RndT()<<5),obj->y+0x4000+(US_RndT()<<5)); + SpawnExplosion(obj->x-0x4000-(US_RndT()<<5),obj->y-0x4000-(US_RndT()<<5)); + SpawnExplosion(obj->x+0x4000+(US_RndT()<<5),obj->y-0x4000-(US_RndT()<<5)); +} + +//--------------------------------------------------------------------------- +// T_ExplodeDamage() +//--------------------------------------------------------------------------- +void T_ExplodeDamage(objtype *obj) +{ + ExplodeRadius(obj,EXPLODE_DAMAGE, true); +} + +#if GAME_VERSION != SHAREWARE_VERSION +//--------------------------------------------------------------------------- +// T_PainThink() +//--------------------------------------------------------------------------- +void T_PainThink(objtype *obj) +{ + int full_hp = starthitpoints[gamestate.difficulty][obj->obclass-rentacopobj]; + + if (obj->hitpoints > (full_hp>>1)+(full_hp>>2)) + { + // Orginal HitPoints + // + + switch (obj->obclass) + { + case floatingbombobj: + NewState(obj,&s_scout_run); + break; + + case volatiletransportobj: + NewState(obj,&s_scout_path1); + break; + } + } + else + if (obj->hitpoints > (full_hp>>1)) + { + // 3/4 Orginal HitPoints + // + + switch (obj->obclass) + { + case floatingbombobj: + NewState(obj,&s_scout_run2); + break; + + case volatiletransportobj: + NewState(obj,&s_scout_path2); + break; + } + } + else + if (obj->hitpoints > (full_hp>>2)) + { + // 1/2 Orginal HitPoints + // + + switch (obj->obclass) + { + case floatingbombobj: + NewState(obj,&s_scout_run3); + break; + + case volatiletransportobj: + NewState(obj,&s_scout_path3); + break; + } + } + else + { + // 1/4 Orginal HitPoints + // + + switch (obj->obclass) + { + case floatingbombobj: + NewState(obj,&s_scout_run4); + break; + + case volatiletransportobj: + NewState(obj,&s_scout_path4); + break; + } + } +} +#endif + +//========================================================================== +// +// EXPLOSION STUFF +// +//========================================================================== + +//------------------------------------------------------------------------- +// SpawnCusExplosion() - Spawns an explosion at a given x & y. +//------------------------------------------------------------------------- +void SpawnCusExplosion(fixed x, fixed y, unsigned StartFrame, unsigned NumFrames, unsigned Delay, unsigned Class) +{ + int tilex=x>>TILESHIFT, tiley=y>>TILESHIFT; + + usedummy = nevermark = true; + SpawnNewObj(tilex,tiley,&s_ofs_smart_anim); + usedummy = nevermark = false; + new->x = x; + new->y = y; + new->flags = FL_OFFSET_STATES|FL_NONMARK|FL_NEVERMARK; + new->obclass = Class; + new->lighting = NO_SHADING; + InitAnim(new,StartFrame,0,NumFrames,at_ONCE,ad_FWD,(US_RndT()&0x7),Delay); + A_DeathScream(new); + MakeAlertNoise(new); +} + + +//--------------------------------------------------------------------------- +// T_SpawnExplosion() +//--------------------------------------------------------------------------- +void T_SpawnExplosion(objtype *obj) +{ + SpawnCusExplosion(obj->x,obj->y,SPR_EXPLOSION_1,4,4,explosionobj); +} + +//========================================================================== +// +// STEAM OBJECT STUFF +// +//========================================================================== +void T_SteamObj(objtype *obj); + +extern statetype s_steamrelease1; +extern statetype s_steamrelease2; +extern statetype s_steamrelease3; +extern statetype s_steamrelease4; +extern statetype s_steamrelease5; +extern statetype s_steamrelease6; + +statetype s_steamgrate = {false,0,1,T_SteamObj,NULL,&s_steamgrate}; + +statetype s_steamrelease1 = {false,1,1,NULL,A_DeathScream,&s_steamrelease2}; +statetype s_steamrelease2 = {false,2,14,NULL,NULL,&s_steamrelease3}; +statetype s_steamrelease3 = {false,3,14,NULL,NULL,&s_steamrelease4}; +statetype s_steamrelease4 = {false,2,14,NULL,NULL,&s_steamrelease5}; +statetype s_steamrelease5 = {false,3,14,NULL,NULL,&s_steamrelease6}; +statetype s_steamrelease6 = {false,4,16,NULL,NULL,&s_steamgrate}; + +//------------------------------------------------------------------------- +// T_SteamObj() +//------------------------------------------------------------------------- +void T_SteamObj(objtype *obj) +{ + if (obj->flags & FL_VISABLE) + { + if ((obj->temp2-=tics) <= 0) + { + NewState(obj,&s_steamrelease1); + obj->temp2 = US_RndT()<<3; // Up to 34 Seconds + } + else + obj->temp2 -= tics; + } +} + + + + + +//=========================================================================== + +/* +=============== += += 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 && checktilex != ob->tilex) || (player->tiley != ob->tiley)) && // Can you see + CheckView(ob,player) && (!PlayerInvisable)) + { + dx = ob->tilex - player->tilex; + dx = LABS(dx); + dy = ob->tiley - player->tiley; + dy = LABS(dy); + + if (dy < MAX_VIS_DIST && dx < MAX_VIS_DIST) + { + dist = dx>dy ? dx : dy; + + if (!dist || (dist==1 && ob->distance<0x4000)) + chance = 300; + else + chance = US_RndT()/dist; + + if (US_RndT()flags & FL_STATIONARY)) + { + if (target_found) + { + NewState(ob,&s_terrot_found); + } + else + if ((ob->temp2-=tics)<=0) + { + NewState(ob,&s_terrot_seek1); + + ob->dir++; + ob->temp2 = SEEK_TURN_DELAY; + if (ob->dir == nodir) + ob->dir = east; + } + } +} + +//--------------------------------------------------------------------------- +// SpawnProjectile() +//--------------------------------------------------------------------------- +void SpawnProjectile(objtype *shooter, classtype class) +{ + short angle_adj = 0; + unsigned temp=0; + fixed x,y; + + x = shooter->x; + y = shooter->y; + + usedummy=nevermark = true; + switch (class) + { + case spider_mutantshotobj: + case acid_dragonshotobj: + case final_boss4shotobj: + SpawnNewObj(x>>TILESHIFT,y>>TILESHIFT,&s_ofs_random); + PlaySoundLocActor(SPITATTACKSND,new); + new->speed = SPDPROJ; + angle_adj = 1-(US_RndT()&3); + new->temp1 = BossShotShapes[class-spider_mutantshotobj]; + new->flags = FL_OFFSET_STATES|FL_PROJ_CHECK_TRANSPARENT|FL_STORED_OBJPTR; + new->temp3 = (unsigned)shooter; + break; + + case mut_hum1shotobj: + case goldmorphshotobj: + case electroshotobj: + case final_boss2shotobj: + SpawnNewObj(x>>TILESHIFT,y>>TILESHIFT,&s_ofs_shot1); + PlaySoundLocActor(ELECTSHOTSND,new); + new->speed = SPDPROJ; + angle_adj = 1-(US_RndT()&3); + new->temp1 = SPR_ELEC_SHOT1; + new->flags = FL_OFFSET_STATES|FL_PROJ_CHECK_TRANSPARENT|FL_STORED_OBJPTR; + new->temp3 = (unsigned)shooter; + switch (class) + { + case final_boss2shotobj: + case goldmorphshotobj: + new->temp1 = SPR_MGOLD_SHOT1; + + case electroshotobj: + new->lighting = NO_SHADING; + } + break; + + case lcanshotobj: + case podshotobj: + temp = SPR_SPIT3_1-SPR_SPIT1_1; + + case scanshotobj: + case dogshotobj: + SpawnNewObj(x>>TILESHIFT,y>>TILESHIFT,&s_liquid_shot); + PlaySoundLocActor(SPITATTACKSND,new); + new->temp2 = SPR_SPIT1_1+temp; + new->flags = FL_OFFSET_STATES|FL_PROJ_CHECK_TRANSPARENT|FL_STORED_OBJPTR; + new->speed = SPDPROJ+US_RndT(); + angle_adj = 2-(US_RndT() % 5); + new->temp3 = (unsigned)shooter; + break; + + case liquidshotobj: + SpawnNewObj(x>>TILESHIFT,y>>TILESHIFT,&s_liquid_shot); + new->temp2 = SPR_LIQUID_SHOT_FLY_1; + new->flags = FL_OFFSET_STATES|FL_PROJ_CHECK_TRANSPARENT|FL_STORED_OBJPTR; + new->speed = SPDPROJ+US_RndT(); + angle_adj = 2-(US_RndT() % 5); + new->s_tilex = new->s_tiley = 0; + new->temp3 = (unsigned)shooter; + break; + + case grenadeobj: + SpawnNewObj(x>>TILESHIFT,y>>TILESHIFT,&s_ofs_random); + new->speed = SPDPROJ+Random(SPDPROJ>>1); + new->angle = player->angle+1-(US_RndT() & 3); + new->temp1 = grenade_shapes[0]; + new->flags = FL_OFFSET_STATES; //|FL_PROJ_CHECK_TRANSPARENT; + new->lighting = NO_SHADING; // no shading + + // Store off start tile x & y + + new->s_tilex = x>>TILESHIFT; + new->s_tiley = y>>TILESHIFT; + break; + + case bfg_shotobj: + SpawnNewObj(x>>TILESHIFT,y>>TILESHIFT,&s_ofs_random); + new->speed = SPDPROJ+Random(SPDPROJ); + new->angle = player->angle+1-(US_RndT() & 3); + new->temp1 = SPR_BFG_WEAPON_SHOT2; + new->flags = FL_OFFSET_STATES; + new->lighting = NO_SHADING; + + // Store off start tile x & y + + new->s_tilex = x>>TILESHIFT; + new->s_tiley = y>>TILESHIFT; + break; + + } + + usedummy=nevermark = false; + if (new==&dummyobj) + return; + + +// new->flags2 = shooter->flags2 & FL2_CLOAKED; + new->x=x; + new->y=y; + new->active = true; + new->obclass = class; + if (class != grenadeobj && class != bfg_shotobj) + new->angle=CalcAngle(new,player)+angle_adj; + + if (shooter->obclass == gold_morphobj) + new->angle += morph_angle_adj; + + if (new->angle<=0) + new->angle+=359; + else + if (new->angle>359) + new->angle-=359; + + new->flags |= (FL_NONMARK|FL_NEVERMARK); + +// Move grenade slightly in front of player so you can see instant +// explosions (ie: when you're face up against a wall). +// + if (class==grenadeobj || class == bfg_shotobj) + { + long deltax,deltay; + + deltax = FixedByFrac(mindist+(mindist>>3),costable[new->angle]); + deltay = -FixedByFrac(mindist+(mindist>>3),sintable[new->angle]); + + new->x += deltax; + new->y += deltay; + } + +// if (actorat[new->tilex][new->tiley]==new); +// actorat[new->tilex][new->tiley]=NULL; +} + +objtype *proj_check; +unsigned char proj_wall; + +//--------------------------------------------------------------------------- +// ProjectileTryMove() +// +// deltax - 'X' distance to travel +// deltay - 'Y' distance to travel +// distance - vectoral distance of travel +// +//--------------------------------------------------------------------------- +boolean ProjectileTryMove(objtype *ob, fixed deltax, fixed deltay) +{ + #define PROJECTILE_MAX_STEP PROJWALLSIZE + + unsigned xl,yl,xh,yh,x,y,steps; + fixed dx, dy; + short ydist,xdist; + + proj_wall = 0; + steps = tics; + // + // Move that lil' projectile + // + + while (steps) + { + ob->x+=deltax; + ob->y+=deltay; + + steps--; + + xl = (ob->x-PROJECTILE_MAX_STEP) >>TILESHIFT; + yl = (ob->y-PROJECTILE_MAX_STEP) >>TILESHIFT; + + xh = (ob->x+PROJECTILE_MAX_STEP) >>TILESHIFT; + yh = (ob->y+PROJECTILE_MAX_STEP) >>TILESHIFT; + + + // Check for solid walls and actors. + // + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { +#pragma warn -pia + if (proj_check = actorat[x][y]) +#pragma warn +pia + if (proj_check < objlist) + { + if (proj_check == (objtype *)1 && tilemap[x][y] == 0) + { + // We have a static! + // + // Test for collision radius using CENTER of static + // NOT tile size boundries + + ydist = (y<y; + ydist = ABS(ydist); + + xdist = (x<x; + xdist = ABS(xdist); + + if ((unsigned)xdist < PROJCHECKSIZE && (unsigned)ydist < PROJCHECKSIZE) + { + proj_check=false; + proj_wall = NULL; + ob->tilex = ob->x>>TILESHIFT; + ob->tiley = ob->y>>TILESHIFT; + return(false); + } + + } + else + { + // We have a wall! + + proj_wall = (unsigned char)proj_check; + proj_check=false; + ob->tilex = ob->x>>TILESHIFT; + ob->tiley = ob->y>>TILESHIFT; + return(false); + } + } + else + if (proj_check < &objlist[MAXACTORS]) + { + if ((ob->flags & FL_PROJ_CHECK_TRANSPARENT) && (proj_check->flags & FL_PROJ_TRANSPARENT)) + break; + else + if (proj_check->flags & FL_SOLID) + { + ob->tilex = ob->x>>TILESHIFT; + ob->tiley = ob->y>>TILESHIFT; + return(false); + } + } + } + } + + return(true); +} + +//-------------------------------------------------------------------------- +// T_Projectile() +//-------------------------------------------------------------------------- +void T_Projectile(objtype *ob) +{ + long deltax,deltay; + int damage; + long speed; + objtype *attacker; + +// Move this object. +// + speed = ob->speed; + + deltax = FixedByFrac(speed,costable[ob->angle]); + deltay = -FixedByFrac(speed,sintable[ob->angle]); + +// ob->x += deltax; +// ob->y += deltay; + +// Did movement hit anything solid. +// +#pragma warn -rch + + proj_check=false; + + if (!ProjectileTryMove (ob,deltax,deltay)) + { + switch (ob->obclass) + { + case spider_mutantshotobj: + InitSmartSpeedAnim(ob,SPR_BOSS1_EXP1,0,2,at_ONCE,ad_FWD,5); + return; + break; + + case acid_dragonshotobj: + InitSmartSpeedAnim(ob,SPR_BOSS5_EXP1,0,2,at_ONCE,ad_FWD,5); + return; + break; + + case mut_hum1shotobj: + case electroshotobj: // Explode on walls + InitSmartSpeedAnim(ob,SPR_ELEC_SHOT_EXP1,0,1,at_ONCE,ad_FWD,5+(US_RndT()&3)); + return; + break; + + case final_boss2shotobj: + case goldmorphshotobj: + InitSmartSpeedAnim(ob,SPR_MGOLD_SHOT_EXP1,0,1,at_ONCE,ad_FWD,5+(US_RndT()&3)); + return; + break; + + case final_boss4shotobj: + InitSmartSpeedAnim(ob,SPR_BOSS10_SPIT_EXP1,0,1,at_ONCE,ad_FWD,5+(US_RndT()&3)); + return; + break; + + case lcanshotobj: // Explode on walls + case podshotobj: + InitSmartSpeedAnim(ob,SPR_SPIT_EXP3_1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&3)); + return; + break; + + case scanshotobj: // Explode on walls + case dogshotobj: + InitSmartSpeedAnim(ob,SPR_SPIT_EXP1_1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + return; + break; + + + case liquidshotobj: // Explode on walls + InitSmartSpeedAnim(ob,SPR_LIQUID_SHOT_BURST_1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + return; + break; + + case grenadeobj: + // Hit actor - Hurt 'Em! + if (proj_check) + { + if (proj_check->flags & FL_SHOOTABLE) + DamageActor(proj_check,GR_DAMAGE,ob); + } + + // + // Start Anim, Sound, and mark as exploded... + // + + ob->obclass = gr_explosionobj; + InitSmartSpeedAnim(ob,SPR_EXPLOSION_1,0,4,at_ONCE,ad_FWD,3+(US_RndT()&7)); + A_DeathScream(ob); + return; + break; + + + case bfg_shotobj: + +#if BFG_SHOT_STOPS + // + // Check to see if a collison has already occured at this + // tilex and tiley + // + if (ob->s_tilex == ob->tilex && ob->s_tiley == ob->tiley) + return; + + ob->s_tilex = ob->tilex; + ob->s_tilex = ob->tilex; +#endif + + if (proj_wall & 0x80) + TryBlastDoor(proj_wall & (~0x80)); + + if (proj_check) + { + // Hit actor - Hurt 'Em! + + if (proj_check->flags2 & FL2_BFG_SHOOTABLE) + { + // Damage that actor + + DamageActor(proj_check, BFG_DAMAGE>>1, ob); // bfg_damage>>3 + + // Stop on actors that you don't kill. + + } + +#if BFG_SHOT_STOPS + if (proj_check->flags2 & FL2_BFGSHOT_SOLID) + goto BlowIt; + + if (proj_check->flags & FL_DEADGUY) + return; +#endif + } + + +BlowIt: + // + // Start Anim, Sound, and mark as exploded... + // + ob->obclass = bfg_explosionobj; + InitAnim(ob,SPR_BFG_EXP1,0,7,at_ONCE,ad_FWD,(US_RndT()&7),5); + A_DeathScream(ob); + return; + break; + } + } +#pragma warn +rch + +// Determine if object hit player. +// + if (ob->obclass != grenadeobj && ob->obclass != bfg_shotobj) + { + // Determine distance from player. + // + deltax = ob->x - player->x; + deltax = LABS(deltax); + deltay = ob->y - player->y; + deltay = LABS(deltay); + + if (deltax < PROJECTILESIZE && deltay < PROJECTILESIZE) + { + deltax = FixedByFrac(PROJECTILESIZE, costable[ob->angle]); + deltay = -FixedByFrac(PROJECTILESIZE, sintable[ob->angle]); + + ob->x -= deltax; + ob->y -= deltay; + + if (ob->flags & FL_STORED_OBJPTR) + attacker = (objtype *)ob->temp3; + else + attacker = ob; + + switch (ob->obclass) + { + case mut_hum1shotobj: + case electroshotobj: + damage = (US_RndT() >>5); + InitSmartSpeedAnim(ob,SPR_ELEC_SHOT_EXP1,0,1,at_ONCE,ad_FWD,3+(US_RndT()&7)); + break; + + case final_boss4shotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_BOSS10_SPIT_EXP1,0,1,at_ONCE,ad_FWD,3+(US_RndT()&3)); + break; + + case goldmorphshotobj: + case final_boss2shotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_MGOLD_SHOT_EXP1,0,1,at_ONCE,ad_FWD,5+(US_RndT()&7)); + break; + + case lcanshotobj: + case podshotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_SPIT_EXP3_1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + break; + + case scanshotobj: + case dogshotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_SPIT_EXP1_1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + break; + + case liquidshotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_LIQUID_SHOT_BURST_1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + break; + + case spider_mutantshotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_BOSS1_EXP1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + break; + + case acid_dragonshotobj: + damage = (US_RndT()>>4); + InitSmartSpeedAnim(ob,SPR_BOSS5_EXP1,0,2,at_ONCE,ad_FWD,5+(US_RndT()&7)); + break; + } + + TakeDamage (damage,attacker); + return; + } + } + +// Align tile coordinates on boundaries. +// + ob->tilex = ob->x >> TILESHIFT; + ob->tiley = ob->y >> TILESHIFT; +} + + + +#define EX_RADIUS 2 // Tiles out from center + +char ff_buffer[EX_RADIUS*2+1][EX_RADIUS*2+1]; +short ff_damageplayer,ff_damage; +objtype *ff_obj; + +void ExplodeFill(char tx, char ty); + +//--------------------------------------------------------------------------- +// ExplodeRadius() +//--------------------------------------------------------------------------- +void ExplodeRadius(objtype *obj, short damage, boolean damageplayer) +{ + int xl,yl,xh,yh,y,x; + +// +// Did this object start out in a wall? +// + if (tilemap[obj->tilex][obj->tiley]) + return; + +// Setup globals to minimize parameter passing while recursing! +// + ff_obj = obj; + ff_damage = damage; + +// Check to see if play is a Baby and should not be hurt by explosions, +// except from actors that use explosions for attacks (IE. PerScan Drones). +// + if (gamestate.difficulty > gd_baby || obj->obclass == floatingbombobj) + ff_damageplayer = damageplayer; + else + ff_damageplayer = false; + + +#if 0 + +// Back actor out of wall... +// +// This "back actor out" code was my first thought on how to pull it out... +// Might be better to remove "while" loop and just do some tile alignment +// calculations... At any rate, this code is hardly ever executed -- when +// it is, it only runs through the "while" 2-3 times. +// + obj->angle -= ANGLES/2; + if (obj->angle < 0) + obj->angle += ANGLES; + + if (tilemap[obj->tilex][obj->tiley]&63) + { + long deltax,deltay; + + deltax = FixedByFrac(obj->speed/2,costable[obj->angle]); + deltay = -FixedByFrac(obj->speed/2,sintable[obj->angle]); + + while (tilemap[obj->tilex][obj->tiley]&63) + { + obj->x += deltax; + obj->y += deltay; + + obj->x &= 0x3fffff; + obj->y &= 0x3fffff; + + obj->tilex = obj->x>>TILESHIFT; + obj->tiley = obj->y>>TILESHIFT; + } + } + +// Start flood-fill explosion! +// +#endif + + ff_obj = obj; + memset(ff_buffer,0,sizeof(ff_buffer)); + ExplodeFill(obj->tilex,obj->tiley); + ExplodeStatics(obj->tilex,obj->tiley); +} + +//--------------------------------------------------------------------------- +// ExplodeFill() +//--------------------------------------------------------------------------- +void ExplodeFill(char tx, char ty) +{ + char bx=tx-ff_obj->tilex+EX_RADIUS, + by=ty-ff_obj->tiley+EX_RADIUS, + door,no_wall; + + statobj_t *statobj; + +// Damage actors on this spot! +// + if (ff_damageplayer && tx == player->tilex && ty == player->tiley) + TakeDamage(EXPLODE_DAMAGE,ff_obj); + else + { + proj_check = actorat[tx&63][ty&63]; + + if ((proj_check >= objlist) && (proj_check < &objlist[MAXACTORS])) + { + if ((proj_check->flags & FL_SHOOTABLE)) + switch (proj_check->obclass) + { + // Detinate all floating bombs & VMTs + // + case floatingbombobj: + case volatiletransportobj: + DamageActor(proj_check,500,ff_obj); + break; + + // Hanging turrets are not effected by + // concussion weapons. + // + case hang_terrotobj: + case arc_barrierobj: + case post_barrierobj: + case vpost_barrierobj: + case vspike_barrierobj: + break; + + // + // Test for Level completion object + // + case rotating_cubeobj: + if (ff_obj->obclass == pd_explosionobj) + { + proj_check->lighting = EXPLOSION_SHADING; + proj_check->flags &= ~(FL_SOLID|FL_SHOOTABLE); + InitSmartSpeedAnim(proj_check,SPR_CUBE_EXP1,0,8,at_ONCE,ad_FWD,5); + PlaySoundLocActor(EXPLODE1SND,proj_check); + + // Unlock Next floor + + gamestuff.level[gamestate.mapon+1].locked=false; + gamestate.key_floor=gamestate.mapon+1; + } + break; + + // + // Plasma/Fision Detonators (already armed) + // + case plasma_detonatorobj: + if (ff_obj == player || // Player shot it with gun + ff_obj->tilex == tx && ff_obj->tiley == ty) // Direct Hit with grenade + DamageActor(proj_check,1,ff_obj); + else + DamageActor(proj_check,20,ff_obj); // An explosion has started a chain reaction + break; + + + // Everyone else gets the shit kicked + // out of them... + // + default: + if (!(proj_check->flags2 & FL2_CLOAKED)) + SpawnFlash(proj_check->x,proj_check->y); + DamageActor(proj_check,ff_damage,ff_obj); + break; + } + } + } + +// Mark spot as exploded! +// + ff_buffer[bx][by] = 1; + +// Explode to the EAST! +// + bx += 1; + tx += 1; + + door = tilemap[tx][ty]; + if (door & 0x80) + no_wall = doorobjlist[door&0x7f].action != dr_closed; + else + no_wall = !tilemap[tx][ty]; + + if ((!ff_buffer[bx][by]) && (no_wall) && (bx <= EX_RADIUS*2)) + ExplodeFill(tx,ty); + +// Explode to the WEST! +// + bx -= 2; + tx -= 2; + + door = tilemap[tx][ty]; + if (door & 0x80) + no_wall = doorobjlist[door&0x7f].action != dr_closed; + else + no_wall = !tilemap[tx][ty]; + + if ((!ff_buffer[bx][by]) && (no_wall) && (bx >= 0)) + ExplodeFill(tx,ty); + +// Explode to the SOUTH! +// + bx++; + tx++; + by += 1; + ty += 1; + + door = tilemap[tx][ty]; + if (door & 0x80) + no_wall = doorobjlist[door&0x7f].action != dr_closed; + else + no_wall = !tilemap[tx][ty]; + + if ((!ff_buffer[bx][by]) && (no_wall) && (by <= EX_RADIUS*2)) + ExplodeFill(tx,ty); + +// Explode to the NORTH! +// + by -= 2; + ty -= 2; + + door = tilemap[tx][ty]; + if (door & 0x80) + no_wall = doorobjlist[door&0x7f].action != dr_closed; + else + no_wall = !tilemap[tx][ty]; + + if ((!ff_buffer[bx][by]) && (no_wall) && (by >= 0)) + ExplodeFill(tx,ty); +} + +//--------------------------------------------------------------------------- +// CalcAngle() - Calculates angle from 1st object to 2nd object. +//--------------------------------------------------------------------------- +int CalcAngle(objtype *from_obj, objtype *to_obj) +{ + long deltax,deltay,from_x,from_y,to_x,to_y; + float angle; + int iangle; + + from_x = from_obj->x; + from_y = from_obj->y; + + to_x = to_obj->x; + to_y = to_obj->y; + + // Calculate deltas from "from_obj" to "to_obj". + // + + deltax = to_x - from_x; + deltay = from_y - to_y; + + if (!(deltax|deltay)) + return(1); + + // Calc Arc Tan from Obj1 to Obj2 - Returns radians + + angle = atan2 (deltay,deltax); + + if (angle<0) + angle = M_PI*2+angle; +// else +// if (!angle) +// angle = 1; + + // Convert rads to degs + + iangle = angle/(M_PI*2)*ANGLES; + + return(iangle); +} + +#if 0 + +//-------------------------------------------------------------------------- +// CalcDistance() - Calculates the distance from coords #1 to coords #2 +//-------------------------------------------------------------------------- +unsigned CalcDistance(unsigned x1, unsigned y1, unsigned x2, unsigned y2) +{ + int norm_dx,norm_dy; + + norm_dx = x2-x1; + norm_dy = y1-y2; + + return(IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy))); +} + + +#pragma warn -rvl +//-------------------------------------------------------------------------- +// IntSqrt() +//-------------------------------------------------------------------------- +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 +} +#pragma warn +rvl + + +#endif + +//-------------------------------------------------------------------------- +// T_BlowBack() +//-------------------------------------------------------------------------- +void T_BlowBack(objtype *obj) +{ + #define SLIDE_SPEED 0x2000 + + unsigned dist_table[] = {0x1000, // wp_autocharge, + 0x2000, // wp_pistol, + 0x3000, // wp_burst_rifle, + 0x4000, // wp_ion_cannon, + 0x5000, // wp_grenade, + }; + + long deltax,deltay; + unsigned angle,dist; + objtype *killer; + + if (obj->flags & FL_NO_SLIDE) + return; + + if (!(obj->flags & FL_SLIDE_INIT)) + { + // Check for NULL ptr + + killer = (objtype *)SLIDE_TEMP(obj); + if (!killer) + { + obj->flags |= FL_NO_SLIDE; + return; + } + + obj->angle = CalcAngle(killer,obj); + + if ((killer = (objtype *)SLIDE_TEMP(obj)) == player) + SLIDE_TEMP(obj) = dist_table[gamestate.weapon]; + else + SLIDE_TEMP(obj) = dist_table[wp_grenade]; + + obj->flags |= FL_SLIDE_INIT; + } + + + if (SLIDE_TEMP(obj) > SLIDE_SPEED) + { + dist = SLIDE_SPEED; + SLIDE_TEMP(obj) -= SLIDE_SPEED; + } + else + { + dist = SLIDE_TEMP(obj); + obj->flags |= FL_NO_SLIDE; // Stop any more sliding + } + + deltax = FixedByFrac(dist, costable[obj->angle]); // Optomize - Store in actor + deltay = -FixedByFrac(dist, sintable[obj->angle]); // + + if (ClipMove(obj,deltax,deltay)) + obj->flags |= FL_NO_SLIDE; + + obj->tilex = obj->x>>TILESHIFT; + obj->tiley = obj->y>>TILESHIFT; + + if (obj->flags & FL_NO_SLIDE) + { + // Set actor WHERE IT SLID in actorat[], IF there's a door! + // + if (tilemap[obj->tilex][obj->tiley] & 0x80) + { + actorat[obj->tilex][obj->tiley]=obj; + obj->flags &= ~FL_NEVERMARK; + } + } +} + \ No newline at end of file diff --git a/3D_AGENT.C b/3D_AGENT.C new file mode 100644 index 0000000..5ce3f57 --- /dev/null +++ b/3D_AGENT.C @@ -0,0 +1,5291 @@ +// 3D_AGENT.C + +#include "3D_DEF.H" +#pragma hdrstop + +#include + +#include "jm_tp.h" + +void InitWeaponBounce(void); +void HandleWeaponBounce(void); + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +//#define ACTIVATE_TERMINAL + +#define MAXMOUSETURN 10 + +#define MOVESCALE 150l +#define BACKMOVESCALE 100l +#define ANGLESCALE 20 +#define MAX_DA 100 + +#define MAX_TERM_COMMAND_LEN 31 + +// Max Score displayable + +#define MAX_DISPLAY_SCORE (9999999L) +#define SCORE_ROLL_WAIT (60*10) // Tics + +// IFDEF switches + +//#define NO_STATUS_BAR + +// ECG scroll rate (delay). + +#define HEALTH_SCROLL_RATE 7 +#define HEALTH_PULSE 70 + +// Text "InfoArea" defines +#define INFOAREA_X 3 +#define INFOAREA_Y ((unsigned)200-STATUSLINES+3) +#define INFOAREA_W 109 +#define INFOAREA_H 37 + +#define INFOAREA_BCOLOR 0x01 +#define INFOAREA_CCOLOR 0x1A +#define INFOAREA_TCOLOR 0xA6 +#define INFOAREA_TSHAD_COLOR 0x04 // Text Shadow Color + +#define GRENADE_ENERGY_USE 4 +#define BFG_ENERGY_USE (GRENADE_ENERGY_USE<<1) + + +#define NUM_AMMO_SEGS 21 + + +#define AMMO_SMALL_FONT_NUM_WIDTH 5 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +extern boolean noShots; +extern short bounceOk; + +short tryDetonatorDelay=0; + +// +// player state info +// +long thrustspeed; + +//unsigned plux,pluy; // player coordinates scaled to unsigned + +int anglefrac; + +objtype *LastAttacker; + +boolean PlayerInvisable = false; + +char LocationText[MAX_LOCATION_DESC_LEN]; + +#ifdef ACTIVATE_TERMINAL +char term_com_name[13]= {"TERM_CMD."}; +char term_msg_name[13]= {"TERM_MSG."}; +#endif + +unsigned player_oldtilex; +unsigned player_oldtiley; + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +void writeTokenStr(char far *str); + +void ShowOverheadChunk(void); +void LoadOverheadChunk(short tpNum); +void SaveOverheadChunk(short tpNum); +void DisplayTeleportName(char tpNum, boolean locked); + +void ForceUpdateStatusBar(void); + +void UpdateRadarGuage(void); +void DrawLedStrip(short x,short y,short frac,short max); +void DisplayPinballBonus(void); +void CheckPinballBonus(long points); +byte LevelCompleted(void); +void T_Player (objtype *ob); +void T_Attack (objtype *ob); + +statetype s_player = {0,0,0,&T_Player,NULL,NULL}; +statetype s_attack = {0,0,0,&T_Attack,NULL,NULL}; + +long playerxmove,playerymove; + +atkinf_t far attackinfo[7][14] = + +{ +{ {6,0,1},{6,2,2},{6,0,3},{6,-1,4} }, // Auto charge +{ {6,0,1},{6,1,2},{6,0,3},{6,-1,4} }, // Pistol +{ {6,0,1},{6,1,2},{5,3,3},{5,-1,4} }, // Pulse +{ {6,0,1},{6,1,2},{3,4,3},{3,-1,4} }, // ION +{ {6,0,1},{6,5,2},{6,6,3},{6,-1,4} }, +{ {6,0,1},{6,9,2},{6,10,3},{6,-1,4} }, +{ {5,7,0},{5,8,0},{2,-2,0},{0,0,0} }, +}; + +//int strafeangle[9] = {0,90,180,270,45,135,225,315,0}; + +#define GD0 0x55 +#define YD0 0x35 +#define RD0 0x15 + +#define GD1 0x53 +#define YD1 0x33 +#define RD1 0x13 + +char far DimAmmo[2][22] = {{GD0,GD0,GD0,GD0,GD0,GD0,GD0,YD0,YD0,YD0,YD0,YD0,YD0,YD0,RD0,RD0,RD0,RD0,RD0,RD0,RD0,RD0}, + {GD1,GD1,GD1,GD1,GD1,GD1,GD1,YD1,YD1,YD1,YD1,YD1,YD1,YD1,RD1,RD1,RD1,RD1,RD1,RD1,RD1,RD1}}; + +#define GL0 0x58 +#define YL0 0x38 +#define RL0 0x18 + +#define GL1 0x56 +#define YL1 0x36 +#define RL1 0x16 + +char far LitAmmo[2][22] = {{GL0,GL0,GL0,GL0,GL0,GL0,GL0,YL0,YL0,YL0,YL0,YL0,YL0,YL0,RL0,RL0,RL0,RL0,RL0,RL0,RL0,RL0}, + {GL1,GL1,GL1,GL1,GL1,GL1,GL1,YL1,YL1,YL1,YL1,YL1,YL1,YL1,RL1,RL1,RL1,RL1,RL1,RL1,RL1,RL1}}; + + +#define IA_MAX_LINE 30 +typedef struct InfoArea_Struct +{ + int x,y; + int text_color; + int backgr_color; + int left_margin; + char delay; + char numanims; + char framecount; +} InfoArea_Struct; + +unsigned LastMsgPri = 0; +short MsgTicsRemain = 0; +classtype LastInfoAttacker = nothing; +int LastInfoAttacker_Cloaked = 0; +infomsg_type LastMsgType = MT_NOTHING; +InfoArea_Struct InfoAreaSetup; + +char DrawRadarGuage_COUNT = 3; +char DrawAmmoNum_COUNT = 3; +char DrawAmmoPic_COUNT = 3; +//char DrawPDAmmoPic_COUNT = 3; +char DrawScoreNum_COUNT = 3; +char DrawWeaponPic_COUNT = 3; +char DrawKeyPics_COUNT = 3; +char DrawHealthNum_COUNT = 3; + +char DrawInfoArea_COUNT = 3; +char InitInfoArea_COUNT = 3; +char ClearInfoArea_COUNT = 3; + +void DrawWeapon (void); +void GiveWeapon (int weapon); +void GiveAmmo (int ammo); +void DrawGAmmoNum(void); +void DrawMAmmoNum(void); +void DrawPDAmmoMsg(void); +void ComputeAvailWeapons(void); +void SW_HandleActor(objtype *obj); +void SW_HandleStatic(statobj_t *stat,unsigned tilex, unsigned tiley); + +//=========================================================================== + +//---------- + +byte ShowRatio(short bx, short by, short px, short py, long total, long perc, ss_type type); +void Attack (void); +void Use (void); +void Search (objtype *ob); +void SelectWeapon (void); +void SelectItem (void); + +//---------- + +void SpawnPlayer (int tilex, int tiley, int dir); +void Thrust (int angle, long speed); +boolean TryMove (objtype *ob); +void T_Player (objtype *ob); + +boolean ClipMove (objtype *ob, long xmove, long ymove); + +void SocketToggle(boolean TurnOn); +void CheckStatsBonus(void); + +void T_Stand (objtype *ob); + +/* +============================================================================= + + CONTROL STUFF + +============================================================================= +*/ + +/* +====================== += += CheckWeaponChange += += Keys 1-6 change weapons += += +====================== +*/ + + +void CheckWeaponChange (void) +{ + int i,buttons,last; + + for (i=wp_autocharge;i<=wp_bfg_cannon;i++) + { + if (buttonstate[bt_ready_autocharge+i-wp_autocharge]) + { + if (gamestate.useable_weapons & (1<x; + oldy = player->y; + +// +// side to side move +// + + if ((buttonstate[bt_strafe]) && !(gamestate.turn_around)) + { + // + // 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 + { + if (gamestate.turn_around) + { + controlx = 100*tics; + if (gamestate.turn_around < 0) + controlx = -controlx; + } + + // + // 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; + + if (gamestate.turn_around) + { + boolean done=false; + + if (gamestate.turn_around > 0) + { + gamestate.turn_around -= angleunits; + if (gamestate.turn_around <= 0) + done=true; + } + else + { + gamestate.turn_around -= angleunits; + if (gamestate.turn_around >= 0) + done=true; + } + + if (done) + { + gamestate.turn_around=0; + ob->angle = gamestate.turn_angle; + } + } + } + + +// +// 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 + } + else + if (bounceOk) + bounceOk--; + + if (controly) + bounceOk = 8; + else + if (bounceOk) + bounceOk--; + + ob->dir = ((ob->angle + 22) % 360)/45; + +// +// calculate total move +// + playerxmove = player->x - oldx; + playerymove = player->y - oldy; +} + +/* +============================================================================= + + STATUS WINDOW STUFF + +============================================================================= +*/ + +#define STATUSDRAWPIC(x, y, picnum) JLatchDrawPic((x),(y+(200-STATUSLINES)),(picnum)) + + +/* +================== += += StatusDrawPic += +================== +*/ +void StatusAllDrawPic(unsigned x, unsigned y, unsigned picnum) +{ + unsigned temp; + +#ifdef PAGEFLIP + + temp = bufferofs; + bufferofs = PAGE1START+(200-STATUSLINES)*SCREENWIDTH; + JLatchDrawPic (x,y,picnum); + bufferofs = PAGE2START+(200-STATUSLINES)*SCREENWIDTH; + JLatchDrawPic (x,y,picnum); + bufferofs = PAGE3START+(200-STATUSLINES)*SCREENWIDTH; + JLatchDrawPic (x,y,picnum); + bufferofs = temp; + +#else + + temp = bufferofs; + bufferofs = screenloc[0]+(200-STATUSLINES)*SCREENWIDTH; + JLatchDrawPic (x,y,picnum); + bufferofs = screenloc[1]+(200-STATUSLINES)*SCREENWIDTH; + JLatchDrawPic (x,y,picnum); + bufferofs = screenloc[2]+(200-STATUSLINES)*SCREENWIDTH; + JLatchDrawPic (x,y,picnum); + bufferofs = temp; + +#endif + +} + + +void JLatchDrawPic (unsigned x, unsigned y, unsigned picnum) +{ + unsigned wide, height, source; + + x <<= 3; + wide = pictable[picnum-STARTPICS].width; + height = pictable[picnum-STARTPICS].height; + source = latchpics[2+picnum-LATCHPICS_LUMP_START]; + VL_LatchToScreen (source,wide/4,height,x,y); +} + + + +/* +=============== += += LatchNumber += += right justifies and pads with blanks += +=============== +*/ +void LatchNumber (int x, int y, int width, long number) +{ + unsigned length,wide=0,c; + char str[20]; + + ltoa(number,str,10); + + length = strlen(str); + + while ((lengthobclass)) + MsgTicsRemain = DISPLAY_MSG_STD_TIME; + else + { + if (DISPLAY_TIMED_MSG(ActorInfoMsg[attacker->obclass-rentacopobj],MP_TAKE_DAMAGE,MT_ATTACK)) + { + LastInfoAttacker = attacker->obclass; + LastInfoAttacker_Cloaked = attacker->flags2 & FL2_CLOAKED; + } + } + } + + if (godmode) + return; + + if (gamestate.difficulty==gd_baby) + points>>=2; + + gamestate.health -= points; + + if (gamestate.health<=0) + { + gamestate.health = 0; + playstate = ex_died; + killerobj = attacker; + if (killerobj) + killerobj->flags |= FL_FREEZE; + } + + StartDamageFlash (points); + DrawHealth(); +} + +//--------------------------------------------------------------------------- +// HealSelf() +//--------------------------------------------------------------------------- +void HealSelf(int points) +{ + gamestate.health += points; + if (gamestate.health > 100) + gamestate.health = 100; + + DrawHealth (); +} + + + + +//=========================================================================== +// +// +// SCORE DISPLAY ROUTINES +// +// +//=========================================================================== + +//-------------------------------------------------------------------------- +// DrawScore() +// +// PURPOSE : Marks the Score to be refreshed durring the next +// StatusBarRefresh. +//-------------------------------------------------------------------------- +void DrawScore(void) +{ + DrawScoreNum_COUNT = 3; +} + +extern unsigned char music_num; + +//-------------------------------------------------------------------------- +// DrawScoreNum() +// +// NOTE : Could do some sort of "scrolling" animation on LED screen with +// chars and a simple table..... +//-------------------------------------------------------------------------- +void DrawScoreNum(void) +{ + #define Y 3 + #define X 32 + + if (gamestate.tic_score > MAX_DISPLAY_SCORE) + { + if (gamestate.score_roll_wait) + { + JLatchDrawPic(X+0,(200-STATUSLINES)+Y,N_BLANKPIC); + JLatchDrawPic(X+1,(200-STATUSLINES)+Y,N_DASHPIC); + JLatchDrawPic(X+2,(200-STATUSLINES)+Y,N_RPIC); + JLatchDrawPic(X+3,(200-STATUSLINES)+Y,N_OPIC); + JLatchDrawPic(X+4,(200-STATUSLINES)+Y,N_LPIC); + JLatchDrawPic(X+5,(200-STATUSLINES)+Y,N_LPIC); + JLatchDrawPic(X+6,(200-STATUSLINES)+Y,N_DASHPIC); + } + else + { + LatchNumber(X,Y,7,gamestate.tic_score%(MAX_DISPLAY_SCORE+1)); + } + } + else + { + if (gamestate.flags & GS_TICS_FOR_SCORE) + LatchNumber(X,Y,7,realtics); + else + if (gamestate.flags & GS_MUSIC_TEST) + LatchNumber(X,Y,7,music_num); + else + LatchNumber(X,Y,7,gamestate.tic_score); + } +} + +//-------------------------------------------------------------------------- +// UpdateScore() +//-------------------------------------------------------------------------- +void UpdateScore(void) +{ + long score_diff, temp_tics; + boolean RollSound; + + score_diff = gamestate.score - gamestate.tic_score; + + if (score_diff) + { + if (score_diff > 1500) + temp_tics = score_diff>>2; + else + temp_tics = tics<<3; + + if (score_diff > temp_tics) + gamestate.tic_score += temp_tics; + else + gamestate.tic_score = gamestate.score; + + DrawScore(); + } + + + if (gamestate.score_roll_wait) + { + if ((gamestate.score_roll_wait-=tics) <= 0) + { + gamestate.score_roll_wait = 0; + } + DrawScore(); + } +} + +//-------------------------------------------------------------------------- +// GivePoints() +// +// .score = Holds real score +// .tic_score = Holds displayed score (tic'ing toward .score) +// +//-------------------------------------------------------------------------- +void GivePoints(long points,boolean add_to_stats) +{ +// Add score to statistics. +// + if (add_to_stats) + gamestuff.level[gamestate.mapon].stats.accum_points += points; + +// Check for bonuses! +// + CheckPinballBonus(points); + +// Add points to score +// + gamestate.score += points; +} + +//=========================================================================== +// +// +// SECURITY KEY DISPLAY ROUTINES +// +// +//=========================================================================== + + + +//--------------------------------------------------------------------------- +// DrawKeys() +// +// PURPOSE : Marks the security key pics to be refreshed during the next +// StatusBarRefresh. +//--------------------------------------------------------------------------- +void DrawKeys (void) +{ + DrawKeyPics_COUNT = 3; +} + +//--------------------------------------------------------------------------- +// DrawKeyPics() +//--------------------------------------------------------------------------- +void DrawKeyPics(void) +{ + char loop; + + DrawKeyPics_COUNT--; + + for (loop=0; loop NUM_AMMO_SEGS) + temp = NUM_AMMO_SEGS; + + if (!temp) + temp = 1; + } + else + temp = 0; + + gamestate.radar_leds = temp; + + if (temp != gamestate.lastradar_leds) + gamestate.lastradar_leds = temp; + + DrawRadarGuage_COUNT=3; +} + +//--------------------------------------------------------------------------- +// DrawRadarGuage() +//--------------------------------------------------------------------------- +void DrawRadarGuage(void) +{ + char zoom; + + DrawLedStrip(235,155,gamestate.radar_leds,NUM_AMMO_SEGS); + + if (gamestate.rpower) + zoom = gamestate.rzoom; + else + zoom = 0; + + JLatchDrawPic(22,152,ONEXZOOMPIC+zoom); +} + +//--------------------------------------------------------------------------- +// DrawLedStrip() +//--------------------------------------------------------------------------- +void DrawLedStrip(short x,short y,short frac,short max) +{ + int ypos; + unsigned amount; + char leds; + + leds = frac; + + if (leds) + amount = max-leds; + else + amount = max; + +// Draw dim LEDs. +// + for (ypos = 0;ypos < amount;ypos++) + { + VW_Hlin (x,x+4,y++,DimAmmo[0][amount]); + VW_Hlin (x,x+4,y++,DimAmmo[1][amount]); + } + +// Draw lit LEDs. +// + for (;ypos MP_BONUS + if (LastMsgType == MT_OUT_OF_AMMO) + { + MsgTicsRemain = 1; + LastMsgType = MT_CLEAR; + } +#endif + + gamestate.ammo += ammo; + if (gamestate.ammo > MAX_AMMO) + { + gamestate.ammo = MAX_AMMO; + } + + DrawAmmo(false); + + if (gamestate.weapon != gamestate.chosenweapon) + { + if (gamestate.useable_weapons & (1< MP_BONUS + if (LastMsgType == MT_OUT_OF_AMMO) + { + MsgTicsRemain = 1; + LastMsgType = MT_CLEAR; + } +#endif + + gamestate.ammo += ammo; + if (gamestate.ammo > MAX_AMMO) + { + gamestate.ammo = MAX_AMMO; + } + + // JIM - This needs to be optomized. + + if (gamestate.weapon != gamestate.chosenweapon) + { + if (!((gamestate.chosenweapon == wp_grenade) && (gamestate.ammo < GRENADE_ENERGY_USE)) || + !((gamestate.chosenweapon == wp_bfg_cannon) && (gamestate.ammo < BFG_ENERGY_USE))) + { + gamestate.weapon = gamestate.chosenweapon; + DrawWeapon (); + } + } + + DrawAmmo(false); + SD_PlaySound (GETAMMOSND); +#endif +} + + +//--------------------------------------------------------------------------- +//ComputeAvailWeapons() +// +// This function creates a Bit MASK for gamestate.weapons according to what +// weapon is available for useage due to ammo avail. +// +//--------------------------------------------------------------------------- +void ComputeAvailWeapons(void) +{ + + // + // Determine what ammo ammounts we have avail + // + + if (gamestate.ammo) + { + if (gamestate.ammo >= BFG_ENERGY_USE) + gamestate.useable_weapons = (1<= GRENADE_ENERGY_USE) + gamestate.useable_weapons = (1< MAX_PLASMA_DETONATORS) + gamestate.plasma_detonators = MAX_PLASMA_DETONATORS; + +// if (gamestate.chosenweapon == wp_plasma_detonators) +// { +// gamestate.weapon = gamestate.chosenweapon; +// DrawWeapon (); +// } + + ComputeAvailWeapons(); +} + + +//--------------------------------------------------------------------------- +// GiveToken() +//--------------------------------------------------------------------------- +void GiveToken (int tokens) +{ +#if MP_NO_MORE_TOKENS > MP_BONUS + if (LastMsgType == MT_NO_MO_FOOD_TOKENS) + { + MsgTicsRemain = 1; + LastMsgType = MT_CLEAR; + } +#endif + + gamestate.tokens += tokens; + if (gamestate.tokens > MAX_TOKENS) + { + gamestate.tokens = MAX_TOKENS; + } + + SD_PlaySound (GOTTOKENSND); +} + +//=========================================================================== +// +// +// INFO AREA ROUTINES +// +// +//=========================================================================== + +//-------------------------------------------------------------------------- +// DisplayInfoMsg() - Returns if Higher Pri message is holding. +// +// SEE MACROS: DISPLAY_TIMED_MSG() & DISPLAY_MSG() -- Def.h +// +// DISPLAY_TIMED_MSG(msg,pri,type) - For E-Z Timed Msgs (std. display time) +// DISPLAY_MSG(msg,pri,type) - For E-Z NON-Timed Msgs. +//-------------------------------------------------------------------------- +boolean DisplayInfoMsg(char far *Msg,msg_priorities Priority,short DisplayTime,short MsgType) +{ + if (Priority >= LastMsgPri) + { + if (Priority == MP_max_val) // "System" msgs + LastMsgPri = MP_min_val; + else + LastMsgPri = Priority; + +#pragma warn -pia + if (MsgTicsRemain = DisplayTime) + StatusAllDrawPic(0,40,BRI_LIGHTPIC); +#pragma warn +pia + + gamestate.msg = Msg; + + DrawInfoArea_COUNT = InitInfoArea_COUNT = 3; + + LastMsgType = MsgType; + + if (LastMsgType != MT_ATTACK) + LastInfoAttacker_Cloaked = 0; + + return(true); + } + else + return(false); +} + + +//-------------------------------------------------------------------------- +// ClearInfoArea() +//-------------------------------------------------------------------------- +void ClearInfoArea(void) +{ + unsigned i,old_ofs; + +#if IN_DEVELOPMENT + if (gamestate.flags & GS_SHOW_OVERHEAD) + return; +#endif + + if (ClearInfoArea_COUNT) + ClearInfoArea_COUNT--; + + InfoAreaSetup.x = InfoAreaSetup.left_margin; + InfoAreaSetup.y = INFOAREA_Y; + InfoAreaSetup.framecount = InfoAreaSetup.numanims = 0; + + JLatchDrawPic(0,200-STATUSLINES,INFOAREAPIC); +} + + +//-------------------------------------------------------------------------- +// InitInfoArea() +//-------------------------------------------------------------------------- +void InitInfoArea(void) +{ + InfoAreaSetup.left_margin = INFOAREA_X; + InfoAreaSetup.text_color = INFOAREA_TCOLOR; + InfoAreaSetup.backgr_color = INFOAREA_BCOLOR; + InitInfoArea_COUNT--; + + ClearInfoArea(); +} + + +//-------------------------------------------------------------------------- +// UpdateInfoArea() +//-------------------------------------------------------------------------- +void UpdateInfoArea(void) +{ + + if (InfoAreaSetup.numanims) + { + AnimatePage(); + } + + if (InitInfoArea_COUNT) + InitInfoArea(); + else + if (ClearInfoArea_COUNT) + ClearInfoArea(); + + if (DrawInfoArea_COUNT) + DrawInfoArea(); +} + +//--------------------------------------------------------------------------- +// UpdateInfoAreaClock() - This routine is called ONLY ONCE per refresh +// to update the InfoArea Clock and to release +// any messages that have expired. +//--------------------------------------------------------------------------- +void UpdateInfoAreaClock(void) +{ + + if (playstate == ex_title || playstate == ex_victorious) + return; + + // + // Check for existing timed messages + // + + if (LastMsgPri && MsgTicsRemain) + { + // + // Tic' that 'Puppy' down - Yea! + // + + if ((MsgTicsRemain -= tics) <= 0) + { + // Message has expired. + DisplayNoMoMsgs(); + } + } + +} + +//--------------------------------------------------------------------------- +// DisplayTokens() +//--------------------------------------------------------------------------- +char default_msg[] = { "\r NO MESSAGES." + "^FCA8\r FOOD TOKENS: " + " " + }; + +char needDetonator_msg[]="\r\r^FC39 FIND THE DETONATOR!"; + +char haveDetonator_msg[]="\r\r^FC39DESTROY SECURITY CUBE!"; + +char destroyGoldfire_msg[]="\r\r^FC39 DESTROY GOLDFIRE!"; + +void DisplayNoMoMsgs(void) +{ + char buffer[9]; + + LastMsgPri = MP_min_val; + + if (BONUS_QUEUE) + { + DisplayPinballBonus(); + return; + } + + MsgTicsRemain = 0; + StatusAllDrawPic (0,40,DIM_LIGHTPIC); + sprintf((char *)&default_msg[40],"%-d",gamestate.tokens); + if (gamestuff.level[gamestate.mapon+1].locked) + { + switch (gamestate.mapon) + { + case 19: + strcat(default_msg,destroyGoldfire_msg); + break; + + case 20: + case 21: + case 22: + case 23: + break; + + default: + if (gamestate.plasma_detonators) + strcat(default_msg,haveDetonator_msg); + else + strcat(default_msg,needDetonator_msg); + break; + } + } + + DisplayInfoMsg(default_msg,MP_max_val,0,MT_NOTHING); +} + +//-------------------------------------------------------------------------- +// DrawInfoArea() +// +// +// Active control codes: +// +// ^ANnn - define animation +// ^FCnn - set font color +// ^LMnnn - set left margin (if 'nnn' == "fff" uses current x) +// ^EP - end of page (waits for 'M' to read MORE) +// ^PXnnn - move x to coordinate 'n' +// ^PYnnn - move y to coordinate 'n' +// ^SHnnn - display shape 'n' at current x,y +// ^BGn - set background color +// ^DM - Default Margins +// +// Other info: +// +// All 'n' values are hex numbers (0 - f), case insensitive. +// The number of N's listed is the number of digits REQUIRED by that control +// code. (IE: ^LMnnn MUST have 3 values! --> 003, 1a2, 01f, etc...) +// +// If a line consists only of control codes, the cursor is NOT advanced +// to the next line (the ending is skipped). If just ONE non-control +// code is added, the number "8" for example, then the "8" is displayed +// and the cursor is advanced to the next line. +// +// The text presenter now handles sprites, but they are NOT masked! Also, +// sprite animations will be difficult to implement unless all frames are +// of the same dimensions. +// +//-------------------------------------------------------------------------- + +char far *HandleControlCodes(char far *first_ch); + + +void DrawInfoArea(void) +{ + #define IA_FONT_HEIGHT 6 + +// short length,i; + char far *first_ch; + char far *scan_ch,temp; + unsigned old_ofs; + +#if IN_DEVELOPMENT + if (gamestate.flags & GS_SHOW_OVERHEAD) + return; +#endif + + DrawInfoArea_COUNT--; + + if (!*gamestate.msg) + return; + + first_ch = gamestate.msg; + + fontnumber = 2; + fontcolor = InfoAreaSetup.text_color; + + while (first_ch && *first_ch) + { + + if (*first_ch != TP_CONTROL_CHAR) + { + scan_ch = first_ch; + + while ((*scan_ch) && (*scan_ch != '\n') && (*scan_ch != TP_RETURN_CHAR) && (*scan_ch != TP_CONTROL_CHAR)) + scan_ch++; + + // print current line + // + + temp = *scan_ch; + *scan_ch = 0; + + if (*first_ch != TP_RETURN_CHAR) + { + int i; + char temp_color; + + temp_color = fontcolor; + fontcolor = INFOAREA_TSHAD_COLOR; + + px = InfoAreaSetup.x+1; + py = InfoAreaSetup.y+1; + VW_DrawPropString(first_ch); + fontcolor = temp_color; + + px = InfoAreaSetup.x; + py = InfoAreaSetup.y; + VW_DrawPropString(first_ch); + } + + *scan_ch = temp; + first_ch = scan_ch; + + // skip SPACES / RETURNS at end of line + // + + if ((*first_ch==' ') || (*first_ch==TP_RETURN_CHAR)) + first_ch++; + + // TP_CONTROL_CHARs don't advance to next character line + // + + if (*scan_ch != TP_CONTROL_CHAR) + { + InfoAreaSetup.x = InfoAreaSetup.left_margin; + InfoAreaSetup.y += IA_FONT_HEIGHT; + } + else + InfoAreaSetup.x = px; + } + else + first_ch = HandleControlCodes(first_ch); + } +} + +//--------------------------------------------------------------------------- +// HandleControlCodes() +//--------------------------------------------------------------------------- +char far *HandleControlCodes(char far *first_ch) +{ + spritetabletype far *spr; +// piShapeInfo far *shape_info; + piShapeInfo far *shape; + piAnimInfo far *anim; + unsigned shapenum; + short length,width; + char far *s; + + first_ch++; + +#ifndef TP_CASE_SENSITIVE + *first_ch=toupper(*first_ch); + *(first_ch+1)=toupper(*(first_ch+1)); +#endif + + switch (*((unsigned far *)first_ch)++) + { + + // INIT ANIMATION --------------------------------------------------- + // + case TP_CNVT_CODE('A','N'): + shapenum = TP_VALUE(first_ch,2); + first_ch += 2; + _fmemcpy(&piAnimList[InfoAreaSetup.numanims],&piAnimTable[shapenum],sizeof(piAnimInfo)); + anim = &piAnimList[InfoAreaSetup.numanims++]; + shape = &piShapeTable[anim->baseshape+anim->frame]; // BUG!! (assumes "pia_shapetable") +// spr = &spritetable[shape->shapenum-STARTSPRITES]; + + anim->y=InfoAreaSetup.y; + anim->x=DrawShape(InfoAreaSetup.x,InfoAreaSetup.y,shape->shapenum,shape->shapetype); + InfoAreaSetup.framecount = 3; + InfoAreaSetup.left_margin = InfoAreaSetup.x; + break; + + // DRAW SHAPE ------------------------------------------------------- + // + case TP_CNVT_CODE('S','H'): + + // NOTE : This needs to handle the left margin.... + + shapenum = TP_VALUE(first_ch,3); + first_ch += 3; + shape = &piShapeTable[shapenum]; +// spr = &spritetable[shape->shapenum-STARTSPRITES]; + + DrawShape(InfoAreaSetup.x,InfoAreaSetup.y,shape->shapenum,shape->shapetype); + InfoAreaSetup.left_margin = InfoAreaSetup.x; + break; + + // FONT COLOR ------------------------------------------------------- + // + case TP_CNVT_CODE('F','C'): + InfoAreaSetup.text_color = fontcolor = TP_VALUE(first_ch,2); + first_ch += 2; + break; + + // BACKGROUND COLOR ------------------------------------------------- + // + case TP_CNVT_CODE('B','G'): + InfoAreaSetup.backgr_color = TP_VALUE(first_ch,2); + first_ch += 2; + break; + + // DEFAULT MARGINS ------------------------------------------------- + // + case TP_CNVT_CODE('D','M'): + InfoAreaSetup.left_margin = INFOAREA_X; + break; + + // LEFT MARGIN ------------------------------------------------------ + // + case TP_CNVT_CODE('L','M'): + shapenum = TP_VALUE(first_ch,3); + first_ch += 3; + if (shapenum == 0xfff) + InfoAreaSetup.left_margin = InfoAreaSetup.x; + else + InfoAreaSetup.left_margin = shapenum; + break; + +#ifdef UNLOCK_FLOORS + // UNLOCK FLOOR ---------------------------------------------------- + // + case TP_CNVT_CODE('U','F'): + shapenum = TP_VALUE(first_ch++,1); + gamestuff.level[shapenum].locked=false; + break; +#endif + } + + return(first_ch); + +} + +//-------------------------------------------------------------------------- +// DrawShape() +//-------------------------------------------------------------------------- +short DrawShape(short x, short y, short shapenum, pisType shapetype) +{ + short width; + unsigned i,old_ofs,shade; + +// width=TP_BoxAroundShape(x,y,shapenum,shapetype); + + // + // If Image is Cloaked... Shade the image + // + if (LastInfoAttacker_Cloaked) + shade = 35; // 63 == BLACK | 0 == NO SHADING + else + shade = 0; + + switch (shapetype) + { + case pis_scaled: +// old_ofs = bufferofs; +// for (i=0;i<3;i++) +// { +// bufferofs = screenloc[i]; +// VWB_Bar(x,y,37,37,InfoAreaSetup.backgr_color); + VW_Bar(x,y,37,37,InfoAreaSetup.backgr_color); // JTR changed + MegaSimpleScaleShape(x+19,y+20,shapenum,37,shade); +// } +// bufferofs = old_ofs; + width = 37; + break; + +#if NUMPICS + case pis_latchpic: + x = (x+7) & 0xFFF8; +// old_ofs = bufferofs; +// for (i=0;i<3;i++) +// { +// bufferofs = screenloc[i]; + JLatchDrawPic(x>>3,y,shapenum); +// } +// bufferofs = old_ofs; + break; + + case pis_pic: + x = (x+7) & 0xFFF8; + width = pictable[shapenum-STARTPICS].width; + CA_MarkGrChunk(shapenum); + CA_CacheMarks(); +// old_ofs = bufferofs; +// for (i=0;i<3;i++) +// { +// bufferofs = screenloc[i]; + VWB_DrawPic(x,y,shapenum); +// } +// bufferofs = old_ofs; + UNCACHEGRCHUNK(shapenum); + break; +#endif + +#if NUMSPRITES && 0 + case pis_sprite: +// VW_geDrawSprite(x,y-(spr->orgy>>G_P_SHIFT),shapenum,shapetype == pis_sprite2x); + break; +#endif + } + + InfoAreaSetup.x += width; + return(x); +} + +//-------------------------------------------------------------------------- +// AnimatePage() +//-------------------------------------------------------------------------- +void AnimatePage(void) +{ + piAnimInfo far *anim=piAnimList; + piShapeInfo far *shape; + + // Dec Timers + // + + anim->delay += tics; + + if (anim->delay >= anim->maxdelay) + { + InfoAreaSetup.framecount = 3; + anim->delay = 0; + } + + // Test framecount - Do we need to draw a shape? + // + + if (InfoAreaSetup.framecount) + { + // Draw shapes + + switch (anim->animtype) + { + case pia_shapetable: + shape = &piShapeTable[anim->baseshape+anim->frame]; + DrawShape(anim->x,anim->y,shape->shapenum,shape->shapetype); + break; + + case pia_grabscript: + shape = &piShapeTable[anim->baseshape]; + DrawShape(anim->x,anim->y,shape->shapenum+anim->frame,shape->shapetype); + break; + } + + // Dec frame count + + InfoAreaSetup.framecount--; + if (!InfoAreaSetup.framecount) + { + // Have drawn all pages... Inc Frame count + + anim->frame++; + if (anim->frame == anim->maxframes) + anim->frame = 0; + } + } + +} + +#if 0 + +//-------------------------------------------------------------------------- +// AnimatePage() +//-------------------------------------------------------------------------- +void AnimatePage(short numanims) +{ + piAnimInfo far *anim=piAnimList; + piShapeInfo far *shape; + + anim->delay += tics; + + if (anim->delay >= anim->maxdelay) + { + anim->delay = 0; + anim->frame++; + + if (anim->frame == anim->maxframes) + anim->frame = 0; + + switch (anim->animtype) + { + case pia_shapetable: + shape = &piShapeTable[anim->baseshape+anim->frame]; + DrawShape(anim->x,anim->y,shape->shapenum,shape->shapetype); + break; + + case pia_grabscript: + shape = &piShapeTable[anim->baseshape]; + DrawShape(anim->x,anim->y,shape->shapenum+anim->frame,shape->shapetype); + break; + } + } +} + +#endif + +//=========================================================================== +// +// +// STATUS BAR REFRESH ROUTINES +// +// +//=========================================================================== + + +//--------------------------------------------------------------------------- +// UpdateStatusBar() +//--------------------------------------------------------------------------- +void UpdateStatusBar(void) +{ + if (playstate == ex_title || playstate == ex_victorious) + return; + +#ifdef NO_STATUS_BAR + return; +#endif + + + // + // Call specific status bar managers + // + + UpdateScore(); + UpdateInfoArea(); + + // + // Refresh Status Area + // + + if (DrawAmmoPic_COUNT) + DrawAmmoPic(); + +// if (DrawScoreNum_COUNT) + DrawScoreNum(); + + if (DrawWeaponPic_COUNT) + DrawWeaponPic(); + + if (DrawRadarGuage_COUNT) + DrawRadarGuage(); + +// if (DrawAmmoNum_COUNT) + DrawAmmoNum(); + + if (DrawKeyPics_COUNT) + DrawKeyPics(); + + if (DrawHealthNum_COUNT) + DrawHealthNum(); + + if (gamestate.flags & (GS_TICS_FOR_SCORE)) + DrawScore(); + +} + +//--------------------------------------------------------------------------- +// ForceUpdateStatusBar() - Force Draw status bar onto ALL display pages +//--------------------------------------------------------------------------- +void ForceUpdateStatusBar(void) +{ + unsigned old_ofs,i; + + old_ofs = bufferofs; + + DrawScore(); + DrawWeapon(); + DrawKeys(); + DrawHealth(); + UpdateRadarGuage(); + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + UpdateStatusBar(); + } + + bufferofs = old_ofs; +} + + +/* +============================================================================= + + MOVEMENT + +============================================================================= +*/ + + + +/* +=================== += += GetBonus += +=================== +*/ + +unsigned far static_points[]={ 100, // money bag + 500, // loot + 250, // gold1 + 500, // gold2 + 750, // gold3 + 1000, // major gold! + 5000 // bonus +}; + +unsigned far static_health[][3] = +{ + {100,HEALTH2SND,-1}, // Full Heal + { 30,HEALTH1SND,-1}, // First Aid + { 20,HEALTH1SND,SPR_STAT_45}, // Steak + { 15,HEALTH1SND,SPR_STAT_43}, // Chicken Leg + { 10,HEALTH1SND,SPR_SANDWICH_WRAPER}, // Sandwich + { 8,HEALTH1SND,SPR_CANDY_WRAPER}, // Candy Bar + { 5,HEALTH1SND,SPR_STAT_41}, // Water bowl + { 5,HEALTH1SND,-1}, // Water puddle +}; + +extern char far bonus_msg24[]; +extern char far bonus_msg25[]; + +void GetBonus (statobj_t *check) +{ + boolean givepoints=false; + short shapenum = -1,possible; + + switch (check->itemnumber) + { + case bo_red_key: + case bo_yellow_key: + case bo_blue_key: + { + unsigned keynum = check->itemnumber - bo_red_key; + + if (gamestate.numkeys[keynum] >= MAXKEYS) + return; + + GiveKey(keynum); + SD_PlaySound(GETKEYSND); + TravelTable[check->tilex][check->tiley] &= ~TT_KEYS; + break; + } + + case bo_money_bag: + SD_PlaySound (BONUS1SND); + givepoints=true; + break; + + case bo_loot: + SD_PlaySound (BONUS2SND); + givepoints=true; + break; + + + case bo_gold1: + case bo_gold2: + case bo_gold3: + case bo_gold: + SD_PlaySound (BONUS3SND); + givepoints=true; + break; + + + case bo_bonus: + SD_PlaySound (BONUS4SND); + givepoints=true; + break; + + case bo_water_puddle: + if (gamestate.health > 15) + return; + case bo_fullheal: + case bo_firstaid: + case bo_ham: // STEAK + case bo_chicken: + case bo_sandwich: + case bo_candybar: + case bo_water: + if (gamestate.health == 100) + return; + SD_PlaySound (static_health[check->itemnumber-bo_fullheal][1]); + HealSelf (static_health[check->itemnumber-bo_fullheal][0]); + check->flags &= ~FL_BONUS; + shapenum = static_health[check->itemnumber-bo_fullheal][2]; + break; + + case bo_clip: + if (gamestate.ammo == MAX_AMMO) + return; + GiveAmmo (8); + bonus_msg7[45] = '8'; + break; + + case bo_clip2: + { + unsigned char ammo; + + if (gamestate.ammo == MAX_AMMO) + return; + + ammo = 1+(US_RndT() & 0x7); + bonus_msg7[45] = '0'+ammo; + GiveAmmo (ammo); + } + break; + + case bo_plasma_detonator: + TravelTable[check->tilex][check->tiley] &= ~TT_KEYS; + GivePlasmaDetonator(1); + SD_PlaySound (GETDETONATORSND); + break; + + case bo_pistol: + SD_PlaySound (GETPISTOLSND); + GiveWeapon(wp_pistol); + break; + + case bo_burst_rifle: + SD_PlaySound (GETBURSTRIFLESND); + GiveWeapon (wp_burst_rifle); + break; + + case bo_ion_cannon: + SD_PlaySound (GETIONCANNONSND); + GiveWeapon (wp_ion_cannon); + break; + + case bo_grenade: + SD_PlaySound (GETCANNONSND); + GiveWeapon (wp_grenade); + break; + + + case bo_bfg_cannon: + SD_PlaySound (GETCANNONSND); + GiveWeapon (wp_bfg_cannon); + break; + + + case bo_coin: + if (gamestate.tokens == MAX_TOKENS) + return; + GiveToken(1); + + writeTokenStr(bonus_msg24); + break; + + case bo_coin5: + if (gamestate.tokens == MAX_TOKENS) + return; + GiveToken(5); + + writeTokenStr(bonus_msg25); + break; + + case bo_automapper1: + if (gamestate.rpower > MAX_RADAR_ENERGY-(RADAR_PAK_VALUE/8)) + return; + gamestate.rpower += RADAR_PAK_VALUE; + SD_PlaySound(RADAR_POWERUPSND); + UpdateRadarGuage(); + break; + } + + if (givepoints) + { + GivePoints(static_points[check->itemnumber-bo_money_bag],true); +#if IN_DEVELOPMENT +#ifdef DEBUG_STATICS + debug_bonus[1][db_count++] = static_points[check->itemnumber-bo_money_bag]; +#endif +#endif + } + + DISPLAY_TIMED_MSG(BonusMsg[check->itemnumber-1],MP_BONUS,MT_BONUS); + StartBonusFlash (); + check->shapenum = shapenum; // remove from list if shapenum == -1 + check->itemnumber = bo_nothing; +} + +void writeTokenStr(char far *str) +{ + char buffer[3],len; + + len = _fstrlen(str); + if (gamestate.tokens > 9) + itoa(gamestate.tokens,buffer,10); + else + { + buffer[0]='0'; + itoa(gamestate.tokens,buffer+1,10); + } + + _fstrcpy(str+len-2,buffer); +} + + +/* +=================== += += TryMove += += returns true if move ok += debug: use pointers to optimize +=================== +*/ + + +boolean TryMove (objtype *ob) +{ + int xl,yl,xh,yh,x,y,xx,yy; + objtype *check; + long deltax,deltay; + + if (ob==player) + { + xl = (ob->x-PLAYERSIZE) >>TILESHIFT; + yl = (ob->y-PLAYERSIZE) >>TILESHIFT; + xh = (ob->x+PLAYERSIZE) >>TILESHIFT; + yh = (ob->y+PLAYERSIZE) >>TILESHIFT; + } + else + { + if (ob->obclass == blakeobj) + { + xl = (ob->x-(0x1000l)) >>TILESHIFT; + yl = (ob->y-(0x1000l)) >>TILESHIFT; + xh = (ob->x+(0x1000l)) >>TILESHIFT; + yh = (ob->y+(0x1000l)) >>TILESHIFT; + } + else + { + xl = (ob->x-(0x7FFFl)) >>TILESHIFT; + yl = (ob->y-(0x7FFFl)) >>TILESHIFT; + xh = (ob->x+(0x7FFFl)) >>TILESHIFT; + yh = (ob->y+(0x7FFFl)) >>TILESHIFT; + } + } + + +// +// check for solid walls +// +#pragma warn -pia + + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + if (check = actorat[x][y]) + if ((check < objlist) || (check->flags & FL_FAKE_STATIC)) + return(false); + } + +#pragma warn +pia + +// +// check for actors.... +// + + yl-=2; + yh+=2; + xl-=2; + xh+=2; + + // NOTE: xl,yl may go NEGITIVE! + // ---- xh,yh may exceed 63 (MAPWIDTH-1) + + for (y=yl;y<=yh;y++) + for (x=xl;x<=xh;x++) + { + xx = x & 0x3f; + + yy = y & 0x3f; + + check = actorat[xx][yy]; + + if ((check > objlist) && ((check->flags & (FL_SOLID|FL_FAKE_STATIC)) == FL_SOLID)) + { + 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 += += returns true if object hit a wall += +=================== +*/ + +boolean 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(false); + +#if (!BETA_TEST) && IN_DEVELOPMENT + if ((!(gamestate.flags & GS_CLIP_WALLS)) && (ob == player)) + return(true); +#endif + + if (!SD_SoundPlaying()) + SD_PlaySound (HITWALLSND); + + ob->x = (basex+xmove); + ob->y = basey; + + if (TryMove (ob)) + return(true); + + ob->x = basex; + ob->y = (basey+ymove); + + + if (TryMove (ob)) + return(true); + + ob->x = basex; + ob->y = basey; + + return(true); +} + +//========================================================================== + +/* +=================== += += Thrust += +=================== +*/ + +void Thrust (int angle, long speed) +{ + extern byte far TravelTable[MAPSIZE][MAPSIZE]; + objtype dumb; + long xmove,ymove; + long slowmax; + unsigned offset, far *map[2]; + short dx,dy; + int dangle; + boolean ignore_map1; + + 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_oldtilex = player->tilex; + player_oldtiley = player->tiley; + player->tilex = player->x >> TILESHIFT; // scale to tile values + player->tiley = player->y >> TILESHIFT; + + player->areanumber=GetAreaNumber(player->tilex,player->tiley); + areabyplayer[player->areanumber] = true; + TravelTable[player->tilex][player->tiley] |= TT_TRAVELED; + + offset = farmapylookup[player->tiley]+player->tilex; + map[0]=mapsegs[0]+offset; + map[1]=mapsegs[1]+offset; + +// Check for trigger tiles. +// + switch (*map[0]) + { + case DOORTRIGGERTILE: + dx = *map[1]>>8; // x + dy = *map[1]&255; // y + if (OperateSmartSwitch(dx,dy,ST_TOGGLE,false)) // Operate & Check for removeal + *map[0] = AREATILE+player->areanumber; // Remove switch + ignore_map1 = true; + break; + + case SMART_OFF_TRIGGER: + case SMART_ON_TRIGGER: + dx = *map[1]>>8; + dy = *map[1]&255; + OperateSmartSwitch(dx,dy,(*map[0])-SMART_OFF_TRIGGER,false); + ignore_map1 = true; + break; + + case WINTIGGERTILE: + playstate = ex_victorious; + dumb.x = ((long)gamestate.wintilex<>8; + BottomColor = offset&0xff; + BottomColor |= BottomColor<<8; + break; +#else +#if IN_DEVELOPMENT + case 0xfe00: + // Give error + break; +#endif +#endif + +#if 0 + case 0xF600: // Lighting effects + normalshade_div = (offset&0xff00) >> 8; + if (normalshade_div > 12) + AGENT_ERROR(NORMAL_SHADE_TOO_BIG); + shade_max = offset&0xff; + if (shade_max > 63 || shade_max < 5) + AGENT_ERROR(SHADEMAX_VALUE_BAD); + normalshade=(3*(maxscale>>2))/normalshade_div; + break; +#endif + } + } + +} + +extern short an_offset[]; + +#pragma warn -pia + +boolean GAN_HiddenArea; + +//------------------------------------------------------------------------ +// GetAreaNumber() +//------------------------------------------------------------------------ +char GetAreaNumber(char tilex, char tiley) +{ + unsigned offset, far *map, far *ptr[2], loop; + byte areanumber; + + GAN_HiddenArea = false; + +// Are we on a wall? +// + if (tilemap[tilex][tiley] && (!(tilemap[tilex][tiley]&0xc0))) + return(127); + +// Get initial areanumber from map +// + offset = farmapylookup[tiley]+tilex; + ptr[0]=mapsegs[0]+offset; + ptr[1]=mapsegs[1]+offset; + +// Special tile areas must use a valid areanumber tile around it. +// + if (!(areanumber=ValidAreaTile(ptr[0]))) + { + for (loop=0; loop<8; loop++) + if (areanumber=ValidAreaTile(ptr[0]+an_offset[loop])) + break; + + if (loop==8) + areanumber = AREATILE; + } + +// Merge hidden areanumbers into non-hidden areanumbers AND pull all +// values down to an indexable range. +// + if (areanumber >= HIDDENAREATILE) + { + GAN_HiddenArea = true; + areanumber -= HIDDENAREATILE; + } + else + areanumber -= AREATILE; + + return(areanumber); +} + +#pragma warn +pia + +#pragma warn -rch +#pragma warn -rvl + +//------------------------------------------------------------------------ +// ValidAreaTile() +//------------------------------------------------------------------------ +byte ValidAreaTile(unsigned far *ptr) +{ + switch (*ptr) + { + case AREATILE: + case HIDDENAREATILE: + case DOORTRIGGERTILE: + case WINTIGGERTILE: + case SMART_ON_TRIGGER: + case SMART_OFF_TRIGGER: + case AMBUSHTILE: + case LINC_TILE: + case CLOAK_AMBUSH_TILE: + break; + + default: + if (*ptr > AREATILE) + return(*ptr); + break; + } + + return(0); +} + +#pragma warn +rch +#pragma warn +rvl + +/* +============================================================================= + + ACTIONS + +============================================================================= +*/ + + +/* +=============== += += Cmd_Fire += +=============== +*/ + +void Cmd_Fire (void) +{ + if (noShots) + return; + + if ((gamestate.weapon == wp_autocharge) && (gamestate.weapon_wait)) + return; + + 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; +} + +//=========================================================================== + + +void Cmd_Use (void) +{ + objtype *check; + int checkx,checky,doornum,dir; + unsigned iconnum; + unsigned offset,new_level; + unsigned char static interrogate_delay=0; + boolean tryDetonator = false; + +// 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; + } + else if (player->angle < 3*ANGLES/8) + { + checkx = player->tilex; + checky = player->tiley-1; + dir = di_north; + } + else if (player->angle < 5*ANGLES/8) + { + checkx = player->tilex - 1; + checky = player->tiley; + dir = di_west; + } + else + { + checkx = player->tilex; + checky = player->tiley + 1; + dir = di_south; + } + + doornum = tilemap[checkx][checky]; + iconnum = *(mapsegs[1]+farmapylookup[checky]+checkx); + +// Test for a pushable wall +// + if (iconnum == PUSHABLETILE) + { + PushWall (checkx,checky,dir); + } + else + if (!buttonheld[bt_use]) + { + // Test for doors / elevator + // + if ((doornum & 0x80) && ((pwallx != checkx) || (pwally != checky))) + { + buttonheld[bt_use] = true; + OperateDoor(doornum & ~0x80); + } + else + // Test for special tile types... + // + switch (doornum & 63) + { + // Test for 'display elevator buttons' + // + case TRANSPORTERTILE: + { + short new_floor; + + if ((new_floor=InputFloor()) != -1 && new_floor != gamestate.mapon) + { + int angle = player->angle; + + gamestuff.level[gamestate.mapon].ptilex = player->tilex; + gamestuff.level[gamestate.mapon].ptiley = player->tiley; + angle = player->angle - 180; + if (angle < 0) + angle += ANGLES; + gamestuff.level[gamestate.mapon].pangle = angle; + + playstate=ex_transported; + gamestate.lastmapon=gamestate.mapon; + gamestate.mapon=new_floor-1; + } + else + DrawPlayScreen(false); + } + break; + + case DIRECTTRANSPORTTILE: + switch (iconnum & 0xff00) + { + case 0xf400: + playstate = ex_transported; + gamestate.lastmapon=gamestate.mapon; + gamestate.mapon=(iconnum & 0xff)-1; + break; + + default: + // Stay in current level warp to new location + + playstate = ex_transported; + Warped(); + playstate = ex_stillplaying; + + player->tilex = (iconnum >> 8); + player->tiley = iconnum & 0xff; + player->x = ((long)player->tilex<y = ((long)player->tiley<tilex+x][player->tiley+y]) && + (actorat[player->tilex+x][player->tiley+y] >= objlist)) + ob = actorat[player->tilex+x][player->tiley+y]; + else + continue; + dx = player->x - ob->x; + dx = LABS(dx); + dy = player->y - ob->y; + dy = LABS(dy); + dist = dxobclass==gen_scientistobj) && + ((ob->flags&(FL_FRIENDLY|FL_VISABLE))==(FL_FRIENDLY|FL_VISABLE)) && + (dist < intg_dist)) + { + short angle=CalcAngle(player,ob); + + angle = ABS(player->angle-angle); + if (angle > INTG_ANGLE/2) + continue; + + intg_ob=ob; + intg_dist=dist; + } + } + + if (intg_ob) + { + if (Interrogate(intg_ob)) + interrogate_delay=20; // Informants have 1/3 sec delay + else + interrogate_delay=120; // Non-informants have 2 sec delay + } + else + tryDetonator = true; + } + else + { + if (tics < interrogate_delay) + interrogate_delay-=tics; + else + interrogate_delay=0; + + tryDetonator = true; + } + + if (tryDetonator) + { + if ((!tryDetonatorDelay) && gamestate.plasma_detonators) + { + TryDropPlasmaDetonator(); + tryDetonatorDelay = 60; + } + } + else + tryDetonatorDelay = 60; + + if (!buttonheld[bt_use]) + interrogate_delay=0; +} + +//========================================================================== +// +// INTERROGATE CODE +// +//========================================================================== + +#define MSG_BUFFER_LEN 150 + +char far msg[MSG_BUFFER_LEN+1]; + +memptr InfAreaMsgs[MAX_INF_AREA_MSGS]; +byte NumAreaMsgs,LastInfArea; +short FirstGenInfMsg,TotalGenInfMsgs; + +scientist_t InfHintList; // Informant messages +scientist_t NiceSciList; // Non-informant, non-pissed messages +scientist_t MeanSciList; // Non-informant, pissed messages + +char far int_interrogate[]="INTERROGATE:", + far int_informant[]=" ^FC3aINFORMANT^FCa6", + far int_rr[]="\r\r", + far int_xx[]="^XX", + far int_haveammo[]=" HEY BLAKE,\r TAKE MY CHARGE PACK!", + far int_havetoken[]=" HEY BLAKE,\r TAKE MY FOOD TOKENS!"; + +//-------------------------------------------------------------------------- +// Interrogate() +//-------------------------------------------------------------------------- +boolean Interrogate(objtype *ob) +{ + boolean rt_value=true; + char far *msgptr=NULL; + + _fstrcpy(msg,int_interrogate); + + if (ob->flags & FL_INFORMANT) // Informant + { + short msgnum; + + _fstrcat(msg,int_informant); + + if (ob->flags & FL_INTERROGATED) + { + if ((ob->flags & FL_HAS_AMMO) && (gamestate.ammo != MAX_AMMO)) + { + GiveAmmo((US_RndT()%8)+1); + ob->flags &= ~FL_HAS_AMMO; + msgptr=int_haveammo; + } + else + if ((ob->flags & FL_HAS_TOKENS) && (gamestate.tokens != MAX_TOKENS)) + { + GiveToken(5); + ob->flags &= ~FL_HAS_TOKENS; + msgptr=int_havetoken; + } + } + + if (!msgptr) + { + // If new areanumber OR no 'area msgs' have been compiled, compile + // a list of all special messages for this areanumber. + // + if ((LastInfArea==0xff) || (LastInfArea!=ob->areanumber)) + { + sci_mCacheInfo *ci = InfHintList.smInfo; + + NumAreaMsgs=0; + for (;ci->areanumber != 0xff;ci++) + if (ci->areanumber == ob->areanumber) + InfAreaMsgs[NumAreaMsgs++]=InfHintList.smInfo[ci->mInfo.local_val].mInfo.mSeg; + + LastInfArea=ob->areanumber; + } + + // Randomly select an informant hint, either: specific to areanumber + // or general hint... + // + if (NumAreaMsgs) + { + if (ob->ammo != ob->areanumber) + ob->s_tilex = 0xff; + ob->ammo = ob->areanumber; + if (ob->s_tilex == 0xff) + ob->s_tilex=Random(NumAreaMsgs); + msgptr=InfAreaMsgs[ob->s_tilex]; + } + else + { + if (ob->s_tiley == 0xff) + ob->s_tiley=FirstGenInfMsg+Random(TotalGenInfMsgs); + msgptr=InfHintList.smInfo[ob->s_tiley].mInfo.mSeg; + } + + // Still no msgptr? This is a shared message! Use smInfo[local_val] + // for this message. + // + if (!msgptr) + msgptr=InfHintList.smInfo[InfHintList.smInfo[ob->s_tiley].mInfo.local_val].mInfo.mSeg; + + ob->flags |= FL_INTERROGATED; // Scientist has been interrogated + } + } + else // Non-Informant + { + scientist_t *st; + + rt_value=false; + if ((ob->flags & FL_MUST_ATTACK) || (US_RndT()&1)) // Mean + { + ob->flags &= ~FL_FRIENDLY; // Make him attack! + ob->flags |= FL_INTERROGATED; // " " " + st = &MeanSciList; + } + else // Nice + { + ob->flags |= FL_MUST_ATTACK; // Make him mean! + st = &NiceSciList; + } + + msgptr=st->smInfo[Random(st->NumMsgs)].mInfo.mSeg; + } + + if (msgptr) + { + _fstrcat(msg,int_rr); + _fstrcat(msg,msgptr); + _fstrcat(msg,int_xx); + if (_fstrlen(msg) > MSG_BUFFER_LEN) + AGENT_ERROR(INTERROGATE_LONG_MSG); + DisplayInfoMsg(msg,MP_INTERROGATE,DISPLAY_MSG_STD_TIME*2,MT_GENERAL); + SD_PlaySound(INTERROGATESND); + } + + return(rt_value); +} + +//========================================================================== +// +// ELEVATOR CODE +// +//========================================================================== + + +extern boolean pollMouseUsed; + +char far if_help[]="UP/DN MOVES SELECTOR - ENTER ACTIVATES"; +char far if_noImage[]=" AREA\n" + " UNMAPPED\n" + "\n" + "\n" + " PRESS ENTER\n" + " TO TELEPORT"; + +statsInfoType ov_stats; +memptr ov_buffer; +boolean ov_noImage=false; + +#define TOV_X 16 +#define TOV_Y 132 + +//-------------------------------------------------------------------------- +// InputFloor +//-------------------------------------------------------------------------- +short InputFloor(void) +{ + #define RADAR_FLAGS OV_KEYS + #define MAX_TELEPORTS 20 + #define MAX_MOVE_DELAY 10 + + short buttonPic,buttonY; + short rt_code=-2,tpNum=gamestate.mapon,lastTpNum=tpNum; + short teleX[MAX_TELEPORTS]={16,40,86,23,44,62,83,27,118,161,161,161,213,213,184,205,226,256,276,276}; + short teleY[MAX_TELEPORTS]={13,26, 9,50,50,50,50,62, 42, 17, 26, 35, 41, 50, 62, 62, 62, 10, 10, 30}; + char moveActive=0; + objtype old_player; + boolean locked=false,buttonsDrawn=false; + + ClearMemory(); + VW_FadeOut(); + + CacheDrawPic(0,0,TELEPORTBACKTOPPIC); + CacheDrawPic(0,12*8,TELEPORTBACKBOTPIC); + DisplayTeleportName(tpNum,locked); + CacheLump(TELEPORT_LUMP_START,TELEPORT_LUMP_END); + VWB_DrawMPic(teleX[tpNum],teleY[tpNum],TELEPORT1ONPIC+tpNum); + + memcpy(&old_player,player,sizeof(objtype)); + player->angle = 90; + player->x = player->y = ((long)32<0 || controly>0) + { + if (!moveActive && tpNum=0; buttonY -= 2) + { + char shps[]={TELEPORT1ONPIC,TELEPORT1OFFPIC}; + + if (rt_code != -1) + VWB_DrawMPic(teleX[tpNum],teleY[tpNum],shps[(buttonY&4)>>2]+tpNum); + + if (locked) + { + ShowOverhead(TOV_X,TOV_Y,32,-locked,RADAR_FLAGS); + VW_MarkUpdateBlock(TOV_X,TOV_Y,79,195); + } + + CycleColors(); + VL_SetPaletteIntensity(0,255,&vgapal,buttonY); + VW_UpdateScreen(); + } +#else + VW_FadeOut(); +#endif + + MM_FreePtr(&ov_buffer); + memcpy(player,&old_player,sizeof(objtype)); + UnCacheLump(TELEPORT_LUMP_START,TELEPORT_LUMP_END); + + PM_CheckMainMem(); + DrawPlayScreen(false); + IN_ClearKeysDown(); + + return(rt_code); +} + +//-------------------------------------------------------------------------- +// ShowOverheadChunk() +//-------------------------------------------------------------------------- +void ShowOverheadChunk(void) +{ + VL_MemToScreen(ov_buffer,64,64,TOV_X,TOV_Y); + VW_MarkUpdateBlock(TOV_X,TOV_Y,79,195); + ShowStats(235,138,ss_quick,&ov_stats); +} + +//-------------------------------------------------------------------------- +// LoadOverheadChunk() +//-------------------------------------------------------------------------- +void LoadOverheadChunk(short tpNum) +{ + int handle; + long offset; + char chunk[5]="OVxx"; + +// Open PLAYTEMP file +// + MakeDestPath(PLAYTEMP_FILE); + if ((handle=open(tempPath,O_CREAT|O_RDWR|O_BINARY,S_IREAD|S_IWRITE))==-1) + MAIN_ERROR(SAVELEVEL_DISKERR); + +// Find and load chunk +// + sprintf(&chunk[2],"%02x",tpNum); + if (FindChunk(handle,chunk)) + { + ov_noImage=false; + IO_FarRead(handle,ov_buffer,4096); + IO_FarRead(handle,(char far *)&ov_stats,sizeof(statsInfoType)); + } + else + { + ov_noImage=true; + _fmemset(ov_buffer,0x52,4096); + memset(&ov_stats,0,sizeof(statsInfoType)); + } + +// Close file +// + close(handle); +} + +//-------------------------------------------------------------------------- +// SaveOverheadChunk() +//-------------------------------------------------------------------------- +void SaveOverheadChunk(short tpNum) +{ + int handle; + long cksize=4096+sizeof(statsInfoType); + char chunk[5]="OVxx"; + +// Open PLAYTEMP file +// + MakeDestPath(PLAYTEMP_FILE); + if ((handle=open(tempPath,O_CREAT|O_RDWR|O_BINARY,S_IREAD|S_IWRITE))==-1) + MAIN_ERROR(SAVELEVEL_DISKERR); + +// Remove level chunk from file +// + sprintf(&chunk[2],"%02x",tpNum); + DeleteChunk(handle,chunk); + +// Prepare buffer +// + VL_ScreenToMem(ov_buffer, 64, 64, TOV_X, TOV_Y); + +// Write chunk ID, SIZE, and IMAGE +// + write(handle,chunk,4); + IO_FarWrite(handle,(char far *)&cksize,sizeof(cksize)); + IO_FarWrite(handle,ov_buffer,4096); + IO_FarWrite(handle,(char far *)&ov_stats,sizeof(statsInfoType)); + +// Close file +// + close(handle); +} + +//-------------------------------------------------------------------------- +// DisplayTeleportName() +//-------------------------------------------------------------------------- +void DisplayTeleportName(char tpNum, boolean locked) +{ + char far *s; + word w,h; + + if (locked) + { + fontcolor = 0xf5; + s = "-- TELEPORT DISABLED --"; + } + else + { + fontcolor = 0x57; + LoadLocationText(tpNum); + s = LocationText; + } + VW_MeasurePropString(s,&w,&h); + py = 103; + px = 160-w/2; + VW_Bar(54,101,212,9,0x52); + VW_MarkUpdateBlock(54,101,265,108); + ShPrint(s,0,false); +} + +//-------------------------------------------------------------------------- +// CacheDrawPic() +//-------------------------------------------------------------------------- +void CacheDrawPic(short x, short y, short pic) +{ + CA_CacheGrChunk(pic); + VWB_DrawPic(x,y,pic); + UNCACHEGRCHUNK(pic); +} + +//=========================================================================== +// +// MISSION STATISTICS CODE +// +//=========================================================================== + + #define BAR_W 48 + #define BAR_H 5 + + #define BAR1_COLOR 0xe0 + #define BAR2_COLOR 0x30 + #define BAR3_COLOR 0x10 + + #define PERC_W 13 + #define PERC_H 5 + +boolean show_stats_quick; + +//-------------------------------------------------------------------------- +// ShowStats() +//-------------------------------------------------------------------------- +short ShowStats(short bx, short by, ss_type type, statsInfoType far *stats) +{ + short floor,total=0,mission=0,p1,p2,p3,loop,maxPerFloor; + +// Define max points per floor... +// + if (stats->total_points || stats->total_inf || stats->total_enemy) + maxPerFloor = 300; + else + maxPerFloor = 0; + +// Setup to test for bypassing stats. +// + LastScan=0; + + if (type == ss_quick) + show_stats_quick=true; + else + show_stats_quick=false; + +// Show ratio for each statistic: +// +// TOTAL POINTS, INFORMANTS ALIVE, ENEMY DESTROYED, +// OVERALL FLOOR, OVERALL MISSION +// + +// Show TOTAL POINTS ratio. +// + p1=ShowRatio(bx,by,bx+52,by,stats->total_points,stats->accum_points,type); + +// Show INFORMANTS ALIVE ratio. +// + by += 7; + p2=ShowRatio(bx,by,bx+52,by,stats->total_inf,stats->accum_inf,type); + +// Show ENEMY DESTROYED ratio. +// + by += 7; + p3=ShowRatio(bx,by,bx+52,by,stats->total_enemy,stats->accum_enemy,type); + +// Show OVERALL FLOOR ratio. +// + by += 13; + floor=p1+p2+p3; + ShowRatio(bx,by,bx+52,by,maxPerFloor,floor,type); + +// Show OVERALL MISSION ratio. +// + by += 7; + stats->overall_floor=floor; + for (loop=0; loop total) +// perc = total; + +// Catch those nasty divide-by-zeros! +// + if (total) + { + maxperc=LRATIO(100,total,perc,10); + numbars=LRATIO(48,100,maxperc,10); + } + else + { + if (type != ss_justcalc) + { + fontcolor = 0x57; + VW_Bar(bx,by,BAR_W,BAR_H,0); + VW_MarkUpdateBlock(bx,by,bx+(BAR_W-1),by+(BAR_H-1)); + VW_Bar(nx,ny,PERC_W+6,PERC_H,0); + PrintX=nx; PrintY=ny; + US_Print("N/A"); + } + return(100); + } + + if (type == ss_justcalc) + return(maxperc); + + PrintY=ny; + fontcolor=0xaf; + fontnumber=2; + + VW_Bar(bx,by,BAR_W,BAR_H,0x07); + PrintStatPercent(nx,ny,0); + VW_MarkUpdateBlock(bx,by,bx+(BAR_W-1),by+(BAR_H-1)); + for (loop=0; loop MAX_DISPLAY_SCORE) + ActivatePinballBonus(B_SCORE_ROLLED); + +// Check ONE MILLION bonus +// + if (score_before < 500000l && score_after >= 500000l) + ActivatePinballBonus(B_ONE_MILLION); + +// Check EXTRA MAN bonus +// + if (score_after >= gamestate.nextextra) + { + gamestate.nextextra += EXTRAPOINTS; + if (gamestate.lives < MAX_EXTRA_LIVES) + { + gamestate.lives++; + ActivatePinballBonus(B_EXTRA_MAN); + } + } + +// Check TOTAL ENEMY bonus +// + if (gamestuff.level[gamestate.mapon].stats.total_enemy == gamestuff.level[gamestate.mapon].stats.accum_enemy) + ActivatePinballBonus(B_ENEMY_DESTROYED); + +// Check TOTAL POINTS bonus +// + if (gamestuff.level[gamestate.mapon].stats.total_points == gamestuff.level[gamestate.mapon].stats.accum_points) + ActivatePinballBonus(B_TOTAL_POINTS); + +// Check INFORMANTS ALIVE bonus +// + if ((gamestuff.level[gamestate.mapon].stats.total_inf == gamestuff.level[gamestate.mapon].stats.accum_inf) && // All informants alive? + (gamestuff.level[gamestate.mapon].stats.total_inf) && // Any informants in level? + ((BONUS_SHOWN & (B_TOTAL_POINTS|B_ENEMY_DESTROYED)) == (B_TOTAL_POINTS|B_ENEMY_DESTROYED))) // Got ENEMY and POINTS bonuses? + ActivatePinballBonus(B_INFORMANTS_ALIVE); + +// Display bonuses? +// + if (BONUS_QUEUE) + DisplayPinballBonus(); +} + +//=========================================================================== +// +// +// COMPUTER TERMINAL ROUTINES +// +// +//=========================================================================== + +#ifdef ACTIVATE_TERMINAL + +#define TERM_BUFFERED_DISPLAY +#define TERM_VIEW_WIDTH 246 +#define TERM_VIEW_HEIGHT 95 +//#define TERM_BACK_COLOR 2 // Defined in 3d)menu.h +#define TERM_BACK_XOFS 8 +#define TERM_BACK_YOFS 22 +#define TERM_BACK_WIDTH 304 +#define TERM_BACK_HEIGHT 124 + +#define TERM_BCOLOR 3 // Dark Grey +#define TERM_TCOLOR 88 // Green MONO text color 87=LOW intensity +#define TERM_TSHAD_COLOR 0 // "Shadow" color + +#define TERM_SCREEN_XOFS (TERM_BACK_XOFS+19) +#define TERM_SCREEN_YOFS (TERM_BACK_YOFS+14) + +static unsigned far tcursor_x = TERM_SCREEN_XOFS, + far tcursor_y = TERM_SCREEN_YOFS; + + +char TERM_sound_on = 1; + + +char far *Commands[TC_LAST]; + +memptr TermMessages = NULL; +memptr TermCommands = NULL; + +#define FreeTerminalCommands() MM_FreePtr(&TermCommands) +#define FreeTerminalMessages() MM_FreePtr(&TermMessages) +#define LoadTerminalText() IO_LoadFile(term_msg_name,&TermMessages) +//#define LoadTerminalText() IO_LoadFile("TERM_MSG.TXT",&TermMessages) + +//--------------------------------------------------------------------------- +// +// LoadTerminalCommands() +// +// Caches in the commands in TERM_COMMANDS grsegs and sparces the file for +// commands - Assigning Commands[] to the beginning of each command and +// null terminating the command. Each command is seperated with ^XX. Leading +// returns&linefeeds are skipped. +// +// NOTE: This expects that TC_LAST in the enum list of commands is concurrent +// with the grseg TERM_COMMANDS. +// +//--------------------------------------------------------------------------- +void LoadTerminalCommands(void) +{ + char far *Message; + unsigned char pos; + +// IO_LoadFile("TERM_CMD.TXT",&TermCommands); + IO_LoadFile(term_com_name,&TermCommands); + Message = TermCommands; + + for (pos = 0;posheight; + + while (msg && *msg) + { + buf[0] = *msg++; + + if (buf[0] == '^') + { + // + // Handle Control Codes + // + + switch (*((unsigned far *)msg)++) + { + // FONT COLOR + // + case TP_CNVT_CODE('F','C'): + fontcolor = TP_VALUE(msg,2); + msg += 2; + break; + + // BELL + // + case TP_CNVT_CODE('B','E'): + SD_PlaySound(TERM_BEEPSND); + SD_WaitSoundDone(); + break; + + // HIDE CURSOR + // + case TP_CNVT_CODE('H','I'): + px = tcursor_x; + py = tcursor_y; + old_color = fontcolor; + fontcolor = TERM_BCOLOR; + VW_DrawPropString("@"); + fontcolor = old_color; + break; + + + // PAUSE + // + case TP_CNVT_CODE('P','A'): + VW_WaitVBL(30); + break; + + + // END OF MSG + // + + case TP_CNVT_CODE('X','X'): + msg = NULL; + break; + + } + } + else + { + // + // Process Text Char (Like print it!) + // + + bufferofs = displayofs; + + if (term_cursor_vis) + { + px = tcursor_x; + py = tcursor_y; + + old_color = fontcolor; // Store Cursor Color + fontcolor = TERM_BCOLOR; + + VW_DrawPropString("@"); + + fontcolor = old_color; + } + + if (buf[0] != '\n') + { + // Blast "Shadow" on screen + + if (shadow_text) + { + px = tcursor_x+1; + py = tcursor_y+1; + old_color2 = fontcolor; // STORE Old Colr + fontcolor = TERM_TSHAD_COLOR; + VW_DrawPropString(buf); + fontcolor = old_color2; // RESTORE Old Colr + } + + // Blast normal Text color to screen + + px = tcursor_x; + py = tcursor_y; + VW_DrawPropString(buf); + + if (sound_on) + if (buf[0] != ' ') + SD_PlaySound(TERM_TYPESND); + + tcursor_x = px; + + if (term_cursor_vis) + { + VW_DrawPropString("@"); + } + } + else + { + if (tcursor_y > 90+TERM_SCREEN_XOFS) + VL_ScreenToScreen(displayofs+((TERM_SCREEN_YOFS+fontheight)*SCREENWIDTH)+(TERM_SCREEN_XOFS/4), + displayofs+TERM_SCREEN_YOFS*SCREENWIDTH+TERM_SCREEN_XOFS/4, + (248/4),93); + else + tcursor_y += fontheight; + + tcursor_x = TERM_SCREEN_XOFS; + } + + if (!FastPrint) + VL_WaitVBL(TERM_PRINT_DELAY); + + VW_UpdateScreen(); + } + } +} + +#endif + + + + +//--------------------------------------------------------------------------- +// CacheTerminalPrint() +// +// This prints a message in the TERM_MESSAGES grsegs which MUST +// already be loaded into memory. +//--------------------------------------------------------------------------- +void CacheTerminalPrint(short MsgNum,boolean FastPrint) +{ + char far *Message; + + Message = TermMessages; + +// Search for end of MsgNum-1 (Start of our message) +// +#pragma warn -pia + while (MsgNum--) + { + if (!(Message = _fstrstr(Message,int_xx))) + AGENT_ERROR(BAD_TERMINAL_MSG_NUM); + Message += 3; // Bump to start of next Message + } +#pragma warn +pia + +// Move past LFs and CRs that follow "^XX" +// +// while ((*Message=='\n') || (*Message=='\r')) +// Message++; + + Message += 2; // Move past LF and CR that follows "^XX" + + TerminalPrint(Message,FastPrint); +} + + + +char far TERM_MSG[]="^ST1^CEEnter commands and press ENTER.\r^CEPress ESC to exit terminal.^XX"; + +//--------------------------------------------------------------------------- +// ActivateTerminal() +//--------------------------------------------------------------------------- +void ActivateTerminal(boolean skiplink) +{ + #define MAX_INPUT 30 + char buffer[MAX_INPUT]; + boolean temp_caps = allcaps,ExitMoFo; + unsigned oldwidth; + US_CursorStruct TermCursor = {'@',0,0x58,2}; // Holds Font#, etc. + short msgnum; + + +// Setup for text presenter +// + memset(&Terminal_PI,0,sizeof(Terminal_PI)); + Terminal_PI.flags=TPF_USE_CURRENT|TPF_SHOW_CURSOR|TPF_SCROLL_REGION; + Terminal_PI.xl=21; + Terminal_PI.yl=32; + Terminal_PI.xh=277; + Terminal_PI.yh=134; + Terminal_PI.ltcolor=255; + Terminal_PI.bgcolor=TERM_BCOLOR; + Terminal_PI.dkcolor=255; + Terminal_PI.shcolor=TERM_TSHAD_COLOR; + Terminal_PI.fontnumber=2; + Terminal_PI.cur_x = -1; + Terminal_PI.print_delay=1; + + + #ifndef TERM_BUFFERED_DISPLAY + bufferofs = displayofs; + #endif + + ClearMemory(); + + oldwidth = viewwidth/16; + if (oldwidth != FULL_VIEW_WIDTH) + NewViewSize(FULL_VIEW_WIDTH); + + + DrawPlayScreen(false); + + StopMusic(); + + fontnumber = 1; + CA_CacheGrChunk(STARTFONT+FONT2); // Medium font + + BMAmsg(TERM_MSG); + + CacheDrawPic(TERM_BACK_XOFS,TERM_BACK_YOFS,TERMINAL_SCREENPIC); + + LoadTerminalText(); + LoadTerminalCommands(); + + #ifdef TERM_BUFFERED_DISPLAY + VW_UpdateScreen(); + #endif + + + fontnumber = 2; + allcaps = true; + fontcolor = TERM_TCOLOR; + tcursor_x = TERM_SCREEN_XOFS; + tcursor_y = TERM_SCREEN_YOFS; + + // + // Set up custom cursor + // + + use_custom_cursor = true; + US_CustomCursor = TermCursor; + + // + // Start term stuff.. + // + + VW_FadeIn(); + + ExitMoFo = false; + + TerminalPrint("^ST1^XX",false); + if (!skiplink) + { + CacheTerminalPrint(TM_LINK,false); + + if (Keyboard[sc_H] & Keyboard[sc_O] & Keyboard[sc_T]) + { + CacheTerminalPrint(TM_CHEATER,false); + } + else + { + VW_WaitVBL(1*60 + (US_RndT() % 60*2)); + + if (gamestate.TimeCount & 0x1000) + { + CacheTerminalPrint(TM_LINK_BAD,false); + IN_Ack(); + ExitMoFo = true; + } + else + { + CacheTerminalPrint(TM_LINK_OK,false); + } + } + } + + + IN_ClearKeysDown(); + + while (!ExitMoFo) + { + backcolor = TERM_BCOLOR; + CacheTerminalPrint(TM_READY,false); + + if (US_LineInput(px+1,py,buffer,nil,true,MAX_INPUT,240+TERM_SCREEN_XOFS-px)) + { + CacheTerminalPrint(TM_RETURN,false); + if (*buffer) + switch (msgnum = US_CheckParm(buffer,Commands)) + { + case TC_HINT: +// case TC_GOLDSTERN: + case TC_JAM: + case TC_HELP: + case TC_APOGEE: + case TC_THANKS: + case TC_GOOBERS: +// case TC_BSTONE: + case TC_JERRY: + case TC_MIKE: + case TC_JIM: + CacheTerminalPrint(msgnum,false); + break; + + + case TC_EXIT: + case TC_QUIT: + case TC_OFF: + case TC_BYE: + ExitMoFo = true; + break; + + case TC_STAR: + CacheTerminalPrint(TM_STAR,false); + break; + + case TC_JOSHUA: + CacheTerminalPrint(TM_JOSHUA,false); +// PowerBall = 1; + break; + + case TC_BLUEPRINT: + FloorCheat(255); + break; + + case TC_SOUND: + TERM_sound_on ^= 1; + CacheTerminalPrint(TM_SOUND_OFF+TERM_sound_on,false); + break; + + case TC_ARRIVAL_GOLDSTERN: + { + if (GoldsternInfo.GoldSpawned) + CacheTerminalPrint(TM_GOLDSTERN_ARRIVED,false); + else + if (GoldsternInfo.flags == GS_COORDFOUND) + { + CacheTerminalPrint(TM_GOLDSTERN_WILL_AR,false); + sprintf(buffer," %d^XX",GoldsternInfo.WaitTime/60); + TerminalPrint(buffer,false); + CacheTerminalPrint(TM_SECONDS,false); + } + else + { + if (GoldsternInfo.WaitTime) + { + CacheTerminalPrint(TM_GOLDSTERN_NO_PICK,false); + sprintf(buffer," %d^XX",GoldsternInfo.WaitTime/60); + TerminalPrint(buffer,false); + CacheTerminalPrint(TM_SECONDS,false); + } + else + CacheTerminalPrint(TM_GOLDSTERN_NO_INFO,false); + } + } + break; + + case TC_DEACTIVATE_SECURITY: + { + objtype *obj; + + CacheTerminalPrint(TM_RESET_SECURITY,false); + for (obj = player;obj;obj = obj->next) + { + if (obj->obclass == security_lightobj) + { + obj->temp1 = 0; + obj->flags &= ~FL_ALERTED; + } + } + } + break; + + case TC_SATALITE_STATUS: + { + CacheTerminalPrint(TM_VITALS1,false); + TerminalPrint(buffer,false); + + CacheTerminalPrint(TM_VITALS2,false); + sprintf(buffer, " %d\r\n^XX", gamestate.VitalsRemain); + TerminalPrint(buffer,false); + } + break; + + case TC_PROFILE: + CacheTerminalPrint(TM_PROFILE_WHO,false); + if (US_LineInput(px+1,py,buffer,nil,true,MAX_INPUT,246+TERM_SCREEN_XOFS-px)) + { + CacheTerminalPrint(TM_RETURN,false); + if (*buffer) + switch (US_CheckParm(buffer,Commands)) + { + case TC_GOLDSTERN: + CacheTerminalPrint(TM_PROFILE_GOLDSTERN,false); + break; + + case TC_BSTONE: + CacheTerminalPrint(TM_PROFILE_BLAKE,false); + break; + + case TC_SSTONE: + CacheTerminalPrint(TM_PROFILE_SARA,false); + break; + + default: + CacheTerminalPrint(TM_PROFILE_UNKNOWN,false); + break; + } + } + break; + + default: + CacheTerminalPrint(TM_UNRECOGNIZED_COMMAND,false); + break; + } + } + else + { + // User pressed escape.... + + ExitMoFo = true; + } + + #ifdef TERM_BUFFERED_DISPLAY + VW_UpdateScreen(); + #endif + } + + // + // Free everything cached in...Exit terminal + // + + FreeTerminalCommands(); + FreeTerminalMessages(); + + UNCACHEGRCHUNK(STARTFONT+1); + NewViewSize(oldwidth); + + + StartMusic(false); + PM_CheckMainMem(); + + DrawPlayScreen(false); + + IN_ClearKeysDown(); + allcaps = temp_caps; + use_custom_cursor = false; + +} + + +//--------------------------------------------------------------------------- +// FloorCheat() +//--------------------------------------------------------------------------- +void FloorCheat(unsigned RadarFlags) +{ + #define FC_EMBED_COLOR(ColorCodes) {_fstrncpy(&pbuffer[pos],ColorCodes,5);pos+=5;} + #define FC_NORM_COLOR() FC_EMBED_COLOR("^FC57") + + unsigned x,y,pos; + objtype *actor; + char far *pbuffer; + memptr buffer; + + MM_GetPtr(&buffer,512); + + pbuffer = buffer; + + CacheTerminalPrint(TM_BLUEPRINTS,false); + + shadow_text = term_cursor_vis = false; + Terminal_PI.flags &= ~TPF_SHOW_CURSOR; + + // + // Cache in the "Radar Font" + // + + CA_CacheGrChunk(STARTFONT+5); +// fontnumber = 5; + Terminal_PI.fontnumber = 5; + + // + // Display the radar/floor-plans + +// TerminalPrint("\r\n^XX",true); + for (y=0;y<64;y++) + { + pos = 0; + for (x=0;x<64;x++) + { + // + // Get wall/actor && Check for force placement of player on radar.. + // + + if (DebugOk && x == player->tilex && y == player->tiley) + actor = player; + else + actor = actorat[x][y]; + + // + // Check for walls + + if (!TravelTable[x][y]) // Map only shows where you've seen! + { + pbuffer[pos++]='!'; + } + else + if (((unsigned)actor && (unsigned)actor<108) || // 108 == LAST WALL TILE + +#if IN_DEVELOPMENT + + (*(mapsegs[0]+farmapylookup[y]+x) >= HIDDENAREATILE && (!DebugOk)) || + +#endif + + (((unsigned)actor & 0x80) && actor= objlist) && (!(actor->flags & FL_DEADGUY))) + { + switch (actor->obclass) + { + case playerobj: + if (RadarFlags & RS_PERSONNEL_TRACKER) + { + // + // Mark map piece as the "player" + // + FC_EMBED_COLOR("^FC0F"); // WHITE + pbuffer[pos++] = '!'; + FC_NORM_COLOR(); + } + else + pbuffer[pos++] = ' '; + break; + + case security_lightobj: + if (RadarFlags & RS_SECURITY_STATUS) + { + // + // Mark map piece as "Alerted Security Lamp" + // + if (actor->temp1) + { + FC_EMBED_COLOR("^FC1C"); // Red + } + else + { + FC_EMBED_COLOR("^FC5C"); // Green + } + + pbuffer[pos++] = '!'; + FC_NORM_COLOR(); + break; + } + else + pbuffer[pos++] = ' '; + break; + + case lcan_wait_alienobj: + case scan_wait_alienobj: + case hang_terrotobj: + case gurney_waitobj: + pbuffer[pos++] = ' '; + break; + + case goldsternobj: + if (RadarFlags & RS_GOLDSTERN_TRACKER) + { + // + // Mark map piece as "goldstern" + // + FC_EMBED_COLOR("^FC38"); // Yellow ...or.. err, like gold! + pbuffer[pos++] = '!'; + FC_NORM_COLOR(); + break; + } + else + pbuffer[pos++] = ' '; + break; + + default: + if (RadarFlags & RS_PERSONNEL_TRACKER) + { + // + // Mark map piece as a "general object" + // + FC_EMBED_COLOR("^FC18"); // Red + pbuffer[pos++] = '!'; + FC_NORM_COLOR(); + } + else + pbuffer[pos++] = ' '; + break; + } + } + else + pbuffer[pos++] = ' '; + + } + + } + +// pbuffer[pos++] = '\n'; + _fstrcpy(pbuffer+pos,"\r\n^XX"); + pbuffer[pos+5] = 0; + + TerminalPrint(pbuffer,true); + } + + + TerminalPrint("\r\n\r\n\r\n\r\n^XX",true); + MM_FreePtr(&buffer); + + UNCACHEGRCHUNK(STARTFONT+5); + Terminal_PI.fontnumber = 2; + TerminalPrint("\r\n^XX",true); + Terminal_PI.flags |= TPF_SHOW_CURSOR; + +} + +#endif + + +/* +============================================================================= + + PLAYER CONTROL + +============================================================================= +*/ + + + + +void SpawnPlayer (int tilex, int tiley, int dir) +{ + if (gamestuff.level[gamestate.mapon].ptilex && + gamestuff.level[gamestate.mapon].ptiley) + { + tilex = gamestuff.level[gamestate.mapon].ptilex; + tiley = gamestuff.level[gamestate.mapon].ptiley; + dir = 1+(gamestuff.level[gamestate.mapon].pangle/90); + } + + player->obclass = playerobj; + player->active = true; + player->tilex = tilex; + player->tiley = tiley; + + player->areanumber=GetAreaNumber(player->tilex,player->tiley); + + 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 (); + + InitWeaponBounce(); +} + + +//=========================================================================== + +//------------------------------------------------------------------------ +// GunAttack() +//------------------------------------------------------------------------ +void GunAttack (objtype *ob) +{ + objtype *check,*closest,*oldclosest; + int damage; + int dx,dy,dist; + long viewdist; + boolean skip = false; + + if (gamestate.weapon != wp_autocharge) + { + MakeAlertNoise(ob); + } + + switch (gamestate.weapon) + { + case wp_autocharge: + SD_PlaySound (ATKAUTOCHARGESND); + skip = true; + break; + + case wp_pistol: + SD_PlaySound (ATKCHARGEDSND); + skip = true; + break; + + case wp_burst_rifle: + SD_PlaySound (ATKBURSTRIFLESND); + break; + + case wp_ion_cannon: + SD_PlaySound (ATKIONCANNONSND); + break; + + } + + // + // 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) + { + if ((skip && (check->obclass == hang_terrotobj)) + || (check->flags2 & FL2_NOTGUNSHOOTABLE)) + continue; + + viewdist = check->transx; + closest = check; + } + } + + if (closest == oldclosest) + return; // no more targets, all missed + + // + // trace a line from player to enemey + // + if (CheckLine(closest,player)) + 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() / 2; // 4 + else if (dist<4) + damage = US_RndT() / 4; // 6 + else + { + if ( (US_RndT() / 12) < dist) // missed + return; + damage = US_RndT() / 4; // 6 + } + + DamageActor (closest,damage,player); +} + + +//=========================================================================== + + + + +//=========================================================================== + + +/* +=============== += += T_Attack += +=============== +*/ + + +void T_Attack (objtype *ob) +{ + atkinf_t far *cur; + int x, wp_start; + + if (noShots) + { + ob->state = &s_player; + gamestate.attackframe = gamestate.weaponframe = 0; + return; + } + + if (gamestate.weapon == wp_autocharge) + UpdateAmmoMsg(); + + if ( buttonstate[bt_use] && !buttonheld[bt_use] ) + buttonstate[bt_use] = false; + + if ( buttonstate[bt_attack] && !buttonheld[bt_attack]) + buttonstate[bt_attack] = false; + + ControlMovement (ob); + + player->tilex = player->x >> TILESHIFT; // scale to tile values + player->tiley = player->y >> TILESHIFT; + +// +// change frame and fire +// + gamestate.attackcount -= tics; + if (gamestate.attackcount <= 0) + { + cur = &attackinfo[gamestate.weapon][gamestate.attackframe]; + switch (cur->attack) + { + case -1: + ob->state = &s_player; + + if (!gamestate.ammo) + { + if (gamestate.weapon != wp_autocharge) + { + gamestate.weapon = wp_autocharge; + DrawWeapon(); + DisplayInfoMsg(EnergyPackDepleted,MP_NO_MORE_AMMO,DISPLAY_MSG_STD_TIME<<1,MT_OUT_OF_AMMO); + } + } + else + { + if (!(gamestate.useable_weapons & (1<state = &s_player; + if (!gamestate.plasma_detonators) + { + // Check to see what weapons are possible. + // + + for (x=wp_bfg_cannon;x>=wp_autocharge;x--) + { + if (gamestate.useable_weapons & (1<= GRENADE_ENERGY_USE) + { + gamestate.ammo-=GRENADE_ENERGY_USE; + DrawAmmo(false); + } + else + gamestate.attackframe++; + } + SD_PlaySound(ATKGRENADESND); + SpawnProjectile(ob,grenadeobj); + MakeAlertNoise(ob); + } + break; + + + case 7: + TryDropPlasmaDetonator(); + DrawAmmo(false); + break; + + case 8: + if (gamestate.plasma_detonators && buttonstate[bt_attack]) + gamestate.attackframe -= 2; + break; + + case 9: + if (!objfreelist) + { + DISPLAY_TIMED_MSG(WeaponMalfunction,MP_WEAPON_MALFUNCTION,MT_MALFUNCTION); + gamestate.attackframe++; + } + else + { + if (LastMsgType == MT_MALFUNCTION) + MsgTicsRemain = 1; // Clear Malfuction Msg before anim + + if (!godmode) + { + if (gamestate.ammo >= BFG_ENERGY_USE) + { + gamestate.ammo-=BFG_ENERGY_USE; + DrawAmmo(false); + } + else + gamestate.attackframe++; + } + SD_PlaySound(ATKIONCANNONSND); // JTR - this needs to change + SpawnProjectile(ob,bfg_shotobj); + MakeAlertNoise(ob); + } + break; + + case 10: + if (gamestate.ammo && buttonstate[bt_attack]) + if (objfreelist) + gamestate.attackframe -= 2; + else + { + DISPLAY_TIMED_MSG(WeaponMalfunction,MP_WEAPON_MALFUNCTION,MT_MALFUNCTION); + } + break; + + } + + gamestate.attackcount += cur->tics; + gamestate.attackframe++; + gamestate.weaponframe = + attackinfo[gamestate.weapon][gamestate.attackframe].frame; + } +} + + +//=========================================================================== + +/* +=============== += += T_Player += +=============== +*/ + +void T_Player (objtype *ob) +{ + CheckWeaponChange (); + + if (gamestate.weapon == wp_autocharge) + UpdateAmmoMsg(); + + if (tryDetonatorDelay > tics) + tryDetonatorDelay -= tics; + else + tryDetonatorDelay = 0; + + if ( buttonstate[bt_use] ) + { + Cmd_Use(); + SD_PlaySound(HITWALLSND); + } + + if ( buttonstate[bt_attack] && !buttonheld[bt_attack]) + Cmd_Fire (); + + ControlMovement (ob); + HandleWeaponBounce(); + + +// 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; +} + +#if 0 +//------------------------------------------------------------------------- +// RunBlakeRun() +//------------------------------------------------------------------------- +void RunBlakeRun() +{ + #define BLAKE_SPEED (MOVESCALE*50) + + long xmove,ymove,speed; + objtype *blake; + short startx,starty,dx,dy; + +// Spawn Blake and set pointer. +// + SpawnPatrol(en_blake,player->tilex,player->tiley,player->dir>>1); + blake=new; + +// Blake object starts one tile behind player object. +// + switch (blake->dir) + { + case north: + blake->tiley+=2; + break; + + case south: + blake->tiley-=2; + break; + + case east: + blake->tilex-=2; + break; + + case west: + blake->tilex+=2; + break; + } + +// Align Blake on the middle of the tile. +// + blake->x = ((long)blake->tilex<y = ((long)blake->tiley<tilex = blake->x >> TILESHIFT; + starty=blake->tiley = blake->y >> TILESHIFT; + +// Run, Blake, Run! +// + do + { + // Calc movement in X and Y directions. + // + xmove = FixedByFrac(BLAKE_SPEED,costable[player->angle]); + ymove = -FixedByFrac(BLAKE_SPEED,sintable[player->angle]); + + // Move, animate, and redraw. + // + if (ClipMove(blake,xmove,ymove)) + break; + DoActor(blake); + ThreeDRefresh(); + + // Calc new tile X/Y. + // + blake->tilex = blake->x >> TILESHIFT; + blake->tiley = blake->y >> TILESHIFT; + + // Evaluate distance from start. + // + dx=blake->tilex-startx; + dx=ABS(dx); + dy=blake->tiley-starty; + dy=ABS(dy); + + } while ((dx < 6) && (dy < 6)); +} + +#endif + +//------------------------------------------------------------------------- +// SW_HandleActor() - Handle all actors connected to a smart switch. +//------------------------------------------------------------------------- +void SW_HandleActor(objtype *obj) +{ + if (!obj->active) + obj->active = ac_yes; + + switch (obj->obclass) + { + case rentacopobj: + case gen_scientistobj: + case swatobj: + case goldsternobj: + case proguardobj: + if (!(obj->flags & (FL_ATTACKMODE|FL_FIRSTATTACK))) + FirstSighting(obj); + break; + + case morphing_spider_mutantobj: + case morphing_reptilian_warriorobj: + case morphing_mutanthuman2obj: + case crate1obj: + case crate2obj: + case crate3obj: + case podeggobj: + KillActor(obj); + break; + + case gurney_waitobj: + case scan_wait_alienobj: + case lcan_wait_alienobj: + break; + +// case electrosphereobj: +// break; + + case floatingbombobj: + case volatiletransportobj: + if (obj->flags & FL_STATIONARY) + KillActor(obj); + else + if (!(obj->flags & (FL_ATTACKMODE|FL_FIRSTATTACK))) + FirstSighting(obj); + break; + + case spider_mutantobj: + case breather_beastobj: + case cyborg_warriorobj: + case reptilian_warriorobj: + case acid_dragonobj: + case mech_guardianobj: + case liquidobj: + case genetic_guardobj: + case mutant_human1obj: + case mutant_human2obj: + case lcan_alienobj: + case scan_alienobj: + case gurneyobj: + case podobj: + case final_boss1obj: + case final_boss2obj: + case final_boss3obj: + case final_boss4obj: + if (!(obj->flags & (FL_ATTACKMODE|FL_FIRSTATTACK))) + FirstSighting(obj); + break; + +// case electroobj: +// case liquidobj: +// break; + + case post_barrierobj: + case arc_barrierobj: + break; + } +} + + +//------------------------------------------------------------------------- +// SW_HandleStatic() - Handle all statics connected to a smart switch. +//------------------------------------------------------------------------- +void SW_HandleStatic(statobj_t *stat, unsigned tilex, unsigned tiley) +{ + switch (stat->itemnumber) + { + case bo_clip: + case bo_clip2: + SpawnCusExplosion((((fixed)tilex)<shapenum = -1; + stat->itemnumber = bo_nothing; + break; + } +} + + +//------------------------------------------------------------------------- +// OperateSmartSwitch() - Operates a Smart Switch +// +// PARAMETERS: +// tilex - Tile X coord that the Smart switch points to. +// tiley - Tile Y coord that the Smart switch points to. +// force - Force switch operation. Will not check the players current +// and last tilex & tiley coords. This is usefull for other +// actors toggling barrier switches. +// +// RETURNS: Boolean: TRUE - Remove switch from map +// FALSE - Keep switch in map +// +//------------------------------------------------------------------------- +boolean OperateSmartSwitch(unsigned tilex, unsigned tiley, char Operation, boolean Force) +{ + typedef enum + { + wit_NOTHING, + wit_DOOR, + wit_WALL, + wit_STATIC, + wit_ACTOR, + } what_is_it; + + what_is_it WhatItIs; + objtype *obj; + statobj_t *stat; + unsigned char tile, DoorNum; + unsigned iconnum; + + // + // Get some information about what + // this switch is pointing to. + // + + tile = tilemap[tilex][tiley]; + obj = actorat[tilex][tiley]; + iconnum = *(mapsegs[1]+farmapylookup[tiley]+tilex); + WhatItIs = wit_NOTHING; + + // + // Deterimine if the switch points to an + // actor, door, wall, static or is Special. + // + + if (obj < objlist) + { + if (obj == (objtype *)1 && tile == 0) + { + // We have a SOLID static! + + WhatItIs = wit_STATIC; + } + else + { + if (tile) + { + // + // We have a wall of some type (maybe a door). + // + + if (tile & 0x80) + { + // We have a door + + WhatItIs = wit_DOOR; + DoorNum = tile & 0x7f; + } + else + { + // We have a wall + + WhatItIs = wit_WALL; + } + } + else + { +#pragma warn -pia + if (stat = FindStatic(tilex,tiley)) + WhatItIs = wit_STATIC; +#pragma warn +pia + } + } + } + else + { + if (obj < &objlist[MAXACTORS]) + { + // We have an actor. + + WhatItIs = wit_ACTOR; + } + else + WhatItIs = wit_NOTHING; + } + + // + // Ok... Now do that voodoo that you do so well... + // + + switch (WhatItIs) + { + // + // Handle Doors + // + case wit_DOOR: + if (doorobjlist[DoorNum].action == dr_jammed) + return(false); + + doorobjlist[DoorNum].lock = kt_none; + OpenDoor(DoorNum); + return(false); + + + // + // Handle Actors + // + case wit_ACTOR: + if (!(obj->flags & FL_DEADGUY)) + SW_HandleActor(obj); + return(true); + + + // + // Handle Walls + // + case wit_WALL: + { + if (Force || player_oldtilex != player->tilex || player_oldtiley != player->tiley) + switch (tile) + { + case OFF_SWITCH: + if (Operation == ST_TURN_OFF) + return(false); + + ActivateWallSwitch(iconnum, tilex, tiley); + break; + + case ON_SWITCH: + if (Operation == ST_TURN_ON) + return(false); + ActivateWallSwitch(iconnum, tilex, tiley); + break; + } + } + return(false); + + + // + // Handle Statics + // + case wit_STATIC: + SW_HandleStatic(stat,tilex,tiley); + return (true); + + + // + // Handle NON connected smart switches... + // + case wit_NOTHING: + // Actor (or something) that was to be triggered has + // moved... SSSOOOoo, Remove the switch. + return(true); + } + + return(false); +} + +//========================================================================== +// +// WEAPON BOUNCE CODE +// +//========================================================================== + +#define wb_MaxPoint ((long)10 << TILESHIFT) +#define wb_MidPoint ((long)6 << TILESHIFT) +#define wb_MinPoint ((long)2 << TILESHIFT) +#define wb_MaxGoalDist (wb_MaxPoint - wb_MidPoint) + +#define wb_MaxOffset (wb_MaxPoint+((long)2 << TILESHIFT)) +#define wb_MinOffset (wb_MinPoint-((long)2 << TILESHIFT)) + +extern fixed bounceOffset; + +fixed bounceVel,bounceDest; +short bounceOk; + +//-------------------------------------------------------------------------- +// InitWeaponBounce() +//-------------------------------------------------------------------------- +void InitWeaponBounce() +{ + bounceOffset = wb_MidPoint; + bounceDest = wb_MaxPoint; + bounceVel = bounceOk = 0; +} + +//-------------------------------------------------------------------------- +// HandleWeaponBounce() +//-------------------------------------------------------------------------- +void HandleWeaponBounce() +{ + short bounceSpeed; + + bounceSpeed = 90-((20-viewsize)*6); + + if (bounceOk) + { + if (bounceOffset < bounceDest) + { + bounceVel += (sintable[bounceSpeed]+1) >> 1; + bounceOffset += bounceVel; + if (bounceOffset > bounceDest) + { + bounceDest = wb_MinPoint; + bounceVel >>= 2; + } + } + else + if (bounceOffset > bounceDest) + { + bounceVel -= sintable[bounceSpeed] >> 2; + bounceOffset += bounceVel; + + if (bounceOffset < bounceDest) + { + bounceDest = wb_MaxPoint; + bounceVel >>= 2; + } + } + } + else + { + if (bounceOffset > wb_MidPoint) + { + bounceOffset -= ((long)2< wb_MidPoint) + bounceOffset = wb_MidPoint; + } + + bounceDest = wb_MaxPoint; + bounceVel = 0; + } + + if (bounceOffset > wb_MaxOffset) + bounceOffset = wb_MaxOffset; + else + if (bounceOffset < wb_MinOffset) + bounceOffset = wb_MinOffset; +} + diff --git a/3D_DEBUG.C b/3D_DEBUG.C new file mode 100644 index 0000000..fdbc9d0 --- /dev/null +++ b/3D_DEBUG.C @@ -0,0 +1,1057 @@ +// 3D_DEBUG.C + +#include "3D_DEF.H" +#pragma hdrstop +#include + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + + + +#define VIEWTILEX (viewwidth/16) +#define VIEWTILEY (viewheight/16) + +#define MAX_WARP_LEVEL 23 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +boolean ForceLoadDefault=false; + +int DebugKeys (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +boolean PP_step=false; +#if IN_DEVELOPMENT +/* +================ += += PicturePause += +================ +*/ +void PicturePause (void) +{ + int i; + byte p; + unsigned x; + byte far *dest,far *src; + memptr buffer; + +#if RESTART_PICTURE_PAUSE + PP_step=false; +#endif + + VW_ColorBorder (15); + FinishPaletteShifts (); + + LastScan = 0; + while (!LastScan); + +#if RESTART_PICTURE_PAUSE + + if (LastScan != sc_Enter) + { + switch (LastScan) + { + case sc_O: + case sc_Space: + case sc_Control: + case sc_Alt: + break; + + default: + PP_step=true; + break; + } + + VW_ColorBorder (0); + return; + } + + VW_ColorBorder (1); + VW_SetScreen (0,0); + +// Save screen to buffer. +// + ClearMemory (); + MM_GetPtr (&buffer,64000); + + for (p=0;p<4;p++) + { + src = MK_FP(0xa000,displayofs); + dest = (byte far *)buffer+p; + VGAREADMAP(p); + for (x=0;x<16000;x++,dest+=4) + *dest = *src++; + } + +// Switch to mode 13h VGA. +// +asm mov ax,0x13 +asm int 0x10 + +// Restore buffer to screen. +// + VL_SetPalette (0,256,&vgapal); + dest = MK_FP(0xa000,0); + _fmemcpy (dest,buffer,64000); + MM_FreePtr(&buffer); + +// Enable keyboard and wait for grab. +// + while (Keyboard[sc_Enter]); + IN_Shutdown (); + bioskey(0); + +// Restart play +// + IN_Startup(); + VL_SetVGAPlaneMode(); + VL_SetPalette (0,256,&vgapal); + LoadLatchMem(); + playstate=ex_transported; + DrawPlayScreen(false); + DrawInfoArea_COUNT = InitInfoArea_COUNT = 3; + playstate=ex_stillplaying; + PM_CheckMainMem(); + PP_step=true; + return; + +#else + VW_ColorBorder (0); + return; +#endif +} + +#endif +int maporgx; +int maporgy; +enum {mapview,tilemapview,actoratview,visview} viewtype; + +void ViewMap (void); + +//=========================================================================== + +/* +================== += += DebugMemory += +================== +*/ +void DebugMemory (void) +{ + int i,temp; + char scratch[80],str[10]; + long mem; + spritetype _seg *block; + + CenterWindow (22,15); + + 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\nTics :"); + US_PrintUnsigned (tics); + US_Print ("\nReal Tics :"); + US_PrintUnsigned (realtics); + +#ifdef CEILING_FLOOR_COLORS + if (gamestate.flags & GS_DRAW_CEILING) +#endif + { + US_Print ("\n\nCeiling TEX: "); + US_PrintUnsigned (CeilingTile-START_TEXTURES); + + US_Print(" Floor TEX: "); + US_PrintUnsigned(FloorTile-START_TEXTURES); + } +#ifdef CEILING_FLOOR_COLORS + else + { + US_Print("\n\nTop COL: "); + US_PrintUnsigned(TopColor&0xff); + + US_Print(" Bottom COL: "); + US_PrintUnsigned(BottomColor&0xff); + } +#endif + + if (gamestate.flags & GS_LIGHTING) + { + US_Print ("\nShade div :"); + US_PrintUnsigned (normalshade_div); + + US_Print ("\nShade max :"); + US_PrintUnsigned (shade_max); + } + + VW_UpdateScreen(); + IN_Ack (); + + temp = bufferofs; + WindowW = 253; + WindowH = 8; + fontnumber = 2; + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + LatchDrawPic(0,0,TOP_STATUSBARPIC); + ShadowPrintLocationText(sp_normal); + } + + bufferofs = temp; +} + +//=========================================================================== + +/* +================== += += CountObjects += +================== +*/ + +void CountObjects (void) +{ + int i,total,count,active,inactive,doors; + objtype *obj; + + CenterWindow (16,7); + active = inactive = count = doors = 0; + + US_Print ("Total statics :"); + total = laststatobj-&statobjlist[0]; + US_PrintUnsigned (total); + + US_Print ("\nIn use statics:"); + for (i=0;inext;obj;obj=obj->next) + { + if (obj->active) + active++; + else + inactive++; + } + + US_Print ("\nTotal actors :"); + US_PrintUnsigned (active+inactive); + + US_Print ("\nActive actors :"); + US_PrintUnsigned (active); + + VW_UpdateScreen(); + IN_Ack (); +} + + +//------------------------------------------------------------------------ +// CountTotals +//------------------------------------------------------------------------ +void CountTotals(void) +{ + CenterWindow (20,11); + + US_Print (" CURRENT MAP TOTALS\n"); + + US_Print ("\nTotal Enemy:\n"); + US_PrintUnsigned (gamestuff.level[gamestate.mapon].stats.total_enemy); + + US_Print ("\nTotal Points:\n"); + US_PrintUnsigned (gamestuff.level[gamestate.mapon].stats.total_points); + + US_Print ("\nTotal Informants:\n"); + US_PrintUnsigned (gamestuff.level[gamestate.mapon].stats.total_inf); + + VW_UpdateScreen(); + IN_Ack (); +} + + +//------------------------------------------------------------------------ +// ShowMap() +//------------------------------------------------------------------------ +void ShowMap(void) +{ + objtype old_player; + + memcpy(&old_player,player,sizeof(objtype)); + player->angle = 90; + player->x = player->y = ((long)32<xmsPage != -1) + US_PrintUnsigned(page->xmsPage); + else + US_Print("No"); + + US_Print("\n Main: "); + if (page->mainPage != -1) + US_PrintUnsigned(page->mainPage); + else if (page->emsPage != -1) + { + US_Print("EMS "); + US_PrintUnsigned(page->emsPage); + } + else + US_Print("No"); + + US_Print("\n Last hit: "); + US_PrintUnsigned(page->lastHit); + + US_Print("\n Address: "); + addr = PM_GetPageAddress(i); + sprintf(buf,"0x%04x",(word)addr); + US_Print(buf); + + if (addr) + { + if (i < PMSpriteStart) + { + // + // draw the wall + // + bufferofs += 32*SCREENWIDTH; + postx = 128; + postwidth = 1; + postsource = ((long)((unsigned)addr))<<16; + for (x=0;x<64;x++,postx++,postsource+=64) + { + wallheight[postx] = 256; + FarScalePost (); + } + bufferofs -= 32*SCREENWIDTH; + } + else if (i < PMSoundStart) + { + // + // draw the sprite + // + bufferofs += 32*SCREENWIDTH; + SimpleScaleShape (160, i-PMSpriteStart, 64); + bufferofs -= 32*SCREENWIDTH; + } + else if (i == ChunksInFile - 1) + { + US_Print("\n\n Number of sounds: "); + US_PrintUnsigned(NumDigi); + for (l = j = k = 0;j < NumDigi;j++) + { + l += DigiList[(j * 2) + 1]; + k += (DigiList[(j * 2) + 1] + (PMPageSize - 1)) / PMPageSize; + } + US_Print("\n Total bytes: "); + US_PrintUnsigned(l); + US_Print("\n Total pages: "); + US_PrintUnsigned(k); + } + else + { + byte far *dp = (byte far *)MK_FP(addr,0); + + sound = i-PMSoundStart; + US_Print("\n Sound #"); + US_PrintUnsigned(sound); + + for (j = 0;j < page->length;j += 32) + { + byte v = dp[j]; + int v2 = (unsigned)v; + v2 -= 128; + v2 /= 4; + if (v2 < 0) + VWB_Vlin(WindowY + WindowH - 32 + v2, + WindowY + WindowH - 32, + WindowX + 8 + (j / 32),BLACK); + else + VWB_Vlin(WindowY + WindowH - 32, + WindowY + WindowH - 32 + v2, + WindowX + 8 + (j / 32),BLACK); + } + } + } + + VW_UpdateScreen(); + + while (!(scan = LastScan)) + SD_Poll(); + + IN_ClearKey(scan); + switch (scan) + { + case sc_LeftArrow: + if (i) + i--; + break; + case sc_RightArrow: + if (++i >= ChunksInFile) + i--; + break; + case sc_W: // Walls + i = 0; + break; + case sc_S: // Sprites + i = PMSpriteStart; + break; + case sc_D: // Digitized + i = PMSoundStart; + break; + case sc_I: // Digitized info + i = ChunksInFile - 1; + break; + case sc_L: // Load all pages + for (j = 0;j < ChunksInFile;j++) + PM_GetPage(j); + break; + case sc_P: + if (sound != -1) + SD_PlaySound(sound); + break; + case sc_Escape: + done = true; + break; + case sc_Enter: + PM_GetPage(i); + break; + } + } + SD_StopDigitized(); + + RedrawStatusAreas(); +} +#pragma warn +pia + +#endif + + +//=========================================================================== + + +//--------------------------------------------------------------------------- +// IncRange - Incs a value to a MAX value (including max value) +// +// NOTE: Assumes that 0 is the lowest value +//--------------------------------------------------------------------------- +unsigned IncRange(unsigned Value,unsigned MaxValue) +{ + if (Value == MaxValue) + Value = 0; + else + Value++; + + return (Value); +} + +//--------------------------------------------------------------------------- +// DecRange - Decs a value to 0 and resets to MAX_VALUE +// +// NOTE: Assumes that 0 is the lowest value +//--------------------------------------------------------------------------- +unsigned DecRange(unsigned Value,unsigned MaxValue) +{ + if (Value == 0) + Value = MaxValue; + else + Value--; + + return (Value); +} + + + +/* +================ += += DebugKeys += +================ +*/ + +#if IN_DEVELOPMENT +char far TestAutoMapperMsg[] = {"AUTOMAPPER TEST\n ENTER COUNT:"}; +char far TestQuickSaveMsg[] = {"QUICK SAVE TEST\n ENTER COUNT:"}; +#endif + + +int DebugKeys (void) +{ + char str[3]; + boolean esc; + int level,i; + + if (Keyboard[sc_A]) // A = Show Actors on AutoMap + { + ExtraRadarFlags ^= OV_ACTORS; + CenterWindow (24,3); + if (ExtraRadarFlags & OV_ACTORS) + US_PrintCentered ("AUTOMAP: Show Actors ON"); + else + US_PrintCentered ("AUTOMAP: Show Actors OFF"); + VW_UpdateScreen(); + IN_Ack(); + return 1; + } + else + 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 (Keyboard[sc_K]) // K = Map Content totals + { + CountTotals(); + return 1; + } + else if (Keyboard[sc_C]) // C = count objects + { + CountObjects(); + return 1; + } + else if (Keyboard[sc_R]) // C = count objects + { + ShowMap(); + return 1; + } + else if (Keyboard[sc_D]) // D = Dumb/Blind Objects (Player Invisable) + { + CenterWindow (19,3); + PlayerInvisable ^= 1; + if (PlayerInvisable) + US_PrintCentered ("Player Invisible!"); + else + US_PrintCentered ("Player visible"); + + VW_UpdateScreen(); + IN_Ack (); + return 1; + } + else if (Keyboard[sc_E]) // E = Win Mission + { + CenterWindow (19,3); + US_PrintCentered ("Instant Wiener!"); + InstantWin = 1; + playstate = ex_victorious; + VW_UpdateScreen(); + IN_Ack (); + return 1; + } + else + if (Keyboard[sc_F]) // F = facing spot + { + CenterWindow (18,5); + US_Print ("X:"); + US_PrintUnsigned (player->x); + US_Print (" "); + US_PrintUnsigned (player->x>>TILESHIFT); + US_Print ("\nY:"); + US_PrintUnsigned (player->y); + US_Print (" "); + US_PrintUnsigned (player->y>>TILESHIFT); + US_Print ("\nA:"); + US_PrintUnsigned (player->angle); + US_Print ("\nD:"); + US_PrintUnsigned (player->dir); + 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 + { + IN_ClearKeysDown (); + TakeDamage (1,NULL); + } + else + if (Keyboard[sc_I]) // I = item cheat + { + char loop; + CenterWindow (12,3); + US_PrintCentered ("Free items!"); + VW_UpdateScreen(); + HealSelf (99); + GiveToken(5); + + for (i=wp_autocharge;i<=wp_bfg_cannon;i++) + if (!(gamestate.weapons & (1< MAX_AMMO) + gamestate.ammo = MAX_AMMO; + DrawAmmo(true); + DrawScore(); + IN_Ack (); + return 1; + } + else + if (Keyboard[sc_M]) // M = memory info + { + DebugMemory(); + return 1; + } +#if IN_DEVELOPMENT +#if (!BETA_TEST) + else if (Keyboard[sc_N]) // N = no clip + { + gamestate.flags ^= GS_CLIP_WALLS; + CenterWindow (18,3); + if (gamestate.flags & GS_CLIP_WALLS) + US_PrintCentered ("NO clipping OFF"); + else + US_PrintCentered ("No clipping ON"); + VW_UpdateScreen(); + IN_Ack (); + return 1; + } +#endif + else if (Keyboard[sc_P]) // P = pause with no screen disruptioon + { + PicturePause (); + return 1; + } +#endif + else if (Keyboard[sc_Q]) // Q = fast quit + Quit (NULL); +#if IN_DEVELOPMENT + else if (Keyboard[sc_T]) // T = shape test + { + ShapeTest (); + return 1; + } +#endif + else if (Keyboard[sc_O]) // O = Show Push Walls + { + ExtraRadarFlags ^= OV_PUSHWALLS; + CenterWindow (24,3); + if (ExtraRadarFlags & OV_PUSHWALLS) + US_PrintCentered ("AUTOMAP: Show PWalls ON"); + else + US_PrintCentered ("AUTOMAP: Show PWalls OFF"); + VW_UpdateScreen(); + IN_Ack(); + return 1; + } + else if (Keyboard[sc_U]) // Unlock All Floors + { + int i; + CenterWindow (24,3); + US_PrintCentered ("Unlock All Floors!"); + VW_UpdateScreen(); + IN_Ack(); + + for (i=0;i<11;i++) + gamestuff.level[i].locked=false; + + 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_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_W]) // W = warp to level + { + ForceLoadDefault=Keyboard[sc_LShift]|Keyboard[sc_RShift]|Keyboard[sc_CapsLock]; + + CenterWindow(26,5); + PrintY+=6; + if (ForceLoadDefault) + US_Print(" --- LOAD DEFAULT ---\n"); + US_Print(" Current map: "); + US_PrintUnsigned(gamestate.mapon); + US_Print("\n Enter map number: "); + VW_UpdateScreen(); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (!esc) + { + level = atoi (str); + if (level>-1 && level<=MAX_WARP_LEVEL) + { + gamestate.lastmapon = gamestate.mapon; + gamestate.mapon = level-1; + playstate = ex_warped; + if (ForceLoadDefault) + BONUS_QUEUE = BONUS_SHOWN = 0; + } + } + return 1; + } + else if (Keyboard[sc_Home]) // Dec top color + { +#ifdef CEILING_FLOOR_COLORS + if (gamestate.flags & GS_DRAW_CEILING) +#endif + { + CeilingTile = DecRange(CeilingTile,NUM_TILES-1); + SetPlaneViewSize (); // Init new textures + return(1); + } +#ifdef CEILING_FLOOR_COLORS + else + { + TopColor = DecRange((TopColor&0xff),0xff); + TopColor |= (TopColor<<8); + } +#endif + } + else if (Keyboard[sc_PgUp]) // Inc top color + { +#ifdef CEILING_FLOOR_COLORS + if (gamestate.flags & GS_DRAW_CEILING) +#endif + { + CeilingTile = IncRange(CeilingTile,NUM_TILES-1); + SetPlaneViewSize (); // Init new textures + return(1); + } +#ifdef CEILING_FLOOR_COLORS + else + { + TopColor = IncRange((TopColor&0xff),0xff); + TopColor |= (TopColor<<8); + } +#endif + } + else if (Keyboard[sc_End]) // Dec bottom color + { +#ifdef CEILING_FLOOR_COLORS + if (gamestate.flags & GS_DRAW_FLOOR) +#endif + { + FloorTile = DecRange(FloorTile,NUM_TILES-1); + SetPlaneViewSize (); // Init new textures + return(1); + } +#ifdef CEILING_FLOOR_COLORS + else + { + BottomColor = DecRange((BottomColor&0xff),0xff); + BottomColor |= (BottomColor<<8); + } +#endif + } + else if (Keyboard[sc_PgDn]) // Inc bottom color + { +#ifdef CEILING_FLOOR_COLORS + if (gamestate.flags & GS_DRAW_FLOOR) +#endif + { + FloorTile = IncRange(FloorTile,NUM_TILES-1); + SetPlaneViewSize (); // Init new textures + return(1); + } +#ifdef CEILING_FLOOR_COLORS + else + { + BottomColor = IncRange((BottomColor&0xff),0xff); + BottomColor |= (BottomColor<<8); + } +#endif + } + +#if (IN_DEVELOPMENT) +#if !BETA_TEST + else if (Keyboard[sc_Y]) + { + GivePoints(100000L,false); + } +#endif +#endif + + if (gamestate.flags & GS_LIGHTING) // Shading adjustments + { + if (Keyboard[sc_Plus] && normalshade_div < 12) + normalshade_div++; + else + if (Keyboard[sc_Minus] && normalshade_div>1) + normalshade_div--; + + normalshade=(3*(maxscale>>2))/normalshade_div; + + if (Keyboard[sc_RBrace] && shade_max < 63) + shade_max++; + else + if (Keyboard[sc_LBrace] && shade_max > 5) + shade_max--; + } + + return 0; +} + +#if 0 +/* +=================== += += OverheadRefresh += +=================== +*/ + +void OverheadRefresh (void) +{ + unsigned x,y,endx,endy,sx,sy; + unsigned tile; + + + endx = maporgx+VIEWTILEX; + endy = maporgy+VIEWTILEY; + + for (y=maporgy;y>12)); + LatchDrawChar(sx+8,sy,NUMBERCHARS+((tile&0x0f00)>>8)); + LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4)); + LatchDrawChar(sx+8,sy+8,NUMBERCHARS+(tile&0x000f)); + } + } + +} + + +/* +=================== += += ViewMap += +=================== +*/ + +void ViewMap (void) +{ + boolean button0held; + + viewtype = actoratview; +// button0held = false; + + + maporgx = player->tilex - VIEWTILEX/2; + if (maporgx<0) + maporgx = 0; + if (maporgx>MAPSIZE-VIEWTILEX) + maporgx=MAPSIZE-VIEWTILEX; + maporgy = player->tiley - VIEWTILEY/2; + if (maporgy<0) + maporgy = 0; + if (maporgy>MAPSIZE-VIEWTILEY) + maporgy=MAPSIZE-VIEWTILEY; + + do + { +// +// let user pan around +// + PollControls (); + if (controlx < 0 && maporgx>0) + maporgx--; + if (controlx > 0 && maporgx0) + maporgy--; + if (controly > 0 && maporgyvisview) + viewtype = mapview; + } + if (!c.button0) + button0held = false; +#endif + + OverheadRefresh (); + + } while (!Keyboard[sc_Escape]); + + IN_ClearKeysDown (); +} +#endif + + + +#if IN_DEVELOPMENT +//------------------------------------------------------------------------- +// CalcMemFree() +//------------------------------------------------------------------------- +void CalcMemFree(void) +{ + __PUR_MEM_AVAIL__ = MM_TotalFree(); + __FREE_MEM_AVAIL__ = MM_UnusedMemory(); +} +#endif diff --git a/3D_DEF.H b/3D_DEF.H new file mode 100644 index 0000000..b00f244 --- /dev/null +++ b/3D_DEF.H @@ -0,0 +1,3331 @@ +#include "ID_HEADS.H" +#include "jm_io.h" +#include +#include +#include +#include "jm_cio.h" + +//#define DEMOS_EXTERN +//#define MYPROFILE +//#define TRACK_ENEMY_COUNT +#define OBJ_RESERV + +#define __VERSION__ "V1.01" + +#define GOLD_MORPH_LEVEL (19) // Level which Dr. GoldFire Morphs. + +#define VERSION_TEXT_COLOR (0x82) +#define VERSION_TEXT_BKCOLOR (0x80) + +#define NO_SHADING ((char)0x80) +#define LAMP_ON_SHADING ((char)-12) +#define EXPLOSION_SHADING ((char)-52) + +#define PAGEFLIP + +#define BETA_TEST (false) +#define LOOK_FOR_DEAD_GUYS (false) + +#define BETA_CODE "NEWGAME" + +#define MIN_MEM_NEEDED (231568l) // 560K +#define LIMITED_AMMO + +#define SHADE_MAX 51 +#define SHADE_DIV 6 + +#define BETA_MONTH 8 +#define BETA_DAY 1 +#define BETA_YEAR 1994 + +#define PLAYTEMP_FILE "PLAYTEMP" +#define OLD_PLAYTEMP_FILE "OLDPTEMP" + +#define DISK_SPACE_NEEDED (1024l*1024l) + +#define BORDER_HI_COLOR 0x85 +#define BORDER_MED_COLOR 0x82 +#define BORDER_LO_COLOR 0x80 +#define BORDER_TEXT_COLOR 0xaf + + +// FONT DEFINES +// +#define ID_FONT (STARTFONT) +#define BIG_FONT (STARTFONT+1) +#define SIXPOINT_FONT (STARTFONT+2) +#define TALL_FONT (STARTFONT+3) +#define COAL_FONT (STARTFONT+4) +#define RADAR_FONT (STARTFONT+5) + +//#define LOCKED_FLOORS + +#define ID_CACHE_BRIEFS +#define ID_CACHE_HELP +#define ID_CACHE_LOSE +#define ID_CACHE_CREDITS + + +//#define DEBUG_STATICS +//#define DEBUG_ACTORS + +//========================================================================== +// +// GAME VERSION DEPENDANT DEFINATIONS +// +//========================================================================== + +void jsprintf(char *msg, ...); + +/* +============================================================================= + + MACROS + +============================================================================= +*/ + + +#define BASE_GRAY 0x8 +#define HIGH_GRAY 0xb +#define LOW_GRAY 0x5 +#define BASE_GRN 0x76 +#define HIGH_GRN 0x78 +#define LOW_GRN 0x74 + + +#define MAX_GUN_DELAY 12 + +#define MAX_RADAR_ENERGY 14400 +#define RADAR_PAK_VALUE 60*15 + +#define MAXKEYS 1 + +#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)) + +//#define STATUSDRAWPIC(x, y, picnum) JLatchDrawPic((x),(y+(200-STATUSLINES)),(picnum)) + +#define FMAPWIDTH ((fixed)mapwidth<x,from_obj->y,to_obj->x,to_obj->y,from_obj->angle) + +// SmartAnim macro +#define ANIM_INFO(o) ((ofs_anim_t *)&(o)->temp3) + + +#define DISPLAY_MSG_STD_TIME (5*60) // Tics display len +#define DISPLAY_TIMED_MSG(msg,pri,type) DisplayInfoMsg((msg),(pri),DISPLAY_MSG_STD_TIME,(type)) +#define DISPLAY_MSG(msg,pri,type) DisplayInfoMsg((msg),(pri),0,(type)) + + +// SMART_ACTORS is the "case" used for certain switch statements. +// + +#define SMART_ACTORS proguardobj: \ + case rentacopobj: \ + case gen_scientistobj + + +#define SECURITY_LAMPS_ALERTED (madenoise) + +// Barrier Code Stuff + +#define MAX_BARRIER_SWITCHES 40 // max number level wall switches + + +#define SLIDE_TEMP(obj) ((unsigned)obj->hitpoints) + +// +// M_BASE1 - represents 100 percent in 1st base +// M_BASE2 - represents 100 percent in 2nd base +// F_BASE2 - fractional portion of 2nd base +// SCALE - arbitrary scaling value (bigger number means more accurate) +// +// RETURNS: F_BASE1 - represents fractional portion of 1st base +// +// ex: RATIO(320,16,8,7) returns 160 +// +// Make sure values used won't overflow a WORD! In general, if largest number +// to be used (320 in ex: above) * (1<>SCALE) +#define LRATIO(M_BASE1,M_BASE2,F_BASE2,SCALE) (((long)M_BASE1*(((long)F_BASE2<>SCALE) + + +#define MAX_INF_AREA_MSGS 6 +#define MAX_LOCATION_DESC_LEN 45 + +#define DOOR_RUBBLE_STATNUM 112 // Door Rubble sprite + +#define SpawnExplosion(a,b) SpawnCusExplosion((a),(b),SPR_EXPLOSION_1,4,5,explosionobj) +#define SpawnFlash(a,b) SpawnCusExplosion((a),(b),SPR_EXPLOSION_1,4,5,deadobj) +#define InitSmartSpeedAnim(a, b, c, d, e, f, g) InitAnim((a),(b),(c),(d),(e),(f),(g),(g)) + +/* +============================================================================= + + GLOBAL CONSTANTS + +============================================================================= +*/ + +#define OV_ACTORS 0x0001 +#define OV_SHOWALL 0x0002 +#define OV_KEYS 0x0004 +#define OV_PUSHWALLS 0x0008 + +#define TT_TRAVELED 0x01 +#define TT_KEYS 0x02 + +// Max number of concession reply messages + +//#define CON_HINTS // Un/Comment to support concession hints + +#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 MAXCONCESSIONS 15 // max number of concession machines +#define MAXWALLTILES 64 // max number of wall tiles +#define MAXEAWALLS 12 // max electro-alien wall sockets + + + + +#define GS_NEEDCOORD 0 +#define GS_FIRSTTIME 1 +#define GS_COORDFOUND 2 +#define GS_NO_MORE 3 + +#define GOLDIE_MAX_SPAWNS 10 // Max Number of spawn points for Goldstern +#define MIN_GOLDIE_FIRST_WAIT (5*60) // Min wait time for FIRST goldstern (5 Secs) +#define MAX_GOLDIE_FIRST_WAIT (15*60) // Max wait time for FIRST goldstern (15 Secs) +#define MIN_GOLDIE_WAIT (30*60) // Min wait time for next goldstern (30 Secs) +#define MAX_GOLDIE_WAIT (4*60*60) // Max wait time for next goldstern (4 Mins) + + + + +// +// tile constants +// + +#define ICONARROWS 90 +#define PUSHABLETILE 98 +#define EXITTILE 99 // at end of castle +#define AREATILE 108 // first of NUMAREAS floor tiles +#define HIDDENAREATILE 162 // first of hidden floor tiles +#define NUMAREAS 45 +#define DOORTRIGGERTILE 158 +#define SMART_OFF_TRIGGER 159 +#define SMART_ON_TRIGGER 160 +#define ELEVATORTILE2 27 // Space Will Switch +#define TRANSPORTERTILE 21 +#define DIRECTTRANSPORTTILE 32 // Wall to use on direct level transporters +#define SODATILE 15 +#define TERMINALTILE 17 +#define FOODTILE 18 +#define AMBUSHTILE 106 +#define RKEY_TILE 72 +#define YKEY_TILE 73 +#define BKEY_TILE 74 +#define BFG_TILE 75 +#define ION_TILE 76 +#define DETONATOR_TILE 77 +#define CLOAK_TILE 78 +#define LINC_TILE 79 +#define CLOAK_AMBUSH_TILE 80 +#define EATILE 24 +#define ON_SWITCH 45 // ON Wall Switch - +#define OFF_SWITCH 57 // OFF Wall Switch - +#define WINTIGGERTILE 157 // Win Tile +#define NUMBERCHARS 9 + +#define START_TEXTURES 125 // Start of Textures - (Also LAST_WALL_TILE NUM) +#define NUM_TILES (PMSpriteStart) + + +//---------------- + +#define EXTRAPOINTS 400000 + +#define MAX_EXTRA_LIVES 4 + +#define PLAYERSPEED 3000 +#define RUNSPEED 6000 + +#define SCREENSEG 0xa000 + +#define SCREENBWIDE 80 + +#define HEIGHTRATIO 0.41 +#define TOP_STRIP_HEIGHT 16 // Pix height of top strip. + +//#define BORDERCOLOR 116 +#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 MAX_WVIEW_DIST (44) // Max wrap_view dist in TILES + + + +#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 48 + +#define SCREENSIZE (SCREENBWIDE*208) +#define PAGE1START 0 +#define PAGE2START (SCREENSIZE) +#define PAGE3START (SCREENSIZE*2u) +#define FREESTART (SCREENSIZE*3u) + + +#define PIXRADIUS 512 + +#define STARTAMMO 8 + +// Token Definations + +#define MAX_TOKENS 25 + + + +// Ammo/Weapon Definations + +//#define NUM_AMMO_SEGS 42 // 42 Color segments (OR 21 2-Color segs) +#define MAX_AMMO 100 // Max ammount of ammo for any weapon +#define AUTOCHARGE_WAIT 50 // Tics wait time for a full charge +#define MAX_PLASMA_DETONATORS 100 // Max number of Plasma Detonators +#define PLASMA_DETONATORS_DELAY 60*4 // Number of tics before plasma detonator explodes + + + + +// gamestate.flags flag values + +#define GS_HEARTB_SOUND 0x0001 + +#ifdef CEILING_FLOOR_COLORS +#define GS_DRAW_CEILING 0x0002 +#endif + +#define GS_CLIP_WALLS 0x0004 + +#ifdef CEILING_FLOOR_COLORS +#define GS_DRAW_FLOOR 0x0008 +#endif + +#define GS_VIRGIN_LEVEL 0x0010 +#define GS_CHECK_STATS_BONUS 0x0020 +#define GS_ATTACK_INFOAREA 0x0040 +#define GS_KILL_INF_WARN 0x0080 +#define GS_SHOW_OVERHEAD 0x0100 +#define GS_BAD_DIZ_FILE 0x0200 +#define GS_MUSIC_TEST 0x0400 +#define GS_LIGHTING 0x0800 +#define GS_TICS_FOR_SCORE 0x1000 +#define GS_NOWAIT 0x2000 +#define GS_STARTLEVEL 0x4000 +#define GS_QUICKRUN 0x8000 + +// object flag values - Oh Shit Longs! + +#define FL_SHOOTABLE 0x00000001 +#define FL_BONUS 0x00000002 +#define FL_NEVERMARK 0x00000004 +#define FL_VISABLE 0x00000008 +#define FL_ATTACKMODE 0x00000010 +#define FL_FIRSTATTACK 0x00000020 +#define FL_AMBUSH 0x00000040 +#define FL_NONMARK 0x00000080 +#define FL_SOLID 0x00000100 +#define FL_STATIONARY 0x00000200 +#define FL_FRIENDLY 0x00000400 +#define FL_DEADGUY 0x00000800 +#define FL_RUNAWAY 0x00001000 +#define FL_RUNTOSTATIC 0x00002000 +#define FL_OFFSET_STATES 0x00004000 +#define FL_INFORMANT 0x00008000 +#define FL_INTERROGATED 0x00010000 +#define FL_RANDOM_TURN 0x00020000 +#define FL_NO_SLIDE 0x00040000 +#define FL_MUST_ATTACK 0x00080000 +#define FL_ALERTED 0x00100000 +#define FL_FREEZE 0x00200000 +#define FL_HAS_AMMO 0x00400000 +#define FL_PROJ_TRANSPARENT 0x00800000 +#define FL_PROJ_CHECK_TRANSPARENT 0x01000000 +#define FL_HAS_TOKENS 0x02000000 +#define FL_LOCKED_STATE 0x04000000 +#define FL_BARRIER 0x08000000 +#define FL_SHOOTMODE 0x10000000 +#define FL_SLIDE_INIT 0x20000000 +#define FL_STORED_OBJPTR 0x40000000 +#define FL_FAKE_STATIC 0x80000000 + +#define FL_BARRIER_DAMAGE FL_HAS_TOKENS + + +// object flags2 values + +#define FL2_BFGSHOT_SOLID 0x0001 +#define FL2_BFG_SHOOTABLE 0x0002 +#define FL2_NOTGUNSHOOTABLE 0x0004 +#define FL2_SCARED 0x0008 +#define FL2_DROP_RKEY 0x0010 +#define FL2_DROP_YKEY 0x0020 +#define FL2_DROP_BKEY 0x0040 +#define FL2_DROP_BFG 0x0080 +#define FL2_DROP_ION 0x0100 +#define FL2_DROP_DETONATOR 0x0200 +#define FL2_CLOAKED 0x0400 +#define FL2_LINC 0x0800 +#define FL2_DAMAGE_CLOAK 0x1000 + + +// Run Reason Flags -- Why am I running..Duh.. + +#define RR_AMMO 0x0001 +#define RR_HEALTH 0x0002 +#define RR_INTERROGATED 0x0004 +#define RR_CASUAL_PICKUP 0x0008 +#define RR_SCARED 0x0010 + +// 0xFFxx Special Tile Flags (Flags in MASKED plane of Ted Maps) + +#define TF_STARFIELD 0x01 +#define TF_WRAP_VIEW 0x02 + +// +// Concession Machine Types + +#define CT_HINT 0x0 +#define CT_FOOD 0x1 +#define CT_BEVS 0x2 + + +// +// Radar switches for terminal Floor Cheat + +#define RS_GOLDSTERN_TRACKER 0x0001 +#define RS_PERSONNEL_TRACKER 0x0002 +#define RS_SECURITY_STATUS 0x0004 + +// +// Door Flags +// +#define DR_BLASTABLE 0x01 + +// +// Smart Trigger Possiblities +// +#define ST_TURN_OFF 0x00 +#define ST_TURN_ON 0x01 +#define ST_TOGGLE 0x02 + + +// +// +// + +#define LT_GUN_DELAY gamestate.lastammo_leds +#define RT_GUN_DELAY gamestate.ammo_leds +#define GUN_TOGGLE gamestate.ammo + + +#define CANT_SAVE_GAME_TXT " Can't save this game! \n" \ + " Hard Disk FULL!" + +typedef enum ss_type { + ss_normal, + ss_quick, + ss_justcalc, +} ss_type; + +typedef enum cds_io_type { + cds_dos_print, + cds_id_print, + cds_menu_print, +} cds_io_type; + + +typedef enum sp_type { + sp_normal, + sp_loading, + sp_saving, + sp_changeview, + sp_teleporting, +} sp_type; + +// +// sprite constants +// + +typedef enum { + SPR_DEMO, + +// +// static sprites +// + SPR_STAT_0, // SPR1V +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_1, +#endif + SPR_STAT_2, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_3,SPR_STAT_4, + SPR_STAT_5,SPR_STAT_6,SPR_STAT_7, +#endif + + SPR_STAT_8,SPR_STAT_9,SPR_STAT_10,SPR_STAT_11, // SPR2V + SPR_STAT_12,SPR_STAT_13,SPR_STAT_14,SPR_STAT_15, + +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_16,SPR_STAT_17, // SPR3V +#endif + SPR_STAT_18, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_19, + SPR_STAT_20,SPR_STAT_21,SPR_STAT_22,SPR_STAT_23, +#endif + + SPR_STAT_24, // SPR4V +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_25, +#endif + SPR_STAT_26,SPR_STAT_27, + SPR_STAT_28,SPR_STAT_29, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_30, +#endif + SPR_STAT_31, + + SPR_STAT_32,SPR_STAT_33,SPR_STAT_34,SPR_STAT_35, // SPR5V + SPR_STAT_36, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_37, +#endif + SPR_STAT_38, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_39, +#endif + + SPR_STAT_40,SPR_STAT_41,SPR_STAT_42,SPR_STAT_43, // SPR7V + SPR_STAT_44,SPR_STAT_45,SPR_STAT_46,SPR_STAT_47, + + SPR_STAT_48,SPR_STAT_49, // SPR8V + SPR_STAT_50,SPR_STAT_51, + + SPR_STAT_52,SPR_STAT_53,SPR_STAT_54,SPR_STAT_55, // SPR9V + SPR_STAT_56,SPR_CRATE_1, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_CRATE_2,SPR_CRATE_3, +#endif + + SPR_STAT_57, // SPR10V +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_58,SPR_STAT_59, +#endif + SPR_STAT_60, + SPR_STAT_61,SPR_STAT_62,SPR_STAT_63, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_64, +#endif + + SPR_STAT_65,SPR_STAT_66,SPR_STAT_67,SPR_STAT_68, // SPR11V + SPR_STAT_69, +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_70,SPR_STAT_71,SPR_STAT_72, +#endif + +#if GAME_VERSION != SHAREWARE_VERSION + SPR_STAT_73,SPR_STAT_74,SPR_STAT_75,SPR_STAT_76, // SPR12V +#endif + SPR_STAT_77,SPR_STAT_78,SPR_STAT_79, + + SPR_DOORBOMB,SPR_ALT_DOORBOMB,SPR_RUBBLE,SPR_BONZI_TREE, // OBJECTS1V + SPR_AUTOMAPPER,SPR_POT_PLANT,SPR_TUBE_PLANT,SPR_HITECH_CHAIR, + + +// +// aesthetics +// + SPR_AIR_VENT, + SPR_BLOOD_DRIP1,SPR_BLOOD_DRIP2,SPR_BLOOD_DRIP3,SPR_BLOOD_DRIP4, + SPR_WATER_DRIP1,SPR_WATER_DRIP2,SPR_WATER_DRIP3,SPR_WATER_DRIP4, + + SPR_DECO_ARC_1,SPR_DECO_ARC_2,SPR_DECO_ARC_3, + + SPR_GRATE, + SPR_STEAM_1,SPR_STEAM_2,SPR_STEAM_3,SPR_STEAM_4, + + SPR_STEAM_PIPE, + SPR_PIPE_STEAM_1,SPR_PIPE_STEAM_2,SPR_PIPE_STEAM_3,SPR_PIPE_STEAM_4, + +// +// Dead Actors (from Blake Stone: AOG) +// + SPR_DEAD_RENT, SPR_DEAD_PRO, SPR_DEAD_SWAT, + +// +// Rent-A-Cop +// + SPR_RENT_S_1,SPR_RENT_S_2,SPR_RENT_S_3,SPR_RENT_S_4, + SPR_RENT_S_5,SPR_RENT_S_6,SPR_RENT_S_7,SPR_RENT_S_8, + + SPR_RENT_W1_1,SPR_RENT_W1_2,SPR_RENT_W1_3,SPR_RENT_W1_4, + SPR_RENT_W1_5,SPR_RENT_W1_6,SPR_RENT_W1_7,SPR_RENT_W1_8, + + SPR_RENT_W2_1,SPR_RENT_W2_2,SPR_RENT_W2_3,SPR_RENT_W2_4, + SPR_RENT_W2_5,SPR_RENT_W2_6,SPR_RENT_W2_7,SPR_RENT_W2_8, + + SPR_RENT_W3_1,SPR_RENT_W3_2,SPR_RENT_W3_3,SPR_RENT_W3_4, + SPR_RENT_W3_5,SPR_RENT_W3_6,SPR_RENT_W3_7,SPR_RENT_W3_8, + + SPR_RENT_W4_1,SPR_RENT_W4_2,SPR_RENT_W4_3,SPR_RENT_W4_4, + SPR_RENT_W4_5,SPR_RENT_W4_6,SPR_RENT_W4_7,SPR_RENT_W4_8, + + SPR_RENT_DIE_1,SPR_RENT_DIE_2,SPR_RENT_DIE_3,SPR_RENT_DIE_4, + SPR_RENT_PAIN_1,SPR_RENT_DEAD, + + SPR_RENT_SHOOT1,SPR_RENT_SHOOT2,SPR_RENT_SHOOT3, + +// +// PRO-GUARD +// + SPR_PRO_S_1,SPR_PRO_S_2,SPR_PRO_S_3,SPR_PRO_S_4, + SPR_PRO_S_5,SPR_PRO_S_6,SPR_PRO_S_7,SPR_PRO_S_8, + + SPR_PRO_W1_1,SPR_PRO_W1_2,SPR_PRO_W1_3,SPR_PRO_W1_4, + SPR_PRO_W1_5,SPR_PRO_W1_6,SPR_PRO_W1_7,SPR_PRO_W1_8, + + SPR_PRO_W2_1,SPR_PRO_W2_2,SPR_PRO_W2_3,SPR_PRO_W2_4, + SPR_PRO_W2_5,SPR_PRO_W2_6,SPR_PRO_W2_7,SPR_PRO_W2_8, + + SPR_PRO_W3_1,SPR_PRO_W3_2,SPR_PRO_W3_3,SPR_PRO_W3_4, + SPR_PRO_W3_5,SPR_PRO_W3_6,SPR_PRO_W3_7,SPR_PRO_W3_8, + + SPR_PRO_W4_1,SPR_PRO_W4_2,SPR_PRO_W4_3,SPR_PRO_W4_4, + SPR_PRO_W4_5,SPR_PRO_W4_6,SPR_PRO_W4_7,SPR_PRO_W4_8, + + SPR_PRO_PAIN_1,SPR_PRO_DIE_1,SPR_PRO_DIE_2,SPR_PRO_DIE_3, + SPR_PRO_PAIN_2,SPR_PRO_DIE_4,SPR_PRO_DEAD, + + SPR_PRO_SHOOT1,SPR_PRO_SHOOT2,SPR_PRO_SHOOT3, + +// +// swat +// + SPR_SWAT_S_1,SPR_SWAT_S_2,SPR_SWAT_S_3,SPR_SWAT_S_4, + SPR_SWAT_S_5,SPR_SWAT_S_6,SPR_SWAT_S_7,SPR_SWAT_S_8, + + SPR_SWAT_W1_1,SPR_SWAT_W1_2,SPR_SWAT_W1_3,SPR_SWAT_W1_4, + SPR_SWAT_W1_5,SPR_SWAT_W1_6,SPR_SWAT_W1_7,SPR_SWAT_W1_8, + + SPR_SWAT_W2_1,SPR_SWAT_W2_2,SPR_SWAT_W2_3,SPR_SWAT_W2_4, + SPR_SWAT_W2_5,SPR_SWAT_W2_6,SPR_SWAT_W2_7,SPR_SWAT_W2_8, + + SPR_SWAT_W3_1,SPR_SWAT_W3_2,SPR_SWAT_W3_3,SPR_SWAT_W3_4, + SPR_SWAT_W3_5,SPR_SWAT_W3_6,SPR_SWAT_W3_7,SPR_SWAT_W3_8, + + SPR_SWAT_W4_1,SPR_SWAT_W4_2,SPR_SWAT_W4_3,SPR_SWAT_W4_4, + SPR_SWAT_W4_5,SPR_SWAT_W4_6,SPR_SWAT_W4_7,SPR_SWAT_W4_8, + + SPR_SWAT_PAIN_1,SPR_SWAT_DIE_1,SPR_SWAT_DIE_2,SPR_SWAT_DIE_3, + SPR_SWAT_PAIN_2,SPR_SWAT_DIE_4,SPR_SWAT_DEAD, + + SPR_SWAT_SHOOT1,SPR_SWAT_SHOOT2,SPR_SWAT_SHOOT3, + + SPR_SWAT_WOUNDED1,SPR_SWAT_WOUNDED2,SPR_SWAT_WOUNDED3,SPR_SWAT_WOUNDED4, + +// +// GENERAL SCIENTIST +// + 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, + +// +// Bad Boy Dr. Goldstern +// + SPR_GOLD_S_1,SPR_GOLD_S_2,SPR_GOLD_S_3,SPR_GOLD_S_4, + SPR_GOLD_S_5,SPR_GOLD_S_6,SPR_GOLD_S_7,SPR_GOLD_S_8, + + SPR_GOLD_W1_1,SPR_GOLD_W1_2,SPR_GOLD_W1_3,SPR_GOLD_W1_4, + SPR_GOLD_W1_5,SPR_GOLD_W1_6,SPR_GOLD_W1_7,SPR_GOLD_W1_8, + + SPR_GOLD_W2_1,SPR_GOLD_W2_2,SPR_GOLD_W2_3,SPR_GOLD_W2_4, + SPR_GOLD_W2_5,SPR_GOLD_W2_6,SPR_GOLD_W2_7,SPR_GOLD_W2_8, + + SPR_GOLD_W3_1,SPR_GOLD_W3_2,SPR_GOLD_W3_3,SPR_GOLD_W3_4, + SPR_GOLD_W3_5,SPR_GOLD_W3_6,SPR_GOLD_W3_7,SPR_GOLD_W3_8, + + SPR_GOLD_W4_1,SPR_GOLD_W4_2,SPR_GOLD_W4_3,SPR_GOLD_W4_4, + SPR_GOLD_W4_5,SPR_GOLD_W4_6,SPR_GOLD_W4_7,SPR_GOLD_W4_8, + + SPR_GOLD_PAIN_1, + + SPR_GOLD_WRIST_1,SPR_GOLD_WRIST_2, + + SPR_GOLD_SHOOT1,SPR_GOLD_SHOOT2,SPR_GOLD_SHOOT3, + + SPR_GOLD_WARP1,SPR_GOLD_WARP2,SPR_GOLD_WARP3,SPR_GOLD_WARP4, + SPR_GOLD_WARP5, + + SPR_GOLD_DEATH1,SPR_GOLD_DEATH2,SPR_GOLD_DEATH3,SPR_GOLD_DEATH4, + SPR_GOLD_DEATH5,SPR_MGOLD_OUCH, + + SPR_GOLD_MORPH1,SPR_GOLD_MORPH2,SPR_GOLD_MORPH3,SPR_GOLD_MORPH4, + SPR_GOLD_MORPH5,SPR_GOLD_MORPH6,SPR_GOLD_MORPH7,SPR_GOLD_MORPH8, + + SPR_MGOLD_WALK1,SPR_MGOLD_WALK2,SPR_MGOLD_WALK3,SPR_MGOLD_WALK4, + SPR_MGOLD_ATTACK1,SPR_MGOLD_ATTACK2,SPR_MGOLD_ATTACK3,SPR_MGOLD_ATTACK4, + + SPR_MGOLD_SHOT1,SPR_MGOLD_SHOT2,SPR_MGOLD_SHOT3, + SPR_MGOLD_SHOT_EXP1,SPR_MGOLD_SHOT_EXP2,SPR_MGOLD_SHOT_EXP3, + +// +// GROUND SCOUT +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_GSCOUT_W1_1,SPR_GSCOUT_W1_2,SPR_GSCOUT_W1_3,SPR_GSCOUT_W1_4, + SPR_GSCOUT_W1_5,SPR_GSCOUT_W1_6,SPR_GSCOUT_W1_7,SPR_GSCOUT_W1_8, + + SPR_GSCOUT_W2_1,SPR_GSCOUT_W2_2,SPR_GSCOUT_W2_3,SPR_GSCOUT_W2_4, + SPR_GSCOUT_W2_5,SPR_GSCOUT_W2_6,SPR_GSCOUT_W2_7,SPR_GSCOUT_W2_8, + + SPR_GSCOUT_W3_1,SPR_GSCOUT_W3_2,SPR_GSCOUT_W3_3,SPR_GSCOUT_W3_4, + SPR_GSCOUT_W3_5,SPR_GSCOUT_W3_6,SPR_GSCOUT_W3_7,SPR_GSCOUT_W3_8, + + SPR_GSCOUT_W4_1,SPR_GSCOUT_W4_2,SPR_GSCOUT_W4_3,SPR_GSCOUT_W4_4, + SPR_GSCOUT_W4_5,SPR_GSCOUT_W4_6,SPR_GSCOUT_W4_7,SPR_GSCOUT_W4_8, + + SPR_GSCOUT_DIE1,SPR_GSCOUT_DIE2,SPR_GSCOUT_DIE3,SPR_GSCOUT_DIE4, + SPR_GSCOUT_DIE5,SPR_GSCOUT_DIE6,SPR_GSCOUT_DIE7,SPR_GSCOUT_DIE8, + + SPR_GSCOUT_DEAD, +#endif + +// +// FLOATING SCOUT +// + SPR_FSCOUT_W1_1,SPR_FSCOUT_W1_2,SPR_FSCOUT_W1_3,SPR_FSCOUT_W1_4, + SPR_FSCOUT_W1_5,SPR_FSCOUT_W1_6,SPR_FSCOUT_W1_7,SPR_FSCOUT_W1_8, + +#if GAME_VERSION != SHAREWARE_VERSION + SPR_FSCOUT_W2_1,SPR_FSCOUT_W2_2,SPR_FSCOUT_W2_3,SPR_FSCOUT_W2_4, + SPR_FSCOUT_W2_5,SPR_FSCOUT_W2_6,SPR_FSCOUT_W2_7,SPR_FSCOUT_W2_8, + + SPR_FSCOUT_W3_1,SPR_FSCOUT_W3_2,SPR_FSCOUT_W3_3,SPR_FSCOUT_W3_4, + SPR_FSCOUT_W3_5,SPR_FSCOUT_W3_6,SPR_FSCOUT_W3_7,SPR_FSCOUT_W3_8, + + SPR_FSCOUT_W4_1,SPR_FSCOUT_W4_2,SPR_FSCOUT_W4_3,SPR_FSCOUT_W4_4, + SPR_FSCOUT_W4_5,SPR_FSCOUT_W4_6,SPR_FSCOUT_W4_7,SPR_FSCOUT_W4_8, +#endif + + SPR_FSCOUT_DIE1,SPR_FSCOUT_DIE2,SPR_FSCOUT_DIE3,SPR_FSCOUT_DIE4, + SPR_FSCOUT_DIE5,SPR_FSCOUT_DIE6,SPR_FSCOUT_DIE7,SPR_FSCOUT_DEAD, + +// +// GENERAL EXPLOSION ANIM +// + SPR_EXPLOSION_1,SPR_EXPLOSION_2,SPR_EXPLOSION_3,SPR_EXPLOSION_4, + SPR_EXPLOSION_5, + +// +// VITAL DEFENCE OBJECT +// +//#if GAME_VERSION != SHAREWARE_VERSION +// SPR_VITAL_STAND, +// SPR_VITAL_DIE_1, SPR_VITAL_DIE_2, SPR_VITAL_DIE_3,SPR_VITAL_DIE_4, +// SPR_VITAL_DIE_5, SPR_VITAL_DIE_6, SPR_VITAL_DIE_7, SPR_VITAL_DIE_8, +// SPR_VITAL_DEAD_1,SPR_VITAL_DEAD_2,SPR_VITAL_DEAD_3, +// SPR_VITAL_OUCH, +//#endif + +// +// ROTATING CUBE +// + SPR_CUBE1,SPR_CUBE2,SPR_CUBE3,SPR_CUBE4, + SPR_CUBE5,SPR_CUBE6,SPR_CUBE7,SPR_CUBE8, + SPR_CUBE9,SPR_CUBE10, + SPR_CUBE_EXP1,SPR_CUBE_EXP2,SPR_CUBE_EXP3,SPR_CUBE_EXP4, + SPR_CUBE_EXP5,SPR_CUBE_EXP6,SPR_CUBE_EXP7,SPR_CUBE_EXP8, + SPR_DEAD_CUBE, + + +// +// RED SECURITY LIGHTS +// + SPR_SECURITY_NORMAL,SPR_SECURITY_ALERT, + +// +// P.O.D. Alien +// + SPR_POD_EGG,SPR_POD_HATCH1,SPR_POD_HATCH2,SPR_POD_HATCH3, + SPR_POD_WALK1,SPR_POD_WALK2,SPR_POD_WALK3,SPR_POD_WALK4, + SPR_POD_ATTACK1,SPR_POD_ATTACK2,SPR_POD_ATTACK3, + SPR_POD_OUCH, + SPR_POD_DIE1,SPR_POD_DIE2,SPR_POD_DIE3, + SPR_POD_SPIT1,SPR_POD_SPIT2,SPR_POD_SPIT3, + +// +// Electro-Alien +// + SPR_ELEC_APPEAR1,SPR_ELEC_APPEAR2,SPR_ELEC_APPEAR3, + SPR_ELEC_WALK1,SPR_ELEC_WALK2,SPR_ELEC_WALK3,SPR_ELEC_WALK4, + SPR_ELEC_OUCH, + SPR_ELEC_SHOOT1,SPR_ELEC_SHOOT2,SPR_ELEC_SHOOT3, + SPR_ELEC_DIE1,SPR_ELEC_DIE2,SPR_ELEC_DIE3, + SPR_ELEC_SHOT1,SPR_ELEC_SHOT2, + SPR_ELEC_SHOT_EXP1,SPR_ELEC_SHOT_EXP2, + +// +// ElectroSphere +// + SPR_ELECTRO_SPHERE_ROAM1,SPR_ELECTRO_SPHERE_ROAM2,SPR_ELECTRO_SPHERE_ROAM3, + SPR_ELECTRO_SPHERE_OUCH, + SPR_ELECTRO_SPHERE_DIE1,SPR_ELECTRO_SPHERE_DIE2,SPR_ELECTRO_SPHERE_DIE3, + SPR_ELECTRO_SPHERE_DIE4, + +// +// Genetic Guard +// + SPR_GENETIC_W1,SPR_GENETIC_W2,SPR_GENETIC_W3,SPR_GENETIC_W4, + SPR_GENETIC_SWING1,SPR_GENETIC_SWING2,SPR_GENETIC_SWING3, + SPR_GENETIC_DEAD, + SPR_GENETIC_DIE1,SPR_GENETIC_DIE2,SPR_GENETIC_DIE3,SPR_GENETIC_DIE4, + SPR_GENETIC_OUCH, + SPR_GENETIC_SHOOT1,SPR_GENETIC_SHOOT2,SPR_GENETIC_SHOOT3, + +// +// Muntant human type 1 +// + SPR_MUTHUM1_W1,SPR_MUTHUM1_W2,SPR_MUTHUM1_W3,SPR_MUTHUM1_W4, + SPR_MUTHUM1_SWING1,SPR_MUTHUM1_SWING2,SPR_MUTHUM1_SWING3, + SPR_MUTHUM1_DEAD, + SPR_MUTHUM1_DIE1,SPR_MUTHUM1_DIE2,SPR_MUTHUM1_DIE3,SPR_MUTHUM1_DIE4, + SPR_MUTHUM1_OUCH, + SPR_MUTHUM1_SPIT1,SPR_MUTHUM1_SPIT2,SPR_MUTHUM1_SPIT3, + +// +// Muntant human type 2 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_MUTHUM2_W1,SPR_MUTHUM2_W2,SPR_MUTHUM2_W3,SPR_MUTHUM2_W4, + SPR_MUTHUM2_SWING1,SPR_MUTHUM2_SWING2,SPR_MUTHUM2_SWING3, + SPR_MUTHUM2_DEAD, + SPR_MUTHUM2_DIE1,SPR_MUTHUM2_DIE2,SPR_MUTHUM2_DIE3,SPR_MUTHUM2_DIE4, + SPR_MUTHUM2_OUCH, + SPR_MUTHUM2_SPIT1,SPR_MUTHUM2_SPIT2,SPR_MUTHUM2_SPIT3, + + SPR_MUTHUM2_MORPH1,SPR_MUTHUM2_MORPH2,SPR_MUTHUM2_MORPH3,SPR_MUTHUM2_MORPH4, + SPR_MUTHUM2_MORPH5,SPR_MUTHUM2_MORPH6,SPR_MUTHUM2_MORPH7,SPR_MUTHUM2_MORPH8, + SPR_MUTHUM2_MORPH9, +#endif + +// +// Large Cantained Alien +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_LCAN_ALIEN_READY,SPR_LCAN_ALIEN_B1,SPR_LCAN_ALIEN_B2, + SPR_LCAN_ALIEN_B3,SPR_LCAN_ALIEN_EMPTY, + + SPR_LCAN_ALIEN_W1,SPR_LCAN_ALIEN_W2,SPR_LCAN_ALIEN_W3,SPR_LCAN_ALIEN_W4, + SPR_LCAN_ALIEN_SWING1,SPR_LCAN_ALIEN_SWING2,SPR_LCAN_ALIEN_SWING3, + SPR_LCAN_ALIEN_DEAD, + SPR_LCAN_ALIEN_DIE1,SPR_LCAN_ALIEN_DIE2,SPR_LCAN_ALIEN_DIE3, + SPR_LCAN_ALIEN_DIE4,SPR_LCAN_ALIEN_OUCH, + SPR_LCAN_ALIEN_SPIT1,SPR_LCAN_ALIEN_SPIT2,SPR_LCAN_ALIEN_SPIT3, +#endif + +// +// Small Canister Alien +// + SPR_SCAN_ALIEN_READY,SPR_SCAN_ALIEN_B1,SPR_SCAN_ALIEN_B2, + SPR_SCAN_ALIEN_B3,SPR_SCAN_ALIEN_EMPTY, + + SPR_SCAN_ALIEN_W1,SPR_SCAN_ALIEN_W2,SPR_SCAN_ALIEN_W3,SPR_SCAN_ALIEN_W4, + SPR_SCAN_ALIEN_SWING1,SPR_SCAN_ALIEN_SWING2,SPR_SCAN_ALIEN_SWING3, + SPR_SCAN_ALIEN_DEAD, + SPR_SCAN_ALIEN_DIE1,SPR_SCAN_ALIEN_DIE2,SPR_SCAN_ALIEN_DIE3, + SPR_SCAN_ALIEN_DIE4,SPR_SCAN_ALIEN_OUCH, + + SPR_SCAN_ALIEN_SPIT1,SPR_SCAN_ALIEN_SPIT2,SPR_SCAN_ALIEN_SPIT3, + +// +// Gurney Mutant +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_GURNEY_MUT_READY,SPR_GURNEY_MUT_B1,SPR_GURNEY_MUT_B2, + SPR_GURNEY_MUT_B3,SPR_GURNEY_MUT_EMPTY, + + SPR_GURNEY_MUT_W1,SPR_GURNEY_MUT_W2,SPR_GURNEY_MUT_W3,SPR_GURNEY_MUT_W4, + SPR_GURNEY_MUT_SWING1,SPR_GURNEY_MUT_SWING2,SPR_GURNEY_MUT_SWING3, + SPR_GURNEY_MUT_DEAD, + SPR_GURNEY_MUT_DIE1,SPR_GURNEY_MUT_DIE2,SPR_GURNEY_MUT_DIE3, + SPR_GURNEY_MUT_DIE4,SPR_GURNEY_MUT_OUCH, +#endif + +// +// Liquid Alien +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_LIQUID_M1,SPR_LIQUID_M2,SPR_LIQUID_M3, + SPR_LIQUID_R1,SPR_LIQUID_R2,SPR_LIQUID_R3,SPR_LIQUID_R4, + SPR_LIQUID_S1,SPR_LIQUID_S2,SPR_LIQUID_S3, + SPR_LIQUID_OUCH, + SPR_LIQUID_DIE_1,SPR_LIQUID_DIE_2,SPR_LIQUID_DIE_3,SPR_LIQUID_DIE_4, + SPR_LIQUID_DEAD, + SPR_LIQUID_SHOT_FLY_1,SPR_LIQUID_SHOT_FLY_2,SPR_LIQUID_SHOT_FLY_3, + SPR_LIQUID_SHOT_BURST_1,SPR_LIQUID_SHOT_BURST_2,SPR_LIQUID_SHOT_BURST_3, +#endif + +// +// SPIT SHOTS +// + SPR_SPIT1_1,SPR_SPIT1_2,SPR_SPIT1_3, + SPR_SPIT_EXP1_1,SPR_SPIT_EXP1_2,SPR_SPIT_EXP1_3, + + SPR_SPIT2_1,SPR_SPIT2_2,SPR_SPIT2_3, + SPR_SPIT_EXP2_1,SPR_SPIT_EXP2_2,SPR_SPIT_EXP2_3, + + SPR_SPIT3_1,SPR_SPIT3_2,SPR_SPIT3_3, + SPR_SPIT_EXP3_1,SPR_SPIT_EXP3_2,SPR_SPIT_EXP3_3, + +// +// Hanging Terrot +// + + SPR_TERROT_1,SPR_TERROT_2,SPR_TERROT_3,SPR_TERROT_4, + SPR_TERROT_5,SPR_TERROT_6,SPR_TERROT_7,SPR_TERROT_8, + + SPR_TERROT_FIRE_1,SPR_TERROT_FIRE_2,SPR_TERROT_DIE_1,SPR_TERROT_DIE_2, + SPR_TERROT_DIE_3,SPR_TERROT_DIE_4,SPR_TERROT_DEAD, + +// +// 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, + + SPR_GRENADEREADY,SPR_GRENADEATK1,SPR_GRENADEATK2,SPR_GRENADEATK3, + SPR_GRENADEATK4, + + SPR_GRENADE_FLY1,SPR_GRENADE_FLY2,SPR_GRENADE_FLY3,SPR_GRENADE_FLY4, + SPR_GRENADE_EXPLODE1,SPR_GRENADE_EXPLODE2,SPR_GRENADE_EXPLODE3,SPR_GRENADE_EXPLODE4, + SPR_GRENADE_EXPLODE5, + + SPR_ELEC_ARC1,SPR_ELEC_ARC2,SPR_ELEC_ARC3,SPR_ELEC_ARC4, + SPR_ELEC_POST1,SPR_ELEC_POST2,SPR_ELEC_POST3,SPR_ELEC_POST4, + + SPR_VPOST1,SPR_VPOST2,SPR_VPOST3,SPR_VPOST4, + SPR_VPOST5,SPR_VPOST6,SPR_VPOST7,SPR_VPOST8, + + SPR_VSPIKE1,SPR_VSPIKE2,SPR_VSPIKE3,SPR_VSPIKE4, + SPR_VSPIKE5,SPR_VSPIKE6,SPR_VSPIKE7,SPR_VSPIKE8, + + SPR_GREEN_OOZE1,SPR_GREEN_OOZE2,SPR_GREEN_OOZE3, + SPR_BLACK_OOZE1,SPR_BLACK_OOZE2,SPR_BLACK_OOZE3, + + SPR_GREEN2_OOZE1,SPR_GREEN2_OOZE2,SPR_GREEN2_OOZE3, + SPR_BLACK2_OOZE1,SPR_BLACK2_OOZE2,SPR_BLACK2_OOZE3, + + SPR_CANDY_BAR,SPR_CANDY_WRAPER, + SPR_SANDWICH,SPR_SANDWICH_WRAPER, + + SPR_BLAKE_W1,SPR_BLAKE_W2,SPR_BLAKE_W3,SPR_BLAKE_W4, + +// +// BOSS 1 +// + SPR_BOSS1_W1,SPR_BOSS1_W2,SPR_BOSS1_W3,SPR_BOSS1_W4, + SPR_BOSS1_SWING1,SPR_BOSS1_SWING2,SPR_BOSS1_SWING3, + SPR_BOSS1_DEAD, + SPR_BOSS1_DIE1,SPR_BOSS1_DIE2,SPR_BOSS1_DIE3,SPR_BOSS1_DIE4, + SPR_BOSS1_OUCH, + SPR_BOSS1_PROJ1,SPR_BOSS1_PROJ2,SPR_BOSS1_PROJ3, + SPR_BOSS1_EXP1,SPR_BOSS1_EXP2,SPR_BOSS1_EXP3, + SPR_BOSS1_MORPH1,SPR_BOSS1_MORPH2,SPR_BOSS1_MORPH3,SPR_BOSS1_MORPH4, + SPR_BOSS1_MORPH5,SPR_BOSS1_MORPH6,SPR_BOSS1_MORPH7,SPR_BOSS1_MORPH8, + SPR_BOSS1_MORPH9, + +// +// BOSS 2 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS2_W1,SPR_BOSS2_W2,SPR_BOSS2_W3,SPR_BOSS2_W4, + SPR_BOSS2_SWING1,SPR_BOSS2_SWING2,SPR_BOSS2_SWING3, + SPR_BOSS2_DEAD, + SPR_BOSS2_DIE1,SPR_BOSS2_DIE2,SPR_BOSS2_DIE3,SPR_BOSS2_DIE4, + SPR_BOSS2_OUCH, +#endif + +// +// BOSS 3 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS3_W1,SPR_BOSS3_W2,SPR_BOSS3_W3,SPR_BOSS3_W4, + SPR_BOSS3_SWING1,SPR_BOSS3_SWING2,SPR_BOSS3_SWING3, + SPR_BOSS3_DEAD, + SPR_BOSS3_DIE1,SPR_BOSS3_DIE2,SPR_BOSS3_DIE3,SPR_BOSS3_DIE4, + SPR_BOSS3_OUCH, +#endif + +// +// BOSS 4 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS4_W1,SPR_BOSS4_W2,SPR_BOSS4_W3,SPR_BOSS4_W4, + SPR_BOSS4_SWING1,SPR_BOSS4_SWING2,SPR_BOSS4_SWING3, + SPR_BOSS4_DEAD, + SPR_BOSS4_DIE1,SPR_BOSS4_DIE2,SPR_BOSS4_DIE3,SPR_BOSS4_DIE4, + SPR_BOSS4_OUCH, + SPR_BOSS4_MORPH1,SPR_BOSS4_MORPH2,SPR_BOSS4_MORPH3,SPR_BOSS4_MORPH4, + SPR_BOSS4_MORPH5,SPR_BOSS4_MORPH6,SPR_BOSS4_MORPH7,SPR_BOSS4_MORPH8, + SPR_BOSS4_MORPH9, +#endif + +// +// BOSS 5 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS5_W1,SPR_BOSS5_W2,SPR_BOSS5_W3,SPR_BOSS5_W4, + SPR_BOSS5_SWING1,SPR_BOSS5_SWING2,SPR_BOSS5_SWING3, + SPR_BOSS5_DEAD, + SPR_BOSS5_DIE1,SPR_BOSS5_DIE2,SPR_BOSS5_DIE3,SPR_BOSS5_DIE4, + SPR_BOSS5_OUCH, + SPR_BOSS5_PROJ1,SPR_BOSS5_PROJ2,SPR_BOSS5_PROJ3, + SPR_BOSS5_EXP1,SPR_BOSS5_EXP2,SPR_BOSS5_EXP3, +#endif + +// +// BOSS 6 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS6_W1,SPR_BOSS6_W2,SPR_BOSS6_W3,SPR_BOSS6_W4, + SPR_BOSS6_SWING1,SPR_BOSS6_SWING2,SPR_BOSS6_SWING3, + SPR_BOSS6_DEAD, + SPR_BOSS6_DIE1,SPR_BOSS6_DIE2,SPR_BOSS6_DIE3,SPR_BOSS6_DIE4, + SPR_BOSS6_OUCH, +#endif + + +// +// BOSS 7 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS7_W1,SPR_BOSS7_W2,SPR_BOSS7_W3,SPR_BOSS7_W4, + SPR_BOSS7_SHOOT1,SPR_BOSS7_SHOOT2,SPR_BOSS7_SHOOT3, + SPR_BOSS7_DEAD, + SPR_BOSS7_DIE1,SPR_BOSS7_DIE2,SPR_BOSS7_DIE3,SPR_BOSS7_DIE4, + SPR_BOSS7_OUCH, +#endif + + +// +// BOSS 8 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS8_W1,SPR_BOSS8_W2,SPR_BOSS8_W3,SPR_BOSS8_W4, + SPR_BOSS8_SHOOT1,SPR_BOSS8_SHOOT2,SPR_BOSS8_SHOOT3, + SPR_BOSS8_DIE1,SPR_BOSS8_DIE2,SPR_BOSS8_DIE3,SPR_BOSS8_DIE4, + SPR_BOSS8_DEAD,SPR_BOSS8_OUCH, +#endif + + +// +// BOSS 9 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS9_W1,SPR_BOSS9_W2,SPR_BOSS9_W3,SPR_BOSS9_W4, + SPR_BOSS9_SHOOT1,SPR_BOSS9_SHOOT2,SPR_BOSS9_SHOOT3, + SPR_BOSS9_DIE1,SPR_BOSS9_DIE2,SPR_BOSS9_DIE3,SPR_BOSS9_DIE4, + SPR_BOSS9_DEAD, + SPR_BOSS9_OUCH, +#endif + + +// +// BOSS 10 +// +#if GAME_VERSION != SHAREWARE_VERSION + SPR_BOSS10_W1,SPR_BOSS10_W2,SPR_BOSS10_W3,SPR_BOSS10_W4, + SPR_BOSS10_SHOOT1,SPR_BOSS10_SHOOT2,SPR_BOSS10_SHOOT3, + SPR_BOSS10_DEAD, + SPR_BOSS10_DIE1,SPR_BOSS10_DIE2,SPR_BOSS10_DIE3,SPR_BOSS10_DIE4, + SPR_BOSS10_OUCH, + + SPR_BOSS10_SPIT1,SPR_BOSS10_SPIT2,SPR_BOSS10_SPIT3, + SPR_BOSS10_SPIT_EXP1,SPR_BOSS10_SPIT_EXP2,SPR_BOSS10_SPIT_EXP3, +#endif + + +// +// PLASMA DETONATOR EXPLOSION +// + SPR_DETONATOR_EXP1, SPR_DETONATOR_EXP2, SPR_DETONATOR_EXP3, SPR_DETONATOR_EXP4, + SPR_DETONATOR_EXP5, SPR_DETONATOR_EXP6, SPR_DETONATOR_EXP7, SPR_DETONATOR_EXP8, + +// +// CLIP EXPLOSION +// + SPR_CLIP_EXP1, SPR_CLIP_EXP2, SPR_CLIP_EXP3, SPR_CLIP_EXP4, + SPR_CLIP_EXP5, SPR_CLIP_EXP6, SPR_CLIP_EXP7, SPR_CLIP_EXP8, + + +// +// BFG CANNON WEAPON +// + SPR_BFG_WEAPON1,SPR_BFG_WEAPON2,SPR_BFG_WEAPON3,SPR_BFG_WEAPON4,SPR_BFG_WEAPON5, + +// +// BFG CANNON WEAPON SHOTS +// + SPR_BFG_WEAPON_SHOT1,SPR_BFG_WEAPON_SHOT2,SPR_BFG_WEAPON_SHOT3, + +// +// BFG SHOT EXPLOSION +// + SPR_BFG_EXP1, SPR_BFG_EXP2, SPR_BFG_EXP3, SPR_BFG_EXP4, + SPR_BFG_EXP5, SPR_BFG_EXP6, SPR_BFG_EXP7, SPR_BFG_EXP8, + + + +} sprite_t; + +#if GAME_VERSION == SHAREWARE_VERSION +typedef enum { + + SPR_STAT_1=0, + SPR_STAT_3=0, + SPR_STAT_4=0, + SPR_STAT_5=0, + SPR_STAT_6=0, + SPR_STAT_7=0, + SPR_STAT_16=0, + SPR_STAT_17=0, + SPR_STAT_19=0, + SPR_STAT_20=0, + SPR_STAT_21=0, + SPR_STAT_22=0, + SPR_STAT_23=0, + SPR_STAT_25=0, + SPR_STAT_30=0, + SPR_STAT_37=0, + SPR_STAT_39=0, + SPR_CRATE_2=0, + SPR_CRATE_3=0, + SPR_STAT_58=0, + SPR_STAT_59=0, + SPR_STAT_64=0, + SPR_STAT_70=0, + SPR_STAT_71=0, + SPR_STAT_72=0, + SPR_STAT_73=0, + SPR_STAT_74=0, + SPR_STAT_75=0, + SPR_STAT_76=0, + + SPR_GSCOUT_W1_1,SPR_GSCOUT_W1_2,SPR_GSCOUT_W1_3,SPR_GSCOUT_W1_4, + SPR_GSCOUT_W1_5,SPR_GSCOUT_W1_6,SPR_GSCOUT_W1_7,SPR_GSCOUT_W1_8, + + SPR_GSCOUT_W2_1,SPR_GSCOUT_W2_2,SPR_GSCOUT_W2_3,SPR_GSCOUT_W2_4, + SPR_GSCOUT_W2_5,SPR_GSCOUT_W2_6,SPR_GSCOUT_W2_7,SPR_GSCOUT_W2_8, + + SPR_GSCOUT_W3_1,SPR_GSCOUT_W3_2,SPR_GSCOUT_W3_3,SPR_GSCOUT_W3_4, + SPR_GSCOUT_W3_5,SPR_GSCOUT_W3_6,SPR_GSCOUT_W3_7,SPR_GSCOUT_W3_8, + + SPR_GSCOUT_W4_1,SPR_GSCOUT_W4_2,SPR_GSCOUT_W4_3,SPR_GSCOUT_W4_4, + SPR_GSCOUT_W4_5,SPR_GSCOUT_W4_6,SPR_GSCOUT_W4_7,SPR_GSCOUT_W4_8, + + SPR_GSCOUT_DIE1,SPR_GSCOUT_DIE2,SPR_GSCOUT_DIE3,SPR_GSCOUT_DIE4, + SPR_GSCOUT_DIE5,SPR_GSCOUT_DIE6,SPR_GSCOUT_DIE7,SPR_GSCOUT_DIE8, + + SPR_GSCOUT_DEAD, + + SPR_FSCOUT_W2_1,SPR_FSCOUT_W2_2,SPR_FSCOUT_W2_3,SPR_FSCOUT_W2_4, + SPR_FSCOUT_W2_5,SPR_FSCOUT_W2_6,SPR_FSCOUT_W2_7,SPR_FSCOUT_W2_8, + + SPR_FSCOUT_W3_1,SPR_FSCOUT_W3_2,SPR_FSCOUT_W3_3,SPR_FSCOUT_W3_4, + SPR_FSCOUT_W3_5,SPR_FSCOUT_W3_6,SPR_FSCOUT_W3_7,SPR_FSCOUT_W3_8, + + SPR_FSCOUT_W4_1,SPR_FSCOUT_W4_2,SPR_FSCOUT_W4_3,SPR_FSCOUT_W4_4, + SPR_FSCOUT_W4_5,SPR_FSCOUT_W4_6,SPR_FSCOUT_W4_7,SPR_FSCOUT_W4_8, + + SPR_VITAL_STAND, + SPR_VITAL_DIE_1, SPR_VITAL_DIE_2, SPR_VITAL_DIE_3,SPR_VITAL_DIE_4, + SPR_VITAL_DIE_5, SPR_VITAL_DIE_6, SPR_VITAL_DIE_7, SPR_VITAL_DIE_8, + SPR_VITAL_DEAD_1,SPR_VITAL_DEAD_2,SPR_VITAL_DEAD_3, + SPR_VITAL_OUCH, + + SPR_MUTHUM2_W1,SPR_MUTHUM2_W2,SPR_MUTHUM2_W3,SPR_MUTHUM2_W4, + SPR_MUTHUM2_SWING1,SPR_MUTHUM2_SWING2,SPR_MUTHUM2_SWING3, + SPR_MUTHUM2_DEAD, + SPR_MUTHUM2_DIE1,SPR_MUTHUM2_DIE2,SPR_MUTHUM2_DIE3,SPR_MUTHUM2_DIE4, + SPR_MUTHUM2_OUCH, + SPR_MUTHUM2_SPIT1,SPR_MUTHUM2_SPIT2,SPR_MUTHUM2_SPIT3, + + SPR_LCAN_ALIEN_READY,SPR_LCAN_ALIEN_B1,SPR_LCAN_ALIEN_B2, + SPR_LCAN_ALIEN_B3,SPR_LCAN_ALIEN_EMPTY, + + SPR_LCAN_ALIEN_W1,SPR_LCAN_ALIEN_W2,SPR_LCAN_ALIEN_W3,SPR_LCAN_ALIEN_W4, + SPR_LCAN_ALIEN_SWING1,SPR_LCAN_ALIEN_SWING2,SPR_LCAN_ALIEN_SWING3, + SPR_LCAN_ALIEN_DEAD, + SPR_LCAN_ALIEN_DIE1,SPR_LCAN_ALIEN_DIE2,SPR_LCAN_ALIEN_DIE3, + SPR_LCAN_ALIEN_DIE4,SPR_LCAN_ALIEN_OUCH, + SPR_LCAN_ALIEN_SPIT1,SPR_LCAN_ALIEN_SPIT2,SPR_LCAN_ALIEN_SPIT3, + + SPR_GURNEY_MUT_READY,SPR_GURNEY_MUT_B1,SPR_GURNEY_MUT_B2, + SPR_GURNEY_MUT_B3,SPR_GURNEY_MUT_EMPTY, + + SPR_GURNEY_MUT_W1,SPR_GURNEY_MUT_W2,SPR_GURNEY_MUT_W3,SPR_GURNEY_MUT_W4, + SPR_GURNEY_MUT_SWING1,SPR_GURNEY_MUT_SWING2,SPR_GURNEY_MUT_SWING3, + SPR_GURNEY_MUT_DEAD, + SPR_GURNEY_MUT_DIE1,SPR_GURNEY_MUT_DIE2,SPR_GURNEY_MUT_DIE3, + SPR_GURNEY_MUT_DIE4,SPR_GURNEY_MUT_OUCH, + + SPR_LIQUID_M1,SPR_LIQUID_M2,SPR_LIQUID_M3, + SPR_LIQUID_R1,SPR_LIQUID_R2,SPR_LIQUID_R3,SPR_LIQUID_R4, + SPR_LIQUID_S1,SPR_LIQUID_S2,SPR_LIQUID_S3, + SPR_LIQUID_OUCH, + SPR_LIQUID_DIE_1,SPR_LIQUID_DIE_2,SPR_LIQUID_DIE_3,SPR_LIQUID_DIE_4, + SPR_LIQUID_DEAD, + SPR_LIQUID_SHOT_FLY_1,SPR_LIQUID_SHOT_FLY_2,SPR_LIQUID_SHOT_FLY_3, + SPR_LIQUID_SHOT_BURST_1,SPR_LIQUID_SHOT_BURST_2,SPR_LIQUID_SHOT_BURST_3, + + SPR_GREEN_OOZE1,SPR_GREEN_OOZE2,SPR_GREEN_OOZE3, + SPR_BLACK_OOZE1,SPR_BLACK_OOZE2,SPR_BLACK_OOZE3, + + SPR_BOSS2_W1,SPR_BOSS2_W2,SPR_BOSS2_W3,SPR_BOSS2_W4, + SPR_BOSS2_SWING1,SPR_BOSS2_SWING2,SPR_BOSS2_SWING3, + SPR_BOSS2_DEAD, + SPR_BOSS2_DIE1,SPR_BOSS2_DIE2,SPR_BOSS2_DIE3,SPR_BOSS2_DIE4, + SPR_BOSS2_OUCH, + SPR_BOSS2_SHOOT1,SPR_BOSS2_SHOOT2,SPR_BOSS2_SHOOT3, + SPR_BOSS2_PROJ1,SPR_BOSS2_PROJ2,SPR_BOSS2_PROJ3, + SPR_BOSS2_EXP1,SPR_BOSS2_EXP2,SPR_BOSS2_EXP3, + + SPR_BOSS3_W1,SPR_BOSS3_W2,SPR_BOSS3_W3,SPR_BOSS3_W4, + SPR_BOSS3_SWING1,SPR_BOSS3_SWING2,SPR_BOSS3_SWING3, + SPR_BOSS3_DEAD, + SPR_BOSS3_DIE1,SPR_BOSS3_DIE2,SPR_BOSS3_DIE3,SPR_BOSS3_DIE4, + SPR_BOSS3_OUCH, + SPR_BOSS3_SHOOT1,SPR_BOSS3_SHOOT2,SPR_BOSS3_SHOOT3, + SPR_BOSS3_PROJ1,SPR_BOSS3_PROJ2,SPR_BOSS3_PROJ3, + SPR_BOSS3_EXP1,SPR_BOSS3_EXP2,SPR_BOSS3_EXP3, + + SPR_BOSS4_W1,SPR_BOSS4_W2,SPR_BOSS4_W3,SPR_BOSS4_W4, + SPR_BOSS4_SWING1,SPR_BOSS4_SWING2,SPR_BOSS4_SWING3, + SPR_BOSS4_DEAD, + SPR_BOSS4_DIE1,SPR_BOSS4_DIE2,SPR_BOSS4_DIE3,SPR_BOSS4_DIE4, + SPR_BOSS4_OUCH, + SPR_BOSS4_SHOOT1,SPR_BOSS4_SHOOT2,SPR_BOSS4_SHOOT3, + SPR_BOSS4_PROJ1,SPR_BOSS4_PROJ2,SPR_BOSS4_PROJ3, + SPR_BOSS4_EXP1,SPR_BOSS4_EXP2,SPR_BOSS4_EXP3, + + SPR_BOSS5_W1,SPR_BOSS5_W2,SPR_BOSS5_W3,SPR_BOSS5_W4, + SPR_BOSS5_SWING1,SPR_BOSS5_SWING2,SPR_BOSS5_SWING3, + SPR_BOSS5_DEAD, + SPR_BOSS5_DIE1,SPR_BOSS5_DIE2,SPR_BOSS5_DIE3,SPR_BOSS5_DIE4, + SPR_BOSS5_OUCH, + SPR_BOSS5_SHOOT1,SPR_BOSS5_SHOOT2,SPR_BOSS5_SHOOT3, + SPR_BOSS5_PROJ1,SPR_BOSS5_PROJ2,SPR_BOSS5_PROJ3, + SPR_BOSS5_EXP1,SPR_BOSS5_EXP2,SPR_BOSS5_EXP3, + + SPR_BOSS6_W1,SPR_BOSS6_W2,SPR_BOSS6_W3,SPR_BOSS6_W4, + SPR_BOSS6_SWING1,SPR_BOSS6_SWING2,SPR_BOSS6_SWING3, + SPR_BOSS6_DEAD, + SPR_BOSS6_DIE1,SPR_BOSS6_DIE2,SPR_BOSS6_DIE3,SPR_BOSS6_DIE4, + SPR_BOSS6_OUCH, + SPR_BOSS6_SHOOT1,SPR_BOSS6_SHOOT2,SPR_BOSS6_SHOOT3, + SPR_BOSS6_PROJ1,SPR_BOSS6_PROJ2,SPR_BOSS6_PROJ3, + SPR_BOSS6_EXP1,SPR_BOSS6_EXP2,SPR_BOSS6_EXP3, +} dummy_sprite_t; +#endif + +// +// Door Objects +// + +typedef enum +{ + // LOCKED DOORS + + L_METAL, + L_METAL_SHADE, + + L_BIO, + L_BIO_SHADE, + + L_ELEVATOR, + L_ELEVATOR_SHADE, + + L_SPACE, + L_SPACE_SHADE, + + L_PRISON, + L_PRISON_SHADE, + + L_HIGH_SECURITY, + L_HIGH_SECURITY_SHADE, + + L_ENTER_ONLY, + L_ENTER_ONLY_SHADE, + + L_HIGH_TECH, + L_HIGH_TECH_SHADE, + + // UNLOCKED DOORS + + + UL_METAL, + UL_METAL_SHADE, + + UL_BIO, + UL_BIO_SHADE, + + UL_ELEVATOR, + UL_ELEVATOR_SHADE, + + UL_SPACE, + UL_SPACE_SHADE, + + UL_PRISON, + UL_PRISON_SHADE, + + UL_HIGH_SECURITY, + UL_HIGH_SECURITY_SHADE, + + UL_ENTER_ONLY, + UL_ENTER_ONLY_SHADE, + + UL_HIGH_TECH, + UL_HIGH_TECH_SHADE, + + // MISC DOORS + + NOEXIT, + NOEXIT_SHADE, + + STEEL_JAM, + STEEL_JAM_SHADE, + + SPACE_JAM, + SPACE_JAM_SHADE, + + OFFICE_JAM, + OFFICE_JAM_SHADE, + + BIO_JAM, + BIO_JAM_SHADE, + + SPACE_JAM_2, + SPACE_JAM_2_SHADE, + + // END OF DOOR LIST + + NUMDOORTYPES, + + +} doortype; + + +// +// Breifing types - Note these are ordered to an char array in Breifing(). +// + +typedef enum +{ + BT_LOSE, + BT_WIN, + BT_INTRO, + +} breifing_type; + +// Terminal Messages - These correspond to the order in which they are +// grabbed in VGAGRAPH.BS?. See BSTONEV.I +// + +typedef enum +{ + TM_HINT, + TM_JAM, + TM_HELP, + TM_APOGEE, + TM_ID, + TM_GOOBERS, + TM_MIKE, + TM_JIM, + TM_JERRY, + + TM_JOSHUA, + + TM_STAR, + + TM_VITALS1, + TM_VITALS2, + + TM_DEACTIVATE_TURRET, + TM_TURRETS_ACTIVATED, + TM_TURRETS_DEACTIVATED, + + TM_LINK, + TM_LINK_OK, + TM_LINK_BAD, + + TM_RADAR_OFF, + TM_RADAR_ON, + + TM_SOUND_OFF, + TM_SOUND_ON, + + TM_GOLDSTERN_TRACK_OFF, + TM_GOLDSTERN_TRACK_ON, + TM_GOLDSTERN_ARRIVED, + TM_GOLDSTERN_WILL_AR, + TM_GOLDSTERN_NO_PICK, + TM_GOLDSTERN_NO_INFO, + + TM_RESET_SECURITY, + TM_SECURITY_STATUS_OFF, + TM_SECURITY_STATUS_ON, + + TM_TURRET_DEACTIVATE_BAD, + TM_TURRET_DEACTIVATE_GOOD, + + TM_UNRECOGNIZED_COMMAND, + TM_READY, + TM_RETURN, + TM_SECONDS, + TM_CHEATER, + TM_BLUEPRINTS, + + TM_PROFILE_WHO, + TM_PROFILE_SARA, + TM_PROFILE_BLAKE, + TM_PROFILE_GOLDSTERN, + TM_PROFILE_UNKNOWN, + TM_DEACTIVATE_SOCKETS, + TM_ACTIVATE_SOCKETS, + TM_UNABLE_TO_PERFORM, + TM_NO_SOCKETS, + TM_ALREADY_ACTIVATED, + TM_ALREADY_DEACTIVATED, + TM_LAST, +} term_msg_type; + +// Terminal Commands - The first set of commands TC_HINT - TC_end_of_1to1 +// are directly mapped 1 to 1 to the terminal msgs. +// + +typedef enum +{ + TC_HINT, + TC_JAM, + TC_HELP, + TC_APOGEE, + TC_THANKS, + TC_GOOBERS, + TC_MIKE, + TC_JIM, + TC_JERRY, + + // END OF ONE TO ONE LIST + + TC_JOSHUA, + TC_STAR, + TC_BLUEPRINT, + TC_EXIT, + TC_QUIT, + TC_OFF, + TC_BYE, + TC_DISPLAY_PERSONNEL, + TC_SOUND, + TC_DISPLAY_GOLDSTERN, + TC_ARRIVAL_GOLDSTERN, + TC_DEACTIVATE_SECURITY, + TC_DISPLAY_SECURITY, + TC_SATALITE_STATUS, + TC_DEACTIVATE_TURRETS, + TC_TURRET_STATUS, + TC_PROFILE, + TC_SSTONE, + TC_BSTONE, + TC_GOLDSTERN, + TC_DEACTIVATE_SOCKETS, + TC_ACTIVATE_SOCKETS, + + TC_LAST, +} term_cmd_type; + +// +// Barrier State Transistions +// + +typedef enum +{ + bt_OFF, + bt_ON, + bt_DISABLING, + bt_DISABLED, + + bt_OPENING, // For physical barriers + bt_CLOSING, // " " " + +} barrier_state_type; + + + +/* +============================================================================= + + GLOBAL TYPES + +============================================================================= +*/ + +typedef long fixed; + +// Display priority is determined by the order of these bits! +// And, this order must match the PinballBonus table in AGENT.C! +// +#define B_GALIEN_DESTROYED 0x0001 +#define B_SCORE_ROLLED 0x0002 +#define B_ONE_MILLION 0x0004 +#define B_EXTRA_MAN 0x0008 +#define B_ENEMY_DESTROYED 0x0010 +#define B_TOTAL_POINTS 0x0020 +#define B_INFORMANTS_ALIVE 0x0040 + +#define BONUS_QUEUE gamestuff.level[gamestate.mapon].bonus_queue +#define BONUS_SHOWN gamestuff.level[gamestate.mapon].bonus_shown + +#define PinballBonusShown(bonus) (BONUS_SHOWN & bonus) +#define ActivatePinballBonus(bonus) if (!PinballBonusShown(bonus)) BONUS_QUEUE |= bonus + +typedef struct { + char far *BonusText; // REBA text pointer + long Points; // Score for this bonus + boolean Recurring; // Appear multiple times in a single level? + void (far *func)(); // Code to execute when you get this bonus. +} PinballBonusInfo; + +typedef struct +{ + char tics; + char attack; + char frame; // attack is 1 for gun, 2 for knife +} atkinf_t; + + +typedef enum // NOTE - This enum list is ORDERED! +{ + mv_intro, + mv_final, + mv_NUM_MOVIES, + +} movie_t; + +typedef enum { + di_north, + di_east, + di_south, + di_west +} controldir_t; + +typedef enum { // NOTE - This enum list is ORDERED! + dr_bio, + dr_normal, + dr_prison, + dr_elevator, + dr_high_security, + dr_office, + dr_oneway_left, + dr_oneway_up, + dr_oneway_right, + dr_oneway_down, + dr_space, +} door_t; + +typedef enum { + kt_none =-1, + kt_red, + kt_yellow, + kt_blue, + NUMKEYS, +} keytype; + +typedef enum { + ac_badobject = -1, + ac_no, + ac_yes, + ac_allways +} activetype; + +typedef enum { + nothing, + playerobj, + inertobj, + fixup_inertobj, + deadobj, + + + // BEGIN - Start of ordered list for ActorInfoMsg[] for attacking + // actor REBA messages + + rentacopobj, + hang_terrotobj, + gen_scientistobj, + podobj, + electroobj, + electrosphereobj, + proguardobj, + genetic_guardobj, + mutant_human1obj, + mutant_human2obj, + lcan_wait_alienobj, + lcan_alienobj, + scan_wait_alienobj, + scan_alienobj, + gurney_waitobj, + gurneyobj, + liquidobj, + swatobj, + goldsternobj, + gold_morphobj, + volatiletransportobj, + floatingbombobj, + rotating_cubeobj, + + spider_mutantobj, + breather_beastobj, + cyborg_warriorobj, + reptilian_warriorobj, + acid_dragonobj, + mech_guardianobj, + + final_boss1obj, + final_boss2obj, + final_boss3obj, + final_boss4obj, + + blakeobj, + + crate1obj, + crate2obj, + crate3obj, + + green_oozeobj, + black_oozeobj, + green2_oozeobj, + black2_oozeobj, + podeggobj, + + morphing_spider_mutantobj, + morphing_reptilian_warriorobj, + morphing_mutanthuman2obj, + + SPACER1_OBJ, + electroshotobj, // NON-HITPOINT objects... + post_barrierobj, + arc_barrierobj, + vpost_barrierobj, + vspike_barrierobj, + goldmorphshotobj, + + security_lightobj, + explosionobj, + steamgrateobj, + steampipeobj, + + liquidshotobj, + + lcanshotobj, + podshotobj, + scanshotobj, + dogshotobj, + mut_hum1shotobj, + + ventdripobj, + playerspshotobj, + flickerlightobj, + + plasma_detonatorobj, + plasma_detonator_reserveobj, + + grenadeobj, + bfg_shotobj, + bfg_explosionobj, + pd_explosionobj, + + spider_mutantshotobj, + breather_beastshotobj, + cyborg_warriorshotobj, + reptilian_warriorshotobj, + acid_dragonshotobj, + mech_guardianshotobj, + final_boss2shotobj, + final_boss4shotobj, + + doorexplodeobj, // Door explosion_anim acto + gr_explosionobj, + gold_morphingobj, + +} classtype; + + +// +// NOTE: When adding bonus objects - Make sure that they are added +// ----- at the bottom of the list or that BonusMsg[] is correctly +// updated. +// + +typedef enum { + dressing, + + bo_red_key, + bo_yellow_key, + bo_blue_key, + + bo_clip, + bo_clip2, + bo_pistol, + bo_burst_rifle, + bo_ion_cannon, + bo_grenade, + bo_bfg_cannon, + + // START of Bonus Health Ordered list + + bo_fullheal, + bo_firstaid, + bo_ham, + bo_chicken, + bo_sandwich, + bo_candybar, + bo_water, + bo_water_puddle, + + // END of ordered ... + + bo_money_bag, + bo_loot, + + bo_gold1, + bo_gold2, + bo_gold3, + + bo_gold, + bo_bonus, + + bo_plainvent, + bo_bloodvent, + bo_watervent, + + bo_coin, + bo_coin5, + + bo_plasma_detonator, + bo_automapper1, + + bo_nothing, + + block, + +} stat_t; + +typedef struct +{ + int picnum; + stat_t type; +} stattype; + +typedef enum { + east, + northeast, + north, + northwest, + west, + southwest, + south, + southeast, + nodir +} dirtype; + + +typedef enum { + en_rentacop, // Actors with hitpoints (normal actors) + en_hang_terrot, + en_gen_scientist, + en_pod, + en_electro_alien, + en_electrosphere, + en_proguard, + en_genetic_guard, + en_mutant_human1, + en_mutant_human2, + en_lcan_wait_alien, + en_lcan_alien, + en_scan_wait_alien, + en_scan_alien, + en_gurney_wait, + en_gurney, + en_liquid, + en_swat, + en_goldstern, + en_gold_morph, + en_volatiletransport, + en_floatingbomb, + en_rotating_cube, + + en_spider_mutant, + en_breather_beast, + en_cyborg_warrior, + en_reptilian_warrior, + en_acid_dragon, + en_mech_guardian, + + en_final_boss1, + en_final_boss2, + en_final_boss3, + en_final_boss4, + + en_blake, + + en_crate1, + en_crate2, + en_crate3, + + en_green_ooze, + en_black_ooze, + en_green2_ooze, + en_black2_ooze, + en_podegg, + + en_morphing_spider_mutant, + en_morphing_reptilian_warrior, + en_morphing_mutanthuman2, + + NUMHITENEMIES, + + en_electro_shot, // Actors WITHOUT hitpoints (abnormal actors?) + en_post_barrier, + en_arc_barrier, + en_vpost_barrier, + en_vspike_barrier, + en_goldmorphshot, + + en_security_light, + en_explosion, + en_steamgrate, + en_steampipe, + + en_liquidshot, + + en_lcanshot, + en_podshot, + en_scanshot, + en_dogshot, + en_mut_hum1shot, + + en_ventdrip, + en_playerspshotobj, + en_flickerlight, + + en_plasma_detonator, + en_plasma_detonator_reserve, + + en_vertsphere, // Actor types only used for spawning. + en_horzsphere, + en_diagsphere, + en_bloodvent, + en_watervent, + NUMENEMIES +} enemy_t; + +#define SF_ROTATE 0x01 +#define SF_PAINFRAME 0x02 + +typedef struct statestruct +{ + byte flags; + 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 areanumber; + + byte *visspot; + int shapenum; // if shapenum == -1 the obj has been removed + unsigned flags; + byte itemnumber; + char lighting; +} statobj_t; + +//--------------------- +// +// door actor structure +// +//--------------------- + +typedef struct doorstruct +{ + byte tilex,tiley; + boolean vertical; + char flags; + keytype lock; + door_t type; + enum {dr_open,dr_closed,dr_opening,dr_closing,dr_jammed} action; + int ticcount; + byte areanumber[2]; +} doorobj_t; + + +//-------------------- +// +// thinking actor structure +// +//-------------------- + +typedef struct objstruct +{ + byte tilex,tiley; + byte areanumber; + + activetype active; + int ticcount; + classtype obclass; + statetype *state; + + unsigned long flags; + unsigned flags2; // Aux flags + + long distance; // if negative, wait for that door to open + dirtype dir; + dirtype trydir; // "bit 7" == "direction to turn" flag + + fixed x,y; + unsigned char s_tilex,s_tiley; // s_tilex==0, running for corner + + int viewx; + unsigned viewheight; + fixed transx,transy; // in global coord + + int hitpoints; + unsigned char ammo; + char lighting; + unsigned linc; + int angle; + long speed; + + int temp1; + int temp2; + unsigned temp3; // holds 'last door used' by 'smart' actors + + struct objstruct *next,*prev; +} objtype; + + + + +enum +{ + bt_nobutton=-1, + bt_attack=0, + bt_strafe, + bt_run, + bt_use, + bt_ready_autocharge, + bt_ready_pistol, + bt_ready_burst_rifle, + bt_ready_ion_cannon, + bt_ready_grenade, + bt_ready_bfg_cannon, + bt_ready_plasma_detonators, + + bt_SPACER, + + NUMBUTTONS, +}; + + +typedef enum +{ + wp_autocharge, + wp_pistol, + wp_burst_rifle, + wp_ion_cannon, + wp_grenade, + wp_bfg_cannon, +// wp_plasma_detonators, + + wp_SPACER, + NUMWEAPONS, +} weapontype; + + + +typedef enum { + gd_baby, + gd_easy, + gd_medium, + gd_hard +}; + + + +typedef enum +{ + ELEVATOR_BACK, + TRANSPORTER_BACK + +} backgroundtype; + + + + +// +// General Coord (tile) structure +// +typedef struct +{ + unsigned char tilex, tiley; +} tilecoord_t; + +//----------------------------------- +// +// barrier coord/table structure +// +//----------------------------------- + +typedef struct +{ + tilecoord_t coord; + unsigned char on; +} barrier_type; + + +//--------------- +// +// gamestate structure +// +//--------------- + +typedef struct statsInfoType { + long total_points,accum_points; + byte total_enemy,accum_enemy; + byte total_inf,accum_inf; + short overall_floor; +} statsInfoType; + +typedef struct { + unsigned bonus_queue; // bonuses that need to be shown + unsigned bonus_shown; // bonuses that have been shown + boolean locked; + statsInfoType stats; + byte ptilex,ptiley; + int pangle; +} levelinfo; + + +typedef struct +{ + levelinfo old_levelinfo[MAPS_PER_EPISODE]; + levelinfo level[MAPS_PER_EPISODE]; +} fargametype; + +typedef struct +{ + int turn_around,turn_angle; + unsigned flags; + int lastmapon; + int difficulty; + int mapon; + int status_refresh; + long oldscore,tic_score,score,nextextra; + int score_roll_wait; + int lives; + int health; + int health_delay; + char health_str[4]; + + int rpower,old_rpower; + char rzoom; + char radar_leds,lastradar_leds; + + char lastammo_leds; + char ammo_leds; + int ammo,old_ammo; + + int plasma_detonators,old_plasma_detonators; + + char useable_weapons; + char weapons,weapon,chosenweapon,old_weapons[4]; + char key_floor; + + char weapon_wait; + int attackframe,attackcount,weaponframe; + int episode; + long TimeCount; + long killx,killy; + char far *msg; // InfoArea msg... + char numkeys[NUMKEYS],old_numkeys[NUMKEYS]; + barrier_type barrier_table[MAX_BARRIER_SWITCHES]; + barrier_type old_barrier_table[MAX_BARRIER_SWITCHES]; + unsigned tokens,old_tokens; + boolean boss_key_dropped,old_boss_key_dropped; + int wintilex,wintiley; +} gametype; + +typedef enum { + ex_stillplaying, + ex_completed, + ex_transported, + ex_died, + ex_warped, + ex_resetgame, + ex_loadedgame, + ex_victorious, + ex_abort, + ex_demodone, + ex_secretlevel, + ex_title, +} exit_t; + + +typedef struct +{ + unsigned char init_delay; + unsigned char delay_count; + unsigned char firstreg; + unsigned char lastreg; +} CycleInfo; + + +typedef struct +{ + int viewx, + viewheight, + shapenum; + char lighting; + char cloaked; +} visobj_t; + + +typedef enum {at_NONE=0,at_CYCLE,at_REBOUND,at_ONCE} animtype_t; +typedef enum {ad_FWD=0,ad_REV} animdir_t; + + +typedef struct ofs_anim_struct +{ + unsigned animtype:2; // animtype_t + unsigned curframe:5; + unsigned maxframe:5; + unsigned animdir:1; // animdir_t + unsigned extra:3; + +} ofs_anim_t; + + +// +// DisplayInfoMsg Priorities - Duplicate Values are Allowed. +// +// ("enum" list used simply for debuging use like Object Classes) +// + +// +// Msg_Priorities - Hell.. Lets just make them all the same... + +typedef enum +{ + MP_min_val = 0, + + MP_HEARTB_SND = 0x0200, + MP_WALLSWITCH_OPERATE = 0x0200, + + MP_DOOR_OPERATE = 0x0200, + MP_CONCESSION_OPERATE = 0x0200, + MP_WEAPON_AVAIL = 0x0200, + + MP_ATTACK_INFO = 0x0200, + MP_NO_MORE_AMMO = 0x0200, + MP_WEAPON_MALFUNCTION = 0x0200, + + MP_INTERROGATE = 0x0200, + MP_CONCESSION_HINT = 0x0200, + MP_NO_MORE_TOKENS = 0x0200, + MP_CONCESSION_OUT_ORDER = 0x0200, + + MP_BONUS = 0x0200, + + MP_TAKE_DAMAGE = 0x0200, + MP_DETONATOR = 0x0200, + + MP_PINBALL_BONUS = 0x3000, + MP_FLOOR_UNLOCKED = 0x3000, + + MP_POWERUP = 0x7000, // Power-Up/Game-Start Value + MP_max_val = 0x7FFF, // DO NOT USE/EXCEED - MAX Val +} msg_priorities; + +typedef enum +{ + MT_NOTHING, + MT_CLEAR, + MT_ATTACK, + MT_GENERAL, + MT_OUT_OF_AMMO, + MT_MALFUNCTION, + MT_NO_MO_FOOD_TOKENS, + MT_BONUS, + +} infomsg_type; + + +// +// Menu Instruction Text types... +// +typedef enum +{ + IT_STANDARD, + IT_HIGHSCORES, + IT_ENTER_HIGHSCORE, + IT_MOUSE_SEN, + MAX_INSTRUCTIONS, + +} inst_type; + + + +#define MAX_CACHE_MSGS 30 +#define MAX_CACHE_MSG_LEN 190 + +//------------------------- BASIC STRUCTURES ----------------------------- + +// Basic 'message info' structure +// +typedef struct { + unsigned char local_val; // where msg is in 'local' list + unsigned char global_val; // where msg was in 'global' list + memptr mSeg; // pointer to message +} mCacheInfo; + +// Basic 'message list' structure +// +typedef struct { + short NumMsgs; // number of messages + mCacheInfo mInfo[MAX_CACHE_MSGS]; // table of message 'info' +} mCacheList; + +//----------------------- CONCESSION STRUCTURES -------------------------- + +// Concession 'message info' structure +// +typedef struct { + mCacheInfo mInfo; + unsigned char type; // type of concession + unsigned char operate_cnt; // # of times req'd to operate +} con_mCacheInfo; + +// Concession 'message list' structure +// +typedef struct { + short NumMsgs; // also, num concessions + con_mCacheInfo cmInfo[MAX_CACHE_MSGS]; +} concession_t; + +//------------------------ INFORMANT STRUCTURES -------------------------- + +// Informant 'message info' structure +// +typedef struct { + mCacheInfo mInfo; + byte areanumber; // 'where' msg can be used +} sci_mCacheInfo; + +// Informant 'message list' structure +// +typedef struct { + short NumMsgs; + sci_mCacheInfo smInfo[MAX_CACHE_MSGS]; +} scientist_t; + +//------------------------------------------------------------------------ + + +// Electro-Alien controller structer +// +typedef struct +{ + char tilex,tiley; // where this controller is in the map. + char aliens_out; // aliens spawned by this controller. + short delay; // delay before spawning another alien. +} eaWallInfo; + + + + + +// General Structure to hold goldstern specific stuff... +// + +typedef struct +{ + unsigned char LastIndex; // Last Spawn Coord Index + unsigned char SpawnCnt; // Num of Spawnpoints for Goldstern + unsigned flags; // What type of command/operation is needed... + unsigned WaitTime; // Wait time for Goldstern Spawn (current & Next) + boolean GoldSpawned; // Has Goldstern been spawned? +} GoldsternInfo_t; + + +typedef struct +{ + long x,y,z; + byte color; +} star_t; + +#define MAX_SPACE_THRUST ((long)0x4fff) + +#define MAX_STARS 128 +#define MAX_SPACE_STATS (((MAXSTATS*sizeof(statobj_t))-((MAX_STARS+1)*sizeof(star_t)))/sizeof(statobj_t)) + + + + + +/* +============================================================================= + + 3D_MAIN DEFINITIONS + +============================================================================= +*/ + +#define TITLE_LOOP_MUSIC PLOT_MUS + +#define CANT_PLAY_TXT "\n" \ + "ERROR: Insufficient disk space.\n" \ + "Try deleting some files from your hard disk.\n\n" + + +extern char tempPath[]; + +extern const float radtoint; // = (float)FINEANGLES/2/PI; + +extern levelinfo far default_level[MAPS_PER_EPISODE]; +extern short view_xl,view_xh,view_yl,view_yh; +extern short starting_level, debug_value, starting_episode, starting_difficulty; + +extern boolean MS_CheckParm (char far *string); + +extern signed char lastmap_tilex,lastmap_tiley; +extern unsigned TopColor, BottomColor; +extern char str[80],str2[20]; +//extern unsigned tedlevelnum; +//extern boolean tedlevel; +extern boolean nospr; +extern boolean IsA386; + +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; +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]; + +extern boolean ShowQuickMsg; + +long DeleteChunk(int handle, char *chunk); + +void LoadFonts(void); +void ClearNClose(void); +void CycleColors(void); +void LoadAccessCodes(void); +void AlignPlayerInElevator(void); +void HelpScreens (void); +void OrderingInfo (void); +void TEDDeath(void); +void CalcProjection (long focal); +boolean SetViewSize (unsigned width, unsigned height); +void NewGame (int difficulty,int episode); +void NewViewSize (int width); +unsigned scan_atoi(char *s); +void AlignPlayerOnTransporter(void); + +unsigned UseFunc(char huge *first, char huge *next); +boolean DoMovie(movie_t movie, memptr palette); +boolean CheckDiskSpace(long needed,char far *text,cds_io_type io_type); +boolean SaveTheGame(int handle, char far *description); +long ChecksumFile(char *file, long checksum); +void BadChecksum(void); +void InvalidLevels(void); +void CheckValidity(char *file, long valid_checksum); +void UnauthorizedLevels(void); +void ShowChecksums(void); +void fprint(char far *text); + +void SetupWalls (void); +void InitDigiMap (void); + +void CleanUpDoors_N_Actors(void); + + +void MakeDestPath(char far *file); +void InitDestPath(void); + +extern long FindChunk(int file, char *chunk); +extern long NextChunk(int file); + +/* +============================================================================= + + 3D_GAME DEFINITIONS + +============================================================================= +*/ + +extern int db_count; +extern classtype far debug_bonus[2][800]; +extern fargametype far gamestuff; +extern tilecoord_t far GoldieList[GOLDIE_MAX_SPAWNS]; +extern GoldsternInfo_t GoldsternInfo; + +extern unsigned char VitalsRemain,VitalsOnFloor; + +extern eaWallInfo eaList[]; +extern char NumEAWalls,NumEASpawned; +extern boolean ingame,fizzlein,screensplit; +extern unsigned latchpics[NUMLATCHPICS]; +extern gametype gamestate; +extern int doornum; + +extern char demoname[13]; + +void DrawPlayBorder (void); +void ScanInfoPlane (void); +void SetupGameLevel (void); +void NormalScreen (void); +void DrawPlayScreen (boolean); +void FizzleOut (void); +void GameLoop (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 Warped (void); +void RotateView(int DestAngle,unsigned char RotSpeed); +void DrawWarpIn(void); +void BMAmsg(char far *msg); +void CacheBMAmsg(unsigned MsgNum); +void BevelBox(short xl, short yl, short w, short h, byte hi, byte med, byte lo); + +void AddTotalPoints(unsigned points); +void AddTotalInformants(char informants); +void AddTotalEnemy(unsigned enemies); + +void ShadowPrintLocationText(sp_type type); +void LoseScreen(void); +void LoadLocationText(short textNum); + +/* +============================================================================= + + 3D_PLAY DEFINITIONS + +============================================================================= +*/ + +extern int objcount; + +extern objtype *DeadGuys[],dummyobj; +extern unsigned char NumDeadGuys; + +extern exit_t playstate; + +extern int bordertime; + +extern boolean madenoise,usedummy,nevermark; +extern unsigned char alerted,alerted_areanum; + +extern objtype objlist[MAXACTORS],*new,*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; +extern int DebugOk; +extern int InstantWin, InstantQuit; +extern boolean PowerBall; +extern int TestQuickSave, TestAutoMapper; +extern unsigned ExtraRadarFlags; + +// +// 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; + +extern char far Computing[]; + + +void CenterWindow(word w,word h); +void InitActorList (void); +void GetNewActor (void); +void RemoveObj (objtype *gone); +void PollControls (void); +void StopMusic(void); +void StartMusic(boolean startit); +void PlayLoop (void); + +void ChangeSwapFiles(boolean display); +void OpenPageFile(void); +void CheckMusicToggle(void); + +/* +============================================================================= + + 3D_DEBUG + +============================================================================= +*/ + +int DebugKeys (void); +void CalcMemFree(void); + + +/* +============================================================================= + + 3D_DRAW DEFINITIONS + +============================================================================= +*/ + +extern byte far TravelTable[MAPSIZE][MAPSIZE]; + +extern int weaponchangetics,itemchangetics,bodychangetics; +extern int plaqueon,plaquetime,plaquetimefrac,getpic; + +extern statobj_t *firststarobj; + +extern unsigned screenloc[3]; +extern unsigned freelatch; + +extern long space_xmove,space_ymove; + +extern long lasttimecount; +extern long framecount; +extern long frameon; +extern boolean fizzlein; + +extern unsigned wallheight[MAXVIEWWIDTH]; + +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; + +extern boolean cloaked_shape; + +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); +void DrawStars(void); + + + +boolean TransformTile (int tx, int ty, int *dispx, int *dispheight); +void WrapTransformActor (objtype *ob); +void ComputeActorPosition(objtype *ob, char adjust_x, char adjust_y); +void WrapDrawScaleds (void); +boolean WrapActorPosition(objtype *obj); +void WrapStaticPosition(statobj_t *statptr, visobj_t *visptr); +void ShowOverhead(short bx, short by, short radius, short zoom, unsigned flags); +void UpdateTravelTable(void); + + + +/* +============================================================================= + + 3D_DRAW2 DEFINITIONS + +============================================================================= +*/ +extern unsigned CeilingTile, FloorTile; +extern void (*MapRowPtr)(); + +void DrawPlanes (void); + + +void MapRow(); +void C_MapRow(); +void F_MapRow(); + + +/* +============================================================================= + + 3D_STATE DEFINITIONS + +============================================================================= +*/ + +extern unsigned far actor_points[]; +extern dirtype opposite[9]; +extern dirtype diagonal[9][9]; + + +void SeekPlayerOrStatic(objtype *ob, int *deltax, int *deltay); +unsigned CheckRunChase(objtype *ob); +void GetCornerSeek(objtype *ob); +boolean LookForGoodies(objtype *ob, unsigned RunReason); +void InitHitRect (objtype *ob, unsigned radius); +void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state); +void NewState (objtype *ob, statetype *state); + +boolean TryWalk (objtype *ob, boolean moveit); +void SelectChaseDir (objtype *ob); +void SelectDodgeDir (objtype *ob); +void MoveObj (objtype *ob, long move); + +void KillActor (objtype *ob); +void DamageActor (objtype *ob, unsigned damage, objtype *attacker); + +boolean CheckLine (objtype *from_obj, objtype *to_obj); +boolean CheckSight (objtype *from_obj, objtype *to_obj); + +boolean PosVisable(fixed from_x, fixed from_y, fixed to_x, fixed to_y, int from_angle); +boolean PlayerIsBlocking(objtype *ob); + +void MakeAlertNoise(objtype *obj); +objtype *CheckAndReserve(void); + + +/* +============================================================================= + + 3D_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 scaledir_avail; + +extern int normalshade; +extern int normalshade_div; +extern int shade_max; + + +void FreeScaleDirectory(void); +void SetupScaling (int maxscaleheight); +void ScaleShape (int xcenter, int shapenum, unsigned height); +void SimpleScaleShape (int xcenter, int shapenum, unsigned height); +void MegaSimpleScaleShape (int xcenter, int ycenter, int shapenum, unsigned height, unsigned shade); + +/* +============================================================================= + + 3D_AGENT DEFINITIONS + +============================================================================= +*/ + +extern scientist_t InfHintList; +extern scientist_t NiceSciList; +extern scientist_t MeanSciList; + +extern unsigned far static_points[]; +extern boolean GAN_HiddenArea; +extern memptr InfAreaMsgs[]; +extern byte NumAreaMsgs,LastInfArea; +extern short FirstGenInfMsg,TotalGenInfMsgs; +extern classtype LastInfoAttacker; +extern int LastInfoAttacker_Cloaked; + +extern char term_com_name[]; +extern char term_msg_name[]; + +extern atkinf_t far attackinfo[7][14]; + +// +// player state info +// +extern boolean commandmode; +extern long thrustspeed; +extern unsigned plux,pluy; // player coordinates scaled to unsigned +extern boolean PlayerInvisable; +extern char DrawInfoArea_COUNT; +extern char InitInfoArea_COUNT; + +extern unsigned player_oldtilex; +extern unsigned player_oldtiley; + +// Terminal variables + +extern unsigned RadarSw; + +// Location Bar message string... + +extern char LocationText[MAX_LOCATION_DESC_LEN]; + +void JLatchDrawPic (unsigned x, unsigned y, unsigned picnum); + + +// +// search / move info +// +extern unsigned searchon; // held object number looking at +extern int searchtics; // when it reaches SEARCHTICS, get an obj +extern objtype *searchobj; // current object being searched +extern unsigned foundflag; // only show NOTHING if nothing was found +extern objtype *moveobj; // current object being draged + +extern int anglefrac; +extern int facecount; + +extern unsigned LastMsgPri; +extern short MsgTicsRemain; + +void GivePoints(long score,boolean add_to_stats); +void SpawnPlayer (int tilex, int tiley, int dir); +void DrawCash(void); +void UpdateHealth(void); + +void DrawAmmoGuage(void); +void DrawAmmoMsg(void); +void DrawAmmo(boolean ForceRefresh); +boolean DisplayInfoMsg(char far *Msg,msg_priorities Priority,short DisplayTime,short MessageType); +void UpdateInfoAreaClock(void); +void UpdateInfoArea(void); +void DrawHealthMonitor(void); +void CalcHealthDisplay(void); +void UpdateScore(void); + +byte ValidAreaTile(unsigned far *ptr); +char GetAreaNumber(char tilex, char tiley); +short InputFloor(void); + +void RestoreInfoArea(void); +void DrawHeartPic(void); +void DrawInfoArea(void); +short DrawShape(short x, short y, short shapenum, pisType shapetype); + +void AnimatePage(void); + +void ActivateTerminal(boolean); +void TerminalPrint(char far *msg,boolean FastPrint); +void FloorCheat(unsigned RadarFlags); +boolean Interrogate(objtype *ob); + +void GiveKey(int key); +void TakeKey(int key); +void GiveToken (int tokens); + +void TakePlasmaDetonator (int count); +void GivePlasmaDetonator (int count); + +void CacheDrawPic(short x, short y, short pic); +void LoadTerminalCommands(void); + +void ActivateWallSwitch(unsigned iconnum, short x, short y); +unsigned UpdateBarrierTable(unsigned char x, unsigned char y, boolean OnOff); +unsigned ScanBarrierTable(unsigned char x, unsigned char y); +void DisplaySwitchOperateMsg(unsigned coords); + +void DisplayNoMoMsgs(void); +void PrintStatPercent(short nx, short ny, char percentage); +short ShowStats(short bx, short by, ss_type type, statsInfoType far *stats); +boolean PerfectStats(void); +boolean CheckPerfectStats(void); +boolean OperateSmartSwitch(unsigned tilex, unsigned tiley, char Operation, boolean Force); + +/* +============================================================================= + + 3D_ACT1 DEFINITIONS + +============================================================================= +*/ +extern char xy_offset[8][2]; +extern stattype far statinfo[]; +extern concession_t far ConHintList; + +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,pwalldist; + + +statobj_t *ReserveStatic(void); +void SpawnStatic (int tilex, int tiley, int type); +void SpawnDoor (int tilex, int tiley, boolean vertical, keytype lock, door_t type); + +void OperateConcession(unsigned concession); +void SpawnConcession(int tilex, int tiley, unsigned credits,unsigned machinetype); +unsigned LoadConcessionHint(unsigned MsgNum); +void CacheInfoAreaMsg(unsigned block, unsigned MsgNum, char far *hint_buffer,unsigned MaxBufferLen); +void CheckSpawnEA(void); + +int TransformAreas(char tilex, char tiley, char xform); + + +void CheckSpawnGoldstern(void); +void FindNewGoldieSpawnSite(void); + +void InitMsgCache(mCacheList *mList, unsigned listSize, unsigned infoSize); +void FreeMsgCache(mCacheList *mList, unsigned listSize); +void CacheMsg(mCacheInfo *ci, unsigned SegNum, unsigned MsgNum); +short LoadMsg(char far *hint_buffer, unsigned SegNum, unsigned MsgNum, unsigned MaxMsgLen); +void CacheConcessionMsg(void); +boolean ReuseMsg(mCacheInfo *ci, short count, short struct_size); + +void DropPlasmaDetonator(void); +void BlockDoorOpen(int door); +void BlastNearDoors(int tilex, int tiley); +void TryBlastDoor(char door); + +statobj_t *FindStatic(unsigned tilex, unsigned tiley); +statobj_t *UseReservedStatic(int itemtype, int tilex, int tiley); +void PlaceReservedItemNearTile(int itemtype, int tilex, int tiley); +void ExplodeStatics(int tilex, int tiley); + + +/* +============================================================================= + + 3D_ACT2 DEFINITIONS + +============================================================================= +*/ + +#define s_nakedbody s_static10 + + +#define BARRIER_STATE(obj) ((obj)->ammo) + +#define InitSmartAnim(obj, ShapeNum, StartOfs, MaxOfs, AnimType, AnimDir) \ + InitSmartSpeedAnim(obj, ShapeNum, StartOfs, MaxOfs, AnimType, AnimDir, 7) + + + +void MakeFakeStatic(objtype *ob); +void UnmakeFakeStatic(objtype *ob); + +extern char detonators_spawned; +extern int far starthitpoints[][NUMHITENEMIES]; +extern unsigned far MorphClass[]; + +extern statetype s_ofs_bounce; + +extern statetype s_ofs_esphere_death1; +extern statetype s_ofs_ouch; + +extern statetype s_ofs_static; + +extern statetype s_rent_die1; +extern statetype s_ofcdie1; +extern statetype s_swatdie1; +extern statetype s_prodie1; +extern statetype s_proshoot1; + + +extern statetype s_rent_chase1; +extern statetype s_ofcchase1; +extern statetype s_prochase1; +extern statetype s_swatchase1; + + +extern statetype s_rent_pain; +extern statetype s_ofcpain; +extern statetype s_propain; +extern statetype s_swatpain; + +extern statetype s_hold; + + +extern statetype s_swatwounded1; + +extern statetype s_deathcam; + + +extern statetype s_terrot_wait; +extern statetype s_terrot_seek1; + + +extern statetype s_ofs_stand; +extern statetype s_ofs_chase1; +extern statetype s_ofs_pain; +extern statetype s_ofs_die1; +extern statetype s_ofs_pod_death1; +extern statetype s_ofs_pod_ouch; +extern statetype s_ofs_attack1; + +extern statetype s_electro_appear1; +extern statetype s_electro_chase1; +extern statetype s_electro_ouch; +extern statetype s_electro_shoot1; +extern statetype s_electro_die1; + + +extern statetype s_liquid_wait; +extern statetype s_liquid_move; +extern statetype s_liquid_rise1; +extern statetype s_liquid_shoot1; +extern statetype s_liquid_ouch; +extern statetype s_liquid_shot; +extern statetype s_liquid_die1; +extern statetype s_liquid_shot_exp1; + + + +extern statetype s_goldstand; +extern statetype s_goldpath1; +extern statetype s_goldpain; +extern statetype s_goldshoot1; +extern statetype s_goldchase1; +extern statetype s_goldwarp_it; // Warp In w/button pressing +extern statetype s_goldwarp_out1; +extern statetype s_goldwarp_in1; + +extern statetype s_goldmorph1; +extern statetype s_goldmorph2; +extern statetype s_goldmorph3; +extern statetype s_goldmorph4; +extern statetype s_goldmorph5; +extern statetype s_goldmorph6; +extern statetype s_goldmorph7; +extern statetype s_goldmorph8; + +extern statetype s_mgold_chase1; +extern statetype s_mgold_chase2; +extern statetype s_mgold_chase3; +extern statetype s_mgold_chase4; + +extern statetype s_mgold_shoot1; +extern statetype s_mgold_shoot2; +extern statetype s_mgold_shoot3; +extern statetype s_mgold_shoot4; + +extern statetype s_mgold_pain; + +extern statetype s_security_light; + + +extern statetype s_scout_path1; +extern statetype s_scout_path2; +extern statetype s_scout_path3; +extern statetype s_scout_path4; +extern statetype s_scout_pain; +extern statetype s_scout_run; +extern statetype s_scout_run2; +extern statetype s_scout_run3; +extern statetype s_scout_run4; +extern statetype s_scout_die1; +extern statetype s_scout_stand; +extern statetype s_scout_dead; + +extern statetype s_explosion1; + +extern statetype s_steamgrate; +extern statetype s_vital; + +extern statetype s_vital_ouch; +extern statetype s_vital_die; +extern statetype s_vital_die1; +extern statetype s_vital_die2; +extern statetype s_vital_die3; +extern statetype s_vital_die4; +extern statetype s_vital_die5; +extern statetype s_vital_die6; +extern statetype s_vital_die7; +extern statetype s_vital_die8; + +extern statetype s_ooze_chase; +extern statetype s_vpost_barrier; +extern statetype s_spike_barrier; + +void T_PainThink(objtype *obj); +void T_ExplodeScout(objtype *obj); + + + +void T_Security(objtype *obj); + +void T_ChangeShape(objtype *obj); +void T_MakeOffset(objtype *obj); +void T_LiquidStand(objtype *obj); + +void PlaceTowardPlayer(objtype *obj); + +void T_Seek(objtype *ob); + +void SpawnProjectile(objtype *shooter, classtype class); +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); + +int IntSqrt(long va); +unsigned CalcDistance(unsigned x1, unsigned y1, unsigned x2, unsigned y2); + + +void T_Hit(objtype *ob); +void SpawnOffsetObj (enemy_t which, int tilex, int tiley); + + +void InitSmartAnimStruct(objtype *obj, unsigned ShapeNum, unsigned char StartOfs, unsigned char MaxOfs, animtype_t AnimType, animdir_t AnimDir); +boolean AnimateOfsObj(objtype *obj); +void AdvanceAnimREV(objtype *obj); +void AdvanceAnimFWD(objtype *obj); + +void SpawnCusExplosion(fixed x, fixed y, unsigned StartFrame, unsigned NumFrames, unsigned Delay, unsigned Class); +void T_SpawnExplosion(objtype *obj); +void T_ExplodeDamage(objtype *obj); + +void ExplodeRadius(objtype *obj, short damage,boolean damageplayer); + +extern statetype s_barrier_transition; +extern statetype s_barrier_shutdown; + +void SpawnBarrier (enemy_t which, int tilex, int tiley,boolean OnOff); +void ToggleBarrier(objtype *obj); + +void InitAnim(objtype *obj, unsigned ShapeNum, unsigned char StartOfs, unsigned char MaxOfs, animtype_t AnimType, animdir_t AnimDir, unsigned Delay, unsigned WaitDelay); + +objtype *FindObj(classtype which, short tilex, short tiley); +objtype *FindHiddenOfs(classtype which); +void SpawnHiddenOfs(enemy_t which, int tilex, int tiley); +objtype *MoveHiddenOfs(classtype which_class, classtype new, fixed x, fixed y); + +void CheckForSpecialTile(objtype *obj, unsigned tilex, unsigned tiley); +void DropCargo(objtype *obj); + + +/* +============================================================================= + + 3D_TEXT DEFINITIONS + +============================================================================= +*/ + +extern char helpfilename[],endfilename[]; + +extern void HelpScreens(void); +extern void EndText(void); + + +/* +============================================================================= + + 3D_MSGS TEXT DEFINITIONS + +============================================================================= +*/ + + +extern char far noeat_msg1[]; +extern char far bevs_msg1[]; +extern char far food_msg1[]; + +extern char far bonus_msg7[]; +extern char far bonus_msg26[]; + +extern char far * far BonusMsg[]; +extern char far * far ActorInfoMsg[]; +extern char far ekg_heartbeat_enabled[]; +extern char far ekg_heartbeat_disabled[]; +extern char far attacker_info_enabled[]; +extern char far attacker_info_disabled[]; +extern char far WeaponNotAvailMsg[]; +extern char far WeaponAvailMsg[]; +extern char far RadarAvailMsg[]; +extern char far RadarEnergyGoneMsg[]; +extern char far WeaponAutoSelectMsg[]; +extern char far EnergyPackDepleted[]; +extern char far NotEnoughEnergyForWeapon[]; + +extern char far WeaponMalfunction[]; + +extern char far SwitchNotActivateMsg[]; +extern char far NoFoodTokens[]; +extern char far ExtraMan[]; +extern char far OneMillion[]; +extern char far TenMillion[]; + +extern char far NoAdLibCard[]; +extern char far MusicOn[]; +extern char far MusicOff[]; +extern char far SoundOn[]; +extern char far SoundOff[]; + +extern char far pd_dropped[]; +extern char far pd_nomore[]; +extern char far pd_switching[]; +extern char far pd_notnear[]; +extern char far pd_getcloser[]; +extern char far pd_floorunlocked[]; +extern char far pd_donthaveany[]; +extern char far pd_no_computer[]; +extern char far pd_floornotlocked[]; + + + +/* +============================================================================= + + 3D_INTER DEFINITIONS + +============================================================================= +*/ + +extern char BreifingText[]; + +void UpdateScreenPic(void); +void DisplayPrepingMsg(char far *text); +boolean Breifing(breifing_type BreifingType,unsigned episode); +void ShPrint(char far *text, char shadow_color, boolean single_char); +unsigned Random(unsigned Max); + + +//=========================================================================== +// +// 3D_MENU DEFINATIONS - WHICH NEED TO BE GLOBAL +// +//=========================================================================== + +extern boolean EscPressed; + +void DrawInstructions(inst_type Type); +void CacheMessage(unsigned MessageNum); +void TerminateStr(char far *pos); +unsigned long CacheCompData(unsigned ItemNum, memptr *dest_loc); +boolean CheckForSpecialCode(unsigned ItemNum); + + +//=========================================================================== +// +// 3D_FREE DEFINATIONS - WHICH NEED TO BE GLOBAL +// +//=========================================================================== + + +extern char far JM_FREE_DATA_END[]; +extern char far JM_FREE_DATA_START[]; + diff --git a/3D_DRAW.C b/3D_DRAW.C new file mode 100644 index 0000000..87abb24 --- /dev/null +++ b/3D_DRAW.C @@ -0,0 +1,2214 @@ +// 3D_DRAW.C + +#include "3D_DEF.H" +#include +#pragma hdrstop + +//#define DEBUGWALLS +//#define DEBUGTICS + +//#define WOLFDOORS + +#define MASKABLE_DOORS (false) +#define MASKABLE_POSTS (false | MASKABLE_DOORS) + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +// the door is the last picture before the sprites + +#define DOORWALL (PMSpriteStart-(NUMDOORTYPES)) + +#define ACTORSIZE 0x4000 + +void DrawRadar(void); + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +// +// player interface stuff +// +int weaponchangetics,itemchangetics,bodychangetics; +int plaqueon,plaquetime,getpic; + +star_t *firststar,*laststar; + + +#ifdef DEBUGWALLS +unsigned screenloc[3]= {PAGE1START,PAGE1START,PAGE1START}; +#else +unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START}; +#endif +unsigned freelatch = FREESTART; + +long lasttimecount; +long frameon; +long framecount; + +unsigned wallheight[MAXVIEWWIDTH]; + +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; + +#ifndef WOLFDOORS +char far thetile[64]; +byte far * mytile; +#endif + + +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]; + + + +unsigned viewflags; +extern byte lightson; + +// Global Cloaked Shape flag.. + +boolean cloaked_shape = false; + + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +void AsmRefresh (void); // in 3D_DR_A.ASM +void NoWallAsmRefresh (void); // in 3D_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 + asm cmp ax,8 + asm jge exit_func + asm mov ax,8 + +exit_func: + +} + + +//========================================================================== + + + +/* +=================== += += ScalePost += +=================== +*/ + +long postsource; +unsigned postx; +unsigned bufx; +unsigned postwidth; +unsigned postheight; +byte far * shadingtable; +extern byte far * lightsource; + +void ScalePost (void) // VGA version +{ + int height; + long i; + byte ofs; + byte msk; + + height=(wallheight[postx])>>3; + postheight=height; + if (gamestate.flags & GS_LIGHTING) + { + + i=shade_max-(63l*(unsigned long)height/(unsigned long)normalshade); + + if (i<0) + i=0; + else + if (i > 63) + i = 63; // Debugging.. put break point here! + + shadingtable=lightsource+(i<<8); + bufx=postx>>2; + ofs=((postx&3)<<3)+postwidth-1; + outp(SC_INDEX+1,(byte)*((byte *)mapmasks1+ofs)); + DrawLSPost(); + msk=(byte)*((byte *)mapmasks2+ofs); + if (msk==0) + return; + bufx++; + outp(SC_INDEX+1,msk); + DrawLSPost(); + msk=(byte)*((byte *)mapmasks3+ofs); + if (msk==0) + return; + bufx++; + outp(SC_INDEX+1,msk); + DrawLSPost(); + } + else + { + bufx=postx>>2; + ofs=((postx&3)<<3)+postwidth-1; + outp(SC_INDEX+1,(byte)*((byte *)mapmasks1+ofs)); + DrawPost(); + msk=(byte)*((byte *)mapmasks2+ofs); + if (msk==0) + return; + bufx++; + outp(SC_INDEX+1,msk); + DrawPost(); + msk=(byte)*((byte *)mapmasks3+ofs); + if (msk==0) + return; + bufx++; + outp(SC_INDEX+1,msk); + DrawPost(); + } +} + +void FarScalePost () // 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 += +==================== +*/ + +unsigned far DoorJamsShade[] = +{ + BIO_JAM_SHADE, // dr_bio + SPACE_JAM_2_SHADE, // dr_normal + STEEL_JAM_SHADE, // dr_prison + SPACE_JAM_2_SHADE, // dr_elevator + STEEL_JAM_SHADE, // dr_high_sec + OFFICE_JAM_SHADE, // dr_office + STEEL_JAM_SHADE, // dr_oneway_left + STEEL_JAM_SHADE, // dr_oneway_up + STEEL_JAM_SHADE, // dr_oneway_right + STEEL_JAM_SHADE, // dr_oneway_down + SPACE_JAM_SHADE, // dr_space +}; + +unsigned far DoorJams[] = +{ + BIO_JAM, // dr_bio + SPACE_JAM_2, // dr_normal + STEEL_JAM, // dr_prison + SPACE_JAM_2, // dr_elevator + STEEL_JAM, // dr_high_sec + OFFICE_JAM, // dr_office + STEEL_JAM, // dr_oneway_left + STEEL_JAM, // dr_oneway_up + STEEL_JAM, // dr_oneway_right + STEEL_JAM, // dr_oneway_down + SPACE_JAM, // dr_space +}; + + + +void HitVertWall (void) +{ + int wallpic; + unsigned texture; + unsigned char doornum; + + 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 && postwidth < 8) + { + // 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 ((doornum = tilemap[xtile-xtilestep][ytile])&0x80 ) + wallpic = DOORWALL+DoorJamsShade[doorobjlist[doornum & 0x7f].type]; + 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; + unsigned char doornum; + + 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 && postwidth < 8) + { + // 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 ((doornum = tilemap[xtile][ytile-ytilestep]) & 0x80) + { + wallpic = DOORWALL+DoorJams[doorobjlist[doornum & 0x7f].type]; + } + 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 = -1,doornum,xint; + boolean lockable = true; + + doornum = tilehit&0x7f; + + if (doorobjlist[doornum].action == dr_jammed) + return; + +#ifdef WOLFDOORS + texture = ((xintercept-doorposition[doornum]) >> 4) &0xfc0; +#else + xint=xintercept&0xffff; + + if (xint>0x7fff) + texture = ( (xint-(unsigned)(doorposition[doornum]>>1)) >> 4) &0xfc0; + else + texture = ( (xint+(unsigned)(doorposition[doornum]>>1)) >> 4) &0xfc0; +#endif + + wallheight[pixx] = CalcHeight(); + + if (lasttilehit == tilehit) + { + // in the same door as last time, so check for optimized draw + + if (texture == (unsigned)postsource && postwidth < 8) + { + // wide scale + + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { +#if MASKABLE_DOORS + ScaleMPost(); +#else + ScalePost (); +#endif + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + if (lastside != -1) // if not the first scaled post +#if MASKABLE_DOORS + ScaleMPost(); +#else + ScalePost (); +#endif + + // first pixel in this door + + lastside = 2; + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + switch (doorobjlist[doornum].type) + { + case dr_normal: + doorpage = DOORWALL+L_METAL; + break; + + case dr_elevator: + doorpage = DOORWALL+L_ELEVATOR; + break; + + case dr_prison: + doorpage = DOORWALL+L_PRISON; + break; + + case dr_space: + doorpage = DOORWALL+L_SPACE; + break; + + case dr_bio: + doorpage = DOORWALL+L_BIO; + break; + + case dr_high_security: + doorpage = DOORWALL+L_HIGH_SECURITY; // Reverse View + break; + + case dr_oneway_up: + case dr_oneway_left: + if (player->tiley > doorobjlist[doornum].tiley) + doorpage = DOORWALL+L_ENTER_ONLY; // normal view + else + { + doorpage = DOORWALL+NOEXIT; // Reverse View + lockable = false; + } + break; + + case dr_oneway_right: + case dr_oneway_down: + if (player->tiley > doorobjlist[doornum].tiley) + { + doorpage = DOORWALL+NOEXIT; // normal view + lockable = false; + } + else + doorpage = DOORWALL+L_ENTER_ONLY; // Reverse View + break; + + case dr_office: + doorpage = DOORWALL+L_HIGH_TECH; + break; + } + + + // + // If door is unlocked, Inc shape ptr to unlocked door shapes + // + + if (lockable && doorobjlist[doornum].lock == kt_none) + doorpage += UL_METAL; + + + *( ((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage); + (unsigned)postsource = texture; + } +} + +//========================================================================== + + + +/* +==================== += += HitVertDoor += +==================== +*/ + +void HitVertDoor (void) +{ + unsigned texture,doorpage,doornum,yint; + boolean lockable = true; + + doornum = tilehit&0x7f; + + if (doorobjlist[doornum].action == dr_jammed) + return; + +#ifdef WOLFDOORS + texture = ( (yintercept-doorposition[doornum]) >> 4) &0xfc0; +#else + yint=yintercept&0xffff; + if (yint>0x7fff) + texture = ( (yint-(unsigned)(doorposition[doornum]>>1)) >> 4) &0xfc0; + else + texture = ( (yint+(unsigned)(doorposition[doornum]>>1)) >> 4) &0xfc0; +#endif + + wallheight[pixx] = CalcHeight(); + + if (lasttilehit == tilehit) + { + // in the same door as last time, so check for optimized draw + if (texture == (unsigned)postsource && postwidth < 8) + { + // wide scale + + postwidth++; + wallheight[pixx] = wallheight[pixx-1]; + return; + } + else + { +#if MASKABLE_DOORS + ScaleMPost(); +#else + ScalePost (); +#endif + (unsigned)postsource = texture; + postwidth = 1; + postx = pixx; + } + } + else + { + if (lastside != -1) // if not the first scaled post +#if MASKABLE_DOORS + ScaleMPost(); +#else + ScalePost (); +#endif + + // first pixel in this door + + lastside = 2; + lasttilehit = tilehit; + postx = pixx; + postwidth = 1; + + switch (doorobjlist[doornum].type) + { + case dr_normal: + doorpage = DOORWALL+L_METAL_SHADE; + break; + + case dr_elevator: + doorpage = DOORWALL+L_ELEVATOR_SHADE; + break; + + case dr_prison: + doorpage = DOORWALL+L_PRISON_SHADE; + break; + + case dr_space: + doorpage = DOORWALL+L_SPACE_SHADE; + break; + + case dr_bio: + doorpage = DOORWALL+L_BIO; + break; + + case dr_high_security: + doorpage = DOORWALL+L_HIGH_SECURITY_SHADE; + break; + + case dr_oneway_left: + case dr_oneway_up: + if (player->tilex > doorobjlist[doornum].tilex) + doorpage = DOORWALL+L_ENTER_ONLY_SHADE; // Reverse View + else + { + doorpage = DOORWALL+NOEXIT_SHADE; // Normal view + lockable = false; + } + break; + + case dr_oneway_right: + case dr_oneway_down: + if (player->tilex > doorobjlist[doornum].tilex) + { + doorpage = DOORWALL+NOEXIT_SHADE; // Reverse View + lockable = false; + } + else + doorpage = DOORWALL+L_ENTER_ONLY_SHADE; // Normal View + break; + + + case dr_office: + doorpage = DOORWALL+L_HIGH_TECH_SHADE; + break; + + } + + // + // If door is unlocked, Inc shape ptr to unlocked door shapes + // + + if (lockable && doorobjlist[doornum].lock == kt_none) + doorpage += UL_METAL; + + *(((unsigned *)&postsource)+1) = (unsigned)PM_GetPage(doorpage); + (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 && postwidth < 8) + { + // 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 && postwidth < 8) + { + // 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*MAPS_PER_EPISODE+mapon], + ceiling=egaCeiling[gamestate.episode*MAPS_PER_EPISODE+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,3 +asm sub dx,ax // dx = 40-viewwidth/8 + +asm mov bx,[viewwidth] +asm shr bx,4 // bl = viewwidth/16 +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 +//========================================================================== + + +#ifdef CEILING_FLOOR_COLORS +/* +===================== += += VGAClearScreen += += NOTE: Before calling this function - Check to see if there even needs += ==== to be a solid floor or solid ceiling color drawn. += +===================== +*/ + +void VGAClearScreen (void) +{ + viewflags = gamestate.flags; + +// +// 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,2 +asm sub dx,ax // dx = 40-viewwidth/2 + +asm mov bx,[viewwidth] +asm shr bx,3 // bl = viewwidth/8 +asm mov bh,BYTE PTR [viewheight] +asm shr bh,1 + +asm mov es,[screenseg] +asm mov di,[bufferofs] + +asm mov ax,[viewflags] +asm test ax,GS_DRAW_CEILING +asm jnz skiptop + +asm mov ax,[TopColor] + +// +// Draw Top +// + +toploop: + +asm mov cl,bl +asm rep stosw +asm add di,dx +asm dec bh +asm jnz toploop + +// +// Skip 'SkipTop' mods... +// + + +asm jmp bottominit + +// +// SkipTop mods - Compute the correct offset for the floor +// + +skiptop: +asm mov al,bh +asm mov cl,80 +asm mul cl +asm add di,ax + +// +// Test to see if bottom needs drawing +// + +bottominit: +asm mov ax,[viewflags] +asm test ax,GS_DRAW_FLOOR +asm jnz exit_mofo + +asm mov bh,BYTE PTR [viewheight] +asm shr bh,1 +asm mov ax,[BottomColor] + +// +// Draw Bottom +// + +bottomloop: +asm mov cl,bl +asm rep stosw +asm add di,dx +asm dec bh +asm jnz bottomloop + +exit_mofo: + +} +#endif + +//========================================================================== + +/* +===================== += += CalcRotate += +===================== +*/ + +int CalcRotate (objtype *ob) +{ + int angle,viewangle; + dirtype dir=ob->dir; + + // 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 (dir == nodir) + dir = ob->trydir&127; + angle = (viewangle-180)- dirangle[dir]; + + angle+=ANGLES/16; + while (angle>=ANGLES) + angle-=ANGLES; + while (angle<0) + angle+=ANGLES; + + if ((ob->state->flags & SF_PAINFRAME)) // 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 + + +#if 0 +typedef struct +{ + int viewx, + viewheight, + shapenum; +} visobj_t; +#endif + + +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 ((Keyboard[sc_6] && (Keyboard[sc_7] || Keyboard[sc_8]) && DebugOk) && (statptr->flags & FL_BONUS)) + { + GetBonus(statptr); + continue; + } + + 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 + + visptr->cloaked = false; + visptr->lighting = statptr->lighting; // Could add additional + // flashing/lighting + if (visptr < &vislist[MAXVISABLE-1]) // don't let it overflow + visptr++; + } + +// +// place active objects +// + for (obj = player->next;obj;obj=obj->next) + { + + + if (obj->flags & FL_OFFSET_STATES) + { + if (!(visptr->shapenum = obj->temp1+obj->state->shapenum)) + continue; // no shape + } + else + 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 + + if ((obj->flags2 & (FL2_CLOAKED|FL2_DAMAGE_CLOAK)) == (FL2_CLOAKED)) + { + visptr->cloaked = 1; + visptr->lighting = 0; + } + else + { + visptr->cloaked = 0; + visptr->lighting = obj->lighting; + } + + if (!(obj->flags & FL_DEADGUY)) + obj->flags2 &= ~FL2_DAMAGE_CLOAK; + + visptr->viewx = obj->viewx; + visptr->viewheight = obj->viewheight; + + if (visptr->shapenum == -1) + visptr->shapenum = obj->temp1; // special shape + + if (obj->state->flags & SF_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; + } + } + + // + // Init our global flag... + // + + cloaked_shape = farthest->cloaked; + + // + // draw farthest + // + if (gamestate.flags & GS_LIGHTING && (farthest->lighting != NO_SHADING) || cloaked_shape) + ScaleLSShape(farthest->viewx,farthest->shapenum,farthest->viewheight,farthest->lighting); + else + ScaleShape(farthest->viewx,farthest->shapenum,farthest->viewheight); + + farthest->viewheight = 32000; + } + + cloaked_shape = false; + +} + + +//========================================================================== + +/* +============== += += DrawPlayerWeapon += += Draw the player's hands += +============== +*/ + +int weaponscale[NUMWEAPONS] = {SPR_KNIFEREADY,SPR_PISTOLREADY + ,SPR_MACHINEGUNREADY,SPR_CHAINREADY,SPR_GRENADEREADY,SPR_BFG_WEAPON1,0}; + +boolean useBounceOffset=false; + +void DrawPlayerWeapon (void) +{ + int shapenum; + + if (playstate==ex_victorious) + return; + + if (gamestate.weapon != -1) + { + shapenum = weaponscale[gamestate.weapon]+gamestate.weaponframe; + if (shapenum) + { + static int vh=63; + static int ce=100; + + char v_table[15]={87,81,77,63,61,60,56,53,50,47,43,41,39,35,31}; + char c_table[15]={88,85,81,80,75,70,64,59,55,50,44,39,34,28,24}; + + int oldviewheight=viewheight; + int centery; + + useBounceOffset=true; +#if 1 +#if 0 + if (Keyboard[sc_PgUp]) + { + vh++; + Keyboard[sc_PgUp] = 0; + } + + if (Keyboard[sc_PgDn]) + { + if (vh) + vh--; + Keyboard[sc_PgDn] = 0; + } + + if (Keyboard[sc_End]) + { + ce++; + Keyboard[sc_End] = 0; + } + + if (Keyboard[sc_Home]) + { + if (ce) + ce--; + Keyboard[sc_Home] = 0; + } + + viewheight = vh; + centery = ce; +#endif + + viewheight = v_table[20-viewsize]; + centery = c_table[20-viewsize]; + MegaSimpleScaleShape(centerx,centery,shapenum,viewheight+1,0); + +#if 0 + mclear(); + mprintf("viewheight: %d \n",viewheight); + mprintf(" centery: %d \n",centery); +#endif +#else + SimpleScaleShape(viewwidth/2,shapenum,viewheight+1); +#endif + useBounceOffset=false; + + viewheight=oldviewheight; + } + } +} + +//========================================================================== + + +/* +===================== += += CalcTics += +===================== +*/ + +void CalcTics (void) +{ + long newtime,oldtimecount; + +#ifdef MYPROFILE + tics = 3; + 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; + } + } +} + + +//========================================================================== + + +/* +======================== += += 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 +} + + + +//========================================================================== + +extern short MsgTicsRemain; +extern unsigned LastMsgPri; + +//------------------------------------------------------------------------- +// RedrawStatusAreas() +//------------------------------------------------------------------------- +void RedrawStatusAreas() +{ + char loop; + + DrawInfoArea_COUNT = InitInfoArea_COUNT = 3; + + + for (loop=0; loop<3; loop++) + { + LatchDrawPic(0,0,TOP_STATUSBARPIC); + ShadowPrintLocationText(sp_normal); + + JLatchDrawPic(0,200-STATUSLINES,STATUSBARPIC); + DrawAmmoPic(); + DrawScoreNum(); + DrawWeaponPic(); + DrawAmmoNum(); + DrawKeyPics(); + DrawHealthNum(); + + bufferofs += SCREENSIZE; + if (bufferofs > PAGE3START) + bufferofs = PAGE1START; + } +} + +void F_MapLSRow(); +void C_MapLSRow(); +void MapLSRow(); + + +/* +======================== += += 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 + +#ifndef PAGEFLIP + bufferofs = displayofs = screenloc[0]; +#endif + + UpdateInfoAreaClock(); + UpdateStatusBar(); + + bufferofs += screenofs; + +// +// follow the walls from there to the right, drawwing as we go +// + +#ifdef CEILING_FLOOR_COLORS + if (gamestate.flags & GS_LIGHTING) + switch (gamestate.flags & (GS_DRAW_FLOOR|GS_DRAW_CEILING)) + { + case GS_DRAW_FLOOR|GS_DRAW_CEILING: + MapRowPtr = MapLSRow; + WallRefresh(); + DrawPlanes(); + break; + + case GS_DRAW_FLOOR: + MapRowPtr = F_MapLSRow; + VGAClearScreen(); + WallRefresh(); + DrawPlanes(); + break; + + case GS_DRAW_CEILING: + MapRowPtr = C_MapLSRow; + VGAClearScreen(); + WallRefresh(); + DrawPlanes(); + break; + + default: + VGAClearScreen(); + WallRefresh(); + break; + } + else + switch (gamestate.flags & (GS_DRAW_FLOOR|GS_DRAW_CEILING)) + { + case GS_DRAW_FLOOR|GS_DRAW_CEILING: + MapRowPtr = MapRow; + WallRefresh(); + DrawPlanes(); + break; + + case GS_DRAW_FLOOR: + MapRowPtr = F_MapRow; + VGAClearScreen(); + WallRefresh(); + DrawPlanes(); + break; + + case GS_DRAW_CEILING: + MapRowPtr = C_MapRow; + VGAClearScreen(); + WallRefresh(); + DrawPlanes(); + break; + + default: + VGAClearScreen(); + WallRefresh(); + break; + } +#else + + if (gamestate.flags & GS_LIGHTING) + MapRowPtr = MapLSRow; + else + MapRowPtr = MapRow; + + WallRefresh(); + DrawPlanes(); + +#endif + + UpdateTravelTable(); + +// +// draw all the scaled images +// + + DrawScaleds(); // draw scaled stuf + + DrawPlayerWeapon (); // draw player's hands + + +// +// show screen and time last cycle +// + if (fizzlein) + { + FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,70,false); + fizzlein = false; + + lasttimecount = TimeCount; // don't make a big tic count + } + + bufferofs -= screenofs; + + DrawRadar(); + +// VW_WaitVBL(1); // mike check this out + +#ifdef PAGEFLIP + NextBuffer(); +#endif + + frameon++; + PM_NextFrame(); +} + +//-------------------------------------------------------------------------- +// NextBuffer() +//-------------------------------------------------------------------------- +int NextBuffer() +{ + displayofs=bufferofs; + +#ifdef PAGEFLIP + 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 sti +#endif + + bufferofs += SCREENSIZE; + if (bufferofs > PAGE3START) + bufferofs = PAGE1START; +} + +byte far TravelTable[MAPSIZE][MAPSIZE]; + +//-------------------------------------------------------------------------- +// UpdateTravelTable() +//-------------------------------------------------------------------------- +void UpdateTravelTable() +{ +asm mov si,OFFSET [spotvis] +asm mov ax,SEG [TravelTable] +asm mov es,ax +asm mov di,OFFSET [TravelTable] +asm mov cx,00800h // HARDCODED for 64x64 / 2!! + +loop1: +asm mov ax,[si] +asm inc si +asm inc si +asm or [es:di],ax +asm inc di +asm inc di +asm loop loop1 +} + +extern short an_offset[]; + + +//-------------------------------------------------------------------------- +// DrawRadar() +//-------------------------------------------------------------------------- +void DrawRadar() +{ + char zoom=gamestate.rzoom; + byte flags = OV_KEYS|OV_PUSHWALLS|OV_ACTORS; + + if (gamestate.rpower) + { + if ((frameon & 1) && (!godmode)) + if (zoom) + gamestate.rpower -= tics<angle]; + pcos=costable[player->angle]; + +// Convert radius to fixed integer and calc rotation. +// + dx = dy = (long)radius<x+(FixedByFrac(dx,pcos)-FixedByFrac(dy,psin)); + baselmy = player->y-(FixedByFrac(dx,psin)+FixedByFrac(dy,pcos)); + +// Carmack's sin/cos tables use one's complement for negative numbers -- +// convert it to two's complement! +// + if (pcos & 0x80000000) + pcos = -(pcos & 0xffff); + + if (psin & 0x80000000) + psin = -(psin & 0xffff); + +// Get x/y increment values. +// + xinc = -pcos; + yinc = psin; + +// Calculate starting destination address. +// + basedst=MK_FP(SCREENSEG,bufferofs+ylookup[by]+(bx>>2)); + switch (zoom) + { + case 1: + startmask = 1; + mask = pixmasks[bx&3]; + break; + + case 2: // bx MUST be byte aligned for 2x zoom + mask = startmask = 3; + break; + + case 4: // bx MUST be byte aligned for 4x zoom + mask = startmask = 15; + break; + } + VGAMAPMASK(mask); + +// Draw rotated radar. +// + rx = radius*2; + while (rx--) + { + lmx = baselmx; + lmy = baselmy; + + dstptr = basedst; + + ry = radius*2; + while (ry--) + { + if (snow) + { + color = 0x42+(rndtable[rndindex]&3); + rndindex++; // += ((rndindex<<1) + 1); + goto nextx; + } + + // Don't evaluate if point is outside of map. + // + color = UNMAPPED_COLOR; + mx = lmx>>16; + my = lmy>>16; + if (mx<0 || mx>63 || my<0 || my>63) + goto nextx; + + // SHOW PLAYER + // + if (drawplayerok && player->tilex==mx && player->tiley==my) + { + color = PLAYER_COLOR; + drawplayerok=false; + } + else + // SHOW TRAVELED + // + if ((TravelTable[mx][my] & TT_TRAVELED) || (flags & OV_SHOWALL)) + { + // What's at this map location? + // + tile=tilemap[mx][my]; + door=tile&0x3f; + + // Evaluate wall or floor? + // + if (tile) + { + // SHOW DOORS + // + if (tile & 0x80) + if (doorobjlist[door].lock!=kt_none) + color=0x18; // locked! + else + if (doorobjlist[door].action==dr_closed) + color=0x58; // closed! + else + color = MAPPED_COLOR; // floor! + } + else + color = MAPPED_COLOR; // floor! + + // SHOW KEYS + // + if ((flags & OV_KEYS) && (TravelTable[mx][my] & TT_KEYS)) + color = 0xf3; + + if ((zoom > 1) || (ExtraRadarFlags & OV_ACTORS)) + { + ob=(objtype *)actorat[mx][my]; + + // SHOW ACTORS + // + if ((flags & OV_ACTORS) && (ob >= objlist) && (!(ob->flags & FL_DEADGUY)) && + (ob->obclass > deadobj) && (ob->obclass < SPACER1_OBJ)) + color = 0x10+ob->obclass; + + if ((zoom == 4) || (ExtraRadarFlags & OV_PUSHWALLS)) + { + unsigned iconnum; + + iconnum = *(mapsegs[1]+farmapylookup[my]+mx); + + // SHOW PUSHWALLS + // + if ((flags & OV_PUSHWALLS) && (iconnum == PUSHABLETILE)) + color = 0x79; + } + } + } + else + color = UNMAPPED_COLOR; + +nextx:; + // Display pixel for this quadrant and add x/y increments + // + *dstptr = color; + dstptr += 80; + + if (zoom > 1) // handle 2x zoom + { + *dstptr = color; + dstptr += 80; + + if (zoom > 2) // handle 4x zoom + { + *dstptr = color; + dstptr += 80; + + *dstptr = color; + dstptr += 80; + } + } + + lmx += xinc; + lmy += yinc; + } + + baselmx += yinc; + baselmy -= xinc; + + mask <<= zoom; + if (mask>15) + { + mask=startmask; + basedst++; + } + VGAMAPMASK(mask); + } + + VGAMAPMASK(15); +} diff --git a/3D_DRAW2.C b/3D_DRAW2.C new file mode 100644 index 0000000..6621b7f --- /dev/null +++ b/3D_DRAW2.C @@ -0,0 +1,248 @@ +// WOLFHACK.C + +#include "3D_DEF.H" + +#define MAXVIEWHEIGHT 200 +#define GAMESTATE_TEST (true) + + +unsigned CeilingTile=126, FloorTile=126; + +void (*MapRowPtr)(); + +int far spanstart[MAXVIEWHEIGHT/2]; // jtr - far + +fixed stepscale[MAXVIEWHEIGHT/2]; +fixed basedist[MAXVIEWHEIGHT/2]; + +extern char far planepics[8192]; // 4k of ceiling, 4k of floor + +int halfheight = 0; + +byte far *planeylookup[MAXVIEWHEIGHT/2]; +unsigned far mirrorofs[MAXVIEWHEIGHT/2]; + +fixed psin, pcos; + +fixed FixedMul (fixed a, fixed b) +{ + return (a>>8)*(b>>8); +} + + +int mr_rowofs; +int mr_count; +int mr_xstep; +int mr_ystep; +int mr_xfrac; +int mr_yfrac; +int mr_dest; + + +/* +============== += += DrawSpans += += Height ranges from 0 (infinity) to viewheight/2 (nearest) +============== +*/ +extern byte far * lightsource; +extern byte far * shadingtable; + + +void DrawSpans (int x1, int x2, int height) +{ + fixed length; + int ofs; + int prestep; + fixed startxfrac, startyfrac; + + int x, startx, count, plane, startplane; + byte far *toprow, far *dest; + long i; + + toprow = planeylookup[height]+bufferofs; + mr_rowofs = mirrorofs[height]; + + mr_xstep = (psin<<1)/height; + mr_ystep = (pcos<<1)/height; + + length = basedist[height]; + startxfrac = (viewx + FixedMul(length,pcos)); + startyfrac = (viewy - FixedMul(length,psin)); + +// draw two spans simultaniously + + if (gamestate.flags & GS_LIGHTING) + { + + i=shade_max-(63l*(unsigned long)height/(unsigned long)normalshade); + if (i<0) + i=0; + else + if (i>63) + i = 63; + shadingtable=lightsource+(i<<8); + plane = startplane = x1&3; + prestep = viewwidth/2 - x1; + do + { + outportb (SC_INDEX+1,1<>2)*prestep; + mr_yfrac = startyfrac - (mr_ystep>>2)*prestep; + + startx = x1>>2; + mr_dest = (unsigned)toprow + startx; + mr_count = ((x2-plane)>>2) - startx + 1; + x1++; + prestep--; + +#if GAMESTATE_TEST + if (mr_count) + MapRowPtr(); +#else + if (mr_count) + MapLSRow (); +#endif + + plane = (plane+1)&3; + } while (plane != startplane); + } + else + { + plane = startplane = x1&3; + prestep = viewwidth/2 - x1; + do + { + outportb (SC_INDEX+1,1<>2)*prestep; + mr_yfrac = startyfrac - (mr_ystep>>2)*prestep; + + startx = x1>>2; + mr_dest = (unsigned)toprow + startx; + mr_count = ((x2-plane)>>2) - startx + 1; + x1++; + prestep--; + +#if GAMESTATE_TEST + if (mr_count) + MapRowPtr(); +#else + if (mr_count) + MapRow (); +#endif + + plane = (plane+1)&3; + } while (plane != startplane); + } +} + + + + +/* +=================== += += SetPlaneViewSize += +=================== +*/ + +void SetPlaneViewSize (void) +{ + int x,y; + byte far *dest, far *src; + + halfheight = viewheight>>1; + + + for (y=0 ; y0) + basedist[y] = GLOBAL1/2*scale/y; + } + + src = PM_GetPage(CeilingTile); + dest = planepics; + for (x=0 ; x<4096 ; x++) + { + *dest = *src++; + dest += 2; + } + src = PM_GetPage(FloorTile); + dest = planepics+1; + for (x=0 ; x<4096 ; x++) + { + *dest = *src++; + dest += 2; + } + +} + + +/* +=================== += += DrawPlanes += +=================== +*/ + +void DrawPlanes (void) +{ + int height, lastheight; + int x; + +#if IN_DEVELOPMENT + if (!MapRowPtr) + DRAW2_ERROR(NULL_FUNC_PTR_PASSED); +#endif + + + if (viewheight>>1 != halfheight) + SetPlaneViewSize (); // screen size has changed + + + psin = viewsin; + if (psin < 0) + psin = -(psin&0xffff); + pcos = viewcos; + if (pcos < 0) + pcos = -(pcos&0xffff); + +// +// loop over all columns +// + lastheight = halfheight; + + for (x=0 ; x>3; + if (height < lastheight) + { // more starts + do + { + spanstart[--lastheight] = x; + } while (lastheight > height); + } + else if (height > lastheight) + { // draw spans + if (height > halfheight) + height = halfheight; + for ( ; lastheight < height ; lastheight++) + if (lastheight>0) + DrawSpans (spanstart[lastheight], x-1, lastheight); + } + } + + height = halfheight; + for ( ; lastheight < height ; lastheight++) + if (lastheight>0) + DrawSpans (spanstart[lastheight], x-1, lastheight); +} + diff --git a/3D_GAME.C b/3D_GAME.C new file mode 100644 index 0000000..fa81ebc --- /dev/null +++ b/3D_GAME.C @@ -0,0 +1,3522 @@ +// 3D_GAME.C + +#include "3D_DEF.H" +#pragma hdrstop + +#ifdef MYPROFILE +#include +#endif + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define LOCATION_TEXT_COLOR 0xAF +extern char far prep_msg[]; +extern char LS_current,LS_total; +void Died (void); + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +#if IN_DEVELOPMENT +int db_count=0; +#ifdef DEBUG_STATICS +classtype far debug_bonus[2][800]; +#endif +#endif + +fargametype far gamestuff; +gametype gamestate; +boolean ingame,fizzlein; +unsigned latchpics[NUMLATCHPICS]; +eaWallInfo eaList[MAXEAWALLS]; +char NumEAWalls; + +tilecoord_t far GoldieList[GOLDIE_MAX_SPAWNS]; +GoldsternInfo_t GoldsternInfo; + +extern unsigned scan_value; + + +// +// ELEVATOR BACK MAPS - REMEMBER (-1)!! +// +// int ElevatorBackTo[]={1,1,7,3,5,3}; + +void ScanInfoPlane (void); +void SetupGameLevel (void); +void DrawPlayScreen (boolean InitInfoMsg); +void LoadLatchMem (void); +void GameLoop (void); + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + + +// +// NOTE: This array indexs the "statinfo" array in ACT1.C and is indexed +// upon tile number/values. +// + +char far ExpCrateShapes[] = +{ + 42, // Chicken Leg + 44, // Ham/Steak + 26, // Clip + 24, // Pistol + 27, // Pulse + 28, // ION + 46, // Grenade + 62, // Money Bag + 63, // Loot + 64, // Gold + 65, // Bonus + 71, // Gore 1 + 74, // Gore 2 + 32, // red key + 33, // yel key + 52-23, // grn key + 35, // blu key + 488-375, // gld key +}; + + + + + +//=========================================================================== +//=========================================================================== + + +/* +========================== += += 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 (); +} + +#if 0 + +//------------------------------------------------------------------------- +// FreeAllMemory() - This is an attempt to FREE All possible memory +// for memory hungry routines (ie. MOVIE_Play()) +// +// NOTE: If any sounds, music, etc are currently playing, they will +// adbruply cut off. +//------------------------------------------------------------------------- +void FreeAllMemory(void) +{ + StopMusic(); + SD_StopSound(); + ClearMemory(); +} + +#endif + +#ifdef TRACK_ENEMY_COUNT +short numEnemy[gold_morphingobj]; +#endif + +/* +========================== += += ScanInfoPlane += += Spawn all actors and mark down special places += +========================== +*/ + +#define INVALID_ACTOR_ERR Quit("Invalid actor: %d %d",x,y) + +void ScanInfoPlane (void) +{ + unsigned x,y,i,j; + int tile; + unsigned far *start, far *floor; + boolean gotlight = false,gottextures = false; + +#ifdef CEILING_FLOOR_COLORS + boolean gotcolors=false; +#endif + +#if IN_DEVELOPMENT + db_count=0; +#endif + detonators_spawned = 0; + +#ifdef TRACK_ENEMY_COUNT +memset(numEnemy,0,sizeof(numEnemy)); +#endif + + new=NULL; + start = mapsegs[1]; + for (y=0;y>8; + tilelo = (tile&0xff); + + if ((*start & 0xff00) == 0xfa00) + scan_value = *start & 0x00ff; + else + scan_value = 0xffff; + +#pragma warn -rch + switch (tilehi) + { + + +// case 0xff: // Special background +// continue; +// break; + +#ifdef CEILING_FLOOR_COLORS + case 0xfe: // Top/Bottom colors + if (gotcolors) + break; + x++; + tile = *start++; + TopColor = tile&0xff00; + TopColor |= TopColor>>8; + BottomColor = tile&0xff; + BottomColor |= BottomColor<<8; + gotcolors=true; + continue; + break; +#else + case 0xfe: // Top/Bottom colors + x++; + tile = *start++; + // Give error + continue; + break; +#endif + + + case 0xFB: // Global Ceiling/Floor textures + if (gottextures) + break; + x++; + tile = *start++; + + CeilingTile = START_TEXTURES+((tile&0xff00)>>8); + if (CeilingTile > NUM_TILES-1) + GAME_ERROR(CEILING_TILE_OUT_OF_RANGE); + + FloorTile = START_TEXTURES+(tile&0xff); + if (FloorTile > NUM_TILES-1) + GAME_ERROR(FLOOR_TILE_OUT_OF_RANGE); + + gottextures = true; + continue; + break; + + + case 0xf5: // IntraLevel warp + *(start-1) = *start; // Move Coord right on top + *start = 0; + continue; + break; + +#if 0 + case 0xF6: // Lighting effects + if (gotlight) + break; + x++; + tile = *start++; + normalshade_div = (tile&0xff00)>>8 ; + if (normalshade_div > 12) + AGENT_ERROR(NORMAL_SHADE_TOO_BIG); + + shade_max = tile&0xff; + if (shade_max > 63 || shade_max < 5) + AGENT_ERROR(SHADEMAX_VALUE_BAD); + + normalshade=(3*(maxscale>>2))/normalshade_div; + gotlight = true; + continue; + break; +#endif + + case 0xfa: + continue; + + case 0xf1: // Informant messages + case 0xf2: // "Nice" scientist messages + case 0xf3: // "Mean" scientist messages + switch (tilehi) + { + case 0xf1: + block=INFORMANT_HINTS; + st=&InfHintList; + break; + + case 0xf2: + block=NICE_SCIE_HINTS; + st=&NiceSciList; + break; + + case 0xf3: + block=MEAN_SCIE_HINTS; + st=&MeanSciList; + break; + } + + ci = &st->smInfo[st->NumMsgs]; + ci->mInfo.local_val = 0xff; + ci->mInfo.global_val = tilelo; + if (!ReuseMsg((mCacheInfo *)ci,st->NumMsgs,sizeof(sci_mCacheInfo))) + { + CacheMsg((mCacheInfo *)ci,block,ci->mInfo.global_val); + ci->mInfo.local_val = InfHintList.NumMsgs; + } + + if (++st->NumMsgs > MAX_CACHE_MSGS) + GAME_ERROR(SCANINFO_CACHE_MSG_OVERFLOW); + + ci->areanumber=GetAreaNumber(x,y); + + if (ci->areanumber >= NUMAREAS) + ci->areanumber = 0xff; + continue; + break; + + case 0: + if (!tilelo) + continue; + break; + } + +#pragma warn +rch + + // + // SPECIAL SPAWN CODING FOR BLASTABLE CRATES... + // + + if (tile >=432 && tile <=485) + { +#if GAME_VERSION != SHAREWARE_VERSION + if (tile>=468) + { + SpawnOffsetObj(en_crate3,x,y); + new->temp2 = ExpCrateShapes[tile - 468]; + new->temp3 = (unsigned)ReserveStatic(); + + if ((tile >= 475) && (tile <= 478)) + tile=(tile-475)+bo_money_bag; + else + tile=0; + } + else + if (tile >=450) + { + SpawnOffsetObj(en_crate2,x,y); + new->temp2 = ExpCrateShapes[tile - 450]; + new->temp3 = (unsigned)ReserveStatic(); + + if ((tile >= 457) && (tile <= 460)) + tile=(tile-457)+bo_money_bag; + else + tile=0; + } + else +#endif +#if GAME_VERSION == SHAREWARE_VERSION +#if IN_DEVELOPMENT + if (tile >= 450) + INVALID_ACTOR_ERR; + else +#endif +#endif + if (tile >= 432) + { + SpawnOffsetObj(en_crate1,x,y); + new->temp2 = ExpCrateShapes[tile - 432]; + new->temp3 = (unsigned)ReserveStatic(); + + if ((tile >= 439) && (tile <= 442)) + tile=(tile-439)+bo_money_bag; + else + tile=0; + } + + if (tile) + { + if (tile > bo_loot) + tile += 3; + tile -= bo_money_bag; + AddTotalPoints(static_points[tile]); +#if IN_DEVELOPMENT +#ifdef DEBUG_STATICS + debug_bonus[0][db_count++] = static_points[tile]; +#endif +#endif + } + + continue; + } + + switch (tile) + { + case 19: + case 20: + case 21: + case 22: + SpawnPlayer(x,y,NORTH+tile-19); + break; + + case 85: // Money bag + case 86: // Loot + case 87: // Gold + case 88: // Bonus + AddTotalPoints(static_points[statinfo[tile-23].type-bo_money_bag]); +#if IN_DEVELOPMENT +#ifdef DEBUG_STATICS + debug_bonus[0][db_count++] = static_points[statinfo[tile-23].type-bo_money_bag]; +#endif +#endif + + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + + +#if GAME_VERSION != SHAREWARE_VERSION + case 30: // Yellow Puddle +#endif + + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: // BFG Weapon + case 72: // Gurney Mutant + case 73: // Large Canister + case 74: // Small Canister + case 75: // Empty Gurney + case 76: // Empty Large Canister + case 77: // Empty Small Canister + case 78: // Dead Gen. Sci. + + case 80: + case 83: // Floor Grate + case 84: // Floor Pipe + SpawnStatic(x,y,tile-23); + break; + + case 399: // gold 1 + case 400: // gold 2 + case 401: // gold 3 + AddTotalPoints(static_points[statinfo[tile-315].type-bo_money_bag]); +#if IN_DEVELOPMENT +#ifdef DEBUG_STATICS + debug_bonus[0][db_count++] = static_points[statinfo[tile-315].type-bo_money_bag]; +#endif +#endif + + case 381: + case 382: + case 383: + case 384: + case 385: + case 386: + case 387: + case 388: +// case 389: + case 390: // candy bar + case 391: // sandwich + + case 395: // Table + case 396: // Chair + case 397: // Stool + case 398: // Gore + + case 402: // + case 403: // + case 404: // + case 405: // + case 406: // + case 407: // + case 408: // + case 409: // + case 410: // + case 411: // + case 412: // + case 413: // + case 414: // + case 415: // + case 416: // + case 417: // + case 418: // + case 419: // + case 420: // + case 421: // + case 422: // + case 423: // bo_coin + case 424: // bo_coin5 + SpawnStatic(x,y,tile-315); + break; + + case 486: // Plasma Detonator + SpawnHiddenOfs(en_plasma_detonator_reserve, x, y); // Spawn a reserve + SpawnStatic(x,y,486-375); + break; + + case 487: // Door rubble + case 488: // AutoMapper Bonus #1 + case 489: // BonziTree + case 490: // Yellow Potted plant + case 491: // Tube Plant + case 492: // HiTech Chair + case 493: // AOG: Rent A Cop - Dead. + case 494: // AOG: Pro Guard - Dead. + case 495: // AOG: Swat Guard - Dead. + SpawnStatic(x,y,tile-375); + break; + + + case 393: // crate 2 + case 394: // crate 3 +#if GAME_VERSION == SHAREWARE_VERSION +#if IN_DEVELOPMENT + INVALID_ACTOR_ERR; +#endif +#endif + case 392: // crate 1 + SpawnStatic(x,y,tile-315); + break; + + case 81: + case 82: + SpawnOffsetObj(en_bloodvent+tile-81,x,y); + break; + + + // + // GREEN OOZE + // + + case 208: + if (gamestate.difficultylighting = LAMP_ON_SHADING; + break; + + + + // Arc Barrier + // + case 174: + case 175: + // + // 174=off,175=on + // + SpawnBarrier(en_post_barrier,x,y,tile-174); + break; + + // Plasma Post Barrier + // + case 138: + case 139: + // + // 138=off,139=on + // + SpawnBarrier(en_arc_barrier,x,y,tile-138); + break; + + + // + // VPOST Barrier + // + + // + // Switchable + // + case 563: // On + case 562: // Off + SpawnBarrier(en_vpost_barrier,x,y,tile-562); + break; + + + // + // Cycle + // + case 567: + if (gamestate.difficultyflags & FL_INFORMANT) + { + AddTotalInformants(1); + new=NULL; + } + break; + + + case 192: + case 193: + case 194: + case 195: + if (gamestate.difficultyflags & FL_INFORMANT) + { + AddTotalInformants(1); + new=NULL; + } + break; + + + // + // PROGUARD + // + case 198: + case 199: + case 200: + case 201: + if (gamestate.difficultyflags |= FL_STATIONARY; + break; + + + // + // FLOATING BOMB - Start Stationary + // + + case 296: + case 297: + case 298: + case 299: + if (gamestate.difficultyobclass = deadobj; + else + { + AddTotalPoints(actor_points[podobj-rentacopobj]); + AddTotalEnemy(1); +#ifdef TRACK_ENEMY_COUNT +numEnemy[podobj]++; +#endif + } + scan_value=0xffff; + break; + + + // Morphing Brown/LBlue Post -> Spider Mutant + // + case 610: + if (gamestate.difficultyobclass]++; +#endif + } + scan_value=0xffff; + break; + + + // Morphing Gray/Green Post -> Reptilian Warrior + // + case 592: + if (gamestate.difficultyobclass]++; +#endif + } + scan_value=0xffff; + break; + + + + // Morphing Statue -> Blue Boy + // + case 628: + if (gamestate.difficultyobclass]++; +#endif + } + scan_value=0xffff; + break; + + + + // P.O.D. Alien + // + case 309: + if (gamestate.difficultyflags |= FL_STATIONARY; + break; + + + // + // ROTATING HANGING TURRETS + // + case 372: + case 373: + case 374: + case 375: + if (gamestate.difficultyflags &= ~FL_RANDOM_TURN; + break; + + // + // VOLATILE MAT. TRANSPORT + // + + case 548: + case 549: + case 550: + case 551: + if (gamestate.difficultyflags &= ~FL_RANDOM_TURN; + break; + + // + // FLOATING BOMB - + // + + case 544: + case 545: + case 546: + case 547: + if (gamestate.difficultyflags &= ~FL_RANDOM_TURN; + break; + + // + // PRO GUARD + // + + case 594: + case 595: + case 596: + case 597: + if (gamestate.difficultyflags &= ~FL_RANDOM_TURN; + break; + + + // + // RENT-A-COP + // + + case 552: + case 553: + case 554: + case 555: + if (gamestate.difficultyflags &= ~FL_RANDOM_TURN; + break; + + +//----------------------- +// BOSS ACTORS +//----------------------- + + case 630: // FINAL BOSS 1 + case 631: // FINAL BOSS 2 + case 632: // FINAL BOSS 3 + case 633: // FINAL BOSS 4 + SpawnOffsetObj(en_final_boss1+tile-630,x,y); + break; + + } + + // If "new" is an object that gives points, add those points to level total... + // + // "new" is cleared to keep from re-adding points from the previous actor! + // + if (new && (new->obclass >= rentacopobj) && (new->obclassobclass; + + switch (obclass) + { + case lcan_wait_alienobj: + case scan_wait_alienobj: + case gurney_waitobj: + obclass++; + break; + } + + AddTotalPoints(actor_points[obclass-rentacopobj]); + AddTotalEnemy(1); +#ifdef TRACK_ENEMY_COUNT +numEnemy[new->obclass]++; +#endif + new = NULL; + } + + // Skip past FA code... + // + if (scan_value != 0xffff) + { + x++; + start++; + } + } + + if (!loadedgame) + gamestuff.level[gamestate.mapon].stats.accum_inf = gamestuff.level[gamestate.mapon].stats.total_inf; + +#if IN_DEVELOPMENT + db_count=0; +#endif +} + +//-------------------------------------------------------------------------- +// AddTotalPoints() +//-------------------------------------------------------------------------- +void AddTotalPoints(unsigned points) +{ + if (loadedgame) + return; + + gamestuff.level[gamestate.mapon].stats.total_points += points; +} + +//-------------------------------------------------------------------------- +// AddTotalInformants() +//-------------------------------------------------------------------------- +void AddTotalInformants(char informants) +{ + if (loadedgame) + return; + + gamestuff.level[gamestate.mapon].stats.total_inf += informants; +} + +//-------------------------------------------------------------------------- +// AddTotalEnemy() +//-------------------------------------------------------------------------- +void AddTotalEnemy(unsigned enemies) +{ + if (loadedgame) + return; + + gamestuff.level[gamestate.mapon].stats.total_enemy += enemies; +} + +//========================================================================== + +/* +================== += += SetupGameLevel += +================== +*/ + +short an_offset[8] = {1,-1,64,-64,-65,-63,63,65}; + +void SetupGameLevel (void) +{ + extern boolean ForceLoadDefault; + boolean switchon = false; + memptr hold; + sci_mCacheInfo *ci = InfHintList.smInfo; + int x,y,i; + unsigned far *map,tile,spot,icon; + keytype lock; + unsigned far *map1,far *map2; + char far *temp_ptr; + short count; + + if (!loadedgame) + { + gamestate.flags |= GS_CLIP_WALLS; + InitGoldsternInfo(); + } + + if (demoplayback || demorecord) + US_InitRndT (false); + else + US_InitRndT (true); + +// +// load the level +// + CA_CacheMap (gamestate.mapon+MAPS_PER_EPISODE*gamestate.episode); + mapon-=gamestate.episode*MAPS_PER_EPISODE; + + mapwidth = mapheaderseg[mapon]->width; + mapheight = mapheaderseg[mapon]->height; + + if (mapwidth != 64 || mapheight != 64) + GAME_ERROR(SETUPGAME_BAD_MAP_SIZE); + + LoadLocationText(gamestate.mapon+MAPS_PER_EPISODE*gamestate.episode); + +// +// copy the wall data to a data segment array +// + _fmemset (TravelTable,0,sizeof(TravelTable)); + _fmemset (gamestate.barrier_table,0xff,sizeof(gamestate.barrier_table)); + _fmemset (gamestate.old_barrier_table,0xff,sizeof(gamestate.old_barrier_table)); + memset (tilemap,0,sizeof(tilemap)); + memset (actorat,0,sizeof(actorat)); + memset (wallheight,0,sizeof(wallheight)); + + map = mapsegs[0]; + map2 = mapsegs[1]; + for (y=0;y= 88 && tile <= 105) + { + // + // KEYS + // + + switch (lock) + { + case 55: + case 56: + lock = kt_red+lock-55; + *map1 = 0; + break; + + case 58: + lock = kt_blue; + *map1 = 0; + break; + + default: + lock = kt_none; + } + + // + // DOOR + // + + switch (tile) + { + + case 88: + case 89: + SpawnDoor (x,y,!(tile%2),lock,dr_bio); + break; + + case 90: + case 91: + SpawnDoor (x,y,!(tile%2),lock,dr_normal); + break; + + case 92: + case 93: + SpawnDoor (x,y,!(tile%2),lock,dr_prison); + break; + + case 94: + case 95: + SpawnDoor (x,y,!(tile%2),lock,dr_elevator); + break; + + case 96: + case 97: + SpawnDoor (x,y,!(tile%2),lock,dr_high_security); + break; + + case 98: // oneway left - Vert + case 99: // oneway up - Horz + case 100: // oneway right - Vert + case 101: // oneway down - Horz + SpawnDoor (x,y,!(tile%2),lock,dr_oneway_left+(tile-98)); + break; + + case 102: + case 103: + SpawnDoor (x,y,!(tile%2),lock,dr_office); + break; + + case 104: + case 105: + SpawnDoor (x,y,!(tile%2),lock,dr_space); + break; + + + } + } + else + switch (tile) + { + case SODATILE: + if (!loadedgame) + { + SpawnConcession(x,y,lock,CT_BEVS); + *map1 = 0; + } + break; + + + + case FOODTILE: + if (!loadedgame) + { + SpawnConcession(x,y,lock,CT_FOOD); + *map1 = 0; + } + break; + + case EATILE: + eaList[NumEAWalls].tilex=x; + eaList[NumEAWalls].tiley=y; + eaList[NumEAWalls].aliens_out=0; + if ((lock & 0xff00) == 0xfa00) + eaList[NumEAWalls].delay=60*(lock&0xff); + else + eaList[NumEAWalls].delay=60*8+Random(60*22); + if (NumEAWalls++ == MAXEAWALLS) + GAME_ERROR(SETUPGAME_MAX_EA_WALLS); + break; + + case ON_SWITCH: + switchon = true; + case OFF_SWITCH: + *(map1) = 0xf800 | UpdateBarrierTable((*(map1)>>8),*(map1)&0xff,switchon); + + // Init for next time. + + switchon = false; + break; + } + + map1++; + } + + +// +// spawn actors +// + + ScanInfoPlane (); + ConnectBarriers(); + +// Init informant stuff +// + count = InfHintList.NumMsgs; + LastInfArea=0xff; + FirstGenInfMsg=0; + for (;(ci->areanumber != 0xff) && (count--);ci++) + FirstGenInfMsg++; + TotalGenInfMsgs=InfHintList.NumMsgs-FirstGenInfMsg; + + + // + // Take out the special tiles that were not used... + // + + map = mapsegs[0]; + for (y=0;yheight*numlines+1+(TP_MARGIN*2); + + pi.xl=BMAx2+1; + pi.yl=BMAy2+(BMAh2-cheight)/2; + pi.xh=pi.xl+BMAw2-3; + pi.yh=pi.yl+cheight-1; + pi.bgcolor = BORDER_MED_COLOR; + pi.ltcolor = BORDER_HI_COLOR; + fontcolor = BORDER_TEXT_COLOR; + pi.shcolor = pi.dkcolor = BORDER_LO_COLOR; + pi.fontnumber=fontnumber; + TP_InitScript(&pi); + TP_Presenter(&pi); + } +} + +//---------------------------------------------------------------------- +// CacheBMAmsg() - Caches in a Message Number and displays it using +// BMAmsg() +//---------------------------------------------------------------------- +void CacheBMAmsg(unsigned MsgNum) +{ + char far *string, far *pos; + + CA_CacheGrChunk(MsgNum); + string = MK_FP(grsegs[MsgNum],0); + + pos = _fstrstr(string,"^XX"); + *(pos+3) = 0; + + BMAmsg(string); + + UNCACHEGRCHUNK(MsgNum); + +} + +//-------------------------------------------------------------------------- +// BevelBox() +//-------------------------------------------------------------------------- +void BevelBox(short xl, short yl, short w, short h, byte hi, byte med, byte lo) +{ + short xh=xl+w-1,yh=yl+h-1; + byte hc; + + VWB_Bar (xl,yl,w,h,med); // inside + +#if 0 + hc = (hi-lo+1)/2; + hc = ABS(hc); + if (hi > lo) + hc = hi-hc; + else + hc = hi+hc; +#else + hc = med+1; +#endif + + VWB_Hlin (xl,xh,yl,hi); // top + VWB_Hlin (xl,xh,yh,lo); // bottom + VWB_Vlin (yl,yh,xl,hi); // left + VWB_Vlin (yl,yh,xh,lo); // right + VWB_Plot (xl,yh,hc); // lower-left + VWB_Plot (xh,yl,hc); // upper-right +} + +//-------------------------------------------------------------------------- +// ShadowPrintLocationText() +//-------------------------------------------------------------------------- +void ShadowPrintLocationText(sp_type type) +{ + char *DemoMsg="-- DEMO --"; + char *DebugText= "-- DEBUG MODE ENABLED --"; + char str[8],far *s,*ls_text[3]={"-- LOADING --","-- SAVING --","-- CHANGE VIEW SIZE --"}; + unsigned w,h; + +// Used for all fields... +// + py = 5; + fontcolor = 0xaf; + +// Print LOCATION info... +// + switch (type) + { + case sp_normal: + // Print LEVEL info... + // + px = 13; + if (gamestate.mapon>19) + ShPrint(" SECRET ",0,false); + else + { + ShPrint(" AREA: ",0,false); + if (!type) + ShPrint(itoa(gamestate.mapon+1,str,10),0,false); + } + + // Print LIVES info... + // + px = 267; + ShPrint("LIVES: ",0,false); + if (!type) + ShPrint(itoa(gamestate.lives,str,10),0,false); + + // Print location text + // + + if (demoplayback || demorecord) + s = DemoMsg; + else +#if IN_DEVELOPMENT + if (DebugOk) +#else + if (DebugOk || (gamestate.flags & (GS_QUICKRUN|GS_STARTLEVEL|GS_TICS_FOR_SCORE|GS_MUSIC_TEST|GS_SHOW_OVERHEAD))) +#endif + s=DebugText; + else + s=LocationText; + break; + + case sp_changeview: + case sp_loading: + case sp_saving: + s=ls_text[type-sp_loading]; + break; + } + + VW_MeasurePropString(s,&w,&h); + px = 160-w/2; + ShPrint(s,0,false); +} + +//-------------------------------------------------------------------------- +// DrawTopInfo() +//-------------------------------------------------------------------------- +void DrawTopInfo(sp_type type) +{ + char old=fontnumber; + + LatchDrawPic(0,0,TOP_STATUSBARPIC); + fontnumber=2; + ShadowPrintLocationText(type); + fontnumber=old; +} + +/* +=================== += += DrawPlayScreen += +=================== +*/ +void DrawPlayScreen (boolean InitInfoMsg) +{ + int i; + unsigned temp; + + if (loadedgame) + return; + + if (playstate != ex_transported) + VW_FadeOut (); + + temp = bufferofs; + WindowW = 253; + WindowH = 8; + fontnumber = 2; + + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + DrawPlayBorder(); + + JLatchDrawPic(0,200-STATUSLINES,STATUSBARPIC); + LatchDrawPic(0,0,TOP_STATUSBARPIC); + + ShadowPrintLocationText(sp_normal); + } + + bufferofs = temp; + + DrawHealth(); + DrawKeys(); + DrawWeapon(); + DrawScore(); + + InitInfoArea(); + + if (InitInfoMsg) + DISPLAY_MSG("R.E.B.A.\rAGENT: BLAKE STONE\rALL SYSTEMS READY.", MP_max_val, MT_NOTHING); + else + DisplayNoMoMsgs(); + + ForceUpdateStatusBar(); +} + +//--------------------------------------------------------------------------- +// void DrawWarpIn(void) +//--------------------------------------------------------------------------- +void DrawWarpIn(void) +{ + int i; + unsigned temp; + + temp = bufferofs; + InitInfoArea(); + DisplayInfoMsg("\r\r TRANSPORTING...",MP_POWERUP,2*60,MT_GENERAL); + + DrawHealth(); + DrawKeys(); + DrawWeapon(); + DrawScore(); + WindowW = 253; + WindowH = 8; + fontnumber = 2; + for (i=0;i<3;i++) + { + bufferofs = screenloc[i]; + VW_Bar ((320-viewwidth)/2,(200-STATUSLINES-viewheight+TOP_STRIP_HEIGHT)/2,viewwidth,viewheight,BLACK); + JLatchDrawPic(0,200-STATUSLINES,STATUSBARPIC); + LatchDrawPic(0,0,TOP_STATUSBARPIC); + + ShadowPrintLocationText(sp_normal); + UpdateStatusBar(); + } + + bufferofs = temp; + + SD_PlaySound(WARPINSND); + + fizzlein = true; + + ThreeDRefresh(); + +} + +//--------------------------------------------------------------------------- +// Warped +//--------------------------------------------------------------------------- +void Warped(void) +{ + int iangle,i; + + DisplayInfoMsg("\r\r\r TRANSPORTING OUT",MP_POWERUP,7*60,MT_GENERAL); + gamestate.old_weapons[3] = gamestate.weapon; + gamestate.weapon = -1; // take away weapon + + ThreeDRefresh(); + + if (screenfaded) + VW_FadeIn(); + + iangle = (((player->dir+4) % 8)>>1) * 90; + + RotateView(iangle,2); + + gamestate.weapon = gamestate.old_weapons[3]; + gamestate.attackframe = gamestate.attackcount = gamestate.weaponframe = 0; + + bufferofs += screenofs; + + VW_Bar (0,0,viewwidth,viewheight,BLACK); + + IN_ClearKeysDown (); + SD_PlaySound (WARPINSND); + + FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,70,false); + + bufferofs-= screenofs; + + IN_UserInput(100); + SD_WaitSoundDone (); +} + +//========================================================================== + +#if GAME_VERSION == SHAREWARE_VERSION +char demoname[13] = "DEMO?S."; +#else +char demoname[13] = "DEMO?."; +#endif + +#ifdef DEMOS_EXTERN + +/* +================== += += StartDemoRecord += +================== +*/ + +#define MAXDEMOSIZE 16384 + +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 += +================== +*/ + +void FinishDemoRecord (void) +{ + char str[3]; + long length,level; + + demorecord = false; + + length = demoptr - (char far *)demobuffer; + + demoptr = ((char far *)demobuffer)+1; + *(unsigned far *)demoptr = length; + + VW_FadeIn(); + + CenterWindow(24,3); + PrintY+=6; + fontnumber = 4; + 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; + IO_WriteFile (demoname,(void far *)demobuffer,length); + } + } + + VW_FadeOut(); + + MM_FreePtr (&demobuffer); +} + +//========================================================================== + +/* +================== += += RecordDemo += += Fades the screen out, then starts a demo. Exits with the screen faded += +================== +*/ + + +#if 0 // JAM's Modified Version - HELP! - Comment out + +void RecordDemo (void) +{ + CenterWindow(26,3); + PrintY+=6; + fontnumber=4; + + SETFONTCOLOR(0,15); + VW_FadeOut (); + + NewGame (gd_hard,gamestate.episode); + StartDemoRecord (gamestate.mapon); + + DrawPlayScreen (true); + VW_FadeIn (); + + startgame = false; + demorecord = true; + + LoadLevel(gamestate.mapon); + StartMusic(false); + PM_CheckMainMem (); + fizzlein = true; + + PlayLoop (); + + demoplayback = false; + + StopMusic (); + VW_FadeOut (); + ClearMemory (); + + FinishDemoRecord (); +} + +#endif + +void RecordDemo (void) +{ + char str[3]; + int level,esc; + + CenterWindow(26,3); + PrintY+=6; + fontnumber=4; + US_Print(" Demo which level(0-23):"); + VW_UpdateScreen(); + VW_FadeIn (); + esc = !US_LineInput (px,py,str,NULL,true,2,0); + if (esc) + return; + + level = atoi (str); +// level--; + if (level > 23) + return; + + SETFONTCOLOR(0,15); + VW_FadeOut (); + + NewGame (gd_easy,level/MAPS_PER_EPISODE); + gamestate.mapon = level%MAPS_PER_EPISODE; + StartDemoRecord (level); + + DrawPlayScreen (true); + DrawTopInfo(sp_loading); + DisplayPrepingMsg(prep_msg); + LS_current = 1; + LS_total = 20; + + VW_FadeIn (); + + startgame = false; + demorecord = true; + + LoadLevel(gamestate.mapon); + + VW_FadeOut(); + DrawPlayScreen(true); + StartMusic(false); + PM_CheckMainMem (); + fizzlein = true; + + PlayLoop (); + + demoplayback = false; + + StopMusic (); + VW_FadeOut (); + ClearMemory (); + + FinishDemoRecord (); +} + +#endif + +//========================================================================== + +/* +================== += += PlayDemo += += Fades the screen out, then starts a demo. Exits with the screen faded += +================== +*/ + +void PlayDemo (int demonumber) +{ +// static int numloops=0; + int length,off; + +#ifndef DEMOS_EXTERN +// debug: load chunk +#if GAME_VERSION == SHAREWARE_VERSION + int dems[4]={T_DEMO0,T_DEMO1,T_DEMO2,T_DEMO3}; +#else + int dems[6]={T_DEMO0,T_DEMO1,T_DEMO2,T_DEMO3,T_DEMO4,T_DEMO5}; +#endif + + CA_CacheGrChunk(dems[demonumber]); + demoptr = grsegs[dems[demonumber]]; +#else + demoname[4] = '0'+demonumber; + IO_LoadFile (demoname,&demobuffer); + MM_SetLock (&demobuffer,true); + demoptr = (char far *)demobuffer; +#endif + + NewGame (1,0); + gamestate.mapon = *demoptr++; + gamestate.difficulty = gd_easy; + length = *((unsigned far *)demoptr)++; + demoptr++; + lastdemoptr = demoptr-4+length; + + VW_FadeOut (); + + SETFONTCOLOR(0,15); + DrawPlayScreen (true); + DrawTopInfo(sp_loading); + DisplayPrepingMsg(prep_msg); + LS_current = 1; + LS_total = 20; + VW_FadeIn (); + + startgame = false; + demoplayback = true; + + StartMusic(false); + LoadLevel(gamestate.mapon); + + VW_FadeOut(); + DrawPlayScreen(true); + PM_CheckMainMem (); + SetPlaneViewSize(); + fizzlein = true; + +#ifndef DEMOS_EXTERN + off = FP_OFF(demoptr); + demoptr = grsegs[dems[demonumber]]; + demoptr += off; +#endif + + PlayLoop (); + + if (gamestate.health<=0) + Died(); + +#ifndef DEMOS_EXTERN + UNCACHEGRCHUNK(dems[demonumber]); +#else + MM_FreePtr (&demobuffer); +#endif + + demoplayback = false; + LS_current = LS_total = -1; + + StopMusic (); + VW_FadeOut (); + ClearMemory (); + + playstate = ex_title; +} + +//========================================================================== + +/* +================== += += 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); + + iangle = CalcAngle(player,killerobj); + + RotateView(iangle,DEATHROTATE); + +// +// fade to red +// + FinishPaletteShifts (); + + bufferofs += screenofs; + VW_Bar (0,0,viewwidth,viewheight,0x17); + IN_ClearKeysDown (); + FizzleFade(bufferofs,displayofs+screenofs,viewwidth,viewheight,70,false); + bufferofs -= screenofs; + if (demoplayback) + return; + IN_UserInput(100); + + SD_WaitSoundDone(); + StopMusic(); + + gamestate.lives--; + + if (gamestate.lives > -1) + { + gamestate.health = 100; + gamestate.weapons = 1< ... + // + + VW_FadeIn(); + TP_Presenter(&pi); + VW_FadeOut(); + +#ifdef ID_CACHE_LOSE + TP_FreeScript(&pi,LOSETEXT); +#else + TP_FreeScript(&pi,0); +#endif + + screenfaded = true; + + IN_ClearKeysDown(); +} + +//-------------------------------------------------------------------------- +// RotateView() +// +// PARAMETERS: +// DestAngle - Destination angle to rotate player->angle to. +// RotSpeed - Rotation Speed +//-------------------------------------------------------------------------- +void RotateView(int DestAngle,unsigned char RotSpeed) +{ + int curangle,clockwise,counter,change; + objtype *obj; + boolean old_godmode=godmode; + + if (player->angle > DestAngle) + { + counter = player->angle - DestAngle; + clockwise = ANGLES-player->angle + DestAngle; + } + else + { + clockwise = DestAngle - player->angle; + counter = player->angle + ANGLES-DestAngle; + } + + godmode = true; + curangle = player->angle; + + controly = 0; + if (clockwiseDestAngle) + curangle -= ANGLES; + controlx = -1; + do + { + change = tics*RotSpeed; + if (curangle + change > DestAngle) + change = DestAngle-curangle; + + curangle += change; + player->angle += change; + if (player->angle >= ANGLES) + player->angle -= ANGLES; + + for (obj = player->next;obj;obj = obj->next) + DoActor (obj); + ThreeDRefresh (); + CalcTics (); + } while (curangle != DestAngle); + } + else + { + // + // rotate counterclockwise + // + if (curangleangle += change; + if (player->angle < 0) + player->angle += ANGLES; + + for (obj = player->next;obj;obj = obj->next) + DoActor (obj); + ThreeDRefresh (); + CalcTics (); + } while (curangle != DestAngle); + } + + controlx = 0; + player->dir = ((player->angle + 22) % 360)/45; + godmode = old_godmode; + +} + +//========================================================================== + + +/* +=================== += += GameLoop += +=================== +*/ + +void GameLoop (void) +{ + extern boolean sqActive; + + char mod; + + int i,xl,yl,xh,yh; + char Score[13]; + boolean died; +#ifdef MYPROFILE + clock_t start,end; +#endif + +restartgame: + + ClearMemory(); + SETFONTCOLOR(0,15); + DrawPlayScreen (true); + + died = false; +restart: + do + { + extern int pickquick; + + ingame = true; + + if (died && pickquick) + { + char string[]=" Auto Quick Load? "; + + WindowX=WindowY=0; + WindowW=320; + WindowH=152; + + if (Confirm(string)) + { + playstate=0; + DrawPlayBorder(); + VW_UpdateScreen(); + US_ControlPanel(sc_F9); + } + + DrawPlayBorder(); + VW_UpdateScreen(); + } + + if (!sqActive) + StartMusic(false); + + if (!(loadedgame || LevelInPlaytemp(gamestate.mapon))) + { + gamestate.tic_score = gamestate.score = gamestate.oldscore; + memcpy(gamestate.numkeys,gamestate.old_numkeys,sizeof(gamestate.numkeys)); + memcpy(gamestate.barrier_table,gamestate.old_barrier_table,sizeof(gamestate.barrier_table)); + gamestate.rpower = gamestate.old_rpower; + gamestate.tokens = gamestate.old_tokens; + gamestate.weapons = gamestate.old_weapons[0]; + gamestate.weapon = gamestate.old_weapons[1]; + gamestate.chosenweapon = gamestate.old_weapons[2]; + gamestate.ammo = gamestate.old_ammo; + gamestate.plasma_detonators = gamestate.old_plasma_detonators; + gamestate.boss_key_dropped=gamestate.old_boss_key_dropped; + _fmemcpy(&gamestuff.level[0],gamestuff.old_levelinfo,sizeof(gamestuff.old_levelinfo)); + DrawKeys(); + DrawScore(); + } + +#ifdef MYPROFILE +start = clock(); +while (start == clock()); +start++; +#endif + + startgame = false; + if (!loadedgame) + { +// ClearMemory(); + if (LS_current==-1) + { +// ClearMemory(); + DrawTopInfo(sp_loading); + DisplayPrepingMsg(prep_msg); + LS_current = 1; + LS_total = 20; + } +// ClearMemory(); + LoadLevel(gamestate.mapon); + } + + LS_current = LS_total = -1; + + PM_CheckMainMem (); + SetPlaneViewSize(); + if (loadedgame) + loadedgame=false; + + if (died) + { + WindowY=188; + PreloadUpdate(1,1); + died = false; + DrawPlayScreen(true); + } + else + { + PreloadGraphics(); + if (playstate == ex_transported) + DrawWarpIn(); + else + DrawPlayScreen(false); + } + + if (!sqActive) + StartMusic(false); + + PlayLoop (); + LS_current = LS_total = -1; + died = false; + + StopMusic (); + ingame = false; + +#ifdef MYPROFILE +end = clock(); +strcpy (str,"300 frames in 1/18ths:"); // defined in 3d_main.c +itoa(end-start,str2,10); // defined in 3d_main.c +strcat (str,str2); // defined in 3d_main.c + Quit (str); // defined in 3d_main.c +#endif + +#ifdef DEMOS_EXTERN + if (demorecord && playstate != ex_warped) + FinishDemoRecord (); +#endif + + if (startgame || loadedgame) + goto restartgame; + + switch (playstate) + { + + case ex_transported: // Same as ex_completed + Warped(); + + case ex_completed: + case ex_secretlevel: + case ex_warped: +#if 0 + gamestate.keys = 0; + DrawKeys (); + VW_FadeOut (); +#endif +// StopMusic(); // JTR + ClearMemory (); + gamestate.mapon++; + ClearNClose(); + DrawTopInfo(sp_loading); + DisplayPrepingMsg(prep_msg); + WindowY = 181; + LS_current=1; + LS_total=38; + StartMusic(false); + SaveLevel(gamestate.lastmapon); + + gamestate.old_rpower = gamestate.rpower; + gamestate.oldscore = gamestate.score; + memcpy(gamestate.old_numkeys,gamestate.numkeys,sizeof(gamestate.old_numkeys)); + gamestate.old_tokens = gamestate.tokens; + memcpy(gamestate.old_barrier_table,gamestate.barrier_table,sizeof(gamestate.old_barrier_table)); + gamestate.old_weapons[0] = gamestate.weapons; + gamestate.old_weapons[1] = gamestate.weapon; + gamestate.old_weapons[2] = gamestate.chosenweapon; + gamestate.old_ammo = gamestate.ammo; + gamestate.old_boss_key_dropped = gamestate.boss_key_dropped; + _fmemcpy(gamestuff.old_levelinfo,&gamestuff.level[0],sizeof(gamestuff.old_levelinfo)); + +#if 0 + if (gamestate.mapon == 9) + gamestate.mapon = ElevatorBackTo[gamestate.episode]; // back from secret + else if (playstate == ex_secretlevel) + gamestate.mapon = 9; + else +#endif + break; + + case ex_died: + if (InstantQuit) + InstantQuit = false; + else + { + Died (); + + died = true; // don't "get psyched!" + + if (gamestate.lives > -1) + { + ClearMemory(); + break; // more lives left + } + + LoseScreen(); + } + + + case ex_victorious: + MainMenu[MM_SAVE_MISSION].active=AT_DISABLED; +#pragma warn -sus + MainMenu[MM_VIEW_SCORES].routine=&CP_ViewScores; + _fstrcpy(MainMenu[MM_VIEW_SCORES].string,"HIGH SCORES"); +#pragma warn +sus + + if (playstate==ex_victorious) + { + ThreeDRefresh(); + ThreeDRefresh(); + } + + ClearMemory (); + + if (playstate==ex_victorious) + { + char loop; + + fontnumber=1; + CA_CacheGrChunk(STARTFONT+1); + memset (update,0,sizeof(update)); + CacheBMAmsg(YOUWIN_TEXT); + for (loop=0; loop<2; loop++) + { + VW_ScreenToScreen(displayofs,bufferofs,320,200); + NextBuffer(); + } + UNCACHEGRCHUNK(STARTFONT+1); + SD_PlaySound(BONUS1SND); + SD_WaitSoundDone(); + IN_UserInput(5*60); + PM_CheckMainMem(); + ClearMemory(); + } + + VW_FadeOut (); +// StopMusic(); // JTR + + sprintf(Score,"%ld",gamestate.score); + piStringTable[0]=Score; + + if (playstate==ex_victorious) + { + CA_CacheGrChunk(ENDINGPALETTE); +// VL_SetPalette (0,256,grsegs[ENDINGPALETTE]); + + DoMovie(mv_final,grsegs[ENDINGPALETTE]); + + UNCACHEGRCHUNK(ENDINGPALETTE); + NewViewSize(viewsize); // Recreates & Allocs the ScaleDirectory + Breifing(BT_WIN,gamestate.episode); + } + + CheckHighScore (gamestate.score,gamestate.mapon+1); + + return; + + default: + ClearMemory (); + break; + } + + } while (1); + +} + diff --git a/3D_INTER.C b/3D_INTER.C new file mode 100644 index 0000000..0e3bf8f --- /dev/null +++ b/3D_INTER.C @@ -0,0 +1,357 @@ +// 3D_INTER.C + +#include "3D_DEF.H" +#pragma hdrstop + + +//========================================================================== +// +// LOCAL CONSTANTS +// +//========================================================================== + + +//========================================================================== +// +// LOCAL VARABLES +// +//========================================================================== + +#ifndef ID_CACHE_BRIEFS +char BreifingText[13] = {"BRIEF_Wx.TXT"}; +#endif + +//========================================================================== + +/* +================== += += CLearSplitVWB += +================== +*/ + +void ClearSplitVWB (void) +{ + memset (update,0,sizeof(update)); + WindowX = 0; + WindowY = 0; + WindowW = 320; + WindowH = 152; +} + + +//========================================================================== + + + + +/* +================== += += Breifing += +================== +*/ + +boolean Breifing(breifing_type BreifingType,unsigned episode) +{ +#ifndef ID_CACHE_BRIEFS + char chars[3] = {'L','W','I'}; + + BreifingText[6] = chars[BreifingType]; + BreifingText[7] = '1'+episode; + + HelpPresenter(BreifingText,true,0,false); +#else + HelpPresenter(NULL,true,BRIEF_W1+(episode*2)+BreifingType-1,false); +#endif + + return(EscPressed); +} + + +//========================================================================== + + +/* +================= += += PreloadGraphics += += Fill the cache up += +================= +*/ + +void ShPrint(char far *text, char shadow_color, boolean single_char) +{ + unsigned old_color=fontcolor,old_x=px,old_y=py; + char far *str,buf[2]={0,0}; + + if (single_char) + { + str = buf; + buf[0]=*text; + } + else + str = text; + + fontcolor = shadow_color; + py++; + px++; + USL_DrawString(str); // JTR - This marks blocks! + + fontcolor = old_color; + py = old_y; + px = old_x; + USL_DrawString(str); // JTR - This marks blocks! +} + +void PreloadUpdate(unsigned current, unsigned total) +{ + unsigned w=WindowW-10; + + if (current > total) + current=total; + + w = ((long)w * current) / total; + if (w) + VWB_Bar(WindowX,WindowY,w-1,1,BORDER_TEXT_COLOR); + + VW_UpdateScreen(); +} + +char far prep_msg[]="^ST1^CEGet Ready, Blake!\r^XX"; + +void DisplayPrepingMsg(char far *text) +{ +#if GAME_VERSION != SHAREWARE_VERSION + +// Bomb out if FILE_ID.DIZ is bad!! +// + if (((gamestate.mapon != 1) || (gamestate.episode != 0)) && + (gamestate.flags & GS_BAD_DIZ_FILE)) + Quit(NULL); + +#endif + +// Cache-in font +// + fontnumber=1; + CA_CacheGrChunk(STARTFONT+1); + BMAmsg(text); + UNCACHEGRCHUNK(STARTFONT+1); + +// Set thermometer boundaries +// + WindowX = 36; + WindowY = 188; + WindowW = 260; + WindowH = 32; + + +// Init MAP and GFX thermometer areas +// + VWB_Bar(WindowX,WindowY-7,WindowW-10,2,BORDER_LO_COLOR); + VWB_Bar(WindowX,WindowY-7,WindowW-11,1,BORDER_TEXT_COLOR-15); + VWB_Bar(WindowX,WindowY,WindowW-10,2,BORDER_LO_COLOR); + VWB_Bar(WindowX,WindowY,WindowW-11,1,BORDER_TEXT_COLOR-15); + +// Update screen and fade in +// + VW_UpdateScreen(); + if (screenfaded) + VW_FadeIn(); +} + +void PreloadGraphics(void) +{ + WindowY=188; + + if (!(gamestate.flags & GS_QUICKRUN)) + VW_FadeIn (); + + PM_Preload(PreloadUpdate); + IN_UserInput(70); + + if (playstate != ex_transported) + VW_FadeOut (); + + DrawPlayBorder (); + VW_UpdateScreen (); +} + +//========================================================================== + +/* +================== += += DrawHighScores += +================== +*/ + +#define SCORE_Y_SPACING 7 // + +void DrawHighScores(void) +{ + char buffer[16],*str; + word i, + w,h; + HighScore *s; + + ClearMScreen(); + CA_CacheScreen (BACKGROUND_SCREENPIC); + DrawMenuTitle("HIGH SCORES"); + + if (playstate != ex_title) + DrawInstructions(IT_HIGHSCORES); + + fontnumber=2; + SETFONTCOLOR(ENABLED_TEXT_COLOR,TERM_BACK_COLOR); + + ShadowPrint("NAME",86,60); +// ShadowPrint("MISSION",150,60); + ShadowPrint("SCORE",175,60); + ShadowPrint("MISSION",247,53); + ShadowPrint("RATIO",254,60); + + for (i = 0,s = Scores;i < MaxScores;i++,s++) + { + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR-1,TERM_BACK_COLOR); + // + // name + // + if (*s->name) + ShadowPrint(s->name,45,68 + (SCORE_Y_SPACING * i)); + +#if 0 + // + // mission + // + + ltoa(s->episode+1,buffer,10); + ShadowPrint(buffer,165,68 + (SCORE_Y_SPACING * i)); +#endif + + // + // score + // + + if (s->score > 9999999) + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR+1,TERM_BACK_COLOR); + + ltoa(s->score,buffer,10); + USL_MeasureString(buffer,&w,&h); + ShadowPrint(buffer,205 - w,68 + (SCORE_Y_SPACING * i)); // 235 + + // + // mission ratio + // + ltoa(s->ratio,buffer,10); + USL_MeasureString(buffer,&w,&h); + ShadowPrint(buffer,272-w,68 + (SCORE_Y_SPACING * i)); + } + + VW_UpdateScreen (); +} + +//=========================================================================== + + +/* +======================= += += CheckHighScore += +======================= +*/ + +void CheckHighScore (long score,word other) +{ + word i,j; + int n; + HighScore myscore; + US_CursorStruct TermCursor = {'@',0,HIGHLIGHT_TEXT_COLOR,2}; + + + // Check for cheaters + + if (DebugOk) + { + SD_PlaySound(NOWAYSND); + return; + } + + strcpy(myscore.name,""); + myscore.score = score; + myscore.episode = gamestate.episode; + myscore.completed = other; + myscore.ratio = ShowStats(0,0,ss_justcalc,&gamestuff.level[gamestate.mapon].stats); + + 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; + } + } + + StartCPMusic (ROSTER_MUS); + DrawHighScores (); + + VW_FadeIn (); + + if (n != -1) + { + // + // got a high score + // + + DrawInstructions(IT_ENTER_HIGHSCORE); + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,TERM_BACK_COLOR); + PrintY = 68+(SCORE_Y_SPACING * n); + PrintX = 45; + use_custom_cursor = true; + allcaps = true; + US_CustomCursor = TermCursor; + US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100); + } + else + { + IN_ClearKeysDown (); + IN_UserInput(500); + } + + StopMusic(); + use_custom_cursor = false; +} + +//=========================================================================== + +//-------------------------------------------------------------------------- +// Random() +//-------------------------------------------------------------------------- +unsigned Random(unsigned Max) +{ + unsigned returnval; + + if (Max) + { + if (Max > 255) + returnval = (US_RndT()<<8) + US_RndT(); + else + returnval = US_RndT(); + + return(returnval % Max); + } + else + return(0); +} + diff --git a/3D_MAIN.C b/3D_MAIN.C new file mode 100644 index 0000000..88209ca --- /dev/null +++ b/3D_MAIN.C @@ -0,0 +1,1905 @@ +// 3D_MAIN.C + +#include "3D_DEF.H" +#pragma hdrstop +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jm_io.h" +#include "jm_cio.h" +#include "jm_lzh.h" +#include "jm_error.h" + +/* +============================================================================= + + BLAKE STONE + (C)opyright 1993, JAM Productions, Inc. + + 3D engine licensed by ID Software, Inc. + Shareware distribution by Apogee Software, Inc. + +============================================================================= +*/ + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define SKIP_TITLE_AND_CREDITS (false) + + +#define FOCALLENGTH (0x5700l) // in global coordinates +#define VIEWGLOBAL 0x10000 // globals visable flush to wall + +#define VIEWWIDTH 256 // size of view window +#define VIEWHEIGHT 144 + + +#define MAX_DEST_PATH_LEN 30 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + +extern int pickquick; + + +void DrawCreditsPage(void); +void unfreed_main(void); +void ShowPromo(void); + +char far * far MainStrs[] = { + "q","nowait","l","e", + "version","system", + "dval","tics","mem","powerball","music","d", + "radar",BETA_CODE, + nil +}; + +short starting_episode,starting_level,starting_difficulty; + +char destPath[MAX_DEST_PATH_LEN+1]; +char tempPath[MAX_DEST_PATH_LEN+15]; + +#if BETA_TEST +char far bc_buffer[]=BETA_CODE; +#endif + +void InitPlaytemp(void); + + +char QuitMsg[] = {"Unit: $%02x Error: $%02x"}; + +#ifdef CEILING_FLOOR_COLORS +unsigned TopColor,BottomColor; +#endif + +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; + + +boolean startgame,loadedgame; +int mouseadjustment; + +char configname[13]="CONFIG."; + +short view_xl,view_xh,view_yl,view_yh; + +#if IN_DEVELOPMENT +unsigned democount=0,jim=0; +#endif + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + +#if 0 + +unsigned mspeed; + +void CalcSpeedRating() +{ + short loop; + + for (loop=0; loop<10; loop++) + { + ThreeDRefresh(); + mspeed += tics; + } +} + +#endif + +/* +==================== += += WriteConfig += +==================== +*/ + +void WriteConfig(void) +{ + int file; + + MakeDestPath(configname); + file = open(tempPath,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)); + + write(file,&gamestate.flags,sizeof(gamestate.flags)); + + close(file); + } +} + + +//=========================================================================== + +/* +===================== += += NewGame += += Set up new game to start from the beginning += +===================== +*/ + +boolean ShowQuickMsg; +void NewGame (int difficulty,int episode) +{ + unsigned oldf=gamestate.flags,loop; + + InitPlaytemp(); + playstate = ex_stillplaying; + + ShowQuickMsg=true; + _fmemset (&gamestuff,0,sizeof(gamestuff)); + memset (&gamestate,0,sizeof(gamestate)); + + memset(&gamestate.barrier_table,0xff,sizeof(gamestate.barrier_table)); + memset(&gamestate.old_barrier_table,0xff,sizeof(gamestate.old_barrier_table)); + gamestate.flags = oldf & ~(GS_KILL_INF_WARN); +// LoadAccessCodes(); + + gamestate.difficulty = difficulty; + + +// +// The following are set to 0 by the memset() to gamestate - Good catch! :JR +// +// gamestate.rzoom +// gamestate.rpower +// gamestate.old_door_bombs +// gamestate.plasma_detonators +// + + gamestate.weapons = 1< LZH_WORK_BUFFER_SIZE) + MAIN_ERROR(WRITEINFO_BIGGER_BUF); + IO_FarWrite (file,(char far *)&csize,sizeof(csize)); + IO_FarWrite (file,lzh_work_buffer,csize); + checksum=DoChecksum(lzh_work_buffer,csize,checksum); + csize += sizeof(csize); + } + else + { + IO_FarWrite (file,src,size); + checksum=DoChecksum(src,size,checksum); + csize=size; + } + + return(csize); +} + + + +//-------------------------------------------------------------------------- +// LoadLevel() +//-------------------------------------------------------------------------- +boolean LoadLevel(short levelnum) +{ + extern boolean ShowQuickMsg; + extern boolean ForceLoadDefault; + extern unsigned destoff; + + boolean oldloaded=loadedgame; + long oldchecksum; + objtype *ob; + statobj_t *statptr; + int handle,picnum; + memptr temp; + unsigned count; + char far *ptr; + char chunk[5]="LVxx"; + +extern int nsd_table[]; +extern int sm_table[]; + +char mod; + + WindowY=181; + gamestuff.level[levelnum].locked=false; + + mod = levelnum % 6; + normalshade_div = nsd_table[mod]; + shade_max = sm_table[mod]; + normalshade=(3*(maxscale>>2))/normalshade_div; + +// Open PLAYTEMP file +// + MakeDestPath(PLAYTEMP_FILE); + handle=open(tempPath,O_RDONLY|O_BINARY); + +// If level exists in PLAYTEMP file, use it; otherwise, load it from scratch! +// + sprintf(&chunk[2],"%02x",levelnum); + if ((handle==-1) || (!FindChunk(handle,chunk)) || ForceLoadDefault) + { + close(handle); + + PreloadUpdate(LS_current+((LS_total-LS_current)>>1),LS_total); + SetupGameLevel(); + gamestate.flags |= GS_VIRGIN_LEVEL; + gamestate.turn_around=0; + + PreloadUpdate(1,1); + ForceLoadDefault=false; + goto overlay; + } + + gamestate.flags &= ~GS_VIRGIN_LEVEL; + +// Setup for LZH decompression +// + LZH_Startup(); + MM_GetPtr(&lzh_work_buffer,LZH_WORK_BUFFER_SIZE); + +// Read all sorts of stuff... +// + checksum = 0; + + loadedgame=true; + SetupGameLevel(); + loadedgame=oldloaded; + + ReadIt(true, tilemap, sizeof(tilemap)); + ReadIt(true, actorat, sizeof(actorat)); + ReadIt(true, areaconnect, sizeof(areaconnect)); + ReadIt(true, areabyplayer, sizeof(areabyplayer)); + +// Restore 'save game' actors +// + ReadIt(false, &count, sizeof(count)); + MM_GetPtr(&temp,count*sizeof(*ob)); + ReadIt(true, temp, count*sizeof(*ob)); + ptr=temp; + + InitActorList (); // start with "player" actor + _fmemcpy(new,ptr,sizeof(*ob)-4); // don't copy over links! + ptr += sizeof(*ob); // + + while (--count) + { + GetNewActor(); + _fmemcpy(new,ptr,sizeof(*ob)-4); // don't copy over links! + actorat[new->tilex][new->tiley]=new; +#if LOOK_FOR_DEAD_GUYS + if (new->flags & FL_DEADGUY) + DeadGuys[NumDeadGuys++]=new; +#endif + ptr += sizeof(*ob); + } + MM_FreePtr(&temp); + + + // + // Re-Establish links to barrier switches + // + +#pragma warn -pia + + ob = objlist; + do + { + switch (ob->obclass) + { + case arc_barrierobj: + case post_barrierobj: + case vspike_barrierobj: + case vpost_barrierobj: + ob->temp2 = ScanBarrierTable(ob->tilex,ob->tiley); + break; + } + } while (ob = ob->next); + +#pragma warn +pia + + ConnectBarriers(); + +// Read all sorts of stuff... +// + ReadIt(false, &laststatobj, sizeof(laststatobj)); + ReadIt(true, statobjlist, sizeof(statobjlist)); + ReadIt(true, doorposition, sizeof(doorposition)); + ReadIt(true, doorobjlist, sizeof(doorobjlist)); + ReadIt(false, &pwallstate, sizeof(pwallstate)); + ReadIt(false, &pwallx, sizeof(pwallx)); + ReadIt(false, &pwally, sizeof(pwally)); + ReadIt(false, &pwalldir, sizeof(pwalldir)); + ReadIt(false, &pwallpos, sizeof(pwallpos)); + ReadIt(false, &pwalldist, sizeof(pwalldist)); + ReadIt(true, TravelTable, sizeof(TravelTable)); + ReadIt(true, &ConHintList, sizeof(ConHintList)); + ReadIt(true, eaList, sizeof(eaWallInfo)*MAXEAWALLS); + ReadIt(true, &GoldsternInfo, sizeof(GoldsternInfo)); + ReadIt(true, &GoldieList,sizeof(GoldieList)); + ReadIt(false, gamestate.barrier_table,sizeof(gamestate.barrier_table)); + ReadIt(false, &gamestate.plasma_detonators,sizeof(gamestate.plasma_detonators)); + +// Read and evaluate checksum +// + PreloadUpdate(LS_current++,LS_total); + IO_FarRead (handle,(void far *)&oldchecksum,sizeof(oldchecksum)); + + if (oldchecksum != checksum) + { + int old_wx=WindowX,old_wy=WindowY,old_ww=WindowW,old_wh=WindowH, + old_px=px,old_py=py; + + WindowX=0; WindowY=16; WindowW=320; WindowH=168; + CacheMessage(BADINFO_TEXT); + WindowX=old_wx; WindowY=old_wy; WindowW=old_ww; WindowH=old_wh; + px=old_px; py=old_py; + + IN_ClearKeysDown(); + IN_Ack(); + + gamestate.score = 0; + gamestate.nextextra = EXTRAPOINTS; + gamestate.lives = 1; + + gamestate.weapon = gamestate.chosenweapon = wp_autocharge; + gamestate.weapons = 1<next,count++,ptr+=sizeof(*ob)) + _fmemcpy(ptr,ob,sizeof(*ob)); + WriteIt(false, &count, sizeof(count)); + WriteIt(true, temp, count*sizeof(*ob)); + MM_FreePtr(&temp); + +// Write all sorts of info... +// + WriteIt(false, &laststatobj, sizeof(laststatobj)); + WriteIt(true, statobjlist, sizeof(statobjlist)); + WriteIt(true, doorposition, sizeof(doorposition)); + WriteIt(true, doorobjlist, sizeof(doorobjlist)); + WriteIt(false, &pwallstate, sizeof(pwallstate)); + WriteIt(false, &pwallx, sizeof(pwallx)); + WriteIt(false, &pwally, sizeof(pwally)); + WriteIt(false, &pwalldir, sizeof(pwalldir)); + WriteIt(false, &pwallpos, sizeof(pwallpos)); + WriteIt(false, &pwalldist, sizeof(pwalldist)); + WriteIt(true, TravelTable, sizeof(TravelTable)); + WriteIt(true, &ConHintList, sizeof(ConHintList)); + WriteIt(true, eaList, sizeof(eaWallInfo)*MAXEAWALLS); + WriteIt(true, &GoldsternInfo, sizeof(GoldsternInfo)); + WriteIt(true, &GoldieList,sizeof(GoldieList)); + WriteIt(false, gamestate.barrier_table,sizeof(gamestate.barrier_table)); + WriteIt(false, &gamestate.plasma_detonators,sizeof(gamestate.plasma_detonators)); + +// Write checksum and determine size of file +// + WriteIt(false, &checksum, sizeof(checksum)); + offset=tell(handle); + +// Write chunk size, set file size, and close file +// + lseek(handle,-(cksize+4),SEEK_CUR); + write(handle,&cksize,4); + + chsize(handle,offset); + close(handle); + rt_value=true; + +// Clean-up LZH compression +// +exit_func:; + MM_FreePtr(&lzh_work_buffer); + LZH_Shutdown(); + NewViewSize(viewsize); + gamestate.flags = gflags; + + return(rt_value); +} + +#pragma warn -pia + +//-------------------------------------------------------------------------- +// DeleteChunk() +//-------------------------------------------------------------------------- +long DeleteChunk(int handle, char *chunk) +{ + long filesize,cksize,offset,bmove; + int dhandle; + + lseek(handle,0,SEEK_SET); + filesize=lseek(handle,0,SEEK_END); + lseek(handle,0,SEEK_SET); + + if (cksize=FindChunk(handle,chunk)) + { + offset=lseek(handle,0,SEEK_CUR)-8; // move back to CKID/SIZE + bmove=filesize-(offset+8+cksize); // figure bytes to move + + if (bmove) // any data to move? + { + // Move data: FROM --> the start of NEXT chunk through the end of file. + // TO --> the start of THIS chunk. + // + // (ie: erase THIS chunk and re-write it at the end of the file!) + // + lseek(handle,cksize,SEEK_CUR); // seek source to NEXT chunk + + MakeDestPath(PLAYTEMP_FILE); + if ((dhandle=open(tempPath,O_CREAT|O_RDWR|O_BINARY,S_IREAD|S_IWRITE))==-1) + MAIN_ERROR(SAVELEVEL_DISKERR); + + lseek(dhandle,offset,SEEK_SET); // seek dest to THIS chunk + IO_CopyHandle(handle,dhandle,bmove); // copy "bmove" bytes + + close(dhandle); + + lseek(handle,offset+bmove,SEEK_SET); // go to end of data moved + } + else + lseek(handle,offset,SEEK_SET); + } + + return(cksize); +} + +#pragma warn +pia + + + +char far SavegameInfoText[]="\n\r" + "\n\r" + "-------------------------------------\n\r" + " Blake Stone: Aliens Of Gold\n\r" + "Copyright 1993, JAM Productions, Inc.\n\r" + "\n\r" + "SAVEGAME file is from version: "__VERSION__"\n\r" + " Compile Date :"__DATE__" : "__TIME__"\n\r" + "-------------------------------------\n\r" + "\x1a"; + + +//-------------------------------------------------------------------------- +// LoadTheGame() +//-------------------------------------------------------------------------- +boolean LoadTheGame(int handle) +{ + extern int lastmenumusic; + + int shandle; + long cksize; + memptr temp=NULL; + boolean rt_value=false; + char InfoSpace[400]; + memptr tempspace; + +// Setup LZH decompression +// + LZH_Startup(); + MM_GetPtr(&lzh_work_buffer,LZH_WORK_BUFFER_SIZE); + + +// Read in VERSion chunk +// + if (!FindChunk(handle,"VERS")) + goto cleanup; + + cksize = sizeof(SavegameInfoText); + read(handle, InfoSpace, cksize); + if (_fmemcmp(InfoSpace, SavegameInfoText, cksize)) + { + // Old Version of game + + int old_wx=WindowX,old_wy=WindowY,old_ww=WindowW,old_wh=WindowH, + old_px=px,old_py=py; + + WindowX=0; WindowY=16; WindowW=320; WindowH=168; + CacheMessage(BADSAVEGAME_TEXT); + SD_PlaySound (NOWAYSND); + WindowX=old_wx; WindowY=old_wy; WindowW=old_ww; WindowH=old_wh; + px=old_px; py=old_py; + + IN_ClearKeysDown(); + IN_Ack(); + + VW_FadeOut(); + screenfaded = true; + + goto cleanup; + } + +// Read in HEAD chunk +// + if (!FindChunk(handle,"HEAD")) + goto cleanup; + + ReadIt(true, &gamestate, sizeof(gamestate)); + ReadIt(true, &gamestuff, sizeof(gamestuff)); + +// Reinitialize page manager +// +#if DUAL_SWAP_FILES + PM_Shutdown(); + PM_Startup (); + PM_UnlockMainMem(); +#endif + + +// Start music for the starting level in this loaded game. +// + + FreeMusic(); + StartMusic(false); + +// Copy all remaining chunks to PLAYTEMP file +// + MakeDestPath(PLAYTEMP_FILE); + if ((shandle=open(tempPath,O_CREAT|O_RDWR|O_TRUNC|O_BINARY,S_IREAD|S_IWRITE))==-1) + goto cleanup; + +#pragma warn -pia + while (cksize=NextChunk(handle)) + { + cksize += 8; // include chunk ID and LENGTH + lseek(handle,-8,SEEK_CUR); // seek to start of chunk + MM_GetPtr(&temp,cksize); // alloc temp buffer + IO_FarRead(handle,temp,cksize); // read chunk from SAVEGAME file + IO_FarWrite(shandle,temp,cksize); // write chunk to PLAYTEMP file + MM_FreePtr(&temp); // free temp buffer + } +#pragma warn +pia + + close(shandle); + rt_value=true; + +// Clean-up LZH decompression +// +cleanup:; + MM_FreePtr(&lzh_work_buffer); + LZH_Shutdown(); + NewViewSize(viewsize); + +// Load current level +// + if (rt_value) + { + LoadLevel(0xff); + ShowQuickMsg=false; + } + + return(rt_value); +} + + +//-------------------------------------------------------------------------- +// SaveTheGame() +//-------------------------------------------------------------------------- +boolean SaveTheGame(int handle, char far *description) +{ + struct ffblk finfo; + unsigned long cksize,offset; + int shandle; + memptr temp; + char nbuff[GAME_DESCRIPTION_LEN+1]; + boolean rt_value=false,exists; + +// +// Save PLAYTEMP becuase we'll want to restore it to the way it was +// before the save. +// +// IO_CopyFile(PLAYTEMP_FILE,OLD_PLAYTEMP_FILE); +// + +// Save current level -- saves it into PLAYTEMP. +// + SaveLevel(0xff); + +// Setup LZH compression +// + LZH_Startup(); + MM_GetPtr(&lzh_work_buffer,LZH_WORK_BUFFER_SIZE); + +// Write VERSion chunk +// + cksize=sizeof(SavegameInfoText); + write(handle,"VERS",4); + write(handle,&cksize,4); + IO_FarWrite(handle,SavegameInfoText,cksize); + +// Write DESC chunk +// + _fmemcpy(nbuff,description,sizeof(nbuff)); + cksize=strlen(nbuff)+1; + write(handle,"DESC",4); + write(handle,&cksize,4); + write(handle,nbuff,cksize); + +// Write HEAD chunk +// + cksize=0; + write(handle,"HEAD",4); + lseek(handle,4,SEEK_CUR); // leave four bytes for chunk size + + WriteIt(true, &gamestate, sizeof(gamestate)); + WriteIt(true, &gamestuff, sizeof(gamestuff)); + + lseek(handle,-(cksize+4),SEEK_CUR); + write(handle,&cksize,4); + lseek(handle,cksize,SEEK_CUR); + +// Append PLAYTEMP file to savegame file +// + MakeDestPath(PLAYTEMP_FILE); + if (findfirst(tempPath,&finfo,0)) + goto cleanup; + + if ((shandle=open(tempPath,O_RDONLY|O_BINARY))==-1) + goto cleanup; + + IO_CopyHandle(shandle,handle,-1); + + close(shandle); + rt_value=true; + +// Clean-up LZH compression +// +cleanup:; + MM_FreePtr(&lzh_work_buffer); + LZH_Shutdown(); + NewViewSize(viewsize); + +// +// Return PLAYTEMP to original state! +// +// remove(PLAYTEMP_FILE); +// rename(OLD_PLAYTEMP_FILE,PLAYTEMP_FILE); +// + + return(rt_value); +} + +//-------------------------------------------------------------------------- +// LevelInPlaytemp() +//-------------------------------------------------------------------------- +boolean LevelInPlaytemp(char levelnum) +{ + int handle; + char chunk[5]="LVxx"; + boolean rt_value; + +// Open PLAYTEMP file +// + MakeDestPath(PLAYTEMP_FILE); + handle=open(tempPath,O_RDONLY|O_BINARY); + +// See if level exists in PLAYTEMP file... +// + sprintf(&chunk[2],"%02x",levelnum); + rt_value=FindChunk(handle,chunk); + +// Close PLAYTEMP file +// + close(handle); + + return(rt_value); +} + +//-------------------------------------------------------------------------- +// CheckDiskSpace() +//-------------------------------------------------------------------------- +boolean CheckDiskSpace(long needed,char far *text,cds_io_type io_type) +{ + struct ffblk finfo; + struct diskfree_t dfree; + long avail; + +// Figure amount of space free on hard disk and let the gamer know if +// disk space is too low. +// + if (_dos_getdiskfree(0,&dfree)) + MAIN_ERROR(CHECKDISK_GDFREE); + + avail = (long)dfree.avail_clusters * + dfree.bytes_per_sector * + dfree.sectors_per_cluster; + + if (avail < needed) + { + unsigned old_DS=_DS; + + switch (io_type) + { + case cds_dos_print: + _DS=FP_SEG(text); + printf("%s",text); + _DS=old_DS; + exit(0); + break; + + case cds_menu_print: + case cds_id_print: + WindowX=0; WindowY=16; WindowW=320; WindowH=168; + SD_PlaySound (NOWAYSND); + Message(text); + IN_ClearKeysDown(); + IN_Ack(); + if (io_type==cds_menu_print) + MenuFadeOut(); + break; + } + + return(false); + } + + return(true); +} + + + +//-------------------------------------------------------------------------- +// CleanUpDoors_N_Actors() +//-------------------------------------------------------------------------- +void CleanUpDoors_N_Actors(void) +{ + char x,y; + objtype *obj; + objtype **actor_ptr; + byte *tile_ptr; + unsigned door; + + actor_ptr = (objtype **)actorat; + tile_ptr = (byte *)tilemap; + + for (y=0;y= objlist) && (obj < &objlist[MAXACTORS])) + { + // Found an actor + + // Determine door number... + + door = *tile_ptr & 0x3F; + + if ((obj->flags & (FL_SOLID|FL_DEADGUY)) == (FL_SOLID|FL_DEADGUY)) + obj->flags &= ~(FL_SHOOTABLE | FL_SOLID | FL_FAKE_STATIC); + + // Make sure door is open + + doorobjlist[door].ticcount = 0; + doorobjlist[door].action = dr_open; + doorposition[door] = 0xffff; + } + } + + tile_ptr++; + actor_ptr++; + } +} + + +//-------------------------------------------------------------------------- +// ClearNClose() - Use when changing levels via standard elevator. +// +// - This code doesn't CLEAR the elevator door as originally +// planned because, actors were coded to stay out of the +// elevator doorway. +// +//-------------------------------------------------------------------------- +void ClearNClose() +{ + char x,y,tx=0,ty=0,px=player->x>>TILESHIFT,py=player->y>>TILESHIFT; + + // Locate the door. + // + for (x=-1; x<2 && !tx; x+=2) + for (y=-1; y<2; y+=2) + if (tilemap[px+x][py+y] & 0x80) + { + tx=px+x; + ty=py+y; + break; + } + + // Close the door! + // + if (tx) + { + char doornum=tilemap[tx][ty]&63; + + doorobjlist[doornum].action = dr_closed; // this door is closed! + doorposition[doornum]=0; // draw it closed! + (unsigned)actorat[tx][ty] = doornum | 0x80; // make it solid! + } +} + +//-------------------------------------------------------------------------- +// CycleColors() +//-------------------------------------------------------------------------- +void CycleColors() +{ + #define NUM_RANGES 5 + #define CRNG_LOW 0xf0 + #define CRNG_HIGH 0xfe + #define CRNG_SIZE (CRNG_HIGH-CRNG_LOW+1) + + static CycleInfo crng[NUM_RANGES] = {{7,0,0xf0,0xf1}, + {15,0,0xf2,0xf3}, + {30,0,0xf4,0xf5}, + {10,0,0xf6,0xf9}, + {12,0,0xfa,0xfe}, + }; + + byte loop,cbuffer[CRNG_SIZE][3]; + boolean changes=false; + + for (loop=0; loop= c->delay_count) + { + byte temp[3],first,last,numregs; + + if (!changes) + { + VL_GetPalette(CRNG_LOW,CRNG_SIZE,(byte far *)cbuffer); + changes=true; + } + + first = c->firstreg-CRNG_LOW; + numregs = c->lastreg-c->firstreg; // is one less than in range + last = first+numregs; + + memcpy(temp,cbuffer[last],3); + memmove(cbuffer[first+1],cbuffer[first],numregs*3); + memcpy(cbuffer[first],temp,3); + + c->delay_count = c->init_delay; + } + else + c->delay_count -= tics; + } + + if (changes) + VL_SetPalette(CRNG_LOW,CRNG_SIZE,(byte far *)cbuffer); + else + VW_WaitVBL(1); +} + + +//=========================================================================== + +/* +========================== += += ShutdownId += += Shuts down all ID_?? managers += +========================== +*/ + +void ShutdownId (void) +{ + US_Shutdown (); + SD_Shutdown (); + PM_Shutdown (); + IN_Shutdown (); + VW_Shutdown (); + CA_Shutdown (); + MM_Shutdown (); +} + + +//=========================================================================== + + +/* +==================== += += CalcProjection += += Uses focallength += +==================== +*/ + +void CalcProjection (long focal) +{ + int i; + long intang; + float angle; + double tang; + double planedist; + double globinhalf; + int halfview; + double halfangle,facedist; + + + focallength = focal; + facedist = focal+MINDIST; + halfview = viewwidth/2; // half view in pixels + +// +// calculate scale value for vertical height calculations +// and sprite x calculations +// + scale = halfview*facedist/(VIEWGLOBAL/2); + +// +// divide heightnumerator by a posts distance to get the posts height for +// the heightbuffer. The pixel height is height>>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; +} + + + +//=========================================================================== + +//-------------------------------------------------------------------------- +// DoMovie() +//-------------------------------------------------------------------------- +boolean DoMovie(movie_t movie, memptr palette) +{ + boolean ReturnVal; +// StopMusic(); + SD_StopSound(); + + ClearMemory(); + UnCacheLump(STARTFONT,STARTFONT+NUMFONT); + CA_LoadAllSounds(); + + if (palette) + Movies[movie].palette = palette; + else + Movies[movie].palette = (memptr)FP_SEG(&vgapal); + + ReturnVal = MOVIE_Play(&Movies[movie]); + + SD_StopSound(); + ClearMemory(); + LoadFonts(); + + return(ReturnVal); +} + +//=========================================================================== + +/* +================= += += MS_CheckParm += +================= +*/ + +boolean MS_CheckParm (char far *check) +{ + int i; + char *parm; + + for (i = 1;i<_argc;i++) + { + parm = _argv[i]; + + while ( !isalpha(*parm) ) // skip - / \ etc.. in front of parm + if (!*parm++) + break; // hit end of string without an alphanum + + if ( !_fstricmp(check,parm) ) + return true; + } + + return false; +} + +//=========================================================================== + +//-------------------------------------------------------------------------- +// LoadFonts() +//-------------------------------------------------------------------------- +void LoadFonts(void) +{ + CA_CacheGrChunk(STARTFONT+4); + MM_SetLock (&grsegs[STARTFONT+4],true); + + CA_CacheGrChunk(STARTFONT+2); + MM_SetLock (&grsegs[STARTFONT+2],true); +} + +//=========================================================================== + +/* +========================== += += SetViewSize += +========================== +*/ + +boolean SetViewSize (unsigned width, unsigned height) +{ + viewwidth = width&~15; // must be divisable by 16 + viewheight = height&~1; // must be even + centerx = viewwidth/2-1; + shootdelta = viewwidth/10; + screenofs = ((200-STATUSLINES-viewheight+TOP_STRIP_HEIGHT)/2*SCREENWIDTH+(320-viewwidth)/8); + +// +// calculate trace angles and projection constants +// + CalcProjection (FOCALLENGTH); + +// +// build all needed compiled scalers +// + SetupScaling (viewwidth*1.5); + + view_xl=0; + view_xh=view_xl+viewwidth-1; + view_yl=0; + view_yh=view_yl+viewheight-1; + + return true; +} + + +void ShowViewSize (int width) +{ + int oldwidth,oldheight; + + oldwidth = viewwidth; + oldheight = viewheight; + + viewwidth = width*16; + viewheight = width*16*HEIGHTRATIO; + VWB_Bar (0,TOP_STRIP_HEIGHT,320,200-STATUSLINES-TOP_STRIP_HEIGHT,BORDER_MED_COLOR); +// VWB_Bar (0,0,320,200-STATUSLINES,BORDER_MED_COLOR); + DrawPlayBorder (); + + viewheight = oldheight; + viewwidth = oldwidth; +} + + +void NewViewSize (int width) +{ + CA_UpLevel (); + MM_SortMem (); + viewsize = width; + while (1) + { + if (SetViewSize (width*16,width*16*HEIGHTRATIO)) + break; + width--; + }; + CA_DownLevel (); +} + + +//=========================================================================== + +/* +========================== += += Quit += +========================== +*/ + + +void Quit (char *error,...) +{ + unsigned finscreen; + memptr diz; + char far *screen; + unsigned unit,err; + va_list ap; + + va_start(ap,error); + + MakeDestPath(PLAYTEMP_FILE); + remove(tempPath); + ClearMemory (); + + if (!*error) + { +#if GAME_VERSION != SHAREWARE_VERSION + if (gamestate.flags & GS_BAD_DIZ_FILE) + { + char far *end; + + CA_CacheGrChunk(DIZ_ERR_TEXT); + diz = grsegs[DIZ_ERR_TEXT]; + end=_fstrstr(diz,"^XX"); + *end=0; + } + else + if (!IsA386) + { + CA_CacheGrChunk (NO386SCREEN); + screen = MK_FP(grsegs[NO386SCREEN],7); + } +// else +#endif + +#if 0 + { + CA_CacheGrChunk (ORDERSCREEN); + screen = MK_FP(grsegs[ORDERSCREEN],0); + } +#endif + } + else + { + CA_CacheGrChunk (ERRORSCREEN); + screen = MK_FP(grsegs[ERRORSCREEN],7); + } + + WriteConfig (); + ShutdownId (); + + if (error && *error) + { + FILE *fp; + + unit=va_arg(ap,unsigned); + err=va_arg(ap,unsigned); +// movedata ((unsigned)screen,7,0xb800,0,7*160); + _fmemcpy(MK_FP(0xB800,0), screen, 7*160); + + textcolor(14); + textbackground(4); + gotoxy (10,4); + cprintf(error,unit,err); + + gotoxy (65-strlen(__VERSION__),2); + cprintf(" Ver:%s ",__VERSION__); + + gotoxy (1,8); + + MakeDestPath("BS_VSI.ERR"); + fp = fopen(tempPath,"wb"); + fprintf(fp,"$%02x%02x",unit,err); + if (fp) + fclose(fp); + + exit(1); + } + +#if 0 + if (!error || !(*error)) + { + unsigned far *clear = MK_FP(0xb800,23*80*2); + unsigned len = 0; + + clrscr(); +#if GAME_VERSION != SHAREWARE_VERSION + if (gamestate.flags & GS_BAD_DIZ_FILE) + fprint(diz); + else +#endif + { +// movedata ((unsigned)screen,0,0xb800,0,4000); + _fmemcpy(MK_FP(0xB800,0),screen,4000); + + // Far mem set (WORD)! - This is STUPID! Borland SUCKS! + + while (len != 80*2) + { + *clear++ = 0x700; + len++; + } + gotoxy (1,24); + } + } +#endif + + va_end(ap); + exit(0); +} + + +//=========================================================================== + +/* +===================== += += DemoLoop += +===================== +*/ + +void DemoLoop (void) +{ + int i,level; + int LastDemo=0; + boolean breakit; + unsigned old_bufferofs; + + while (1) + { + playstate = ex_title; + if (!screenfaded) + VW_FadeOut(); + VL_SetPaletteIntensity(0,255,&vgapal,0); + + while (!(gamestate.flags & GS_NOWAIT)) + { + extern boolean sqActive; + + // Start music when coming from menu... + // + if (!sqActive) + { + // Load and start music + // + CA_CacheAudioChunk(STARTMUSIC+TITLE_LOOP_MUSIC); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC+TITLE_LOOP_MUSIC]); + } + +// +// title page +// +#if !SKIP_TITLE_AND_CREDITS + breakit = false; + + CA_CacheScreen(TITLE1PIC); + CA_CacheGrChunk(TITLEPALETTE); + old_bufferofs = bufferofs; + bufferofs=displayofs; + VW_Bar(0,0,320,200,0); + bufferofs=old_bufferofs; + VL_SetPalette (0,256,grsegs[TITLEPALETTE]); + VL_SetPaletteIntensity(0,255,grsegs[TITLEPALETTE],0); + + fontnumber = 2; + PrintX = WindowX = 270; + PrintY = WindowY = 179; + WindowW = 29; + WindowH = 8; + VWB_Bar(WindowX,WindowY-1,WindowW,WindowH,VERSION_TEXT_BKCOLOR); + SETFONTCOLOR(VERSION_TEXT_COLOR, VERSION_TEXT_BKCOLOR); + US_Print(__VERSION__); + + VW_UpdateScreen(); + VL_FadeIn(0,255,grsegs[TITLEPALETTE],30); + UNCACHEGRCHUNK(TITLEPALETTE); + if (IN_UserInput(TickBase*6)) + breakit= true; + + // Cache screen 2 with Warnings and Copyrights + + CA_CacheScreen(TITLE2PIC); + fontnumber = 2; + PrintX = WindowX = 270; + PrintY = WindowY = 179; + WindowW = 29; + WindowH = 8; + VWB_Bar(WindowX,WindowY-1,WindowW,WindowH,VERSION_TEXT_BKCOLOR); + SETFONTCOLOR(VERSION_TEXT_COLOR, VERSION_TEXT_BKCOLOR); + US_Print(__VERSION__); + + // Fizzle whole screen incase of any last minute changes needed + // on title intro. + + FizzleFade(bufferofs,displayofs,320,200,70,false); + + IN_UserInput(TickBase*2); + if (breakit || IN_UserInput(TickBase*6)) + break; + VW_FadeOut(); + +// +// credits page +// + DrawCreditsPage(); + VW_UpdateScreen(); + VW_FadeIn(); + if (IN_UserInput(TickBase*6)) + break; + VW_FadeOut(); + +#endif + +// +// demo +// + +#if DEMOS_ENABLED +#if IN_DEVELOPMENT + if (!MS_CheckParm("recdemo")) +#endif + PlayDemo(LastDemo++%6); + + if (playstate == ex_abort) + break; + else + { + // Start music when coming from menu... + // + if (!sqActive) +// if (!SD_MusicPlaying()) + { + // Load and start music + // + CA_CacheAudioChunk(STARTMUSIC+TITLE_LOOP_MUSIC); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC+TITLE_LOOP_MUSIC]); + } + } +#endif + +// +// high scores +// +#if !SKIP_TITLE_AND_CREDITS + CA_CacheScreen (BACKGROUND_SCREENPIC); + DrawHighScores (); + VW_UpdateScreen (); + VW_FadeIn (); + + if (IN_UserInput(TickBase*9)) + break; + VW_FadeOut(); +#endif + } + + + if (audiosegs[STARTMUSIC+TITLE_LOOP_MUSIC]) + MM_FreePtr((memptr *)&audiosegs[STARTMUSIC+TITLE_LOOP_MUSIC]); + + if (!screenfaded) + VW_FadeOut(); + +#ifdef DEMOS_EXTERN + if (MS_CheckParm("recdemo")) + RecordDemo (); + else +#endif + { +#if IN_DEVELOPMENT || TECH_SUPPORT_VERSION + if (gamestate.flags & GS_QUICKRUN) + { + ReadGameNames(); + CA_LoadAllSounds(); + NewGame(2,gamestate.episode); + startgame = true; + } + else +#endif + US_ControlPanel (0); + } + if (startgame || loadedgame) + GameLoop (); + } +} + +//------------------------------------------------------------------------- +// DrawCreditsPage() +//------------------------------------------------------------------------- +void DrawCreditsPage() +{ + PresenterInfo pi; + + CA_CacheScreen(BACKGROUND_SCREENPIC); + + memset(&pi,0,sizeof(pi)); + pi.flags = TPF_CACHE_NO_GFX; + pi.xl=38; + pi.yl=28; + pi.xh=281; + pi.yh=170; + pi.bgcolor = 2; + pi.ltcolor = BORDER_HI_COLOR; + fontcolor = BORDER_TEXT_COLOR; + pi.shcolor = pi.dkcolor = 0; + pi.fontnumber=fontnumber; + +#ifdef ID_CACHE_CREDITS + TP_LoadScript(NULL,&pi,CREDITSTEXT); +#else + TP_LoadScript("CREDITS.TXT",&pi,0); +#endif + + TP_Presenter(&pi); +} + + +//=========================================================================== + + +extern void JM_FREE_START(); +extern void JM_FREE_END(); + +/* +========================== += += main += +========================== +*/ + +//char *nosprtxt[] = {"nospr",nil}; +#if IN_DEVELOPMENT || TECH_SUPPORT_VERSION +short starting_episode=0,starting_level=0,starting_difficulty=2; +#endif +short debug_value=0; + +void main (void) +{ +#if IN_DEVELOPMENT + MakeDestPath(ERROR_LOG); + remove(tempPath); +#endif + + MakeDestPath(PLAYTEMP_FILE); + remove(tempPath); + + freed_main(); + +#if FREE_FUNCTIONS + UseFunc((char huge *)JM_FREE_START,(char huge *)JM_FREE_END); + UseFunc((char huge *)JM_FREE_DATA_START,(char huge *)JM_FREE_DATA_END); +#endif + + DemoLoop(); + + Quit(NULL); +} + +#if FREE_FUNCTIONS + +//------------------------------------------------------------------------- +// UseFunc() +//------------------------------------------------------------------------- +unsigned UseFunc(char huge *first, char huge *next) +{ + unsigned start,end; + unsigned pars; + + first += 15; + next++; + next--; + + start = FP_SEG(first); + end = FP_SEG(next); + if (!FP_OFF(next)) + end--; + pars = end - start - 1; + _fmemset(MK_FP(start,0),0,pars*16); + MML_UseSpace(start,pars); + + return(pars); +} + +#endif + + +//------------------------------------------------------------------------- +// fprint() +//------------------------------------------------------------------------- +void fprint(char far *text) +{ + while (*text) + printf("%c",*text++); +} + + +//------------------------------------------------------------------------- +// InitDestPath() +//------------------------------------------------------------------------- +void InitDestPath(void) +{ + char *ptr; + +#pragma warn -pia + if (ptr=getenv("APOGEECD")) + { + struct ffblk ffblk; + short len; + + len = _fstrlen(ptr); + if (len > MAX_DEST_PATH_LEN) + { + printf("\nAPOGEECD path too long.\n"); + exit(0); + } + + _fstrcpy(destPath,ptr); + if (destPath[len-1] == '\\') + destPath[len-1]=0; + + if (findfirst(destPath,&ffblk,FA_DIREC) == -1) + { + printf("\nAPOGEECD directory not found.\n"); + exit(0); + } + + _fstrcat(destPath,"\\"); + } + else + _fstrcpy(destPath,""); +#pragma warn +pia +} + +//------------------------------------------------------------------------- +// MakeDestPath() +//------------------------------------------------------------------------- +void MakeDestPath(char far *file) +{ + _fstrcpy(tempPath,destPath); + _fstrcat(tempPath,file); +} + +#if IN_DEVELOPMENT + +//------------------------------------------------------------------------- +// ShowMemory() +//------------------------------------------------------------------------- +void ShowMemory(void) +{ + long psize,size; + + size = MM_TotalFree(); + psize = MM_LargestAvail(); + mprintf("Mem free: %ld %ld\n",size,psize); +} + +#endif diff --git a/3D_MENU.C b/3D_MENU.C new file mode 100644 index 0000000..1d6cf47 --- /dev/null +++ b/3D_MENU.C @@ -0,0 +1,3846 @@ + +#include "3d_def.h" + +#include "jm_tp.h" +#include "jm_io.h" + +#pragma hdrstop + +// As is, this switch will not work ... the data associated with this +// is not saved out correctly. +// +//#define CACHE_KEY_DATA + + +// +// End game message +// + +char far EndGameStr[] = {" End current game?\n" + " Are you sure (Y or N)?" +}; + + +#define ENDGAMESTR (EndGameStr) + +char far QuitToDosStr[] = {" Quit to DOS?\n" + " Are you sure (Y or N)?" +}; + +//#define FREEFONT(fontnum) {MM_SetPurge (&(memptr)grsegs[fontnum],3);} +#define FREEFONT(fontnum) {if (fontnum != STARTFONT+2 && grsegs[fontnum]) UNCACHEGRCHUNK(fontnum);} + + +boolean EscPressed = false; + +int lastmenumusic; + + +//=========================================================================== +// +// PRIVATE PROTOTYPES +// +//=========================================================================== + +void CP_ReadThis(void); +void CP_OrderingInfo(void); +void DrawEpisodePic(int w); +void DrawAllSoundLights(int which); +void ReadGameNames(void); +void FreeMusic(void); +void CP_GameOptions(void); +void DrawGopMenu(void); +void CalibrateJoystick(void); +void ExitGame(void); +void CP_Switches(void); +void DrawSwitchMenu(void); +void DrawAllSwitchLights(int which); +void DrawSwitchDescription(int which); + + +extern boolean refresh_screen; + + +//=========================================================================== +// +// LOCAL DATA... +// +//=========================================================================== + +CP_iteminfo + far MainItems= {MENU_X,MENU_Y,12,MM_READ_THIS,0,9,{77, 1,154,9,1}}, + far GopItems= {MENU_X,MENU_Y+30,4,0,0,9,{77, 1,154,9,1}}, + far SndItems= {SM_X,SM_Y,12,0,0,7, {87,-1,144,7,1}}, + far LSItems= {LSM_X,LSM_Y,10,0,0,8, {86,-1,144,8,1}}, + far CtlItems= {CTL_X,CTL_Y,7,-1,0,9, {87,1,174,9,1}}, + far CusItems= {CST_X,CST_Y+7,6,-1,0,15,{54,-1,203,7,1}}, + far NewEitems= {NE_X,NE_Y,11,0,0,16, {43,-2,119,16,1}}, + far NewItems= {NM_X,NM_Y,4,1,0,16, {60,-2,105,16,1}}, + far SwitchItems= {MENU_X,MENU_Y+25,4,0,0,9,{87,-1,132,7,1}}; + + + +#pragma warn -sus + +CP_itemtype far MainMenu[]= +{ + {AT_ENABLED,"NEW MISSION",CP_NewGame,COAL_FONT}, + {AT_READIT,"ORDERING INFO",CP_OrderingInfo}, + {AT_READIT,"INSTRUCTIONS",CP_ReadThis}, + {AT_ENABLED,"STORY",CP_BlakeStoneSaga}, + {AT_DISABLED,"",0}, + {AT_ENABLED,"GAME OPTIONS",CP_GameOptions}, + {AT_ENABLED,"HIGH SCORES",CP_ViewScores}, + {AT_ENABLED,"LOAD MISSION",CP_LoadGame}, + {AT_DISABLED,"SAVE MISSION",CP_SaveGame}, + {AT_DISABLED,"",0}, + {AT_ENABLED,"BACK TO DEMO",CP_ExitOptions}, + {AT_ENABLED,"LOGOFF",0} +}, + +far GopMenu[]= +{ + {AT_ENABLED,"SOUND",CP_Sound}, + {AT_ENABLED,"CONTROLS",CP_Control}, + {AT_ENABLED,"CHANGE VIEW",CP_ChangeView}, + {AT_ENABLED,"SWITCHES",CP_Switches}, +}, + +far SndMenu[]= +{ + {AT_ENABLED,"NONE",0}, + {AT_ENABLED,"PC SPEAKER",0}, + {AT_ENABLED,"ADLIB/SOUND BLASTER",0}, + {AT_DISABLED,"",0}, + {AT_DISABLED,"",0}, + {AT_ENABLED,"NONE",0}, + {AT_ENABLED,"DISNEY SOUND SOURCE",0}, + {AT_ENABLED,"SOUND BLASTER",0}, + {AT_DISABLED,"",0}, + {AT_DISABLED,"",0}, + {AT_ENABLED,"NONE",0}, + {AT_ENABLED,"ADLIB/SOUND BLASTER",0} +}, + +far CtlMenu[]= +{ + {AT_DISABLED,"MOUSE ENABLED",0}, + {AT_DISABLED,"JOYSTICK ENABLED",0}, + {AT_DISABLED,"USE JOYSTICK PORT 2",0}, + {AT_DISABLED,"GRAVIS GAMEPAD ENABLED",0}, + {AT_DISABLED,"CALIBRATE JOYSTICK",0}, + {AT_DISABLED,"MOUSE SENSITIVITY",MouseSensitivity}, + {AT_ENABLED,"CUSTOMIZE CONTROLS",CustomControls} +}, + +far SwitchMenu[]= +{ + {AT_ENABLED,"LIGHTING",0}, + {AT_ENABLED,"REBA ATTACK INFO",0}, + {AT_ENABLED,"SHOW CEILINGS",0}, + {AT_ENABLED,"SHOW FLOORS",0} +}, + + +#pragma warn +sus + +#if 0 +far NewEmenu[]= +{ + {AT_ENABLED,"MISSION 1:\n" + "STAR INSTITUTE",0}, + + {AT_NON_SELECTABLE,"MISSION 2:\n" + "FLOATING FORTRESS",0}, + + {AT_NON_SELECTABLE,"MISSION 3:\n" + "UNDERGROUND NETWORK",0}, + + {AT_NON_SELECTABLE,"MISSION 4:\n" + "STAR PORT",0}, + + {AT_NON_SELECTABLE,"MISSION 5:\n" + "HABITAT II",0}, + + {AT_NON_SELECTABLE,"MISSION 6:\n" + "SATELLITE DEFENSE",0}, +}, +#endif + +far NewMenu[]= +{ + {AT_ENABLED,"LEVEL 1:\n" + "NOVICE AGENT",0}, + {AT_ENABLED,"LEVEL 2:\n" + "SKILLED AGENT",0}, + {AT_ENABLED,"LEVEL 3:\n" + "EXPERT AGENT",0}, + {AT_ENABLED,"LEVEL 4:\n" + "VETERAN AGENT",0} +}, + +far LSMenu[]= +{ + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0} +}, + +far CusMenu[]= +{ + {AT_ENABLED,"",0}, + {0,"",0}, + {AT_ENABLED,"",0}, + {0,"",0}, + {AT_ENABLED,"",0}, + {AT_ENABLED,"",0} +} +; + + +int color_hlite[]= +{ + HIGHLIGHT_DISABLED_COLOR, + HIGHLIGHT_TEXT_COLOR, + READHCOLOR, + HIGHLIGHT_DEACTIAVED_COLOR, +}; + +int color_norml[]= +{ + DISABLED_TEXT_COLOR, + ENABLED_TEXT_COLOR, + READCOLOR, + DEACTIAVED_TEXT_COLOR, +}; + +int EpisodeSelect[6]={1}; + +int SaveGamesAvail[10],StartGame,SoundStatus=1,pickquick; +char far SaveGameNames[10][GAME_DESCRIPTION_LEN+1],far SaveName[13]="SAVEGAM?."; + + + +//////////////////////////////////////////////////////////////////// +// +// INPUT MANAGER SCANCODE TABLES +// +//////////////////////////////////////////////////////////////////// + +#ifndef CACHE_KEY_DATA + +static byte + far * far 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 + }, + far * far ExtScanNames[] = // Names corresponding to ExtScanCodes + { + "ESC","BKSP","TAB","CTRL","LSHFT","SPACE","CAPSLK","F1","F2","F3","F4", + "F5","F6","F7","F8","F9","F10","F11","F12","SCRLK","ENTER","RSHFT", + "PRTSC","ALT","HOME","PGUP","END","PGDN","INS","DEL","NUMLK","UP", + "DOWN","LEFT","RIGHT","" + }; + +#else + +byte far *ScanNames, far * ExtScanNames; +ScanCode far *ExtScanCodes; + +#endif + +static byte special_keys[] = +{ + sc_Tilde,sc_Plus,sc_Minus,sc_L,sc_P,sc_M,sc_S,sc_I,sc_Q,sc_W,sc_E,sc_Enter,sc_1,sc_2,sc_3,sc_4,sc_5,sc_Tab +}; + + + + +//------------------------------------------------------------------------- +// HelpScreens() +//------------------------------------------------------------------------- +void HelpScreens() +{ +#ifndef ID_CACHE_HELP + HelpPresenter("HELP.TXT",false,0,true); +#else + HelpPresenter(NULL,false,HELPTEXT,true); +#endif +} + + +//------------------------------------------------------------------------- +// HelpPresenter() +//------------------------------------------------------------------------- +void HelpPresenter(char *fname,boolean continue_keys, unsigned id_cache, boolean startmusic) +{ + #define FULL_VIEW_WIDTH 19 + + PresenterInfo pi; + short oldwidth; + + memset(&pi,0,sizeof(pi)); + + pi.flags = TPF_SHOW_PAGES; + if (continue_keys) + pi.flags |= TPF_CONTINUE; + + VW_FadeOut(); + +// Change view size to MAX! (scaler clips shapes on smaller views) +// + oldwidth = viewwidth/16; + if (oldwidth != FULL_VIEW_WIDTH) + NewViewSize(FULL_VIEW_WIDTH); + +// Draw help border +// + CacheLump(H_TOPWINDOWPIC,H_BOTTOMINFOPIC); + VWB_DrawPic (0,0,H_TOPWINDOWPIC); + VWB_DrawPic (0,8,H_LEFTWINDOWPIC); + VWB_DrawPic (312,8,H_RIGHTWINDOWPIC); + VWB_DrawPic (8,176,H_BOTTOMINFOPIC); + UnCacheLump(H_TOPWINDOWPIC,H_BOTTOMINFOPIC); + +// Setup for text presenter +// + pi.xl=8; + pi.yl=8; + pi.xh=311; + pi.yh=175; + pi.ltcolor=0x7b; + pi.bgcolor=0x7d; + pi.dkcolor=0x7f; + pi.shcolor=0x00; + pi.fontnumber=4; + + if (continue_keys) + pi.infoline = (char far *)" UP / DN - PAGES ENTER - CONTINUES ESC - EXITS"; + else + pi.infoline = (char far *)" UP / DN - PAGES ESC - EXITS"; + + if (startmusic) + StartCPMusic(TEXTSONG); + +// Load, present, and free help text. +// + TP_LoadScript(fname,&pi,id_cache); + TP_Presenter(&pi); + TP_FreeScript(&pi,id_cache); + + MenuFadeOut(); + +// Reset view size +// + if (oldwidth != FULL_VIEW_WIDTH) + NewViewSize(oldwidth); + + if (startmusic && TPscan==sc_Escape) + StartCPMusic(MENUSONG); + IN_ClearKeysDown(); +} + +//-------------------------------------------------------------------------- +// US_ControlPanel() - Control Panel! Ta Da! +//-------------------------------------------------------------------------- +void US_ControlPanel(byte scancode) +{ + int which; + +#ifdef CACHE_KEY_DATA + CA_CacheGrChunk(SCANNAMES_DATA); + CA_CacheGrChunk(EXTSCANNAMES_DATA); + CA_CacheGrChunk(EXTSCANCODES_DATA); + + ScanNames = grsegs[SCANNAMES_DATA]; + ExtScanNames = grsegs[EXTSCANNAMES_DATA]; + ExtScanCodes = grsegs[EXTSCANCODES_DATA]; +#else + +// +// This code doesn't correctly save the table data -- it saves garbage +// for SCANNAMES and EXTSCANCODES... +// + +// IO_WriteFile("SCANNAME.BIN",ScanNames,sizeof(ScanNames)); +// IO_WriteFile("EXTSCNAM.BIN",ExtScanNames,sizeof(ExtScanNames)); +// IO_WriteFile("EXTSCCOD.BIN",ExtScanCodes,sizeof(ExtScanCodes)); + +#endif + + if (ingame) + if (CP_CheckQuick(scancode)) + return; + + SetupControlPanel(); + StartCPMusic(MENUSONG); + + // + // F-KEYS FROM WITHIN GAME + // + switch(scancode) + { + case sc_F1: + CleanupControlPanel(); + HelpScreens(); + return; + + case sc_F2: + CP_SaveGame(0); + goto finishup; + + case sc_F3: + CP_LoadGame(0); +// refresh_screen=false; + 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(); + return; + } + + + DrawMainMenu(); + MenuFadeIn(); + StartGame=0; + + // + // MAIN MENU LOOP + // + do + { + which=HandleMenu(&MainItems,&MainMenu[0],NULL); + + switch(which) + { + case MM_VIEW_SCORES: + if (MainMenu[MM_VIEW_SCORES].routine == NULL) + if (CP_EndGame()) + StartGame=1; + + DrawMainMenu(); + MenuFadeIn(); + break; + + case -1: + case MM_LOGOFF: + CP_Quit(); + break; + + default: + if (!StartGame) + { + DrawMainMenu(); + MenuFadeIn(); + } + } + + // + // "EXIT OPTIONS" OR "NEW GAME" EXITS + // + } while(!StartGame); + + // + // DEALLOCATE EVERYTHING + // + CleanupControlPanel(); + if (!loadedgame) + StopMusic(); + + + // + // CHANGE MAINMENU ITEM + // + if (startgame || loadedgame) + { + #pragma warn -sus + MainMenu[MM_VIEW_SCORES].routine = NULL; + _fstrcpy(MainMenu[MM_VIEW_SCORES].string,"END GAME"); + #pragma warn +sus + } + + if (ingame && loadedgame) + refresh_screen=false; + + +#ifdef CACHE_KEY_DATA + FREEFONT(SCANNAMES_DATA); + FREEFONT(EXTSCANNAMES_DATA); + FREEFONT(EXTSCANCODES_DATA); +#endif + + // RETURN/START GAME EXECUTION +} + +//-------------------------------------------------------------------------- +// DrawMainMenu(void) +//-------------------------------------------------------------------------- +void DrawMainMenu(void) +{ + ControlPanelFree(); + CA_CacheScreen (BACKGROUND_SCREENPIC); + ControlPanelAlloc(); + + ClearMScreen(); + DrawMenuTitle("MAIN OPTIONS"); + DrawInstructions(IT_STANDARD); + + // + // CHANGE "MISSION" AND "DEMO" + // + if (ingame) + { + _fstrcpy(&MainMenu[MM_BACK_TO_DEMO].string[8],"MISSION"); + MainMenu[MM_BACK_TO_DEMO].active=AT_READIT; + } + else + { + _fstrcpy(&MainMenu[MM_BACK_TO_DEMO].string[8],"DEMO"); + MainMenu[MM_BACK_TO_DEMO].active=AT_ENABLED; + } + + fontnumber = 4; // COAL + + DrawMenu(&MainItems,&MainMenu[0]); + + VW_UpdateScreen(); +} + +//-------------------------------------------------------------------------- +// READ THIS! +//-------------------------------------------------------------------------- +void CP_ReadThis(void) +{ + ControlPanelFree(); + HelpScreens(); + ControlPanelAlloc(); +} + + +//-------------------------------------------------------------------------- +// CP_OrderingInfo() +//-------------------------------------------------------------------------- +void CP_OrderingInfo(void) +{ + ControlPanelFree(); +#ifndef ID_CACHE_HELP + HelpPresenter("ORDER.TXT",false,0,true); +#else + HelpPresenter(NULL,false,ORDERTEXT,true); +#endif + ControlPanelAlloc(); +} + + +//------------------------------------------------------------------------- +// CP_BlakeStoneSaga() +//------------------------------------------------------------------------- +void CP_BlakeStoneSaga() +{ + ControlPanelFree(); +#ifndef ID_CACHE_HELP + HelpPresenter("SAGA.TXT",false,0,true); +#else + HelpPresenter(NULL,false,SAGATEXT,true); +#endif + ControlPanelAlloc(); +} + +//-------------------------------------------------------------------------- +// CP_CheckQuick() - CHECK QUICK-KEYS & QUIT (WHILE IN A GAME) +//-------------------------------------------------------------------------- +int CP_CheckQuick(unsigned scancode) +{ + switch(scancode) + { + // END GAME + // + case sc_F7: + VW_ScreenToScreen (displayofs,bufferofs,80,160); + CA_CacheGrChunk(STARTFONT+1); + + WindowH=160; + if (Confirm(ENDGAMESTR)) + { + playstate = ex_died; + pickquick = gamestate.lives = 0; + } + + WindowH=200; + fontnumber=4; + return(1); + + // QUICKSAVE + // + case sc_F8: + if (SaveGamesAvail[LSItems.curpos] && pickquick) + { + char string[100]="Quick Save will overwrite:\n\""; + + CA_CacheGrChunk(STARTFONT+1); + + _fstrcat(string,SaveGameNames[LSItems.curpos]); + strcat(string,"\"?"); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + +#if IN_DEVELOPMENT + if (TestQuickSave || Confirm(string)) + { + if (TestQuickSave) + TestQuickSave--; +#else + if (Confirm(string)) + { +#endif + CA_CacheGrChunk(STARTFONT+1); + CP_SaveGame(1); + fontnumber=4; + } + else + refresh_screen=false; + } + else + { + CA_CacheGrChunk(STARTFONT+1); + + VW_FadeOut (); + + StartCPMusic(MENUSONG); + pickquick=CP_SaveGame(0); + + lasttimecount = TimeCount; + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + } + + return(1); + + // QUICKLOAD + // + case sc_F9: + if (SaveGamesAvail[LSItems.curpos] && pickquick) + { + char string[100]="Quick Load:\n\""; + + CA_CacheGrChunk(STARTFONT+1); + + _fstrcat(string,SaveGameNames[LSItems.curpos]); + strcat(string,"\"?"); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + + if (Confirm(string)) + CP_LoadGame(1); + else + { + refresh_screen=false; + return(1); + } + + fontnumber=4; + } + else + { + CA_CacheGrChunk(STARTFONT+1); + + VW_FadeOut (); + + StartCPMusic(MENUSONG); + pickquick=CP_LoadGame(0); + + lasttimecount = TimeCount; + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + PM_CheckMainMem (); + } + + if (pickquick) + refresh_screen=false; + return(1); + + // QUIT + // + case sc_F10: + CA_CacheGrChunk(STARTFONT+1); + VW_ScreenToScreen (displayofs,bufferofs,80,160); + + WindowX=WindowY=0; + WindowW=320; + WindowH=160; + if (Confirm(QuitToDosStr)) + ExitGame(); + + refresh_screen=false; + WindowH=200; + fontnumber=4; + + return(1); + } + + return(0); +} + +//-------------------------------------------------------------------------- +// END THE CURRENT GAME +//-------------------------------------------------------------------------- +int CP_EndGame(void) +{ + if (!Confirm(ENDGAMESTR)) + return 0; + + pickquick = gamestate.lives = 0; + playstate = ex_died; + InstantQuit = 1; + +#if 0 +#pragma warn -sus + MainMenu[MM_VIEW_SCORES].routine=&CP_ViewScores; + _fstrcpy(MainMenu[MM_VIEW_SCORES].string,"HIGH SCORES"); +#pragma warn +sus +#endif + + return 1; +} + +//-------------------------------------------------------------------------- +// CP_ViewScores() - VIEW THE HIGH SCORES +//-------------------------------------------------------------------------- +void CP_ViewScores(void) +{ + fontnumber=4; + StartCPMusic(ROSTER_MUS); + DrawHighScores (); + VW_UpdateScreen (); + MenuFadeIn(); + fontnumber=1; + + IN_Ack(); + + StartCPMusic(MENUSONG); + MenuFadeOut(); +} + +//-------------------------------------------------------------------------- +// CP_NewGame() - START A NEW GAME +//-------------------------------------------------------------------------- +void CP_NewGame(void) +{ + int which,episode; + + DrawMenuTitle("Difficulty Level"); + DrawInstructions(IT_STANDARD); + + +#if 0 + +firstpart: + + DrawNewEpisode(); + do + { + which=HandleMenu(&NewEitems,&NewEmenu[0],DrawEpisodePic); + switch(which) + { + case -1: + MenuFadeOut(); + return; + + default: + if (!EpisodeSelect[which]) + { + SD_PlaySound (NOWAYSND); + CacheMessage(READTHIS_TEXT); + IN_ClearKeysDown(); + IN_Ack(); + VL_Bar(35,69,250,62,TERM_BACK_COLOR); + DrawNewEpisode(); + which = 0; + } + else + { + episode = which; + which = 1; + } + break; + } + + } while (!which); + + ShootSnd(); +#else + episode = 0; +#endif + +#if 0 + // + // ALREADY IN A GAME? + // + if (ingame) + if (!Confirm(CURGAME)) + { + MenuFadeOut(); + return; + } +#endif + +secondpart: + + MenuFadeOut(); + if (ingame) + CA_CacheScreen(BACKGROUND_SCREENPIC); + DrawNewGame(); + which=HandleMenu(&NewItems,&NewMenu[0],DrawNewGameDiff); + + if (which<0) + { + MenuFadeOut(); +#if 0 + goto firstpart; +#else + return; +#endif + } + + ShootSnd(); + MenuFadeOut(); + ControlPanelFree(); + + if (Breifing(BT_INTRO,episode)) + { + CA_CacheScreen (BACKGROUND_SCREENPIC); + ControlPanelAlloc(); + goto secondpart; + } + + StartGame=1; + NewGame(which,episode); + + // + // CHANGE "READ THIS!" TO NORMAL COLOR + // + MainMenu[MM_READ_THIS].active=AT_ENABLED; + +} + +//--------------------------------------------------------------------------- +// DrawMenuTitle() - Draws the menu title +//--------------------------------------------------------------------------- +void DrawMenuTitle(char *title) +{ + + fontnumber = 3; + CA_CacheGrChunk(STARTFONT+3); + + PrintX = WindowX = 32; + PrintY = WindowY = 32; + WindowW = 244; + WindowH = 20; + + SETFONTCOLOR(TERM_SHADOW_COLOR,TERM_BACK_COLOR); + US_PrintCentered(title); + + WindowX = 32-1; + WindowY = 32-1; + + SETFONTCOLOR(ENABLED_TEXT_COLOR,TERM_BACK_COLOR); + US_PrintCentered(title); + + FREEFONT(STARTFONT+3); + +} + +//--------------------------------------------------------------------------- +// DrawInstructions() - Draws instructions centered at the bottom of +// the view screen. +// +// NOTES: Orginal font number or font color is not maintained. +//--------------------------------------------------------------------------- +void DrawInstructions(inst_type Type) +{ + #define INSTRUCTIONS_Y_POS 154+10 + + char *instr[MAX_INSTRUCTIONS] = {{"UP/DN SELECTS - ENTER CHOOSES - ESC EXITS"}, + {"PRESS ANY KEY TO CONTINUE"}, + {"ENTER YOUR NAME AND PRESS ENTER"}, + {"RT/LF ARROW SELECTS - ENTER CHOOSES"}}; + + fontnumber = 2; + + WindowX = 48; + WindowY = INSTRUCTIONS_Y_POS; + WindowW = 236; + WindowH = 8; + + VWB_Bar(WindowX,WindowY-1,WindowW,WindowH,TERM_BACK_COLOR); + + SETFONTCOLOR(TERM_SHADOW_COLOR,TERM_BACK_COLOR); + US_PrintCentered(instr[Type]); + + WindowX--; + WindowY--; + + SETFONTCOLOR(INSTRUCTIONS_TEXT_COLOR,TERM_BACK_COLOR); + US_PrintCentered(instr[Type]); +} + +#if 0 + +//-------------------------------------------------------------------------- +// DrawNewEpisode() - DRAW NEW EPISODE MENU +//-------------------------------------------------------------------------- +void DrawNewEpisode(void) +{ + ClearMScreen(); + + DrawMenuTitle("CHOOSE A MISSION"); + DrawInstructions(IT_STANDARD); + + PrintY=51; + WindowX=58; + + fontnumber = 2; // six point font + DrawMenu(&NewEitems,&NewEmenu7[0]); + + DrawEpisodePic(NewEitems.curpos); + + VW_UpdateScreen(); + MenuFadeIn(); + WaitKeyUp(); + +} + +#endif + +//-------------------------------------------------------------------------- +// DrawNewGame() - DRAW NEW GAME MENU +//-------------------------------------------------------------------------- +void DrawNewGame(void) +{ + ClearMScreen(); + DrawMenuTitle("DIFFICULTY LEVEL"); + DrawInstructions(IT_STANDARD); + + fontnumber = 2; // six point font + DrawMenu(&NewItems,&NewMenu[0]); + + DrawNewGameDiff(NewItems.curpos); + + px=48; + py=INSTRUCTIONS_Y_POS-24; + ShPrint(" HIGHER DIFFICULTY LEVELS CONTAIN",TERM_SHADOW_COLOR,false); + + px=48; + py+=6; + ShPrint(" MORE, STRONGER ENEMIES",TERM_SHADOW_COLOR,false); + + + VW_UpdateScreen(); + + MenuFadeIn(); + WaitKeyUp(); +} + +//-------------------------------------------------------------------------- +// DRAW NEW GAME GRAPHIC +//-------------------------------------------------------------------------- +void DrawNewGameDiff(int w) +{ + VWB_DrawPic(192,77,w+C_BABYMODEPIC); +} + +//-------------------------------------------------------------------------- +// DRAW NEW GAME GRAPHIC +//-------------------------------------------------------------------------- +void DrawEpisodePic(int w) +{ + VWB_DrawPic(176,72,w+C_EPISODE1PIC); +} + +//-------------------------------------------------------------------------- +// CP_GameOptions() - DRAW THE GAME OPTIONS MENU +//-------------------------------------------------------------------------- +void CP_GameOptions(void) +{ + int which,i; + + CA_CacheScreen (BACKGROUND_SCREENPIC); + DrawGopMenu(); + MenuFadeIn(); + WaitKeyUp(); + + do + { + which=HandleMenu(&GopItems,&GopMenu[0],NULL); + + if (which != -1) + { + DrawGopMenu(); + MenuFadeIn(); + } + + } while(which>=0); + + MenuFadeOut(); +} + +//-------------------------------------------------------------------------- +// DrawGopMenu(void) +//-------------------------------------------------------------------------- +void DrawGopMenu(void) +{ + CA_CacheScreen (BACKGROUND_SCREENPIC); + + ClearMScreen(); + DrawMenuTitle("GAME OPTIONS"); + DrawInstructions(IT_STANDARD); + + fontnumber = 4; // COAL + + DrawMenu(&GopItems,&GopMenu[0]); + + VW_UpdateScreen(); +} + + +void ChangeSwaps(void) +{ + WindowX=WindowY=0; + WindowW=320; + WindowH=200; + Message(Computing); + + PM_Shutdown(); + PM_Startup (); + ClearMemory(); + ControlPanelAlloc(); + + IN_UserInput(50); + IN_ClearKeysDown(); + +} + +//-------------------------------------------------------------------------- +// GAME SWITCHES MENU +//-------------------------------------------------------------------------- +void CP_Switches(void) +{ + int which,i; + + CA_CacheScreen (BACKGROUND_SCREENPIC); + DrawSwitchMenu(); + MenuFadeIn(); + WaitKeyUp(); + + do + { + which=HandleMenu(&SwitchItems,&SwitchMenu[0],DrawAllSwitchLights); + + switch (which) + { + case SW_LIGHTING: + gamestate.flags ^= GS_LIGHTING; + ShootSnd(); + DrawSwitchMenu(); + break; + + case SW_REBA_ATTACK_INFO: + gamestate.flags ^= GS_ATTACK_INFOAREA; + ShootSnd(); + DrawSwitchMenu(); + break; + + case SW_CEILING: + gamestate.flags ^= GS_DRAW_CEILING; + ShootSnd(); + DrawSwitchMenu(); + break; + + case SW_FLOORS: + gamestate.flags ^= GS_DRAW_FLOOR; + ShootSnd(); + DrawSwitchMenu(); + break; + } + + } while(which>=0); + + MenuFadeOut(); +} + +//-------------------------------------------------------------------------- +// DrawSwitchMenu(void) +//-------------------------------------------------------------------------- +void DrawSwitchMenu(void) +{ + CA_CacheScreen (BACKGROUND_SCREENPIC); + + ClearMScreen(); + DrawMenuTitle("GAME SWITCHES"); + DrawInstructions(IT_STANDARD); + + fontnumber = 2; + + DrawMenu(&SwitchItems,&SwitchMenu[0]); + DrawAllSwitchLights(SwitchItems.curpos); + + VW_UpdateScreen(); +} + +//-------------------------------------------------------------------------- +// DrawAllSwitchLights() +//-------------------------------------------------------------------------- +void DrawAllSwitchLights(int which) +{ + short i; + unsigned Shape; + + for (i=0;i=0); + + MenuFadeOut(); +} + +//-------------------------------------------------------------------------- +// DrawSoundMenu() - DRAW THE SOUND MENU +//-------------------------------------------------------------------------- +void DrawSoundMenu(void) +{ + int i,on; + + // + // DRAW SOUND MENU + // + + ClearMScreen(); + DrawMenuTitle("SOUND SETTINGS"); + DrawInstructions(IT_STANDARD); + + // + // IF NO ADLIB, NON-CHOOSENESS! + // + + if (!AdLibPresent && !SoundBlasterPresent) + { + SndMenu[2].active=SndMenu[10].active=SndMenu[11].active=AT_DISABLED; + } + + if (!SoundSourcePresent) + SndMenu[6].active=AT_DISABLED; + + if (!SoundBlasterPresent) + SndMenu[7].active=AT_DISABLED; + + if (!SoundSourcePresent && !SoundBlasterPresent) + SndMenu[5].active=AT_DISABLED; + + fontnumber = 4; + + SETFONTCOLOR(DISABLED_TEXT_COLOR,TERM_BACK_COLOR); + ShadowPrint("SOUND EFFECTS",105,52); + ShadowPrint("DIGITIZED SOUNDS",105,87); + ShadowPrint("BACKGROUND MUSIC",105,121); + + fontnumber = 2; + DrawMenu(&SndItems,&SndMenu[0]); + + + DrawAllSoundLights(SndItems.curpos); + + VW_UpdateScreen(); + +} + +//-------------------------------------------------------------------------- +// DrawAllSoundLights() +//-------------------------------------------------------------------------- +void DrawAllSoundLights(int which) +{ + short i; + unsigned Shape; + + for (i=0;i=0 && SaveGamesAvail[which]) + { + ShootSnd(); + name[7]=which+'0'; + + MakeDestPath(name); + handle=open(tempPath,O_RDONLY | O_BINARY); + + DrawLSAction(0); + + if (!LoadTheGame(handle)) + { + exit = StartGame = loadedgame = 0; + LS_current = -1; // Clean up + goto restart; + } + close(handle); + + loadedgame = StartGame= true; + ShootSnd(); + // + // CHANGE "READ THIS!" TO NORMAL COLOR + // + MainMenu[MM_READ_THIS].active=AT_ENABLED; + exit=1; + break; + } + + } while(which>=0); + + if (which==-1) + MenuFadeOut(); + + if (loadedgame) + refresh_screen=false; + + return exit; +} + +/////////////////////////////////// +// +// HIGHLIGHT CURRENT SELECTED ENTRY +// +void TrackWhichGame(int w) +{ + static int lastgameon=0; + + PrintLSEntry(lastgameon,ENABLED_TEXT_COLOR); + PrintLSEntry(w,HIGHLIGHT_TEXT_COLOR); + + lastgameon=w; +} + +//-------------------------------------------------------------------------- +// DRAW THE LOAD/SAVE SCREEN +//-------------------------------------------------------------------------- +void DrawLoadSaveScreen(int loadsave) +{ + #define DISKX 100 + #define DISKY 0 + + int i; + + CA_CacheScreen (BACKGROUND_SCREENPIC); + ClearMScreen(); + + fontnumber=1; + + if (!loadsave) + DrawMenuTitle("Load Mission"); + else + DrawMenuTitle("Save Mission"); + + DrawInstructions(IT_STANDARD); + + for (i=0;i<10;i++) + PrintLSEntry(i,ENABLED_TEXT_COLOR); + + fontnumber = 4; + DrawMenu(&LSItems,&LSMenu[0]); + + VW_UpdateScreen(); + MenuFadeIn(); + WaitKeyUp(); +} + +//-------------------------------------------------------------------------- +// PRINT LOAD/SAVE GAME ENTRY W/BOX OUTLINE +//-------------------------------------------------------------------------- +void PrintLSEntry(int w,int color) +{ + char buff[4]; + SETFONTCOLOR(color,BKGDCOLOR); + DrawOutline(LSM_X+LSItems.indent,LSM_Y+w*LSItems.y_spacing-2,LSM_W-LSItems.indent,8,color,color); + + fontnumber=2; + + PrintX=LSM_X+LSItems.indent+2; + PrintY=LSM_Y+w*LSItems.y_spacing; + + if (SaveGamesAvail[w]) + US_Print(SaveGameNames[w]); + else + US_Print(" ----- EMPTY -----"); + + fontnumber=1; +} + +//-------------------------------------------------------------------------- +// SAVE CURRENT GAME +//-------------------------------------------------------------------------- +int CP_SaveGame(int quick) +{ + + int handle,which,exit=0; + unsigned nwritten; + char name[13],input[GAME_DESCRIPTION_LEN+1]; + boolean temp_caps = allcaps; + US_CursorStruct TermCursor = {'@',0,HIGHLIGHT_TEXT_COLOR,2}; + + _fstrcpy(name,SaveName); + + allcaps = true; + use_custom_cursor = true; + US_CustomCursor = TermCursor; + + // + // QUICKSAVE? + // + if (quick) + { + which=LSItems.curpos; + + if (SaveGamesAvail[which]) + { + DrawLSAction(1); // Testing... + name[7]=which+'0'; + unlink(name); + _fmode=O_BINARY; + MakeDestPath(name); + handle=creat(tempPath,S_IREAD|S_IWRITE); + + lseek(handle,0,SEEK_SET); + SaveTheGame(handle,&SaveGameNames[which][0]); + close(handle); + + return 1; + } + } + + DrawLoadSaveScreen(1); + + do + { + which=HandleMenu(&LSItems,&LSMenu[0],TrackWhichGame); + if (which>=0) + { + // + // OVERWRITE EXISTING SAVEGAME? + // + if (SaveGamesAvail[which]) + if (!Confirm(GAMESVD)) + { + DrawLoadSaveScreen(1); + continue; + } + else + { + DrawLoadSaveScreen(1); + PrintLSEntry(which,HIGHLIGHT_TEXT_COLOR); + VW_UpdateScreen(); + } + + ShootSnd(); + + _fstrcpy(input,&SaveGameNames[which][0]); + name[7]=which+'0'; + + fontnumber=2; + VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*LSItems.y_spacing-1,LSM_W-LSItems.indent-1,7,HIGHLIGHT_BOX_COLOR); + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,HIGHLIGHT_BOX_COLOR); + VW_UpdateScreen(); + + + if (US_LineInput(LSM_X+LSItems.indent+2,LSM_Y+which*LSItems.y_spacing,input,input,true,GAME_DESCRIPTION_LEN,LSM_W-LSItems.indent-10)) + { + SaveGamesAvail[which]=1; + _fstrcpy(&SaveGameNames[which][0],input); + + unlink(name); + _fmode=O_BINARY; + MakeDestPath(name); + handle=creat(tempPath,S_IREAD|S_IWRITE); + lseek(handle,0,SEEK_SET); + + DrawLSAction(1); + SaveTheGame(handle,input); + + close(handle); + + ShootSnd(); + exit=1; + } + else + { + VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*LSItems.y_spacing-1,LSM_W-LSItems.indent-1,7,TERM_BACK_COLOR); + PrintLSEntry(which,HIGHLIGHT_TEXT_COLOR); + VW_UpdateScreen(); + SD_PlaySound(ESCPRESSEDSND); + continue; + } + + fontnumber=1; + break; + } + + } while(which>=0); + + MenuFadeOut(); + use_custom_cursor = false; + allcaps = temp_caps; + return exit; +} + +//-------------------------------------------------------------------------- +// EXIT OPTIONS +//-------------------------------------------------------------------------- +void CP_ExitOptions(void) +{ + StartGame=1; +} + +//-------------------------------------------------------------------------- +// DEFINE CONTROLS +//-------------------------------------------------------------------------- +void CP_Control(void) +{ + + enum {MOUSEENABLE,JOYENABLE,USEPORT2,PADENABLE,CALIBRATEJOY,MOUSESENS,CUSTOMIZE}; + + int i,which; + + CA_CacheScreen (BACKGROUND_SCREENPIC); + + 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) + CalibrateJoystick(); + DrawCtlScreen(); + CusItems.curpos=-1; + ShootSnd(); + break; + + case USEPORT2: + joystickport^=1; + DrawCtlScreen(); + ShootSnd(); + break; + + case PADENABLE: + joypadenabled^=1; + DrawCtlScreen(); + ShootSnd(); + break; + + case CALIBRATEJOY: + CalibrateJoystick(); + DrawCtlScreen(); + break; + + + case MOUSESENS: + case CUSTOMIZE: + DrawCtlScreen(); + MenuFadeIn(); + WaitKeyUp(); + break; + } + } while(which>=0); + + MenuFadeOut(); +} + +//-------------------------------------------------------------------------- +// DRAW MOUSE SENSITIVITY SCREEN +//-------------------------------------------------------------------------- +void DrawMousePos(void) +{ + VWB_Bar(74,92,160,8,HIGHLIGHT_BOX_COLOR); + DrawOutline(73,91,161,9,ENABLED_TEXT_COLOR,ENABLED_TEXT_COLOR); + VWB_Bar(74+160/10*mouseadjustment,92,16,8,HIGHLIGHT_TEXT_COLOR); +} + +void DrawMouseSens(void) +{ + ClearMScreen(); + DrawMenuTitle("MOUSE SENSITIVITY"); + DrawInstructions(IT_MOUSE_SEN); + + fontnumber = 4; + + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,TERM_BACK_COLOR); + PrintX=36; + PrintY=91; + US_Print("SLOW"); + PrintX=242; + US_Print("FAST"); + + DrawMousePos(); + + VW_UpdateScreen(); + MenuFadeIn(); +} + +//-------------------------------------------------------------------------- +// CalibrateJoystick() +//-------------------------------------------------------------------------- +void CalibrateJoystick(void) +{ + word minx,maxx,miny,maxy; + + CacheMessage(CALJOY1_TEXT); + VW_UpdateScreen(); + + while (IN_GetJoyButtonsDB(joystickport)); + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joystickport)); + if (LastScan == sc_Escape) + return; + + IN_GetJoyAbs(joystickport,&minx,&miny); + while (IN_GetJoyButtonsDB(joystickport)); + + CacheMessage(CALJOY2_TEXT); + VW_UpdateScreen(); + + while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joystickport)); + if (LastScan == sc_Escape) + return; + + IN_GetJoyAbs(joystickport,&maxx,&maxy); + if ((minx == maxx) || (miny == maxy)) + return; + + IN_SetupJoy(joystickport,minx,maxx,miny,maxy); + while (IN_GetJoyButtonsDB(joystickport)); + + IN_ClearKeysDown(); + JoystickCalibrated = true; +} + + +//-------------------------------------------------------------------------- +// 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--; + DrawMousePos(); + VW_UpdateScreen(); + SD_PlaySound(MOVEGUN1SND); + while(Keyboard[sc_LeftArrow]); + WaitKeyUp(); + } + break; + + case dir_South: + case dir_East: + if (mouseadjustment<9) + { + mouseadjustment++; + DrawMousePos(); + VW_UpdateScreen(); + SD_PlaySound(MOVEGUN1SND); + while(Keyboard[sc_RightArrow]); + WaitKeyUp(); + } + break; + } + + 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(); +} + +//-------------------------------------------------------------------------- +// DrawCtlScreen() - DRAW CONTROL MENU SCREEN +//-------------------------------------------------------------------------- +void DrawCtlScreen(void) +{ + #define Y_CTL_PIC_OFS (3) + + int i,x,y; + + ClearMScreen(); + DrawMenuTitle("CONTROL"); + DrawInstructions(IT_STANDARD); + + WindowX=0; + WindowW=320; + SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + + if (JoysPresent[0]) + CtlMenu[1].active= + CtlMenu[2].active= + CtlMenu[3].active= + CtlMenu[4].active=AT_ENABLED; + + CtlMenu[2].active=CtlMenu[3].active=CtlMenu[4].active=joystickenabled; + + if (MousePresent) + { + CtlMenu[5].active= + CtlMenu[0].active=AT_ENABLED; + } + + CtlMenu[5].active=mouseenabled; + + fontnumber = 4; + DrawMenu(&CtlItems,&CtlMenu[0]); + + x=CTL_X+CtlItems.indent-24; + y=CTL_Y+Y_CTL_PIC_OFS; + if (mouseenabled) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + y=CTL_Y+9+Y_CTL_PIC_OFS; + if (joystickenabled) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + y=CTL_Y+9*2+Y_CTL_PIC_OFS; + if (joystickport) + VWB_DrawPic(x,y,C_SELECTEDPIC); + else + VWB_DrawPic(x,y,C_NOTSELECTEDPIC); + + y=CTL_Y+9*3+Y_CTL_PIC_OFS; + 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(); +} + +enum {FIRE,STRAFE,RUN,OPEN}; +char mbarray[4][3]={"B0","B1","B2","B3"}, + order[4]={RUN,OPEN,FIRE,STRAFE}; + +//-------------------------------------------------------------------------- +// CustomControls() CUSTOMIZE CONTROLS +//-------------------------------------------------------------------------- +void CustomControls(void) +{ + int which; + + DrawCustomScreen(); + + do + { + which=HandleMenu(&CusItems,&CusMenu[0],FixupCustom); + + switch(which) + { + case 0: + DefineMouseBtns(); + DrawCustMouse(1); + break; + + case 2: + DefineJoyBtns(); + DrawCustJoy(0); + break; + + case 4: + DefineKeyBtns(); + DrawCustKeybd(0); + break; + + case 5: + DefineKeyMove(); + DrawCustKeys(0); + } + } while(which>=0); + + + + MenuFadeOut(); +} + +//-------------------------------------------------------------------------- +// DEFINE THE MOUSE BUTTONS +//-------------------------------------------------------------------------- +void DefineMouseBtns(void) +{ + CustomCtrls mouseallowed={1,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); +} + +//-------------------------------------------------------------------------- +// TestForValidKey +//-------------------------------------------------------------------------- +boolean TestForValidKey(ScanCode Scan) +{ + char far *pos; + +#pragma warn -pia + + if (!(pos = _fmemchr(buttonscan,Scan,sizeof(buttonscan)))) + pos = _fmemchr(dirscan,Scan,sizeof(dirscan)); + +#pragma warn +pia + + if (pos) + { + *pos = sc_None; + SD_PlaySound(SHOOTDOORSND); + DrawCustomScreen(); + } + + return(!(boolean)pos); +} + + +//-------------------------------------------------------------------------- +// EnterCtrlData() - 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; + boolean clean_display = true; + + ShootSnd(); + PrintY=CST_Y+13*index; + IN_ClearKeysDown(); + exit=0; + redraw=1; + + CA_CacheGrChunk(STARTFONT+fontnumber); + + // + // 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; + DrawRtn(1); + + VWB_Bar(x-1,PrintY-1,CST_SPC,7,HIGHLIGHT_BOX_COLOR); + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,HIGHLIGHT_BOX_COLOR); + PrintRtn(which); + PrintX=x; + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,TERM_BACK_COLOR); + 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(HIGHLIGHT_TEXT_COLOR,HIGHLIGHT_BOX_COLOR); + + do + { + int button,result=0; + + if (type==KEYBOARDBTNS||type==KEYBOARDMOVE) + IN_ClearKeysDown(); + + // + // FLASH CURSOR + // + + if (TimeCount>10) + { + switch(tick) + { + case 0: + VWB_Bar(x-1,PrintY-1,CST_SPC,7,HIGHLIGHT_BOX_COLOR); + 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); + clean_display = false; + } + 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); + clean_display = false; + } + break; + + case KEYBOARDBTNS: + if (LastScan) + { + if (LastScan == sc_Escape) + break; + + if (_fmemchr(special_keys,LastScan,sizeof(special_keys))) + SD_PlaySound(NOWAYSND); + else + { +#pragma warn -pia + if (clean_display = TestForValidKey(LastScan)) + ShootSnd(); +#pragma warn +pia + + buttonscan[order[which]]=LastScan; + picked=1; + } + IN_ClearKeysDown(); + } + break; + + + case KEYBOARDMOVE: + if (LastScan) + { + if (LastScan == sc_Escape) + break; + + if (_fmemchr(special_keys,LastScan,sizeof(special_keys))) + SD_PlaySound(NOWAYSND); + else + { +#pragma warn -pia + if (clean_display = TestForValidKey(LastScan)) + ShootSnd(); +#pragma warn +pia + + dirscan[moveorder[which]]=LastScan; + picked=1; + } + IN_ClearKeysDown(); + } + break; + } + + + // + // EXIT INPUT? + // + + if (IN_KeyDown(sc_Escape)) + { + picked=1; + continue; + } + + } while(!picked); + + if (!clean_display) + DrawCustomScreen(); + + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,TERM_BACK_COLOR); + redraw=1; + WaitKeyUp(); + continue; + } + + if (ci.button1 || IN_KeyDown(sc_Escape)) + exit=1; + + // + // MOVE TO ANOTHER SPOT? + // + switch(ci.dir) + { + + case dir_West: + VWB_Bar(x-1,PrintY-1,CST_SPC,7,TERM_BACK_COLOR); + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,TERM_BACK_COLOR); + PrintRtn(which); + 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: + VWB_Bar(x-1,PrintY-1,CST_SPC,7,TERM_BACK_COLOR); + SETFONTCOLOR(HIGHLIGHT_TEXT_COLOR,TERM_BACK_COLOR); + PrintRtn(which); + 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); + + FREEFONT(STARTFONT+fontnumber); + + SD_PlaySound(ESCPRESSEDSND); + WaitKeyUp(); +} + + +//-------------------------------------------------------------------------- +// FixupCustom() - FIXUP GUN CURSOR OVERDRAW SHIT +//-------------------------------------------------------------------------- +void FixupCustom(int w) +{ + static int lastwhich=-1; + + switch(w) + { + case 0: DrawCustMouse(1); break; + case 2: DrawCustJoy(1); break; + case 4: DrawCustKeybd(1); break; + case 5: DrawCustKeys(1); + } + + + if (lastwhich>=0) + { + if (lastwhich!=w) + switch(lastwhich) + { + case 0: DrawCustMouse(0); break; + case 2: DrawCustJoy(0); break; + case 4: DrawCustKeybd(0); break; + case 5: DrawCustKeys(0); + } + } + + lastwhich=w; +} + + +//--------------------------------------------------------------------------- +// DrawCustomScreen() - DRAW CUSTOMIZE SCREEN +//--------------------------------------------------------------------------- +void DrawCustomScreen(void) +{ + int i; + + ClearMScreen(); + DrawMenuTitle("CUSTOMIZE"); + DrawInstructions(IT_STANDARD); + + // + // MOUSE + // + + WindowX=32; + WindowW=244; + + fontnumber = 4; + + SETFONTCOLOR(0x0C,TERM_BACK_COLOR); + + + PrintY=49; + US_CPrint("MOUSE\n"); + PrintY=79; + US_CPrint("JOYSTICK/GRAVIS GAMEPAD\n"); + PrintY=109; + US_CPrint("KEYBOARD\n"); + + fontnumber = 2; + + SETFONTCOLOR(DISABLED_TEXT_COLOR,TERM_BACK_COLOR); + + for (i=60;i<=120;i+=30) + { + ShadowPrint("RUN",CST_START,i); + ShadowPrint("OPEN",CST_START+CST_SPC*1,i); + ShadowPrint("FIRE",CST_START+CST_SPC*2,i); + ShadowPrint("STRAFE",CST_START+CST_SPC*3,i); + } + + ShadowPrint("LEFT",CST_START,135); + ShadowPrint("RIGHT",CST_START+CST_SPC*1,135); + ShadowPrint("FWRD",CST_START+CST_SPC*2,135); + ShadowPrint("BKWRD",CST_START+CST_SPC*3,135); + + + DrawCustMouse(0); + DrawCustJoy(0); + DrawCustKeybd(0); + DrawCustKeys(0); + + // + // PICK STARTING POINT IN MENU + // + if (CusItems.curpos<0) + for (i=0;i20) + newview=20; + ShowViewSize(newview); + VW_UpdateScreen(); + if (newview != lastview) + SD_PlaySound(HITWALLSND); + TicDelay(10); + lastview=newview; + break; + } + + 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); + + ControlPanelFree(); + + if (oldview!=newview) + { + SD_PlaySound (SHOOTSND); + Message(Computing); + NewViewSize(newview); + } + + ControlPanelAlloc(); + + ShootSnd(); + MenuFadeOut(); +} + +//--------------------------------------------------------------------------- +// DrawChangeView() +//--------------------------------------------------------------------------- +void DrawChangeView(int view) +{ + DrawTopInfo(sp_changeview); + ShowViewSize(view); + + fontnumber = 1; + CA_CacheGrChunk(STARTFONT+1); + CacheBMAmsg(CHANGEVIEW_TEXT); + FREEFONT(STARTFONT+1); + + VW_UpdateScreen(); + + MenuFadeIn(); +} + +//--------------------------------------------------------------------------- +// CP_Quit() - QUIT THIS INFERNAL GAME! +//--------------------------------------------------------------------------- +void CP_Quit(void) +{ + if (Confirm(QuitToDosStr)) + ExitGame(); + + DrawMainMenu(); +} + + +//////////////////////////////////////////////////////////////////// +// +// SUPPORT ROUTINES +// +//////////////////////////////////////////////////////////////////// + + + +//--------------------------------------------------------------------------- +// Clear Menu screens to dark red +//--------------------------------------------------------------------------- +void ClearMScreen(void) +{ + VWB_Bar(SCREEN_X,SCREEN_Y,SCREEN_W,SCREEN_H,TERM_BACK_COLOR); +} + + + +//--------------------------------------------------------------------------- +// Un/Cache a LUMP of graphics +//--------------------------------------------------------------------------- +void CacheLump(int lumpstart,int lumpend) +{ + int i; + + for (i=lumpstart;i<=lumpend;i++) + CA_CacheGrChunk(i); +} + + + +//--------------------------------------------------------------------------- +// UnCacheLump +//--------------------------------------------------------------------------- +void UnCacheLump(int lumpstart,int lumpend) +{ + int i; + + for (i=lumpstart;i<=lumpend;i++) + FREEFONT(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); +} + + + +//--------------------------------------------------------------------------- +// DrawOutline +//--------------------------------------------------------------------------- +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); +} + + +//--------------------------------------------------------------------------- +// SetupControlPanel() - Setup Control Panel stuff - graphics, etc. +//--------------------------------------------------------------------------- +void SetupControlPanel(void) +{ + + // + // CACHE GRAPHICS & SOUNDS + // +// CA_CacheScreen (BACKGROUND_SCREENPIC); + + ControlPanelAlloc(); + +// SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR); + fontnumber=2; + + WindowH=200; + + if (!ingame) + CA_LoadAllSounds(); + else + MainMenu[MM_SAVE_MISSION].active=AT_ENABLED; + + ReadGameNames(); + + // + // CENTER MOUSE + // + + _CX=_DX=CENTER; + Mouse(4); + +} + + +//--------------------------------------------------------------------------- +// ReadGameNames() +//--------------------------------------------------------------------------- +void ReadGameNames() +{ + struct ffblk f; + char name[13]; + int which; + +// SEE WHICH SAVE GAME FILES ARE AVAILABLE & READ STRING IN +// + _fstrcpy(name,SaveName); + MakeDestPath(name); + if (!findfirst(tempPath,&f,0)) + do + { + which=f.ff_name[7]-'0'; + if (which<10) + { + int handle; + char temp[GAME_DESCRIPTION_LEN+1]; + + SaveGamesAvail[which]=1; + MakeDestPath(f.ff_name); + handle=open(tempPath,O_RDONLY | O_BINARY); + if (FindChunk(handle,"DESC")) + { + read(handle,temp,GAME_DESCRIPTION_LEN+1); + _fstrcpy(&SaveGameNames[which][0],temp); + } + else + _fstrcpy(&SaveGameNames[which][0],"DESCRIPTION LOST"); + close(handle); + } + } while(!findnext(&f)); +} + +//--------------------------------------------------------------------------- +// CleanupControlPanel() - Clean up all the Control Panel stuff +//--------------------------------------------------------------------------- +void CleanupControlPanel(void) +{ + if (!loadedgame) + FreeMusic(); + ControlPanelFree(); + fontnumber = 4; +} + +//--------------------------------------------------------------------------- +// ControlPanelFree() - This FREES the control panel lump from memory +// and REALLOCS the ScaledDirectory +//--------------------------------------------------------------------------- +void ControlPanelFree(void) +{ + UnCacheLump(CONTROLS_LUMP_START,CONTROLS_LUMP_END); + NewViewSize(viewsize); +} + +//--------------------------------------------------------------------------- +// ControlPanelAlloc() - This CACHEs the control panel lump into memory +// and FREEs the ScaledDirectory. +//--------------------------------------------------------------------------- +void ControlPanelAlloc(void) +{ + CacheLump(CONTROLS_LUMP_START,CONTROLS_LUMP_END); +} + +//--------------------------------------------------------------------------- +// ShadowPrint() - Shadow Prints given text @ a given x & y in default font +// +// NOTE: Font MUST already be loaded +//--------------------------------------------------------------------------- +void ShadowPrint(char far *strng,int x, int y) +{ + int old_bc,old_fc; + + old_fc = fontcolor; + old_bc = backcolor; + + PrintX = x+1; + PrintY = y+1; + + SETFONTCOLOR(TERM_SHADOW_COLOR,TERM_BACK_COLOR); + US_Print(strng); + + PrintX = x; + PrintY = y; + SETFONTCOLOR(old_fc,old_bc); + US_Print(strng); +} + +//--------------------------------------------------------------------------- +// HandleMenu() - Handle moving gun around a menu +//--------------------------------------------------------------------------- +int HandleMenu(CP_iteminfo far *item_i,CP_itemtype far *items,void (*routine)(int w)) +{ + #define box_on item_i->cursor.on + char key; + static int redrawitem=1,lastitem=-1; + int i,x,y,basey,exit,which,flash_tics; + ControlInfo ci; + + which=item_i->curpos; + x=item_i->x; + basey=item_i->y; + y=basey+which*item_i->y_spacing; + box_on = 1; + DrawGun(item_i,items,x,&y,which,basey,routine); + + SetTextColor(items+which,1); + + if (redrawitem) + { + ShadowPrint((items+which)->string,item_i->x+item_i->indent,item_i->y+which*item_i->y_spacing); + } + + // + // CALL CUSTOM ROUTINE IF IT IS NEEDED + // + + if (routine) + routine(which); + + VW_UpdateScreen(); + + flash_tics=40; + exit=0; + TimeCount=0; + IN_ClearKeysDown(); + + do + { + CalcTics(); + flash_tics -= tics; + + CycleColors(); + + // + // CHANGE GUN SHAPE + // + + if (flash_tics <= 0) + { + flash_tics = 40; + + box_on ^= 1; + + if (box_on) + DrawGun(item_i,items,x,&y,which,basey,routine); + else + { + EraseGun(item_i,items,x,y,which); + if (routine) + routine(which); + } + + + VW_UpdateScreen(); + } + + CheckPause(); + + + // + // SEE IF ANY KEYS ARE PRESSED FOR INITIAL CHAR FINDING + // + + key=LastASCII; + if (key) + { + int ok=0; + + 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; + item_i->curpos=which; // jtr -testing + box_on = 1; + DrawGun(item_i,items,x,&y,which,basey,routine); + VW_UpdateScreen(); + + 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; + item_i->curpos=which; // jtr -testing + box_on = 1; + DrawGun(item_i,items,x,&y,which,basey,routine); + VW_UpdateScreen(); + + IN_ClearKeysDown(); + break; + } + } + } + + // + // GET INPUT + // + + ReadAnyControl(&ci); + + switch(ci.dir) + { + //------------------------ + // MOVE UP + // + case dir_North: + EraseGun(item_i,items,x,y,which); + + do + { + if (!which) + which=item_i->amount-1; + else + which--; + + } while(!(items+which)->active); + + item_i->curpos=which; // jtr -testing + + box_on = 1; + DrawGun(item_i,items,x,&y,which,basey,routine); + + VW_UpdateScreen(); + + TicDelay(20); + break; + + //-------------------------- + // MOVE DOWN + // + case dir_South: + EraseGun(item_i,items,x,y,which); + + do + { + if (which==item_i->amount-1) + which=0; + else + which++; + } while(!(items+which)->active); + + item_i->curpos=which; // jtr -testing + + box_on = 1; + DrawGun(item_i,items,x,&y,which,basey,routine); + + VW_UpdateScreen(); + + 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) +// { + box_on = 0; + redrawitem=1; + EraseGun(item_i,items,x,y,which); +// } +// 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) + { + // Make sure there's room to save when CP_SaveGame() is called. + // + if ((long)((items+which)->routine)==(long)(CP_SaveGame)) + if (!CheckDiskSpace(DISK_SPACE_NEEDED,CANT_SAVE_GAME_TXT,cds_menu_print)) + return(which); + + // + // ALREADY IN A GAME? + // + if (ingame && ((items+which)->routine == CP_NewGame)) + if (!Confirm(CURGAME)) + { + MenuFadeOut(); + return 0; + } + + ShootSnd(); + MenuFadeOut(); + (items+which)->routine(0); + } + return which; + + case 2: + SD_PlaySound(ESCPRESSEDSND); + return -1; + } + + return 0; // JUST TO SHUT UP THE ERROR MESSAGES! +} + +//--------------------------------------------------------------------------- +// EraseGun() - ERASE GUN & DE-HIGHLIGHT STRING +//--------------------------------------------------------------------------- +void EraseGun(CP_iteminfo far *item_i,CP_itemtype far *items,int x,int y,int which) +{ + VWB_Bar(item_i->cursor.x,y+item_i->cursor.y_ofs,item_i->cursor.width,item_i->cursor.height,TERM_BACK_COLOR); + SetTextColor(items+which,0); + + ShadowPrint((items+which)->string,item_i->x+item_i->indent,y); + +// VW_UpdateScreen(); + + x++; // Shut up compiler +} + + +//--------------------------------------------------------------------------- +// DrawGun() - DRAW GUN AT NEW POSITION +//--------------------------------------------------------------------------- +void DrawGun(CP_iteminfo far *item_i,CP_itemtype far *items,int x,int *y,int which,int basey,void (*routine)(int w)) +{ + *y=basey+which*item_i->y_spacing; + + VWB_Bar(item_i->cursor.x,*y+item_i->cursor.y_ofs,item_i->cursor.width,item_i->cursor.height,HIGHLIGHT_BOX_COLOR); + SetTextColor(items+which,1); + + ShadowPrint((items+which)->string,item_i->x+item_i->indent,item_i->y+which*item_i->y_spacing); + + // + // CALL CUSTOM ROUTINE IF IT IS NEEDED + // + + if (routine) + routine(which); + +// VW_UpdateScreen(); +// SD_PlaySound(MOVEGUN2SND); + + x++; // Shutup compiler +} + +//--------------------------------------------------------------------------- +// TicDelay() - 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); + ShadowPrint((items+i)->string,WindowX,item_i->y+i*item_i->y_spacing); + } +} + +//--------------------------------------------------------------------------- +// SetTextColor() - SET TEXT COLOR (HIGHLIGHT OR NO) +//--------------------------------------------------------------------------- +void SetTextColor(CP_itemtype far *items,int hlight) +{ + if (hlight) + { + SETFONTCOLOR(color_hlite[items->active],TERM_BACK_COLOR); + } + else + { + SETFONTCOLOR(color_norml[items->active],TERM_BACK_COLOR); + } +} + +//--------------------------------------------------------------------------- +// WaitKeyUp() - 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]); +} + +//--------------------------------------------------------------------------- +// ReadAnyControl() - READ KEYBOARD, JOYSTICK AND MOUSE FOR INPUT +//--------------------------------------------------------------------------- +void ReadAnyControl(ControlInfo *ci) +{ + int mouseactive=0; + + IN_ReadControl(0,ci); + + // + // UNDO some of the ControlInfo vars that were init + // with IN_ReadControl() for the mouse... + // + if (ControlTypeUsed == ctrl_Mouse) + { + // + // Clear directions & buttons (if enabled or not) + // + ci->dir = dir_None; + ci->button0 = ci->button1 = ci->button2 = ci->button3 = 0; + } + + 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); + +// Next two lines needed for flashing cursor ... +// + SETFONTCOLOR(BORDER_TEXT_COLOR,BORDER_MED_COLOR); + CA_CacheGrChunk(STARTFONT+fontnumber); + + IN_ClearKeysDown(); + + // + // BLINK CURSOR + // + x=PrintX; + y=PrintY; + TimeCount=0; + do + { + if (TimeCount>=10) + { + switch(tick) + { + case 0: + VWB_Bar(x,y,8,13,BORDER_MED_COLOR); + break; + + case 1: + PrintX=x; + PrintY=y; + US_Print("_"); + } + + VW_UpdateScreen(); + tick^=1; + TimeCount=0; + } + } while(!Keyboard[sc_Y] && !Keyboard[sc_N] && !Keyboard[sc_Escape]); + + + if (Keyboard[sc_Y]) + { + xit=1; + ShootSnd(); + } + + while(Keyboard[sc_Y] || Keyboard[sc_N] || Keyboard[sc_Escape]); + + IN_ClearKeysDown(); + SD_PlaySound(whichsnd[xit]); + + FREEFONT(STARTFONT+fontnumber); + + return xit; +} + +//--------------------------------------------------------------------------- +// Message() - 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; + unsigned OldPrintX,OldPrintY; + + fontnumber=1; + CA_CacheGrChunk(STARTFONT+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; + + OldPrintY = PrintY=(WindowH/2)-h/2; + OldPrintX = PrintX=WindowX=160-mw/2; + + // bump down and to right for shadow + + PrintX++; + PrintY++; + WindowX++; + + BevelBox(WindowX-6,PrintY-6,mw+10,h+10,BORDER_HI_COLOR,BORDER_MED_COLOR,BORDER_LO_COLOR); + + SETFONTCOLOR(BORDER_LO_COLOR,BORDER_MED_COLOR); + US_Print(string); + + PrintY=OldPrintY; + WindowX=PrintX=OldPrintX; + + SETFONTCOLOR(BORDER_TEXT_COLOR,BORDER_MED_COLOR); + US_Print(string); + + FREEFONT(STARTFONT+1); + + VW_UpdateScreen(); +} + + + +//-------------------------------------------------------------------------- +// TerminateStr - Searches for an "^XX" and replaces with a 0 (NULL) +//-------------------------------------------------------------------------- +void TerminateStr(char far *pos) +{ + pos = _fstrstr(pos,"^XX"); + +#if IN_DEVELOPMENT + if (!pos) + MENU_ERROR(CACHE_MESSAGE_NO_END_MARKER); +#endif + + *pos = 0; +} + +//--------------------------------------------------------------------------- +// CacheMessage() - Caches and prints a message in a window. +//--------------------------------------------------------------------------- +void CacheMessage(unsigned MessageNum) +{ + char far *string; + + CA_CacheGrChunk(MessageNum); + string = MK_FP(grsegs[MessageNum],0); + + TerminateStr(string); + + Message(string); + + FREEFONT(MessageNum); +} + + +//--------------------------------------------------------------------------- +// CacheCompData() - Caches and Decompresses data from the VGAGRAPH +// +// NOTE: - User is responsible for freeing loaded data +// - Returns the size of the data +// - Does not call TerminateStr() for loaded TEXT data +// +// RETURNS: Lenght of loaded (decompressed) data +// +//--------------------------------------------------------------------------- +unsigned long CacheCompData(unsigned ItemNum, memptr *dest_loc) +{ + char far *compdata, far *dest_ptr; + CompHeader_t far *CompHeader; + unsigned long data_len; + + // Load compressed data + + CA_CacheGrChunk(ItemNum); + compdata = MK_FP(grsegs[ItemNum],0); + MM_SetLock (&grsegs[ItemNum], true); + CompHeader = (CompHeader_t far *)compdata; + data_len = CompHeader->OriginalLen; + + compdata+=sizeof(CompHeader_t); + + // Allocate Dest Memory + + MM_GetPtr(dest_loc,data_len); + MM_SetLock (dest_loc, true); + dest_ptr = MK_FP(*dest_loc,0); + + // Decompress and terminate string + + if (!LZH_Startup()) + Quit("out of memory"); + + LZH_Decompress(compdata,dest_ptr,data_len,CompHeader->CompressLen,(SRC_MEM|DEST_MEM)); + LZH_Shutdown(); + + // Free compressed data + + UNCACHEGRCHUNK(ItemNum); + + // Return loaded size + + MM_SetLock (dest_loc, false); + return(data_len); +} + +//------------------------------------------------------------------------- +// CheckForSpecialCode() - Scans the Command Line parameters for +// special code word and returns true if found. +// +// NOTE: - Requires that the MEMORY and CACHE manager be started up. +// - The chunk being checked MUST be JAMPAKd - (this may change) +// +//------------------------------------------------------------------------- +boolean CheckForSpecialCode(unsigned ItemNum) +{ + memptr code; + boolean return_val = false; + char i; + char far *code_ptr; + + // Allocate, Cache & Decomp into ram + + CacheCompData(ItemNum, &code); + code_ptr = MK_FP(code,0); + TerminateStr(code_ptr); + + // Check for code + + for (i=1; i<_argc; i++) + if (!_fmemicmp(_argv[i],code_ptr,_fstrlen(code_ptr))) + return_val = true; + + // free allocated memory + + MM_FreePtr(&code); + + return(return_val); +} + + +//////////////////////////////////////////////////////////////////// +// +// THIS MAY BE FIXED A LITTLE LATER... +// +//////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------------------------- +// StartCPMusic() +//--------------------------------------------------------------------------- +void StartCPMusic(int song) +{ + musicnames chunk; + + if (audiosegs[STARTMUSIC + lastmenumusic]) // JDC + MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + lastmenumusic]); + lastmenumusic = 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]); + } +} + +//--------------------------------------------------------------------------- +// FreeMusic () +//--------------------------------------------------------------------------- +void FreeMusic (void) +{ + SD_MusicOff(); + if (audiosegs[STARTMUSIC + lastmenumusic]) // JDC + MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + lastmenumusic]); +} + + +#ifdef CACHE_KEY_DATA + +//--------------------------------------------------------------------------- +// IN_GetScanName() - Returns a string containing the name of the +// specified scan code +//--------------------------------------------------------------------------- +byte far* IN_GetScanName(ScanCode scan) +{ + byte far *p; + ScanCode far *s; + + for (s = ExtScanCodes,p = ExtScanNames;*s;p+=7,s++) + if (*s == scan) + return((byte far *)p); + + return((byte far *)(ScanNames+(scan<<1))); +} + +#else + +//--------------------------------------------------------------------------- +// IN_GetScanName() - Returns a string containing the name of the +// specified scan code +//--------------------------------------------------------------------------- +byte far* IN_GetScanName(ScanCode scan) +{ + byte far * far *p; + ScanCode far *s; + + for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++) + if (*s == scan) + return(*p); + + return(ScanNames[scan]); +} + +#endif + + +//--------------------------------------------------------------------------- +// CheckPause() - 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; + } +} + +//------------------------------------------------------------------------- +// DrawMenuGun() - DRAW GUN CURSOR AT CORRECT POSITION IN MENU +//------------------------------------------------------------------------- +void DrawMenuGun(CP_iteminfo far *iteminfo) +{ + int x,y; + + x=iteminfo->cursor.x; + y=iteminfo->y+iteminfo->curpos*iteminfo->y_spacing+iteminfo->cursor.y_ofs; + + VWB_Bar(x,y,iteminfo->cursor.width,iteminfo->cursor.height,HIGHLIGHT_BOX_COLOR); +} + +//------------------------------------------------------------------------- +// ShootSnd() +//------------------------------------------------------------------------- +void ShootSnd(void) +{ + SD_PlaySound(SHOOTSND); +} + +#if GAME_VERSION == SHAREWARE_VERSION + + +//------------------------------------------------------------------------- +// ShowPromo() +//------------------------------------------------------------------------- +void ShowPromo() +{ + #define PROMO_MUSIC HIDINGA_MUS + +// Load and start music +// + CA_CacheAudioChunk(STARTMUSIC+PROMO_MUSIC); + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC+PROMO_MUSIC]); + +// Show promo screen 1 +// + MenuFadeOut(); + CA_CacheScreen(PROMO1PIC); + VW_UpdateScreen(); + MenuFadeIn(); + IN_UserInput(TickBase*20); + +// Show promo screen 2 +// + MenuFadeOut(); + CA_CacheScreen(PROMO2PIC); + VW_UpdateScreen(); + MenuFadeIn(); + IN_UserInput(TickBase*20); + +// Music off and freed! +// + StopMusic(); +} + +#endif + +//------------------------------------------------------------------------- +// ExitGame() +//------------------------------------------------------------------------- +void ExitGame() +{ + int i; + + VW_FadeOut(); +#if GAME_VERSION == SHAREWARE_VERSION + ShowPromo(); +#endif + + SD_MusicOff(); + SD_StopSound(); + +// SHUT-UP THE ADLIB +// + for (i=1;i<=0xf5;i++) + alOut(i,0); + Quit(NULL); +} diff --git a/3D_MENU.H b/3D_MENU.H new file mode 100644 index 0000000..27af2b8 --- /dev/null +++ b/3D_MENU.H @@ -0,0 +1,301 @@ +// +// WL_MENU.H +// +#include "id_heads.h" + +#define GAME_DESCRIPTION_LEN 31 + + +// +// Menu Color Defines +// + +#define HIGHLIGHT_BOX_COLOR 0x52 // Box behind text for cursor +#define HIGHLIGHT_TEXT_COLOR 0x59 // Text color for text on cursor +#define HIGHLIGHT_DISABLED_COLOR 0x56 // Text color for text on cursor for a turned off item +#define HIGHLIGHT_DEACTIAVED_COLOR 0x55 + + +#define ENABLED_TEXT_COLOR 0x56 +#define DISABLED_TEXT_COLOR 0x53 +#define DEACTIAVED_TEXT_COLOR 0x52 + +#define INSTRUCTIONS_TEXT_COLOR 0x53 + +#define TERM_BACK_COLOR 0x02 +#define TERM_SHADOW_COLOR 0x01 + +// +// Clearable Menu Terminal Area +// +#define SCREEN_X 32 +#define SCREEN_Y 28 +#define SCREEN_W 244 +#define SCREEN_H 132 + + +#define BORDCOLOR (0x78) +#define BORD2COLOR (0x74) +#define DEACTIVE (0x72) +#define BKGDCOLOR (0x76) +#define STRIPE 0x2c + +#define MenuFadeOut() VL_FadeOut(0,255,40,44,44,10) + + +#define READCOLOR 0x4a +#define READHCOLOR 0x47 +#define VIEWCOLOR 0x7f +#define TEXTCOLOR WHITE +#define HIGHLIGHT 0x13 + +#define MenuFadeIn() VL_FadeIn(0,255,&vgapal,10) + +#define MENUSONG LASTLAFF_MUS +#define ROSTER_MUS HISCORE_MUS +#define TEXTSONG TOHELL_MUS + +#define QUITSUR "Are you sure you want\n"\ + "to quit this great game? (Y/N)" + +#define CURGAME " Continuing past this\n"\ + " point will end the game\n"\ + " you're currently playing.\n"\ + "\n"\ + " Start a NEW game? (Y/N)" + +#if 0 +#define CURGAME "You are currently in\n"\ + "a game. Continuing will\n"\ + "erase the old game.\n"\ + "Start a NEW game? (Y/N)" +#endif + +#define GAMESVD "There's already a game\n"\ + "saved at this position.\n"\ + "\n"\ + " Overwrite? (Y/N)" + + + +#define SENSITIVE 60 +#define CENTER SENSITIVE*2 + +#define MENU_X 111 +#define MENU_Y 50 + +#define SM_X 121 +#define SM_Y 64 +#define SM_W 54 + +#define CTL_X 100 +#define CTL_Y 70 + +#define LSM_X 85 +#define LSM_Y 55 +#define LSM_W 144 +#define LSM_H 10*13+10 + +#define NM_X 71 +#define NM_Y 66 + +#define NE_X 58 +#define NE_Y 54 + +#define CST_X 77 +#define CST_Y 60 + +#define CST_START 77 +#define CST_SPC 41 + +#define LSA_X 96 +#define LSA_Y 80 +#define LSA_W 130 +#define LSA_H 42 + +typedef enum mm_labels {MM_NEW_MISSION, + MM_ORDERING_INFO, + MM_READ_THIS, + MM_BLAKE_STONE_SAGA, + MM_BLANK1, + MM_GAME_OPTIONS, + MM_VIEW_SCORES, + MM_LOAD_MISSION, + MM_SAVE_MISSION, + MM_BLANK2, + MM_BACK_TO_DEMO, + MM_LOGOFF, +} mm_labels; + +// CP_Switch() menu labels +// + +typedef enum +{ + SW_LIGHTING, + SW_REBA_ATTACK_INFO, + SW_CEILING, + SW_FLOORS, + +} sw_labels; + + + +// +// ActiveType flags for menu options (SEE CP_itemtype.active) +// +typedef enum +{ + AT_DISABLED = 0, + AT_ENABLED, + AT_READIT, + AT_NON_SELECTABLE, // Menu Bar/Separator - Not a selectable item. + + +} activetypes; + +// +// TYPEDEFS +// +typedef struct +{ + unsigned char x; + char y_ofs; + unsigned char width; + unsigned char height; + char on; +} CP_cursortype; + +typedef struct +{ + unsigned char x; + unsigned char y; + unsigned char amount; + char curpos; + unsigned char indent; + unsigned char y_spacing; + + CP_cursortype cursor; + +} CP_iteminfo; + + +typedef struct +{ + activetypes active; + char string[36]; + void (* routine)(int temp1); + unsigned char fontnumber; // Font to print text in + unsigned char height; // Hight of text (Y_Offset from previous line) +} CP_itemtype; + + +typedef struct +{ + int allowed[4]; +} CustomCtrls; + +extern CP_itemtype far MainMenu[],far NewEMenu[]; +extern CP_iteminfo far MainItems; + +// +// FUNCTION PROTOTYPES +// +void SetupControlPanel(void); +void CleanupControlPanel(void); +void ControlPanelFree(void); +void ControlPanelAlloc(void); + +void DrawMenu(CP_iteminfo far *item_i,CP_itemtype far *items); +int HandleMenu(CP_iteminfo far *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 DrawGun(CP_iteminfo far *item_i,CP_itemtype far *items,int x,int *y,int which,int basey,void (*routine)(int w)); +void DrawHalfStep(int x,int y,int y_spacing); +void EraseGun(CP_iteminfo far *item_i,CP_itemtype far *items,int x,int y,int which); +void SetTextColor(CP_itemtype far *items,int hlight); +void DrawMenuGun(CP_iteminfo far *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_BlakeStoneSaga(void); +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 DrawMenuTitle(char *title); +void CheckForEpisodes(void); +void HelpPresenter(char *fname,boolean continuekeys, unsigned id_cache, boolean startmusic); +void ShadowPrint(char far *string, int x, int y); + +// +// VARIABLES +// +extern int SaveGamesAvail[10],StartGame,SoundStatus; +extern char far SaveGameNames[10][GAME_DESCRIPTION_LEN+1], far SaveName[13]; + +enum {MOUSE,JOYSTICK,KEYBOARDBTNS,KEYBOARDMOVE}; // FOR INPUT TYPES + +// +// WL_INTER +// +typedef struct { + int kill,secret,treasure; + long time; + } LRstruct; + +extern LRstruct LevelRatios[]; + +void Write (int x,int y,char *string); diff --git a/3D_MSGS.C b/3D_MSGS.C new file mode 100644 index 0000000..5b0cb79 --- /dev/null +++ b/3D_MSGS.C @@ -0,0 +1,607 @@ +#include "3d_def.h" + + + +//--------------------------------------------------------------------------- +// +// FOOD MACHINE MESSAGES +// +//--------------------------------------------------------------------------- + +char far food_msg1[]="\r FOOD UNIT DISPENSES\r" + " SOMETHING EDIBLE.\r" + "\r TOKENS: XX"; + + +//--------------------------------------------------------------------------- +// +// BEVERAGE MACHINE MESSAGES +// +//--------------------------------------------------------------------------- + + +char far bevs_msg1[]="\r FOOD UNIT DISPENSES\r" + " A COLD BEVERAGE.\r" + "\r TOKENS: XX"; + + +//--------------------------------------------------------------------------- +// +// GENERAL HINT MESSAGES +// +//--------------------------------------------------------------------------- + +#ifdef CON_HINTS + +char far genhint_msg1[]="\r\rTERMINALS ACCESS\rALL INFORMATION."; +char far genhint_msg2[]="\r\rALL FLOORS ARE ON-LINE."; +char far genhint_msg3[]="\r\rSOME SCIENTIST\rARE INFORMANTS."; +char far genhint_msg4[]="\r\rELEVATOR CODES ARE\rINFORMATION."; +char far genhint_msg5[]="\r\rTOO MUCH CANDY IS\rBAD FOR YOUR TEETH."; +char far genhint_msg6[]="\r\rINFORMANTS ARE\rEVERYWHERE!"; +char far genhint_msg7[]="\r\rINFORMANTS ARE\rINTERACTIVE."; +char far genhint_msg8[]="\r\rBEWARE OF EXPERIMENTS!"; +char far genhint_msg9[]="\r\rBEWARE OF GOLDFIRE!"; +char far genhint_msg10[]="\r\rTHERE ARE HIDDEN\rROOMS BEHIND PANELS."; +char far genhint_msg11[]="\r\rSOME WALLS ARE PANELS."; +char far genhint_msg12[]="\r\rORDER ALL 6 MISSIONS\rOF BLAKE STONE TODAY!"; +char far genhint_msg13[]="\r\rCALL APOGEE AND REGISTER!"; +char far genhint_msg14[]="\r\rTALK TO SCIENTISTS.\r"; +char far genhint_msg15[]="\r\rSOME SCIENTISTS\rARE INFORMANTS."; +char far genhint_msg16[]="\r\rLEVEL BLUEPRINTS\rARE INFORMATION."; +char far genhint_msg17[]="\r\rUSE TRANSPORTERS WHEN\rAVAILABLE."; + + + + +char far * far ConcessionGenHints[NUM_GEN_HINTS]= +{ + genhint_msg1,genhint_msg2,genhint_msg3,genhint_msg4,genhint_msg5, + genhint_msg6,genhint_msg7,genhint_msg8,genhint_msg9,genhint_msg10, + genhint_msg11,genhint_msg12,genhint_msg13,genhint_msg14,genhint_msg15, + genhint_msg16,genhint_msg17, +}; + +#endif + + +//--------------------------------------------------------------------------- +// +// NO EAT MESSAGES +// +//--------------------------------------------------------------------------- + + +char far noeat_msg1[]="\r\r CAN'T EAT NOW," + "\r NOT HUNGRY."; + + + +//--------------------------------------------------------------------------- +// +// GENERAL MESSAGES +// +//--------------------------------------------------------------------------- + + +char far NoAdLibCard[]="^FC57\r MUSIC:\r" + "^FCA6 YOU DON'T HAVE AN\r" + " ADLIB COMPATABLE\r" + " SOUND CARD."; + + + +char far MusicOn[] = "^FC57\r\r MUSIC:\r" + "^FCA6 BACKGROUND MUSIC\r" + " IS XXXX"; + +char far SoundOn[] = "^FC57\r\r SOUNDS:\r" + "^FCA6 SOUND EFFECTS\r" + " ARE XXXX"; + + +char far ekg_heartbeat_enabled[] = "\r\r EKG HEART BEAT\r" + " SOUND ENABLED."; +char far ekg_heartbeat_disabled[] = "\r\r EKG HEART BEAT\r" + " SOUND DISABLED."; + +char far attacker_info_enabled[] = "\r\rDETAILED ATTACKER INFO\r" + " DISPLAY ENABLED."; +char far attacker_info_disabled[] = "\r\rDETAILED ATTACKER INFO\r" + " DISPLAY DISABLED."; + +char far WeaponNotAvailMsg[] = "\r\r SELECTED WEAPON NOT\r" + " CURRENTLY AVAILABLE."; + +char far WeaponAvailMsg[] = "\r\r SELECTED WEAPON\r" + " ACTIVATED AND READY."; + + +char far RadarEnergyGoneMsg[] = "\r\r RADAR MAGNIFICATION\r" + " ENERGY DEPLETED."; + + +char far EnergyPackDepleted[] = "^FC19\r WARNING:\r" + "^FC17ENERGY PACK DEPLETED\r" + "^FCA6 SWITCHING TO\r" + " AUTOCHARGE PISTOL."; + +char far WeaponMalfunction[] = "^FC19\r WARNING:\r\r" + "^FC17 WEAPON MALFUNCTION!\r"; + +char far NotEnoughEnergyForWeapon[] = "^FC17\r NOT ENOUGH ENERGY\r" + " FOR SELECTED WEAPON\r" + "^FCA6 SWITCHING TO\r" + " AUTOCHARGE PISTOL."; + + +char far SwitchNotActivateMsg[] = "\r\r WALL SWITCH NOT\r" + " OPERATIONAL!!"; + +char far NoFoodTokens[] = "\r\r YOU DON'T HAVE ANY\r" + " FOOD TOKENS!"; + + + + +//--------------------------------------------------------------------------- +// +// FISSION DETONATOR(S) MESSAGES +// +//--------------------------------------------------------------------------- + + +char far pd_dropped[]="^FC19\r WARNING:\r" + "^FCA6 FISSION DETONATOR\r" + " DROPPED!"; + + +char far pd_nomore[]="^FCA6\r\r NO FISSION\r" + " DETONATORS AVAIL."; + + +char far pd_notnear[]= "^SH035^FCA6\r YOU MUST\r" + " FIND THE\r" + " SECURITY\r" + " CUBE."; + + +char far pd_getcloser[]= "^SH035^FCA6\r TRANSPORTER\r" + " SECURITY OUT\r" + " OF RANGE"; + + +char far pd_floorunlocked[]= "^SH035^FCA6\r TRANSPORTER\r" + " SECURITY\r" + " DISABLED."; + + +char far pd_donthaveany[]="^SH0E6^FCA6\r NO FISSION\r" + " DETONATOR\r" + " AVAILABLE."; + + +char far pd_no_computer[]= "^SH035^FCA6\r A SECURITY \r" + " CUBE IS NOT\r" + " LOCATED IN\r" + " THIS SECTOR."; + + +char far pd_floornotlocked[] ="^SH035^FCA6\r TRANSPORTER\r" + " SECURITY\r" + " ALREADY\r" + " DISABLED."; + + +//--------------------------------------------------------------------------- +// +// BONUS MSGS +// +//--------------------------------------------------------------------------- + + +char far bonus_msg1[]="^SH001^FC57\r\r ACCESS CARD:\r" + "^FCA6 RED LEVEL"; + +char far bonus_msg2[]="^SH002^FC57\r\r ACCESS CARD:\r" + "^FCA6 YELLOW LEVEL"; + +char far bonus_msg4[]="^SH004^FC57\r\r ACCESS CARD:\r" + "^FCA6 BLUE LEVEL"; + +char far bonus_msg7[]= "^SH006^FC57\r WEAPON:\r" + "^FCA6 ENERGY PACK\r" + " ( UNITS)"; + +char far bonus_msg8[]="^SH007^FC57\r\r WEAPON:\r" + "^FCA6 SLOW FIRE\r" + " PROTECTOR\r"; + +char far bonus_msg9[]="^SH008^FC57\r\r WEAPON:\r" + "^FCA6 RAPID ASSAULT\r" + " WEAPON"; + +char far bonus_msg10[]= "^SH009^FC57\r\r WEAPON:\r" + "^FCA6 DUAL NEUTRON\r" + " DISRUPTER"; + + +char far bonus_msg13[]="^SH00C^FC57\r\r BONUS:\r" + "^FCA6 MONEY BAG"; + +char far bonus_msg14[]="^SH00D^FC57\r\r BONUS:\r" + "^FCA6 LOOT"; + +char far bonus_msg15[]="^SH00E^FC57\r\r BONUS:\r" + "^FCA6 GOLD BARS"; + +char far bonus_msg16[]="^SH00F^FC57\r\r BONUS:\r" + "^FCA6 XYLAN ORB"; + + +char far bonus_msg21[]="^SH08A^FC57\r WEAPON:\r" + "^FCA6 PLASMA\r" + " DISCHARGE\r" + " UNIT"; + +char far bonus_msg21a[]="^SH0E4^FC57\r\r WEAPON:\r" + "^FCA6 ANTI-PLASMA\r" + " CANNON"; + +char far bonus_msg24[]="^SH020^FC57\r FOOD TOKEN:\r" + "^FCA6 1 CREDIT\r" + "\r TOKENS: XX"; + +char far bonus_msg25[]="^SH021^FC57\r FOOD TOKEN:\r" + "^FCA6 5 CREDITS" + "\r TOKENS: XX"; + +char far bonus_msg12[]="^SH00B^FC57\r\r HEALTH:\r" + "^FCA6 PLASMA BAG"; + +char far bonus_msg11[]="^SH00A^FC57\r\r HEALTH:\r" + "^FCA6 FIRST AID\r" + " KIT"; + +char far bonus_msg17[]="^SH010^FC57\r\r FOOD:\r" + "^FCA6 RAW MEAT"; + +char far bonus_msg18[]="^SH011^FC57\r\r FOOD:\r" + "^FCA6 RAW MEAT"; + +char far bonus_msg23[]="^SH089^FC57\r\r FOOD:\r" + "^FCA6 SANDWICH"; + +char far bonus_msg22[]="^SH088^FC57\r\r FOOD:\r" + "^FCA6 CANDY BAR"; + +char far bonus_msg19[]="^SH012^FC57\r\r FOOD:\r" + "^FCA6 FRESH WATER"; + +char far bonus_msg20[]="^SH013^FC57\r\r FOOD:\r" + "^FCA6 WATER PUDDLE"; + +char far bonus_msg26[]= "^SH0D8^FC57 FISSION\r" + " DETONATOR\r\r" + "^FCA6PRESS TILDE OR\r" + "SPACE TO DROP"; + +char far bonus_msg27[]= "^SH0D9^FC57\r RADAR: \r" + "^FCA6MAGNIFICATION\r" + " ENERGY"; + +char far * far BonusMsg[]= +{ + bonus_msg1,bonus_msg2,bonus_msg4, + bonus_msg7,bonus_msg7,bonus_msg8,bonus_msg9,bonus_msg10,bonus_msg21,bonus_msg21a, + + bonus_msg12,bonus_msg11, + bonus_msg18,bonus_msg17,bonus_msg23,bonus_msg22,bonus_msg19, + + bonus_msg20, + bonus_msg13,bonus_msg14,bonus_msg15, + bonus_msg15,bonus_msg15,bonus_msg15, + bonus_msg16, + + 0,0,0, + bonus_msg24,bonus_msg25, + bonus_msg26,bonus_msg27, +}; + +//--------------------------------------------------------------------------- +// +// ACTOR MSGS (ATTACKING & GEN INFO) +// +//--------------------------------------------------------------------------- + +// Sector Patrol +char far actor_info4[]="^AN04^FC17\r\r ATTACKING:\r" + "^FCA6 SECTOR GUARD"; + +// hang_terrotobj, +char far actor_info5[]="^AN05^FC17\r ATTACKING:\r" + "^FCA6 AUTOMATED\r" + "HEAVY ARMORED\r" + " ROBOT TURRET"; +// Bio-Tech +char far actor_info9[]="^AN09^FC17\r\r ATTACKING:\r" + "^FCA6 BIO-TECH"; + +// podobj, +char far actor_info10[]="^AN0A^FC17\r\r ATTACKING:\r" + "^FCA6 POD ALIEN"; +// electroobj, +char far actor_info11[]="^AN0B^FC17\r ATTACKING:\r" + "^FCA6 HIGH ENERGY\r" + " PLASMA ALIEN"; +// electrosphereobj, +char far actor_info12[]="^AN0C^FC17\r\r ATTACKING:\r" + "^FCA6PLASMA SPHERE"; +// STAR Sentinel +char far actor_info13[]="^AN0D^FC17\r\r ATTACKING:\r" + "^FCA6 TECH WARRIOR"; +// genetic_guardobj, +char far actor_info14[]="^AN0E^FC17\r ATTACKING:\r" + "^FCA6 HIGH-SECURITY\r" + " GENETIC GUARD"; +// mutant_human1obj, +char far actor_info15[]="^AN0F^FC17\r ATTACKING:\r" + "^FCA6 EXPERIMENTAL\r" + " MECH-SENTINEL"; +// mutant_human2obj, +char far actor_info16[]="^AN10^FC17\r ATTACKING:\r" + "^FCA6 EXPERIMENTAL\r" + " MUTANT HUMAN"; + +// lcan_alienobj, +char far actor_info18[]="^AN12^FC17\r ATTACKING:\r" + "^FCA6 EXPERIMENTAL\r" + " GENETIC ALIEN"; +// scan_alienobj, +char far actor_info20[]="^AN14^FC17\r ATTACKING:\r" + "^FCA6 EXPERIMENTAL\r" + " GENETIC ALIEN"; + +// gurneyobj, +char far actor_info22[]="^AN16^FC17\r ATTACKING:\r" + "^FCA6 MUTATED\r" + " GUARD"; + +// Alien Protector (old STAR Trooper) +char far actor_info24[]="^AN18^FC17\r ATTACKING:\r" + "^FCA6 ALIEN\r" + " PROTECTOR"; + +// goldsternobj, +char far actor_info25[]="^AN19^FC17\r\r ATTACKING:\r" + "^FCA6 DR GOLDFIRE"; + +// gold_morphobj, +char far actor_info25m[]="^AN28^FC17\r\r ATTACKING:\r" + "^FCA6 MORPHED\r" + " DR GOLDFIRE"; + +// volatiletransportobj, +char far actor_info27[]="^SH072^FC17\r ATTACKING:\r" + "^FCA6 VOLATILE MAT.\r" + " TRANSPORT\r" + " EXPLOSION"; +// floatingbombobj, +char far actor_info28[]="^SH076^FC17\r ATTACKING:\r" + "^FCA6PERSCAN DRONE\r" + " EXPLOSION"; +// electroshotobj, +char far actor_info31[]="^AN0B^FC17\r ATTACKING:\r" + "^FCA6 HIGH ENERGY\r" + " PLASMA ALIEN"; +// explosionobj, +char far actor_info33[]="^SH08B^FC17\r ATTACKING:\r" + "^FCA6 EXPLOSION\r" + " BLAST"; +// liquidshotobj, +char far actor_info36[]="^AN17^FC17\r\r ATTACKING:\r" + "^FCA6 FLUID ALIEN"; + + +char far actor_info41[]="^SH000^FC17\r ATTACKING:\r" + "^FCA6 STANDING IN\r" + " BIO TOXIC\r" + " WASTE."; + +char far actor_info42[]="^SH08C^FC17\r ATTACKING:\r" + "^FCA6 STANDING IN\r" + " TOXIC SLUDGE."; + + +char far actor_info41a[]="^SH0E2^FC17\r ATTACKING:\r" + "^FCA6 STANDING IN\r" + " TOXIC SLUDGE."; + + +char far actor_info42a[]="^SH0E3^FC17\r ATTACKING:\r" + "^FCA6 STANDING IN\r" + " BIO TOXIC\r" + " WASTE."; + + +char far actor_info43[]="^AN1D^FC17\r ATTACKING:\r" + "^FCA6 ELECTRIC ARC\r" + " BARRIER."; + + +char far actor_info43a[]="^SH0F4^FC17\r ATTACKING:\r" + "^FCA6 POST\r" + " BARRIER."; + +char far actor_info43b[]="^SH0FC^FC17\r ATTACKING:\r" + "^FCA6 SPIKE\r" + " BARRIER."; + + +char far actor_info44[]="^AN1e^FC17\r ATTACKING:\r" + "^FCA6 SPIDER\r" + " MUTANT"; + +char far actor_info45[]="^AN1f^FC17\r ATTACKING:\r" + "^FCA6 BREATHER\r" + " BEAST"; + +char far actor_info46[]="^AN20^FC17\r ATTACKING:\r" + "^FCA6 CYBORG\r" + " WARRIOR"; + +char far actor_info47[]="^AN21^FC17\r ATTACKING:\r" + "^FCA6 REPTILIAN\r" + " WARRIOR"; + +char far actor_info48[]="^AN22^FC17\r\r ATTACKING:\r" + "^FCA6 ACID DRAGON"; + +char far actor_info49[]="^AN23^FC17\r ATTACKING:\r" + "^FCA6 BIO-MECH\r" + " GUARDIAN"; + + +char far actor_info50[]="^SH07A^FC17\r ATTACKING:\r" + "^FCA6 SECURITY\r" + " CUBE\r" + " EXPLOSION"; + +// explosionobj, +char far actor_info51[]="^SH08B^FC17\r ATTACKING:\r" + "^FCA6 ANTI-PLASMA\r" + " EXPLOSION\r" + " BLAST"; +// pd_explosionobj, +char far actor_info52[]="^SH0E6^FC17\r ATTACKING:\r" + "^FCA6 DETONATOR\r" + " EXPLOSION"; + +// Final Boss #1 +char far actor_info53[]="^AN29^FC17\r ATTACKING:\r" + "^FCA6 THE GIANT\r" + " STALKER"; + +// Final Boss #2 +char far actor_info54[]="^AN2A^FC17\r ATTACKING:\r" + "^FCA6 THE SPECTOR\r" + " DEMON"; + +// Final Boss #3 +char far actor_info55[]="^AN2b^FC17\r ATTACKING:\r" + "^FCA6 THE ARMORED\r" + " STALKER"; + +// Final Boss #4 +char far actor_info56[]="^AN2c^FC17\r ATTACKING:\r" + "^FCA6 THE CRAWLER\r" + " BEAST"; + + + + +char far * far ActorInfoMsg[]= +{ +// 0,0, // nothing,player +// 0,0,0, // inert,fixup,dead + + actor_info4, // Sector Patrol + actor_info5, // Turret + actor_info9, // Bio-Tech + actor_info10, // Pod + actor_info11, // Electro-Alien + actor_info12, // Electro-Sphere + actor_info13, // STAR Sentinel + actor_info14, // Genetic Guard + actor_info15, // Mutant Human 1 + actor_info16, // Mutant Human 2 + 0, // lg canister wait + actor_info18, // Lg Canister Alien + 0, // sm canister wait + actor_info20, // Sm canister Alien + 0, // gurney wait + actor_info22, // Gurney Mutant + actor_info36, // Liquid Alien + actor_info24, // Alien Protector (old STAR Trooper) + actor_info25, // Goldstern + actor_info25m, // Goldstern Morphed + actor_info27, // Volatile Transport + actor_info28, // Floating Bomb + + actor_info50, // vital defence + + actor_info44, // Spider Mutant + actor_info45, // breather beast + actor_info46, // cyborg warrior + actor_info47, // reptilian warrior + actor_info48, // acid dragon + actor_info49, // mech guardian + + actor_info53, // Final Boss 1 + actor_info54, // Final Boss 2 + actor_info55, // Final Boss 3 + actor_info56, // Final Boss 4 + + 0,0,0,0, // blake,crate 1, crate 2, crate 3, + + actor_info41, // Green Ooze + actor_info42, // Black Ooze + actor_info41a, // Green2 Ooze + actor_info42a, // Black2 Ooze + 0, // Pod Egg + + actor_info44, // morphing_spider mutant + actor_info47, // morphing_reptilian warrior + actor_info16, // morphing_Mutant Human 2 + + 0, // SPACER + + actor_info31, // Electro-Alien SHOT + 0, // Post Barrier + actor_info43, // Arc Barrier + actor_info43a, // VPost Barrier + actor_info43b, // VSpike Barrier + actor_info25m, // Gold Morph Shot obj + 0, // Security Light + actor_info33, // Explosion + 0, 0, // Steam Grate, Steam Pipe + actor_info36, // Liquid SHOT + + actor_info18, // Lg Canister Alien SHOT + actor_info10, // POD Alien SHOT + actor_info20, // Genetic Alien SHOT + actor_info16, // Mutant Human 2 SHOT + actor_info15, // Mutant Human 1 SHOT + + 0,0, // vent drip, player sp shot, + 0, // flicker light, + 0,0, // Door Bomb, Door Bomb reserve + 0, // grenade, + + 0, // BFG Shot + actor_info51, // BFG Explosion + actor_info52, // BFG Explosion + + actor_info44, // Boss 1 SHOT + actor_info45, // Boss 2 SHOT + actor_info46, // Boss 3 SHOT + actor_info47, // Boss 4 SHOT + actor_info48, // Boss 5 SHOT + actor_info49, // Boss 6 SHOT + actor_info54, // Boss 8 SHOT + actor_info56, // Boss 10 SHOT + + 0, // Doorexplosion + actor_info52, // gr_explosion +}; + + + + + + + + + + + diff --git a/3D_PLAY.C b/3D_PLAY.C new file mode 100644 index 0000000..48f2e8c --- /dev/null +++ b/3D_PLAY.C @@ -0,0 +1,1880 @@ +// 3D_PLAY.C + +#include "3D_DEF.H" +#pragma hdrstop + + +/* +============================================================================= + + LOCAL CONSTANTS + +============================================================================= +*/ + +#define sc_Question 0x35 + +/* +============================================================================= + + GLOBAL VARIABLES + +============================================================================= +*/ + + +unsigned char music_num=0; + +#if LOOK_FOR_DEAD_GUYS +objtype *DeadGuys[MAXACTORS]; +unsigned char NumDeadGuys; +#endif + +boolean madenoise; // true when shooting or screaming +unsigned char alerted = 0,alerted_areanum; + + +exit_t playstate; + +boolean PowerBall = false; + +#if TECH_SUPPORT_VERSION +int bordertime,DebugOk = true,InstantWin = 0,InstantQuit = 0; +#else +int bordertime,DebugOk = false,InstantWin = 0,InstantQuit = 0; +#endif + +unsigned ExtraRadarFlags = 0; + + + +#if IN_DEVELOPMENT + +int TestQuickSave = 0, TestAutoMapper = 0; + +#endif + +objtype objlist[MAXACTORS],*new,*player,*lastobj, + *objfreelist,*killerobj; + +unsigned farmapylookup[MAPSIZE]; +byte *nearmapylookup[MAPSIZE]; + +boolean singlestep=false,godmode; //,noclip; +int extravbls = 0; + +byte tilemap[MAPSIZE][MAPSIZE]; // wall values only +byte spotvis[MAPSIZE][MAPSIZE]; +objtype *actorat[MAPSIZE][MAPSIZE]; + +// +// replacing refresh manager +// +unsigned mapwidth,mapheight,tics,realtics; +boolean compatability,usedummy=false, nevermark = false; +byte *updateptr; +unsigned mapwidthtable[64]; +unsigned uwidthtable[UPDATEHIGH]; +unsigned blockstarts[UPDATEWIDE*UPDATEHIGH]; +byte update[UPDATESIZE]; + +// +// 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,sc_5,sc_6,sc_7}; +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; + +// Light sourcing flag + +byte lightson; + +// +// 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(boolean preload); +void PlayLoop (void); +void SpaceEntryExit(boolean entry); +void FinishPaletteShifts (void); +void ShowQuickInstructions(void); +void CleanDrawPlayBorder(void); +void PopupAutoMap(void); + + +/* +============================================================================= + + LOCAL VARIABLES + +============================================================================= +*/ + + +objtype dummyobj; + +// +// LIST OF SONGS FOR EACH LEVEL +// + +int far songs[]= +{ + MAJMIN_MUS, // 0 + STICKS_MUS, // 1 + MOURNING_MUS, // 2 + LURKING_MUS, // 3 + CIRCLES_MUS, // 4 + TIME_MUS, // 5 + TOHELL_MUS, // 6 + FORTRESS_MUS, // 7 + GIVING_MUS, // 8 + HARTBEAT_MUS, // 9 + MOURNING_MUS, // 10 + MAJMIN_MUS, // 11 + VACCINAP_MUS, // 12 + LURKING_MUS, // 13 + MONASTRY_MUS, // 14 + TOMBP_MUS, // 15 + DARKNESS_MUS, // 16 + MOURNING_MUS, // 17 + SERPENT_MUS, // 18 + TIME_MUS, // 19 + CATACOMB_MUS, // 20 + PLOT_MUS, // 21 + GIVING_MUS, // 22 + VACCINAP_MUS, // 23 +}; + +/* +============================================================================= + + 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; + + controlx = 0; + controly = 0; + memcpy (buttonheld,buttonstate,sizeof(buttonstate)); + memset (buttonstate,0,sizeof(buttonstate)); + +#ifdef MYPROFILE + controlx = 100; // just spin in place + return; +#endif + + if (demoplayback) + { + // + // read commands from demo buffer + // + buttonbits = *demoptr++; + for (i=0;i>= 1; + } + + controlx = *demoptr++; + controly = *demoptr++; + tics = *demoptr++; + + while (TimeCount-lasttimecount < tics) + ; + lasttimecount = TimeCount; + + if (demoptr == lastdemoptr) + playstate = ex_completed; // demo is done + + controlx *= (int)tics; + controly *= (int)tics; + + + return; + } + +// +// get timing info for last frame +// + CalcTics (); + +// +// get button states +// + PollKeyboardButtons (); + + if (mouseenabled) + PollMouseButtons (); + + if (joystickenabled) + PollJoystickButtons (); + +#if 0 +if (buttonstate[bt_run]) + VL_ColorBorder (1); +else + VL_ColorBorder (0); +#endif + +// +// 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; + +#ifdef DEMOS_EXTERN + + 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; + *demoptr++ = tics; + + if (demoptr >= lastdemoptr) + PLAY_ERROR(POLLCONTROLS_DEMO_OV); + + controlx *= (int)tics; + controly *= (int)tics; + } + +#endif + +} + + + +//========================================================================== + + + +/////////////////////////////////////////////////////////////////////////// +// +// 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 += +===================== +*/ + +extern boolean PP_step,sqActive; +extern int pickquick; + +boolean refresh_screen; +#if (GAME_VERSION != SHAREWARE_VERSION) || GEORGE_CHEAT +byte jam_buff_cmp[]={sc_J,sc_A,sc_M}; +byte jam_buff[sizeof(jam_buff_cmp)]; +#endif + +char far PAUSED_MSG[]="^ST1^CEGame Paused\r^CEPress any key to resume.^XX"; + +void CheckKeys (void) +{ + boolean one_eighty=false; + int i; + byte scan; + unsigned temp; + static boolean Plus_KeyReleased; + static boolean Minus_KeyReleased; + static boolean I_KeyReleased; + static boolean S_KeyReleased; + +#if IN_DEVELOPMENT || BETA_TEST +// if (DebugOk && (Keyboard[sc_P] || PP_step)) +// PicturePause (); +#endif + + + if (screenfaded || demoplayback) // don't do anything with a faded screen + return; + + scan = LastScan; + + +#if IN_DEVELOPMENT +#ifdef ACTIVATE_TERMINAL + if (Keyboard[sc_9] && Keyboard[sc_0]) + ActivateTerminal(true); +#endif +#endif + + // + // SECRET CHEAT CODE: 'JAM' + // + +#if GAME_VERSION != SHAREWARE_VERSION + if (Keyboard[sc_J] || Keyboard[sc_A] || Keyboard[sc_M]) + { + if (jam_buff[sizeof(jam_buff_cmp)-1] != LastScan) + { + memcpy(jam_buff,jam_buff+1,sizeof(jam_buff_cmp)-1); + jam_buff[sizeof(jam_buff_cmp)-1] = LastScan; + } + } +#endif + + CheckMusicToggle(); + + if (gamestate.rpower) + { + if (Keyboard[sc_Plus] || Keyboard[sc_kpPlus]) + { + if (Plus_KeyReleased && gamestate.rzoom<2) + { + UpdateRadarGuage(); + gamestate.rzoom++; + Plus_KeyReleased=false; + } + } + else + Plus_KeyReleased=true; + + if (Keyboard[sc_Minus] || Keyboard[sc_kpMinus]) + { + if (Minus_KeyReleased && gamestate.rzoom) + { + UpdateRadarGuage(); + gamestate.rzoom--; + Minus_KeyReleased=false; + } + } + else + Minus_KeyReleased=true; + } + + if (Keyboard[sc_S]) + { + if (S_KeyReleased) + { + if ((SoundMode != sdm_Off) || (DigiMode!=sds_Off)) + { + if (SoundMode != sdm_Off) + { + SD_WaitSoundDone(); + SD_SetSoundMode(sdm_Off); + } + + if (DigiMode!=sds_Off) + SD_SetDigiDevice(sds_Off); + + _fmemcpy((char far *)&SoundOn[55],"OFF.",4); + } + else + { + ClearMemory(); + if (SoundBlasterPresent || AdLibPresent) + SD_SetSoundMode(sdm_AdLib); + else + SD_SetSoundMode(sdm_PC); + + if (SoundBlasterPresent) + SD_SetDigiDevice(sds_SoundBlaster); + else + if (SoundSourcePresent) + SD_SetDigiDevice(sds_SoundSource); + else + SD_SetDigiDevice(sds_Off); + + CA_LoadAllSounds(); + PM_CheckMainMem(); + + _fmemcpy((char far *)&SoundOn[55],"ON. ",4); + } + + DISPLAY_TIMED_MSG(SoundOn,MP_BONUS,MT_GENERAL); + S_KeyReleased=false; + } + } + else + S_KeyReleased=true; + + if (Keyboard[sc_Enter]) + { +#if (GAME_VERSION != SHAREWARE_VERSION) || GEORGE_CHEAT + char loop; + + if ((!memcmp(jam_buff,jam_buff_cmp,sizeof(jam_buff_cmp)))) + { + jam_buff[0]=0; + + for (loop=0; loopangle + 90; + if (gamestate.turn_angle > 359) + gamestate.turn_angle -= ANGLES; + } + + // 180 degrees right + // + if ((Keyboard[sc_W]) || (one_eighty)) + { + gamestate.turn_around = 180; + gamestate.turn_angle = player->angle + 180; + if (gamestate.turn_angle > 359) + gamestate.turn_angle -= ANGLES; + } + + // 90 degrees right + // + if (Keyboard[sc_E]) + { + gamestate.turn_around = 90; + gamestate.turn_angle = player->angle - 90; + if (gamestate.turn_angle < 0) + gamestate.turn_angle += ANGLES; + } + } + +// +// pause key weirdness can't be checked as a scan code +// + if (Paused || Keyboard[sc_P]) + { + SD_MusicOff(); + fontnumber = 4; + BMAmsg(PAUSED_MSG); + IN_Ack(); + IN_ClearKeysDown(); + fontnumber = 2; + RedrawStatusAreas(); + SD_MusicOn(); + Paused = false; + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + return; + } + +#if IN_DEVELOPMENT + if (TestQuickSave) + { +// TestQuickSave--; + scan = sc_F8; + } + + if (TestAutoMapper) + PopupAutoMap(); + +#endif + + switch (scan) + { + case sc_F7: // END GAME + case sc_F10: // QUIT TO DOS + FinishPaletteShifts(); + ClearMemory(); + US_ControlPanel(scan); + PM_CheckMainMem(); + CleanDrawPlayBorder(); + return; + + case sc_F2: // SAVE MISSION + case sc_F8: // QUICK SAVE + // Make sure there's room to save... + // + ClearMemory(); + FinishPaletteShifts(); + if (!CheckDiskSpace(DISK_SPACE_NEEDED,CANT_SAVE_GAME_TXT,cds_id_print)) + { + PM_CheckMainMem(); + CleanDrawPlayBorder(); + break; + } + + case sc_F1: // HELP + case sc_F3: // LOAD MISSION + case sc_F4: // SOUND MENU + case sc_F5: // RESIZE VIEW + case sc_F6: // CONTROLS MENU + case sc_F9: // QUICK LOAD + case sc_Escape: // MAIN MENU + refresh_screen=true; + if (scan < sc_F8) + VW_FadeOut(); + StopMusic(); + ClearMemory(); + ClearSplitVWB(); + US_ControlPanel(scan); + if (refresh_screen) + { + boolean old=loadedgame; + + loadedgame=false; + DrawPlayScreen(false); + loadedgame=old; + } + ClearMemory(); + if (!sqActive || !loadedgame) + StartMusic(false); + PM_CheckMainMem(); + IN_ClearKeysDown(); + if (loadedgame) + { + PreloadGraphics(); + loadedgame=false; + DrawPlayScreen(false); + } + else + if (!refresh_screen) + CleanDrawPlayBorder(); + if (!sqActive) + StartMusic(false); + return; + } + + if (Keyboard[sc_Tab]) + PopupAutoMap(); + + if (Keyboard[sc_Tilde]) + { + Keyboard[sc_Tilde] = 0; + TryDropPlasmaDetonator(); + } + + + if ((DebugOk || gamestate.flags & GS_MUSIC_TEST) && (Keyboard[sc_BackSpace])) + { + unsigned char old_num=music_num; + + if (gamestate.flags & GS_MUSIC_TEST) + { + if (Keyboard[sc_LeftArrow]) + { + if (music_num) + music_num--; + Keyboard[sc_LeftArrow]=false; + } + else + if (Keyboard[sc_RightArrow]) + { + if (music_num < LASTMUSIC-1) + music_num++; + Keyboard[sc_RightArrow]=false; + } + + if (old_num != music_num) + { + ClearMemory(); + MM_FreePtr ((memptr *)&audiosegs[STARTMUSIC + old_num]); + StartMusic(false); + PM_CheckMainMem(); + DrawScore(); + } + } + + if (old_num == music_num) + { + fontnumber=4; + SETFONTCOLOR(0,15); + if (DebugKeys()) + { + CleanDrawPlayBorder(); + } + + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + lasttimecount = TimeCount; + return; + } + } + + if (Keyboard[sc_I]) + { + if (I_KeyReleased) + { + gamestate.flags ^= GS_ATTACK_INFOAREA; + if (gamestate.flags & GS_ATTACK_INFOAREA) + DISPLAY_TIMED_MSG(attacker_info_enabled,MP_ATTACK_INFO,MT_GENERAL); + else + DISPLAY_TIMED_MSG(attacker_info_disabled,MP_ATTACK_INFO,MT_GENERAL); + I_KeyReleased = false; + } + } + else + I_KeyReleased = true; + + +#ifdef CEILING_FLOOR_COLORS + if (Keyboard[sc_C]) + { + gamestate.flags ^= GS_DRAW_CEILING; + Keyboard[sc_C] = 0; + } + + if (Keyboard[sc_F]) + { + ThreeDRefresh(); + ThreeDRefresh(); + + gamestate.flags ^= GS_DRAW_FLOOR; + + Keyboard[sc_F] = 0; +#if DUAL_SWAP_FILES + ChangeSwapFiles(true); +#endif + } +#endif + + if (Keyboard[sc_L]) + { + Keyboard[sc_L]=0; + gamestate.flags ^= GS_LIGHTING; + } +} + + +//------------------------------------------------------------------------- +// CheckMusicToggle() +//------------------------------------------------------------------------- +void CheckMusicToggle(void) +{ + static boolean M_KeyReleased; + + if (Keyboard[sc_M]) + { + if (M_KeyReleased +#if GAME_VERSION != SHAREWARE_VERSION + && ((jam_buff[0] != sc_J) || (jam_buff[1] != sc_A)) +#endif + ) + { + if (!AdLibPresent) + { + DISPLAY_TIMED_MSG(NoAdLibCard,MP_BONUS,MT_GENERAL); + SD_PlaySound(NOWAYSND); + return; + } + else + if (MusicMode != smm_Off) + { + SD_SetMusicMode(smm_Off); + _fmemcpy((char far *)&MusicOn[58],"OFF.",4); + } + else + { + SD_SetMusicMode(smm_AdLib); + StartMusic(false); + _fmemcpy((char far *)&MusicOn[58],"ON. ",4); + } + + DISPLAY_TIMED_MSG(MusicOn,MP_BONUS,MT_GENERAL); + M_KeyReleased=false; + } + } + else + M_KeyReleased=true; +} + + +char far Computing[] = {"Computing..."}; + +#if DUAL_SWAP_FILES +//-------------------------------------------------------------------------- +// ChangeSwapFiles() +// +// PURPOSE: To chance out swap files durring game play - +// +// ASSUMES: PageManager is installed. +// +//-------------------------------------------------------------------------- + +void ChangeSwapFiles(boolean display) +{ + ClearMemory(); + + if (display) + { + WindowX=WindowY=0; + WindowW=320; + WindowH=200; + Message(Computing); + } + + PM_Shutdown(); + PM_Startup (); + + PM_CheckMainMem(); + + if (display) + { + IN_UserInput(50); + CleanDrawPlayBorder(); + IN_ClearKeysDown(); + } +} +#endif + + +//-------------------------------------------------------------------------- +// OpenPageFile() +//-------------------------------------------------------------------------- +void OpenPageFile(void) +{ +#if DUAL_SWAP_FILES + + if (gamestate.flags & GS_DRAW_FLOOR || (!ShadowsAvail)) + { + PML_OpenPageFile(PageFileName); + FileUsed = sd_NO_SHADOWS; + } + else + { + PML_OpenPageFile(AltPageFileName); + FileUsed = sd_SHADOWS; + } + +#else + PML_OpenPageFile(PageFileName); +#endif +} + +//-------------------------------------------------------------------------- +// PopupAutoMap() +//-------------------------------------------------------------------------- +void PopupAutoMap() +{ + #define BASE_X 64 + #define BASE_Y 44 + + ThreeDRefresh(); + ThreeDRefresh(); + + SD_StopSound(); + ClearMemory(); + CacheDrawPic(BASE_X,BASE_Y,AUTOMAPPIC); + + ShowStats(BASE_X+101,BASE_Y+22,ss_quick,&gamestuff.level[gamestate.mapon].stats); + + while (Keyboard[sc_Tilde]) + CalcTics(); + +#if GAME_VERSION != SHAREWARE_VERSION && IN_DEVELOPMENT +// if (DebugOk && PP_step) +// PicturePause(); +#endif + + IN_StartAck (); + while (!IN_CheckAck ()) + CalcTics(); + + PM_CheckMainMem(); + CleanDrawPlayBorder(); + IN_ClearKeysDown(); +} + + +//=========================================================================== + +/* +############################################################################# + + 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 +// +#if LOOK_FOR_DEAD_GUYS + NumDeadGuys=0; + memset(DeadGuys,0,sizeof(DeadGuys)); +#endif + + memset(statobjlist,0,sizeof(statobjlist)); + for (i=0;i= MAXACTORS-1) + { + objtype *obj=player->next; + + while (obj) + { + if ((obj->flags & (FL_DEADGUY|FL_VISABLE)) == FL_DEADGUY) + { + RemoveObj(obj); + obj = NULL; + } + else + obj = obj->next; + } + } + + if (!objfreelist) + if (usedummy) + { + new = &dummyobj; + memset (new,0,sizeof(*new)); + } + else + PLAY_ERROR(GETNEWACTOR_NO_FREE_SPOTS); + else + { + new = objfreelist; + objfreelist = new->prev; + + 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 == &dummyobj) + return; + + if (gone == player) + PLAY_ERROR(REMOVEOBJ_REMOVED_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_FreePtr(&((memptr)audiosegs[STARTMUSIC + i])); +} + +//========================================================================== + +//------------------------------------------------------------------------- +// StartMusic() +// o preload = true, music is cached but not started +//------------------------------------------------------------------------- +void StartMusic(boolean preload) +{ + musicnames musicchunk; + + SD_MusicOff(); +#if IN_DEVELOPMENT || GAME_VERSION != SHAREWARE_VERSION || TECH_SUPPORT_VERSION + if (gamestate.flags & GS_MUSIC_TEST) + musicchunk=music_num; + else +#endif + if (playstate==ex_victorious) + musicchunk = FORTRESS_MUS; + else + musicchunk = songs[gamestate.mapon+gamestate.episode*MAPS_PER_EPISODE]; + + if (!audiosegs[STARTMUSIC+musicchunk]) + { + MM_BombOnError(false); + CA_CacheAudioChunk(STARTMUSIC + musicchunk); + MM_BombOnError(true); + } + + if (mmerror) + mmerror = false; + else + { + MM_SetLock(&((memptr)audiosegs[STARTMUSIC + musicchunk]),true); + if (!preload) + SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + musicchunk]); + } +} + +/* +============================================================================= + + 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 vgapal; + +/* +===================== += += 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 = &vgapal; + + 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 = &vgapal; + + 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 (0,256,redshifts[red-1]); + palshifted = true; + } + else if (white) + { + VW_WaitVBL(1); + VL_SetPalette (0,256,whiteshifts[white-1]); + palshifted = true; + } + else if (palshifted) + { + VW_WaitVBL(1); + VL_SetPalette (0,256,&vgapal); // back to normal + palshifted = false; + } +} + + +/* +===================== += += FinishPaletteShifts += += Resets palette to normal if needed += +===================== +*/ + +void FinishPaletteShifts (void) +{ + if (palshifted) + { + palshifted = 0; + VW_WaitVBL(1); + VL_SetPalette (0,256,&vgapal); + } +} + + +/* +============================================================================= + + CORE PLAYLOOP + +============================================================================= +*/ + + +/* +===================== += += DoActor += +===================== +*/ + +void DoActor (objtype *ob) +{ + void (*think)(objtype *); + objtype *actor; + + + if (ob->flags & FL_FREEZE) + return; + +#pragma warn -pia + if (ob->flags & FL_BARRIER) + { + actor = actorat[ob->tilex][ob->tiley]; + if (BARRIER_STATE(ob) == bt_ON) + { + if (actor) + { + short damage = 0; + + actor->flags |= FL_BARRIER_DAMAGE; + if ((US_RndT() < 0x7f) && (actor->flags & FL_SHOOTABLE)) + { + switch (ob->obclass) + { + case arc_barrierobj: // arc barrier - Mild Damage + damage = 500; // 100 + break; + + case post_barrierobj: // post barrier - Butt kicker + damage = 500; + break; + } + DamageActor(actor,damage,ob); + } + } + } + else + if (actor) + actor->flags &= ~FL_BARRIER_DAMAGE; + } +#pragma warn +pia + + 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) ) + { + if (ob->flags&FL_NONMARK) + { + if (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) ) + { + if (ob->flags&FL_NONMARK) + { + if (actorat[ob->tilex][ob->tiley]) + { + return; + } + } + actorat[ob->tilex][ob->tiley] = ob; + } + return; +} + +//========================================================================== + + +/* +=================== += += PlayLoop += +=================== +*/ + +extern boolean ShowQuickMsg; + + +void PlayLoop (void) +{ + boolean reset_areas=false; + int give; + objtype *obj; + + playstate = TimeCount = lasttimecount = 0; + framecount = frameon = 0; + pwallstate = anglefrac = 0; + memset (buttonstate,0,sizeof(buttonstate)); + ClearPaletteShifts (); + ForceUpdateStatusBar(); + + if (MousePresent) + Mouse(MDelta); // Clear accumulated mouse movement + + tics = 1; // for first time through + if (demoplayback) + IN_StartAck (); + + do + { + PollControls(); + +// +// actor thinking +// + madenoise = false; + + if (alerted) + alerted--; + + MoveDoors (); + MovePWalls (); + + for (obj = player;obj;obj = obj->next) + { + if ((obj != player) && (Keyboard[sc_6] || Keyboard[sc_7]) && Keyboard[sc_8] && DebugOk) + { + if (!reset_areas) + memset(areabyplayer,1,sizeof(areabyplayer)); + reset_areas=true; + + if ((((!(obj->flags & FL_INFORMANT)) && (obj->flags & FL_SHOOTABLE))) || + (obj->obclass == liquidobj && !(obj->flags & FL_DEADGUY))) + DamageActor(obj,1000,player); + } + else + if (reset_areas) + { + ConnectAreas(); + reset_areas=false; + } + DoActor (obj); + } + + if (NumEAWalls) + CheckSpawnEA(); + + if ((!GoldsternInfo.GoldSpawned) && GoldsternInfo.SpawnCnt) + CheckSpawnGoldstern(); + + UpdatePaletteShifts (); + + + ThreeDRefresh (); + + gamestate.TimeCount+=tics; + + SD_Poll (); + UpdateSoundLoc(); // JAB + + if (screenfaded & !playstate) + VW_FadeIn(); + + // Display first-time instructions. + // + if (ShowQuickMsg) + ShowQuickInstructions(); + + CheckKeys(); + + if (demoplayback && demoptr == lastdemoptr) + playstate = ex_title; + +// +// debug aids +// + if (singlestep) + { + VW_WaitVBL(14); + lasttimecount = TimeCount; + } + if (extravbls) + VW_WaitVBL(extravbls); + + if ((demoplayback) && (IN_CheckAck())) + { + IN_ClearKeysDown (); + playstate = ex_abort; + } + + + }while (!playstate && !startgame); + + if (playstate != ex_died) + FinishPaletteShifts (); + + gamestate.flags &= ~GS_VIRGIN_LEVEL; +} + +//-------------------------------------------------------------------------- +// ShowQuickInstructions() +//-------------------------------------------------------------------------- +void ShowQuickInstructions() +{ + ShowQuickMsg=false; + + if ((demoplayback) || (gamestate.mapon) || (gamestate.flags & GS_QUICKRUN)) + return; + + ThreeDRefresh(); + ThreeDRefresh(); + ClearMemory(); + WindowX=0; WindowY=16; WindowW=320; WindowH=168; + CacheMessage(QUICK_INFO1_TEXT); + VW_WaitVBL(120); + CacheMessage(QUICK_INFO2_TEXT); + IN_Ack(); + IN_ClearKeysDown(); + PM_CheckMainMem(); + CleanDrawPlayBorder(); +} + +//-------------------------------------------------------------------------- +// CleanDrawPlayBorder() +//-------------------------------------------------------------------------- +void CleanDrawPlayBorder() +{ + DrawPlayBorder(); + ThreeDRefresh(); + DrawPlayBorder(); + ThreeDRefresh(); + DrawPlayBorder(); +} + diff --git a/3D_SCALE.C b/3D_SCALE.C new file mode 100644 index 0000000..36865a4 --- /dev/null +++ b/3D_SCALE.C @@ -0,0 +1,971 @@ +// 3D_SCALE.C + +#include "3D_DEF.H" +#pragma hdrstop + +#define OP_RETF 0xcb + +#define CLOAKED_SHAPES (true) + +/* +============================================================================= + + GLOBALS + +============================================================================= +*/ + +//t_compscale _seg *scaledirectory[MAXSCALEHEIGHT+1]; +//long fullscalefarcall[MAXSCALEHEIGHT+1]; + +int maxscale,maxscaleshl2; +unsigned centery; + +int normalshade; +int normalshade_div = 1; +int shade_max = 1; + +int nsd_table[] = { 1, 6, 3, 4, 1, 2}; +int sm_table[] = {36,51,62,63,18,52}; + + +/* +============================================================================= + + LOCALS + +============================================================================= +*/ + +//t_compscale _seg *work; +unsigned BuildCompScale (int height, memptr *finalspot); + +int stepbytwo; + +//=========================================================================== + +#if 0 +/* +============== += += BadScale += +============== +*/ + +void far BadScale (void) +{ + SCALE_ERROR(BADSCALE_ERROR); +} +#endif + + + + +/* +========================== += += SetupScaling += +========================== +*/ + +void SetupScaling (int maxscaleheight) +{ + int i,x,y; + byte far *dest; + + maxscaleheight/=2; // one scaler every two pixels + + maxscale = maxscaleheight-1; + maxscaleshl2 = maxscale<<2; + normalshade=(3*(maxscale>>2))/normalshade_div; + centery=viewheight>>1; +} + +//=========================================================================== + +/* +======================== += += BuildCompScale += += Builds a compiled scaler object that will scale a 64 tall object to += the given height (centered vertically on the screen) += += height should be even += += Call with += --------- += DS:SI Source for scale += ES:DI Dest for scale += += Calling the compiled scaler only destroys AL += +======================== +*/ + +#if 0 +unsigned BuildCompScale (int height, memptr *finalspot) +{ + byte far *code; + + int i; + long fix,step; + unsigned src,totalscaled,totalsize; + int startpix,endpix,toppix; + + + step = ((long)height<<16) / 64; + code = &work->code[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; +} + +#endif + + +// Draw Column vars + +longword dc_iscale; +longword dc_frac; +unsigned dc_source; +unsigned dc_seg; +unsigned dc_length; +unsigned dc_dest; + +#define SFRACUNIT 0x10000 + +extern unsigned far * linecmds; + +extern boolean useBounceOffset; + +fixed bounceOffset=0; + +/* +======================= += += ScaleMaskedLSPost with Light sourcing += +======================= +*/ + + +void ScaleMaskedLSPost (int height, unsigned buf) +{ + int length; + unsigned end; + unsigned start; + long sprtopoffset; + long topscreen; + long bottomscreen; + longword screenstep; + long dc_yl,dc_yh; + unsigned far * srcpost; + + + fixed bounce; + + if (useBounceOffset) + bounce=bounceOffset; + else + bounce=0; + + srcpost=linecmds; + dc_iscale=(64u*65536u)/(longword)height; + screenstep = ((longword)height)<<10; + + sprtopoffset=((long)viewheight<<15)-((long)height<<15)+(bounce>>1); + dc_seg=*(((unsigned *)&srcpost)+1); + + end=(*(srcpost++))>>1; + for (;end!=0;) + { + dc_source=*(srcpost++); + + start=(*(srcpost++))>>1; + + dc_source+=start; + length=end-start; + topscreen = sprtopoffset + (long)(screenstep*(long)start); + bottomscreen = topscreen + (long)(screenstep*(long)length); + + dc_yl = (topscreen+SFRACUNIT-1)>>16; + dc_yh = (bottomscreen-1)>>16; + + if (dc_yh >= viewheight) + dc_yh = viewheight-1; + + if (dc_yl < 0) + { + dc_frac=dc_iscale*(-dc_yl); + dc_yl = 0; + } + else + dc_frac=0; + + if (dc_yl<=dc_yh) + { + dc_dest=buf+(unsigned)ylookup[(unsigned)dc_yl]; + dc_length=(unsigned)(dc_yh-dc_yl+1); +#if CLOAKED_SHAPES + if (cloaked_shape) + R_DrawSLSColumn(); + else +#endif + R_DrawLSColumn(); + } + end=(*(srcpost++))>>1; + } +} + +/* +======================= += += ScaleMaskedWideLSPost with Light sourcing += +======================= +*/ +void ScaleMaskedWideLSPost (int height, unsigned buf, unsigned xx, unsigned pwidth) +{ + byte ofs; + byte msk; + unsigned ii; + + buf+=(unsigned)xx>>2; + ofs=((byte)(xx&3)<<3)+(byte)pwidth-1; + outp(SC_INDEX+1,(byte)*((byte *)mapmasks1+ofs)); + ScaleMaskedLSPost(height,buf); + msk=(byte)*((byte *)mapmasks2+ofs); + if (msk==0) + return; + buf++; + outp(SC_INDEX+1,msk); + ScaleMaskedLSPost(height,buf); + msk=(byte)*((byte *)mapmasks3+ofs); + if (msk==0) + return; + buf++; + outp(SC_INDEX+1,msk); + ScaleMaskedLSPost(height,buf); +} + +/* +======================= += += ScaleMaskedPost without Light sourcing += +======================= +*/ +void ScaleMaskedPost (int height, unsigned buf) +{ + int length; + unsigned end; + unsigned start; + long sprtopoffset; + long topscreen; + long bottomscreen; + longword screenstep; + long dc_yl,dc_yh; + unsigned far * srcpost; + + fixed bounce; + + if (useBounceOffset) + bounce=bounceOffset; + else + bounce=0; + + srcpost=linecmds; + dc_iscale=(64u*65536u)/(longword)height; + screenstep = ((longword)height)<<10; + + sprtopoffset=((long)viewheight<<15)-((long)height<<15)+(bounce>>1); + dc_seg=*(((unsigned *)&srcpost)+1); + + end=(*(srcpost++))>>1; + for (;end!=0;) + { + dc_source=*(srcpost++); + start=(*(srcpost++))>>1; + dc_source+=start; + length=end-start; + topscreen = sprtopoffset + (long)(screenstep*(long)start); + bottomscreen = topscreen + (long)(screenstep*(long)length); + dc_yl = (topscreen+SFRACUNIT-1)>>16; + dc_yh = (bottomscreen-1)>>16; + if (dc_yh >= viewheight) + dc_yh = viewheight-1; + if (dc_yl < 0) + { + dc_frac=dc_iscale*(-dc_yl); + dc_yl = 0; + } + else + dc_frac=0; + if (dc_yl<=dc_yh) + { + dc_dest=buf+(unsigned)ylookup[(unsigned)dc_yl]; + dc_length=(unsigned)(dc_yh-dc_yl+1); + R_DrawColumn(); + } + end=(*(srcpost++))>>1; + } + +} + + +/* +======================= += += ScaleMaskedWidePost without Light sourcing += +======================= +*/ +void ScaleMaskedWidePost (int height, unsigned buf, unsigned xx, unsigned pwidth) +{ + byte ofs; + byte msk; + unsigned ii; + + buf+=(unsigned)xx>>2; + ofs=((byte)(xx&3)<<3)+(byte)pwidth-1; + outp(SC_INDEX+1,(byte)*((byte *)mapmasks1+ofs)); + ScaleMaskedPost(height,buf); + msk=(byte)*((byte *)mapmasks2+ofs); + if (msk==0) + return; + buf++; + outp(SC_INDEX+1,msk); + ScaleMaskedPost(height,buf); + msk=(byte)*((byte *)mapmasks3+ofs); + if (msk==0) + return; + buf++; + outp(SC_INDEX+1,msk); + ScaleMaskedPost(height,buf); +} + + + +/* +======================= += += ScaleLSShape with Light sourcing += += 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 += +======================= +*/ +extern byte far * shadingtable; +extern byte far * lightsource; + +void ScaleLSShape (int xcenter, int shapenum, unsigned height, char lighting) +{ + t_compshape _seg *shape; + int dest; + int i; + longword frac; + unsigned width; + int x1,x2; + longword xscale; + longword screenscale; + long texturecolumn; + long lastcolumn; + int startx; + unsigned swidth; + long xcent; + + if ((height>>1>maxscaleshl2)||(!(height>>1))) + return; + shape = PM_GetSpritePage (shapenum); + *(((unsigned *)&linecmds)+1)=(unsigned)shape; // seg of shape + xscale=(longword)height<<12; + xcent=(long)((long)xcenter<<20)-((long)height<<17)+0x80000; +// +// calculate edges of the shape +// + x1 = (int)((long)(xcent+((long)shape->leftpix*xscale))>>20); + if (x1 >= viewwidth) + return; // off the right side + x2 = (int)((long)(xcent+((long)shape->rightpix*xscale))>>20); + if (x2 < 0) + return; // off the left side + screenscale=(256L<<20L)/(longword)height; +// +// store information in a vissprite +// + if (x1<0) + { + frac=((long)-x1)*(long)screenscale; + x1=0; + } + else + frac=screenscale>>1; + x2 = x2 >= viewwidth ? viewwidth-1 : x2; + + i=shade_max-(63l*(unsigned long)(height>>3)/(unsigned long)normalshade)+lighting; + + if (i<0) + i=0; + else + if (i > 63) + i = 63; + + shadingtable=lightsource+(i<<8); + swidth=shape->rightpix-shape->leftpix; + if (height>256) + { + width=1; + startx=0; + lastcolumn=-1; + for (; x1<=x2 ; x1++, frac += screenscale) + { + if (wallheight[x1]>height) + { + if (lastcolumn>=0) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWideLSPost(height>>2,(unsigned)bufferofs,(unsigned)startx,width); + width=1; + lastcolumn=-1; + } + continue; + } + texturecolumn = (long)(frac>>20); + if (texturecolumn>swidth) + texturecolumn=swidth; + if (texturecolumn==lastcolumn) + { + width++; + continue; + } + else + { + if (lastcolumn>=0) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWideLSPost(height>>2,(unsigned)bufferofs,(unsigned)startx,width); + width=1; + startx=x1; + lastcolumn=texturecolumn; + } + else + { + startx=x1; + width=1; + lastcolumn=texturecolumn; + } + } + } + if (lastcolumn!=-1) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWideLSPost(height>>2,bufferofs,(unsigned)startx,width); + } + } + else + { + for (; x1<=x2 ; x1++, frac += screenscale) + { + if (wallheight[x1]>height) + continue; + outp(SC_INDEX+1,1<<(byte)(x1&3)); + texturecolumn=frac>>20; + if (texturecolumn>swidth) + texturecolumn=swidth; + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)texturecolumn]; + ScaleMaskedLSPost(height>>2,bufferofs+((unsigned)x1>>2)); + } + } +} + + + + +/* +======================= += += 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 += +======================= +*/ +void ScaleShape (int xcenter, int shapenum, unsigned height) +{ + t_compshape _seg *shape; + int dest; + int i; + longword frac; + unsigned width; + int x1,x2; + longword xscale; + longword screenscale; + long texturecolumn; + long lastcolumn; + int startx; + long xcent; + unsigned swidth; + + + if ((height>>1>maxscaleshl2)||(!(height>>1))) + return; + shape = PM_GetSpritePage (shapenum); + *(((unsigned *)&linecmds)+1)=(unsigned)shape; // seg of shape + xscale=(longword)height<<12; + xcent=(long)((long)xcenter<<20)-((long)height<<(17))+0x80000; +// +// calculate edges of the shape +// + x1 = (int)((long)(xcent+((long)shape->leftpix*xscale))>>20); + if (x1 >= viewwidth) + return; // off the right side + x2 = (int)((long)(xcent+((long)shape->rightpix*xscale))>>20); + if (x2 < 0) + return; // off the left side + screenscale=(256L<<20L)/(longword)height; +// +// store information in a vissprite +// + if (x1<0) + { + frac=((long)-x1)*(long)screenscale; + x1=0; + } + else + frac=screenscale>>1; + x2 = x2 >= viewwidth ? viewwidth-1 : x2; + swidth=shape->rightpix-shape->leftpix; + if (height>256) + { + width=1; + startx=0; + lastcolumn=-1; + for (; x1<=x2 ; x1++, frac += screenscale) + { + if (wallheight[x1]>height) + { + if (lastcolumn>=0) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWidePost(height>>2,(unsigned)bufferofs,(unsigned)startx,width); + width=1; + lastcolumn=-1; + } + continue; + } + texturecolumn = (long)(frac>>20); + if (texturecolumn>swidth) + texturecolumn=swidth; + if (texturecolumn==lastcolumn) + { + width++; + continue; + } + else + { + if (lastcolumn>=0) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWidePost(height>>2,(unsigned)bufferofs,(unsigned)startx,width); + width=1; + startx=x1; + lastcolumn=texturecolumn; + } + else + { + startx=x1; + width=1; + lastcolumn=texturecolumn; + } + } + } + if (lastcolumn!=-1) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWidePost(height>>2,bufferofs,(unsigned)startx,width); + } + } + else + { + for (; x1<=x2 ; x1++, frac += screenscale) + { + if (wallheight[x1]>height) + continue; + outp(SC_INDEX+1,1<<(byte)(x1&3)); + texturecolumn=frac>>20; + if (texturecolumn>swidth) + texturecolumn=swidth; + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)texturecolumn]; + ScaleMaskedPost(height>>2,bufferofs+((unsigned)x1>>2)); + } + } +} + + + +/* +======================= += += 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; + int dest; + int i; + longword frac; + int width; + int x1,x2; + longword xscale; + longword screenscale; + long texturecolumn; + long lastcolumn; + int startx; + long xcent; + unsigned swidth; + + shape = PM_GetSpritePage (shapenum); + *(((unsigned *)&linecmds)+1)=(unsigned)shape; // seg of shape + xscale=(longword)height<<10; + xcent=(long)((long)xcenter<<16)-((long)height<<(15))+0x8000; +// +// calculate edges of the shape +// + x1 = (int)((long)(xcent+((long)shape->leftpix*xscale))>>16); + if (x1 >= viewwidth) + return; // off the right side + x2 = (int)((long)(xcent+((long)shape->rightpix*xscale))>>16); + if (x2 < 0) + return; // off the left side + screenscale=(64*65536)/(longword)height; +// +// store information in a vissprite +// + if (x1<0) + { + frac=screenscale*((long)-x1); + x1=0; + } + else + frac=screenscale>>1; + x2 = x2 >= viewwidth ? viewwidth-1 : x2; + swidth=shape->rightpix-shape->leftpix; + if (height>64) + { + width=1; + startx=0; + lastcolumn=-1; + for (; x1<=x2 ; x1++, frac += screenscale) + { + texturecolumn = (long)(frac>>16); + if (texturecolumn>swidth) + texturecolumn=swidth; + if (texturecolumn==lastcolumn) + { + width++; + continue; + } + else + { + if (lastcolumn>=0) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWidePost(height,bufferofs,startx,width); + width=1; + startx=x1; + lastcolumn=texturecolumn; + } + else + { + startx=x1; + lastcolumn=texturecolumn; + } + } + } + if (lastcolumn!=-1) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWidePost(height,bufferofs,startx,width); + } + } + else + { + for (; x1<=x2 ; x1++, frac += screenscale) + { + outp(SC_INDEX+1,1<<(x1&3)); + texturecolumn=frac>>16; + if (texturecolumn>swidth) + texturecolumn=swidth; + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)texturecolumn]; + ScaleMaskedPost(height,bufferofs+(x1>>2)); + } + } +} + +//------------------------------------------------------------------------- +// MegaSimpleScaleShape() +// +// NOTE: Parameter SHADE determines which Shade palette to use on the shape. +// 0 == NO Shading +// 63 == Max Shade (BLACK or near) +//------------------------------------------------------------------------- +void MegaSimpleScaleShape (int xcenter, int ycenter, int shapenum, unsigned height, unsigned shade) +{ + t_compshape _seg *shape; + int dest; + int i; + longword frac; + int width; + int x1,x2; + longword xscale; + longword screenscale; + long texturecolumn; + long lastcolumn; + int startx; + long xcent; + unsigned old_bufferofs; + int swidth; + + + old_bufferofs = bufferofs; + ycenter -=34; + bufferofs -= ((viewheight-64)>>1)*SCREENBWIDE; + bufferofs += SCREENBWIDE*ycenter; + + shape = PM_GetSpritePage (shapenum); + *(((unsigned *)&linecmds)+1)=(unsigned)shape; // seg of shape + xscale=(longword)height<<14; + xcent=(long)((long)xcenter<<20)-((long)height<<(19))+0x80000; +// +// calculate edges of the shape +// + x1 = (int)((long)(xcent+((long)shape->leftpix*xscale))>>20); + if (x1 >= viewwidth) + return; // off the right side + x2 = (int)((long)(xcent+((long)shape->rightpix*xscale))>>20); + if (x2 < 0) + return; // off the left side + + screenscale=(64L<<20L)/(longword)height; + +// +// Choose shade table. +// + shadingtable=lightsource+(shade<<8); + +// +// store information in a vissprite +// + if (x1<0) + { + frac=screenscale*((long)-x1); + x1=0; + } + else + frac=screenscale>>1; + x2 = x2 >= viewwidth ? viewwidth-1 : x2; + swidth=shape->rightpix-shape->leftpix; + if (height>64) + { + width=1; + startx=0; + lastcolumn=-1; + for (; x1<=x2 ; x1++, frac += screenscale) + { + texturecolumn = (long)(frac>>20); + if (texturecolumn==lastcolumn) + { + width++; + continue; + } + else + { + if (lastcolumn>=0) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWideLSPost(height,bufferofs,startx,width); + width=1; + startx=x1; + lastcolumn=texturecolumn; + } + else + { + startx=x1; + lastcolumn=texturecolumn; + } + } + } + if (lastcolumn!=-1) + { + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)lastcolumn]; + ScaleMaskedWideLSPost(height,bufferofs,startx,width); + } + } + else + { + for (; x1<=x2 ; x1++, frac += screenscale) + { + outp(SC_INDEX+1,1<<(x1&3)); + texturecolumn=frac>>20; + if (texturecolumn>swidth) + texturecolumn=swidth; + (unsigned)linecmds=(unsigned)shape->dataofs[(unsigned)texturecolumn]; + ScaleMaskedLSPost(height,bufferofs+(x1>>2)); + } + } + bufferofs = old_bufferofs; + +} + + +// +// 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} }; + + +#if 0 + +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} }; + +#endif + +int slinex,slinewidth; +unsigned far *linecmds; +long linescale; +unsigned maskword; + diff --git a/3D_STATE.C b/3D_STATE.C new file mode 100644 index 0000000..af0a828 --- /dev/null +++ b/3D_STATE.C @@ -0,0 +1,2565 @@ +// 3D_STATE.C + +#include "3D_DEF.H" +//#include + +#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, boolean moveit); +void MoveObj (objtype *ob, long move); + +void KillActor (objtype *ob); + +boolean CheckLine (objtype *from_obj, objtype *to_obj); +void FirstSighting (objtype *ob); +boolean CheckSight (objtype *from_obj, objtype *to_obj); +boolean ElevatorFloor(char x, char y); + +/* +============================================================================= + + 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; + new->ticcount = Random (state->tictime)+1; + + new->tilex = tilex; + new->tiley = tiley; + new->x = ((long)tilex<y = ((long)tiley<dir = new->trydir = nodir; + + if (!nevermark) + if (!actorat[tilex][tiley]) + actorat[tilex][tiley] = new; + + new->areanumber=GetAreaNumber(new->tilex,new->tiley); + +#if IN_DEVELOPMENT + if (new->areanumber >= NUMAREAS && (!nevermark)) + Quit("Actor Spawned on wall at %d %d",new->tilex,new->tiley); +#endif + +} + + + +/* +=================== += += 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_SOLID) \ + return false; \ + } \ + if (ElevatorFloor(x,y)) \ + return(false); \ +} + +#define CHECKSIDE(x,y) \ +{ \ + temp=(unsigned)actorat[x][y]; \ + if (temp) \ + { \ + if (temp<128) \ + return false; \ + if (temp<256) \ + { \ + doornum = temp&63; \ + if (doorobjlist[doornum].lock!=kt_none) \ + return(false); \ + } \ + else if (((objtype *)temp)->flags&FL_SOLID) \ + return false; \ + } \ +} + +boolean TryWalk (objtype *ob, boolean moveit) +{ + int doornum; + unsigned temp; + byte old_tilex=ob->tilex,old_tiley=ob->tiley; + + if (ElevatorFloor(ob->tilex,ob->tiley)) + return(false); + + doornum = -1; + + 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); + } + + if (ElevatorFloor(ob->tilex,ob->tiley-1)) + return(false); + + if (!moveit) + return(true); + + ob->tiley--; + break; + + case northeast: + CHECKDIAG(ob->tilex+1,ob->tiley-1); + CHECKDIAG(ob->tilex+1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley-1); + +// if (ElevatorFloor(ob->tilex+1,ob->tiley-1)) +// return(false); + + if (!moveit) + return(true); + + 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); + } + + if (ElevatorFloor(ob->tilex+1,ob->tiley)) + { + if ((doornum != -1) && (ob->obclass != electrosphereobj)) + OpenDoor(doornum); + + return(false); + } + + if (!moveit) + return(true); + + ob->tilex++; + break; + + case southeast: + CHECKDIAG(ob->tilex+1,ob->tiley+1); + CHECKDIAG(ob->tilex+1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley+1); + +// if (ElevatorFloor(ob->tilex+1,ob->tiley+1)) +// return(false); + + if (!moveit) + return(true); + + 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); + } + + if (ElevatorFloor(ob->tilex,ob->tiley+1)) + return(false); + + if (!moveit) + return(true); + + ob->tiley++; + break; + + case southwest: + CHECKDIAG(ob->tilex-1,ob->tiley+1); + CHECKDIAG(ob->tilex-1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley+1); + +// if (ElevatorFloor(ob->tilex-1,ob->tiley+1)) +// return(false); + + if (!moveit) + return(true); + + 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); + } + + if (ElevatorFloor(ob->tilex-1,ob->tiley)) + { + if ((doornum != -1) && (ob->obclass != electrosphereobj)) + OpenDoor(doornum); + + return(false); + } + + if (!moveit) + return(true); + + ob->tilex--; + break; + + case northwest: + CHECKDIAG(ob->tilex-1,ob->tiley-1); + CHECKDIAG(ob->tilex-1,ob->tiley); + CHECKDIAG(ob->tilex,ob->tiley-1); + +// if (ElevatorFloor(ob->tilex-1,ob->tiley-1)) +// return(false); + + if (!moveit) + return(true); + + ob->tilex--; + ob->tiley--; + break; + + case nodir: + return false; + + default: +// STATE_ERROR(TRYWALK_BAD_DIR); // jam/jdebug + return false; // jam/jdebug + + } + +// Should actor open this door? +// + if (doornum != -1) + { +#pragma warn -rch + switch (ob->obclass) + { + // Actors that don't open doors. + // + case liquidobj: + case electrosphereobj: + ob->tilex = old_tilex; + ob->tiley = old_tiley; + return(false); + break; + + // All other actors open doors. + // + default: + OpenDoor (doornum); + ob->distance = -doornum-1; + return true; + break; + } +#pragma warn +rch + } + + ob->areanumber=GetAreaNumber(ob->tilex,ob->tiley); + +#if IN_DEVELOPMENT + if (ob->areanumber >= NUMAREAS) + Quit("Actor walked on wall at %d %d",ob->tilex,ob->tiley); +#endif + + ob->distance = TILEGLOBAL; + return true; +} + +//-------------------------------------------------------------------------- +// ElevatorFloor() +//-------------------------------------------------------------------------- +boolean ElevatorFloor(char x, char y) +{ + byte tile=*(mapsegs[0]+farmapylookup[y]+x); + + if (tile >= HIDDENAREATILE) + tile -= HIDDENAREATILE; + else + tile -= AREATILE; + + return(tile==0); +} + +/* +================================== += += 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]; + + SeekPlayerOrStatic(ob,&deltax,&deltay); + +// +// 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 if (deltax<=0) + { + dirtry[1]= west; + dirtry[3]= east; + } + + if (deltay>0) + { + dirtry[2]= south; + dirtry[4]= north; + } + else if (deltay<=0) + { + 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,true)) + return; + } + +// +// turn around only as a last resort +// + if (turnaround != nodir) + { + ob->dir = turnaround; + + if (TryWalk(ob,true)) + return; + } + + ob->dir = nodir; + + if (ob->obclass == electrosphereobj) + ob->s_tilex = 0; +} + + +/* +============================ += += 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]; + + SeekPlayerOrStatic(ob,&deltax,&deltay); + + 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,true)) + return; /*either moved forward or attacked*/ + } + + if (d[2]!=nodir) + { + ob->dir=d[2]; + if (TryWalk(ob,true)) + return; + } + +/* there is no direct path to the player, so pick another direction */ + + if (olddir!=nodir) + { + ob->dir=olddir; + if (TryWalk(ob,true)) + 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,true) ) + return; + } + } + } + else + { + for (tdir=west;tdir>=north;tdir--) + { + if (tdir!=turnaround) + { + ob->dir=tdir; + if ( TryWalk(ob,true) ) + return; + } + } + } + + if (turnaround != nodir) + { + ob->dir=turnaround; + if (ob->dir != nodir) + { + if ( TryWalk(ob,true) ) + return; + } + } + + ob->dir = nodir; // can't move + if (ob->obclass == electrosphereobj) + ob->s_tilex = 0; +} + + + +//-------------------------------------------------------------------------- +// GetCornerSeek() +//-------------------------------------------------------------------------- +void GetCornerSeek(objtype *ob) +{ + unsigned char SeekPointX[]={32,63,32,1}; // s_tilex can't seek to 0! + unsigned char SeekPointY[]={1,63,32,1}; + unsigned char seek_tile=US_RndT()&3; + + ob->flags &= ~FL_RUNTOSTATIC; + ob->s_tilex = SeekPointX[seek_tile]; + ob->s_tiley = SeekPointY[seek_tile]; +} + + + +/* +================= += += 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 += +================= +*/ + +extern long last_objy; + +void MoveObj (objtype *ob, long move) +{ + long deltax,deltay; + +// if (DebugOk && Keyboard[sc_Z]) +// return; + + 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: + STATE_ERROR(MOVEOBJ_BAD_DIR); + } + +// +// check to make sure it's not on top of player +// + if (ob->obclass != electrosphereobj) + 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; + + // + // 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; + } + + PlayerIsBlocking(ob); + return; + } +moveok: + ob->distance -=move; +} + +/* +============================================================================= + + STUFF + +============================================================================= +*/ + +/* +=============== += += KillActor += +=============== +*/ + +extern statetype s_terrot_die1; + +char far dki_msg[]= + "^FC39 YOU JUST SHOT AN\r" + " INFORMANT!\r" + "^FC79 ONLY SHOOT BIO-TECHS\r" + " THAT SHOOT AT YOU!\r" + "^FC19 DO NOT SHOOT\r" + " INFORMANTS!!\r"; + +unsigned far actor_points[]={ 1025, // rent-a-cop + 1050, // turret + 500, // general scientist + 5075, // pod alien + 5150, // electric alien + 2055, // electro-sphere + 5000, // pro guard + 10000, // genetic guard + 5055, // mutant human1 + 6055, // mutant human2 + 0, // large canister wait + 6050, // large canister alien + 0, // small canister wait + 3750, // small canister alien + 0, // gurney wait + 3750, // gurney + 12000, // liquid + 7025, // swat + 5000, // goldtern + 5000, // goldstern Morphed + 2025, // volatile transport + 2025, // floating bomb + 0, // rotating cube + + 5000, // spider_mutant + 6000, // breather_beast + 7000, // cyborg_warror + 8000, // reptilian_warrior + 9000, // acid_dragon + 9000, // mech_guardian + 30000, // final boss #1 + 40000, // final_boss #2 + 50000, // final_boss #3 + 60000, // final_boss #4 + + 0,0,0,0,0, // blake,crate1/2/3, oozes + 0, // pod egg + + 5000, // morphing_spider_mutant + 8000, // morphing_reptilian_warrior + 6055, // morphing_mutant human2 + +}; + +//--------------------------------------------------------------------------- +// CheckAndReserve() - Checks for room in the obj_list and returns a ptr +// to the new object or a NULL. +// +//--------------------------------------------------------------------------- +objtype *CheckAndReserve(void) +{ + usedummy = nevermark = true; + SpawnNewObj(0,0,&s_hold); + usedummy = nevermark = false; + + if (new == &dummyobj) + return (NULL); + else + return (new); +} + +#ifdef TRACK_ENEMY_COUNT +extern short numEnemy[]; +#endif + +void KillActor (objtype *ob) +{ + char buff[4]; + int tilex,tiley; + boolean KeepSolid = false, givepoints=true, deadguy = true; + classtype clas; + + tilex = ob->x >> TILESHIFT; // drop item on center + tiley = ob->y >> TILESHIFT; + + ob->flags &= ~(FL_FRIENDLY|FL_SHOOTABLE); + clas=ob->obclass; + + switch (clas) + { + case podeggobj: + PlaySoundLocActor(PODHATCHSND,ob); + InitSmartSpeedAnim(ob,SPR_POD_HATCH1,0,2,at_ONCE,ad_FWD,7); + KeepSolid=true; + deadguy = givepoints=false; + break; + + case morphing_spider_mutantobj: + case morphing_reptilian_warriorobj: + case morphing_mutanthuman2obj: + ob->flags &= ~FL_SHOOTABLE; + InitSmartSpeedAnim(ob,ob->temp1,0,8,at_ONCE,ad_FWD,2); + KeepSolid = true; + deadguy = givepoints = false; + break; + + + case crate1obj: + case crate2obj: + case crate3obj: + #if IN_DEVELOPMENT + if (!ob->temp3) + Quit("exp crate->temp3 is NULL!"); + #endif + + ((statobj_t *)(ob->temp3))->shapenum = -1; // Release reserve static + + SpawnStatic(tilex,tiley,ob->temp2); + ob->obclass = deadobj; + ob->lighting = NO_SHADING; // No Shading + InitSmartSpeedAnim(ob,SPR_GRENADE_EXPLODE2,0,3,at_ONCE,ad_FWD,3+(US_RndT()&7)); + A_DeathScream(ob); + MakeAlertNoise(ob); + break; + + case floatingbombobj: + ob->lighting = EXPLOSION_SHADING; + A_DeathScream(ob); + InitSmartSpeedAnim(ob,SPR_FSCOUT_DIE1,0,7,at_ONCE,ad_FWD,5); + break; + + case volatiletransportobj: + ob->lighting = EXPLOSION_SHADING; + A_DeathScream(ob); + InitSmartSpeedAnim(ob,SPR_GSCOUT_DIE1,0,8,at_ONCE,ad_FWD,5); + break; + + case goldsternobj: + NewState (ob,&s_goldwarp_it); + GoldsternInfo.flags = GS_NEEDCOORD; + GoldsternInfo.GoldSpawned = false; + + // Init timer. Search for a location out of all possible locations. + + GoldsternInfo.WaitTime = MIN_GOLDIE_WAIT + Random(MAX_GOLDIE_WAIT-MIN_GOLDIE_WAIT); // Reinit Delay Timer before spawning on new position + clas = goldsternobj; + break; + + case gold_morphobj: + GoldsternInfo.flags = GS_NO_MORE; + PlaySoundLocActor(PODDEATHSND,ob); + ob->flags |= FL_OFFSET_STATES; + InitAnim(ob, SPR_GOLD_DEATH1, 0, 4, at_ONCE, ad_FWD, 25, 9); + break; + + case gen_scientistobj: + if (ob->flags & FL_INFORMANT) + { + givepoints=false; + clas = nothing; + gamestuff.level[gamestate.mapon].stats.accum_inf--; + if (!(gamestate.flags & GS_KILL_INF_WARN) || (US_RndT() < 25)) + { + DisplayInfoMsg(dki_msg,MP_INTERROGATE-1,DISPLAY_MSG_STD_TIME*3,MT_GENERAL); + gamestate.flags |= GS_KILL_INF_WARN; + } + } + NewState (ob,&s_ofcdie1); + if ((ob->ammo) && !(ob->flags & FL_INFORMANT)) + { + if (US_RndT()<65) + PlaceItemType (bo_coin,tilex,tiley); + else + PlaceItemType (bo_clip2,tilex,tiley); + } + break; + + case rentacopobj: + NewState (ob,&s_rent_die1); + if (!(gamestate.weapons & (1<ammo)) + PlaceItemType (bo_coin,tilex,tiley); + else + if (ob->ammo) + PlaceItemType (bo_clip2,tilex,tiley); + break; + + case swatobj: + NewState (ob,&s_swatdie1); + if (!(gamestate.weapons & (1<ammo)) + PlaceItemType (bo_coin,tilex,tiley); + else + if (ob->ammo) + PlaceItemType(bo_clip2,tilex,tiley); + break; + + + case proguardobj: + NewState (ob,&s_prodie1); + if (!(gamestate.weapons & (1<ammo)) + PlaceItemType (bo_coin,tilex,tiley); + else + if (ob->ammo) + PlaceItemType(bo_clip2,tilex,tiley); + break; + +#pragma warn -rch + case electroobj: + NewState(ob,&s_electro_die1); + eaList[ob->temp2].aliens_out--; + ob->obclass = nothing; + actorat[ob->tilex][ob->tiley] = NULL; + break; +#pragma warn +rch + + case liquidobj: + NewState (ob,&s_liquid_die1); + ob->obclass = nothing; + actorat[ob->tilex][ob->tiley] = NULL; + break; + + case podobj: + ob->temp1=SPR_POD_DIE1; + NewState (ob,&s_ofs_pod_death1); + A_DeathScream(ob); + break; + + case electrosphereobj: + ob->obclass = nothing; + ob->temp1=SPR_ELECTRO_SPHERE_DIE1; + NewState (ob,&s_ofs_esphere_death1); + actorat[ob->tilex][ob->tiley] = NULL; + break; + + case cyborg_warriorobj: + case mech_guardianobj: + case reptilian_warriorobj: + case mutant_human1obj: + PlaceItemNearTile(bo_clip2,tilex,tiley); + case spider_mutantobj: + case breather_beastobj: + case acid_dragonobj: + case final_boss3obj: + case final_boss4obj: + case mutant_human2obj: + case scan_alienobj: + case lcan_alienobj: + NewState (ob,&s_ofs_die1); + break; + + case final_boss2obj: + PlaySoundLocActor(PODDEATHSND,ob); + InitAnim(ob, SPR_BOSS8_DIE1, 0, 4, at_ONCE, ad_FWD, 25, 9); + break; + + case genetic_guardobj: + case final_boss1obj: + case gurneyobj: + if (!(gamestate.weapons & (1<lighting = EXPLOSION_SHADING; + break; + } + +#if LOOK_FOR_DEAD_GUYS + switch (clas) + { + case SMART_ACTORS: + DeadGuys[NumDeadGuys++]=ob; + break; + } +#endif + + if (KeepSolid) + { + ob->flags &= ~(FL_SHOOTABLE); + ob->flags2 &= ~FL2_BFG_SHOOTABLE; + if (deadguy) + ob->flags |= FL_DEADGUY; + } + else + { + if (deadguy) + ob->flags |= (FL_NONMARK | FL_DEADGUY); + + if ((clas>=rentacopobj) && (clasflags &= ~(FL_SHOOTABLE | FL_SOLID | FL_FAKE_STATIC); + ob->flags2 &= ~FL2_BFGSHOT_SOLID; + + if ((actorat[ob->tilex][ob->tiley]) == ob) + { + // Clear actor from WHERE IT WAS GOING in actorat[]. + // + if (!(tilemap[ob->tilex][ob->tiley] & 0x80)) + actorat[ob->tilex][ob->tiley] = NULL; + + // Set actor WHERE IT DIED in actorat[], IF there's a door! + // Otherwise, just leave it removed! + // + if (tilemap[tilex][tiley] & 0x80) + actorat[tilex][tiley]=ob; + else + ob->flags |= FL_NEVERMARK; + } + } + + DropCargo(ob); + + ob->tilex = tilex; + ob->tiley = tiley; + + if ((LastMsgPri == MP_TAKE_DAMAGE) && (LastInfoAttacker == clas)) + MsgTicsRemain = 1; + + switch (clas) + { + case electroobj: + case liquidobj: + case electrosphereobj: + ob->obclass=clas; + ob->flags |= FL_NEVERMARK; + break; + } +} + +/* +=================== += += 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 DoAttack(objtype *ob); +extern statetype s_proshoot2; +extern statetype s_goldmorphwait1; +extern boolean barrier_damage; + +void DamageActor (objtype *ob, unsigned damage, objtype *attacker) +{ + short old_hp = ob->hitpoints,wound_mod,mod_before=0,mod_after=1; + + if (!(ob->flags & FL_SHOOTABLE)) + return; + + if (gamestate.weapon != wp_autocharge) + { + MakeAlertNoise(player); + } + + if (ob->flags & FL_FREEZE) + return; + + switch (ob->obclass) + { + case hang_terrotobj: + if (gamestate.weapon < wp_burst_rifle) + return; + break; + + case gurney_waitobj: + if (ob->temp3) + return; + break; + + case arc_barrierobj: + if (attacker->obclass == bfg_shotobj) + { + if (BARRIER_STATE(ob) != bt_DISABLING) + { + BARRIER_STATE(ob) = bt_DISABLING; + ob->hitpoints = 15; + ob->temp3 =0; + ob->temp2 = US_RndT()&0xf; + NewState(ob,&s_barrier_shutdown); + } + } + return; + + case post_barrierobj: + case rotating_cubeobj: + return; + + case plasma_detonatorobj: + // + // Detonate 'Em! + // + if (attacker == player) + ob->temp3 = 1; + else + ob->temp3 = damage; + return; + } + +// +// do double damage if shooting a non attack mode actor +// + if ( !(ob->flags & FL_ATTACKMODE) ) + damage <<= 1; + + ob->hitpoints -= damage; + ob->flags2 |= FL2_DAMAGE_CLOAK; + + if (ob->hitpoints<=0) + { + switch (ob->obclass) + { +#ifdef OBJ_RESERV + case scan_wait_alienobj: // These actors do not have an ouch! + case lcan_wait_alienobj: // So... RETURN! + case gurney_waitobj: + if (!(ob->temp2 = (unsigned)CheckAndReserve())) + { + ob->hitpoints += damage; + return; + } + break; +#endif + + case goldsternobj: + if (gamestate.mapon == GOLD_MORPH_LEVEL) + { + extern int morphWaitTime; + extern boolean noShots; + + morphWaitTime = 60; + noShots = true; + NewState(ob,&s_goldmorphwait1); + ob->obclass = gold_morphingobj; + ob->flags &= ~FL_SHOOTABLE; + return; + } + break; + + } + + SLIDE_TEMP(ob) = (unsigned)attacker; + KillActor (ob); + return; + } + else + { + switch (ob->obclass) + { + case swatobj: + // Don't get wounded if it's an arc! + // + if ((attacker->obclass == arc_barrierobj) || + (attacker->obclass == post_barrierobj)) + break; + + // Calculate 'wound boundary' (based on NUM_WOUND_STAGES). + // + wound_mod = starthitpoints[gamestate.difficulty][en_swat] / (ob->temp1+1) + 1; + mod_before = old_hp / wound_mod; + mod_after = ob->hitpoints / wound_mod; + + // If modulo 'before' and 'after' are different, we've crossed + // a 'wound boundary'! + // + if (mod_before != mod_after) + { + PlaySoundLocActor(SWATDEATH2SND,ob); + NewState(ob,&s_swatwounded1); + ob->flags &= ~(FL_SHOOTABLE|FL_SOLID); + ob->temp2 = (5*60)+((US_RndT()%20)*60); + return; + } + break; + } + + if (ob->flags & FL_LOCKED_STATE) + return; + + if (! (ob->flags & FL_ATTACKMODE) ) + { + if ((ob->obclass == gen_scientistobj) && (ob->flags & FL_INFORMANT)) + return; + FirstSighting (ob); // put into combat mode + } + + switch (ob->obclass) + { +#if GAME_VERSION != SHAREWARE_VERSION + case volatiletransportobj: + case floatingbombobj: + T_PainThink(ob); + break; +#endif + + case goldsternobj: + NewState(ob,&s_goldpain); + break; + + case gold_morphobj: + NewState(ob,&s_mgold_pain); + break; + + case liquidobj: + NewState(ob,&s_liquid_ouch); + break; + + case rentacopobj: + NewState (ob,&s_rent_pain); + break; + + case podobj: + NewState (ob,&s_ofs_pod_ouch); + break; + + case spider_mutantobj: + case breather_beastobj: + case cyborg_warriorobj: + case reptilian_warriorobj: + case acid_dragonobj: + case mech_guardianobj: + case final_boss1obj: + case final_boss2obj: + case final_boss3obj: + case final_boss4obj: + + case genetic_guardobj: + case mutant_human1obj: + case mutant_human2obj: + case scan_alienobj: + case lcan_alienobj: + case gurneyobj: + NewState (ob,&s_ofs_pain); + break; + + case electrosphereobj: + NewState (ob,&s_ofs_ouch); + ob->temp1 = SPR_ELECTRO_SPHERE_OUCH; + break; + + case electroobj: + NewState (ob,&s_electro_ouch); + break; + + case gen_scientistobj: + NewState (ob,&s_ofcpain); + break; + + case swatobj: + NewState(ob,&s_swatpain); + break; + + case proguardobj: + NewState (ob,&s_propain); + break; + + } + } + +// Make sure actors aren't sitting ducks! +// + + if ((US_RndT() < 192) && + (!(ob->flags & (FL_LOCKED_STATE|FL_BARRIER_DAMAGE)))) + { + ChangeShootMode(ob); + DoAttack(ob); + } + + ob->flags |= FL_LOCKED_STATE; +} + +/* +============================================================================= + + CHECKSIGHT + +============================================================================= +*/ + + +/* +===================== += += CheckLine += += Returns true if a straight line between the player and ob is unobstructed += +===================== +*/ + +#if 0 + +boolean CheckLine (objtype *ob) +{ + int x1,y1,xt1,yt1,x2,y2,xt2,yt2; + int x,y,xl,xh,yl,yh; + int xdist,ydist,xstep,ystep; + int temp; + int partial,delta; + long ltemp; + unsigned 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); + ydist = abs(yt2-yt1); + + if (xdist > 0) + { + if (xt2 > xt1) + { + partial = 256-(x1&0xff); + xl = xt1; + xh = xt2; + yl = y1; + yh = y2; + deltafrac = x2-x1; + } + else + { + partial = 256-(x2&0xff); + xl = xt2; + xh = xt1; + yl = y2; + yh = y1; + deltafrac = x1-x2; + } + + delta = yh-yl; + ltemp = ((long)delta<<8)/deltafrac; + if (ltemp > 0x7fffl) + ystep = 0x7fff; + else if (ltemp < -0x7fffl) + ystep = -0x7fff; + else + ystep = ltemp; + yfrac = yl + (((long)ystep*partial) >>8); + + for (x=xl+1 ; x <= xh ; x++) + { + y = yfrac>>8; + yfrac += ystep; + + if (!(value = (unsigned)tilemap[x][y]) ) + 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; + } + } + + + if (ydist > 0) + { + if (yt2 > yt1) + { + partial = 256-(y1&0xff); + xl = x1; + xh = x2; + yl = yt1; + yh = yt2; + deltafrac = y2-y1; + } + else + { + partial = 256-(y2&0xff); + xl = x2; + xh = x1; + yl = yt2; + yh = yt1; + deltafrac = y1-y2; + } + + delta = xh-xl; + ltemp = ((long)delta<<8)/deltafrac; + if (ltemp > 0x7fffl) + xstep = 0x7fff; + else if (ltemp < -0x7fffl) + xstep = -0x7fff; + else + xstep = ltemp; + xfrac = xl + (((long)xstep*partial) >>8); + + for (y=yl+1 ; y<= yh ; y++) + { + x = xfrac>>8; + xfrac += xstep; + + if (!(value = (unsigned)tilemap[x][y]) ) + 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; + } + } + + return true; +} + +#endif + + +boolean CheckLine (objtype *from_obj, objtype *to_obj) +{ + 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 = from_obj->x >> UNSIGNEDSHIFT; // 1/256 tile precision + y1 = from_obj->y >> UNSIGNEDSHIFT; + xt1 = x1 >> 8; + yt1 = y1 >> 8; + +// x2 = plux; +// y2 = pluy; + + x2 = to_obj->x >> UNSIGNEDSHIFT; + y2 = to_obj->y >> UNSIGNEDSHIFT; + xt2 = to_obj->tilex; + yt2 = to_obj->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); + if (!deltafrac) + deltafrac=1; + 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); + if (!deltafrac) + deltafrac=1; + 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 *from_obj, objtype *to_obj) +{ + long deltax,deltay; + +// +// don't bother tracing a line if the area isn't connected to the player's +// + if (!areabyplayer[from_obj->areanumber]) + return false; + +#if 0 +// +// If object doesn't have rotated shapes, then don't bother with +// checking to see it object is facing the right direction. +// + if (!from_obj->state->flags) + return(CheckLine(from_obj,to_obj)); +#endif + +// +// if the player is real close, sight is automatic +// + deltax = to_obj->x - from_obj->x; + deltay = to_obj->y - from_obj->y; + + + if (deltax > -MINSIGHT && deltax < MINSIGHT && deltay > -MINSIGHT && deltay < MINSIGHT) + return true; + +// +// see if they are looking in the right direction +// + switch (from_obj->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(from_obj,to_obj)); + +} + + + +/* +=============== += += FirstSighting += += Puts an actor into attack mode and possibly reverses the direction += if the player is behind it += +=============== +*/ + +void FirstSighting (objtype *ob) +{ + if (PlayerInvisable) + return; + +// +// react to the player +// + switch (ob->obclass) + { + case floatingbombobj: + if (ob->flags & FL_STATIONARY) + return; + PlaySoundLocActor(SCOUT_ALERTSND,ob); + NewState(ob,&s_scout_run); + ob->speed *= 3; // Haul Ass + break; + + + case goldsternobj: + PlaySoundLocActor(GOLDSTERNHALTSND,ob); + NewState (ob,&s_goldchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case rentacopobj: + PlaySoundLocActor(HALTSND,ob); + NewState (ob,&s_rent_chase1); + ob->speed *= 3; // go faster when chasing player + break; + + case gen_scientistobj: + PlaySoundLocActor(SCIENTISTHALTSND,ob); + NewState (ob,&s_ofcchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case swatobj: + PlaySoundLocActor(SWATHALTSND,ob); + NewState (ob,&s_swatchase1); + ob->speed *= 3; // go faster when chasing player + break; + + case breather_beastobj: + case reptilian_warriorobj: + case genetic_guardobj: + case final_boss4obj: + case final_boss2obj: + PlaySoundLocActor(GGUARDHALTSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 3; // go faster when chasing player + break; + + + case cyborg_warriorobj: + case mech_guardianobj: + case mutant_human1obj: + case final_boss3obj: + case final_boss1obj: + PlaySoundLocActor(BLUEBOYHALTSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 2; // go faster when chasing player + break; + + case mutant_human2obj: + PlaySoundLocActor(DOGBOYHALTSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 2; // go faster when chasing player + break; + + case liquidobj: + NewState(ob,&s_liquid_move); + break; + + case spider_mutantobj: + case scan_alienobj: + PlaySoundLocActor(SCANHALTSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 3; // go faster when chasing player + break; + + case lcan_alienobj: + PlaySoundLocActor(LCANHALTSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 3; // go faster when chasing player + break; + + case gurneyobj: + PlaySoundLocActor(GURNEYSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 3; // go faster when chasing player + break; + + case acid_dragonobj: + case podobj: + PlaySoundLocActor(PODHALTSND,ob); + NewState (ob,&s_ofs_chase1); + ob->speed *= 2; + break; + + case gurney_waitobj: +#pragma warn -pia + if (ob->temp3) + { + if (ob->temp2 = (unsigned)CheckAndReserve()) + { + ob->flags &= ~(FL_SHOOTABLE); + InitSmartAnim(ob, SPR_GURNEY_MUT_B1, 0, 3,at_ONCE, ad_FWD); + } + else + return; + } +#pragma warn +pia + break; + + case proguardobj: + PlaySoundLocActor(PROHALTSND,ob); + NewState (ob,&s_prochase1); + ob->speed *= 4; // go faster when chasing player + break; + + case hang_terrotobj: + PlaySoundLocActor(TURRETSND,ob); + NewState(ob,&s_terrot_seek1); + break; + + } + +// 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->obclass == gen_scientistobj) + if (ob->flags & FL_INFORMANT) + return(false); + + if (PlayerInvisable) + return(false); + + if (ob->flags & FL_ATTACKMODE) + return(true); +// STATE_ERROR(SIGHTPLAYER_IN_ATKMODE); + + 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,player)) + return false; + ob->flags &= ~FL_AMBUSH; + } + else + { + boolean sighted=false; + + if (madenoise || CheckSight(ob,player)) + sighted=true; + + switch (ob->obclass) + { + // Actors that look fine while JUST STANDING AROUND should go here. + // + case rentacopobj: + case proguardobj: + case swatobj: + case goldsternobj: + case gen_scientistobj: + case floatingbombobj: + case volatiletransportobj: + break; + + // Actors that look funny when just standing around go here... + // + default: + if (ob->flags & FL_VISABLE) + sighted=true; + break; + } + + if (!sighted) + return(false); + } + + switch (ob->obclass) + { + case goldsternobj: + ob->temp2 = 1+US_RndT()/4; + break; + + + case rentacopobj: + ob->temp2 = 1+US_RndT()/4; + break; + + case gen_scientistobj: + ob->temp2 = 2; + break; + + case swatobj: + ob->temp2 = 1+US_RndT()/6; + break; + + case proguardobj: + ob->temp2 = 1+US_RndT()/6; + break; + + case hang_terrotobj: + ob->temp2 = 1+US_RndT()/4; + break; + + case gurney_waitobj: + ob->temp2 = ob->temp3; + break; + + case liquidobj: + ob->temp2 = 1+US_RndT()/6; + break; + + case floatingbombobj: + ob->temp2 = 1+US_RndT()/4; + break; + + case genetic_guardobj: + case mutant_human1obj: + case mutant_human2obj: + case scan_alienobj: + case lcan_alienobj: + case gurneyobj: + case spider_mutantobj: + case breather_beastobj: + case cyborg_warriorobj: + case reptilian_warriorobj: + case acid_dragonobj: + case mech_guardianobj: + case final_boss1obj: + case final_boss2obj: + case final_boss3obj: + case final_boss4obj: + ob->temp2 = 1; + break; + } + ob->flags &= ~FL_FRIENDLY; + return false; + } + + FirstSighting (ob); + + return true; +} + + +#if 0 +//-------------------------------------------------------------------------- +// PosVisable() Checks a straight line from two map coords and then checks +// to see if the passed from_angle is pointing in that direction +// (if the position is seeable by the player). +// +// SEE ALSO: MACRO "ObjVisable(from_obj,to_obj)" -- 3D_DEF.h +// +//-------------------------------------------------------------------------- +boolean PosVisable(fixed from_x, fixed from_y, fixed to_x, fixed to_y, int from_angle) +{ + long deltax,deltay; + int angle,dif; + float fangle; + + deltax = from_x - to_x; + deltay = to_y - from_y; + + fangle = atan2(deltay,deltax); // returns -pi to pi + + if (fangle<0) + fangle += M_PI*2; + + angle = fangle/(M_PI*2)*ANGLES; + angle = (angle+180) % 360; + + dif = angle - from_angle; + + if (abs(dif) < 45 || abs(dif) > 315) + return(true); + else + return(false); +} + + + +#endif + + +//-------------------------------------------------------------------------- +// CheckView() Checks a straight line between player and current object +// If the sight is ok, check angle to see if they notice +// returns true if the player has been spoted +//-------------------------------------------------------------------------- +short AdjAngleTable[2][8] = {{225,270,315,360, 45, 90,135,180}, // Upper Bound + { 180,225,270,315, 0, 45, 90,135}}; // Lower Bound + + +boolean CheckView(objtype *from_obj, objtype *to_obj) +{ + long deltax,deltay; + short angle; + float fangle; + + // + // don't bother tracing a line if the area isn't connected to the player's + // + + if (!areabyplayer[from_obj->areanumber]) + return false; + + deltax = from_obj->x - to_obj->x; + deltay = to_obj->y - from_obj->y; + + + fangle = atan2(deltay,deltax); // returns -pi to pi + if (fangle<0) + fangle = M_PI*2+fangle; + + angle = fangle/(M_PI*2)*ANGLES+23; + + if (angle > 360) + angle = 360; + + if ((angle <= AdjAngleTable[1][from_obj->dir]) || (angle >= AdjAngleTable[0][from_obj->dir])) + return(false); + + // + // trace a line to check for blocking tiles (corners) + // + + return(CheckLine(from_obj,to_obj)); +} + +#if LOOK_FOR_DEAD_GUYS +//-------------------------------------------------------------------------- +// LookForDeadGuys() +//-------------------------------------------------------------------------- +boolean LookForDeadGuys(objtype *obj) +{ + unsigned char loop; + boolean DeadGuyFound=false; + + if ((obj->obclass == gen_scientistobj) && (obj->flags & FL_INFORMANT)) + return(false); + + for (loop=0; loopflags &= ~FL_INTERROGATED; // No longer runs, now attacks! + } + +// We'll let the computer-controlled actors cheat in some circumstances... +// + if ((player->areanumber != ob->areanumber) && (!(ob->flags & FL_VISABLE))) + { + if (!ob->ammo) + ob->ammo += 8; + if (ob->hitpoints <= (starthitpoints[gamestate.difficulty][ob->obclass-rentacopobj]>>1)) + ob->hitpoints+=10; + return(true); + } + +// Let's REALLY look for some goodies! +// + if (!just_find_door) + for (statptr=&statobjlist[0]; statptr!=laststatobj; statptr++) + if ((ob->areanumber==statptr->areanumber) && (statptr->shapenum!=-1)) + switch (statptr->itemnumber) + { + case bo_chicken: + case bo_ham: + case bo_water: + case bo_clip: + case bo_clip2: + case bo_candybar: + case bo_sandwich: + + // If actor is 'on top' of this object, get it! + // + if ((statptr->tilex==ob->tilex) && (statptr->tiley==ob->tiley)) + { + short shapenum = -1; + + switch (statptr->itemnumber) + { + case bo_clip: + case bo_clip2: + if (ob->ammo) // this actor has plenty + continue; // of ammo! + ob->ammo += 8; + break; + + case bo_candybar: + case bo_sandwich: + case bo_chicken: + if (ob->hitpoints > (starthitpoints[gamestate.difficulty][ob->obclass-rentacopobj]>>1)) + continue; // actor has plenty of health! + ob->hitpoints += 8; + shapenum = statptr->shapenum+1; + break; + + case bo_ham: + if (ob->hitpoints > (starthitpoints[gamestate.difficulty][ob->obclass-rentacopobj]>>1)) + continue; // actor has plenty of health! + ob->hitpoints += 12; + shapenum = statptr->shapenum+1; + break; + + case bo_water: + if (ob->hitpoints > (starthitpoints[gamestate.difficulty][ob->obclass-rentacopobj]>>1)) + continue; // actor has plenty of health! + ob->hitpoints += 2; + shapenum = statptr->shapenum+1; + break; + } + + ob->s_tilex=0; // reset for next search! + statptr->shapenum = shapenum; // remove from list if necessary + statptr->itemnumber = bo_nothing; + statptr->flags &= ~FL_BONUS; + return(true); + } + +// Stops searching when (s_tilex,s_tiley) object is found; even though actor +// may be standing on another 'needed' object. +// (Depends on where objects appear in the static object list!) +// +#ifdef ONE_TRACK_MIND + // Is actor looking for current object? + // + if ((statptr->tilex==ob->s_tilex) && (statptr->tiley==ob->s_tiley)) + return(false); +#endif + + // Give actor a chance to run towards this object. + // + if ((!(ob->flags&FL_RUNTOSTATIC))) // &&(US_RndT()<25)) + if ( + ((RunReason & RR_AMMO) && + ((statptr->itemnumber == bo_clip) || + (statptr->itemnumber == bo_clip2))) + || + ((RunReason & RR_HEALTH) && + ((statptr->itemnumber == bo_firstaid) || + (statptr->itemnumber == bo_water) || + (statptr->itemnumber == bo_chicken) || + (statptr->itemnumber == bo_candybar) || + (statptr->itemnumber == bo_sandwich) || + (statptr->itemnumber == bo_ham))) + ) + { + ob->flags |= FL_RUNTOSTATIC; + ob->s_tilex = statptr->tilex; + ob->s_tiley = statptr->tiley; + return(false); + } + } + +// Should actor run for a door? (quick escape!) +// +// if (ob->areanumber == player->areanumber) + if (areabyplayer[ob->areanumber]) + { + #define DOOR_CHOICES 8 + + doorobj_t *door,*doorlist[DOOR_CHOICES]; + char doorsfound=0; + + // If actor is running for a 'goody' or a door -- leave it alone! + // + if (ob->flags & FL_RUNTOSTATIC) + return(false); + + // Search for all doors in actor's current area. + // + for (door=&doorobjlist[0]; door!=lastdoorobj; door++) + { + // Is this an elevator door OR a locked door? + // + if ((!(*(mapsegs[0]+farmapylookup[door->tiley]+(door->tilex-1))-AREATILE)) || + (!(*(mapsegs[0]+farmapylookup[door->tiley]+(door->tilex+1))-AREATILE)) || + (door->lock != kt_none)) + continue; + + // Does this door connect the area the actor is in with another area? + // + if ((door->areanumber[0]==ob->areanumber) || + (door->areanumber[1]==ob->areanumber)) + { + doorlist[doorsfound] = door; // add to list + if (++doorsfound == DOOR_CHOICES) // check for max + break; + } + } + + // Randomly choose a door if any were found. + // + if (doorsfound) + { + char doornum; + + // Randomly choose a door from the list. + // (Only choose the last door used if it's the only door in this area!) + // + doornum = Random(doorsfound); + door = doorlist[doornum]; + if (((unsigned)door == ob->temp3) && (doorsfound > 1)) + { + doornum++; + if (doornum >= doorsfound) + doornum=0; + door = doorlist[doornum]; + } + ob->temp3 = (unsigned)door; + + ob->s_tilex = door->tilex; + ob->s_tiley = door->tiley; + + ob->flags |= FL_RUNTOSTATIC; + } + } + else + { + // Either: actor is running to corner (leave it alone) OR + // actor is chasing an object already removed by another actor + // (make this actor run to corner) + // + if (ob->flags & FL_RUNTOSTATIC) + ob->s_tilex=0; + } + + return(false); +} + + + + +//-------------------------------------------------------------------------- +// CheckRunChase() +//-------------------------------------------------------------------------- +unsigned CheckRunChase(objtype *ob) +{ + #define RUNAWAY_SPEED 1000 + + unsigned RunReason=0; + +// Mark the reason for running. +// + if (!ob->ammo) // Out of ammo! + RunReason |= RR_AMMO; + + if (ob->hitpoints <= (starthitpoints[gamestate.difficulty][ob->obclass-rentacopobj]>>1)) + RunReason |= RR_HEALTH; // Feeling sickly! + + if ((ob->flags & (FL_FRIENDLY|FL_INTERROGATED)) == FL_INTERROGATED) + RunReason |= RR_INTERROGATED; // Non-informant was interrogated! + +// if (ob->flags2 & FL2_SCARED) +// RunReason |= RR_SCARED; // GenSci is scared. + + +// Time to RUN or CHASE? +// + if (RunReason) // run, Run, RUN! + { + if (!(ob->flags & FL_RUNAWAY)) + { + ob->temp3 = 0; + ob->flags |= FL_RUNAWAY; + ob->speed += RUNAWAY_SPEED; + } + } + else // chase, Chase, CHASE! + { + if (ob->flags & FL_RUNAWAY) + { + ob->flags &= ~FL_RUNAWAY; + ob->speed -= RUNAWAY_SPEED; + } + } + + return(RunReason); +} + +//-------------------------------------------------------------------------- +// SeekPlayerOrStatic() +//-------------------------------------------------------------------------- +void SeekPlayerOrStatic(objtype *ob, int *deltax, int *deltay) +{ + unsigned whyrun=0; + boolean smart=false; + +// Is this a "smart" actor? +// + switch (ob->obclass) + { + case SMART_ACTORS: + smart = true; + break; + +#pragma warn -rch + case electrosphereobj: + if (!ob->s_tilex) + GetCornerSeek(ob); + *deltax = ob->s_tilex - ob->tilex; + *deltay = ob->s_tiley - ob->tiley; + return; + break; +#pragma warn +rch + } + +#pragma warn -pia +// Should actor run away (chase static) or chase player? +// + if ((smart) && (whyrun=CheckRunChase(ob))) + { + // Initilize seek tile? + // + if (!ob->s_tilex) + GetCornerSeek(ob); + + // Are there any goodies available? + // + if (!LookForGoodies(ob,whyrun)) + { + // Change seek tile when actor reaches it. + // + if ((ob->tilex==ob->s_tilex) && (ob->tiley==ob->s_tiley)) + { // don't forget me! + GetCornerSeek(ob); + ob->flags &= ~FL_INTERROGATED; + } // add me, too + + // Calculate horizontal / vertical distance to seek point. + // + *deltax = ob->s_tilex - ob->tilex; + *deltay = ob->s_tiley - ob->tiley; + } + else + whyrun=CheckRunChase(ob); + } + +// Make actor chase player if it's not running. +// + if (!whyrun) + { + *deltax = player->tilex - ob->tilex; + *deltay = player->tiley - ob->tiley; + } +#pragma warn +pia +} + +//-------------------------------------------------------------------------- +// PlayerIsBlocking() +//-------------------------------------------------------------------------- +boolean PlayerIsBlocking(objtype *ob) +{ + char opp_off[9][2] = {{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{0,0}}; + +// if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley)) + { + ob->tilex += opp_off[ob->dir][0]; + ob->tiley += opp_off[ob->dir][1]; + ob->dir = opposite[ob->dir]; + ob->distance = TILEGLOBAL-ob->distance; + return(true); + } + +// return(false); +} + + + +//-------------------------------------------------------------------------- +// MakeAlertNoise() - +//-------------------------------------------------------------------------- +void MakeAlertNoise(objtype *obj) +{ + madenoise = true; + alerted = 2; + alerted_areanum = obj->areanumber; +} + + diff --git a/AN_CODES.H b/AN_CODES.H new file mode 100644 index 0000000..887bcba --- /dev/null +++ b/AN_CODES.H @@ -0,0 +1,50 @@ +#ifndef _AN_CODES_H_ +#define _AN_CODES_H_ + +//-------------------------------------------------------------------------- +// +// ANIM CODES - DOCS +// +//-------------------------------------------------------------------------- +// +// FI - Fade In the current frame (Last Frame grabbed) +// FO - Fade Out the current frame (Last Frame grabbed) +// FB - Fade In with rate (a numeral value should follow in the script) +// ** MUST be a divisor of 64 +// FE - Fade Out with rate (a numeral value should follow in the script) +// ** MUST be a divisor of 64 +// SD - Play sounds (a numeral value should follow in the script) +// GR - Graphic Page (full screen) +// +// PA - Pause/Delay 'xxxxxx' number of VBLs +// +// +// +// +// + +//-------------------------------------------------------------------------- +// +// MACROS +// +//-------------------------------------------------------------------------- + + +#define MV_CNVT_CODE(c1,c2) ((unsigned)((c1)|(c2<<8))) + +#define AN_PAUSE MV_CNVT_CODE('P','A') +#define AN_SOUND MV_CNVT_CODE('S','D') +#define AN_MUSIC MV_CNVT_CODE('M','U') +#define AN_PAGE MV_CNVT_CODE('G','R') +#define AN_FADE_IN_FRAME MV_CNVT_CODE('F','I') +#define AN_FADE_OUT_FRAME MV_CNVT_CODE('F','O') +#define AN_FADE_IN MV_CNVT_CODE('F','B') +#define AN_FADE_OUT MV_CNVT_CODE('F','E') +#define AN_PALETTE MV_CNVT_CODE('P','L') + +#define AN_PRELOAD_BEGIN MV_CNVT_CODE('L','B') +#define AN_PRELOAD_END MV_CNVT_CODE('L','E') + +#define AN_END_OF_ANIM MV_CNVT_CODE('X','X') + +#endif \ No newline at end of file diff --git a/AUDIO.H b/AUDIO.H new file mode 100644 index 0000000..fcf20a2 --- /dev/null +++ b/AUDIO.H @@ -0,0 +1,32 @@ +///////////////////////////////////////////////// +// +// MUSE Header for . +// Created Wed Aug 19 11:58:30 1992 +// +///////////////////////////////////////////////// + +#define NUMSOUNDS 1 +#define NUMSNDCHUNKS 3 + +// +// Sound names & indexes +// +typedef enum { + UNTITLED0SND, // 0 + LASTSOUND + } soundnames; + +// +// Base offsets +// +#define STARTPCSOUNDS 0 +#define STARTADLIBSOUNDS 1 +#define STARTDIGISOUNDS 2 +#define STARTMUSIC 3 + + +///////////////////////////////////////////////// +// +// Thanks for playing with MUSE! +// +///////////////////////////////////////////////// diff --git a/AUDIOBS1.H b/AUDIOBS1.H new file mode 100644 index 0000000..980884c --- /dev/null +++ b/AUDIOBS1.H @@ -0,0 +1,156 @@ +///////////////////////////////////////////////// +// +// MUSE Header for .BS1 +// Created Tue Jan 04 11:44:31 1994 +// +///////////////////////////////////////////////// + +#define NUMSOUNDS 100 +#define NUMSNDCHUNKS 319 + +// +// Sound names & indexes +// +typedef enum { + HITWALLSND, // 0 + TERM_TYPESND, // 1 + GETPISTOLSND, // 2 + LIQUIDDIESND, // 3 + MOVEGUN2SND, // 4 + MOVEGUN1SND, // 5 + NOWAYSND, // 6 + SCOUT_ALERTSND, // 7 + GURNEYSND, // 8 + PLAYERDEATHSND, // 9 + CONCESSIONSSND, // 10 + ATKIONCANNONSND, // 11 + GETKEYSND, // 12 + WARPOUTSND, // 13 + WARPINSND, // 14 + ROBOT_SERVOSND, // 15 + INFORMANTDEATHSND, // 16 + GOLDSTERNHALTSND, // 17 + OPENDOORSND, // 18 + CLOSEDOORSND, // 19 + __UNUSED__SND, // 20 + HALTSND, // 21 + RENTDEATH2SND, // 22 + ATKAUTOCHARGESND, // 23 + ATKCHARGEDSND, // 24 + RENTDEATH3SND, // 25 + ATKBURSTRIFLESND, // 26 + VITAL_GONESND, // 27 + SHOOTDOORSND, // 28 + RENTDEATH1SND, // 29 + GETBURSTRIFLESND, // 30 + GETAMMOSND, // 31 + SHOOTSND, // 32 + HEALTH1SND, // 33 + HEALTH2SND, // 34 + BONUS1SND, // 35 + BONUS2SND, // 36 + BONUS3SND, // 37 + GETIONCANNONSND, // 38 + ESCPRESSEDSND, // 39 + ELECAPPEARSND, // 40 + EXTRA_MANSND, // 41 + ELEV_BUTTONSND, // 42 + INTERROGATESND, // 43 + BONUS5SND, // 44 + BONUS4SND, // 45 + PUSHWALLSND, // 46 + TERM_BEEPSND, // 47 + ROLL_SCORESND, // 48 + TURRETSND, // 49 + EXPLODE1SND, // 50 + __UNUSED_2__SND, // 51 + SWATDIESND, // 52 + GGUARDHALTSND, // 53 + EXPLODE2SND, // 54 + BLUEBOYHALTSND, // 55 + PROGUARDDEATHSND, // 56 + DOGBOYHALTSND, // 57 + ENGINE_THRUSTSND, // 58 + SCANHALTSND, // 59 + GETCANNONSND, // 60 + LCANHALTSND, // 61 + PROHALTSND, // 62 + GGUARDDEATHSND, // 63 + BLUEBOYDEATHSND, // 64 + GOLDSTERNLAUGHSND, // 65 + SCIENTISTHALTSND, // 66 + SCIENTISTDEATHSND, // 67 + DOGBOYDEATHSND, // 68 + H_BEATSND, // 69 + SWATHALTSND, // 70 + SCANDEATHSND, // 71 + LCANDEATHSND, // 72 + INFORMDEATH2SND, // 73 + INFORMDEATH3SND, // 74 + GURNEYDEATHSND, // 75 + PRODEATH2SND, // 76 + PRODEATH3SND, // 77 + SWATDEATH2SND, // 78 + LCANBREAKSND, // 79 + SCANBREAKSND, // 80 + HTECHDOOROPENSND, // 81 + HTECHDOORCLOSESND, // 82 + ELECARCDAMAGESND, // 83 + PODHATCHSND, // 84 + ELECTSHOTSND, // 85 + ELECDIESND, // 86 + ATKGRENADESND, // 87 + CLAWATTACKSND, // 88 + PUNCHATTACKSND, // 89 + SPITATTACKSND, // 90 + PODDEATHSND, // 91 + PODHALTSND, // 92 + SWATDEATH3SND, // 93 + SCIDEATH2SND, // 94 + SCIDEATH3SND, // 95 + GOTTOKENSND, // 96 + SWITCHSND, // 97 + STATS1SND, // 98 + STATS2SND, // 99 + LASTSOUND + } soundnames; + +// +// Base offsets +// +#define STARTPCSOUNDS 0 +#define STARTADLIBSOUNDS 100 +#define STARTDIGISOUNDS 200 +#define STARTMUSIC 300 + +// +// Music names & indexes +// +typedef enum { + S2100A_MUS, // 0 + GOLDA_MUS, // 1 + APOGFNFM_MUS, // 2 + DRKHALLA_MUS, // 3 + FREEDOMA_MUS, // 4 + GENEFUNK_MUS, // 5 + TIMEA_MUS, // 6 + HIDINGA_MUS, // 7 + INCNRATN_MUS, // 8 + JUNGLEA_MUS, // 9 + LEVELA_MUS, // 10 + MEETINGA_MUS, // 11 + STRUTA_MUS, // 12 + RACSHUFL_MUS, // 13 + RUMBAA_MUS, // 14 + SEARCHNA_MUS, // 15 + THEME_MUS, // 16 + THEWAYA_MUS, // 17 + INTRIGEA_MUS, // 18 + LASTMUSIC + } musicnames; + +///////////////////////////////////////////////// +// +// Thanks for playing with MUSE! +// +///////////////////////////////////////////////// diff --git a/AUDIOFIR.H b/AUDIOFIR.H new file mode 100644 index 0000000..b88a5b3 --- /dev/null +++ b/AUDIOFIR.H @@ -0,0 +1,158 @@ +///////////////////////////////////////////////// +// +// MUSE Header for .FIR +// Created Wed Jul 06 15:43:15 1994 +// +///////////////////////////////////////////////// + +#define NUMSOUNDS 100 +#define NUMSNDCHUNKS 321 + +// +// Sound names & indexes +// +typedef enum { + HITWALLSND, // 0 + TERM_TYPESND, // 1 + GETPISTOLSND, // 2 + LIQUIDDIESND, // 3 + MOVEGUN2SND, // 4 + MOVEGUN1SND, // 5 + NOWAYSND, // 6 + SCOUT_ALERTSND, // 7 + GURNEYSND, // 8 + PLAYERDEATHSND, // 9 + CONCESSIONSSND, // 10 + ATKIONCANNONSND, // 11 + GETKEYSND, // 12 + WARPOUTSND, // 13 + WARPINSND, // 14 + ROBOT_SERVOSND, // 15 + INFORMANTDEATHSND, // 16 + GOLDSTERNHALTSND, // 17 + OPENDOORSND, // 18 + CLOSEDOORSND, // 19 + GETDETONATORSND, // 20 + HALTSND, // 21 + RENTDEATH2SND, // 22 + ATKAUTOCHARGESND, // 23 + ATKCHARGEDSND, // 24 + RADAR_POWERUPSND, // 25 + ATKBURSTRIFLESND, // 26 + VITAL_GONESND, // 27 + SHOOTDOORSND, // 28 + RENTDEATH1SND, // 29 + GETBURSTRIFLESND, // 30 + GETAMMOSND, // 31 + SHOOTSND, // 32 + HEALTH1SND, // 33 + HEALTH2SND, // 34 + BONUS1SND, // 35 + BONUS2SND, // 36 + BONUS3SND, // 37 + GETIONCANNONSND, // 38 + ESCPRESSEDSND, // 39 + ELECAPPEARSND, // 40 + EXTRA_MANSND, // 41 + ELEV_BUTTONSND, // 42 + INTERROGATESND, // 43 + BONUS5SND, // 44 + BONUS4SND, // 45 + PUSHWALLSND, // 46 + TERM_BEEPSND, // 47 + ROLL_SCORESND, // 48 + TURRETSND, // 49 + EXPLODE1SND, // 50 + __UNUSED_51__SND, // 51 + SWATDIESND, // 52 + GGUARDHALTSND, // 53 + EXPLODE2SND, // 54 + BLUEBOYHALTSND, // 55 + PROGUARDDEATHSND, // 56 + DOGBOYHALTSND, // 57 + ENGINE_THRUSTSND, // 58 + SCANHALTSND, // 59 + GETCANNONSND, // 60 + LCANHALTSND, // 61 + PROHALTSND, // 62 + GGUARDDEATHSND, // 63 + BLUEBOYDEATHSND, // 64 + GOLDSTERNLAUGHSND, // 65 + SCIENTISTHALTSND, // 66 + SCIENTISTDEATHSND, // 67 + DOGBOYDEATHSND, // 68 + H_BEATSND, // 69 + SWATHALTSND, // 70 + SCANDEATHSND, // 71 + LCANDEATHSND, // 72 + INFORMDEATH2SND, // 73 + INFORMDEATH3SND, // 74 + GURNEYDEATHSND, // 75 + PRODEATH2SND, // 76 + __UNUSED_77__SND, // 77 + SWATDEATH2SND, // 78 + LCANBREAKSND, // 79 + SCANBREAKSND, // 80 + HTECHDOOROPENSND, // 81 + HTECHDOORCLOSESND, // 82 + ELECARCDAMAGESND, // 83 + PODHATCHSND, // 84 + ELECTSHOTSND, // 85 + ELECDIESND, // 86 + ATKGRENADESND, // 87 + CLAWATTACKSND, // 88 + PUNCHATTACKSND, // 89 + SPITATTACKSND, // 90 + PODDEATHSND, // 91 + PODHALTSND, // 92 + __UNUSED_93__SND, // 93 + SCIDEATH2SND, // 94 + SCIDEATH3SND, // 95 + GOTTOKENSND, // 96 + SWITCHSND, // 97 + STATS1SND, // 98 + STATS2SND, // 99 + LASTSOUND + } soundnames; + +// +// Base offsets +// +#define STARTPCSOUNDS 0 +#define STARTADLIBSOUNDS 100 +#define STARTDIGISOUNDS 200 +#define STARTMUSIC 300 + +// +// Music names & indexes +// +typedef enum { + CATACOMB_MUS, // 0 + STICKS_MUS, // 1 + APOGFNFM_MUS, // 2 + PLOT_MUS, // 3 + CIRCLES_MUS, // 4 + LASTLAFF_MUS, // 5 + TOHELL_MUS, // 6 + FORTRESP_MUS, // 7 + GIVING_MUS, // 8 + HARTBEAT_MUS, // 9 + LURKING_MUS, // 10 + MAJMIN_MUS, // 11 + VACCINAP_MUS, // 12 + DARKNESS_MUS, // 13 + MONASTRY_MUS, // 14 + TOMBP_MUS, // 15 + TIME_MUS, // 16 + MOURNING_MUS, // 17 + SERPENT_MUS, // 18 + HISCORE_MUS, // 19 + THEME_MUS, // 20 + LASTMUSIC + } musicnames; + +///////////////////////////////////////////////// +// +// Thanks for playing with MUSE! +// +///////////////////////////////////////////////// diff --git a/AUDIOVSI.H b/AUDIOVSI.H new file mode 100644 index 0000000..9cea9de --- /dev/null +++ b/AUDIOVSI.H @@ -0,0 +1,158 @@ +///////////////////////////////////////////////// +// +// MUSE Header for .VSI +// Created Tue Aug 23 16:07:48 1994 +// +///////////////////////////////////////////////// + +#define NUMSOUNDS 100 +#define NUMSNDCHUNKS 321 + +// +// Sound names & indexes +// +typedef enum { + HITWALLSND, // 0 + TERM_TYPESND, // 1 + GETPISTOLSND, // 2 + LIQUIDDIESND, // 3 + MOVEGUN2SND, // 4 + MOVEGUN1SND, // 5 + NOWAYSND, // 6 + SCOUT_ALERTSND, // 7 + GURNEYSND, // 8 + PLAYERDEATHSND, // 9 + CONCESSIONSSND, // 10 + ATKIONCANNONSND, // 11 + GETKEYSND, // 12 + WARPOUTSND, // 13 + WARPINSND, // 14 + ROBOT_SERVOSND, // 15 + INFORMANTDEATHSND, // 16 + GOLDSTERNHALTSND, // 17 + OPENDOORSND, // 18 + CLOSEDOORSND, // 19 + GETDETONATORSND, // 20 + HALTSND, // 21 + RENTDEATH2SND, // 22 + ATKAUTOCHARGESND, // 23 + ATKCHARGEDSND, // 24 + RADAR_POWERUPSND, // 25 + ATKBURSTRIFLESND, // 26 + VITAL_GONESND, // 27 + SHOOTDOORSND, // 28 + RENTDEATH1SND, // 29 + GETBURSTRIFLESND, // 30 + GETAMMOSND, // 31 + SHOOTSND, // 32 + HEALTH1SND, // 33 + HEALTH2SND, // 34 + BONUS1SND, // 35 + BONUS2SND, // 36 + BONUS3SND, // 37 + GETIONCANNONSND, // 38 + ESCPRESSEDSND, // 39 + ELECAPPEARSND, // 40 + EXTRA_MANSND, // 41 + ELEV_BUTTONSND, // 42 + INTERROGATESND, // 43 + BONUS5SND, // 44 + BONUS4SND, // 45 + PUSHWALLSND, // 46 + TERM_BEEPSND, // 47 + ROLL_SCORESND, // 48 + TURRETSND, // 49 + EXPLODE1SND, // 50 + __UNUSED_51__SND, // 51 + SWATDIESND, // 52 + GGUARDHALTSND, // 53 + EXPLODE2SND, // 54 + BLUEBOYHALTSND, // 55 + PROGUARDDEATHSND, // 56 + DOGBOYHALTSND, // 57 + ENGINE_THRUSTSND, // 58 + SCANHALTSND, // 59 + GETCANNONSND, // 60 + LCANHALTSND, // 61 + PROHALTSND, // 62 + GGUARDDEATHSND, // 63 + BLUEBOYDEATHSND, // 64 + GOLDSTERNLAUGHSND, // 65 + SCIENTISTHALTSND, // 66 + SCIENTISTDEATHSND, // 67 + DOGBOYDEATHSND, // 68 + H_BEATSND, // 69 + SWATHALTSND, // 70 + SCANDEATHSND, // 71 + LCANDEATHSND, // 72 + INFORMDEATH2SND, // 73 + INFORMDEATH3SND, // 74 + GURNEYDEATHSND, // 75 + PRODEATH2SND, // 76 + __UNUSED_77__SND, // 77 + SWATDEATH2SND, // 78 + LCANBREAKSND, // 79 + SCANBREAKSND, // 80 + HTECHDOOROPENSND, // 81 + HTECHDOORCLOSESND, // 82 + ELECARCDAMAGESND, // 83 + PODHATCHSND, // 84 + ELECTSHOTSND, // 85 + ELECDIESND, // 86 + ATKGRENADESND, // 87 + CLAWATTACKSND, // 88 + PUNCHATTACKSND, // 89 + SPITATTACKSND, // 90 + PODDEATHSND, // 91 + PODHALTSND, // 92 + __UNUSED_93__SND, // 93 + SCIDEATH2SND, // 94 + SCIDEATH3SND, // 95 + GOTTOKENSND, // 96 + SWITCHSND, // 97 + STATS1SND, // 98 + STATS2SND, // 99 + LASTSOUND + } soundnames; + +// +// Base offsets +// +#define STARTPCSOUNDS 0 +#define STARTADLIBSOUNDS 100 +#define STARTDIGISOUNDS 200 +#define STARTMUSIC 300 + +// +// Music names & indexes +// +typedef enum { + CATACOMB_MUS, // 0 + STICKS_MUS, // 1 + APOGFNFM_MUS, // 2 + PLOT_MUS, // 3 + CIRCLES_MUS, // 4 + LASTLAFF_MUS, // 5 + TOHELL_MUS, // 6 + FORTRESS_MUS, // 7 + GIVING_MUS, // 8 + HARTBEAT_MUS, // 9 + LURKING_MUS, // 10 + MAJMIN_MUS, // 11 + VACCINAP_MUS, // 12 + DARKNESS_MUS, // 13 + MONASTRY_MUS, // 14 + TOMBP_MUS, // 15 + TIME_MUS, // 16 + MOURNING_MUS, // 17 + SERPENT_MUS, // 18 + HISCORE_MUS, // 19 + THEME_MUS, // 20 + LASTMUSIC + } musicnames; + +///////////////////////////////////////////////// +// +// Thanks for playing with MUSE! +// +///////////////////////////////////////////////// diff --git a/Blake Stone source code license.doc b/Blake Stone source code license.doc new file mode 100644 index 0000000..5a19cae Binary files /dev/null and b/Blake Stone source code license.doc differ diff --git a/C0.ASM b/C0.ASM new file mode 100644 index 0000000..a53fa68 --- /dev/null +++ b/C0.ASM @@ -0,0 +1,841 @@ + NAME c0 + PAGE 60,132 + LOCALS +;[]------------------------------------------------------------[] +;| C0.ASM -- Start Up Code | +;| | +;| Turbo C++ Run Time Library | +;| | +;| Copyright (c) 1987, 1991 by Borland International Inc. | +;| All Rights Reserved. | +;[]------------------------------------------------------------[] + + __C0__ = 1 +INCLUDE RULES.ASI + +; Segment and Group declarations + +_TEXT SEGMENT BYTE PUBLIC 'CODE' + ENDS +_FARDATA SEGMENT PARA PUBLIC 'FAR_DATA' + ENDS +_FARBSS SEGMENT PARA PUBLIC 'FAR_BSS' + ENDS +IFNDEF __TINY__ +_OVERLAY_ SEGMENT PARA PUBLIC 'OVRINFO' + ENDS +_1STUB_ SEGMENT PARA PUBLIC 'STUBSEG' + ENDS +ENDIF +_DATA SEGMENT PARA PUBLIC 'DATA' + ENDS +_INIT_ SEGMENT WORD PUBLIC 'INITDATA' +InitStart label byte + ENDS +_INITEND_ SEGMENT BYTE PUBLIC 'INITDATA' +InitEnd label byte + ENDS +_EXIT_ SEGMENT WORD PUBLIC 'EXITDATA' +ExitStart label byte + ENDS +_EXITEND_ SEGMENT BYTE PUBLIC 'EXITDATA' +ExitEnd label byte + ENDS +_CVTSEG SEGMENT WORD PUBLIC 'DATA' + ENDS +_SCNSEG SEGMENT WORD PUBLIC 'DATA' + ENDS +IFNDEF __HUGE__ + _BSS SEGMENT WORD PUBLIC 'BSS' + ENDS + _BSSEND SEGMENT BYTE PUBLIC 'BSSEND' + ENDS +ENDIF +IFNDEF __TINY__ + _STACK SEGMENT STACK 'STACK' + ENDS +ENDIF + + ASSUME CS:_TEXT, DS:DGROUP + +; External References + +extrn _main:DIST +extrn _exit:DIST +extrn __exit:DIST +extrn __nfile:word +extrn __setupio:near ;required! +extrn __stklen:word +IF LDATA EQ false +extrn __heaplen:word +ENDIF + + SUBTTL Start Up Code + PAGE +;/* */ +;/*-----------------------------------------------------*/ +;/* */ +;/* Start Up Code */ +;/* ------------- */ +;/* */ +;/*-----------------------------------------------------*/ +;/* */ +PSPHigh equ 00002h +PSPEnv equ 0002ch +PSPCmd equ 00080h + + public __AHINCR +__AHINCR equ 1000h + public __AHSHIFT +__AHSHIFT equ 12 + +IFDEF __NOFLOAT__ +MINSTACK equ 128 ; minimal stack size in words +ELSE +MINSTACK equ 256 ; minimal stack size in words +ENDIF +; +; At the start, DS and ES both point to the segment prefix. +; SS points to the stack segment except in TINY model where +; SS is equal to CS +; +_TEXT SEGMENT +IFDEF __TINY__ + ORG 100h +ENDIF +STARTX PROC NEAR +; Save general information, such as : +; DGROUP segment address +; DOS version number +; Program Segment Prefix address +; Environment address +; Top of far heap + +IFDEF __TINY__ + mov dx, cs ; DX = GROUP Segment address +ELSE + mov dx, DGROUP ; DX = GROUP Segment address +ENDIF +IFNDEF __BOSS__ + mov cs:DGROUP@@, dx ; __BOSS__ +ENDIF + mov ah, 30h + int 21h ; get DOS version number + mov bp, ds:[PSPHigh]; BP = Highest Memory Segment Addr + mov bx, ds:[PSPEnv] ; BX = Environment Segment address + mov ds, dx + mov _version@, ax ; Keep major and minor version number + mov _psp@, es ; Keep Program Segment Prefix address + mov _envseg@, bx ; Keep Environment Segment address + mov word ptr _heaptop@ + 2, bp +; +; Save several vectors and install default divide by zero handler. +; + call SaveVectors + +;=================== +; +; IDsoft - Check to make sure that we're running on a 286 or better + + pushf ; Save original flags + xor ax,ax ; Clear AX + push ax + popf ; Try to pop the 0 + pushf + pop ax ; Get results of popping 0 into flags + popf ; Restore original flags + or ax,ax + jns @@Have286 ; If no sign bit, have a 286 + + mov cx, lgth_no286MSG + mov dx, offset DGROUP: no286MSG + jmp MsgExit3 + +@@Have286: +; IDsoft - End of modifications (there's also a code segment string) +; +;=================== + +IFDEF __BOSS__ +; Determine if in real mode + mov ax,0FB42h ; find out if DPMI loader is here + mov bx,1 ; get info function + int 2fh ; + + push ax ; + mov ax, cs ; now, save DGROUP + add ax, cx ; + mov es, ax ; + mov dx, ds ; + mov es:DGROUP@@, dx ; + mov es:CSalias@@, ax ; + pop ax ; + +; cmp ax,0001h ; if not "TRUE" +; JNE InRealMode + +; 8 is the value of the alias selector +; in this system + MOV _protected@, cx + MOV _hugeincval@, cx + clc + mov ax, cx + xor cx, cx + or ax, ax + je @@gotshift +@@shiftcnt: + rcr ax,1 + jc @@gotshift + inc cx + jmp @@shiftcnt +@@gotshift: + mov _shiftcount@,cx + +; used by emulator +; PUSH DS +; MOV AX, 0E502H ; prot kernel function, get LDT alias +; INT 21H +; POP DS +; MOV _LDT@, AX + +; cmp _protected@,0001h ; if not "TRUE" +; JNE InRealMode + + .286P +IFE LDATA + mov dx, ds ; +; LSL AX, DX ; +; DEC AX ; + MOV AX, 0FFFEh ; + MOV SP, AX ; + MOV SS, DX ; +ENDIF + .8086 +; JMP BossSkip + +InRealMode label near + +ENDIF + +; Count the number of environment variables and compute the size. +; Each variable is ended by a 0 and a zero-length variable stops +; the environment. The environment can NOT be greater than 32k. + + les di, dword ptr _envLng@ + mov ax, di + mov bx, ax + mov cx, 07FFFh ; Environment cannot be > 32 Kbytes + cld +@@EnvLoop: + repnz scasb + jcxz InitFailed ; Bad environment !!! +IFDEF __BOSS__ + jmp InitOK +InitFailed: jmp near ptr _abort +InitOK: +ENDIF + + inc bx ; BX = Nb environment variables + cmp es:[di], al + jne @@EnvLoop ; Next variable ... + or ch, 10000000b + neg cx + mov _envLng@, cx ; Save Environment size + mov cx, dPtrSize / 2 + shl bx, cl + add bx, dPtrSize * 4 + and bx, not ((dPtrSize * 4) - 1) + mov _envSize@, bx ; Save Environment Variables Nb. + +IFNDEF __BOSS__ + +; Determine the amount of memory that we need to keep + +IFDEF _DSSTACK_ + mov dx, ds +ELSE + mov dx, ss +ENDIF + sub bp, dx ; BP = remaining size in paragraphs +IF LDATA + mov di, seg __stklen + mov es, di + mov di, es:__stklen ; DI = Requested stack size +ELSE + mov di, __stklen ; DI = Requested stack size +ENDIF +; +; Make sure that the requested stack size is at least MINSTACK words. +; + cmp di, 2*MINSTACK ; requested stack big enough ? + jae AskedStackOK + mov di, 2*MINSTACK ; no --> use minimal value +IF LDATA + mov es:__stklen, di ; override requested stack size +ELSE + mov __stklen, di ; override requested stack size +ENDIF + +AskedStackOK label near +IFDEF _DSSTACK_ + add di, offset DGROUP: edata@ + jb InitFailed ; DATA segment can NOT be > 64 Kbytes +ENDIF +IF LDATA EQ false + add di, __heaplen + jb InitFailed ; DATA segment can NOT be > 64 Kbytes +ENDIF + mov cl, 4 + shr di, cl ; $$$ Do not destroy CL $$$ + inc di ; DI = DS size in paragraphs + cmp bp, di +IF LDATA EQ false + jb InitFailed ; Not enough memory + cmp __stklen, 0 + je ExpandDS ; Expand DS up to 64 Kb + cmp __heaplen, 0 + jne ExcessOfMemory ; Much more available than needed +ExpandDS label near + mov di, 1000h + cmp bp, di + ja ExcessOfMemory ; Enough to run the program + mov di, bp + jmp short ExcessOfMemory ; Enough to run the program +ELSE + jnb ExcessOfMemory ; Much more available than needed +ENDIF + +; All initialization errors arrive here + +InitFailed label near + jmp near ptr _abort + +; Return to DOS the amount of memory in excess +; Set far heap base and pointer + +ExcessOfMemory label near + mov bx, di + add bx, dx + mov word ptr _heapbase@ + 2, bx + mov word ptr _brklvl@ + 2, bx + mov ax, _psp@ + sub bx, ax ; BX = Number of paragraphs to keep + mov es, ax ; ES = Program Segment Prefix address + mov ah, 04Ah + push di ; preserve DI + int 021h ; this call clobbers SI,DI,BP !!!!!! + pop di ; restore DI + + shl di, cl ; $$$ CX is still equal to 4 $$$ + + cli ; req'd for pre-1983 88/86s + mov ss, dx ; Set the program stack + mov sp, di + sti + +IFNDEF _DSSTACK_ + mov ax, seg __stklen + mov es, ax + mov es:__stklen, di ; If separate stack segment, save size +ENDIF + +ENDIF ; __BOSS__ + +IFNDEF __HUGE__ + +; Reset uninitialized data area + + xor ax, ax + mov es, cs:DGROUP@@ + mov di, offset DGROUP: bdata@ + mov cx, offset DGROUP: edata@ + sub cx, di + cld + rep stosb +ENDIF + +; If default number of file handles have changed then tell DOS + cmp __nfile, 20 + jbe @@NoChange + + cmp _osmajor@, 3 ; Check for >= DOS 3.3 + jb @@NoChange + ja @@DoChange + cmp _osminor@, 1Eh + jb @@NoChange +@@DoChange: + mov ax, 5801h ; Set last fit allocation + mov bx, 2 + int 21h + jc @@BadInit + + mov ah, 67h ; Expand handle table + mov bx, __nfile + int 21h + jc @@BadInit + + mov ah, 48h ; Allocate 16 bytes to find new + mov bx, 1 ; top of memory address + int 21h + jc @@BadInit + inc ax ; Adjust address to point after block + mov word ptr _heaptop@ + 2, ax + + dec ax ; Change back and release block + mov es, ax + mov ah, 49h + int 21h + jc @@BadInit + + mov ax, 5801h ; Set first fit allocation + mov bx, 0 + int 21h + jnc @@NoChange + +@@BadInit: jmp near ptr _abort + +@@NoChange: + +; Prepare main arguments + + mov ah, 0 + int 1ah ; get current BIOS time in ticks + mov word ptr _StartTime@,dx ; save it for clock() fn + mov word ptr _StartTime@+2,cx + or al,al ; was midnight flag set? + jz @@NotMidnight + mov ax,40h ; set BIOS midnight flag + mov es,ax ; at 40:70 + mov bx,70h + mov byte ptr es:[bx],1 + +@@NotMidnight: + xor bp,bp ; set BP to 0 for overlay mgr + + mov es, cs:DGROUP@@ + mov si,offset DGROUP:InitStart ;si = start of table + mov di,offset DGROUP:InitEnd ;di = end of table + call StartExit + +; ExitCode = main(argc,argv,envp); + +IF LDATA + push word ptr __C0environ+2 + push word ptr __C0environ + push word ptr __C0argv+2 + push word ptr __C0argv +ELSE + push word ptr __C0environ + push word ptr __C0argv +ENDIF + push __C0argc + call _main + +; Flush and close streams and files + + push ax + call _exit + +;--------------------------------------------------------------------------- +; _cleanup() call all #pragma exit cleanup routines. +; _checknull() check for null pointer zapping copyright message +; _terminate(int) exit program with error code +; +; These functions are called by exit(), _exit(), _cexit(), +; and _c_exit(). +;--------------------------------------------------------------------------- + +; Call cleanup routines + +__cleanup PROC DIST + PUBLIC __cleanup + + mov es, cs:DGROUP@@ + push si + push di + mov si,offset DGROUP:ExitStart + mov di,offset DGROUP:ExitEnd + call StartExit + pop di + pop si + ret +__cleanup ENDP + +; Check for null pointers before exit + +__checknull PROC DIST + PUBLIC __checknull + +IF LDATA EQ false + IFNDEF __TINY__ + push si + push di + mov es, cs:DGROUP@@ + xor ax, ax + mov si, ax + mov cx, lgth_CopyRight +ComputeChecksum label near + add al, es:[si] + adc ah, 0 + inc si + loop ComputeChecksum + sub ax, CheckSum + jz @@SumOk + mov cx, lgth_NullCheck + mov dx, offset DGROUP: NullCheck + call ErrorDisplay +@@SumOK: pop di + pop si + ENDIF +ENDIF + ret +__checknull ENDP + +; Exit to DOS + +__terminate PROC DIST + PUBLIC __terminate + mov bp,sp + mov ah,4Ch + mov al,[bp+cPtrSize] + int 21h ; Exit to DOS +__terminate ENDP + +STARTX ENDP + + SUBTTL Vector save/restore & default Zero divide routines + PAGE +;[]------------------------------------------------------------[] +;| | +;| Interrupt Save/Restore routines and default divide by zero | +;| handler. | +;| | +;[]------------------------------------------------------------[] + +ZeroDivision PROC FAR + mov cx, lgth_ZeroDivMSG + mov dx, offset DGROUP: ZeroDivMSG + jmp MsgExit3 +ZeroDivision ENDP + +;-------------------------------------------------------------------------- +; savevectors() +; +; Save vectors for 0, 4, 5 & 6 interrupts. This is for extended +; signal()/raise() support as the signal functions can steal these +; vectors during runtime. +;-------------------------------------------------------------------------- +SaveVectors PROC NEAR + push ds +; Save INT 0 + mov ax, 3500h + int 021h + mov word ptr _Int0Vector@, bx + mov word ptr _Int0Vector@+2, es +; Save INT 4 + mov ax, 3504h + int 021h + mov word ptr _Int4Vector@, bx + mov word ptr _Int4Vector@+2, es +; Save INT 5 + mov ax, 3505h + int 021h + mov word ptr _Int5Vector@, bx + mov word ptr _Int5Vector@+2, es +; Save INT 6 + mov ax, 3506h + int 021h + mov word ptr _Int6Vector@, bx + mov word ptr _Int6Vector@+2, es +; +; Install default divide by zero handler. +; + mov ax, 2500h + mov dx, cs + mov ds, dx + mov dx, offset ZeroDivision + int 21h + + pop ds + ret +SaveVectors ENDP + +;-------------------------------------------------------------------------- +; _restorezero() puts back all the vectors that SaveVectors took. +; +;NOTE : TSRs must BE AWARE that signal() functions which take these +; vectors will be deactivated if the keep() function is executed. +; If a TSR wants to use the signal functions when it is active it +; will have to save/restore these vectors itself when activated and +; deactivated. +;-------------------------------------------------------------------------- +__restorezero PROC DIST + PUBLIC __restorezero +IFDEF __HUGE__ + push ds + mov ds, cs: DGROUP@@ +ENDIF + push ds + mov ax, 2500h + lds dx, _Int0Vector@ + int 21h + pop ds + + push ds + mov ax, 2504h + lds dx, _Int4Vector@ + int 21h + pop ds + + push ds + mov ax, 2505h + lds dx, _Int5Vector@ + int 21h + pop ds + +IFNDEF __HUGE__ + push ds +ENDIF + mov ax, 2506h + lds dx, _Int6Vector@ + int 21h + pop ds + + ret + ENDP + +;------------------------------------------------------------------ +; Loop through a startup/exit (SE) table, +; calling functions in order of priority. +; ES:SI is assumed to point to the beginning of the SE table +; ES:DI is assumed to point to the end of the SE table +; First 64 priorities are reserved by Borland +;------------------------------------------------------------------ +PNEAR EQU 0 +PFAR EQU 1 +NOTUSED EQU 0ffh + +SE STRUC +calltype db ? ; 0=near,1=far,ff=not used +priority db ? ; 0=highest,ff=lowest +addrlow dw ? +addrhigh dw ? +SE ENDS + +StartExit proc near +@@Start: cmp si,offset DGROUP:InitStart ; startup or exit? + je @@StartLow ; it's startup + xor ah,ah ; start with high priority + jmp short @@SaveEnd +@@StartLow: mov ah,0ffh ;start with lowest priority +@@SaveEnd: mov dx,di ;set sentinel to end of table + mov bx,si ;bx = start of table + +@@TopOfTable: cmp bx,di ;and the end of the table? + je @@EndOfTable ;yes, exit the loop + cmp es:[bx.calltype],NOTUSED;check the call type + je @@Next + cmp si,offset DGROUP:InitStart ; startup or exit? + je @@CompareHigh ; it's startup + cmp ah,es:[bx.priority] ; it's exit + jmp short @@CheckPrior ; if priority too low, skip +@@CompareHigh: cmp es:[bx.priority],ah ;check the priority +@@CheckPrior: ja @@Next ;too high? skip + mov ah,es:[bx.priority] ;keep priority + mov dx,bx ;keep index in dx +@@Next: add bx,SIZE SE ;bx = next item in table + jmp @@TopOfTable + +@@EndOfTable: cmp dx,di ;did we exhaust the table? + je @@Done ;yes, quit + mov bx,dx ;bx = highest priority item + cmp es:[bx.calltype],PNEAR ;is it near or far? + mov es:[bx.calltype],NOTUSED;wipe the call type + push es ;save es + je @@NearCall + +@@FarCall: call DWORD PTR es:[bx.addrlow] + pop es ;restore es + jmp short @@Start + +@@NearCall: call WORD PTR es:[bx.addrlow] + pop es ;restore es + jmp short @@Start + +@@Done: ret + endp + +;------------------------------------------------------------------ + +ErrorDisplay PROC NEAR + mov ah, 040h + mov bx, 2 + int 021h + ret +ErrorDisplay ENDP + +_abort PROC DIST + PUBLIC _abort + mov cx, lgth_abortMSG + mov dx, offset DGROUP: abortMSG +MsgExit3 label near + mov ds, cs: DGROUP@@ + call ErrorDisplay +CallExit3 label near + mov ax, 3 + push ax + call __exit ; _exit(3); + ENDP + +; The DGROUP@ variable is used to reload DS with DGROUP + +PubSym@ DGROUP@, , __PASCAL__ + +IFDEF __BOSS__ +PubSym@ CSalias@,, __PASCAL__ +ENDIF + + +; __MMODEL is used to determine the memory model or the default +; pointer types at run time. + + public __MMODEL +__MMODEL dw MMODEL + +_TEXT ENDS + + SUBTTL Start Up Data Area + PAGE +;[]------------------------------------------------------------[] +;| Start Up Data Area | +;| | +;| WARNING Do not move any variables in the data | +;| segment unless you're absolutely sure | +;| that it does not matter. | +;[]------------------------------------------------------------[] + +_DATA SEGMENT + +; Magic symbol used by the debug info to locate the data segment + public DATASEG@ +DATASEG@ label byte + +; The CopyRight string must NOT be moved or changed without +; changing the null pointer check logic + +CopyRight db 4 dup(0) + db 'Borland C++ - Copyright 1991 Borland Intl.',0 +lgth_CopyRight equ $ - CopyRight + +IF LDATA EQ false +IFNDEF __TINY__ +CheckSum equ 00D5Ch +NullCheck db 'Null pointer assignment', 13, 10 +lgth_NullCheck equ $ - NullCheck +ENDIF +ENDIF + +ZeroDivMSG db 'Divide error', 13, 10 +lgth_ZeroDivMSG equ $ - ZeroDivMSG + +abortMSG db 'Abnormal program termination', 13, 10 +lgth_abortMSG equ $ - abortMSG + +; JAB - Added string for no 286 +no286MSG db 'Sorry, this program requires a 286 or better.', 13, 10 +lgth_no286MSG equ $ - no286MSG +; JAB - End of modifications + +; +; Interrupt vector save areas +; +; Interrupt vectors 0,4,5 & 6 are saved at startup and then restored +; when the program terminates. The signal/raise functions might +; steal these vectors during execution. +; +; Note: These vectors save area must not be altered +; without changing the save/restore logic. +; +PubSym@ _Int0Vector
, __CDECL__ +PubSym@ _Int4Vector
, __CDECL__ +PubSym@ _Int5Vector
, __CDECL__ +PubSym@ _Int6Vector
, __CDECL__ +; +; Miscellaneous variables +; +PubSym@ _C0argc, , __CDECL__ +dPtrPub@ _C0argv, 0, __CDECL__ +dPtrPub@ _C0environ, 0, __CDECL__ +PubSym@ _envLng, , __CDECL__ +PubSym@ _envseg, , __CDECL__ +PubSym@ _envSize, , __CDECL__ +PubSym@ _psp, , __CDECL__ +PubSym@ _version,
, __CDECL__ +PubSym@ _brklvl,
, __CDECL__ +PubSym@ _heaptop,
, __CDECL__ + +; If stack in DS and Large data model then override location of __emu + +IFDEF _DSSTACK_ +IF LDATA +public __emu +__emu db 044h DUP (0) + db 0CCh DUP (?) +ENDIF +ENDIF + +_DATA ENDS + + +_CVTSEG SEGMENT +PubSym@ _RealCvtVector,