830 lines
16 KiB
C++
830 lines
16 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Quake 2 Engine/Sin/code/game/item.cpp $
|
|
// $Revision:: 51 $
|
|
// $Author:: Markd $
|
|
// $Date:: 11/12/98 9:20p $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
// $Log:: /Quake 2 Engine/Sin/code/game/item.cpp $
|
|
//
|
|
// 51 11/12/98 9:20p Markd
|
|
// fixed null sounds for snd_pickup
|
|
//
|
|
// 50 10/24/98 2:07p Aldie
|
|
// Mutants can only pickup health
|
|
//
|
|
// 49 10/22/98 10:22p Markd
|
|
// Put in support for game and level item script variables
|
|
//
|
|
// 48 10/19/98 12:05a Jimdose
|
|
// made all code use fast checks for inheritance (no text lookups when
|
|
// possible)
|
|
// isSubclassOf no longer requires ::_classinfo()
|
|
// got rid of warning in Set
|
|
//
|
|
// 47 10/14/98 1:18a Jimdose
|
|
// Got cross-level persistant info working
|
|
// Added amount_override
|
|
//
|
|
// 46 10/11/98 8:57p Aldie
|
|
// Don't print out error message unecessarily
|
|
//
|
|
// 45 10/10/98 1:28a Jimdose
|
|
// items no longer drop to the floor when loading savegames
|
|
//
|
|
// 44 10/09/98 2:06a Aldie
|
|
// Updated DMFLAGS
|
|
//
|
|
// 43 10/07/98 5:51p Markd
|
|
// Fixed small sized enemies getting resized and causing fall out of level
|
|
// errors
|
|
//
|
|
// 42 10/04/98 6:15p Markd
|
|
// fixed pickup sounds
|
|
//
|
|
// 41 9/29/98 5:59p Markd
|
|
// Added dialog_needed stuff
|
|
//
|
|
// 40 9/29/98 11:05a Markd
|
|
// Added respawnsound support
|
|
//
|
|
// 39 9/15/98 6:37p Markd
|
|
// Added RotatedBounds flag support
|
|
//
|
|
// 38 9/08/98 9:29p Markd
|
|
// fixed picking things up by using them
|
|
//
|
|
// 37 9/02/98 11:53a Markd
|
|
// Put in pickup by pressing the use key
|
|
//
|
|
// 36 9/01/98 7:45p Aldie
|
|
// Added itemname
|
|
//
|
|
// 35 8/29/98 7:23p Aldie
|
|
// Added itemname to get around targetname problem.
|
|
//
|
|
// 34 8/18/98 11:08p Markd
|
|
// Added new Alias System
|
|
//
|
|
// 33 8/15/98 1:48p Aldie
|
|
// Don't turn off glow when objects are picked up
|
|
//
|
|
// 32 8/12/98 4:19p Aldie
|
|
// Fixed icons not showing up correctly
|
|
//
|
|
// 31 7/26/98 1:17a Markd
|
|
// Put in respawn sounds for items
|
|
//
|
|
// 30 7/14/98 6:57p Aldie
|
|
// Made dropped weapons fade out
|
|
//
|
|
// 29 7/14/98 3:53p Markd
|
|
// made pickup sounds work properly
|
|
//
|
|
// 28 7/12/98 4:34p Markd
|
|
// Fixed bounding boxes on items
|
|
//
|
|
// 27 7/11/98 8:19p Jimdose
|
|
// Drop now returns true or false depending upon whether the item can be
|
|
// dropped or not
|
|
//
|
|
// 26 7/10/98 2:48p Aldie
|
|
// Cleared targetname for all items to circumvent an error when removing an
|
|
// actor carrying a weapon of the same targetname as him.
|
|
//
|
|
// 25 6/26/98 11:30a Markd
|
|
// Changed ItemPickup
|
|
//
|
|
// 24 6/25/98 8:47p Markd
|
|
// Added keyed items for Triggers, Rewrote Item class, rewrote every pickup
|
|
// method
|
|
//
|
|
// 23 6/24/98 1:36p Aldie
|
|
// Implementation of inventory system and picking stuff up
|
|
//
|
|
// 22 6/17/98 1:20a Jimdose
|
|
// Moved setOwner to Item.
|
|
// Added EV_Item_Pickup
|
|
//
|
|
// 21 6/16/98 9:37p Markd
|
|
// made items capable of being orientated
|
|
//
|
|
// 20 6/16/98 4:08p Jimdose
|
|
// Gave dropping weapons velocity
|
|
//
|
|
// 19 6/04/98 7:36p Jimdose
|
|
// PlaceItem now posts the EV_Remove instead of processing it
|
|
//
|
|
// 18 6/03/98 4:37p Markd
|
|
// When picking an item up, stop glowing, when dropping it start glowing
|
|
//
|
|
// 17 5/13/98 4:45p Jimdose
|
|
// droptofloor event is now posted with 0 delay
|
|
//
|
|
// 16 5/03/98 4:50p Jimdose
|
|
// Fixed bug where items fell through ground
|
|
//
|
|
// 15 5/03/98 4:44p Jimdose
|
|
// changed Vector class
|
|
//
|
|
// 14 5/02/98 12:39a Jimdose
|
|
// Changed PlaceItem so that items only test if they can drop to the floor. If
|
|
// they can, they're left where they were spawned so that they fall, otherwise
|
|
// they're removed
|
|
//
|
|
// 13 4/05/98 9:41p Markd
|
|
// Put in RF_GLOW
|
|
//
|
|
// 12 4/04/98 6:05p Jimdose
|
|
// ItemPickup no longer activates targets, since the item should alread have
|
|
// triggered its targets when it was touched
|
|
//
|
|
// 11 4/02/98 4:49p Jimdose
|
|
// changed droptofloor
|
|
//
|
|
// 10 3/30/98 2:32p Jimdose
|
|
// now shows model on placement
|
|
//
|
|
// 9 3/28/98 8:57p Jimdose
|
|
// changed bounding box
|
|
//
|
|
// 8 3/23/98 1:31p Jimdose
|
|
// Revamped event and command system
|
|
//
|
|
// 7 3/02/98 8:49p Jimdose
|
|
// Changed the classid parameter of CLASS_DECLARATION to a quoted string so
|
|
// that you could have a NULL classid.
|
|
//
|
|
// 6 2/19/98 2:35p Jimdose
|
|
// Updated to work with Q2 based progs
|
|
//
|
|
// 4 11/07/97 5:59p Markd
|
|
// Removed QUAKE specific sound effects
|
|
//
|
|
// 3 10/27/97 3:29p Jimdose
|
|
// Removed dependency on quakedef.h
|
|
//
|
|
// 2 9/26/97 5:30p Jimdose
|
|
// Added standard Ritual headers
|
|
//
|
|
// DESCRIPTION:
|
|
// Base class for respawnable, carryable objects.
|
|
//
|
|
|
|
#include "g_local.h"
|
|
#include "entity.h"
|
|
#include "trigger.h"
|
|
#include "item.h"
|
|
#include "inventoryitem.h"
|
|
#include "scriptmaster.h"
|
|
#include "health.h"
|
|
|
|
Event EV_Item_Pickup( "item_pickup" );
|
|
Event EV_Item_DropToFloor( "droptofloor" );
|
|
Event EV_Item_Respawn( "respawn" );
|
|
Event EV_Item_SetAmount( "amount" );
|
|
Event EV_Item_SetMaxAmount( "maxamount" );
|
|
Event EV_Item_SetIconName( "iconname" );
|
|
Event EV_Item_SetItemName( "itemname" );
|
|
Event EV_Item_RespawnSound( "respawnsound" );
|
|
Event EV_Item_DialogNeeded( "dialogneeded" );
|
|
|
|
CLASS_DECLARATION( Trigger, Item, NULL );
|
|
|
|
ResponseDef Item::Responses[] =
|
|
{
|
|
{ &EV_Trigger_Effect, ( Response )Item::ItemTouch },
|
|
{ &EV_Item_DropToFloor, ( Response )Item::DropToFloor },
|
|
{ &EV_Item_Respawn, ( Response )Item::Respawn },
|
|
{ &EV_Item_SetAmount, ( Response )Item::SetAmount },
|
|
{ &EV_Item_SetMaxAmount, ( Response )Item::SetMaxAmount },
|
|
{ &EV_Item_SetIconName, ( Response )Item::SetIconName },
|
|
{ &EV_Item_SetItemName, ( Response )Item::SetItemName },
|
|
{ &EV_Item_Pickup, ( Response )Item::Pickup },
|
|
{ &EV_Use, ( Response )Item::TriggerStuff },
|
|
{ &EV_Item_RespawnSound, ( Response )Item::RespawnSound },
|
|
{ &EV_Item_DialogNeeded, ( Response )Item::DialogNeeded },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Item::Item()
|
|
{
|
|
str fullname;
|
|
Vector defangles;
|
|
|
|
setRespawnTime( 20 );
|
|
setRespawn( false );
|
|
|
|
setSolidType( SOLID_NOT );
|
|
|
|
// Set default respawn behavior
|
|
// Derived classes should use setRespawn
|
|
// if they want to override the default behavior
|
|
if ( deathmatch->value )
|
|
{
|
|
setRespawn( true );
|
|
}
|
|
else
|
|
{
|
|
setRespawn( false );
|
|
}
|
|
|
|
edict->s.renderfx |= RF_GLOW;
|
|
|
|
// angles
|
|
defangles = Vector( 0, G_GetFloatArg( "angle", 0 ), 0 );
|
|
if (defangles.y == -1)
|
|
{
|
|
defangles = Vector( -90, 0, 0 );
|
|
}
|
|
else if (defangles.y == -2)
|
|
{
|
|
defangles = Vector( 90, 0, 0 );
|
|
}
|
|
angles = G_GetVectorArg( "angles", defangles );
|
|
setAngles( angles );
|
|
|
|
//
|
|
// we want the bounds of this model auto-rotated
|
|
//
|
|
flags |= FL_ROTATEDBOUNDS;
|
|
|
|
//
|
|
// rotate the mins and maxs for the model
|
|
//
|
|
if ( size.length() < 10 )
|
|
setSize( "-10 -10 0", "10 10 20" );
|
|
|
|
//
|
|
// reset the mins and maxs to pickup the FL_ROTATEDBOUNDS flag
|
|
//
|
|
setSize( mins, maxs );
|
|
|
|
if ( !LoadingSavegame )
|
|
{
|
|
// Items can't be immediately dropped to floor, because they might
|
|
// be on an entity that hasn't spawned yet.
|
|
PostEvent( EV_Item_DropToFloor, 0 );
|
|
}
|
|
|
|
respondto = TRIGGER_PLAYERS;
|
|
|
|
icon_name = str("");
|
|
icon_index = 0;
|
|
item_index = 0;
|
|
maximum_amount = 1;
|
|
playrespawn = false;
|
|
|
|
amount_override = false;
|
|
amount = 1;
|
|
|
|
// FIXME
|
|
// If the targetname is set by the spawn args, then this item
|
|
// will have a targetname. If we try to remove the owner of this
|
|
// item, then we will remove the owner, then try to remove the item
|
|
// which will already have been removed by the previous event.
|
|
// This doesn't allow any items to have a targetname.
|
|
SetTargetName( "" );
|
|
|
|
// Using itemname as a temporary fix to this problem
|
|
itemname = G_GetSpawnArg( "itemname", "");
|
|
|
|
if ( G_GetSpawnArg( "amount" ) )
|
|
{
|
|
amount = G_GetIntArg( "amount" );
|
|
if ( amount >= MaxAmount() )
|
|
{
|
|
SetMax( amount );
|
|
}
|
|
amount_override = true;
|
|
}
|
|
}
|
|
|
|
Item::~Item()
|
|
{
|
|
if ( owner )
|
|
{
|
|
owner->RemoveItem( this );
|
|
owner = NULL;
|
|
}
|
|
}
|
|
|
|
void Item::CreateSpawnArgs
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
G_SetIntArg( "amount", amount );
|
|
G_SetSpawnArg( "model", model.c_str() );
|
|
}
|
|
|
|
/*
|
|
============
|
|
PlaceItem
|
|
|
|
Puts an item back in the world
|
|
============
|
|
*/
|
|
void Item::PlaceItem
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
setSolidType( SOLID_TRIGGER );
|
|
setMoveType( MOVETYPE_TOSS );
|
|
showModel();
|
|
|
|
edict->s.renderfx |= RF_GLOW;
|
|
groundentity = NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
DropToFloor
|
|
|
|
plants the object on the floor
|
|
============
|
|
*/
|
|
void Item::DropToFloor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str fullname;
|
|
Vector save;
|
|
|
|
PlaceItem();
|
|
|
|
setOrigin( origin + "0 0 1" );
|
|
save = origin;
|
|
if ( !droptofloor( 8192 ) )
|
|
{
|
|
gi.dprintf( "%s fell out of level at '%5.1f %5.1f %5.1f'\n",
|
|
getClassID(), origin.x, origin.y, origin.z );
|
|
PostEvent( EV_Remove, 0 );
|
|
return;
|
|
}
|
|
//
|
|
// if the our global variable doesn't exist, lets zero it out
|
|
//
|
|
fullname = str( "playeritem_" ) + getClassname();
|
|
if ( !gameVars.VariableExists( fullname.c_str() ) )
|
|
{
|
|
gameVars.SetVariable( fullname.c_str(), 0 );
|
|
}
|
|
|
|
if ( !levelVars.VariableExists( fullname.c_str() ) )
|
|
{
|
|
levelVars.SetVariable( fullname.c_str(), 0 );
|
|
}
|
|
|
|
setOrigin( save );
|
|
groundentity = NULL;
|
|
}
|
|
|
|
qboolean Item::Drop
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( !owner )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
setOrigin( owner->worldorigin + "0 0 40" );
|
|
|
|
// drop the item
|
|
PlaceItem();
|
|
velocity = owner->velocity * 0.5 + Vector( G_CRandom( 50 ), G_CRandom( 50 ), 100 );
|
|
setAngles( owner->angles );
|
|
avelocity = Vector( 0, G_CRandom( 360 ), 0 );
|
|
|
|
trigger_time = level.time + 1;
|
|
|
|
if ( owner->isClient() )
|
|
{
|
|
spawnflags |= DROPPED_PLAYER_ITEM;
|
|
}
|
|
else
|
|
{
|
|
spawnflags |= DROPPED_ITEM;
|
|
}
|
|
|
|
// Remove this from the owner's item list
|
|
owner->RemoveItem( this );
|
|
owner = NULL;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void Item::ItemTouch
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Entity *other;
|
|
Event *e;
|
|
|
|
if ( owner )
|
|
{
|
|
// Don't respond to trigger events after item is picked up.
|
|
gi.dprintf( "%s with targetname of %s was triggered unexpectedly.\n", getClassID(), TargetName() );
|
|
return;
|
|
}
|
|
|
|
other = ev->GetEntity( 1 );
|
|
|
|
e = new Event( EV_Item_Pickup );
|
|
e->AddEntity( other );
|
|
ProcessEvent( e );
|
|
}
|
|
|
|
void Item::SetOwner
|
|
(
|
|
Sentient *ent
|
|
)
|
|
|
|
{
|
|
assert( ent );
|
|
if ( !ent )
|
|
{
|
|
// return to avoid any buggy behaviour
|
|
return;
|
|
}
|
|
|
|
owner = ent;
|
|
setRespawn( false );
|
|
|
|
edict->s.renderfx &= ~RF_GLOW;
|
|
setSolidType( SOLID_NOT );
|
|
hideModel();
|
|
CancelEventsOfType( EV_Item_DropToFloor );
|
|
CancelEventsOfType( EV_Remove );
|
|
// ItemPickup( ent );
|
|
}
|
|
|
|
Item * Item::ItemPickup
|
|
(
|
|
Entity *other
|
|
)
|
|
|
|
{
|
|
Sentient * sent;
|
|
Item * item;
|
|
str realname;
|
|
|
|
if ( !Pickupable( other ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
sent = ( Sentient * )other;
|
|
|
|
item = sent->giveItem( getClassname(), Amount(), icon_index );
|
|
|
|
if ( !item )
|
|
return NULL;
|
|
|
|
realname = GetRandomAlias( "snd_pickup" );
|
|
if ( realname.length() > 1 )
|
|
sent->sound( realname, 1, CHAN_ITEM, ATTN_NORM );
|
|
|
|
if ( !Removable() )
|
|
{
|
|
// leave the item for others to pickup
|
|
return item;
|
|
}
|
|
|
|
CancelEventsOfType( EV_Item_DropToFloor );
|
|
CancelEventsOfType( EV_Item_Respawn );
|
|
CancelEventsOfType( EV_FadeOut );
|
|
|
|
setSolidType( SOLID_NOT );
|
|
hideModel();
|
|
|
|
|
|
if ( Respawnable() )
|
|
{
|
|
PostEvent( EV_Item_Respawn, RespawnTime() );
|
|
}
|
|
else
|
|
{
|
|
PostEvent( EV_Remove, 0.1 );
|
|
}
|
|
|
|
if ( DM_FLAG( DF_INSTANT_ITEMS ) )
|
|
{
|
|
Event *ev;
|
|
|
|
ev = new Event( EV_InventoryItem_Use );
|
|
ev->AddEntity( other );
|
|
|
|
item->ProcessEvent( ev );
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
void Item::Respawn
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
showModel();
|
|
|
|
// allow it to be touched again
|
|
setSolidType( SOLID_TRIGGER );
|
|
|
|
// play respawn sound
|
|
if ( playrespawn )
|
|
{
|
|
RandomGlobalSound( "snd_itemspawn" );
|
|
}
|
|
|
|
setOrigin( origin );
|
|
};
|
|
|
|
void Item::setRespawn
|
|
(
|
|
qboolean flag
|
|
)
|
|
|
|
{
|
|
respawnable = flag;
|
|
}
|
|
|
|
qboolean Item::Respawnable
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return respawnable;
|
|
}
|
|
|
|
void Item::setRespawnTime
|
|
(
|
|
float time
|
|
)
|
|
|
|
{
|
|
respawntime = time;
|
|
}
|
|
|
|
float Item::RespawnTime
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return respawntime;
|
|
}
|
|
|
|
int Item::Amount
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return amount;
|
|
}
|
|
|
|
int Item::MaxAmount
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return maximum_amount;
|
|
}
|
|
|
|
qboolean Item::Pickupable
|
|
(
|
|
Entity *other
|
|
)
|
|
|
|
{
|
|
if ( !other->isSubclassOf( Sentient ) )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Sentient * sent;
|
|
Item * item;
|
|
|
|
sent = ( Sentient * )other;
|
|
item = sent->FindItem( getClassname() );
|
|
|
|
if ( item && ( item->Amount() >= item->MaxAmount() ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Mutants can't pick up anything but health
|
|
if ( other->flags & (FL_MUTANT|FL_SP_MUTANT) && !( this->isSubclassOf( Health ) ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If deathmatch and already in a powerup, don't pickup anymore when DF_INSTANT_ITEMS is on
|
|
if ( DM_FLAG( DF_INSTANT_ITEMS ) &&
|
|
this->isSubclassOf( InventoryItem ) &&
|
|
sent->PowerupActive()
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Item::Pickup
|
|
(
|
|
Event * ev
|
|
)
|
|
|
|
{
|
|
ItemPickup( ev->GetEntity( 1 ) );
|
|
}
|
|
|
|
void Item::setIcon
|
|
(
|
|
const char *i
|
|
)
|
|
|
|
{
|
|
icon_name = i;
|
|
icon_index = gi.imageindex( i );
|
|
}
|
|
|
|
void Item::setName
|
|
(
|
|
const char *i
|
|
)
|
|
|
|
{
|
|
item_name = i;
|
|
item_index = gi.itemindex( i );
|
|
}
|
|
|
|
int Item::Icon
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( icon_name.length() )
|
|
return icon_index;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
void Item::Set
|
|
(
|
|
int startamount
|
|
)
|
|
|
|
{
|
|
if ( !amount_override )
|
|
{
|
|
amount = startamount;
|
|
if ( amount >= MaxAmount() )
|
|
SetMax( amount );
|
|
}
|
|
}
|
|
|
|
void Item::SetMax
|
|
(
|
|
int maxamount
|
|
)
|
|
|
|
{
|
|
maximum_amount = maxamount;
|
|
}
|
|
|
|
void Item::SetAmount
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Set( ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void Item::SetMaxAmount
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
SetMax( ev->GetInteger( 1 ) );
|
|
}
|
|
|
|
void Item::SetIconName
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
setIcon( ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Item::SetItemName
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
setName( ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Item::Add
|
|
(
|
|
int num
|
|
)
|
|
|
|
{
|
|
amount += num;
|
|
if ( amount >= MaxAmount() )
|
|
amount = MaxAmount();
|
|
}
|
|
|
|
void Item::Remove
|
|
(
|
|
int num
|
|
)
|
|
{
|
|
amount -= num;
|
|
if (amount < 0)
|
|
amount = 0;
|
|
}
|
|
|
|
|
|
qboolean Item::Use
|
|
(
|
|
int num
|
|
)
|
|
|
|
{
|
|
if ( num > amount )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
amount -= num;
|
|
return true;
|
|
}
|
|
|
|
qboolean Item::Removable
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Item::RespawnSound
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
playrespawn = true;
|
|
}
|
|
|
|
void Item::DialogNeeded
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
//
|
|
// if this item is needed for a trigger, play this dialog
|
|
//
|
|
dialog_needed = ev->GetString( 1 );
|
|
}
|
|
|
|
str Item::GetDialogNeeded
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return dialog_needed;
|
|
}
|