mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
* rewrite of saving, replaced operator way with a generic add method, thus not everywhere the formating of the savefiles is hardcoded. since its the last spot which uses the old formating lib, remobed the lib * removed cmake of old format lib * testscripts for load/save of UObject,Item,Armor,Weapon * fixed: member faster_cast_recovery and faster_cast_recovery_mod getter returned a wrong value * wrong/missing docs * test containers, lockable, map, spellbooks fixed save bug in map and spellbook removed ancient savestrategy experiments * added corpse test splitted testfiles * fixed loading (ancient bug) and saving (PR bug) of Reportables added tests for houses, boats, accounts and chars * fixed include * fix shutdown leak * added npc tests upload complete coretest folder also on failure * do not write and load RegisteredHouse for NPCs two times readded extra newline between accounts * tests for guild, party, realm added resource cfgs, but a test is not really possible since nothing of interest is stored * added datafile, storage test * activated resource loading, 25year old bug, but since the stored values are senseless it makes no real difference. added a few wtf comments catch all exceptions during formatting of log lines * replaced .write with explixit .begin, .end and .comment. for leftovers.txt the StreamSaver was also used, since the format is different use directly ofstream * added corechanges
534 lines
16 KiB
C++
534 lines
16 KiB
C++
/** @file
|
|
*
|
|
* @par History
|
|
* - 2005/03/09 Shinigami: Added Prop Delay [ms]
|
|
* - 2005/07/25 Shinigami: added MinDamage and MaxDamage in Weapon-Descriptor
|
|
* - 2011/11/12 Tomi: added extobj.wrestling
|
|
*/
|
|
|
|
|
|
#include "weapon.h"
|
|
|
|
#include <stddef.h>
|
|
#include <string>
|
|
|
|
#include "../../bscript/bstruct.h"
|
|
#include "../../bscript/executor.h"
|
|
#include "../../bscript/impstr.h"
|
|
#include "../../clib/cfgelem.h"
|
|
#include "../../clib/logfacility.h"
|
|
#include "../../clib/passert.h"
|
|
#include "../../clib/streamsaver.h"
|
|
#include "../../clib/strutil.h"
|
|
#include "../../plib/systemstate.h"
|
|
#include "../containr.h"
|
|
#include "../dice.h"
|
|
#include "../equipdsc.h"
|
|
#include "../extobj.h"
|
|
#include "../globals/settings.h"
|
|
#include "../globals/uvars.h"
|
|
#include "../layers.h"
|
|
#include "../mobile/charactr.h"
|
|
#include "../realms/realm.h"
|
|
#include "../syshookscript.h"
|
|
#include "../ufunc.h"
|
|
#include "../umanip.h"
|
|
#include "../uobject.h"
|
|
#include "itemdesc.h"
|
|
#include "wepntmpl.h"
|
|
|
|
namespace Pol
|
|
{
|
|
namespace Items
|
|
{
|
|
/*
|
|
WeaponDesc::WeaponDesc() :
|
|
skillid( SKILLID_WRESTLING ),
|
|
speed( 100 ),
|
|
delay( 0 ),
|
|
damage_dice(),
|
|
projectile(false),
|
|
projectile_type(0),
|
|
maxhp(1)
|
|
{
|
|
}
|
|
*/
|
|
|
|
Core::UACTION remove_action( Clib::ConfigElem& elem, const char* name, Core::UACTION deflt )
|
|
{
|
|
unsigned short tmp = elem.remove_ushort( name, static_cast<unsigned short>( deflt ) );
|
|
if ( Core::UACTION_IS_VALID( tmp ) )
|
|
{
|
|
return static_cast<Core::UACTION>( tmp );
|
|
}
|
|
else
|
|
{
|
|
elem.throw_error( std::string( name ) + " is out of range" );
|
|
}
|
|
}
|
|
Core::UACTION remove_action( Clib::ConfigElem& elem, const char* name )
|
|
{
|
|
unsigned short tmp = elem.remove_ushort( name );
|
|
if ( Core::UACTION_IS_VALID( tmp ) )
|
|
{
|
|
return static_cast<Core::UACTION>( tmp );
|
|
}
|
|
else
|
|
{
|
|
elem.throw_error( std::string( name ) + " is out of range" );
|
|
}
|
|
}
|
|
|
|
Core::UACTION default_anim( bool two_handed )
|
|
{
|
|
if ( two_handed )
|
|
return Core::ACTION_RIDINGHORSE7;
|
|
else
|
|
return Core::ACTION_RIDINGHORSE4;
|
|
}
|
|
|
|
WeaponDesc::WeaponDesc( u32 objtype, Clib::ConfigElem& elem, const Plib::Package* pkg )
|
|
: EquipDesc( objtype, elem, WEAPONDESC, pkg ),
|
|
pAttr( nullptr ),
|
|
delay( elem.remove_ushort( "DELAY", 0 ) ),
|
|
hit_script( elem.remove_string( "HITSCRIPT", "" ), pkg, "scripts/items/" ),
|
|
damage_dice(),
|
|
projectile( elem.remove_bool( "PROJECTILE", false ) ),
|
|
projectile_type( 0 ),
|
|
projectile_anim( 0 ),
|
|
projectile_sound( 0 ),
|
|
anim( remove_action( elem, "Anim", Core::ACTION_HUMAN_ATTACK ) ),
|
|
mounted_anim( remove_action( elem, "MountedAnim", anim ) ),
|
|
hit_sound( elem.remove_ushort( "HITSOUND", 0 ) ),
|
|
miss_sound( elem.remove_ushort( "MISSSOUND", 0 ) ),
|
|
two_handed( elem.remove_bool( "TWOHANDED", false ) ),
|
|
minrange( elem.remove_ushort( "MINRANGE", projectile ? 2 : 0 ) ),
|
|
maxrange( elem.remove_ushort( "MAXRANGE", projectile ? 20 : 1 ) )
|
|
{
|
|
if ( delay == 0 )
|
|
speed = elem.remove_ushort( "SPEED" );
|
|
else
|
|
speed = 35;
|
|
|
|
std::string attrname = elem.remove_string( "Attribute", "" );
|
|
if ( attrname.empty() )
|
|
{
|
|
attrname = elem.remove_string( "SkillID", "" );
|
|
|
|
// throw the error this time
|
|
if ( attrname.empty() )
|
|
attrname = elem.remove_string( "Attribute" );
|
|
|
|
elem.warn( "Element specifies a SkillID instead of an Attribute" );
|
|
}
|
|
|
|
pAttr = Mobile::Attribute::FindAttribute( attrname );
|
|
if ( !pAttr )
|
|
{
|
|
elem.throw_error( "Attribute " + attrname + " not found" );
|
|
}
|
|
|
|
if ( elem.has_prop( "INTRINSIC" ) )
|
|
{
|
|
elem.throw_error( "Weapon has INTRINSIC property, which is no longer needed" );
|
|
}
|
|
|
|
if ( speed <= 0 )
|
|
{
|
|
elem.throw_error( "Weapon has illegal Speed value (Speed must be positive)" );
|
|
}
|
|
if ( maxhp <= 0 )
|
|
{
|
|
elem.throw_error( "Weapon has illegal MaxHP value (MaxHP must be positive)" );
|
|
}
|
|
|
|
std::string errmsg;
|
|
if ( !damage_dice.load( elem.remove_string( "DAMAGE" ).c_str(), &errmsg ) )
|
|
{
|
|
elem.throw_error( "Error parsing DAMAGE string for WeaponTemplate\n" + errmsg );
|
|
|
|
/*cerr << "Error parsing DAMAGE string for WeaponTemplate " << objtype << endl;
|
|
cerr << " " << errmsg << endl;
|
|
throw runtime_error( "Configuration error" );*/
|
|
}
|
|
|
|
|
|
if ( projectile )
|
|
{
|
|
projectile_type = elem.remove_ushort( "ProjectileType" );
|
|
projectile_anim = elem.remove_ushort( "ProjectileAnim" );
|
|
projectile_sound = elem.remove_ushort( "ProjectileSound" );
|
|
}
|
|
}
|
|
|
|
unsigned short WeaponDesc::get_random_damage() const
|
|
{
|
|
return damage_dice.roll();
|
|
}
|
|
|
|
void WeaponDesc::PopulateStruct( Bscript::BStruct* descriptor ) const
|
|
{
|
|
using namespace Bscript;
|
|
base::PopulateStruct( descriptor );
|
|
descriptor->addMember( "Speed", new BLong( speed ) );
|
|
descriptor->addMember( "Delay", new BLong( delay ) );
|
|
|
|
descriptor->addMember( "Projectile", new BLong( projectile ) );
|
|
descriptor->addMember( "ProjectileType", new BLong( projectile_type ) );
|
|
descriptor->addMember( "ProjectileAnim", new BLong( projectile_anim ) );
|
|
descriptor->addMember( "ProjectileSound", new BLong( projectile_sound ) );
|
|
|
|
descriptor->addMember( "HitSound", new BLong( hit_sound ) );
|
|
descriptor->addMember( "MissSound", new BLong( miss_sound ) );
|
|
|
|
descriptor->addMember( "MinRange", new BLong( minrange ) );
|
|
descriptor->addMember( "MaxRange", new BLong( maxrange ) );
|
|
|
|
// descriptor->addMember( "Intrinsic", new BLong(is_intrinsic) );
|
|
descriptor->addMember( "TwoHanded", new BLong( two_handed ) );
|
|
|
|
descriptor->addMember( "Anim", new BLong( anim ) );
|
|
descriptor->addMember( "MountedAnim", new BLong( mounted_anim ) );
|
|
|
|
std::string tempstr;
|
|
damage_dice.die_string( tempstr );
|
|
descriptor->addMember( "Damage", new String( tempstr ) );
|
|
descriptor->addMember( "MinDamage", new BLong( damage_dice.min_value() ) );
|
|
descriptor->addMember( "MaxDamage", new BLong( damage_dice.max_value() ) );
|
|
|
|
descriptor->addMember( "HitScript", new String( hit_script.relativename( pkg ) ) );
|
|
descriptor->addMember( "Attribute", new String( pAttr->name ) );
|
|
}
|
|
|
|
size_t WeaponDesc::estimatedSize() const
|
|
{
|
|
return base::estimatedSize() + sizeof( const Mobile::Attribute* ) /*pAttr*/
|
|
+ sizeof( unsigned short ) /*speed*/
|
|
+ sizeof( unsigned short ) /*delay*/
|
|
+ sizeof( Core::ScriptDef ) /*hit_script*/
|
|
+ sizeof( Core::Dice ) /*damage_dice*/
|
|
+ sizeof( bool ) /*projectile*/
|
|
+ sizeof( unsigned short ) /*projectile_type*/
|
|
+ sizeof( unsigned short ) /*projectile_anim*/
|
|
+ sizeof( unsigned short ) /*projectile_sound*/
|
|
+ sizeof( Core::UACTION ) /*anim*/
|
|
+ sizeof( Core::UACTION ) /*mounted_anim*/
|
|
+ sizeof( unsigned short ) /*hit_sound*/
|
|
+ sizeof( unsigned short ) /*miss_sound*/
|
|
+ sizeof( bool ) /*two_handed*/
|
|
+ sizeof( unsigned short ) /*minrange*/
|
|
+ sizeof( unsigned short ) /*maxrange*/
|
|
+ hit_script.estimatedSize();
|
|
}
|
|
|
|
/// Creates a new intrinsic weapon and returns it
|
|
/// @param name: the unique weapon's name
|
|
/// @param elem: the config element to create from
|
|
/// @param pkg: the package
|
|
UWeapon* create_intrinsic_weapon( const char* name, Clib::ConfigElem& elem,
|
|
const Plib::Package* pkg )
|
|
{
|
|
auto tmpl = new WeaponDesc( Core::settingsManager.extobj.wrestling, elem, pkg );
|
|
tmpl->is_intrinsic = true;
|
|
auto wpn = new UWeapon( *tmpl, tmpl );
|
|
wpn->layer = Core::LAYER_HAND1;
|
|
wpn->tmpl = tmpl;
|
|
wpn->copyprops( tmpl->props );
|
|
|
|
Items::register_intrinsic_equipment( name, wpn );
|
|
|
|
return wpn;
|
|
}
|
|
|
|
/// Creates the intrinsic wrestling weapon for PCs
|
|
/// must be called at startup
|
|
void load_intrinsic_weapons()
|
|
{
|
|
const ItemDesc& id = find_itemdesc( Core::settingsManager.extobj.wrestling );
|
|
if ( id.save_on_exit )
|
|
throw std::runtime_error( "Wrestling weapon " +
|
|
Clib::hexint( Core::settingsManager.extobj.wrestling ) +
|
|
" must specify SaveOnExit 0" );
|
|
|
|
if ( id.type == ItemDesc::WEAPONDESC )
|
|
{
|
|
const WeaponDesc* weapon_descriptor = static_cast<const WeaponDesc*>( &id );
|
|
Core::gamestate.wrestling_weapon = new UWeapon( *weapon_descriptor, weapon_descriptor );
|
|
|
|
{
|
|
// sets wrestling weapondesc as intrinsic
|
|
WeaponDesc* wdesc = const_cast<WeaponDesc*>( weapon_descriptor );
|
|
wdesc->is_intrinsic = true;
|
|
wdesc->is_pc_intrinsic = true;
|
|
}
|
|
Core::gamestate.wrestling_weapon->inuse( true );
|
|
Core::gamestate.wrestling_weapon->layer = Core::LAYER_HAND1;
|
|
|
|
Items::insert_intrinsic_equipment( "PC_weapon", Core::gamestate.wrestling_weapon );
|
|
}
|
|
|
|
// wrestling_weapon = find_intrinsic_weapon( "Wrestling" );
|
|
if ( Core::gamestate.wrestling_weapon == nullptr )
|
|
throw std::runtime_error( "A WeaponTemplate for Wrestling is required in itemdesc.cfg" );
|
|
}
|
|
|
|
/// Creates a new intrinic weapon for an NPC template and returns it
|
|
/// @param elem: The conig element defining the NPC
|
|
/// @param pkg: The package
|
|
/// @returns The created weapon or nullptr if none is defined in the template
|
|
UWeapon* create_intrinsic_weapon_from_npctemplate( Clib::ConfigElem& elem,
|
|
const Plib::Package* pkg )
|
|
{
|
|
if ( !elem.has_prop( "AttackSpeed" ) && !elem.has_prop( "AttackDelay" ) )
|
|
return nullptr;
|
|
|
|
std::string tmp;
|
|
// Construct a WeaponTemplate for this NPC template.
|
|
Clib::ConfigElem wpnelem;
|
|
wpnelem.set_rest( elem.rest() );
|
|
wpnelem.set_source( elem );
|
|
wpnelem.add_prop( "Objtype", "0xFFFF" );
|
|
wpnelem.add_prop( "Graphic", "1" );
|
|
if ( elem.remove_prop( "AttackSpeed", &tmp ) )
|
|
wpnelem.add_prop( "Speed", tmp );
|
|
|
|
if ( elem.remove_prop( "AttackDelay", &tmp ) )
|
|
wpnelem.add_prop( "Delay", tmp );
|
|
|
|
wpnelem.add_prop( "Damage", elem.remove_string( "AttackDamage" ) );
|
|
|
|
if ( elem.has_prop( "AttackSkillId" ) )
|
|
wpnelem.add_prop( "SkillId", elem.remove_string( "AttackSkillId" ) );
|
|
else
|
|
wpnelem.add_prop( "Attribute", elem.remove_string( "AttackAttribute" ) );
|
|
|
|
wpnelem.add_prop( "HitSound", elem.remove_string( "AttackHitSound", "0" ) );
|
|
wpnelem.add_prop( "MissSound", elem.remove_string( "AttackMissSound", "0" ) );
|
|
wpnelem.add_prop( "Anim", elem.remove_string( "AttackAnimation", "0" ) );
|
|
wpnelem.add_prop( "MaxHp", "1" );
|
|
if ( elem.remove_prop( "AttackHitScript", &tmp ) )
|
|
wpnelem.add_prop( "HitScript", tmp );
|
|
if ( elem.remove_prop( "AttackMinRange", &tmp ) )
|
|
wpnelem.add_prop( "MinRange", tmp );
|
|
if ( elem.remove_prop( "AttackMaxRange", &tmp ) )
|
|
wpnelem.add_prop( "MaxRange", tmp );
|
|
|
|
if ( elem.has_prop( "AttackProjectile" ) )
|
|
{
|
|
wpnelem.add_prop( "Projectile", "1" );
|
|
wpnelem.add_prop( "ProjectileType", elem.remove_string( "AttackProjectileType" ) );
|
|
wpnelem.add_prop( "ProjectileAnim", elem.remove_string( "AttackProjectileAnim" ) );
|
|
wpnelem.add_prop( "ProjectileSound", elem.remove_string( "AttackProjectileSound" ) );
|
|
}
|
|
|
|
while ( elem.remove_prop( "AttackCProp", &tmp ) )
|
|
wpnelem.add_prop( "CProp", tmp );
|
|
|
|
return create_intrinsic_weapon( elem.rest(), wpnelem, pkg );
|
|
}
|
|
|
|
UWeapon::UWeapon( const WeaponDesc& descriptor, const WeaponDesc* permanent_descriptor )
|
|
: Equipment( descriptor, Core::UOBJ_CLASS::CLASS_WEAPON, permanent_descriptor ),
|
|
hit_script_( descriptor.hit_script )
|
|
{
|
|
}
|
|
|
|
size_t UWeapon::estimatedSize() const
|
|
{
|
|
return base::estimatedSize() + hit_script_.estimatedSize();
|
|
}
|
|
|
|
unsigned short UWeapon::speed() const
|
|
{
|
|
int speed_ = WEAPON_TMPL->speed + speed_mod();
|
|
|
|
if ( speed_ < 0 )
|
|
return 0;
|
|
else if ( speed_ <= USHRT_MAX )
|
|
return static_cast<u16>( speed_ );
|
|
else
|
|
return USHRT_MAX;
|
|
}
|
|
|
|
unsigned short UWeapon::delay() const
|
|
{
|
|
return WEAPON_TMPL->delay;
|
|
}
|
|
|
|
const Mobile::Attribute& UWeapon::attribute() const
|
|
{
|
|
return *( WEAPON_TMPL->pAttr );
|
|
}
|
|
|
|
unsigned short UWeapon::get_random_damage() const
|
|
{
|
|
int dmg = int( WEAPON_TMPL->get_random_damage() ) * hp_ / maxhp();
|
|
dmg += damage_mod();
|
|
if ( dmg < 0 )
|
|
return 0;
|
|
else if ( dmg <= USHRT_MAX )
|
|
return static_cast<unsigned short>( dmg );
|
|
else
|
|
return USHRT_MAX;
|
|
}
|
|
|
|
unsigned short UWeapon::min_weapon_damage() const
|
|
{
|
|
int dmg = static_cast<int>( WEAPON_TMPL->damage_dice.min_value() ) + damage_mod();
|
|
if ( dmg < 0 )
|
|
return 0;
|
|
else if ( dmg <= USHRT_MAX )
|
|
return static_cast<unsigned short>( dmg );
|
|
return USHRT_MAX;
|
|
}
|
|
|
|
unsigned short UWeapon::max_weapon_damage() const
|
|
{
|
|
int dmg = static_cast<int>( WEAPON_TMPL->damage_dice.max_value() ) + damage_mod();
|
|
if ( dmg < 0 )
|
|
return 0;
|
|
else if ( dmg <= USHRT_MAX )
|
|
return static_cast<unsigned short>( dmg );
|
|
return USHRT_MAX;
|
|
}
|
|
|
|
bool UWeapon::is_projectile() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->projectile;
|
|
}
|
|
|
|
unsigned short UWeapon::projectile_sound() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->projectile_sound;
|
|
}
|
|
|
|
unsigned short UWeapon::projectile_anim() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->projectile_anim;
|
|
}
|
|
|
|
Core::UACTION UWeapon::anim() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->anim;
|
|
}
|
|
|
|
Core::UACTION UWeapon::mounted_anim() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->mounted_anim;
|
|
}
|
|
|
|
unsigned short UWeapon::hit_sound() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->hit_sound;
|
|
}
|
|
|
|
unsigned short UWeapon::miss_sound() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return WEAPON_TMPL->miss_sound;
|
|
}
|
|
|
|
const WeaponDesc& UWeapon::descriptor() const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
return *WEAPON_TMPL;
|
|
}
|
|
|
|
bool UWeapon::consume_projectile( Core::UContainer* cont ) const
|
|
{
|
|
passert( tmpl != nullptr );
|
|
|
|
Item* item = cont->find_objtype_noninuse( WEAPON_TMPL->projectile_type );
|
|
if ( item != nullptr )
|
|
{
|
|
subtract_amount_from_item( item, 1 );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool UWeapon::in_range( const Mobile::Character* wielder, const Mobile::Character* target ) const
|
|
{
|
|
unsigned short dist = wielder->distance_to( target->toplevel_pos() );
|
|
INFO_PRINTLN_TRACE( 22 )
|
|
( "in_range({:#x},{:#x}):\n"
|
|
"dist: {}\n"
|
|
"minrange: {}\n"
|
|
"maxrange: {}\n"
|
|
"has_los: {}",
|
|
wielder->serial, target->serial, dist, WEAPON_TMPL->minrange, WEAPON_TMPL->maxrange,
|
|
wielder->realm()->has_los( *wielder, *target ) );
|
|
return ( dist >= WEAPON_TMPL->minrange && dist <= WEAPON_TMPL->maxrange &&
|
|
wielder->realm()->has_los( *wielder, *target ) );
|
|
}
|
|
|
|
// FIXME weak, weak..
|
|
Item* UWeapon::clone() const
|
|
{
|
|
UWeapon* wpn = static_cast<UWeapon*>( base::clone() );
|
|
wpn->hit_script_ = hit_script_;
|
|
wpn->damage_mod( this->damage_mod() );
|
|
wpn->speed_mod( this->speed_mod() );
|
|
|
|
return wpn;
|
|
}
|
|
|
|
void UWeapon::printProperties( Clib::StreamWriter& sw ) const
|
|
{
|
|
base::printProperties( sw );
|
|
|
|
short speed_mod_ = speed_mod();
|
|
short dmg_mod = damage_mod();
|
|
|
|
if ( dmg_mod )
|
|
sw.add( "dmg_mod", dmg_mod );
|
|
if ( speed_mod_ )
|
|
sw.add( "speed_mod", speed_mod_ );
|
|
if ( !( hit_script_ == WEAPON_TMPL->hit_script ) )
|
|
sw.add( "HitScript", hit_script_.relativename( tmpl->pkg ) );
|
|
}
|
|
|
|
void UWeapon::readProperties( Clib::ConfigElem& elem )
|
|
{
|
|
base::readProperties( elem );
|
|
|
|
damage_mod( static_cast<s16>( elem.remove_int( "DMG_MOD", 0 ) ) );
|
|
speed_mod( static_cast<s16>( elem.remove_int( "SPEED_MOD", 0 ) ) );
|
|
|
|
// if the HITSCRIPT is not specified in the data file, keep the value from the template.
|
|
if ( elem.has_prop( "HITSCRIPT" ) )
|
|
set_hit_script( elem.remove_string( "HITSCRIPT" ) );
|
|
}
|
|
|
|
void UWeapon::set_hit_script( const std::string& scriptname )
|
|
{
|
|
if ( scriptname.empty() )
|
|
{
|
|
hit_script_.clear();
|
|
}
|
|
else
|
|
{
|
|
passert( tmpl != nullptr );
|
|
hit_script_.config( scriptname, tmpl->pkg, "scripts/items/", true );
|
|
}
|
|
}
|
|
|
|
bool UWeapon::get_method_hook( const char* methodname, Bscript::Executor* ex,
|
|
Core::ExportScript** hook, unsigned int* PC ) const
|
|
{
|
|
if ( Core::gamestate.system_hooks.get_method_hook(
|
|
Core::gamestate.system_hooks.weapon_method_script.get(), methodname, ex, hook, PC ) )
|
|
return true;
|
|
return base::get_method_hook( methodname, ex, hook, PC );
|
|
}
|
|
} // namespace Items
|
|
} // namespace Pol
|