//===== Hercules Plugin ======================================
//= Charms
//===== By: ==================================================
//= AnnieRuru
//= original by digitalhamster
//===== Current Version: =====================================
//= 1.1
//===== Compatible With: =====================================
//= Hercules 2015-12-27
//===== Description: =========================================
//= Give item bonus like the game DiabloII
//===== Topic ================================================
//= http://herc.ws/board/topic/11575-charms/
//===== Additional Comments: =================================
//= if you set Charm_Stack: true, it still run the status_calc_pc even if
//= - you already having the charm, but just add it up
//= - you have 10 charms in stack (give only 1 time bonus), but drop 5 charms
//= it still run status_calc_pc everytime, and I don't know a better way to do this ...
//============================================================
#include "common/hercules.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "map/pc.h"
#include "map/itemdb.h"
#include "common/memmgr.h"
#include "common/nullpo.h"
#include "common/HPMDataCheck.h"
HPExport struct hplugin_info pinfo = {
"charms",
SERVER_TYPE_MAP,
"1.1",
HPM_VERSION,
};
struct charm_item_data {
bool charm;
bool charm_stack;
};
struct player_data { // this stupid player variable is needed to prevent item dup
bool recalculate;
};
void itemdb_readdb_additional_fields_pre( int *itemid, config_setting_t *it, int *n_, const char *source ) {
struct item_data *idata = itemdb->load( *itemid );
struct charm_item_data *cidata = getFromITEMDATA( idata, 0 );
config_setting_t *tt;
if ( idata->type != IT_ETC )
return;
if ( !cidata ) {
CREATE( cidata, struct charm_item_data, 1 );
addToITEMDATA( idata, cidata, 0, true );
}
if (( tt = libconfig->setting_get_member( it, "Charm" ) ))
if ( libconfig->setting_get_bool(tt) )
cidata->charm = true;
if (( tt = libconfig->setting_get_member( it, "Charm_Stack" ) ))
if ( libconfig->setting_get_bool(tt) )
cidata->charm_stack = true;
return;
}
int itemdb_isstackable_pre( int *nameid ) {
struct item_data *idata = itemdb->search( *nameid );
struct charm_item_data *cidata;
nullpo_ret(idata);
if ( idata->type != IT_ETC )
return 1;
cidata = getFromITEMDATA( idata, 0 );
if ( !cidata )
return 1;
if ( cidata->charm_stack == true ) {
hookStop();
return 1;
}
if ( cidata->charm == true ) {
hookStop();
return 0;
}
return 1;
}
int itemdb_isstackable2_pre( struct item_data *data ) {
struct charm_item_data *cidata = NULL;
nullpo_ret(data);
if ( data->type != IT_ETC )
return 1;
cidata = getFromITEMDATA( data, 0 );
if( !cidata )
return 1;
if ( cidata->charm_stack == true ) {
hookStop();
return 1;
}
if ( cidata->charm == true ) {
hookStop();
return 0;
}
return 1;
}
// TODO: Maybe should add those job/level/upper flag restriction like I did on eathena ? hmm ... but hercules omit those fields ... using default
void status_calc_pc_additional_pre( struct map_session_data *sd, enum e_status_calc_opt *opt ) {
int i = 0;
struct charm_item_data *cidata = NULL;
for ( i = 0; i < MAX_INVENTORY; ++i ) {
if ( !sd->inventory_data[i] )
continue;
if ( sd->inventory_data[i]->type != IT_ETC )
continue;
cidata = getFromITEMDATA( sd->inventory_data[i], 0 );
if ( !cidata )
continue;
if ( cidata->charm == false )
continue;
if ( sd->inventory_data[i]->script ) {
script->run( sd->inventory_data[i]->script, 0, sd->bl.id, 0 );
}
}
return;
}
int pc_additem_post( int retVal, struct map_session_data *sd, struct item *item_data ,int *amount, e_log_pick_type *log_type ) {
struct item_data *idata = itemdb->search( item_data->nameid );
struct charm_item_data *cidata = NULL;
if ( retVal != 0 )
return retVal;
if ( idata->type != IT_ETC )
return retVal;
cidata = getFromITEMDATA( idata, 0 );
if ( !cidata )
return retVal;
if ( cidata->charm == true ) {
// ShowDebug( "run recalculation" );
status_calc_pc( sd, SCO_NONE );
}
return retVal;
}
int pc_delitem_pre( struct map_session_data *sd, int *n, int *amount, int *type, short *reason, e_log_pick_type *log_type ) {
struct charm_item_data *cidata = NULL;
struct player_data *ssd = NULL;
nullpo_retr( 1, sd );
if ( sd->status.inventory[*n].nameid == 0 || *amount <= 0 || sd->status.inventory[*n].amount < *amount || sd->inventory_data[*n] == NULL ) {
hookStop();
return 1;
}
if ( sd->inventory_data[*n]->type != IT_ETC )
return 0;
cidata = getFromITEMDATA( sd->inventory_data[*n], 0 );
if ( !cidata )
return 0;
if ( cidata->charm == true ) {
ssd = getFromMSD( sd, 0 );
if ( !ssd ) {
CREATE( ssd, struct player_data, 1 );
ssd->recalculate = 1;
addToMSD( sd, ssd, 0, true );
}
else
ssd->recalculate = 1;
}
return 0;
}
// maybe I should've just overload this function ...
int pc_delitem_post( int retVal, struct map_session_data *sd, int *n, int *amount, int *type, short *reason, e_log_pick_type *log_type ) {
struct player_data *ssd = getFromMSD( sd, 0 );
if ( ssd && ssd->recalculate == 1 ) {
// ShowDebug( "run recalculation" );
status_calc_pc( sd, SCO_NONE );
ssd->recalculate = 0;
}
return retVal;
}
HPExport void plugin_init (void) {
addHookPre( "itemdb->readdb_additional_fields", itemdb_readdb_additional_fields_pre );
addHookPre( "itemdb->isstackable", itemdb_isstackable_pre );
addHookPre( "itemdb->isstackable2", itemdb_isstackable2_pre );
addHookPre( "status->calc_pc_additional", status_calc_pc_additional_pre );
addHookPost( "pc->additem", pc_additem_post );
addHookPre( "pc->delitem", pc_delitem_pre );
addHookPost( "pc->delitem", pc_delitem_post );
}