summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshennetsind <shennetsind@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-07-27 00:11:32 +0000
committershennetsind <shennetsind@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-07-27 00:11:32 +0000
commit96cc4f2e7496202c8c278399124f9f85ce3a5ae5 (patch)
tree7455cde7f3b96c014e441b58662630910f49ed36
parent679b172c58b43307a8e25ba85bae0d83f8598926 (diff)
downloadhercules-96cc4f2e7496202c8c278399124f9f85ce3a5ae5.tar.gz
hercules-96cc4f2e7496202c8c278399124f9f85ce3a5ae5.tar.bz2
hercules-96cc4f2e7496202c8c278399124f9f85ce3a5ae5.tar.xz
hercules-96cc4f2e7496202c8c278399124f9f85ce3a5ae5.zip
Fixed bugreport:309 combos may now stack properly. moved all combo processing out of item bonuses and made it's own processing scheme, which is by far more efficient cpu-wise although it requires a little more memory, instead of checking for combo items whenever a status effect is turned on/off it only checks when equipping/un-equipping a item (and on login). Special Thanks to GreenBox, Kenpachi, Skotlex and Trojal
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@16508 54d463be-8e91-2dee-dedb-b68131a5f0ec
-rw-r--r--src/map/battle.c26
-rw-r--r--src/map/clif.c8
-rw-r--r--src/map/itemdb.c169
-rw-r--r--src/map/itemdb.h13
-rw-r--r--src/map/pc.c200
-rw-r--r--src/map/pc.h9
-rw-r--r--src/map/status.c11
-rw-r--r--src/map/unit.c14
8 files changed, 330 insertions, 120 deletions
diff --git a/src/map/battle.c b/src/map/battle.c
index 5e051f6bd..34946aec6 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -3126,34 +3126,28 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
return wd;
}
- if (sd)
- {
- if (!flag.rh && flag.lh)
- { //Move lh damage to the rh
+ if (sd) {
+ if (!flag.rh && flag.lh) { //Move lh damage to the rh
wd.damage = wd.damage2;
wd.damage2 = 0;
flag.rh=1;
flag.lh=0;
- } else if(flag.rh && flag.lh)
- { //Dual-wield
- if (wd.damage)
- {
- if( skill = pc_checkskill(sd,AS_RIGHT) )
+ } else if(flag.rh && flag.lh) { //Dual-wield
+ if (wd.damage) {
+ if( (skill = pc_checkskill(sd,AS_RIGHT)) )
wd.damage = wd.damage * (50 + (skill * 10))/100;
- else if( skill = pc_checkskill(sd,KO_RIGHT) )
+ else if( (skill = pc_checkskill(sd,KO_RIGHT)) )
wd.damage = wd.damage * (70 + (skill * 10))/100;
if(wd.damage < 1) wd.damage = 1;
}
- if (wd.damage2)
- {
- if( skill = pc_checkskill(sd,AS_LEFT) )
+ if (wd.damage2) {
+ if( (skill = pc_checkskill(sd,AS_LEFT)) )
wd.damage2 = wd.damage2 * (30 + (skill * 10))/100;
- else if( skill = pc_checkskill(sd,KO_LEFT) )
+ else if( (skill = pc_checkskill(sd,KO_LEFT)) )
wd.damage2 = wd.damage2 * (50 + (skill * 10))/100;
if(wd.damage2 < 1) wd.damage2 = 1;
}
- } else if(sd->status.weapon == W_KATAR && !skill_num)
- { //Katars (offhand damage only applies to normal attacks, tested on Aegis 10.2)
+ } else if(sd->status.weapon == W_KATAR && !skill_num) { //Katars (offhand damage only applies to normal attacks, tested on Aegis 10.2)
skill = pc_checkskill(sd,TF_DOUBLE);
wd.damage2 = wd.damage * (1 + (skill * 2))/100;
diff --git a/src/map/clif.c b/src/map/clif.c
index c9a8a685c..0bbeb9361 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -9220,8 +9220,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
if (sd->sc.option&OPTION_RIDING)
clif_status_load(&sd->bl, SI_RIDING, 1);
-
- if (sd->sc.option&OPTION_WUGRIDER)
+ else if (sd->sc.option&OPTION_WUGRIDER)
clif_status_load(&sd->bl, SI_WUGRIDER, 1);
if(sd->status.manner < 0)
@@ -9241,12 +9240,11 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
if(merc_is_hom_active(sd->hd))
merc_hom_init_timers(sd->hd);
- if (night_flag && map[sd->bl.m].flag.nightenabled)
- {
+ if (night_flag && map[sd->bl.m].flag.nightenabled) {
sd->state.night = 1;
clif_status_load(&sd->bl, SI_NIGHT, 1);
}
-
+
// Notify everyone that this char logged in [Skotlex].
map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
index 69053d7e5..084109fff 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -761,7 +761,7 @@ int itemdb_combo_split_atoi (char *str, int *val) {
/**
* <combo{:combo{:combo:{..}}}>,<{ script }>
**/
-void itemdb_read_combos(DBMap* item_combo_db) {
+void itemdb_read_combos() {
uint32 lines = 0, count = 0;
char line[1024];
@@ -821,38 +821,73 @@ void itemdb_read_combos(DBMap* item_combo_db) {
} else {
int items[MAX_ITEMS_PER_COMBO];
int v = 0, retcount = 0;
- struct item_combo * ic = NULL;
- char combo_out[2048];
- int len = 0, slen = 0;
- bool exists = false;
+ struct item_data * id = NULL;
+ int idx = 0;
if((retcount = itemdb_combo_split_atoi(str[0], items)) < 2) {
ShowError("itemdb_read_combos: line %d of \"%s\" doesn't have enough items to make for a combo (min:2), skipping.\n", lines, path);
continue;
}
- if ((ic = idb_get(item_combo_db, items[0])) != NULL) {
- slen = strlen(ic->script);
- exists = true;
- } else {
- CREATE(ic, struct item_combo, 1);
+ /* validate */
+ for(v = 0; v < retcount; v++) {
+ if( !itemdb_exists(items[v]) ) {
+ ShowError("itemdb_read_combos: line %d of \"%s\" contains unknown item ID %d, skipping.\n", lines, path,items[v]);
+ break;
+ }
}
+ /* failed at some item */
+ if( v < retcount )
+ continue;
+
+ id = itemdb_exists(items[0]);
- len += sprintf(combo_out + len, "if(isequipped(");
+ idx = id->combos_count;
- /* we skip the first for its not a required check */
- for(v = 1; v < retcount; v++) {
- len += sprintf(combo_out + len, "%d,", items[v]);
+ /* first entry, create */
+ if( id->combos == NULL ) {
+ CREATE(id->combos, struct item_combo*, 1);
+ id->combos_count = 1;
+ } else {
+ RECREATE(id->combos, struct item_combo*, ++id->combos_count);
}
- len += sprintf(combo_out + len - 1, "))%s", str[1]);
-
- safestrncpy(ic->script + slen, combo_out, len);
+ CREATE(id->combos[idx],struct item_combo,1);
- if (!exists) {
- ic->nameid = items[0];
- idb_put(item_combo_db, items[0], ic);
+ id->combos[idx]->nameid = aMalloc( retcount * sizeof(unsigned short) );
+ id->combos[idx]->count = retcount;
+ id->combos[idx]->script = parse_script(str[1], path, lines, 0);
+ id->combos[idx]->id = count;
+ id->combos[idx]->isRef = false;
+ /* populate ->nameid field */
+ for( v = 0; v < retcount; v++ ) {
+ id->combos[idx]->nameid[v] = items[v];
}
+
+ /* populate the children to refer to this combo */
+ for( v = 1; v < retcount; v++ ) {
+ struct item_data * it = NULL;
+ int index;
+
+ it = itemdb_exists(items[v]);
+
+ index = it->combos_count;
+
+ if( it->combos == NULL ) {
+ CREATE(it->combos, struct item_combo*, 1);
+ it->combos_count = 1;
+ } else {
+ RECREATE(it->combos, struct item_combo*, ++it->combos_count);
+ }
+
+ CREATE(it->combos[index],struct item_combo,1);
+
+ /* we copy previously alloc'd pointers and just set it to reference */
+ memcpy(it->combos[index],id->combos[idx],sizeof(struct item_combo));
+ /* we flag this way to ensure we don't double-dealloc same data */
+ it->combos[index]->isRef = true;
+ }
+
}
count++;
@@ -915,8 +950,7 @@ void itemdb_re_split_atoi(char *str, int *atk, int *matk) {
/*==========================================
* processes one itemdb entry
*------------------------------------------*/
-static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scriptopt, char *comboScript)
-{
+static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scriptopt) {
/*
+----+--------------+---------------+------+-----------+------------+--------+--------+---------+-------+-------+------------+-------------+---------------+-----------------+--------------+-------------+------------+------+--------+--------------+----------------+
| 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
@@ -1013,42 +1047,21 @@ static bool itemdb_parse_dbrow(char** str, const char* source, int line, int scr
id->view_id = 0;
id->sex = itemdb_gendercheck(id); //Apply gender filtering.
- if (id->script)
- {
+ if (id->script) {
script_free_code(id->script);
id->script = NULL;
}
- if (id->equip_script)
- {
+ if (id->equip_script) {
script_free_code(id->equip_script);
id->equip_script = NULL;
}
- if (id->unequip_script)
- {
+ if (id->unequip_script) {
script_free_code(id->unequip_script);
id->unequip_script = NULL;
}
- if (*str[19]) {
- char *scriptCode = str[19];
-
- if ( comboScript ) {
- char *script1 = str[19];
-
- while (*script1++ != '{');
-
- scriptCode = (char *)aMalloc(strlen(script1) + strlen(comboScript) + 2); // +2 = {\0
- sprintf(scriptCode, "{%s%s", comboScript, script1);
- }
-
- id->script = parse_script(scriptCode, source, line, scriptopt);
-
- if( comboScript )
- aFree(scriptCode);
-
- } else if ( comboScript )
- id->script = parse_script(comboScript, source, line, scriptopt);
-
+ if (*str[19])
+ id->script = parse_script(str[19], source, line, scriptopt);
if (*str[20])
id->equip_script = parse_script(str[20], source, line, scriptopt);
if (*str[21])
@@ -1067,9 +1080,6 @@ static int itemdb_readdb(void)
"item_db2.txt" };
int fi;
- DBMap* item_combo_db = idb_alloc(DB_OPT_RELEASE_DATA);
-
- itemdb_read_combos(item_combo_db);
for( fi = 0; fi < ARRAYLENGTH(filename); ++fi ) {
uint32 lines = 0, count = 0;
@@ -1090,8 +1100,6 @@ static int itemdb_readdb(void)
{
char *str[32], *p;
int i;
- struct item_combo *ic = NULL;
- char *script2 = NULL;
lines++;
if(line[0] == '/' && line[1] == '/')
continue;
@@ -1165,15 +1173,8 @@ static int itemdb_readdb(void)
continue;
}
- if ((ic = idb_get(item_combo_db, atoi(str[0])))) {
- script2 = ic->script;
- }
-
- if (!itemdb_parse_dbrow(str, path, lines, 0, script2))
+ if (!itemdb_parse_dbrow(str, path, lines, 0))
continue;
-
- if( script2 != NULL )
- idb_remove(item_combo_db,atoi(str[0]));
count++;
}
@@ -1183,22 +1184,6 @@ static int itemdb_readdb(void)
ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename[fi]);
}
- if( db_size(item_combo_db) ) {
- DBIterator * iter = db_iterator(item_combo_db);
- struct item_combo * ic = NULL;
- int icount = 1;
- /* non-processed entries */
- ShowWarning("item_combo_db: There are %d unused entries in the file (combo(s) with non-available item IDs)\n",db_size(item_combo_db));
-
- for( ic = dbi_first(iter); dbi_exists(iter); ic = dbi_next(iter) ) {
- ShowWarning("item_combo_db(%d): (ID:%d) \"%s\" combo unused\n",icount++,ic->nameid,ic->script);
- }
-
- dbi_destroy(iter);
- }
-
- db_destroy(item_combo_db);
-
return 0;
}
@@ -1238,7 +1223,7 @@ static int itemdb_read_sqldb(void)
if( str[i] == NULL ) str[i] = dummy; // get rid of NULL columns
}
- if (!itemdb_parse_dbrow(str, item_db_name[fi], lines, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL))
+ if (!itemdb_parse_dbrow(str, item_db_name[fi], lines, SCRIPT_IGNORE_EXTERNAL_BRACKETS))
continue;
++count;
}
@@ -1261,7 +1246,8 @@ static void itemdb_read(void) {
itemdb_read_sqldb();
else
itemdb_readdb();
-
+
+ itemdb_read_combos();
itemdb_read_itemgroup();
sv_readdb(db_path, "item_avail.txt", ',', 2, 2, -1, &itemdb_read_itemavail);
sv_readdb(db_path, DBPATH"item_noequip.txt", ',', 2, 2, -1, &itemdb_read_noequip);
@@ -1287,6 +1273,17 @@ static void destroy_item_data(struct item_data* self, int free_self)
script_free_code(self->equip_script);
if( self->unequip_script )
script_free_code(self->unequip_script);
+ if( self->combos_count ) {
+ int i;
+ for( i = 0; i < self->combos_count; i++ ) {
+ if( !self->combos[i]->isRef ) {
+ aFree(self->combos[i]->nameid);
+ script_free_code(self->combos[i]->script);
+ }
+ aFree(self->combos[i]);
+ }
+ aFree(self->combos);
+ }
#if defined(DEBUG)
// trash item
memset(self, 0xDD, sizeof(struct item_data));
@@ -1358,10 +1355,20 @@ void itemdb_reload(void)
// readjust itemdb pointer cache for each player
iter = mapit_geteachpc();
- for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) )
- {
+ for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) ) {
memset(sd->item_delay, 0, sizeof(sd->item_delay)); // reset item delays
pc_setinventorydata(sd);
+ /* clear combo bonuses */
+ if( sd->combos.count ) {
+ aFree(sd->combos.bonus);
+ aFree(sd->combos.id);
+ sd->combos.bonus = NULL;
+ sd->combos.id = NULL;
+ sd->combos.count = 0;
+ if( pc_load_combo(sd) > 0 )
+ status_calc_pc(sd,0);
+ }
+
}
mapit_free(iter);
}
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index 75c82088b..d39966285 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -129,8 +129,7 @@ struct item_data {
unsigned autoequip: 1;
unsigned buyingstore : 1;
} flag;
- struct
- {// item stacking limitation
+ struct {// item stacking limitation
unsigned short amount;
unsigned int inventory:1;
unsigned int cart:1;
@@ -138,6 +137,9 @@ struct item_data {
unsigned int guildstorage:1;
} stack;
short gm_lv_trade_override; //GM-level to override trade_restriction
+ /* bugreport:309 */
+ struct item_combo **combos;
+ unsigned char combos_count;
};
struct item_group {
@@ -146,8 +148,11 @@ struct item_group {
};
struct item_combo {
- char script[2048]; /* combo script */
- short nameid;/* id of the first */
+ struct script_code *script;
+ unsigned short *nameid;/* nameid array */
+ unsigned char count;
+ unsigned short id;/* id of this combo */
+ bool isRef;/* whether this struct is a reference or the master */
};
struct item_data* itemdb_searchname(const char *name);
diff --git a/src/map/pc.c b/src/map/pc.c
index 1a7efc260..c697fd046 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -1220,6 +1220,8 @@ int pc_reg_received(struct map_session_data *sd)
map_delnickdb(sd->status.char_id, sd->status.name);
if (!chrif_auth_finished(sd))
ShowError("pc_reg_received: Failed to properly remove player %d:%d from logging db!\n", sd->status.account_id, sd->status.char_id);
+
+ pc_load_combo(sd);
status_calc_pc(sd,1);
chrif_scdata_request(sd->status.account_id, sd->status.char_id);
@@ -7859,12 +7861,158 @@ int pc_cleareventtimer(struct map_session_data *sd)
}
return 0;
}
+/* called when a item with combo is worn */
+int pc_checkcombo(struct map_session_data *sd, struct item_data *data ) {
+ int i, j, k, z;
+ int index, idx, success = 0;
-//
-// ? 備物
-//
+ for( i = 0; i < data->combos_count; i++ ) {
+
+ /* ensure this isn't a duplicate combo */
+ if( sd->combos.bonus != NULL ) {
+ int x;
+ ARR_FIND( 0, sd->combos.count, x, sd->combos.id[x] == data->combos[i]->id );
+
+ /* found a match, skip this combo */
+ if( x < sd->combos.count )
+ continue;
+ }
+
+ for( j = 0; j < data->combos[i]->count; j++ ) {
+ int id = data->combos[i]->nameid[j];
+ bool found = false;
+
+ for( k = 0; k < EQI_MAX; k++ ) {
+ index = sd->equip_index[k];
+ if( index < 0 ) continue;
+ if( k == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index ) continue;
+ if( k == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index ) continue;
+ if( k == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index) ) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if ( itemdb_type(id) != IT_CARD ) {
+ if ( sd->inventory_data[index]->nameid != id )
+ continue;
+
+ found = true;
+ break;
+ } else { //Cards
+ if ( sd->inventory_data[index]->slot == 0 || itemdb_isspecial(sd->status.inventory[index].card[0]) )
+ continue;
+
+ for (z = 0; z < sd->inventory_data[index]->slot; z++) {
+
+ if (sd->status.inventory[index].card[z] != id)
+ continue;
+
+ // We have found a match
+ found = true;
+ break;
+ }
+ }
+
+ }
+
+ if( !found )
+ break;/* we haven't found all the ids for this combo, so we can return */
+ }
+
+ /* means we broke out of the count loop w/o finding all ids, we can move to the next combo */
+ if( j < data->combos[i]->count )
+ continue;
+
+ /* we got here, means all items in the combo are matching */
+
+ idx = sd->combos.count;
+
+ if( sd->combos.bonus == NULL ) {
+ CREATE(sd->combos.bonus, struct script_code *, 1);
+ CREATE(sd->combos.id, unsigned short, 1);
+ sd->combos.count = 1;
+ } else {
+ RECREATE(sd->combos.bonus, struct script_code *, ++sd->combos.count);
+ RECREATE(sd->combos.id, unsigned short, sd->combos.count);
+ }
+
+ /* we simply copy the pointer */
+ sd->combos.bonus[idx] = data->combos[i]->script;
+ /* save this combo's id */
+ sd->combos.id[idx] = data->combos[i]->id;
+
+ success++;
+ }
+ return success;
+}
+
+/* called when a item with combo is removed */
+void pc_removecombo(struct map_session_data *sd, struct item_data *data ) {
+ int i;
+
+ if( sd->combos.bonus == NULL )
+ return;/* nothing to do here, player has no combos */
+ for( i = 0; i < data->combos_count; i++ ) {
+ /* check if this combo exists in this user */
+ int x = 0, cursor = 0, j;
+ ARR_FIND( 0, sd->combos.count, x, sd->combos.id[x] == data->combos[i]->id );
+ /* no match, skip this combo */
+ if( !(x < sd->combos.count) )
+ continue;
+
+ sd->combos.bonus[x] = NULL;
+ sd->combos.id[x] = 0;
+
+ for( j = 0, cursor = 0; j < sd->combos.count; j++ ) {
+ if( sd->combos.bonus[j] == NULL )
+ continue;
+
+ if( cursor != j ) {
+ sd->combos.bonus[cursor] = sd->combos.bonus[j];
+ sd->combos.id[cursor] = sd->combos.id[j];
+ }
+
+ cursor++;
+ }
+
+ /* it's empty, we can clear all the memory */
+ if( (sd->combos.count = cursor) == 0 ) {
+ aFree(sd->combos.bonus);
+ aFree(sd->combos.id);
+ sd->combos.bonus = NULL;
+ sd->combos.id = NULL;
+ return; /* we also can return at this point for we have no more combos to check */
+ }
+
+ }
+
+}
+int pc_load_combo(struct map_session_data *sd) {
+ int i, ret = 0;
+ for( i = 0; i < EQI_MAX; i++ ) {
+ struct item_data *id = NULL;
+ int idx = sd->equip_index[i];
+ if( sd->equip_index[i] < 0 || !(id = sd->inventory_data[idx] ) )
+ continue;
+ if( id->combos_count )
+ ret += pc_checkcombo(sd,id);
+ if(!itemdb_isspecial(sd->status.inventory[idx].card[0])) {
+ struct item_data *data;
+ int j;
+ for( j = 0; j < id->slot; j++ ) {
+ if (!sd->status.inventory[idx].card[j])
+ continue;
+ if ( ( data = itemdb_exists(sd->status.inventory[idx].card[j]) ) != NULL ) {
+ if( data->combos_count )
+ ret += pc_checkcombo(sd,data);
+ }
+ }
+ }
+ }
+ return ret;
+}
/*==========================================
- * アイテムを?備する
+ * Attempt to equip item in inventory index 'n' in the EQP_ 'req_pos'
*------------------------------------------*/
int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
{
@@ -8019,15 +8167,32 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
}
pc_checkallowskill(sd); //Check if status changes should be halted.
-
-
+
+ /* check for combos (MUST be before status_calc_pc) */
+ if ( id ) {
+ struct item_data *data;
+ if( id->combos_count )
+ pc_checkcombo(sd,id);
+ if(itemdb_isspecial(sd->status.inventory[n].card[0]))
+ ; //No cards
+ else {
+ for( i = 0; i < id->slot; i++ ) {
+ if (!sd->status.inventory[n].card[i])
+ continue;
+ if ( ( data = itemdb_exists(sd->status.inventory[n].card[i]) ) != NULL ) {
+ if( data->combos_count )
+ pc_checkcombo(sd,data);
+ }
+ }
+ }
+ }
+
status_calc_pc(sd,0);
if (flag) //Update skill data
clif_skillinfoblock(sd);
//OnEquip script [Skotlex]
if (id) {
- int i;
struct item_data *data;
if (id->equip_script)
run_script(id->equip_script,0,sd->bl.id,fake_nd->bl.id);
@@ -8150,6 +8315,27 @@ int pc_unequipitem(struct map_session_data *sd,int n,int flag)
sd->status.inventory[n].equip=0;
if(flag&1) {
+
+ /* check for combos (MUST be before status_calc_pc) */
+ if ( sd->inventory_data[n] ) {
+ struct item_data *data;
+
+ if( sd->inventory_data[n]->combos_count )
+ pc_removecombo(sd,sd->inventory_data[n]);
+ if(itemdb_isspecial(sd->status.inventory[n].card[0]))
+ ; //No cards
+ else {
+ for( i = 0; i < sd->inventory_data[n]->slot; i++ ) {
+ if (!sd->status.inventory[n].card[i])
+ continue;
+ if ( ( data = itemdb_exists(sd->status.inventory[n].card[i]) ) != NULL ) {
+ if( data->combos_count )
+ pc_removecombo(sd,data);
+ }
+ }
+ }
+ }
+
pc_checkallowskill(sd);
status_calc_pc(sd,0);
}
diff --git a/src/map/pc.h b/src/map/pc.h
index 6f2275fa7..269bc65dc 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -454,6 +454,12 @@ struct map_session_data {
unsigned int npc_idle_tick;
#endif
+ struct {
+ struct script_code **bonus;/* the script */
+ unsigned short *id;/* array of combo ids */
+ unsigned char count;
+ } combos;
+
/**
* Guarantees your friend request is legit (for bugreport:4629)
**/
@@ -909,4 +915,7 @@ void pc_overheat(struct map_session_data *sd, int val);
int pc_banding(struct map_session_data *sd, short skill_lv);
void pc_itemcd_do(struct map_session_data *sd, bool load);
+
+int pc_load_combo(struct map_session_data *sd);
+
#endif /* _PC_H_ */
diff --git a/src/map/status.c b/src/map/status.c
index 94ffc403e..5b401d87f 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -2507,7 +2507,16 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
return 1;
}
}
-
+
+ /* we've got combos to process */
+ if( sd->combos.count ) {
+ for( i = 0; i < sd->combos.count; i++ ) {
+ run_script(sd->combos.bonus[i],0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered this.
+ return 1;
+ }
+ }
+
//Store equipment script bonuses
memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip));
memset(sd->param_bonus, 0, sizeof(sd->param_bonus));
diff --git a/src/map/unit.c b/src/map/unit.c
index 63b2805fb..a65aafb97 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -2254,14 +2254,12 @@ int unit_free(struct block_list *bl, clr_type clrtype)
pc_inventory_rental_clear(sd);
pc_delspiritball(sd,sd->spiritball,1);
- if( sd->reg )
- { //Double logout already freed pointer fix... [Skotlex]
+ if( sd->reg ) { //Double logout already freed pointer fix... [Skotlex]
aFree(sd->reg);
sd->reg = NULL;
sd->reg_num = 0;
}
- if( sd->regstr )
- {
+ if( sd->regstr ) {
int i;
for( i = 0; i < sd->regstr_num; ++i )
if( sd->regstr[i].data )
@@ -2270,12 +2268,16 @@ int unit_free(struct block_list *bl, clr_type clrtype)
sd->regstr = NULL;
sd->regstr_num = 0;
}
- if( sd->st && sd->st->state != RUN )
- {// free attached scripts that are waiting
+ if( sd->st && sd->st->state != RUN ) {// free attached scripts that are waiting
script_free_state(sd->st);
sd->st = NULL;
sd->npc_id = 0;
}
+ if( sd->combos.count ) {
+ aFree(sd->combos.bonus);
+ aFree(sd->combos.id);
+ sd->combos.count = 0;
+ }
break;
}
case BL_PET: