diff options
-rw-r--r-- | src/map/battle.c | 26 | ||||
-rw-r--r-- | src/map/clif.c | 8 | ||||
-rw-r--r-- | src/map/itemdb.c | 169 | ||||
-rw-r--r-- | src/map/itemdb.h | 13 | ||||
-rw-r--r-- | src/map/pc.c | 200 | ||||
-rw-r--r-- | src/map/pc.h | 9 | ||||
-rw-r--r-- | src/map/status.c | 11 | ||||
-rw-r--r-- | src/map/unit.c | 14 |
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: |