2309 lines
53 KiB
C
2309 lines
53 KiB
C
// 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)<<TILESHIFT)+0x7FFF,
|
|
(((fixed)spot->tiley)<<TILESHIFT)+0x7FFF,
|
|
SPR_CLIP_EXP1, 7, 3+(US_RndT()&0x3),explosionobj);
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Check to see if we remove it.
|
|
//
|
|
if (remove)
|
|
{
|
|
// Remove static
|
|
spot->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;i<NUMAREAS;i++)
|
|
{
|
|
if (areaconnect[areanumber][i] && !areabyplayer[i])
|
|
{
|
|
areabyplayer[i] = true;
|
|
RecursiveConnect (i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ConnectAreas (void)
|
|
{
|
|
memset (areabyplayer,0,sizeof(areabyplayer));
|
|
areabyplayer[player->areanumber] = 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)<<TILESHIFT)+0x7FFF,
|
|
(((fixed)doorobjlist[door].tiley)<<TILESHIFT)+0x7FFF,
|
|
SPR_EXPLOSION_1,4,3,doorexplodeobj);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// BlastNearDoors()
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void BlastNearDoors(int tilex, int tiley)
|
|
{
|
|
unsigned char door;
|
|
char far *doorptr;
|
|
int x,y;
|
|
|
|
doorptr = (char far *)&tilemap[tilex][tiley];
|
|
|
|
for (x=-1;x<2;x++)
|
|
for (y=-64;y<128;y+=64)
|
|
{
|
|
if ((door = *(doorptr+x+y)) & 0x80)
|
|
{
|
|
door &= ~0x80;
|
|
TryBlastDoor(door);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// DropPlasmaDetonator() - Will move a Chaff from reserve to the player location.
|
|
//--------------------------------------------------------------------------
|
|
void DropPlasmaDetonator(void)
|
|
{
|
|
objtype *obj;
|
|
|
|
#pragma warn -pia
|
|
|
|
if (obj = MoveHiddenOfs(plasma_detonator_reserveobj,plasma_detonatorobj,player->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<NumEAWalls; loop++)
|
|
{
|
|
// unsigned far *map=mapsegs[0]+farmapylookup[eaList[loop].tiley]+eaList[loop].tilex;
|
|
unsigned far *map1=mapsegs[1]+farmapylookup[eaList[loop].tiley]+eaList[loop].tilex;
|
|
|
|
// Limit the number of aliens spawned by each outlet.
|
|
//
|
|
if (eaList[loop].aliens_out > 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<<TILESHIFT)+((fixed)TILEGLOBAL/2);
|
|
temp.y = ((fixed)temp.tiley<<TILESHIFT)+((fixed)TILEGLOBAL/2);
|
|
if ((!CheckSight(player,&temp)) && (US_RndT() < 200))
|
|
continue;
|
|
|
|
// Spawn Electro-Alien!
|
|
//
|
|
usedummy=true;
|
|
SpawnStand(en_electro_alien,temp.tilex,temp.tiley,0);
|
|
SD_PlaySound(ELECAPPEARSND);
|
|
usedummy=false;
|
|
if (new!=&dummyobj)
|
|
{
|
|
eaList[loop].aliens_out++;
|
|
new->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; loop<GoldsternInfo.SpawnCnt; loop++)
|
|
{
|
|
// Test for repeats - And avoid them!
|
|
//
|
|
|
|
if ((GoldsternInfo.SpawnCnt>1) && (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<<TILESHIFT)+((fixed)TILEGLOBAL/2);
|
|
temp.y = ((fixed)temp.tiley<<TILESHIFT)+((fixed)TILEGLOBAL/2);
|
|
if (!CheckSight(player,&temp))
|
|
continue;
|
|
|
|
// Mark to spawn Dr Goldstern
|
|
//
|
|
|
|
GoldsternInfo.LastIndex = loop;
|
|
if (gamestate.mapon==9)
|
|
GoldsternInfo.WaitTime = 60;
|
|
else
|
|
if (GoldsternInfo.flags == GS_FIRSTTIME)
|
|
GoldsternInfo.WaitTime = MIN_GOLDIE_FIRST_WAIT + Random(MAX_GOLDIE_FIRST_WAIT-MIN_GOLDIE_FIRST_WAIT); // Reinit Delay Timer before spawning on new position
|
|
else
|
|
GoldsternInfo.WaitTime = MIN_GOLDIE_WAIT + Random(MAX_GOLDIE_WAIT-MIN_GOLDIE_WAIT); // Reinit Delay Timer before spawning on new position
|
|
|
|
GoldsternInfo.flags = GS_COORDFOUND;
|
|
break;
|
|
}
|
|
}
|
|
|