summaryrefslogtreecommitdiff
path: root/src/map/pc.c
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 /src/map/pc.c
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
Diffstat (limited to 'src/map/pc.c')
-rw-r--r--src/map/pc.c200
1 files changed, 193 insertions, 7 deletions
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);
}