Mercenary, Regen and Frenzy Improvements (#8185)

- When a Mercenary gains a bonus, there will now be an animation
- Mercenary bonuses now last for 5 minutes instead of 10 minutes
- Fixed HP/SP recovery values of Mercenaries (and Elementals)
- Mercenaries now recover HP when walking
- Homunculi no longer recover SP when walking
- Mercenary natural recovery interval is 8s for HP and 6s for SP
- Homunculus natural recovery interval is 2s for HP and 4s for SP
- MER_CRASH now only deals 1 hit and can be cast-cancelled
- Frenzy now drains HP every 10 seconds instead of every 15 in pre-renewal
- Fixed SP cost Mercenary Frenzy (100 -> 200 SP)
- Killing monsters exactly 2 times below you base level now still counts as mercenary kill
- Fixes #8184 
- Fixes #7663
This commit is contained in:
Playtester 2024-03-27 11:48:05 +01:00 committed by GitHub
parent ed2d03d811
commit 1a004f0164
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 45 additions and 21 deletions

View File

@ -9641,7 +9641,7 @@ Body:
Hit: Single
HitCount: 1
Duration1: 300000
Duration2: 15000
Duration2: 10000
Requires:
SpCost: 200
Status: Berserk
@ -32467,9 +32467,9 @@ Body:
Hit: Single
HitCount: 1
Duration1: 300000
Duration2: 15000
Duration2: 10000
Requires:
SpCost: 100
SpCost: 200
Status: Berserk
- Id: 8207
Name: MA_DOUBLE
@ -33068,9 +33068,10 @@ Body:
Type: Weapon
TargetType: Attack
Range: 1
Hit: Multi_Hit
HitCount: 3
Hit: Single
HitCount: 1
Element: Weapon
CastCancel: true
CastTime: 1000
AfterCastActDelay: 2000
Duration2: 5000

View File

@ -2712,6 +2712,7 @@ Body:
CalcFlags:
Flee: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2720,6 +2721,7 @@ Body:
CalcFlags:
Watk: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2728,6 +2730,7 @@ Body:
CalcFlags:
MaxHp: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2736,6 +2739,7 @@ Body:
CalcFlags:
MaxSp: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2744,6 +2748,7 @@ Body:
CalcFlags:
Hit: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true

View File

@ -46490,9 +46490,10 @@ Body:
Type: Weapon
TargetType: Attack
Range: 1
Hit: Multi_Hit
HitCount: 3
Hit: Single
HitCount: 1
Element: Weapon
CastCancel: true
CastTime: 1000
AfterCastActDelay: 2000
Duration2: 4500

View File

@ -2822,6 +2822,7 @@ Body:
CalcFlags:
Flee: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2830,6 +2831,7 @@ Body:
CalcFlags:
Watk: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2838,6 +2840,7 @@ Body:
CalcFlags:
MaxHp: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2846,6 +2849,7 @@ Body:
CalcFlags:
MaxSp: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true
@ -2854,6 +2858,7 @@ Body:
CalcFlags:
Hit: true
Flags:
BlEffect: true
NoDispell: true
NoBanishingBuster: true
NoClearance: true

View File

@ -6579,7 +6579,8 @@ void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick,
sd = BL_CAST(BL_PC, bl);
if (!(status_efst_get_bl_type((efst_type)type)&bl->type)) // only send status changes that actually matter to the client
// Check if current bl type is in the returned bitmask and only send status changes that actually matter to the client
if (!(status_efst_get_bl_type(static_cast<efst_type>(type)) & bl->type))
return;
clif_status_change_sub(bl, bl->id, type, flag, tick, val1, val2, val3, ((sd ? (pc_isinvisible(sd) ? SELF : AREA) : AREA_WOS)));

View File

@ -421,7 +421,7 @@ bool mercenary_dead(s_mercenary_data *md) {
void mercenary_killbonus(s_mercenary_data *md) {
std::vector<sc_type> scs = { SC_MERC_FLEEUP, SC_MERC_ATKUP, SC_MERC_HPUP, SC_MERC_SPUP, SC_MERC_HITUP };
sc_start(&md->bl,&md->bl, util::vector_random(scs), 100, rnd_value(1, 5), 600000);
sc_start(&md->bl,&md->bl, util::vector_random(scs), 100, rnd_value(1, 5), 300000); //Bonus lasts for 5 minutes
}
/**

View File

@ -3109,8 +3109,8 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
achievement_update_objective(sd, AG_BATTLE, 1, md->mob_id);
}
// The master or Mercenary can increase the kill count
if (sd->md && src && (src->type == BL_PC || src->type == BL_MER) && mob->lv > sd->status.base_level / 2)
// The master or Mercenary can increase the kill count, if the monster level is greater or equal than half the baselevel of the master
if (sd->md && src && (src->type == BL_PC || src->type == BL_MER) && mob->lv >= sd->status.base_level / 2)
mercenary_kills(sd->md);
}

View File

@ -5261,16 +5261,16 @@ void status_calc_regen(struct block_list *bl, struct status_data *status, struct
regen->sp = cap_value(val, 1, SHRT_MAX);
}
} else if( bl->type == BL_MER ) {
val = (status->max_hp * status->vit / 10000 + 1) * 6;
val = static_cast<decltype(val)>((status->max_hp * status->vit / 10000.0 + 1.0) * 6.0);
regen->hp = cap_value(val, 1, SHRT_MAX);
val = (status->max_sp * (status->int_ + 10) / 750) + 1;
val = static_cast<decltype(val)>((status->max_sp * (status->int_ + 10.0) / 750.0) + 1.0);
regen->sp = cap_value(val, 1, SHRT_MAX);
} else if( bl->type == BL_ELEM ) {
val = (status->max_hp * status->vit / 10000 + 1) * 6;
val = static_cast<decltype(val)>((status->max_hp * status->vit / 10000.0 + 1.0) * 6.0);
regen->hp = cap_value(val, 1, SHRT_MAX);
val = (status->max_sp * (status->int_ + 10) / 750) + 1;
val = static_cast<decltype(val)>((status->max_sp * (status->int_ + 10.0) / 750.0) + 1.0);
regen->sp = cap_value(val, 1, SHRT_MAX);
}
}
@ -15237,10 +15237,14 @@ static int status_natural_heal(struct block_list* bl, va_list args)
ud = unit_bl2ud(bl);
if (flag&(RGN_HP|RGN_SHP|RGN_SSP) && ud && ud->walktimer != INVALID_TIMER) {
if (ud && ud->walktimer != INVALID_TIMER) {
flag &= ~(RGN_SHP|RGN_SSP);
if(!regen->state.walk)
//Mercenaries recover HP even while walking
if(bl->type != BL_MER && !regen->state.walk)
flag &= ~RGN_HP;
//Homunculus don't recover SP while walking
if (bl->type == BL_HOM && !regen->state.walk)
flag &= ~RGN_SP;
}
if (flag&(RGN_HP|RGN_SP)) {
@ -15256,11 +15260,15 @@ static int status_natural_heal(struct block_list* bl, va_list args)
if (flag&RGN_HP) {
// Interval to next recovery tick
rate = (int)(battle_config.natural_healhp_interval / (regen->rate.hp/100. * multi));
if (ud && ud->walktimer != INVALID_TIMER)
// Half recovery while moving only applies to players with certain traits
if (sd && ud && ud->walktimer != INVALID_TIMER)
rate *= 2;
// Homun HP regen fix (they should regen as if they were sitting (twice as fast)
// Homun HP regen fix (2 seconds instead of 6 seconds)
if(bl->type == BL_HOM)
rate /= 2;
rate /= 3;
// Mercenary HP regen fix (8 seconds instead of 6 seconds)
if (bl->type == BL_MER)
rate = (rate * 4) / 3;
// Our timer system isn't 100% accurate so make sure we use the closest interval
rate -= NATURAL_HEAL_INTERVAL / 2;
@ -15281,9 +15289,12 @@ static int status_natural_heal(struct block_list* bl, va_list args)
if(flag&RGN_SP) {
// Interval to next recovery tick
rate = (int)(battle_config.natural_healsp_interval / (regen->rate.sp/100. * multi));
// Homun SP regen fix (they should regen as if they were sitting (twice as fast)
// Homun SP regen fix (4 seconds instead of 8 seconds)
if(bl->type==BL_HOM)
rate /= 2;
// Mercenary SP regen fix (6 seconds instead of 8 seconds)
if (bl->type == BL_MER)
rate = (rate * 3) / 4;
#ifdef RENEWAL
if (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_MONK &&
sc && sc->getSCE(SC_EXPLOSIONSPIRITS) && (!sc->getSCE(SC_SPIRIT) || sc->getSCE(SC_SPIRIT)->val2 != SL_MONK))