diff --git a/sql-files/main.sql b/sql-files/main.sql index 133db301bf..34f872b147 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -1,3 +1,17 @@ + +-- +-- Table structure for table `skillcooldown` +-- + +CREATE TABLE IF NOT EXISTS `skillcooldown` ( + `account_id` int(11) unsigned NOT NULL, + `char_id` int(11) unsigned NOT NULL, + `skill` smallint(11) unsigned NOT NULL DEFAULT '0', + `tick` int(11) NOT NULL, + KEY `account_id` (`account_id`), + KEY `char_id` (`char_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + -- -- Table structure for table `auction` -- diff --git a/sql-files/upgrades/upgrade_svn17541.sql b/sql-files/upgrades/upgrade_svn17541.sql new file mode 100644 index 0000000000..4baad04f14 --- /dev/null +++ b/sql-files/upgrades/upgrade_svn17541.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `skillcooldown` ( + `account_id` int(11) unsigned NOT NULL, + `char_id` int(11) unsigned NOT NULL, + `skill` smallint(11) unsigned NOT NULL DEFAULT '0', + `tick` int(11) NOT NULL, + KEY `account_id` (`account_id`), + KEY `char_id` (`char_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/src/char/char.c b/src/char/char.c index 3a3ec39eef..53197194dc 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -39,6 +39,7 @@ static char* msg_table[CHAR_MAX_MSG]; // Login Server messages_conf char char_db[256] = "char"; char scdata_db[256] = "sc_data"; +char skillcooldown_db[256] = "skillcooldown"; char cart_db[256] = "cart_inventory"; char inventory_db[256] = "inventory"; char charlog_db[256] = "charlog"; @@ -2923,6 +2924,51 @@ int parse_frommap(int fd) } break; + case 0x2b0a: //Request skillcooldown data + if (RFIFOREST(fd) < 10) + return 0; + { + int aid, cid; + aid = RFIFOL(fd,2); + cid = RFIFOL(fd,6); + if( SQL_ERROR == Sql_Query(sql_handle, "SELECT skill, tick FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", + skillcooldown_db, aid, cid) ) + { + Sql_ShowDebug(sql_handle); + break; + } + if( Sql_NumRows(sql_handle) > 0 ) + { + int count; + char* data; + struct skill_cooldown_data scd; + + WFIFOHEAD(fd,14 + MAX_SKILLCOOLDOWN * sizeof(struct skill_cooldown_data)); + WFIFOW(fd,0) = 0x2b0b; + WFIFOL(fd,4) = aid; + WFIFOL(fd,8) = cid; + for( count = 0; count < MAX_SKILLCOOLDOWN && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count ) + { + Sql_GetData(sql_handle, 0, &data, NULL); scd.skill_id = atoi(data); + Sql_GetData(sql_handle, 1, &data, NULL); scd.tick = atoi(data); + memcpy(WFIFOP(fd,14+count*sizeof(struct skill_cooldown_data)), &scd, sizeof(struct skill_cooldown_data)); + } + if( count >= MAX_SKILLCOOLDOWN ) + ShowWarning("Too many skillcooldowns for %d:%d, some of them were not loaded.\n", aid, cid); + if( count > 0 ) + { + WFIFOW(fd,2) = 14 + count * sizeof(struct skill_cooldown_data); + WFIFOW(fd,12) = count; + WFIFOSET(fd,WFIFOW(fd,2)); + //Clear the data once loaded. + if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", skillcooldown_db, aid, cid) ) + Sql_ShowDebug(sql_handle); + } + } + Sql_FreeResult(sql_handle); + RFIFOSKIP(fd, 10); + } + break; case 0x2afe: //set MAP user count if (RFIFOREST(fd) < 4) return 0; @@ -3398,6 +3444,37 @@ int parse_frommap(int fd) } break; + case 0x2b15: //Request to save skill cooldown data + if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) ) + return 0; + { + int count, aid, cid; + aid = RFIFOL(fd,4); + cid = RFIFOL(fd,8); + count = RFIFOW(fd,12); + if( count > 0 ) + { + struct skill_cooldown_data data; + StringBuf buf; + int i; + + StringBuf_Init(&buf); + StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `skill`, `tick`) VALUES ", skillcooldown_db); + for( i = 0; i < count; ++i ) + { + memcpy(&data,RFIFOP(fd,14+i*sizeof(struct skill_cooldown_data)),sizeof(struct skill_cooldown_data)); + if( i > 0 ) + StringBuf_AppendStr(&buf, ", "); + StringBuf_Printf(&buf, "('%d','%d','%d','%d')", aid, cid, data.skill_id, data.tick); + } + if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) + Sql_ShowDebug(sql_handle); + StringBuf_Destroy(&buf); + } + RFIFOSKIP(fd, RFIFOW(fd, 2)); + } + break; + case 0x2b23: // map-server alive packet WFIFOHEAD(fd,2); WFIFOW(fd,0) = 0x2b24; diff --git a/src/common/mmo.h b/src/common/mmo.h index 57730b3424..a171d2b934 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -128,6 +128,7 @@ #define MAX_FRIENDS 40 #define MAX_MEMOPOINTS 3 +#define MAX_SKILLCOOLDOWN 20 //Size of the fame list arrays. #define MAX_FAME_LIST 10 @@ -248,6 +249,11 @@ struct status_change_data { long val1, val2, val3, val4, tick; //Remaining duration. }; +struct skill_cooldown_data { + unsigned short skill_id; + long tick; +}; + struct storage_data { int storage_amount; struct item items[MAX_STORAGE]; diff --git a/src/map/battle.c b/src/map/battle.c index c1ab2c58e7..99a50ce652 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -2021,9 +2021,10 @@ static int is_attack_piercing(struct Damage wd, struct block_list *src, struct b sd->left_weapon.def_ratio_atk_race & (1<race) || sd->left_weapon.def_ratio_atk_race & (1<<(is_boss(target)?RC_BOSS:RC_NONBOSS))) ) { //Pass effect onto right hand if configured so. [Skotlex] - if (battle_config.left_cardfix_to_right && is_attack_right_handed(src, skill_id)) + if (battle_config.left_cardfix_to_right && is_attack_right_handed(src, skill_id)){ if (weapon_position == EQI_HAND_R) return 1; + } else if (weapon_position == EQI_HAND_L) return 1; } diff --git a/src/map/chrif.c b/src/map/chrif.c index 6ad62e2b92..9b685c382f 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -41,8 +41,8 @@ static DBMap* auth_db; // int id -> struct auth_node* static const int packet_len_table[0x3d] = { // U - used, F - free 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff 6,-1,18, 7,-1,39,30, 10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07 - 6,30, 0, 0,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, F->2b0a, F->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f - 11,10,10, 0,11, 0,266,10, // 2b10-2b17: U->2b10, U->2b11, U->2b12, F->2b13, U->2b14, F->2b15, U->2b16, U->2b17 + 6,30, 10, -1,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f + 11,10,10, 0,11, -1,266,10, // 2b10-2b17: U->2b10, U->2b11, U->2b12, F->2b13, U->2b14, U->2b15, U->2b16, U->2b17 2,10, 2,-1,-1,-1, 2, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f -1,10, 8, 2, 2,14,19,19, // 2b20-2b27: U->2b20, U->2b21, U->2b22, U->2b23, U->2b24, U->2b25, U->2b26, U->2b27 }; @@ -275,8 +275,10 @@ int chrif_save(struct map_session_data *sd, int flag) { if (flag && sd->state.active) { //Store player data which is quitting //FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex] - if ( chrif_isconnected() ) - chrif_save_scdata(sd); + if (chrif_isconnected()) { + chrif_save_scdata(sd); + chrif_skillcooldown_save(sd); + } if ( !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) ) ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id); } @@ -575,8 +577,20 @@ int chrif_scdata_request(int account_id, int char_id) { WFIFOL(char_fd,6) = char_id; WFIFOSET(char_fd,10); #endif + return 0; +} - return 0; +/*========================================== + * Request skillcooldown from charserver + *------------------------------------------*/ +int chrif_skillcooldown_request(int account_id, int char_id) { + chrif_check(-1); + WFIFOHEAD(char_fd, 10); + WFIFOW(char_fd, 0) = 0x2b0a; + WFIFOL(char_fd, 2) = account_id; + WFIFOL(char_fd, 6) = char_id; + WFIFOSET(char_fd, 10); + return 0; } /*========================================== @@ -1214,6 +1228,44 @@ int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size WFIFOSET(char_fd,WFIFOW(char_fd,2)); #endif + return 0; +} + +int chrif_skillcooldown_save(struct map_session_data *sd) { + int i, count = 0; + struct skill_cooldown_data data; + unsigned int tick; + const struct TimerData *timer; + + chrif_check(-1); + tick = gettick(); + + WFIFOHEAD(char_fd, 14 + MAX_SKILLCOOLDOWN * sizeof (struct skill_cooldown_data)); + WFIFOW(char_fd, 0) = 0x2b15; + WFIFOL(char_fd, 4) = sd->status.account_id; + WFIFOL(char_fd, 8) = sd->status.char_id; + for (i = 0; i < MAX_SKILLCOOLDOWN; i++) { + if (!sd->scd[i]) + continue; + + if (!battle_config.guild_skill_relog_delay && (sd->scd[i]->skill_id >= GD_BATTLEORDER && sd->scd[i]->skill_id <= GD_EMERGENCYCALL)) + continue; + + timer = get_timer(sd->scd[i]->timer); + if (timer == NULL || timer->func != skill_blockpc_end || DIFF_TICK(timer->tick, tick) < 0) + continue; + + data.tick = DIFF_TICK(timer->tick, tick); + data.skill_id = sd->scd[i]->skill_id; + memcpy(WFIFOP(char_fd, 14 + count * sizeof (struct skill_cooldown_data)), &data, sizeof (struct skill_cooldown_data)); + count++; + } + if (count == 0) + return 0; + + WFIFOW(char_fd, 12) = count; + WFIFOW(char_fd, 2) = 14 + count * sizeof (struct skill_cooldown_data); + WFIFOSET(char_fd, WFIFOW(char_fd, 2)); return 0; } @@ -1248,8 +1300,35 @@ int chrif_load_scdata(int fd) { status_change_start(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 1|2|4|8); } #endif + return 0; +} - return 0; +//Retrieve and load skillcooldown for a player + +int chrif_skillcooldown_load(int fd) { + struct map_session_data *sd; + struct skill_cooldown_data *data; + int aid, cid, i, count; + + aid = RFIFOL(fd, 4); + cid = RFIFOL(fd, 8); + + + sd = map_id2sd(aid); + if (!sd) { + ShowError("chrif_skillcooldown_load: Player of AID %d not found!\n", aid); + return -1; + } + if (sd->status.char_id != cid) { + ShowError("chrif_skillcooldown_load: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid); + return -1; + } + count = RFIFOW(fd, 12); //sc_count + for (i = 0; i < count; i++) { + data = (struct skill_cooldown_data*) RFIFOP(fd, 14 + i * sizeof (struct skill_cooldown_data)); + skill_blockpc_start(sd, data->skill_id, data->tick); + } + return 0; } /*========================================== @@ -1443,6 +1522,7 @@ int chrif_parse(int fd) { case 0x2b04: chrif_recvmap(fd); break; case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break; case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break; + case 0x2b0b: chrif_skillcooldown_load(fd); break; case 0x2b0d: chrif_changedsex(fd); break; case 0x2b0f: chrif_char_ask_name_answer(RFIFOL(fd,2), (char*)RFIFOP(fd,6), RFIFOW(fd,30), RFIFOW(fd,32)); break; case 0x2b12: chrif_divorceack(RFIFOL(fd,2), RFIFOL(fd,6)); break; diff --git a/src/map/chrif.h b/src/map/chrif.h index 9fa1909e83..e16e56a52a 100644 --- a/src/map/chrif.h +++ b/src/map/chrif.h @@ -38,6 +38,9 @@ bool chrif_auth_finished(struct map_session_data* sd); void chrif_authreq(struct map_session_data* sd); void chrif_authok(int fd); int chrif_scdata_request(int account_id, int char_id); +int chrif_skillcooldown_request(int account_id, int char_id); +int chrif_skillcooldown_save(struct map_session_data *sd); +int chrif_skillcooldown_load(int fd); int chrif_save(struct map_session_data* sd, int flag); int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip); int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port); diff --git a/src/map/guild.c b/src/map/guild.c index a0a86b3e82..abd2aa0268 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -856,15 +856,16 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c if( i == -1 ) return 0; // not a member (inconsistency!) - online_member_sd = guild_getavailablesd(g); - if(online_member_sd == NULL) - return 0; // noone online to inform - #ifdef BOUND_ITEMS //Guild bound item check guild_retrieveitembound(char_id,account_id,guild_id); #endif + online_member_sd = guild_getavailablesd(g); + if(online_member_sd == NULL) + return 0; // noone online to inform + + if(!flag) clif_guild_leave(online_member_sd, name, mes); else @@ -912,15 +913,15 @@ void guild_retrieveitembound(int char_id,int aid,int guild_id) } else { //Character is offline, ask char server to do the job struct guild_storage* stor = guild2storage2(guild_id); + struct guild *g = guild_search(guild_id); + int i; + nullpo_retv(g); if(stor && stor->storage_status == 1) { //Someone is in guild storage, close them - struct s_mapiterator* iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) { - if(sd->status.guild_id == guild_id && sd->state.storage_flag == 2) { + for(i=0; imax_member; i++){ + TBL_PC *pl_sd = g->member[i].sd; + if(pl_sd && pl_sd->state.storage_flag == 2) storage_guild_storageclose(sd); - break; - } } - mapit_free(iter); } intif_itembound_req(char_id,aid,guild_id); } @@ -1398,7 +1399,7 @@ void guild_block_skill(struct map_session_data *sd, int time) uint16 skill_id[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL }; int i; for (i = 0; i < 4; i++) - skill_blockpc_start_(sd, skill_id[i], time , true); + skill_blockpc_start(sd, skill_id[i], time); } /*==================================================== diff --git a/src/map/intif.c b/src/map/intif.c index dca1aee115..1161524401 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -2182,16 +2182,13 @@ void intif_itembound_req(int char_id,int aid,int guild_id) { WFIFOL(inter_fd,6) = aid; WFIFOW(inter_fd,10) = guild_id; WFIFOSET(inter_fd,12); - if(gstor) - gstor->lock = 1; //Lock for retrieval process + if(gstor) gstor->lock = 1; //Lock for retrieval process } //3856 void intif_parse_itembound_ack(int fd) { - struct guild_storage *gstor; - int guild_id = RFIFOW(char_fd,6); - - gstor = guild2storage2(guild_id); + int guild_id = RFIFOW(fd,6); + struct guild_storage *gstor = guild2storage2(guild_id); if(gstor) gstor->lock = 0; //Unlock now that operation is completed } #endif diff --git a/src/map/npc.c b/src/map/npc.c index d1858d90ab..29996f7b8d 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -963,7 +963,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y) } switch(map[m].npc[i]->subtype) { case WARP: - if (pc_ishiding(sd) || sd->sc.count && sd->sc.data[SC_CAMOUFLAGE] || pc_isdead(sd)) + if (pc_ishiding(sd) || (sd->sc.count && sd->sc.data[SC_CAMOUFLAGE]) || pc_isdead(sd)) break; // hidden or dead chars cannot use warps pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,CLR_OUTSIGHT); break; @@ -2725,7 +2725,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m) { if(!imap) imap = map_mapname2mapid(map[dm].name); - + if( imap == -1 ) { ShowError("npc_duplicate4instance: warp (%s) leading to instanced map (%s), but instance map is not attached to current instance.\n", map[dm].name, snd->exname); return 1; diff --git a/src/map/pc.c b/src/map/pc.c index 64c90ee54d..27e2c18c05 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1147,11 +1147,6 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim clif_changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y); } - /** - * Check if player have any cool downs on - **/ - skill_cooldown_load(sd); - /** * Check if player have any item cooldowns on **/ @@ -1296,7 +1291,7 @@ int pc_reg_received(struct map_session_data *sd) status_calc_pc(sd,1); chrif_scdata_request(sd->status.account_id, sd->status.char_id); - + chrif_skillcooldown_request(sd->status.account_id, sd->status.char_id); intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox intif_request_questlog(sd); diff --git a/src/map/pc.h b/src/map/pc.h index a6753b4929..2e0df3ef20 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -115,6 +115,11 @@ struct s_autobonus { unsigned short pos; }; +struct skill_cooldown_entry { + unsigned short skill_id; + int timer; +}; + enum npc_timeout_type { NPCT_INPUT = 0, NPCT_MENU = 1, @@ -244,7 +249,7 @@ struct map_session_data { uint16 skill_id_old,skill_lv_old; uint16 skill_id_dance,skill_lv_dance; short cook_mastery; // range: [0,1999] [Inkfish] - unsigned char blockskill[MAX_SKILL]; + struct skill_cooldown_entry * scd[MAX_SKILLCOOLDOWN]; // Skill Cooldown int cloneskill_id, reproduceskill_id; int menuskill_id, menuskill_val, menuskill_val2; @@ -321,6 +326,10 @@ struct map_session_data { short flag, rate; unsigned char ele; } subele2[MAX_PC_BONUS]; + struct { + int id; + int val; + } cooldown[MAX_PC_BONUS]; struct { short value; int rate, tick; diff --git a/src/map/skill.c b/src/map/skill.c index 600bcdc036..2e4cf1213e 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -63,20 +63,6 @@ static DBMap* bowling_db = NULL; // int mob_id -> struct mob_data* DBMap* skillunit_db = NULL; // int id -> struct skill_unit* -/** - * Skill Cool Down Delay Saving - * Struct skill_cd is not a member of struct map_session_data - * to keep cooldowns in memory between player log-ins. - * All cooldowns are reset when server is restarted. - **/ -DBMap* skillcd_db = NULL; // char_id -> struct skill_cd -struct skill_cd { - int duration[MAX_SKILL_TREE];//milliseconds - short skidx[MAX_SKILL_TREE];//the skill index entries belong to - short nameid[MAX_SKILL_TREE];//skill id - unsigned char cursor; -}; - /** * Skill Unit Persistency during endack routes (mostly for songs see bugreport:4574) **/ @@ -257,6 +243,27 @@ int skill_tree_get_max(uint16 skill_id, int b_class) return skill_get_max(skill_id); } +int skill_get_cooldown_(struct map_session_data *sd, int id, int lv) { + int i, cooldown; + int idx = skill_get_index (id); + if (!idx) return 0; + + cooldown = 0; + if (skill_db[idx].cooldown[lv - 1]) + cooldown = skill_db[idx].cooldown[lv - 1]; + + for (i = 0; i < ARRAYLENGTH(sd->cooldown) && sd->cooldown[i].id; i++) { + if (sd->cooldown[i].id == id) { + cooldown += sd->cooldown[i].val; + if (cooldown < 0) + cooldown = 0; + break; + } + } + + return cooldown; +} + int skill_frostjoke_scream(struct block_list *bl,va_list ap); int skill_attack_area(struct block_list *bl,va_list ap); struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex] @@ -446,7 +453,7 @@ static short skill_isCopyable (struct map_session_data *sd, uint16 skill_id, str // Only copy skill that player doesn't have or the skill is old clone if (sd->status.skill[skill_id].id != 0 && sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED) return 0; - + // Never copy NPC/Wedding Skills if (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL)) return 0; @@ -510,7 +517,7 @@ bool skill_isNotOk(uint16 skill_id, struct map_session_data *sd) return true; } - if (sd->blockskill[idx] > 0) { + if (skill_blockpc_get(sd, skill_id) != -1){ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0); return true; } @@ -711,7 +718,7 @@ bool skill_isNotOk_npcRange(struct block_list *src, struct block_list *target, u if (src->type == BL_PC && pc_has_permission(BL_CAST(BL_PC,src),PC_PERM_SKILL_UNCONDITIONAL)) return false; - + inf = skill_get_inf(skill_id); //if self skill if (inf&INF_SELF_SKILL) { @@ -9650,15 +9657,8 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data) if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) ) ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] if (sd) { //Cooldown application - int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv); - for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses. - if (sd->skillcooldown[i].id == ud->skill_id){ - cooldown += sd->skillcooldown[i].val; - break; - } - } - if(cooldown) - skill_blockpc_start(sd, ud->skill_id, cooldown); + int cooldown = skill_get_cooldown_(sd,ud->skill_id, ud->skill_lv); // Increases/Decreases cooldown of a skill by item/card bonuses. + if(cooldown) skill_blockpc_start(sd, ud->skill_id, cooldown); } if( battle_config.display_status_timers && sd ) clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); @@ -17337,86 +17337,81 @@ static int skill_destroy_trap( struct block_list *bl, va_list ap ) { return 0; } /*========================================== - * - *------------------------------------------*/ +* +*------------------------------------------*/ +int skill_blockpc_get(struct map_session_data *sd, int skillid) { + int i; + nullpo_retr(-1, sd); + + ARR_FIND(0, MAX_SKILLCOOLDOWN, i, sd->scd[i] && sd->scd[i]->skill_id == skillid); + return (i >= MAX_SKILLCOOLDOWN) ? -1 : i; +} + int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data) { struct map_session_data *sd = map_id2sd(id); - struct skill_cd * cd = NULL; + int i = (int) data; - if (data <= 0 || data >= MAX_SKILL) + if (!sd || data < 0 || data >= MAX_SKILLCOOLDOWN) return 0; - if (!sd) return 0; - if (sd->blockskill[data] != (0x1|(tid&0xFE))) return 0; - if( ( cd = idb_get(skillcd_db,sd->status.char_id) ) ) { - int i,cursor; - ARR_FIND( 0, cd->cursor+1, cursor, cd->skidx[cursor] == data ); - cd->duration[cursor] = 0; - cd->skidx[cursor] = 0; - cd->nameid[cursor] = 0; - // compact the cool down list - for( i = 0, cursor = 0; i < cd->cursor; i++ ) { - if( cd->duration[i] == 0 ) - continue; - if( cursor != i ) { - cd->duration[cursor] = cd->duration[i]; - cd->skidx[cursor] = cd->skidx[i]; - cd->nameid[cursor] = cd->nameid[i]; - } - cursor++; - } - if( cursor == 0 ) - idb_remove(skillcd_db,sd->status.char_id); - else - cd->cursor = cursor; + if (!sd->scd[i] || sd->scd[i]->timer != tid) { + ShowWarning("skill_blockpc_end: Invalid Timer or not Skill Cooldown.\n"); + return 0; } - sd->blockskill[data] = 0; - return 1; + aFree(sd->scd[i]); + sd->scd[i] = NULL; + return 1; } /** - * flags a singular skill as being blocked from persistent usage. - * @param sd the player the skill delay affects - * @param skill_id the skill which should be delayed - * @param tick the length of time the delay should last - * @param load whether this assignment is being loaded upon player login - * @return 0 if successful, -1 otherwise - */ -int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick, bool load) -{ - int oskill_id = skill_id; - struct skill_cd* cd = NULL; - uint16 idx = skill_get_index(skill_id); - - nullpo_retr (-1, sd); - - if (idx == 0) +* flags a singular skill as being blocked from persistent usage. +* @param sd the player the skill delay affects +* @param skill_id the skill which should be delayed +* @param tick the length of time the delay should last +* @param load whether this assignment is being loaded upon player login +* @return 0 if successful, -1 otherwise +*/ +int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick) { + int i; + nullpo_retr(-1, sd); + if (skillid == 0 || tick < 1) return -1; - if (tick < 1) { - sd->blockskill[idx] = 0; - return -1; + ARR_FIND(0, MAX_SKILLCOOLDOWN, i, sd->scd[i] && sd->scd[i]->skill_id == skillid); + if (i < MAX_SKILLCOOLDOWN) { // Skill already with cooldown + delete_timer(sd->scd[i]->timer, skill_blockpc_end); + aFree(sd->scd[i]); + sd->scd[i] = NULL; } - if( battle_config.display_status_timers ) - clif_skill_cooldown(sd, idx, tick); + ARR_FIND(0, MAX_SKILLCOOLDOWN, i, !sd->scd[i]); + if (i < MAX_SKILLCOOLDOWN) { // Free Slot found + CREATE(sd->scd[i], struct skill_cooldown_entry, 1); + sd->scd[i]->skill_id = skillid; + sd->scd[i]->timer = add_timer(gettick() + tick, skill_blockpc_end, sd->bl.id, i); - if( !load ) {// not being loaded initially so ensure the skill delay is recorded - if( !(cd = idb_get(skillcd_db,sd->status.char_id)) ) {// create a new skill cooldown object for map storage - CREATE( cd, struct skill_cd, 1 ); - idb_put( skillcd_db, sd->status.char_id, cd ); - } + if (battle_config.display_status_timers && tick > 0) + clif_skill_cooldown(sd, skillid, tick); - // record the skill duration in the database map - cd->duration[cd->cursor] = tick; - cd->skidx[cd->cursor] = idx; - cd->nameid[cd->cursor] = oskill_id; - cd->cursor++; + return 1; + } else { + ShowWarning("skill_blockpc_start: Too many skillcooldowns, increase MAX_SKILLCOOLDOWN.\n"); + return 0; } +} - sd->blockskill[idx] = 0x1|(0xFE&add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,idx)); - return 0; +int skill_blockpc_clear(struct map_session_data *sd) { + int i; + nullpo_ret(sd); + for (i = 0; i < MAX_SKILLCOOLDOWN; i++) { + if (!sd->scd[i]) + continue; + delete_timer(sd->scd[i]->timer, skill_blockpc_end); + aFree(sd->scd[i]); + sd->scd[i] = NULL; + } + return 1; } int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[orn] @@ -17938,29 +17933,6 @@ int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) { return type; } -/** - * reload stored skill cooldowns when a player logs in. - * @param sd the affected player structure - */ -void skill_cooldown_load(struct map_session_data * sd) -{ - int i; - struct skill_cd* cd = NULL; - - // always check to make sure the session properly exists - nullpo_retv(sd); - - if( !(cd = idb_get(skillcd_db, sd->status.char_id)) ) {// no skill cooldown is associated with this character - return; - } - - // process each individual cooldown associated with the character - for( i = 0; i < cd->cursor; i++ ) { - // block the skill from usage but ensure it is not recorded (load = true) - skill_blockpc_start_( sd, cd->nameid[i], cd->duration[i], true ); - } -} - /*========================================== * sub-function of DB reading. * skill_db.txt @@ -18492,7 +18464,6 @@ int do_init_skill (void) group_db = idb_alloc(DB_OPT_BASE); skillunit_db = idb_alloc(DB_OPT_BASE); - skillcd_db = idb_alloc(DB_OPT_RELEASE_DATA); skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA); bowling_db = idb_alloc(DB_OPT_BASE); skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_NONE); @@ -18514,7 +18485,6 @@ int do_final_skill(void) db_destroy(skilldb_name2id); db_destroy(group_db); db_destroy(skillunit_db); - db_destroy(skillcd_db); db_destroy(skillusave_db); db_destroy(bowling_db); ers_destroy(skill_unit_ers); diff --git a/src/map/skill.h b/src/map/skill.h index fb77c8f5bb..769e0882c4 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -429,11 +429,13 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,uin int skill_castend_damage_id( struct block_list* src, struct block_list *bl,uint16 skill_id,uint16 skill_lv,unsigned int tick,int flag ); int skill_castend_pos2( struct block_list *src, int x,int y,uint16 skill_id,uint16 skill_lv,unsigned int tick,int flag); -int skill_blockpc_start_(struct map_session_data*, uint16 skill_id, int, bool); +int skill_blockpc_start(struct map_session_data*, int, int); +int skill_blockpc_get(struct map_session_data *sd, int skillid); +int skill_blockpc_clear(struct map_session_data *sd); +int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data); int skill_blockhomun_start (struct homun_data*,uint16 skill_id,int); int skill_blockmerc_start (struct mercenary_data*,uint16 skill_id,int); -#define skill_blockpc_start(sd, skill_id, tick) skill_blockpc_start_( sd, skill_id, tick, false ) // (Epoque:) To-do: replace this macro with some sort of skill tree check (rather than hard-coded skill names) #define skill_ischangesex(id) ( \ diff --git a/src/map/unit.c b/src/map/unit.c index 5323996058..281c9e6a07 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2370,6 +2370,7 @@ int unit_free(struct block_list *bl, clr_type clrtype) duel_reject(sd->duel_invite, sd); channel_pcquit(sd,0xF); //leave all chan + skill_blockpc_clear(sd); //clear all skill cooldown related // Notify friends that this char logged out. [Skotlex] map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);