Skip to content

Commit

Permalink
Added combo skill cache mechanic toggle
Browse files Browse the repository at this point in the history
Allows triggering a combo skill automatically if it was requested during an active combo skill delay
  • Loading branch information
csnv committed Dec 6, 2023
1 parent 2762a8a commit 487d804
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 38 deletions.
5 changes: 5 additions & 0 deletions conf/map/battle/skill.conf
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ skill_min_damage: 6
// The delay rate of monk's combo (Note 2)
combo_delay_rate: 100

// Store and postpone execution of combo skills during combo delay instead of rejecting usage?
// Makes executing combo skills with delays more intuitive and easy
// Official behavior is false
combo_cache_skill: false

// Use alternate auto Counter Attack Skill Type? (Note 3)
// For those characters on which it is set, 100% Critical,
// Otherwise it disregard DEF and HIT+20, CRIT*2
Expand Down
51 changes: 26 additions & 25 deletions src/map/battle.c
Original file line number Diff line number Diff line change
Expand Up @@ -7524,6 +7524,7 @@ static const struct config_data_old battle_data[] = {
{ "max_heal_lv", &battle_config.max_heal_lv, 11, 1, INT_MAX, },
{ "max_heal", &battle_config.max_heal, 9999, 0, INT_MAX, },
{ "combo_delay_rate", &battle_config.combo_delay_rate, 100, 0, INT_MAX, },
{ "combo_cache_skill", &battle_config.combo_cache_skill, 0, 0, 1, },
{ "item_check", &battle_config.item_check, 0, 0, 0xF, },
{ "item_use_interval", &battle_config.item_use_interval, 100, 0, INT_MAX, },
{ "wedding_modifydisplay", &battle_config.wedding_modifydisplay, 0, 0, 1, },
Expand Down Expand Up @@ -7758,8 +7759,8 @@ static const struct config_data_old battle_data[] = {
{ "client_sort_storage", &battle_config.client_sort_storage, 0, 0, 1, },
{ "features/buying_store", &battle_config.feature_buying_store, 1, 0, 1, },
{ "features/search_stores", &battle_config.feature_search_stores, 1, 0, 1, },
{ "searchstore_querydelay", &battle_config.searchstore_querydelay, 10, 0, INT_MAX, },
{ "searchstore_maxresults", &battle_config.searchstore_maxresults, 30, 1, INT_MAX, },
{ "searchstore_querydelay", &battle_config.searchstore_querydelay, 10, 0, INT_MAX, },
{ "searchstore_maxresults", &battle_config.searchstore_maxresults, 30, 1, INT_MAX, },
{ "display_party_name", &battle_config.display_party_name, 0, 0, 1, },
{ "send_party_options", &battle_config.send_party_options, 0x31F9, 0, 0x1FFFF, },
{ "cashshop_show_points", &battle_config.cashshop_show_points, 0, 0, 1, },
Expand Down Expand Up @@ -7799,10 +7800,10 @@ static const struct config_data_old battle_data[] = {
{ "guild_notice_changemap", &battle_config.guild_notice_changemap, 7, 0, 7, },
{ "features/banking", &battle_config.feature_banking, 1, 0, 1, },
{ "features/auction", &battle_config.feature_auction, 0, 0, 2, },
{ "idletime_criteria", &battle_config.idletime_criteria, 0x25, 1, INT_MAX, },
{ "idletime_criteria", &battle_config.idletime_criteria, 0x25, 1, INT_MAX, },
{ "mon_trans_disable_in_gvg", &battle_config.mon_trans_disable_in_gvg, 0, 0, 1, },
{ "case_sensitive_aegisnames", &battle_config.case_sensitive_aegisnames, 1, 0, 1, },
{ "search_freecell_map_margin", &battle_config.search_freecell_map_margin, 15, 0, INT_MAX, },
{ "search_freecell_map_margin", &battle_config.search_freecell_map_margin, 15, 0, INT_MAX, },
{ "guild_castle_invite", &battle_config.guild_castle_invite, 0, 0, 1, },
{ "guild_castle_expulsion", &battle_config.guild_castle_expulsion, 0, 0, 1, },
{ "song_timer_reset", &battle_config.song_timer_reset, 0, 0, 1, },
Expand Down Expand Up @@ -7866,30 +7867,30 @@ static const struct config_data_old battle_data[] = {
{ "hit_min_limit", &battle_config.hit_min, 1, 1, INT_MAX, },
{ "hit_max_limit", &battle_config.hit_max, SHRT_MAX, 1, INT_MAX, },
{ "autoloot_adjust", &battle_config.autoloot_adjust, 0, 0, 1, },
{ "hom_bonus_exp_from_master", &battle_config.hom_bonus_exp_from_master, 10, 0, 100, },
{ "hom_bonus_exp_from_master", &battle_config.hom_bonus_exp_from_master, 10, 0, 100, },
{ "allowed_actions_when_dead", &battle_config.allowed_actions_when_dead, 0, 0, 3, },
{ "teleport_close_storage", &battle_config.teleport_close_storage, 1, 0, 1, },
{ "features/show_attendance_window", &battle_config.show_attendance_window, 1, 0, 1, },
{ "elem_natural_heal_hp", &battle_config.elem_natural_heal_hp, 6000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "elem_natural_heal_sp", &battle_config.elem_natural_heal_sp, 8000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "elem_natural_heal_cap", &battle_config.elem_natural_heal_cap, 1000, 1, INT_MAX, },
{ "hom_natural_heal_hp", &battle_config.hom_natural_heal_hp, 2000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "hom_natural_heal_sp", &battle_config.hom_natural_heal_sp, 4000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "hom_natural_heal_cap", &battle_config.hom_natural_heal_cap, 1000, 1, INT_MAX, },
{ "merc_natural_heal_hp", &battle_config.merc_natural_heal_hp, 6000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "merc_natural_heal_sp", &battle_config.merc_natural_heal_sp, 8000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "merc_natural_heal_cap", &battle_config.merc_natural_heal_cap, 1000, 1, INT_MAX, },
{ "macro_detect_retry", &battle_config.macro_detect_retry, 1, 1, INT_MAX, },
{ "macro_detect_timeout", &battle_config.macro_detect_timeout, 0, 0, INT_MAX, },
{ "roulette_gold_step", &battle_config.roulette_gold_step, 10, 1, INT_MAX, },
{ "roulette_silver_step", &battle_config.roulette_silver_step, 10, 1, INT_MAX, },
{ "roulette_bronze_step", &battle_config.roulette_bronze_step, 1, 1, INT_MAX, },
{ "features/grader_max_used", &battle_config.grader_max_used, 0, 0, MAX_ITEM_GRADE, },
{ "dynamic_npc_timeout", &battle_config.dynamic_npc_timeout, 0, 0, INT_MAX, },
{ "dynamic_npc_range", &battle_config.dynamic_npc_range, 0, 0, INT_MAX, },
{ "features/goldpc/enable", &battle_config.feature_goldpc_enable, 0, 0, 1, },
{ "features/goldpc/default_mode", &battle_config.feature_goldpc_default_mode, 1, 0, INT_MAX, },
{ "venom_dust_exp", &battle_config.venom_dust_exp, 0, 0, 1, },
{ "elem_natural_heal_hp", &battle_config.elem_natural_heal_hp, 6000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "elem_natural_heal_sp", &battle_config.elem_natural_heal_sp, 8000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "elem_natural_heal_cap", &battle_config.elem_natural_heal_cap, 1000, 1, INT_MAX, },
{ "hom_natural_heal_hp", &battle_config.hom_natural_heal_hp, 2000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "hom_natural_heal_sp", &battle_config.hom_natural_heal_sp, 4000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "hom_natural_heal_cap", &battle_config.hom_natural_heal_cap, 1000, 1, INT_MAX, },
{ "merc_natural_heal_hp", &battle_config.merc_natural_heal_hp, 6000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "merc_natural_heal_sp", &battle_config.merc_natural_heal_sp, 8000, NATURAL_HEAL_INTERVAL, INT_MAX,},
{ "merc_natural_heal_cap", &battle_config.merc_natural_heal_cap, 1000, 1, INT_MAX, },
{ "macro_detect_retry", &battle_config.macro_detect_retry, 1, 1, INT_MAX, },
{ "macro_detect_timeout", &battle_config.macro_detect_timeout, 0, 0, INT_MAX, },
{ "roulette_gold_step", &battle_config.roulette_gold_step, 10, 1, INT_MAX, },
{ "roulette_silver_step", &battle_config.roulette_silver_step, 10, 1, INT_MAX, },
{ "roulette_bronze_step", &battle_config.roulette_bronze_step, 1, 1, INT_MAX, },
{ "features/grader_max_used", &battle_config.grader_max_used, 0, 0, MAX_ITEM_GRADE, },
{ "dynamic_npc_timeout", &battle_config.dynamic_npc_timeout, 0, 0, INT_MAX, },
{ "dynamic_npc_range", &battle_config.dynamic_npc_range, 0, 0, INT_MAX, },
{ "features/goldpc/enable", &battle_config.feature_goldpc_enable, 0, 0, 1, },
{ "features/goldpc/default_mode", &battle_config.feature_goldpc_default_mode, 1, 0, INT_MAX, },
{ "venom_dust_exp", &battle_config.venom_dust_exp, 0, 0, 1, },
};

static bool battle_set_value_sub(int index, int value)
Expand Down
1 change: 1 addition & 0 deletions src/map/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ struct Battle_Config {
int resurrection_exp;
int shop_exp;
int combo_delay_rate;
int combo_cache_skill;
int item_check;
int item_use_interval; //[Skotlex]
int wedding_modifydisplay;
Expand Down
37 changes: 33 additions & 4 deletions src/map/clif.c
Original file line number Diff line number Diff line change
Expand Up @@ -13490,8 +13490,8 @@ static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct
unit->skilluse_pos(&md->bl, x, y, skill_id, skill_lv);
}

static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id) __attribute__((nonnull (2)));
static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id)
static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id, bool skip_combo_check) __attribute__((nonnull (2)));
static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id, bool skip_combo_check)
{
int64 tick = timer->gettick();

Expand Down Expand Up @@ -13562,6 +13562,18 @@ static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill
if (skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST && sd->auto_cast_current.type == AUTOCAST_NONE)
return;
} else if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) {
// Cannot perform skill yet, delay execution until canact_tick allows it
// Only for combo skills with implicit delays, otherwise we create a speedhack that bypasses client's animation delay
if (battle_config.combo_cache_skill
&& !skip_combo_check
&& sd->sc.data[SC_COMBOATTACK] != NULL
&& skill->is_combo(skill_id)
&& skill->get_delaynodex(skill_id, skill_lv) != 0
) {
timer->add(sd->ud.canact_tick, clif->combo_delay_timer, sd->bl.id, (intptr_t)MakeDWord((uint16)skill_id, (uint16)skill_lv));
return;
}

if (sd->auto_cast_current.type == AUTOCAST_NONE) {
clif->skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0, 0);
return;
Expand Down Expand Up @@ -13608,6 +13620,21 @@ static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill
unit->skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
}

/// Trigger next combo skill as previously requested by client
static int clif_combo_delay_timer(int tid, int64 tick, int id, intptr_t data) {
struct map_session_data* sd = map->id2sd(id);

if (sd == NULL)
return 0;

int skill_id = (int)GetWord((uint32)data, 0);
int skill_lv = (int)GetWord((uint32)data, 1);

clif->useSkillToIdReal(0, sd, skill_id, skill_lv, battle->get_target(&sd->bl), true);

return 0;
}

static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
/// Request to use a targeted skill.
/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL)
Expand All @@ -13619,15 +13646,16 @@ static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
sd,
RFIFOW(fd, packet_db[RFIFOW(fd, 0)].pos[1]),
RFIFOW(fd, packet_db[RFIFOW(fd, 0)].pos[0]),
RFIFOL(fd, packet_db[RFIFOW(fd, 0)].pos[2]));
RFIFOL(fd, packet_db[RFIFOW(fd, 0)].pos[2]),
false);
}

static void clif_parse_startUseSkillToId(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
static void clif_parse_startUseSkillToId(int fd, struct map_session_data *sd)
{
#if PACKETVER_MAIN_NUM >= 20181002 || PACKETVER_RE_NUM >= 20181002 || PACKETVER_ZERO_NUM >= 20181010
const struct PACKET_CZ_USE_SKILL_START *p = RFIFOP(fd, 0);
clif->useSkillToIdReal(fd, sd, p->skillId, p->skillLv, p->targetId);
clif->useSkillToIdReal(fd, sd, p->skillId, p->skillLv, p->targetId, false);
#endif
}

Expand Down Expand Up @@ -27288,6 +27316,7 @@ void clif_defaults(void)
clif->clan_leave = clif_clan_leave;
clif->clan_message = clif_clan_message;
clif->pClanMessage = clif_parse_ClanMessage;
clif->combo_delay_timer = clif_combo_delay_timer;
// -- Hat Effect
clif->hat_effect = clif_hat_effect;
clif->hat_effect_single = clif_hat_effect_single;
Expand Down
3 changes: 2 additions & 1 deletion src/map/clif.h
Original file line number Diff line number Diff line change
Expand Up @@ -1582,7 +1582,8 @@ struct clif_interface {
void (*pChangeCart) (int fd,struct map_session_data *sd);
void (*pStatusUp) (int fd,struct map_session_data *sd);
void (*pSkillUp) (int fd,struct map_session_data *sd);
void (*useSkillToIdReal) (int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id);
void (*useSkillToIdReal) (int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id, bool skip_combo_check);
int (*combo_delay_timer)(int tid, int64 tick, int id, intptr_t data);
void (*pUseSkillToId) (int fd, struct map_session_data *sd);
void (*pStartUseSkillToId) (int fd, struct map_session_data *sd);
void (*pStopUseSkillToId) (int fd, struct map_session_data *sd);
Expand Down
6 changes: 4 additions & 2 deletions src/plugins/HPMHooking/HPMHooking.Defs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2458,8 +2458,10 @@ typedef void (*HPMHOOK_pre_clif_pStatusUp) (int *fd, struct map_session_data **s
typedef void (*HPMHOOK_post_clif_pStatusUp) (int fd, struct map_session_data *sd);
typedef void (*HPMHOOK_pre_clif_pSkillUp) (int *fd, struct map_session_data **sd);
typedef void (*HPMHOOK_post_clif_pSkillUp) (int fd, struct map_session_data *sd);
typedef void (*HPMHOOK_pre_clif_useSkillToIdReal) (int *fd, struct map_session_data **sd, int *skill_id, int *skill_lv, int *target_id);
typedef void (*HPMHOOK_post_clif_useSkillToIdReal) (int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id);
typedef void (*HPMHOOK_pre_clif_useSkillToIdReal) (int *fd, struct map_session_data **sd, int *skill_id, int *skill_lv, int *target_id, bool *skip_combo_check);
typedef void (*HPMHOOK_post_clif_useSkillToIdReal) (int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id, bool skip_combo_check);
typedef int (*HPMHOOK_pre_clif_combo_delay_timer) (int *tid, int64 *tick, int *id, intptr_t *data);
typedef int (*HPMHOOK_post_clif_combo_delay_timer) (int retVal___, int tid, int64 tick, int id, intptr_t data);
typedef void (*HPMHOOK_pre_clif_pUseSkillToId) (int *fd, struct map_session_data **sd);
typedef void (*HPMHOOK_post_clif_pUseSkillToId) (int fd, struct map_session_data *sd);
typedef void (*HPMHOOK_pre_clif_pStartUseSkillToId) (int *fd, struct map_session_data **sd);
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,8 @@ struct {
struct HPMHookPoint *HP_clif_pSkillUp_post;
struct HPMHookPoint *HP_clif_useSkillToIdReal_pre;
struct HPMHookPoint *HP_clif_useSkillToIdReal_post;
struct HPMHookPoint *HP_clif_combo_delay_timer_pre;
struct HPMHookPoint *HP_clif_combo_delay_timer_post;
struct HPMHookPoint *HP_clif_pUseSkillToId_pre;
struct HPMHookPoint *HP_clif_pUseSkillToId_post;
struct HPMHookPoint *HP_clif_pStartUseSkillToId_pre;
Expand Down Expand Up @@ -9373,6 +9375,8 @@ struct {
int HP_clif_pSkillUp_post;
int HP_clif_useSkillToIdReal_pre;
int HP_clif_useSkillToIdReal_post;
int HP_clif_combo_delay_timer_pre;
int HP_clif_combo_delay_timer_post;
int HP_clif_pUseSkillToId_pre;
int HP_clif_pUseSkillToId_post;
int HP_clif_pStartUseSkillToId_pre;
Expand Down
1 change: 1 addition & 0 deletions src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@ struct HookingPointData HookingPoints[] = {
{ HP_POP(clif->pStatusUp, HP_clif_pStatusUp) },
{ HP_POP(clif->pSkillUp, HP_clif_pSkillUp) },
{ HP_POP(clif->useSkillToIdReal, HP_clif_useSkillToIdReal) },
{ HP_POP(clif->combo_delay_timer, HP_clif_combo_delay_timer) },
{ HP_POP(clif->pUseSkillToId, HP_clif_pUseSkillToId) },
{ HP_POP(clif->pStartUseSkillToId, HP_clif_pStartUseSkillToId) },
{ HP_POP(clif->pStopUseSkillToId, HP_clif_pStopUseSkillToId) },
Expand Down
Loading

0 comments on commit 487d804

Please sign in to comment.