1136 lines
22 KiB
C++
1136 lines
22 KiB
C++
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// $Logfile:: /EF2/Code/DLLs/game/item.cpp $
|
||
|
// $Revision:: 56 $
|
||
|
// $Author:: Singlis $
|
||
|
// $Date:: 9/26/03 2:36p $
|
||
|
//
|
||
|
// 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.
|
||
|
//
|
||
|
//
|
||
|
// DESCRIPTION:
|
||
|
// Base class for respawnable, carryable objects.
|
||
|
//
|
||
|
|
||
|
#include "_pch_cpp.h"
|
||
|
#include "entity.h"
|
||
|
#include "trigger.h"
|
||
|
#include "item.h"
|
||
|
#include "inventoryitem.h"
|
||
|
#include "scriptmaster.h"
|
||
|
#include "health.h"
|
||
|
#include "mp_manager.hpp"
|
||
|
#include <qcommon/gameplaymanager.h>
|
||
|
|
||
|
|
||
|
// for bot code
|
||
|
#include "g_local.h"
|
||
|
#include "botlib.h"
|
||
|
#include "be_aas.h"
|
||
|
#include "be_ea.h"
|
||
|
#include "be_ai_char.h"
|
||
|
#include "be_ai_chat.h"
|
||
|
#include "be_ai_gen.h"
|
||
|
#include "be_ai_goal.h"
|
||
|
#include "be_ai_move.h"
|
||
|
#include "be_ai_weap.h"
|
||
|
#include "ai_main.h"
|
||
|
extern bot_state_t *botstates[MAX_CLIENTS];
|
||
|
|
||
|
|
||
|
Event EV_Item_Pickup
|
||
|
(
|
||
|
"item_pickup",
|
||
|
EV_CODEONLY,
|
||
|
"e",
|
||
|
"item",
|
||
|
"Pickup the specified item."
|
||
|
);
|
||
|
Event EV_Item_DropToFloor
|
||
|
(
|
||
|
"item_droptofloor",
|
||
|
EV_CODEONLY,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Drops the item to the ground."
|
||
|
);
|
||
|
Event EV_Item_Respawn
|
||
|
(
|
||
|
"respawn",
|
||
|
EV_CODEONLY,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Respawns the item."
|
||
|
);
|
||
|
Event EV_Item_SetRespawn
|
||
|
(
|
||
|
"set_respawn",
|
||
|
EV_DEFAULT,
|
||
|
"i",
|
||
|
"respawn",
|
||
|
"Turns respawn on or off."
|
||
|
);
|
||
|
Event EV_Item_SetRespawnTime
|
||
|
(
|
||
|
"set_respawn_time",
|
||
|
EV_DEFAULT,
|
||
|
"f",
|
||
|
"respawn_time",
|
||
|
"Sets the respawn time."
|
||
|
);
|
||
|
Event EV_Item_SetAmount
|
||
|
(
|
||
|
"amount",
|
||
|
EV_DEFAULT,
|
||
|
"i",
|
||
|
"amount",
|
||
|
"Sets the amount of the item."
|
||
|
);
|
||
|
Event EV_Item_SetMaxAmount
|
||
|
(
|
||
|
"maxamount",
|
||
|
EV_DEFAULT,
|
||
|
"i",
|
||
|
"max_amount",
|
||
|
"Sets the max amount of the item."
|
||
|
);
|
||
|
Event EV_Item_SetItemName
|
||
|
(
|
||
|
"name",
|
||
|
EV_DEFAULT,
|
||
|
"s",
|
||
|
"item_name",
|
||
|
"Sets the item name."
|
||
|
);
|
||
|
Event EV_Item_RespawnSound
|
||
|
(
|
||
|
"respawnsound",
|
||
|
EV_DEFAULT,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Turns on the respawn sound for this item."
|
||
|
);
|
||
|
Event EV_Item_DialogNeeded
|
||
|
(
|
||
|
"dialogneeded",
|
||
|
EV_DEFAULT,
|
||
|
"s",
|
||
|
"dialog_needed",
|
||
|
"Sets the dialog needed string."
|
||
|
);
|
||
|
Event EV_Item_NoRemove
|
||
|
(
|
||
|
"no_remove",
|
||
|
EV_DEFAULT,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Makes it so the item is not removed from the world when it is picked up."
|
||
|
);
|
||
|
Event EV_Item_RespawnDone
|
||
|
(
|
||
|
"respawn_done",
|
||
|
EV_CODEONLY,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Called when the item respawn is done."
|
||
|
);
|
||
|
Event EV_Item_PickupDone
|
||
|
(
|
||
|
"pickup_done",
|
||
|
EV_CODEONLY,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Called when the item pickup is done."
|
||
|
);
|
||
|
Event EV_Item_SetPickupThread
|
||
|
(
|
||
|
"pickup_thread",
|
||
|
EV_SCRIPTONLY,
|
||
|
"s",
|
||
|
"labelName",
|
||
|
"A thread that is called when an item is picked up."
|
||
|
);
|
||
|
Event EV_Item_SetBotInventoryIndex
|
||
|
(
|
||
|
"bot_inventory",
|
||
|
EV_TIKIONLY,
|
||
|
"i",
|
||
|
"botInventoryIndex",
|
||
|
"sets the index used for bot inventory pickups"
|
||
|
);
|
||
|
Event EV_Item_CoolItem
|
||
|
(
|
||
|
"coolitem",
|
||
|
EV_DEFAULT,
|
||
|
"SS",
|
||
|
"dialog anim_to_play",
|
||
|
"Specify that this is a cool item when we pick it up for the first time.\n"
|
||
|
"If dialog is specified, than the dialog will be played during the pickup.\n"
|
||
|
"If anim_to_play is specified, than the specified anim will be played after\n"
|
||
|
"the initial cinematic."
|
||
|
);
|
||
|
Event EV_Item_ForceCoolItem
|
||
|
(
|
||
|
"forcecoolitem",
|
||
|
EV_DEFAULT,
|
||
|
"SS",
|
||
|
"dialog anim_to_play",
|
||
|
"Specify that this is a cool item when we pick it up regardless of whether or not we have it.\n"
|
||
|
"If dialog is specified, than the dialog will be played during the pickup.\n"
|
||
|
"If anim_to_play is specified, than the specified anim will be played after\n"
|
||
|
"the initial cinematic."
|
||
|
);
|
||
|
Event EV_Item_IconName
|
||
|
(
|
||
|
"iconName",
|
||
|
EV_DEFAULT,
|
||
|
"s",
|
||
|
"iconName",
|
||
|
"Sets the name of the icon to use for this item."
|
||
|
);
|
||
|
Event EV_Item_SetMissingSkin
|
||
|
(
|
||
|
"missingSkin",
|
||
|
EV_TIKIONLY,
|
||
|
"i",
|
||
|
"skinNum",
|
||
|
"Sets the skin number to use when its been picked up and is missing"
|
||
|
);
|
||
|
Event EV_Item_PostSpawn
|
||
|
(
|
||
|
"itemPostSpawn",
|
||
|
EV_CODEONLY,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
"Tells the item that it had spawned successfully"
|
||
|
);
|
||
|
|
||
|
CLASS_DECLARATION( Trigger, Item, NULL )
|
||
|
{
|
||
|
{ &EV_Trigger_Effect, &Item::ItemTouch },
|
||
|
{ &EV_Item_DropToFloor, &Item::DropToFloor },
|
||
|
{ &EV_Item_Respawn, &Item::Respawn },
|
||
|
{ &EV_Item_SetAmount, &Item::SetAmountEvent },
|
||
|
{ &EV_Item_SetMaxAmount, &Item::SetMaxAmount },
|
||
|
{ &EV_Item_SetItemName, &Item::SetItemName },
|
||
|
{ &EV_Item_Pickup, &Item::Pickup },
|
||
|
{ &EV_Use, &Item::TriggerStuff },
|
||
|
{ &EV_Item_RespawnSound, &Item::RespawnSound },
|
||
|
{ &EV_Item_DialogNeeded, &Item::DialogNeeded },
|
||
|
{ &EV_Item_NoRemove, &Item::SetNoRemove },
|
||
|
{ &EV_Item_RespawnDone, &Item::RespawnDone },
|
||
|
{ &EV_Item_PickupDone, &Item::PickupDone },
|
||
|
{ &EV_Item_SetRespawn, &Item::setRespawn },
|
||
|
{ &EV_Item_SetRespawnTime, &Item::setRespawnTime },
|
||
|
{ &EV_Item_SetPickupThread, &Item::SetPickupThread },
|
||
|
{ &EV_Item_CoolItem, &Item::CoolItemEvent },
|
||
|
{ &EV_Item_ForceCoolItem, &Item::ForceCoolItemEvent },
|
||
|
{ &EV_Item_SetBotInventoryIndex, &Item::SetBotInventoryIndex },
|
||
|
{ &EV_Stop, &Item::Landed },
|
||
|
{ &EV_SetAngle, &Item::SetAngleEvent },
|
||
|
{ &EV_Item_IconName, &Item::iconNameEvent },
|
||
|
{ &EV_Item_SetMissingSkin, &Item::setMissingSkin },
|
||
|
{ &EV_Item_PostSpawn, &Item::postSpawn },
|
||
|
{ NULL, NULL }
|
||
|
};
|
||
|
|
||
|
//--------------------------------------------------------------
|
||
|
// Name: Item()
|
||
|
// Class: Item
|
||
|
//
|
||
|
// Description: Constructor
|
||
|
//
|
||
|
// Parameters: None
|
||
|
//
|
||
|
// Returns: None
|
||
|
//--------------------------------------------------------------
|
||
|
Item::Item()
|
||
|
{
|
||
|
str fullname;
|
||
|
animate = new Animate( this );
|
||
|
|
||
|
if ( LoadingSavegame )
|
||
|
return;
|
||
|
|
||
|
setSolidType( SOLID_NOT );
|
||
|
|
||
|
// Set default respawn behavior
|
||
|
// Derived classes should use setRespawn
|
||
|
// if they want to override the default behavior
|
||
|
setRespawn( false );
|
||
|
setRespawnTime( 20 );
|
||
|
|
||
|
if ( multiplayerManager.inMultiplayer() )
|
||
|
{
|
||
|
setRespawn( true );
|
||
|
edict->s.renderfx |= RF_FULLBRIGHT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// we want the bounds of this model auto-rotated
|
||
|
//
|
||
|
flags |= FL_ROTATEDBOUNDS;
|
||
|
|
||
|
//
|
||
|
// set a minimum mins and maxs for the model
|
||
|
//
|
||
|
if ( size.length() < 10.0f )
|
||
|
{
|
||
|
mins = Vector(-10, -10, 0);
|
||
|
maxs = Vector(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, EV_POSTSPAWN );
|
||
|
}
|
||
|
|
||
|
respondto = TRIGGER_PLAYERS;
|
||
|
|
||
|
// items should collide with everything that the player does
|
||
|
edict->clipmask = MASK_PLAYERSOLID;
|
||
|
|
||
|
bot_inventory_index = 0; // INVENTORY_NONE
|
||
|
item_index = 0;
|
||
|
maximum_amount = 1.0f;
|
||
|
playrespawn = false;
|
||
|
|
||
|
// this is an item entity
|
||
|
|
||
|
if ( g_gametype->integer == GT_SINGLE_PLAYER )
|
||
|
edict->s.eType = ET_MODELANIM;
|
||
|
else
|
||
|
edict->s.eType = ET_ITEM;
|
||
|
|
||
|
// Set our default skill level
|
||
|
_skillLevel = 1.0f;
|
||
|
amount = 1.0f;
|
||
|
no_remove = false;
|
||
|
setName( "Unknown Item" );
|
||
|
|
||
|
look_at_me = true;
|
||
|
coolitem = false;
|
||
|
coolitemforced = false;
|
||
|
|
||
|
has_been_looked_at = false;
|
||
|
|
||
|
_nextPickupTime = 0.0f;
|
||
|
|
||
|
_mpItemType = MP_ITEM_TYPE_NORMAL;
|
||
|
|
||
|
_iconIndex = -1;
|
||
|
|
||
|
_missingSkin = 0;
|
||
|
|
||
|
if ( !LoadingSavegame )
|
||
|
{
|
||
|
PostEvent( EV_Item_PostSpawn, EV_POSTSPAWN );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Item::~Item()
|
||
|
{
|
||
|
if ( owner )
|
||
|
{
|
||
|
owner->RemoveItem( this );
|
||
|
owner = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::postSpawn( Event *ev )
|
||
|
{
|
||
|
cacheStrings();
|
||
|
}
|
||
|
|
||
|
void Item::SetNoRemove( Event *ev )
|
||
|
{
|
||
|
no_remove = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
PlaceItem
|
||
|
|
||
|
Puts an item back in the world
|
||
|
============
|
||
|
*/
|
||
|
void Item::PlaceItem( void )
|
||
|
{
|
||
|
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
|
||
|
if ( gpm->hasProperty(getArchetype(), "noautopickup") )
|
||
|
{
|
||
|
setContents( CONTENTS_USABLE );
|
||
|
setSolidType( SOLID_BBOX );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
setSolidType( SOLID_TRIGGER );
|
||
|
}
|
||
|
|
||
|
setMoveType( MOVETYPE_TOSS );
|
||
|
showModel();
|
||
|
|
||
|
groundentity = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
DropToFloor
|
||
|
|
||
|
plants the object on the floor
|
||
|
============
|
||
|
*/
|
||
|
void Item::DropToFloor( Event *ev )
|
||
|
{
|
||
|
str fullname;
|
||
|
Vector save;
|
||
|
|
||
|
PlaceItem();
|
||
|
|
||
|
addOrigin( Vector(0, 0, 1) );
|
||
|
|
||
|
save = origin;
|
||
|
|
||
|
if ( gravity > 0.0f )
|
||
|
{
|
||
|
if ( !droptofloor( 8192.0f ) )
|
||
|
{
|
||
|
gi.WDPrintf( "%s (%d) stuck in world at '%5.1f %5.1f %5.1f'\n",
|
||
|
getClassID(), entnum, origin.x, origin.y, origin.z );
|
||
|
setOrigin( save );
|
||
|
setMoveType( MOVETYPE_NONE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
setMoveType( MOVETYPE_NONE );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// if the our global variable doesn't exist, lets zero it out
|
||
|
//
|
||
|
fullname = str( "playeritem_" ) + getName();
|
||
|
if ( !gameVars.VariableExists( fullname.c_str() ) )
|
||
|
{
|
||
|
gameVars.SetVariable( fullname.c_str(), 0 );
|
||
|
}
|
||
|
|
||
|
if ( !levelVars.VariableExists( fullname.c_str() ) )
|
||
|
{
|
||
|
levelVars.SetVariable( fullname.c_str(), 0 );
|
||
|
}
|
||
|
|
||
|
if ( multiplayerManager.inMultiplayer() && gi.Anim_NumForName( edict->s.modelindex, "idle_onground" ) >= 0 )
|
||
|
{
|
||
|
animate->RandomAnimate( "idle_onground" );
|
||
|
edict->s.eType = ET_MODELANIM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qboolean Item::Drop( void )
|
||
|
{
|
||
|
if ( !owner )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
setOrigin( owner->origin + Vector( "0 0 40" ) );
|
||
|
|
||
|
// drop the item
|
||
|
PlaceItem();
|
||
|
velocity = owner->velocity * 0.5f + Vector( G_CRandom( 50.0f ), G_CRandom( 50.0f ), 100.0f );
|
||
|
setAngles( owner->angles );
|
||
|
avelocity = Vector( 0.0f, G_CRandom( 360.0f ), 0.0f );
|
||
|
|
||
|
trigger_time = level.time + 1.0f;
|
||
|
|
||
|
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.
|
||
|
// we really don't need to see this.
|
||
|
//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;
|
||
|
edict->s.parent = ent->entnum;
|
||
|
setRespawn( false );
|
||
|
|
||
|
setSolidType( SOLID_NOT );
|
||
|
hideModel();
|
||
|
CancelEventsOfType( EV_Touch );
|
||
|
CancelEventsOfType( EV_Item_DropToFloor );
|
||
|
CancelEventsOfType( EV_Remove );
|
||
|
// ItemPickup( ent );
|
||
|
}
|
||
|
|
||
|
void Item::SetBotInventoryIndex( Event *ev )
|
||
|
{
|
||
|
bot_inventory_index = ev->GetInteger(1);
|
||
|
}
|
||
|
|
||
|
int Item::GetBotInventoryIndex( void )
|
||
|
{
|
||
|
return bot_inventory_index;
|
||
|
}
|
||
|
|
||
|
Sentient *Item::GetOwner( void ) const
|
||
|
{
|
||
|
return owner;
|
||
|
}
|
||
|
|
||
|
Item * Item::ItemPickup( Entity *other, qboolean add_to_inventory, qboolean checkautopickup )
|
||
|
{
|
||
|
Sentient * sent;
|
||
|
Item * item = NULL;
|
||
|
str realname;
|
||
|
|
||
|
// Query the gameplay manager and see if we should not auto-pickup this item
|
||
|
if ( checkautopickup )
|
||
|
{
|
||
|
GameplayManager *gpm = GameplayManager::getTheGameplayManager();
|
||
|
if ( gpm->hasProperty(getArchetype(), "noautopickup") )
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( !Pickupable( other ) )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
sent = ( Sentient * )other;
|
||
|
|
||
|
if ( add_to_inventory )
|
||
|
{
|
||
|
item = sent->giveItem( model, getAmount(), true );
|
||
|
|
||
|
|
||
|
if ( !item )
|
||
|
return NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
item = this;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// make sure to copy over the coolness factor :)
|
||
|
//
|
||
|
item->coolitem = coolitem;
|
||
|
item->cool_dialog = cool_dialog;
|
||
|
item->cool_anim = cool_anim;
|
||
|
item->coolitemforced = coolitemforced;
|
||
|
|
||
|
//
|
||
|
// let our sent know they received it
|
||
|
// we put this here so we can transfer information from the original item we picked up
|
||
|
//
|
||
|
|
||
|
if ( !isSubclassOf( Weapon ) || add_to_inventory )
|
||
|
sent->ReceivedItem( item );
|
||
|
|
||
|
realname = GetRandomAlias( "snd_pickup" );
|
||
|
if ( realname.length() > 1 )
|
||
|
sent->Sound( realname, CHAN_ITEM );
|
||
|
|
||
|
if ( !Removable() )
|
||
|
{
|
||
|
// leave the item for others to pickup
|
||
|
return item;
|
||
|
}
|
||
|
|
||
|
look_at_me = false;
|
||
|
|
||
|
CancelEventsOfType( EV_Item_DropToFloor );
|
||
|
CancelEventsOfType( EV_Item_Respawn );
|
||
|
CancelEventsOfType( EV_FadeOut );
|
||
|
|
||
|
setSolidType( SOLID_NOT );
|
||
|
|
||
|
if ( animate && animate->HasAnim( "pickup" ) )
|
||
|
animate->RandomAnimate( "pickup", EV_Item_PickupDone );
|
||
|
else
|
||
|
{
|
||
|
if ( !no_remove )
|
||
|
{
|
||
|
if ( _missingSkin )
|
||
|
{
|
||
|
ChangeSkin( _missingSkin, true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hideModel();
|
||
|
}
|
||
|
|
||
|
if ( !Respawnable() )
|
||
|
PostEvent( EV_Remove, FRAMETIME );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( Respawnable() )
|
||
|
PostEvent( EV_Item_Respawn, RespawnTime() );
|
||
|
|
||
|
// fire off any pickup_thread's
|
||
|
if ( pickup_thread.length() )
|
||
|
{
|
||
|
ExecuteThread( pickup_thread );
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( item && multiplayerManager.checkFlag( MP_FLAG_INSTANT_ITEMS ) )
|
||
|
{
|
||
|
Event *ev;
|
||
|
|
||
|
ev = new Event( EV_InventoryItem_Use );
|
||
|
ev->AddEntity( other );
|
||
|
|
||
|
item->ProcessEvent( ev );
|
||
|
}
|
||
|
|
||
|
return item;
|
||
|
}
|
||
|
|
||
|
void Item::Respawn( Event *ev )
|
||
|
{
|
||
|
if ( _missingSkin )
|
||
|
{
|
||
|
ChangeSkin( _missingSkin, false );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
showModel();
|
||
|
}
|
||
|
|
||
|
// allow it to be touched again
|
||
|
setSolidType( SOLID_TRIGGER );
|
||
|
|
||
|
// play respawn sound
|
||
|
if ( playrespawn )
|
||
|
{
|
||
|
Sound( "snd_itemspawn" );
|
||
|
}
|
||
|
|
||
|
setOrigin();
|
||
|
|
||
|
if ( animate->HasAnim( "respawn" ) )
|
||
|
animate->RandomAnimate( "respawn", EV_Item_RespawnDone );
|
||
|
|
||
|
look_at_me = true;
|
||
|
has_been_looked_at = false;
|
||
|
}
|
||
|
|
||
|
void Item::setRespawn( Event *ev )
|
||
|
{
|
||
|
if ( ev->NumArgs() < 1 )
|
||
|
return;
|
||
|
|
||
|
setRespawn( ev->GetInteger( 1 ) );
|
||
|
}
|
||
|
|
||
|
void Item::setRespawnTime( Event *ev )
|
||
|
{
|
||
|
if ( ev->NumArgs() < 1 )
|
||
|
return;
|
||
|
|
||
|
setRespawnTime( ev->GetFloat( 1 ) );
|
||
|
}
|
||
|
|
||
|
void Item::RespawnDone( Event *ev )
|
||
|
{
|
||
|
animate->RandomAnimate( "idle" );
|
||
|
}
|
||
|
|
||
|
void Item::PickupDone( Event *ev )
|
||
|
{
|
||
|
if ( !no_remove )
|
||
|
{
|
||
|
hideModel();
|
||
|
|
||
|
if ( !Respawnable() )
|
||
|
PostEvent( EV_Remove, FRAMETIME );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( animate->HasAnim( "pickup_idle" ) )
|
||
|
animate->RandomAnimate( "pickup_idle" );
|
||
|
else
|
||
|
animate->RandomAnimate( "pickup" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::setRespawn( qboolean flag )
|
||
|
{
|
||
|
respawnable = flag;
|
||
|
}
|
||
|
|
||
|
qboolean Item::Respawnable( void )
|
||
|
{
|
||
|
return respawnable;
|
||
|
}
|
||
|
|
||
|
void Item::setRespawnTime( float time )
|
||
|
{
|
||
|
respawntime = time;
|
||
|
}
|
||
|
|
||
|
float Item::RespawnTime( void )
|
||
|
{
|
||
|
if ( multiplayerManager.inMultiplayer() )
|
||
|
return respawntime * multiplayerManager.getItemRespawnMultiplayer();
|
||
|
else
|
||
|
return respawntime;
|
||
|
}
|
||
|
|
||
|
float Item::getAmount( void )
|
||
|
{
|
||
|
return amount;
|
||
|
}
|
||
|
|
||
|
float Item::MaxAmount( void )
|
||
|
{
|
||
|
return maximum_amount;
|
||
|
}
|
||
|
|
||
|
qboolean Item::Pickupable( Entity *other )
|
||
|
{
|
||
|
if ( level.time < _nextPickupTime )
|
||
|
return false;
|
||
|
|
||
|
if ( multiplayerManager.inMultiplayer() && other->isSubclassOf( Player ) )
|
||
|
{
|
||
|
if ( !multiplayerManager.canPickup( (Player *)other, getMultiplayerItemType(), getName().c_str() ) )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( getSolidType() == SOLID_NOT )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( !other->isSubclassOf( Sentient ) )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Sentient * sent;
|
||
|
Item * item;
|
||
|
|
||
|
sent = ( Sentient * )other;
|
||
|
|
||
|
if ( sent->deadflag || sent->health <= 0.0f )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
item = sent->FindItem( getName() );
|
||
|
|
||
|
if ( item && ( item->getAmount() >= item->MaxAmount() ) )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// TODO : fixme
|
||
|
// If deathmatch and already in a powerup, don't pickup anymore when DF_INSTANT_ITEMS is on
|
||
|
/* if ( multiplayerManager.checkFlag( MP_FLAG_INSTANT_ITEMS ) &&
|
||
|
this->isSubclassOf( InventoryItem ) &&
|
||
|
sent->PowerupActive()
|
||
|
)
|
||
|
{
|
||
|
return false;
|
||
|
} */
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Item::Pickup( Event * ev )
|
||
|
{
|
||
|
ItemPickup( ev->GetEntity( 1 ) );
|
||
|
}
|
||
|
|
||
|
void Item::setName( const char *i )
|
||
|
{
|
||
|
item_name = i;
|
||
|
item_index = gi.itemindex( i );
|
||
|
strcpy( edict->entname, i );
|
||
|
}
|
||
|
|
||
|
const str Item::getName( void ) const
|
||
|
{
|
||
|
return( item_name );
|
||
|
}
|
||
|
|
||
|
int Item::getIndex( void )
|
||
|
{
|
||
|
return item_index;
|
||
|
}
|
||
|
|
||
|
void Item::setAmount( float startamount )
|
||
|
{
|
||
|
amount = startamount;
|
||
|
if ( amount >= MaxAmount() )
|
||
|
{
|
||
|
SetMax( (int) amount );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Item::SetMax( int maxamount )
|
||
|
{
|
||
|
maximum_amount = maxamount;
|
||
|
}
|
||
|
|
||
|
void Item::SetAmountEvent( Event *ev )
|
||
|
{
|
||
|
setAmount( ev->GetInteger( 1 ) );
|
||
|
}
|
||
|
|
||
|
void Item::SetMaxAmount( Event *ev )
|
||
|
{
|
||
|
SetMax( ev->GetInteger( 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;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// once item has landed on the floor, go to movetype none
|
||
|
//
|
||
|
void Item::Landed( Event *ev )
|
||
|
{
|
||
|
if ( groundentity && ( groundentity->entity != world ) )
|
||
|
{
|
||
|
warning( "Item::Landed", "Item %d has landed on an entity that might move\n", entnum );
|
||
|
}
|
||
|
setMoveType( MOVETYPE_NONE );
|
||
|
}
|
||
|
|
||
|
void Item::SetPickupThread( Event *ev )
|
||
|
{
|
||
|
pickup_thread = ev->GetString( 1 );
|
||
|
}
|
||
|
|
||
|
void Item::SetCoolItem( qboolean cool, const str &dialog, const str &anim )
|
||
|
{
|
||
|
coolitem = cool;
|
||
|
cool_dialog = dialog;
|
||
|
if ( cool_dialog.length() )
|
||
|
{
|
||
|
CacheResource( cool_dialog, this );
|
||
|
}
|
||
|
|
||
|
CacheResource( "models/fx_coolitem.tik", this );
|
||
|
CacheResource( "models/fx_coolitem_reverse.tik", this );
|
||
|
|
||
|
cool_anim = anim;
|
||
|
}
|
||
|
|
||
|
void Item::CoolItemEvent( Event *ev )
|
||
|
{
|
||
|
qboolean cool;
|
||
|
str dialog, anim;
|
||
|
|
||
|
cool = true;
|
||
|
if ( ev->NumArgs() > 0 )
|
||
|
{
|
||
|
dialog = ev->GetString( 1 );
|
||
|
}
|
||
|
if ( ev->NumArgs() > 1 )
|
||
|
{
|
||
|
anim = ev->GetString( 2 );
|
||
|
}
|
||
|
SetCoolItem( cool, dialog, anim );
|
||
|
}
|
||
|
|
||
|
void Item::ForceCoolItemEvent( Event *ev )
|
||
|
{
|
||
|
coolitemforced = true;
|
||
|
CoolItemEvent( ev );
|
||
|
}
|
||
|
|
||
|
qboolean Item::IsItemCool( str * dialog, str * anim, qboolean * forced )
|
||
|
{
|
||
|
*dialog = cool_dialog;
|
||
|
*anim = cool_anim;
|
||
|
*forced = coolitemforced;
|
||
|
return coolitem;
|
||
|
}
|
||
|
|
||
|
MultiplayerItemType Item::getMultiplayerItemType( void )
|
||
|
{
|
||
|
return _mpItemType;
|
||
|
}
|
||
|
|
||
|
void Item::iconNameEvent( Event *ev )
|
||
|
{
|
||
|
str iconName;
|
||
|
|
||
|
iconName = ev->GetString( 1 );
|
||
|
|
||
|
_iconIndex = gi.imageindex( iconName.c_str() );
|
||
|
}
|
||
|
|
||
|
void Item::setMissingSkin( Event *ev )
|
||
|
{
|
||
|
_missingSkin = ev->GetInteger( 1 );
|
||
|
}
|
||
|
|
||
|
void Item::ChangeSkin( int skinNum, qboolean state )
|
||
|
{
|
||
|
str command;
|
||
|
|
||
|
// Build the command
|
||
|
|
||
|
if ( state )
|
||
|
command = "+";
|
||
|
else
|
||
|
command = "-";
|
||
|
|
||
|
command += "skin";
|
||
|
|
||
|
command += skinNum;
|
||
|
|
||
|
// Change the skin
|
||
|
|
||
|
SurfaceCommand( "all", command.c_str() );
|
||
|
}
|
||
|
|
||
|
void Item::cacheStrings( void )
|
||
|
{
|
||
|
G_FindConfigstringIndex( va( "$$PickedUp$$ %d $$Item-%s$$\n", (int)getAmount(), getName().c_str() ), CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true );
|
||
|
G_FindConfigstringIndex( va( "$$PickedUp$$ %d $$Item-%s$$s\n", (int)getAmount(), getName().c_str() ), CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true );
|
||
|
G_FindConfigstringIndex( va( "$$PickedUpThe$$ $$Item-%s$$\n", getName().c_str() ), CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true );
|
||
|
}
|
||
|
|
||
|
CLASS_DECLARATION( Item, MultiplayerItem, NULL )
|
||
|
{
|
||
|
{ &EV_Trigger_Effect, &MultiplayerItem::notifyMultiplayerItemTriggered },
|
||
|
{ &EV_Killed, &MultiplayerItem::notifyMultiplayerItemDestroyed },
|
||
|
{ &EV_Use, &MultiplayerItem::notifyMultiplayerItemUsed },
|
||
|
{ &EV_Damage, &MultiplayerItem::damageEvent },
|
||
|
|
||
|
{ NULL, NULL }
|
||
|
};
|
||
|
|
||
|
MultiplayerItem::MultiplayerItem()
|
||
|
{
|
||
|
edict->s.eType = ET_MODELANIM;
|
||
|
}
|
||
|
|
||
|
void MultiplayerItem::notifyMultiplayerItemTriggered( Event *ev )
|
||
|
{
|
||
|
Entity *entity;
|
||
|
|
||
|
entity = ev->GetEntity( 1 );
|
||
|
|
||
|
if ( entity->isSubclassOf( Player ) )
|
||
|
multiplayerManager.itemTouched( (Player *)entity, this );
|
||
|
}
|
||
|
|
||
|
void MultiplayerItem::notifyMultiplayerItemDestroyed( Event *ev )
|
||
|
{
|
||
|
Entity *entity;
|
||
|
|
||
|
entity = ev->GetEntity( 1 );
|
||
|
|
||
|
if ( entity->isSubclassOf( Player ) )
|
||
|
multiplayerManager.itemDestroyed( (Player *)entity, this );
|
||
|
}
|
||
|
|
||
|
void MultiplayerItem::notifyMultiplayerItemUsed( Event *ev )
|
||
|
{
|
||
|
Entity *entity;
|
||
|
|
||
|
entity = ev->GetEntity( 1 );
|
||
|
|
||
|
multiplayerManager.itemUsed( entity, this );
|
||
|
}
|
||
|
|
||
|
void MultiplayerItem::damageEvent( Event *ev )
|
||
|
{
|
||
|
Entity *attacker;
|
||
|
int meansOfDeath;
|
||
|
float originalDamage;
|
||
|
float realDamage;
|
||
|
|
||
|
// Get the parms
|
||
|
|
||
|
originalDamage = ev->GetFloat( 1 );
|
||
|
attacker = ev->GetEntity( 3 );
|
||
|
meansOfDeath = ev->GetInteger( 9 );
|
||
|
|
||
|
// See what the real damage done is
|
||
|
|
||
|
realDamage = originalDamage;
|
||
|
|
||
|
if ( attacker->isSubclassOf( Player ) )
|
||
|
{
|
||
|
realDamage = multiplayerManager.itemDamaged( this, (Player *)attacker, originalDamage, meansOfDeath );
|
||
|
|
||
|
// Change the event if the damage has changed
|
||
|
|
||
|
if ( realDamage != originalDamage )
|
||
|
{
|
||
|
ev->SetFloat( 1, realDamage );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( realDamage == 0.0f )
|
||
|
return;
|
||
|
|
||
|
// Do the real damage here
|
||
|
|
||
|
Entity::DamageEvent( ev );
|
||
|
}
|
||
|
|
||
|
void MultiplayerItem::cacheStrings( void )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CLASS_DECLARATION( Item, SecretItem, NULL )
|
||
|
{
|
||
|
{ NULL, NULL }
|
||
|
};
|
||
|
|
||
|
SecretItem::SecretItem()
|
||
|
{
|
||
|
if ( LoadingSavegame )
|
||
|
return;
|
||
|
|
||
|
level.total_specialItems++;
|
||
|
levelVars.SetVariable( "total_specialItems" , level.total_specialItems );
|
||
|
|
||
|
edict->s.eType = ET_ITEM;
|
||
|
}
|
||
|
|
||
|
Item* SecretItem::ItemPickup( Entity *other, qboolean add_to_inventory = true, qboolean checkautopickup = true )
|
||
|
{
|
||
|
str pickupSoundName;
|
||
|
|
||
|
PostEvent( EV_Remove, 0.0f );
|
||
|
|
||
|
level.found_specialItems++;
|
||
|
levelVars.SetVariable( "found_specialItems" , level.found_specialItems );
|
||
|
|
||
|
gi.centerprintf ( other->edict, CENTERPRINT_IMPORTANCE_NORMAL, "$$FoundSecretItem$$" );
|
||
|
|
||
|
pickupSoundName = GetRandomAlias( "snd_pickup" );
|
||
|
|
||
|
if ( pickupSoundName.length() > 1 )
|
||
|
{
|
||
|
other->Sound( pickupSoundName, CHAN_ITEM );
|
||
|
}
|
||
|
|
||
|
if ( other && other->isSubclassOf( Player ) )
|
||
|
{
|
||
|
Player *player = (Player *)other;
|
||
|
|
||
|
player->incrementSecretsFound();
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void SecretItem::cacheStrings( void )
|
||
|
{
|
||
|
}
|