MuckyFoot-UrbanChaos/fallen/Source/Special.cpp
2017-05-20 11:14:17 +10:00

1844 lines
38 KiB
C++

// Special.cpp
// Guy Simmons, 28th March 1998.
//
//
//
// WILLIAM SHAKESPEARE (1564-1616)
//
// Shall I compare thee to a summer's day?
// Thou art more lovely and more temperate.
// Rough winds do shake the darling buds of May,
// And summer's lease hath all too short a date.
// Sometime too hot the eye of heaven shines,
// And often is his gold complexion dimm'd;
// And every fair from fair sometime declines,
// By chance or nature's changing course untrimm'd;
// But thy eternal summer shall not fade
// Nor lose possession of that fair thou ow'st;
// Nor shall Death brag thou wander'st in his shade,
// When in eternal lines to time thou grow'st:
// So long as men can breathe or eyes can see,
// So long lives this, and this gives life to thee.
//
#include "Game.h"
#include "StateDef.h"
#include "Special.h"
#include "eway.h"
#include "pcom.h"
#include "night.h"
#include "dirt.h"
#include "animate.h"
#include "cnet.h"
#include "pow.h"
#include "memory.h"
#include "xlat_str.h"
#include "sound.h"
#include "grenade.h"
#ifndef PSX
#include "c:\fallen\ddengine\headers\panel.h"
#else
#include "c:\fallen\psxeng\headers\panel.h"
#endif
extern void add_damage_text(SWORD x,SWORD y,SWORD z,CBYTE *text);
SPECIAL_Info SPECIAL_info[SPECIAL_NUM_TYPES] =
{
{"None", 0, 0 },
{"Key", PRIM_OBJ_ITEM_KEY, SPECIAL_GROUP_USEFUL },
{"Gun", PRIM_OBJ_ITEM_GUN, SPECIAL_GROUP_ONEHANDED_WEAPON },
{"Health", PRIM_OBJ_ITEM_HEALTH, SPECIAL_GROUP_COOKIE },
{"Bomb", PRIM_OBJ_THERMODROID, SPECIAL_GROUP_STRANGE },
{"Shotgun", PRIM_OBJ_ITEM_SHOTGUN, SPECIAL_GROUP_TWOHANDED_WEAPON },
{"Knife", PRIM_OBJ_ITEM_KNIFE, SPECIAL_GROUP_ONEHANDED_WEAPON },
{"Explosives", PRIM_OBJ_ITEM_EXPLOSIVES, SPECIAL_GROUP_USEFUL },
{"Grenade", PRIM_OBJ_ITEM_GRENADE, SPECIAL_GROUP_ONEHANDED_WEAPON },
{"Ak47", PRIM_OBJ_ITEM_AK47, SPECIAL_GROUP_TWOHANDED_WEAPON },
{"Mine", PRIM_OBJ_MINE, SPECIAL_GROUP_STRANGE },
{"Thermodroid", PRIM_OBJ_THERMODROID, SPECIAL_GROUP_STRANGE },
{"Baseballbat", PRIM_OBJ_ITEM_BASEBALLBAT, SPECIAL_GROUP_TWOHANDED_WEAPON },
{"Ammo pistol", PRIM_OBJ_ITEM_AMMO_PISTOL, SPECIAL_GROUP_AMMO },
{"Ammo shotgun", PRIM_OBJ_ITEM_AMMO_SHOTGUN, SPECIAL_GROUP_AMMO },
{"Ammo AK47", PRIM_OBJ_ITEM_AMMO_AK47, SPECIAL_GROUP_AMMO },
{"Keycard", PRIM_OBJ_ITEM_KEYCARD, SPECIAL_GROUP_USEFUL },
{"File", PRIM_OBJ_ITEM_FILE, SPECIAL_GROUP_USEFUL },
{"Floppy_disk", PRIM_OBJ_ITEM_FLOPPY_DISK, SPECIAL_GROUP_USEFUL },
{"Crowbar", PRIM_OBJ_ITEM_CROWBAR, SPECIAL_GROUP_USEFUL },
{"Video", PRIM_OBJ_ITEM_VIDEO, SPECIAL_GROUP_USEFUL },
{"Gloves", PRIM_OBJ_ITEM_GLOVES, SPECIAL_GROUP_USEFUL },
{"WeedAway", PRIM_OBJ_ITEM_WEEDKILLER, SPECIAL_GROUP_USEFUL },
{"Badge", PRIM_OBJ_ITEM_TREASURE, SPECIAL_GROUP_COOKIE },
{"Red Car Keys", PRIM_OBJ_ITEM_KEY, SPECIAL_GROUP_USEFUL },
{"Blue Car Keys", PRIM_OBJ_ITEM_KEY, SPECIAL_GROUP_USEFUL },
{"Green Car Keys", PRIM_OBJ_ITEM_KEY, SPECIAL_GROUP_USEFUL },
{"Black Car Keys", PRIM_OBJ_ITEM_KEY, SPECIAL_GROUP_USEFUL },
{"White Car Keys", PRIM_OBJ_ITEM_KEY, SPECIAL_GROUP_USEFUL },
{"Wire Cutters", PRIM_OBJ_ITEM_WRENCH, SPECIAL_GROUP_USEFUL },
};
void free_special(Thing *s_thing);
//
// Adds/removes the special from a person's
//
void special_pickup(Thing *p_special, Thing *p_person)
{
ASSERT(p_special->Class == CLASS_SPECIAL);
ASSERT(p_person ->Class == CLASS_PERSON);
ASSERT(p_special->Genus.Special->SpecialType!=SPECIAL_MINE);
p_special->Genus.Special->OwnerThing = THING_NUMBER(p_person);
p_special->Genus.Special->NextSpecial = p_person->Genus.Person->SpecialList;
p_person->Genus.Person->SpecialList = THING_NUMBER(p_special);
if (p_special->SubState == SPECIAL_SUBSTATE_PROJECTILE)
{
p_special->SubState = SPECIAL_SUBSTATE_NONE;
}
}
void special_drop(Thing *p_special, Thing *p_person)
{
#ifndef NDEBUG
SLONG count = 0;
#endif
ASSERT(p_special->Class == CLASS_SPECIAL);
ASSERT(p_person ->Class == CLASS_PERSON );
ASSERT(p_special->Genus.Special->SpecialType!=SPECIAL_MINE);
UWORD next = p_person->Genus.Person->SpecialList;
UWORD *prev = &p_person->Genus.Person->SpecialList;
while(1)
{
#ifndef NDEBUG
ASSERT(count++ < 100);
#endif
if (next == NULL)
{
//
// We haven't found the special... is this an error? I guess so.
//
// ASSERT(0);
return;
}
Thing* p_next = TO_THING(next);
ASSERT(p_next->Class == CLASS_SPECIAL);
if (next == THING_NUMBER(p_special))
{
//
// This is the special we want to drop. Take it out the the linked list.
//
*prev = p_special->Genus.Special->NextSpecial;
p_special->Genus.Special->NextSpecial = NULL;
p_special->Genus.Special->OwnerThing = NULL;
{
SLONG gx;
SLONG gy;
SLONG gz;
extern void find_nice_place_near_person( // In person.cpp
Thing *p_person,
SLONG *nice_x, // 8-bits per mapsquare
SLONG *nice_y,
SLONG *nice_z);
find_nice_place_near_person(
p_person,
&gx,
&gy,
&gz);
p_special->WorldPos.X = gx << 8;
p_special->WorldPos.Y = gy << 8;
p_special->WorldPos.Z = gz << 8;
}
//
// Put it on the map.
//
add_thing_to_map(p_special);
if (p_person->Genus.Person->PlayerID == 0)
{
//
// Don't drop a gun with MAX ammo.
//
switch(p_special->Genus.Special->SpecialType)
{
case SPECIAL_SHOTGUN:
p_special->Genus.Special->ammo = (Random() & 0x1) + 2;
break;
case SPECIAL_AK47:
p_special->Genus.Special->ammo = (Random() & 0x7) + 6;
break;
}
}
p_special->Velocity = 5; // can't pick it up for 5 gameturns!
p_special->Genus.Special->counter = 0; // Can't pick it up for one second.
p_special->SubState = SPECIAL_SUBSTATE_NONE;
if (next == p_person->Genus.Person->SpecialUse)
{
p_person->Genus.Person->SpecialUse = NULL;
}
if (next == p_person->Genus.Person->SpecialDraw)
{
p_person->Genus.Person->SpecialDraw = NULL;
}
return;
}
prev = &p_next->Genus.Special->NextSpecial;
next = p_next->Genus.Special->NextSpecial;
}
}
//
// Returns TRUE if the person is carrying a two-handed weapon.
//
SLONG person_has_twohanded_weapon(Thing *p_person)
{
return (person_has_special(p_person, SPECIAL_SHOTGUN) ||
person_has_special(p_person, SPECIAL_AK47 ) ||
person_has_special(p_person, SPECIAL_BASEBALLBAT));
}
//
// Should this person pick up the item?
//
SLONG should_person_get_item(Thing *p_person, Thing *p_special)
{
Thing *p_grenade;
Thing *p_explosives;
Thing *p_has;
/*
if(p_special->Velocity)
return(0);
*/
if (p_person->State == STATE_MOVEING &&
p_person->SubState == SUB_STATE_RUNNING_SKID_STOP)
{
return FALSE;
}
switch(p_special->Genus.Special->SpecialType)
{
case SPECIAL_GUN:
if (p_person->Flags & FLAGS_HAS_GUN)
{
//return p_person->Genus.Person->Ammo < SPECIAL_AMMO_IN_A_PISTOL; // If less than 15 rounds of ammo
return (p_person->Genus.Person->ammo_packs_pistol<255-SPECIAL_AMMO_IN_A_PISTOL);
}
else
{
//
// Always pick up a gun if you haven't got one.
//
return TRUE;
}
case SPECIAL_HEALTH:
return (p_person->Genus.Person->Health < health[p_person->Genus.Person->PersonType]); // If not at maximum health
case SPECIAL_BOMB:
return FALSE;
case SPECIAL_AK47:
p_has = person_has_special(p_person, SPECIAL_AK47);
return (!p_has || p_person->Genus.Person->ammo_packs_ak47 < 255-SPECIAL_AMMO_IN_A_AK47);
case SPECIAL_BASEBALLBAT:
//
// We can only carry one two-handed weapon at once.
//
return !person_has_special(p_person, SPECIAL_BASEBALLBAT);
case SPECIAL_SHOTGUN:
p_has = person_has_special(p_person, SPECIAL_SHOTGUN);
return (!p_has || p_person->Genus.Person->ammo_packs_shotgun < 255-SPECIAL_AMMO_IN_A_SHOTGUN);
case SPECIAL_KNIFE:
//
// We can only have one knife.
//
return !person_has_special(p_person, SPECIAL_KNIFE);
case SPECIAL_EXPLOSIVES:
p_explosives = person_has_special(p_person, SPECIAL_EXPLOSIVES);
//
// You can only carry 4 explosives.
//
return (p_explosives == NULL || p_explosives->Genus.Special->ammo < 4);
case SPECIAL_GRENADE:
p_grenade = person_has_special(p_person, SPECIAL_GRENADE);
//
// You can only carry 8 grenades.
//
return (p_grenade == NULL || p_grenade->Genus.Special->ammo < 8);
//
// Only pickup ammo when you have that weapon.
//
case SPECIAL_AMMO_SHOTGUN:
return(p_person->Genus.Person->ammo_packs_shotgun<255-SPECIAL_AMMO_IN_A_SHOTGUN);
return (SLONG) person_has_special(p_person, SPECIAL_SHOTGUN);
case SPECIAL_AMMO_AK47:
return(p_person->Genus.Person->ammo_packs_ak47<255-SPECIAL_AMMO_IN_A_AK47);
return (SLONG) person_has_special(p_person, SPECIAL_AK47);
case SPECIAL_AMMO_PISTOL:
return(p_person->Genus.Person->ammo_packs_pistol<255-SPECIAL_AMMO_IN_A_PISTOL);
return p_person->Flags & FLAGS_HAS_GUN;
case SPECIAL_MINE:
return(FALSE); //added my MD
default:
return TRUE;
}
}
//
// The player person has picked up the given item.
//
void person_get_item(Thing *p_person, Thing *p_special)
{
Thing *p_gun;
SLONG keep = FALSE;
SLONG x_message = 0;
SLONG overflow = 0; // ammo left over
// PANEL_new_text(0,4000," PICKUP special %d \n",p_special->Genus.Special->SpecialType);
switch(p_special->Genus.Special->SpecialType)
{
case SPECIAL_GUN:
if (p_person->Flags & FLAGS_HAS_GUN)
{
x_message = X_AMMO;
overflow=(SWORD)p_person->Genus.Person->Ammo + p_special->Genus.Special->ammo;
while (overflow>15)
{
p_person->Genus.Person->ammo_packs_pistol += 15;
overflow-=15;
}
p_person->Genus.Person->Ammo=overflow;
/* if ((SWORD)p_person->Genus.Person->Ammo + p_special->Genus.Special->ammo<256)
p_person->Genus.Person->Ammo += p_special->Genus.Special->ammo;
else
p_person->Genus.Person->Ammo = 255;*/
}
else
{
x_message = X_GUN;
p_person->Genus.Person->Ammo = p_special->Genus.Special->ammo;
p_person->Flags |= FLAGS_HAS_GUN;
}
break;
case SPECIAL_HEALTH:
x_message = X_HEALTH;
p_person->Genus.Person->Health += 100;
if (p_person->Genus.Person->Health > health[p_person->Genus.Person->PersonType])
{
p_person->Genus.Person->Health = health[p_person->Genus.Person->PersonType];
// p_person->Genus.Person->Health = 200;
}
break;
case SPECIAL_AMMO_SHOTGUN:
p_person->Genus.Person->ammo_packs_shotgun += SPECIAL_AMMO_IN_A_SHOTGUN;
x_message = X_AMMO;
break;
case SPECIAL_AMMO_AK47:
p_person->Genus.Person->ammo_packs_ak47 += SPECIAL_AMMO_IN_A_AK47;
x_message = X_AMMO;
break;
case SPECIAL_AMMO_PISTOL:
p_person->Genus.Person->ammo_packs_pistol += SPECIAL_AMMO_IN_A_PISTOL;
x_message = X_AMMO;
break;
case SPECIAL_MINE:
//
// You can't pick up mines any more.
//
ASSERT(0);
/*
ASSERT(p_special->SubState == SPECIAL_SUBSTATE_ACTIVATED);
//
// When the special comes to draw itself, it will draw itself at that
// person's hand. We don't remove it from the map or free up the special.
//
special_pickup(p_special, p_person);
keep = TRUE;
*/
break;
case SPECIAL_SHOTGUN:
{
Thing *p_gun = person_has_special(p_person, SPECIAL_SHOTGUN);
if (p_gun)
{
/* p_has->Genus.Special->ammo += p_special->Genus.Special->ammo;
if (p_has->Genus.Special->ammo > SPECIAL_AMMO_IN_A_SHOTGUN)
{
p_has->Genus.Special->ammo = SPECIAL_AMMO_IN_A_SHOTGUN;
}*/
overflow=(SWORD)p_gun->Genus.Special->ammo + p_special->Genus.Special->ammo;
while (overflow>SPECIAL_AMMO_IN_A_SHOTGUN)
{
p_person->Genus.Person->ammo_packs_shotgun += SPECIAL_AMMO_IN_A_SHOTGUN;
overflow-=SPECIAL_AMMO_IN_A_SHOTGUN;
}
p_gun->Genus.Special->ammo=overflow;
x_message = X_AMMO;
break;
}
else
{
special_pickup(p_special, p_person);
//
// Make sure it isn't drawn!
//
remove_thing_from_map(p_special);
//
// Dont free up the special.
//
keep = TRUE;
x_message = X_SHOTGUN;
break;
}
}
case SPECIAL_AK47:
{
Thing *p_gun = person_has_special(p_person, SPECIAL_AK47);
if (p_gun)
{
/* p_has->Genus.Special->ammo += p_special->Genus.Special->ammo;
if (p_has->Genus.Special->ammo > SPECIAL_AMMO_IN_A_AK47)
{
p_has->Genus.Special->ammo = SPECIAL_AMMO_IN_A_AK47;
}*/
overflow=(SWORD)p_gun->Genus.Special->ammo + p_special->Genus.Special->ammo;
while (overflow>SPECIAL_AMMO_IN_A_AK47)
{
p_person->Genus.Person->ammo_packs_ak47 += SPECIAL_AMMO_IN_A_AK47;
overflow-=SPECIAL_AMMO_IN_A_AK47;
}
p_gun->Genus.Special->ammo=overflow;
x_message = X_AMMO;
}
else
{
special_pickup(p_special, p_person);
//
// Make sure it isn't drawn!
//
remove_thing_from_map(p_special);
//
// Dont free up the special.
//
keep = TRUE;
x_message = X_AK;
}
break;
}
case SPECIAL_GRENADE:
{
Thing *p_has = person_has_special(p_person, SPECIAL_GRENADE);
if (p_has)
{
if(p_has->Genus.Special->ammo<100) //just to be on the safe side
p_has->Genus.Special->ammo += SPECIAL_AMMO_IN_A_GRENADE;
x_message = X_AMMO;
}
else
{
special_pickup(p_special, p_person);
//
// Make sure it isn't drawn!
//
remove_thing_from_map(p_special);
//
// Dont free up the special.
//
keep = TRUE;
x_message = X_GRENADE;
}
break;
}
case SPECIAL_EXPLOSIVES:
{
Thing *p_has = person_has_special(p_person, SPECIAL_EXPLOSIVES);
if (p_has)
{
p_has->Genus.Special->ammo += 1;
}
else
{
special_pickup(p_special, p_person);
//
// Make sure it isn't drawn!
//
remove_thing_from_map(p_special);
//
// Dont free up the special.
//
keep = TRUE;
}
if(p_special->SubState == SPECIAL_SUBSTATE_ACTIVATED)
{
//This is active lets make it inactive
p_special->SubState = SPECIAL_SUBSTATE_NONE;
}
x_message = X_EXPLOSIVES;
break;
}
case SPECIAL_TREASURE:
{
Thing *darci = NET_PERSON(0);
//
// Has a player picked us up?
//
if (!(darci->Genus.Person->Flags & FLAG_PERSON_DRIVING))
{
switch(p_special->Draw.Mesh->ObjectId)
{
case 81:
x_message = X_STA_INCREASED;
NET_PLAYER(0)->Genus.Player->Stamina++;
break;
case 94:
x_message = X_STR_INCREASED;
NET_PLAYER(0)->Genus.Player->Strength++;
break;
case 71:
x_message = X_REF_INCREASED;
NET_PLAYER(0)->Genus.Player->Skill++;
break;
case 39:
x_message = X_CON_INCREASED;
NET_PLAYER(0)->Genus.Player->Constitution++;
break;
default:
ASSERT(0);
break;
}
}
#ifdef PSX
PANEL_icon_time=30;
#endif
extern SLONG stat_count_bonus;
stat_count_bonus++;
}
keep = FALSE;
break;
default:
if (p_special->Genus.Special->SpecialType == SPECIAL_KNIFE) {x_message = X_KNIFE;}
if (p_special->Genus.Special->SpecialType == SPECIAL_BASEBALLBAT) {x_message = X_BASEBALL_BAT;}
if (p_special->Genus.Special->SpecialType == SPECIAL_KEY) {x_message = X_KEYCARD;}
if (p_special->Genus.Special->SpecialType == SPECIAL_VIDEO) {x_message = X_VIDEO;}
//
// Use it by default? Nope. Add it to the person's linked list of
// items carried.
//
special_pickup(p_special, p_person);
//
// Make sure it isn't drawn!
//
remove_thing_from_map(p_special);
//
// Dont free up the special.
//
keep = TRUE;
break;
}
if (x_message && p_person->Genus.Person->PlayerID)
{
/*
add_damage_text(
p_special->WorldPos.X >> 8,
p_special->WorldPos.Y + 0x2000 >> 8,
p_special->WorldPos.Z >> 8,
XLAT_str_ptr(x_message));
*/
PANEL_new_info_message(XLAT_str(x_message));
}
if (p_special->Genus.Special->waypoint)
{
//
// Tell the waypoint system.
//
EWAY_item_pickedup(p_special->Genus.Special->waypoint);
}
if (!keep)
{
//
// Don't need the special any more.
//
free_special(p_special);
}
}
void special_activate_mine(Thing *p_mine)
{
ASSERT(p_mine->Class == CLASS_SPECIAL);
ASSERT(p_mine->Genus.Special->SpecialType == SPECIAL_MINE);
if (NET_PERSON(0)->Genus.Person->Target == THING_NUMBER(p_mine))
{
//
// Make the player choose a new target.
//
NET_PERSON(0)->Genus.Person->Target = NULL;
}
/*
PYRO_construct(
p_mine->WorldPos,
-1,
0xa0);
*/
#ifdef PSX
POW_create(
POW_CREATE_LARGE_SEMI,
p_mine->WorldPos.X,
p_mine->WorldPos.Y,
p_mine->WorldPos.Z,
0,0,0);
PYRO_create(p_mine->WorldPos,PYRO_DUSTWAVE);
#else
PYRO_create(p_mine->WorldPos,PYRO_FIREBOMB);
PYRO_create(p_mine->WorldPos,PYRO_DUSTWAVE);
#endif
MFX_play_xyz(THING_NUMBER(p_mine),SOUND_Range(S_EXPLODE_START,S_EXPLODE_END),0,p_mine->WorldPos.X,p_mine->WorldPos.Y,p_mine->WorldPos.Z);
create_shockwave(
p_mine->WorldPos.X >> 8,
p_mine->WorldPos.Y >> 8,
p_mine->WorldPos.Z >> 8,
0x200,
250,
NULL);
free_special(p_mine);
}
DIRT_Info special_di;
void special_normal(Thing *s_thing)
{
SLONG i;
SLONG dx;
SLONG dy;
SLONG dz;
SLONG dist;
if (s_thing->Flags & FLAG_SPECIAL_HIDDEN)
{
//
// Counter is the ob this specail is inside!
//
}
else
{
s_thing->Genus.Special->counter += 16 * TICK_RATIO >> TICK_SHIFT;
}
if (s_thing->SubState == SPECIAL_SUBSTATE_PROJECTILE)
{
SpecialPtr ss;
SLONG velocity;
SLONG ground;
ss = s_thing->Genus.Special;
velocity = ss->timer;
velocity -= 0x8000;
velocity -= 256 * TICK_RATIO >> TICK_SHIFT;
s_thing->WorldPos.Y += velocity * TICK_RATIO >> TICK_SHIFT;
ground = PAP_calc_map_height_at(s_thing->WorldPos.X >> 8, s_thing->WorldPos.Z >> 8) + 0x30 << 8;
if (s_thing->WorldPos.Y < ground)
{
velocity = abs(velocity);
velocity >>= 1;
s_thing->WorldPos.Y = ground;
if (velocity < 0x100)
{
s_thing->SubState = SPECIAL_SUBSTATE_NONE;
s_thing->Genus.Special->timer = 0;
return;
}
}
velocity += 0x8000;
SATURATE(velocity, 0, 0xffff);
ss->timer= velocity;
return;
}
else
{
/*
ASSERT(s_thing->Velocity>=0 &&s_thing->Velocity<10);
if(s_thing->Velocity)
s_thing->Velocity--; // dropped things cant be picked up for a few gameturns.
*/
}
if (s_thing->Genus.Special->OwnerThing && s_thing->Genus.Special->SpecialType != SPECIAL_EXPLOSIVES)
{
Thing *p_owner = TO_THING(s_thing->Genus.Special->OwnerThing);
ASSERT(s_thing->Genus.Special->SpecialType != SPECIAL_MINE);
ASSERT(s_thing->Genus.Special->SpecialType != SPECIAL_NONE);
ASSERT((s_thing->Flags&FLAGS_ON_MAPWHO)==0);
//
// This special is being carried by someone- it can't be on the mapwho
//
s_thing->WorldPos = p_owner->WorldPos;
if (s_thing->Genus.Special->SpecialType == SPECIAL_GRENADE &&
s_thing->SubState == SPECIAL_SUBSTATE_ACTIVATED)
{
SLONG ticks = 16 * TICK_RATIO >> TICK_SHIFT;
if (s_thing->Genus.Special->timer < ticks)
{
//
// The grenade has gone off!
//
#ifndef PSX
CreateGrenadeExplosion(s_thing->WorldPos.X, s_thing->WorldPos.Y + 2256, s_thing->WorldPos.Z, p_owner);
#else
POW_create(
POW_CREATE_LARGE_SEMI,
s_thing->WorldPos.X,
s_thing->WorldPos.Y,
s_thing->WorldPos.Z,0,0,0);
create_shockwave(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y >> 8,
s_thing->WorldPos.Z >> 8,
0x300,
500,
p_owner);
#endif
p_owner->Genus.Person->SpecialUse = NULL;
if (s_thing->Genus.Special->ammo == 1)
{
special_drop(s_thing, p_owner);
free_special(s_thing);
}
else
{
s_thing->SubState = SPECIAL_SUBSTATE_NONE; // let's reset the grenade
s_thing->Genus.Special->ammo -= 1;
}
}
else
{
s_thing->Genus.Special->timer -= ticks;
}
}
}
else
{
//
// This special is lying on the ground. Mostly they rotate.
//
if (s_thing->Genus.Special->SpecialType == SPECIAL_BOMB &&
s_thing->SubState == SPECIAL_SUBSTATE_NONE)
{
//
// Deactivated bombs don't rotate.
//
}
else
{
if (s_thing->Genus.Special->SpecialType!=SPECIAL_MINE) // mines no longer rotate
if (s_thing->Genus.Special->SpecialType!=SPECIAL_EXPLOSIVES || s_thing->SubState != SPECIAL_SUBSTATE_ACTIVATED) // explosives waiting to go off no longer rotate
s_thing->Draw.Mesh->Angle = (s_thing->Draw.Mesh->Angle+32)&2047;
}
switch(s_thing->Genus.Special->SpecialType)
{
case SPECIAL_BOMB:
//
// Has the waypoint that created us gone active?
//
if (s_thing->Genus.Special->waypoint)
{
if (s_thing->SubState == SPECIAL_SUBSTATE_ACTIVATED)
{
if (EWAY_is_active(s_thing->Genus.Special->waypoint))
{
//
// Explode this bomb.
//
#ifndef PSX
PYRO_construct(
s_thing->WorldPos,
-1,
256);
#else
POW_create(
POW_CREATE_LARGE_SEMI,
s_thing->WorldPos.X,
s_thing->WorldPos.Y,
s_thing->WorldPos.Z,0,0,0);
#endif
//
// Don't need the special any more.
//
free_special(s_thing);
}
}
}
break;
case SPECIAL_EXPLOSIVES:
if (s_thing->SubState == SPECIAL_SUBSTATE_ACTIVATED)
{
SLONG tickdown = 0x10 * TICK_RATIO >> TICK_SHIFT;
//
// Time to explode?
//
if (s_thing->Genus.Special->timer <= tickdown)
{
//
// Bang!
//
#ifndef PSX
PYRO_construct(
s_thing->WorldPos,
1|4|8|64,
0xa0);
#else
POW_create(
POW_CREATE_LARGE_SEMI,
s_thing->WorldPos.X,
s_thing->WorldPos.Y,
s_thing->WorldPos.Z,0,0,0);
PYRO_construct(
s_thing->WorldPos,PYRO_DUSTWAVE,0xa0);
#endif
MFX_play_xyz(THING_NUMBER(s_thing),SOUND_Range(S_EXPLODE_MEDIUM,S_EXPLODE_BIG),0,s_thing->WorldPos.X,s_thing->WorldPos.Y,s_thing->WorldPos.Z);
{
SLONG cx,cz;
cx=s_thing->WorldPos.X>>8;
cz=s_thing->WorldPos.Z>>8;
DIRT_gust(s_thing,cx,cz,cx+0xa0,cz);
DIRT_gust(s_thing,cx+0xa0,cz,cx,cz);
}
create_shockwave(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y >> 8,
s_thing->WorldPos.Z >> 8,
0x500,
500,
TO_THING(s_thing->Genus.Special->OwnerThing));
//
// This is the end of the explosives.
//
free_special(s_thing);
}
else
{
s_thing->Genus.Special->timer -= tickdown;
}
}
else
{
if(!s_thing->Genus.Special->OwnerThing )
{
//
// not being carried round may as well see if anyone wants to pick me up
//
goto try_pickup; //I love goto
}
}
break;
case SPECIAL_MINE:
if (s_thing->Genus.Special->ammo > 0)
{
//
// This special has a counter ticking down to explode.
//
if (s_thing->Genus.Special->ammo == 1)
{
special_activate_mine(s_thing);
}
else
{
s_thing->Genus.Special->ammo -= 1;
}
}
else
if ((GAME_TURN&1) == (THING_NUMBER(s_thing)&1))
{
SLONG i;
SLONG j;
SLONG wx;
SLONG wy;
SLONG wz;
SLONG num_found = THING_find_sphere(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y >> 8,
s_thing->WorldPos.Z >> 8,
0x300,
THING_array,
THING_ARRAY_SIZE,
(1 << CLASS_PERSON) | (1 << CLASS_VEHICLE) | (1 << CLASS_BAT));
Thing *p_found;
for (i = 0; i < num_found; i++)
{
p_found = TO_THING(THING_array[i]);
switch(p_found->Class)
{
case CLASS_BAT:
if (p_found->Genus.Bat->type == BAT_TYPE_BALROG)
{
dx = abs(p_found->WorldPos.X - s_thing->WorldPos.X);
dz = abs(p_found->WorldPos.Z - s_thing->WorldPos.Z);
dist = QDIST2(dx,dz);
if (dist < 0x6000)
{
special_activate_mine(s_thing);
return;
}
}
break;
case CLASS_PERSON:
{
Thing *p_person = p_found;
dx = abs(p_person->WorldPos.X - s_thing->WorldPos.X);
dz = abs(p_person->WorldPos.Z - s_thing->WorldPos.Z);
dist = QDIST2(dx,dz);
if (dist < 0x3000)
{
SLONG fx;
SLONG fy;
SLONG fz;
calc_sub_objects_position(
p_person,
p_person->Draw.Tweened->AnimTween,
SUB_OBJECT_LEFT_FOOT,
&fx,
&fy,
&fz);
fx += p_person->WorldPos.X >> 8;
fy += p_person->WorldPos.Y >> 8;
fz += p_person->WorldPos.Z >> 8;
if (fy > (s_thing->WorldPos.Y + 0x2000 >> 8))
{
//
// This person has jumped over the mine!
//
}
else
{
//
// Activate the mine.
//
if (p_person->Genus.Person->pcom_ai== PCOM_AI_BODYGUARD && TO_THING(EWAY_get_person(p_person->Genus.Person->pcom_ai_other))->Genus.Person->PlayerID)
{
//
// players bodyguards dont trigger mines
//
}
else
{
special_activate_mine(s_thing);
return;
}
}
}
}
break;
case CLASS_VEHICLE:
{
SLONG wx;
SLONG wy;
SLONG wz;
Thing *p_vehicle = p_found;
//
// Find out where the wheels are.
//
vehicle_wheel_pos_init(p_vehicle);
for (j = 0; j < 4; j++)
{
vehicle_wheel_pos_get(
j,
&wx,
&wy,
&wz);
dx = abs(wx - (s_thing->WorldPos.X >> 8));
dy = abs(wy - (s_thing->WorldPos.Y >> 8));
dz = abs(wz - (s_thing->WorldPos.Z >> 8));
dist = QDIST3(dx,dy,dz);
if (dist < 0x50)
{
special_activate_mine(s_thing);
return;
}
}
}
break;
default:
ASSERT(0);
break;
}
}
}
break;
case SPECIAL_THERMODROID:
break;
case SPECIAL_TREASURE:
for (i = 0; i < NO_PLAYERS; i++)
{
Thing *darci = NET_PERSON(i);
//
// Has a player picked us up?
//
if (!(darci->Genus.Person->Flags & FLAG_PERSON_DRIVING))
{
dx = darci->WorldPos.X - s_thing->WorldPos.X >> 8;
dy = darci->WorldPos.Y - s_thing->WorldPos.Y >> 8;
dz = darci->WorldPos.Z - s_thing->WorldPos.Z >> 8;
dist = abs(dx) + abs(dy) + abs(dz);
if (dist < 0xa0)
{
SLONG x_message;
//
// Near enough to pick it up.
//
switch(s_thing->Draw.Mesh->ObjectId)
{
case 81:
x_message = X_STA_INCREASED;
add_damage_text(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y + 0x6000 >> 8,
s_thing->WorldPos.Z >> 8,
XLAT_str(X_STA_INCREASED));
NET_PLAYER(0)->Genus.Player->Stamina++;
break;
case 94:
x_message = X_STR_INCREASED;
add_damage_text(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y + 0x6000 >> 8,
s_thing->WorldPos.Z >> 8,
XLAT_str(X_STR_INCREASED));
NET_PLAYER(0)->Genus.Player->Strength++;
break;
case 71:
x_message = X_REF_INCREASED;
add_damage_text(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y + 0x6000 >> 8,
s_thing->WorldPos.Z >> 8,
XLAT_str(X_REF_INCREASED));
NET_PLAYER(0)->Genus.Player->Skill++;
break;
case 39:
x_message = X_CON_INCREASED;
add_damage_text(
s_thing->WorldPos.X >> 8,
s_thing->WorldPos.Y + 0x6000 >> 8,
s_thing->WorldPos.Z >> 8,
XLAT_str(X_CON_INCREASED));
NET_PLAYER(0)->Genus.Player->Constitution++;
break;
default:
ASSERT(0);
break;
}
PANEL_new_info_message(XLAT_str(x_message));
#ifdef PSX
PANEL_icon_time=30;
#endif
// CONSOLE_text(XLAT_str(X_FUSE_SET));
free_special(s_thing);
extern SLONG stat_count_bonus;
stat_count_bonus++;
// NET_PLAYER(i)->Genus.Player->Treasure += 1;
// darci->Genus.Person->Health = health[darci->Genus.Person->PersonType];
/*
{
CBYTE str[64];
sprintf(str, "Badge %d", NET_PLAYER(i)->Genus.Player->Treasure);
CONSOLE_text(str);
}
*/
//
// Reduce crime rate by 10%
//
// CRIME_RATE -= 10;
// SATURATE(CRIME_RATE, 0, 100);
}
}
}
break;
case SPECIAL_HEALTH:
extern SWORD health[];
if(NET_PERSON(0)->Genus.Person->Health>health[NET_PERSON(0)->Genus.Person->PersonType]-100)
{
break;
}
case SPECIAL_GRENADE:
if (s_thing->Genus.Special->SpecialType == SPECIAL_GRENADE &&
s_thing->SubState == SPECIAL_SUBSTATE_ACTIVATED)
{
//
// Don't pickup activated grenades.
//
break;
}
case SPECIAL_AK47:
case SPECIAL_SHOTGUN:
case SPECIAL_GUN:
case SPECIAL_KNIFE:
case SPECIAL_BASEBALLBAT:
case SPECIAL_AMMO_AK47:
case SPECIAL_AMMO_SHOTGUN:
case SPECIAL_AMMO_PISTOL:
default:
try_pickup:;
if (s_thing->Genus.Special->counter > 16 * 20)
{
if (s_thing->Flags & FLAGS_ON_MAPWHO)
{
Thing *darci = NET_PERSON(0);
//
// Has a player picked us up?
//
if (!(darci->Genus.Person->Flags & FLAG_PERSON_DRIVING))
{
dx = darci->WorldPos.X - s_thing->WorldPos.X >> 8;
dy = darci->WorldPos.Y - s_thing->WorldPos.Y >> 8;
dz = darci->WorldPos.Z - s_thing->WorldPos.Z >> 8;
dist = abs(dx) + abs(dy) + abs(dz);
if (dist < 0xa0)
{
if (should_person_get_item(darci, s_thing))
{
person_get_item(darci, s_thing);
}
}
}
}
}
break;
}
}
}
//---------------------------------------------------------------
#ifndef PSX
void init_specials(void)
{
//memset((UBYTE*)SPECIALS,0,sizeof(SPECIALS));
memset((UBYTE*)SPECIALS,0,sizeof(Special) * MAX_SPECIALS);
SPECIAL_COUNT = 0;
}
#endif
//---------------------------------------------------------------
SLONG find_empty_special(void)
{
SLONG c0;
for(c0=1;c0<MAX_SPECIALS;c0++)
{
if(SPECIALS[c0].SpecialType==SPECIAL_NONE)
{
return(c0);
}
}
return NULL;
}
Thing *alloc_special(
UBYTE type,
UBYTE substate,
SLONG world_x,
SLONG world_y,
SLONG world_z,
UWORD waypoint)
{
SLONG c0;
DrawMesh *dm;
Special *new_special;
Thing *special_thing = NULL;
SLONG special_index;
ASSERT(WITHIN(type, 1, SPECIAL_NUM_TYPES - 1));
// Run through the special array & find an unused one.
special_index = find_empty_special();
dm = alloc_draw_mesh();
special_thing = alloc_thing(CLASS_SPECIAL);
if (!special_index || !dm || !special_thing)
{
//
// Oh dear! Dealloc anythings we've got.
//
if (special_index)
{
TO_SPECIAL(special_index)->SpecialType = SPECIAL_NONE;
}
if (dm)
{
free_draw_mesh(dm);
}
if (special_thing)
{
free_thing(special_thing);
}
//
// Find another special and hijack it!
//
SLONG score;
SLONG best_score = -1;
Thing *best_thing = NULL;
SLONG list = thing_class_head[CLASS_SPECIAL];
while(list)
{
Thing *p_hijack = TO_THING(list);
ASSERT(p_hijack->Class == CLASS_SPECIAL);
list = p_hijack->NextLink;
//
// Good special to hijack?
//
score = -1;
if (!(p_hijack->Flags & FLAGS_ON_MAPWHO))
{
continue;
}
if (p_hijack->Genus.Special->OwnerThing)
{
continue;
}
switch(p_hijack->Genus.Special->SpecialType)
{
case SPECIAL_KNIFE:
case SPECIAL_BASEBALLBAT:
score += 4000;
break;
case SPECIAL_AMMO_PISTOL:
case SPECIAL_AMMO_SHOTGUN:
case SPECIAL_AMMO_AK47:
score += 3000;
break;
case SPECIAL_GUN:
case SPECIAL_GRENADE:
case SPECIAL_HEALTH:
case SPECIAL_SHOTGUN:
case SPECIAL_AK47:
score += 2000;
break;
default:
//
// Never hijack one of these...
//
continue;
}
SLONG dx = abs(p_hijack->WorldPos.X - NET_PERSON(0)->WorldPos.X) >> 16;
SLONG dz = abs(p_hijack->WorldPos.Z - NET_PERSON(0)->WorldPos.Z) >> 16;
if (dx + dz > 12)
{
score += dx;
score += dz;
if (score > best_score)
{
best_score = score;
best_thing = p_hijack;
}
}
}
if (best_thing)
{
remove_thing_from_map(best_thing);
special_index = SPECIAL_NUMBER(best_thing->Genus.Special);
special_thing = best_thing;
dm = special_thing->Draw.Mesh;
}
else
{
return NULL;
}
}
new_special = TO_SPECIAL(special_index);
new_special->SpecialType = type;
new_special->Thing = THING_NUMBER(special_thing);
new_special->waypoint = waypoint;
new_special->counter = 0;
new_special->OwnerThing = NULL;
special_thing->Genus.Special = new_special;
special_thing->State = STATE_NORMAL;
special_thing->SubState = substate;
special_thing->StateFn = special_normal;
// Create the visible object.
special_thing->Draw.Mesh = dm;
special_thing->DrawType = DT_MESH;
dm->Angle = 0;
dm->Tilt = 0;
dm->Roll = 0;
switch(type)
{
case SPECIAL_GUN: new_special->ammo = SPECIAL_AMMO_IN_A_PISTOL; break;
case SPECIAL_SHOTGUN: new_special->ammo = SPECIAL_AMMO_IN_A_SHOTGUN; break;
case SPECIAL_AK47: new_special->ammo = SPECIAL_AMMO_IN_A_AK47; break;
case SPECIAL_GRENADE: new_special->ammo = SPECIAL_AMMO_IN_A_GRENADE; break;
case SPECIAL_EXPLOSIVES: new_special->ammo = 1; break;
default:
new_special->ammo = 0;
break;
}
dm->ObjectId = SPECIAL_info[type].prim;
if(dm->ObjectId==PRIM_OBJ_ITEM_TREASURE)
{
switch(Random()&3)
{
case 0:
dm->ObjectId=71;
break;
case 1:
dm->ObjectId=94;
break;
case 2:
dm->ObjectId=81;
break;
case 3:
dm->ObjectId=39;
break;
}
}
special_thing->WorldPos.X = world_x << 8;
special_thing->WorldPos.Y = world_y << 8;
special_thing->WorldPos.Z = world_z << 8;
add_thing_to_map(special_thing);
if (world_y > PAP_calc_map_height_at(world_x, world_z) + 0x50)
{
//
// Make this item start dropping to the ground...
//
special_thing->SubState = SPECIAL_SUBSTATE_PROJECTILE;
special_thing->Genus.Special->timer = 0x8000; // 0x8000 => 0! (Its a UWORD)
}
return special_thing;
}
//---------------------------------------------------------------
void free_special(Thing *special_thing)
{
// Set the special type to none & free the thing.
special_thing->Genus.Special->SpecialType = SPECIAL_NONE;
free_draw_mesh(special_thing->Draw.Mesh);
remove_thing_from_map(special_thing);
free_thing(special_thing);
}
//---------------------------------------------------------------
Thing *person_has_special(Thing *p_person, SLONG special_type)
{
SLONG special;
Thing *p_special;
for (special = p_person->Genus.Person->SpecialList; special; special = p_special->Genus.Special->NextSpecial)
{
p_special = TO_THING(special);
if (p_special->Genus.Special->SpecialType == special_type)
{
return p_special;
}
}
return NULL;
}
void SPECIAL_throw_grenade(Thing *p_special)
{
Thing *p_person = TO_THING(p_special->Genus.Special->OwnerThing);
//
// Convert the grenade to some dirt.
//
if (!CreateGrenadeFromPerson(p_person, p_special->Genus.Special->timer))
{
// no room in grenade array
return;
}
if (p_special->Genus.Special->ammo == 1)
{
//
// Get rid of our special.
//
special_drop(p_special, p_person);
free_special(p_special);
p_person->Genus.Person->SpecialUse = NULL;
}
else
{
p_special->Genus.Special->ammo -= 1;
p_special->SubState = SPECIAL_SUBSTATE_NONE;
}
}
void SPECIAL_prime_grenade(Thing *p_special)
{
p_special->SubState = SPECIAL_SUBSTATE_ACTIVATED;
p_special->Genus.Special->timer = 16 * 20 * 6; // 6 second so self destruct.
}
/*
void SPECIAL_throw_mine(Thing *p_special)
{
UWORD dirt;
UWORD owner;
ASSERT(p_special->Genus.Special->SpecialType == SPECIAL_MINE);
ASSERT(p_special->SubState == SPECIAL_SUBSTATE_ACTIVATED);
ASSERT(p_special->Genus.Special->OwnerThing);
//
// Remember the owner...
//
owner = p_special->Genus.Special->OwnerThing;
//
// Create some dirt that is going to process the physics of the mine
// being thrown through the air.
//
dirt = DIRT_create_mine(TO_THING(p_special->Genus.Special->OwnerThing));
//
// Take it out of the person's special list.
//
special_drop(p_special, TO_THING(p_special->Genus.Special->OwnerThing));
//
// Link the special to the dirt that is going to process its physics.
//
p_special->SubState = SPECIAL_SUBSTATE_IS_DIRT;
p_special->Genus.Special->waypoint = dirt;
p_special->Genus.Special->OwnerThing = owner; // So we know who to blame for the explosion.
}
*/
void SPECIAL_set_explosives(Thing *p_person)
{
Thing *p_special;
p_special = person_has_special(p_person, SPECIAL_EXPLOSIVES);
ASSERT(p_special->Genus.Special->SpecialType == SPECIAL_EXPLOSIVES);
if (p_special)
{
if (p_special->Genus.Special->ammo == 1)
{
//
// Drop the special.
//
p_special->WorldPos = p_person->WorldPos;
special_drop(p_special, p_person);
if (p_person->Genus.Person->SpecialUse == THING_NUMBER(p_special))
{
p_person->Genus.Person->SpecialUse = NULL;
}
}
else
{
p_special->Genus.Special->ammo -= 1;
//
// Create a new explosives special...
//
p_special = alloc_special(
SPECIAL_EXPLOSIVES,
SPECIAL_SUBSTATE_NONE,
p_person->WorldPos.X >> 8,
p_person->WorldPos.Y >> 8,
p_person->WorldPos.Z >> 8,
NULL);
}
//
// Prime it!
//
p_special->SubState = SPECIAL_SUBSTATE_ACTIVATED;
p_special->Genus.Special->timer = 16 * 20 * 5; // 10 seconds so self destruct.
p_special->Genus.Special->OwnerThing = THING_NUMBER(p_person); // So we know who is responsible for the explosion.
//CONSOLE_text("Five second fuse set...");
/*
add_damage_text(
p_special->WorldPos.X >> 8,
p_special->WorldPos.Y + 0x2000 >> 8,
p_special->WorldPos.Z >> 8,
XLAT_str(X_FUSE_SET));
*/
PANEL_new_info_message(XLAT_str(X_FUSE_SET));
}
}