polserver/pol-core/pol/item.cpp
muaddib_pol d5e678bdd6 + Organization updates.
* Account and Multi related pol-2008.vcproj organized.
2009-09-04 06:37:13 +00:00

1162 lines
31 KiB
C++

/*
History
=======
2006/03/01 MuadDib: Altered the decayat_gameclock_ check (to check for != 0).
2008/12/17 MuadDub: Added item.tile_layer - returns layer entry from tiledata/tiles.cfg
2009/08/06 MuadDib: Added gotten_by code for items.
2009/08/25 Shinigami: STLport-5.2.1 fix: new_ar not used
2009/09/03 MuadDib: Changes for account related source file relocation
Changes for multi related source file relocation
Notes
=======
*/
#include "clib/stl_inc.h"
#ifdef _MSC_VER
#pragma warning( disable: 4786 )
#endif
#include "bscript/berror.h"
#include "clib/cfgelem.h"
#include "clib/endian.h"
#include "clib/strutil.h"
#include "plib/mapcell.h"
#include "item.h"
#include "armor.h"
#include "ustruct.h"
#include "uofile.h"
#include "containr.h"
#include "ufunc.h"
#include "client.h"
#include "charactr.h"
#include "itemdesc.h"
#include "objtype.h"
#include "plib/pkg.h"
#include "polcfg.h"
#include "resource.h"
#include "scrsched.h"
#include "scrstore.h"
#include "stackcfg.h" //dave 1/26/3
#include "tooltips.h"
#include "uoscrobj.h"
#include "ssopt.h"
/*
#include "clib/endian.h"
#include "armor.h"
#include "multi/boat.h"
#include "door.h"
#include "dye.h"
#include "objtype.h"
#include "spelbook.h"
#include "weapon.h"
*/
/* This is a pretty good clone. Somewhat inefficient, but does
work for derived classes that do not have data.
(since it calls Item::create, virtual tables are copied)
*/
Item* Item::clone() const
{
Item* item = Item::create( objtype_ );
item->color = color;
item->color_ext = color_ext;
item->graphic = graphic;
item->graphic_ext = graphic_ext;
item->x = x;
item->y = y;
item->z = z;
item->realm = realm;
item->facing = facing;
item->setamount( amount_ );
item->layer = layer;
item->tile_layer = tile_layer;
item->container = NULL; // was container
item->sellprice_ = sellprice_;
item->buyprice_ = buyprice_;
item->newbie_ = newbie_;
item->invisible_ = invisible_; //dave 12-20
item->movable_ = movable_; //dave 12-20
item->on_use_script_ = on_use_script_; //dave 12-20
item->equip_script_ = equip_script_; //dave 12-20
item->unequip_script_ = unequip_script_; //dave 12-20
item->name_ = name_;
item->increv();
item->copyprops( *this );
item->saveonexit( saveonexit() );
// Let's build the resistances defaults.
item->element_resist.cold = element_resist.cold;
item->element_resist.energy = element_resist.energy;
item->element_resist.fire = element_resist.fire;
item->element_resist.poison = element_resist.poison;
item->element_resist.physical = element_resist.physical;
item->element_resist_mod.cold = element_resist_mod.cold;
item->element_resist_mod.energy = element_resist_mod.energy;
item->element_resist_mod.fire = element_resist_mod.fire;
item->element_resist_mod.poison = element_resist_mod.poison;
item->element_resist_mod.physical = element_resist_mod.physical;
// Let's build the resistances defaults.
item->element_damage.cold = element_damage.cold;
item->element_damage.energy = element_damage.energy;
item->element_damage.fire = element_damage.fire;
item->element_damage.poison = element_damage.poison;
item->element_damage.physical = element_damage.physical;
item->element_damage_mod.cold = element_damage_mod.cold;
item->element_damage_mod.energy = element_damage_mod.energy;
item->element_damage_mod.fire = element_damage_mod.fire;
item->element_damage_mod.poison = element_damage_mod.poison;
item->element_damage_mod.physical = element_damage_mod.physical;
return item;
}
string Item::name() const
{
if (name_ != "")
{
return name_;
}
else
{
return tile_desc( graphic );
}
}
const ItemDesc& Item::itemdesc() const
{
return find_itemdesc( objtype_ );
}
/* there are four forms of 'name' in objinfo:
name (normal)
name%s (percent followed by plural-part, then null-term)
name%s% (percent followed by plural-part, then percent, then more)
wheat shea%ves/f% ( '%', plural part, '/', single part, '%', rest )
Some examples:
pil%es/e% of hides
banana%s%
feather%s
Known bugs:
1 gold coin displays as "gold coin". There must be a bit somewhere
that I just don't understand yet.
*/
string Item::description() const
{
if (specific_name())
{
return ::format_description( 0, name(), amount_ ); //dave monkeyed with this 2/4/3
}
else
{
const ItemDesc& id = find_itemdesc( objtype_ );
if (id.desc.empty())
{
return ::format_description( tile_flags( graphic ), tile_desc( graphic ), amount_ );
}
else
{
return ::format_description( tile_flags( graphic ), id.desc, amount_ );
}
}
}
string Item::get_use_script_name() const
{
return on_use_script_;
}
string Item::merchant_description() const
{
if (specific_name())
{
return name();
}
else
{
const ItemDesc& id = find_itemdesc( objtype_ );
if (id.desc.empty())
{
return ::format_description( 0, tile_desc( graphic ), 1 );
}
else
{
return ::format_description( 0, id.desc, 1 );
}
}
}
unsigned long Item::sellprice() const
{
if (sellprice_ < ULONG_MAX) //dave changed 1/15/3 so 0 means 0, not default to itemdesc value
{
return sellprice_;
}
else
{
return itemdesc().vendor_sells_for;
}
}
//Dave add buyprice() 11/28. Dont know wtf getbuyprice() is trying to do.
// Dave, getbuyprice() was trying to return false if the vendor wasn't interested in buying.
// it would return true if the vendor was interested in buying.
// -Eric
unsigned long Item::buyprice() const
{
if (buyprice_ < ULONG_MAX) //dave changed 1/15/3 so 0 means 0, not default to itemdesc value
{
return buyprice_;
}
else
{
return itemdesc().vendor_buys_for;
}
}
bool Item::getbuyprice( unsigned long& bp ) const
{
bp = buyprice();
if (bp > 0)
return true;
else
return false;
//dave ripped out the below, was wrong with the above buyprice() changes.
/*
if (buyprice_ > 0)
{
buyprice = static_cast<unsigned long>(buyprice_);
return true;
}
else
{
buyprice = 0;
return false;
}
}
else
{
buyprice = itemdesc().vendor_buys_for;
return (buyprice > 0);
}
*/
}
u8 Item::los_height() const
{
return height / 2;
/*
if (height)
return height-1;
else
return 0;
*/
}
UObject* Item::owner()
{
if (container != NULL)
return container->self_as_owner();
else
return NULL;
}
const UObject* Item::owner() const
{
if (container != NULL)
return container->self_as_owner();
else
return NULL;
}
UObject* Item::toplevel_owner()
{
Item* item = this;
while (item->container != NULL)
item = item->container;
return item;
}
const UObject* Item::toplevel_owner() const
{
const Item* item = this;
while (item->container != NULL)
item = item->container;
return item;
}
const char* Item::classname() const
{
return "Item";
}
bool Item::default_movable() const
{
const ItemDesc& id = find_itemdesc( objtype_ );
if (id.movable == ItemDesc::DEFAULT)
return ((tile_flags( graphic ) & FLAG::MOVABLE) != 0);
else
return id.movable ? true : false;
}
bool Item::default_invisible() const
{
return itemdesc().invisible;
}
bool Item::default_newbie() const
{
return itemdesc().newbie;
}
void Item::printProperties( ostream& os ) const
{
base::printProperties(os);
if (amount_ != 1)
os << "\tAmount\t" << amount_ << pf_endl;
if (layer != 0)
os << "\tLayer\t" << (int) layer << pf_endl;
if (movable_ != default_movable())
os << "\tMovable\t" << movable_ << pf_endl;
if (invisible_ != default_invisible())
os << "\tInvisible\t" << invisible_ << pf_endl;
if (element_resist_mod.fire != 0)
os << "\tFireResistMod\t" << static_cast<int>(element_resist_mod.fire) << pf_endl;
if (element_resist_mod.cold != 0)
os << "\tColdResistMod\t" << static_cast<int>(element_resist_mod.cold) << pf_endl;
if (element_resist_mod.energy != 0)
os << "\tEnergyResistMod\t" << static_cast<int>(element_resist_mod.energy) << pf_endl;
if (element_resist_mod.poison != 0)
os << "\tPoisonResistMod\t" << static_cast<int>(element_resist_mod.poison) << pf_endl;
if (element_resist_mod.physical != 0)
os << "\tPhysicalResistMod\t" << static_cast<int>(element_resist_mod.physical) << pf_endl;
if (element_damage_mod.fire != 0)
os << "\tFireDamageMod\t" << static_cast<int>(element_damage_mod.fire) << pf_endl;
if (element_damage_mod.cold != 0)
os << "\tColdDamageMod\t" << static_cast<int>(element_damage_mod.cold) << pf_endl;
if (element_damage_mod.energy != 0)
os << "\tEnergyDamageMod\t" << static_cast<int>(element_damage_mod.energy) << pf_endl;
if (element_damage_mod.poison != 0)
os << "\tPoisonDamageMod\t" << static_cast<int>(element_damage_mod.poison) << pf_endl;
if (element_damage_mod.physical != 0)
os << "\tPhysicalDamageMod\t" << static_cast<int>(element_damage_mod.physical) << pf_endl;
if (container != NULL)
os << "\tContainer\t0x" << hex << container->serial << dec << pf_endl;
if (!on_use_script_.empty())
os << "\tOnUseScript\t" << on_use_script_ << pf_endl;
if (equip_script_ != itemdesc().equip_script)
os << "\tEquipScript\t" << equip_script_ << pf_endl;
if (unequip_script_ != itemdesc().unequip_script)
os << "\tUnequipScript\t" << unequip_script_ << pf_endl;
if (decayat_gameclock_ != 0)
os << "\tDecayAt\t" << decayat_gameclock_ << pf_endl;
if (sellprice_ != ULONG_MAX) // recall that ULONG_MAX means use default
os << "\tSellPrice\t" << sellprice_ << pf_endl;
if (buyprice_ != ULONG_MAX) // recall that ULONG_MAX means use default
os << "\tBuyPrice\t" << buyprice_ << pf_endl;
if (newbie_ != default_newbie())
os << "\tNewbie\t" << newbie_ << pf_endl;
}
void Item::printDebugProperties( ostream& os ) const
{
base::printDebugProperties( os );
}
void Item::readProperties( ConfigElem& elem )
{
base::readProperties( elem );
// Changed from Valid Color Mask to cfg mask in ssopt.
color &= ssopt.item_color_mask;
color_ext = ctBEu16( color );
amount_ = elem.remove_ushort( "AMOUNT", 1 );
layer = static_cast<unsigned char>(elem.remove_ushort( "LAYER", 0 ));
movable_ = elem.remove_bool( "MOVABLE", default_movable() );
invisible_ = elem.remove_bool( "INVISIBLE", default_invisible() );
// NOTE, container is handled specially - it is extracted by the creator.
on_use_script_ = elem.remove_string( "ONUSESCRIPT", "" );
equip_script_ = elem.remove_string( "EQUIPSCRIPT", equip_script_.c_str() );
unequip_script_ = elem.remove_string( "UNEQUIPSCRIPT", unequip_script_.c_str() );
decayat_gameclock_ = elem.remove_ulong( "DECAYAT", 0 );
sellprice_ = elem.remove_ulong( "SELLPRICE", ULONG_MAX );
buyprice_ = elem.remove_ulong( "BUYPRICE", ULONG_MAX );
// buyprice used to be read in with remove_int (which was wrong).
// the ULONG_MAX values used to be written out (which was wrong).
// when ULONG_MAX is read in by atoi, it returned 2147483647 (0x7FFFFFFF)
// correct for this.
if (buyprice_ == 2147483647)
buyprice_ = ULONG_MAX;
newbie_ = elem.remove_bool( "NEWBIE", default_newbie() );
element_resist_mod.fire = static_cast<s16>(elem.remove_int( "FIRERESISTMOD", 0 ));
element_resist_mod.cold = static_cast<s16>(elem.remove_int( "COLDRESISTMOD", 0 ));
element_resist_mod.energy = static_cast<s16>(elem.remove_int( "ENERGYRESISTMOD", 0 ));
element_resist_mod.poison = static_cast<s16>(elem.remove_int( "POISONRESISTMOD", 0 ));
element_resist_mod.physical = static_cast<s16>(elem.remove_int( "PHYSICALRESISTMOD", 0 ));
element_damage_mod.fire = static_cast<s16>(elem.remove_int( "FIREDAMAGEMOD", 0 ));
element_damage_mod.cold = static_cast<s16>(elem.remove_int( "COLDDAMAGEMOD", 0 ));
element_damage_mod.energy = static_cast<s16>(elem.remove_int( "ENERGYDAMAGEMOD", 0 ));
element_damage_mod.poison = static_cast<s16>(elem.remove_int( "POISONDAMAGEMOD", 0 ));
element_damage_mod.physical = static_cast<s16>(elem.remove_int( "PHYSICALDAMAGEMOD", 0 ));
}
void Item::builtin_on_use( Client* client )
{
send_sysmessage( client, "I can't think of a way to use that." );
}
void Item::double_click( Client* client )
{
const ItemDesc& itemdesc = find_itemdesc( objtype_ );
if (itemdesc.requires_attention && (client->chr->skill_ex_active() ||
client->chr->casting_spell()))
{
send_sysmessage( client, "I am already doing something else." );
return;
}
if (itemdesc.requires_attention && client->chr->hidden())
client->chr->unhide();
ref_ptr<EScriptProgram> prog;
if (!on_use_script_.empty())
{
ScriptDef sd( on_use_script_, NULL, "" );
prog = find_script2( sd,
true, // complain if not found
config.cache_interactive_scripts );
}
else if (!itemdesc.on_use_script.empty())
{
prog = find_script2( itemdesc.on_use_script,
true,
config.cache_interactive_scripts );
}
if (prog.get() != NULL)
{
if (client->chr->start_itemuse_script( prog.get(), this, itemdesc.requires_attention ))
return;
// else log the fact?
}
else
{
builtin_on_use( client );
}
}
unsigned short Item::get_senditem_amount() const
{
return amount_;
}
bool Item::setlayer( unsigned char in_layer )
{
if (tilelayer( graphic ) == in_layer)
{
layer = in_layer;
return true;
}
else
{
return false;
}
}
bool Item::stackable() const
{
return (tile_flags( graphic ) & FLAG::STACKABLE) ? true : false;
}
void Item::setamount( u16 amount )
{
set_dirty();
int oldweight = weight();
if (amount > amount_) // adding stuff
remove_resources( objtype_, amount - amount_ );
else
return_resources( objtype_, amount_ - amount );
amount_ = amount;
int newweight = weight();
if (container)
container->add_bulk( 0, newweight-oldweight );
increv();
send_object_cache_to_inrange(this);
}
void Item::subamount( u16 amount_subtract )
{
setamount( amount_ - amount_subtract );
increv();
}
void Item::add_to_self( Item*& item )
{
#ifdef PERGON
ct_merge_stacks_pergon( item ); // Pergon: Re-Calculate Property CreateTime after Merging of two Stacks
#endif
setamount( amount_ + item->amount_ );
if (!item->newbie())
newbie_ = false;
item->destroy();
item = NULL;
}
#ifdef PERGON
// Pergon: Re-Calculate Property CreateTime after Merging of two Stacks
void Item::ct_merge_stacks_pergon( Item*& item_sub )
{
long time_self, time_sub, time;
string value_self, value_sub;
// get "ct" of first stack - if error, init
if (getprop( "ct", value_self ))
{
BObjectImp* imp = BObjectImp::unpack( value_self.c_str() );
if (imp != NULL && imp->isa( BObjectImp::OTLong ))
time_self = static_cast<BLong*>(imp)->value();
else
time_self = read_gameclock();
}
else
time_self = read_gameclock();
// get "ct" of second stack - if error, init
if (item_sub->getprop( "ct", value_sub ))
{
BObjectImp* imp = BObjectImp::unpack( value_sub.c_str() );
if (imp != NULL && imp->isa( BObjectImp::OTLong ))
time_sub = static_cast<BLong*>(imp)->value();
else
time_sub = read_gameclock();
}
else
time_sub = read_gameclock();
// calculate new time
if (time_self < time_sub)
{
double factor = (item_sub->amount_ * 1.0) / (amount_ + item_sub->amount_);
time = long((time_sub - time_self) * factor + time_self);
}
else if (time_self > time_sub)
{
double factor = (amount_ * 1.0) / (amount_ + item_sub->amount_);
time = long((time_self - time_sub) * factor + time_sub);
}
else
time = time_self;
setprop("ct", "i" + decint( time ));
increv();
}
// Pergon: Re-Calculate Property CreateTime after Adding Items to a Stack
void Item::ct_merge_stacks_pergon( u16 amount_sub )
{
long time_self, time_sub, time;
string value_self;
// get "ct" of first stack - if error, init
if (getprop( "ct", value_self ))
{
BObjectImp* imp = BObjectImp::unpack( value_self.c_str() );
if (imp != NULL && imp->isa( BObjectImp::OTLong ))
time_self = static_cast<BLong*>(imp)->value();
else
time_self = read_gameclock();
}
else
time_self = read_gameclock();
// set "ct" of second new stack
time_sub = read_gameclock();
// calculate new time
if (time_self != time_sub)
{
double factor = (amount_sub * 1.0) / (amount_ + amount_sub);
time = long((time_sub - time_self) * factor + time_self);
}
else
time = time_self;
setprop("ct", "i" + decint( time ));
increv();
}
#endif
bool Item::can_add_to_self( unsigned short amount ) const
{
unsigned long amount1 = (unsigned long) amount_;
unsigned long amount2 = (unsigned long) amount;
if ((amount1 + amount2) > MAX_STACK_ITEMS)
return false;
if (container != NULL)
{
long more_weight = weight_of(amount_+amount) - weight_of(amount_);
if (more_weight > USHRT_MAX/*std::numeric_limits<unsigned short>::max()*/)
return false;
return container->can_add(static_cast<unsigned short>(more_weight));
}
return true;
}
bool Item::can_add_to_self( const Item& item ) const //dave 1/26/03 totally changed this function to handle the cprop comparisons.
{
bool res = ( (item.objtype_ == objtype_) &&
(item.newbie() == newbie()) &&
(item.graphic == graphic) &&
(item.color == color) &&
(!inuse()) &&
(can_add_to_self( item.amount_ ))
);
if(res == true)
{
//NOTE! this logic is copied in Item::has_only_default_cprops(), so make any necessary changes there too
PropertyList myprops(getprops()); //make a copy :(
myprops -= itemdesc().ignore_cprops;
myprops -= Global_Ignore_CProps;
PropertyList yourprops(item.getprops()); //make a copy :(
yourprops -= item.itemdesc().ignore_cprops;
yourprops -= Global_Ignore_CProps;
res = (myprops == yourprops);
//myprops.printProperties(cout);
}
return res;
}
//dave added 5/11/3
bool Item::has_only_default_cprops(const ItemDesc* compare) const
{
if( compare == NULL )
compare = &(itemdesc());
//logic same as Item::can_add_to_self()
PropertyList myprops(getprops()); //make a copy :(
myprops -= itemdesc().ignore_cprops;
myprops -= Global_Ignore_CProps;
PropertyList yourprops(compare->props); //make a copy :(
yourprops -= compare->ignore_cprops;
yourprops -= Global_Ignore_CProps;
return (myprops == yourprops);
}
bool Item::amount_to_remove_is_partial( u16 this_item_new_amount ) const
{
return ((this_item_new_amount < amount_) && // less than what we have
(amount_ > 1) && // we are a stack
(this_item_new_amount > 0)); // if new size 0, assume whole stack
}
/* slice_stacked_item:
Used when a portion of a stack is dragged.
Given ITEM1, with, for example, amount 75.
If 25 units are dragged off, this function will get called
with 25. What happens then:
ITEM2 is created, with Amount 50. It it placed where ITEM1 used to be.
ITEM1's Amount is set to 25.
In short, we go from:
ITEM1 (Amount:75)
TO
ITEM1 (Amount:25) -under cursor, ITEM2(Amount:50) where ITEM1 was.
*/
Item* Item::slice_stacked_item( u16 this_item_new_amount )
{
Item* new_item = clone();
if (new_item != NULL)
{
new_item->setamount( new_item->amount_ - this_item_new_amount );
setamount( this_item_new_amount );
}
return new_item;
}
Item* Item::remove_part_of_stack( u16 amount_to_remove )
{
Item* new_item = clone();
if (new_item != NULL)
{
new_item->setamount( amount_to_remove );
subamount( amount_to_remove );
}
return new_item;
}
void Item::set_use_script( const string& scriptname )
{
set_dirty();
on_use_script_ = scriptname;
}
bool Item::saveonexit() const
{
return saveonexit_;
}
void Item::saveonexit( bool newvalue )
{
saveonexit_ = newvalue;
}
bool Item::setgraphic( u16 newgraphic )
{
if (graphic <= UOBJ_ITEM__HIGHEST && newgraphic <= UOBJ_ITEM__HIGHEST)
{
set_dirty();
graphic = newgraphic;
graphic_ext = ctBEu16(graphic);
height = tileheight( graphic );
tile_layer = tilelayer( graphic );
increv();
update_item_to_inrange( this );
return true;
}
else
{
return false;
}
}
bool Item::setcolor( u16 newcolor )
{
// return false if the color is invalid (high nibble set)
bool res = true;
u16 theMask = (u16) ssopt.item_color_mask;
if ((newcolor & (~theMask)) != 0)
res = false;
set_dirty();
newcolor &= (theMask);
color = newcolor;
color_ext = ctBEu16( color );
return res;
}
void Item::on_color_changed()
{
update_item_to_inrange( this );
}
void Item::on_invisible_changed()
{
update_item_to_inrange( this );
}
void Item::setfacing( u8 newfacing )
{
// allow 0-127 (all but MSB)
facing = newfacing;
if (facing > 127)
facing = 0;
}
void Item::on_facing_changed()
{
update_item_to_inrange( this );
}
void Item::extricate()
{
if (container != NULL)
{
// hmm, a good place for a virtual?
if (IsCharacter( container->serial ))
{
Character* chr = chr_from_wornitems( container );
passert_always( chr != NULL ); // PRODFIXME linux-crash
passert_always( chr->is_equipped(this) );
chr->unequip(this); //FIXME: should run unequip script
}
else
{
container->remove( this );//FIXME: should probably call can/onRemove for the container
}
}
}
void Item::spill_contents( UMulti* multi )
{
}
unsigned long Item::weight_of( unsigned short amount ) const
{
const ItemDesc& id = find_itemdesc( objtype_ );
unsigned long amt = amount;
amt *= id.weightmult;
if (id.weightdiv != 1 && id.weightdiv != 0)
{
amt = (amt + id.weightdiv - 1 ) / id.weightdiv;
}
return amt;
}
unsigned long Item::weight() const
{
return weight_of(amount_);
}
unsigned long Item::item_count() const
{
return 1;
}
/////////////////////////////////////////////////////////////////////////////
// Decay-related functions
/////////////////////////////////////////////////////////////////////////////
#include "gameclck.h"
void Item::set_decay_after( unsigned long seconds )
{
set_dirty();
// Why alter it, if it should not have it to begin with??
if ( decayat_gameclock_ != 0 )
{
decayat_gameclock_ = read_gameclock() + seconds;
}
}
bool Item::should_decay( unsigned long gameclock ) const
{
return !inuse() &&
(movable_ || (objtype_ == UOBJ_CORPSE)) &&
decayat_gameclock_ &&
(gameclock > decayat_gameclock_);
}
void Item::restart_decay_timer()
{
// Why restart it, if it shouldn't have it to begin with??
if ( decayat_gameclock_ != 0 )
{
unsigned long dtime = itemdesc().decay_time * 60;
if ( should_decay( read_gameclock() + dtime ) )
{
set_decay_after( dtime );
}
}
}
void Item::disable_decay()
{
set_dirty();
decayat_gameclock_ = 0;
}
/////////////////////////////////////////////////////////////////////////////
// Equip-Script related functions
/////////////////////////////////////////////////////////////////////////////
#include "bscript/bobject.h"
bool Item::has_equip_script() const
{
return !equip_script_.empty();
}
BObjectImp* Item::run_equip_script( Character* who, bool startup )
{
ScriptDef sd;
try
{
sd.config( equip_script_, itemdesc().pkg, "scripts/control/" );
}
catch( std::exception& ex )
{
return new BError( string("Script descriptor error") + ex.what() );
}
try
{
return run_script_to_completion( sd,
new ECharacterRefObjImp( who ),
new EItemRefObjImp( this ),
new BLong( startup ? 1 : 0 ) );
}
catch( std::exception& ex )
{
return new BError( string("Script execution error") + ex.what() );
}
}
BObjectImp* Item::run_unequip_script( Character* who )
{
ScriptDef sd;
try
{
sd.config( unequip_script_, itemdesc().pkg, "scripts/control/" );
}
catch( std::exception& ex )
{
return new BError( string("Script descriptor error: ") + ex.what() );
}
try
{
return run_script_to_completion( sd,
new ECharacterRefObjImp( who ),
new EItemRefObjImp( this ) );
}
catch( std::exception& ex )
{
return new BError( string("Script execution error:") + ex.what() );
}
}
bool Item::check_equip_script( Character* chr, bool startup )
{
if (has_equip_script())
{
BObject obj( run_equip_script( chr, startup ) );
return obj.isTrue();
}
else
{
return true;
}
}
bool Item::check_unequip_script()
{
if (!unequip_script_.empty() &&
container != NULL &&
IsCharacter( container->serial ))
{
Character* chr = chr_from_wornitems( container );
passert_always( chr != NULL );
passert_always( chr->is_equipped(this) );
BObject obj( run_unequip_script( chr ) );
return obj.isTrue();
}
else
{
return true;
}
}
void preload_test_scripts(const std::string& script_ecl)
{
ScriptDef sd;
sd.quickconfig( "scripts/misc/" + script_ecl );
if (sd.exists())
{
find_script2( sd, true, true );
}
Packages::iterator itr = packages.begin(), end = packages.end();
for( ; itr != end; ++itr )
{
Package* pkg = *itr;
sd.quickconfig( pkg, script_ecl );
if (sd.exists())
{
find_script2( sd, true, true );
}
}
}
void preload_test_scripts()
{
preload_test_scripts( "equiptest.ecl" );
preload_test_scripts( "unequiptest.ecl" );
}
bool Item::check_test_scripts( Character* chr, const std::string& script_ecl, bool startup )
{
ScriptDef sd;
sd.quickconfig( "scripts/misc/" + script_ecl );
this->inuse( true );
if (script_loaded( sd ))
{
bool res = call_script( sd,
new ECharacterRefObjImp(chr),
new EItemRefObjImp(this),
new BLong(startup));
this->inuse( false );
if (!res)
return false;
}
Packages::iterator itr = packages.begin(), end = packages.end();
for( ; itr != end; ++itr )
{
Package* pkg = *itr;
sd.quickconfig( pkg, script_ecl );
if ( script_loaded( sd ) )
{
bool res = call_script( sd,
new ECharacterRefObjImp(chr),
new EItemRefObjImp(this),
new BLong(startup));
this->inuse( false );
if (!res)
return false;
}
}
this->inuse( false );
return true;
}
bool Item::check_equiptest_scripts( Character* chr, bool startup )
{
return check_test_scripts( chr, "equiptest.ecl", startup );
}
bool Item::check_unequiptest_scripts( Character* chr )
{
return check_test_scripts( chr, "unequiptest.ecl", false );
}
bool Item::check_unequiptest_scripts()
{
if (container != NULL &&
IsCharacter( container->serial ))
{
Character* chr = chr_from_wornitems( container );
passert_always( chr != NULL );
passert_always( chr->is_equipped(this) );
return check_unequiptest_scripts( chr );
}
else
{
return true;
}
}
s16 Item::calc_element_resist( unsigned element ) const
{
switch(element)
{
case ELEMENTAL_FIRE: return (element_resist.fire + element_resist_mod.fire); break;
case ELEMENTAL_COLD: return (element_resist.cold + element_resist_mod.cold); break;
case ELEMENTAL_ENERGY: return (element_resist.energy + element_resist_mod.energy); break;
case ELEMENTAL_POISON: return (element_resist.poison + element_resist_mod.poison); break;
case ELEMENTAL_PHYSICAL: return (element_resist.physical + element_resist_mod.physical); break;
}
return 0;
}
s16 Item::calc_element_damage( unsigned element ) const
{
switch(element)
{
case ELEMENTAL_FIRE: return (element_damage.fire + element_damage_mod.fire); break;
case ELEMENTAL_COLD: return (element_damage.cold + element_damage_mod.cold); break;
case ELEMENTAL_ENERGY: return (element_damage.energy + element_damage_mod.energy); break;
case ELEMENTAL_POISON: return (element_damage.poison + element_damage_mod.poison); break;
case ELEMENTAL_PHYSICAL: return (element_damage.physical + element_damage_mod.physical); break;
}
return 0;
}
bool Item::has_resistance(Character* chr)
{
if ( ((element_resist.fire + element_resist_mod.fire) != 0) || ((element_resist.cold + element_resist_mod.cold) != 0) ||
((element_resist.energy + element_resist_mod.energy) != 0) || ((element_resist.poison + element_resist_mod.poison) != 0) ||
((element_resist.physical + element_resist_mod.physical) != 0))
return true;
else
{
//double new_ar = 0.0;
UArmor* armor = static_cast<UArmor*>(this); // Not sure if I like this method.
if (armor != NULL)
{
if (armor->ar() > 0)
return true;
}
}
return false;
}
bool Item::has_element_damage()
{
if ( ((element_damage.fire + element_damage_mod.fire) != 0) || ((element_damage.cold + element_damage_mod.cold) != 0) ||
((element_damage.energy + element_damage_mod.energy) != 0) || ((element_damage.poison + element_damage_mod.poison) != 0) ||
((element_damage.physical + element_damage_mod.physical) != 0))
{
return true;
}
return false;
}
//DAVE added this 11/17, Shortcut function to get a pointer to the owner character
Character* Item::GetCharacterOwner()
{
UObject* top_level_item = toplevel_owner();
if(top_level_item->isa(CLASS_CONTAINER))
{
Character* chr_owner = chr_from_wornitems( static_cast<UContainer*>(top_level_item) );
if(chr_owner != NULL)
{
return chr_owner;
}
else
return NULL;
}
else
return NULL;
}
const char* Item::target_tag() const
{
return "item";
}