Implements new guild skills (#5192)

* Fixes #4183.
* Adds 3 new guild skills: GD_CHARGESHOUT_FLAG, GD_CHARGESHOUT_BEATING, and GD_EMERGENCY_MOVE
* Changes 5 minute guild global cooldown to be 3 minute individual cooldowns for GD_RESTORE, GD_REGENERATION, GD_BATTLEORDER, GD_EMERGENCYCALL, and GD_ITEMEMERGENCYCALL.
* Reduces GD_RESTORE fixed cast time from 10 seconds to 1.
This commit is contained in:
Aleos 2020-11-28 17:16:22 -05:00 committed by GitHub
parent 103416ca40
commit 2d97ece2a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 180 additions and 54 deletions

View File

@ -18,6 +18,7 @@ guild_max_castles: 0
// Activate guild skills delay by relog?
// Official setting is 5 minutes (300000 ms), otherwise allow guild leaders to relog to cancel the 5 minute delay.
// Note: This was changed in renewal in favor of individual skill cooldown.
guild_skill_relog_delay: 300000
// Melee damage adjustments (non skills) for WoE battles (Guild Vs Guild) (Note 2)

View File

@ -118,6 +118,3 @@ Body:
- Id: GD_DEVELOPMENT
MaxLevel: 1
# - Id: GD_GUILD_STORAGE
# MaxLevel: 5

View File

@ -33671,9 +33671,3 @@ Body:
IgnoreKagehumi: true
CastCancel: true
Duration2: 300000
- Id: 10016
Name: GD_GUILD_STORAGE
Description: Guild Storage Expansion
MaxLevel: 5
Flags:
IsGuild: true

View File

@ -130,3 +130,23 @@ Body:
Level: 1
- Id: GD_HAWKEYES
Level: 1
- Id: GD_CHARGESHOUT_FLAG
MaxLevel: 1
Required:
- Id: GD_EMERGENCYCALL
Level: 1
- Id: GD_CHARGESHOUT_BEATING
MaxLevel: 1
Required:
- Id: GD_CHARGESHOUT_FLAG
Level: 1
- Id: GD_EMERGENCY_MOVE
MaxLevel: 1
Required:
- Id: GD_LEADERSHIP
Level: 1
- Id: GD_GLORYWOUNDS
Level: 1

View File

@ -3410,7 +3410,7 @@
//20266,G_ILL_TEDDY_BEAR_G
//20267,G_ILL_TEDDY_BEAR_W
//20268,G_ILL_TEDDY_BEAR_B
//20269,GUILD_SKILL_FLAG
20269,GUILD_SKILL_FLAG,Guild Skill Flag,Guild Skill Flag,90,30,0,0,0,1,0,0,0,0,1,17,1,80,126,20,10,12,2,0,20,0x120,300,1288,288,384,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
//20270,ILL_TRI_JOINT
//20271,ILL_STALACTIC_GOLEM
//20272,ILL_MEGALITH

View File

@ -37093,12 +37093,10 @@ Body:
SplashArea: 15
CastCancel: true
Duration1: 180000
Duration2: 300000
Cooldown: 180000
CastTimeFlags:
IgnoreDex: true
IgnoreStatus: true
Requires:
SpCost: 1
- Id: 10011
Name: GD_REGENERATION
Description: Regeneration
@ -37112,12 +37110,10 @@ Body:
SplashArea: 15
CastCancel: true
Duration1: 60000
Duration2: 300000
Cooldown: 180000
CastTimeFlags:
IgnoreDex: true
IgnoreStatus: true
Requires:
SpCost: 1
- Id: 10012
Name: GD_RESTORE
Description: Restoration
@ -37130,13 +37126,11 @@ Body:
IsGuild: true
SplashArea: 15
CastCancel: true
Duration2: 300000
FixedCastTime: 10000
Cooldown: 180000
FixedCastTime: 1000
CastTimeFlags:
IgnoreStatus: true
IgnoreItemBonus: true
Requires:
SpCost: 1
- Id: 10013
Name: GD_EMERGENCYCALL
Description: Urgent Call
@ -37149,14 +37143,12 @@ Body:
IsGuild: true
IgnoreKagehumi: true
CastCancel: true
Duration2: 300000
Cooldown: 300000
FixedCastTime: 5000
CastTimeFlags:
IgnoreDex: true
IgnoreStatus: true
IgnoreItemBonus: true
Requires:
SpCost: 1
- Id: 10014
Name: GD_DEVELOPMENT
Description: Permanent Development
@ -37175,13 +37167,49 @@ Body:
IsGuild: true
IgnoreKagehumi: true
CastCancel: true
Duration2: 300000
Cooldown: 300000
FixedCastTime: 5000
Requires:
SpCost: 1
- Id: 10016
Name: GD_GUILD_STORAGE
Description: Guild Storage Expansion
MaxLevel: 5
Flags:
IsGuild: true
- Id: 10017
Name: GD_CHARGESHOUT_FLAG
Description: Charge Shout Flag
MaxLevel: 1
TargetType: Self
DamageFlags:
NoDamage: true
Flags:
IsGuild: true
CastCancel: true
Duration1: 300000
Cooldown: 60000
FixedCastTime: 1000
- Id: 10018
Name: GD_CHARGESHOUT_BEATING
Description: Charge Shout Beating
MaxLevel: 1
TargetType: Self
DamageFlags:
NoDamage: true
Flags:
IsGuild: true
CastCancel: true
Cooldown: 60000
FixedCastTime: 1000
- Id: 10019
Name: GD_EMERGENCY_MOVE
Description: Emergency Move
MaxLevel: 1
TargetType: Self
DamageFlags:
NoDamage: true
Splash: true
Flags:
IsGuild: true
SplashArea: 15
Duration1: 10000
Cooldown: 600000

View File

@ -74,7 +74,11 @@ typedef uint32 t_itemid;
#define MAX_GUILDPOSITION 20 ///Increased max guild positions to accomodate for all members [Valaris] (removed) [PoW]
#define MAX_GUILDEXPULSION 32 ///Max Guild expulsion
#define MAX_GUILDALLIANCE 16 ///Max Guild alliance
#define MAX_GUILDSKILL 17 ///Max Guild skills
#ifdef RENEWAL
#define MAX_GUILDSKILL 20 ///Max Guild skills
#else
#define MAX_GUILDSKILL 15 ///Max Guild skills
#endif
#define MAX_GUILDLEVEL 50 ///Max Guild level
#define MAX_GUARDIANS 8 ///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex]
#define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest
@ -690,6 +694,8 @@ struct guild {
/* Used by char-server to save events for guilds */
unsigned short save_flag;
int32 chargeshout_flag_id;
};
struct guild_castle {
@ -768,24 +774,27 @@ enum e_guild_member_info { //Change Member Infos
};
enum e_guild_skill {
GD_SKILLBASE=10000,
GD_APPROVAL=10000,
GD_KAFRACONTRACT=10001,
GD_GUARDRESEARCH=10002,
GD_GUARDUP=10003,
GD_EXTENSION=10004,
GD_GLORYGUILD=10005,
GD_LEADERSHIP=10006,
GD_GLORYWOUNDS=10007,
GD_SOULCOLD=10008,
GD_HAWKEYES=10009,
GD_BATTLEORDER=10010,
GD_REGENERATION=10011,
GD_RESTORE=10012,
GD_EMERGENCYCALL=10013,
GD_DEVELOPMENT=10014,
GD_ITEMEMERGENCYCALL=10015,
GD_GUILD_STORAGE=10016,
GD_SKILLBASE = 10000,
GD_APPROVAL = 10000,
GD_KAFRACONTRACT,
GD_GUARDRESEARCH,
GD_GUARDUP,
GD_EXTENSION,
GD_GLORYGUILD,
GD_LEADERSHIP,
GD_GLORYWOUNDS,
GD_SOULCOLD,
GD_HAWKEYES,
GD_BATTLEORDER,
GD_REGENERATION,
GD_RESTORE,
GD_EMERGENCYCALL,
GD_DEVELOPMENT,
GD_ITEMEMERGENCYCALL,
GD_GUILD_STORAGE,
GD_CHARGESHOUT_FLAG,
GD_CHARGESHOUT_BEATING,
GD_EMERGENCY_MOVE,
GD_MAX,
};

View File

@ -1822,7 +1822,7 @@ bool battle_can_hit_gvg_target(struct block_list *src,struct block_list *bl,uint
if (ud && ud->immune_attack)
return false;
if(md && md->guardian_data) {
if(md && (md->guardian_data || md->special_state.ai == AI_GUILD)) {
if ((status_bl_has_mode(bl,MD_SKILL_IMMUNE) || (class_ == MOBID_EMPERIUM && !skill_get_inf2(skill_id, INF2_TARGETEMPERIUM))) && flag&BF_SKILL) //Skill immunity.
return false;
if( src->type != BL_MOB || mob_is_clone( ((struct mob_data*)src)->mob_id ) ){
@ -1831,8 +1831,13 @@ bool battle_can_hit_gvg_target(struct block_list *src,struct block_list *bl,uint
if (class_ == MOBID_EMPERIUM && (!g || guild_checkskill(g,GD_APPROVAL) <= 0 ))
return false;
if (g && battle_config.guild_max_castles && guild_checkcastles(g)>=battle_config.guild_max_castles)
return false; // [MouseJstr]
if (g != nullptr) {
if (battle_config.guild_max_castles && guild_checkcastles(g)>=battle_config.guild_max_castles)
return false; // [MouseJstr]
if (md->special_state.ai == AI_GUILD && g->guild_id == md->master_id)
return false;
}
}
}
return true;
@ -8206,8 +8211,12 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
case BL_PET:
if (t_bl->type != BL_MOB && flag&BCT_ENEMY)
return 0; //Pet may not attack non-mobs.
if (t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data && flag&BCT_ENEMY)
return 0; //pet may not attack Guardians/Emperium
if (t_bl->type == BL_MOB && flag & BCT_ENEMY) {
mob_data *md = BL_CAST(BL_MOB, t_bl);
if (md->guardian_data || md->special_state.ai == AI_GUILD)
return 0; //pet may not attack Guardians/Emperium
}
break;
case BL_SKILL: {
struct skill_unit *su = (struct skill_unit *)src;

View File

@ -1364,8 +1364,10 @@ int chrif_skillcooldown_save(struct map_session_data *sd) {
if (!sd->scd[i])
continue;
#ifndef RENEWAL
if (!battle_config.guild_skill_relog_delay && (sd->scd[i]->skill_id >= GD_BATTLEORDER && sd->scd[i]->skill_id <= GD_EMERGENCYCALL))
continue;
#endif
timer = get_timer(sd->scd[i]->timer);
if (timer == NULL || timer->func != skill_blockpc_end || DIFF_TICK(timer->tick, tick) < 0)

View File

@ -12630,7 +12630,7 @@ void clif_parse_skill_toid( struct map_session_data* sd, uint16 skill_id, uint16
sd->skillitem = sd->skillitemlv = 0;
if( SKILL_CHK_GUILD(skill_id) ) {
if( sd->state.gmaster_flag )
if( sd->state.gmaster_flag || skill_id == GD_CHARGESHOUT_BEATING )
skill_lv = guild_checkskill(sd->guild, skill_id);
else
skill_lv = 0;

View File

@ -606,10 +606,12 @@ int guild_recv_info(struct guild *sg) {
//Perform the check on the user because the first load
guild_check_member(sg);
if ((sd = map_nick2sd(sg->master,false)) != NULL) {
#ifndef RENEWAL
//If the guild master is online the first time the guild_info is received,
//that means he was the first to join, so apply guild skill blocking here.
if( battle_config.guild_skill_relog_delay )
guild_block_skill(sd, battle_config.guild_skill_relog_delay);
#endif
//Also set the guild master flag.
sd->guild = g;
@ -804,9 +806,11 @@ void guild_member_joined(struct map_session_data *sd) {
}
if (strcmp(sd->status.name,g->master) == 0) { // set the Guild Master flag
sd->state.gmaster_flag = 1;
#ifndef RENEWAL
// prevent Guild Skills from being used directly after relog
if( battle_config.guild_skill_relog_delay )
guild_block_skill(sd, battle_config.guild_skill_relog_delay);
#endif
}
i = guild_getindex(g, sd->status.account_id, sd->status.char_id);
if (i == -1)
@ -1002,6 +1006,7 @@ int guild_member_withdraw(int guild_id, uint32 account_id, uint32 char_id, int f
status_change_end(&sd->bl,SC_GLORYWOUNDS,INVALID_TIMER);
status_change_end(&sd->bl,SC_SOULCOLD,INVALID_TIMER);
status_change_end(&sd->bl,SC_HAWKEYES,INVALID_TIMER);
status_change_end(&sd->bl,SC_EMERGENCY_MOVE,INVALID_TIMER);
//@TODO: Send emblem update to self and people around
}
return 0;
@ -1882,6 +1887,7 @@ int guild_broken(int guild_id,int flag) {
status_change_end(&sd->bl,SC_GLORYWOUNDS,INVALID_TIMER);
status_change_end(&sd->bl,SC_SOULCOLD,INVALID_TIMER);
status_change_end(&sd->bl,SC_HAWKEYES,INVALID_TIMER);
status_change_end(&sd->bl,SC_EMERGENCY_MOVE,INVALID_TIMER);
}
}
@ -1966,8 +1972,10 @@ int guild_gm_changed(int guild_id, uint32 account_id, uint32 char_id, time_t tim
g->member[0].sd->state.gmaster_flag = 1;
clif_name_area(&g->member[0].sd->bl);
//Block his skills to prevent abuse.
#ifndef RENEWAL
if (battle_config.guild_skill_relog_delay)
guild_block_skill(g->member[0].sd, battle_config.guild_skill_relog_delay);
#endif
}
// announce the change to all guild members

View File

@ -2086,6 +2086,7 @@ int map_quit(struct map_session_data *sd) {
status_change_end(&sd->bl, SC_GLORYWOUNDS, INVALID_TIMER);
status_change_end(&sd->bl, SC_SOULCOLD, INVALID_TIMER);
status_change_end(&sd->bl, SC_HAWKEYES, INVALID_TIMER);
status_change_end(&sd->bl, SC_EMERGENCY_MOVE, INVALID_TIMER);
status_change_end(&sd->bl, SC_CHASEWALK2, INVALID_TIMER);
if(sd->sc.data[SC_PROVOKE] && sd->sc.data[SC_PROVOKE]->timer == INVALID_TIMER)
status_change_end(&sd->bl, SC_PROVOKE, INVALID_TIMER); //Infinite provoke ends on logout

View File

@ -374,6 +374,7 @@ enum mob_ai {
AI_ZANZOU,
AI_LEGION,
AI_FAW,
AI_GUILD,
AI_MAX
};

View File

@ -76,6 +76,7 @@ enum MOBID {
MOBID_S_HORNET = 2158,
MOBID_S_GIANT_HORNET,
MOBID_S_LUCIOLA_VESPA,
MOBID_GUILD_SKILL_FLAG = 20269,
};
///Mob skill states.

View File

@ -1608,6 +1608,7 @@
export_constant(SC_EP16_2_BUFF_SS);
export_constant(SC_EP16_2_BUFF_SC);
export_constant(SC_EP16_2_BUFF_AC);
export_constant(SC_EMERGENCY_MOVE);
#ifdef RENEWAL
export_constant(SC_EXTREMITYFIST2);
#endif
@ -3774,6 +3775,7 @@
export_constant(AI_ZANZOU);
export_constant(AI_LEGION);
export_constant(AI_FAW);
export_constant(AI_GUILD);
/* battle flags */
export_constant(BF_NONE);

View File

@ -2033,6 +2033,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
case SC_ENTRY_QUEUE_APPLY_DELAY: case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT:
case SC_REUSE_LIMIT_LUXANIMA: case SC_LUXANIMA: case SC_SOULENERGY:
case SC_EP16_2_BUFF_SS: case SC_EP16_2_BUFF_SC: case SC_EP16_2_BUFF_AC:
case SC_EMERGENCY_MOVE:
continue;
case SC_WHISTLE: case SC_ASSNCROS: case SC_POEMBRAGI:
case SC_APPLEIDUN: case SC_HUMMING: case SC_DONTFORGETME:
@ -8473,6 +8474,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
case SC_LHZ_DUN_N1: case SC_LHZ_DUN_N2: case SC_LHZ_DUN_N3: case SC_LHZ_DUN_N4:
case SC_REUSE_LIMIT_LUXANIMA: case SC_LUXANIMA: case SC_SOULENERGY:
case SC_EP16_2_BUFF_SS: case SC_EP16_2_BUFF_SC: case SC_EP16_2_BUFF_AC:
case SC_EMERGENCY_MOVE:
continue;
case SC_WHISTLE:
case SC_ASSNCROS:
@ -9342,6 +9344,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
case GD_BATTLEORDER:
case GD_REGENERATION:
case GD_RESTORE:
case GD_EMERGENCY_MOVE:
if(flag&1) {
if (status_get_guild_id(src) == status_get_guild_id(bl)) {
if( skill_id == GD_RESTORE )
@ -9356,7 +9359,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
skill_castend_nodamage_id);
if (sd)
guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
#ifdef RENEWAL
skill_blockpc_start(sd, skill_id, skill_get_cooldown(skill_id, skill_lv));
#else
guild_block_skill(sd, skill_get_time2(skill_id, skill_lv));
#endif
}
break;
case GD_EMERGENCYCALL:
@ -9395,9 +9402,39 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
}
}
if (sd)
guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
#ifdef RENEWAL
skill_blockpc_start(sd, skill_id, skill_get_cooldown(skill_id, skill_lv));
#else
guild_block_skill(sd, skill_get_time2(skill_id, skill_lv));
#endif
}
break;
case GD_CHARGESHOUT_FLAG:
if (sd && sd->guild && sd->state.gmaster_flag == 1) {
mob_data *md = mob_once_spawn_sub(src, src->m, src->x, src->y, sd->guild->name, MOBID_GUILD_SKILL_FLAG, nullptr, SZ_SMALL, AI_GUILD);
if (md) {
sd->guild->chargeshout_flag_id = md->bl.id;
md->master_id = src->id;
if (md->deletetimer != INVALID_TIMER)
delete_timer(md->deletetimer, mob_timer_delete);
md->deletetimer = add_timer(gettick() + skill_get_time(GD_CHARGESHOUT_FLAG, skill_lv), mob_timer_delete, md->bl.id, 0);
mob_spawn(md);
}
}
break;
case GD_CHARGESHOUT_BEATING:
if (sd && sd->guild && map_blid_exists(sd->guild->chargeshout_flag_id)) {
block_list *mob_bl = map_id2bl(sd->guild->chargeshout_flag_id);
if (pc_setpos(sd, map_id2index(mob_bl->m), mob_bl->x, mob_bl->y, CLR_RESPAWN) != SETPOS_OK)
clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
else
clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
} else
clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
break;
case SG_FEEL:
//AuronX reported you CAN memorize the same map as all three. [Skotlex]
@ -9967,6 +10004,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
case SC_ENTRY_QUEUE_APPLY_DELAY: case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT:
case SC_REUSE_LIMIT_LUXANIMA: case SC_LUXANIMA: case SC_SOULENERGY:
case SC_EP16_2_BUFF_SS: case SC_EP16_2_BUFF_SC: case SC_EP16_2_BUFF_AC:
case SC_EMERGENCY_MOVE:
continue;
case SC_ASSUMPTIO:
if( bl->type == BL_MOB )
@ -15894,6 +15932,9 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i
case GD_BATTLEORDER:
case GD_REGENERATION:
case GD_RESTORE:
case GD_CHARGESHOUT_FLAG:
case GD_CHARGESHOUT_BEATING:
case GD_EMERGENCY_MOVE:
if (!map_flag_gvg2(sd->bl.m)) {
clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return false;
@ -15901,7 +15942,7 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i
case GD_EMERGENCYCALL:
case GD_ITEMEMERGENCYCALL:
// other checks were already done in skill_isNotOk()
if (!sd->status.guild_id || !sd->state.gmaster_flag)
if (!sd->status.guild_id || (sd->state.gmaster_flag == 0 && skill_id != GD_CHARGESHOUT_BEATING))
return false;
break;

View File

@ -802,6 +802,10 @@ void initChangeTables(void)
set_sc( GD_BATTLEORDER , SC_BATTLEORDERS , EFST_GDSKILL_BATTLEORDER , SCB_STR|SCB_INT|SCB_DEX );
set_sc( GD_REGENERATION , SC_REGENERATION , EFST_GDSKILL_REGENERATION , SCB_REGEN );
#ifdef RENEWAL
set_sc( GD_EMERGENCY_MOVE , SC_EMERGENCY_MOVE , EFST_INC_AGI , SCB_SPEED );
#endif
/* Rune Knight */
set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , EFST_ENCHANTBLADE , SCB_NONE );
set_sc( RK_DRAGONHOWLING , SC_FEAR , EFST_BLANK , SCB_FLEE|SCB_HIT );
@ -7429,6 +7433,8 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
val = max(val, sc->data[SC_DORAM_WALKSPEED]->val1);
if (sc->data[SC_RUSHWINDMILL])
val = max(val, 25); // !TODO: Confirm bonus movement speed
if (sc->data[SC_EMERGENCY_MOVE])
val = max(val, sc->data[SC_EMERGENCY_MOVE]->val2);
// !FIXME: official items use a single bonus for this [ultramage]
if( sc->data[SC_SPEEDUP0] ) // Temporary item-based speedup
@ -12071,6 +12077,9 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
tick_time = 1000;
val4 = tick / tick_time;
break;
case SC_EMERGENCY_MOVE:
val2 = 25; // Movement speed increase
break;
case SC_SUNSTANCE:
val2 = 2 + val1; // ATK Increase
@ -14948,6 +14957,7 @@ void status_change_clear_buffs(struct block_list* bl, uint8 type)
case SC_GLORYWOUNDS:
case SC_SOULCOLD:
case SC_HAWKEYES:
case SC_EMERGENCY_MOVE:
case SC_SAFETYWALL:
case SC_PNEUMA:
case SC_NOCHAT:

View File

@ -939,6 +939,8 @@ enum sc_type : int16 {
SC_EP16_2_BUFF_SC,
SC_EP16_2_BUFF_AC,
SC_EMERGENCY_MOVE,
#ifdef RENEWAL
SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled
#endif