Natural recovery formula and interval behavior (#6755)

* Implemented official natural recovery formula and interval behavior (fixes #6754)
* Fixed HP recovery per tick being 1 too high (after reaching 200 HP)
* The interval will now work similar to official servers where it remembers the time of the last recovery and checks if the interval has passed since that time
* The natural recovery interval will now be continuous even when at full health (i.e. if you are full while the interval passes and afterwards you lose HP/SP, you will have to wait for the next interval)
* Fixed anything blocking recovery, such as walking, not resetting the natural recovery tick (e.g. you have to wait 6 seconds to recover HP after you stop walking, unless you have moving recovery)
* Applies to players, homunculus, mercenaries and elementals
* Note: This also works with custom intervals, but you should make sure they are multiples of 4*NATURAL_HEAL_INTERVAL, otherwise it will round to the closest possible interval (you can reduce the timer interval in map.hpp when needed)
This commit is contained in:
Playtester 2022-03-30 12:13:42 +02:00 committed by GitHub
parent 630ca789c8
commit 414d43dd05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 35 deletions

View File

@ -227,6 +227,7 @@ void elemental_summon_init(s_elemental_data *ed) {
*/
int elemental_data_received(s_elemental *ele, bool flag) {
map_session_data *sd;
t_tick tick = gettick();
if( (sd = map_charid2sd(ele->char_id)) == NULL )
return 0;
@ -260,6 +261,10 @@ int elemental_data_received(s_elemental *ele, bool flag) {
ed->bl.x = ed->ud.to_x;
ed->bl.y = ed->ud.to_y;
// Ticks need to be initialized before adding bl to map_addiddb
ed->regen.tick.hp = tick;
ed->regen.tick.sp = tick;
map_addiddb(&ed->bl);
status_calc_elemental(ed,SCO_FIRST);
ed->last_spdrain_time = ed->last_thinktime = gettick();

View File

@ -1081,6 +1081,7 @@ void hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
{
struct homun_data *hd;
int i = 0;
t_tick tick = gettick();
nullpo_retv(sd);
@ -1115,6 +1116,10 @@ void hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
hd->bl.x = hd->ud.to_x;
hd->bl.y = hd->ud.to_y;
// Ticks need to be initialized before adding bl to map_addiddb
hd->regen.tick.hp = tick;
hd->regen.tick.sp = tick;
map_addiddb(&hd->bl);
status_calc_homunculus(hd, SCO_FIRST);

View File

@ -325,6 +325,7 @@ void merc_contract_init(s_mercenary_data *md) {
bool mercenary_recv_data(s_mercenary *merc, bool flag)
{
map_session_data *sd;
t_tick tick = gettick();
if( (sd = map_charid2sd(merc->char_id)) == NULL )
return false;
@ -359,6 +360,10 @@ bool mercenary_recv_data(s_mercenary *merc, bool flag)
md->bl.x = md->ud.to_x;
md->bl.y = md->ud.to_y;
// Ticks need to be initialized before adding bl to map_addiddb
md->regen.tick.hp = tick;
md->regen.tick.sp = tick;
map_addiddb(&md->bl);
status_calc_mercenary(md, SCO_FIRST);
md->contract_timer = INVALID_TIMER;

View File

@ -1684,6 +1684,9 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
sd->cansendmail_tick = tick;
sd->idletime = last_tick;
sd->regen.tick.hp = tick;
sd->regen.tick.sp = tick;
for(int i = 0; i < MAX_SPIRITBALL; i++)
sd->spirit_timer[i] = INVALID_TIMER;

View File

@ -4510,7 +4510,7 @@ void status_calc_regen(struct block_list *bl, struct status_data *status, struct
sd = BL_CAST(BL_PC,bl);
sc = status_get_sc(bl);
val = 1 + (status->vit/5) + (status->max_hp/200);
val = (status->vit/5) + max(1, status->max_hp/200);
if( sd && sd->hprecov_rate != 100 )
val = val*sd->hprecov_rate/100;
@ -14285,10 +14285,15 @@ static int status_natural_heal(struct block_list* bl, va_list args)
sd = BL_CAST(BL_PC,bl);
flag = regen->flag;
if (flag&RGN_HP && (status->hp >= status->max_hp || regen->state.block&1))
if (flag&RGN_HP && (regen->state.block&1))
flag &= ~(RGN_HP|RGN_SHP);
if (flag&RGN_SP && (status->sp >= status->max_sp || regen->state.block&2))
if (flag&RGN_SP && (regen->state.block&2))
flag &= ~(RGN_SP|RGN_SSP);
// Only skill-based regen is disabled at max HP/SP
if (flag&RGN_SHP && (status->hp >= status->max_hp))
flag &= ~RGN_SHP;
if (flag&RGN_SSP && (status->sp >= status->max_sp))
flag &= ~RGN_SSP;
if (flag && (
status_isdead(bl) ||
@ -14315,7 +14320,7 @@ static int status_natural_heal(struct block_list* bl, va_list args)
while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval) {
sregen->tick.hp -= battle_config.natural_heal_skill_interval;
if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp) { // Full
flag &= ~(RGN_HP|RGN_SHP);
flag &= ~RGN_SHP;
break;
}
}
@ -14328,7 +14333,7 @@ static int status_natural_heal(struct block_list* bl, va_list args)
while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval) {
sregen->tick.sp -= battle_config.natural_heal_skill_interval;
if(status_heal(bl, 0, sregen->sp, 3) < sregen->sp) { // Full
flag &= ~(RGN_SP|RGN_SSP);
flag &= ~RGN_SSP;
break;
}
}
@ -14346,9 +14351,6 @@ static int status_natural_heal(struct block_list* bl, va_list args)
flag &= ~RGN_HP;
}
if (!flag)
return 0;
if (flag&(RGN_HP|RGN_SP)) {
if(!vd)
vd = status_get_viewdata(bl);
@ -14360,49 +14362,56 @@ static int status_natural_heal(struct block_list* bl, va_list args)
// Natural Hp regen
if (flag&RGN_HP) {
rate = (int)(natural_heal_diff_tick * (regen->rate.hp/100. * multi));
// Interval to next recovery tick
rate = (int)(battle_config.natural_healhp_interval / (regen->rate.hp/100. * multi));
if (ud && ud->walktimer != INVALID_TIMER)
rate /= 2;
rate *= 2;
// Homun HP regen fix (they should regen as if they were sitting (twice as fast)
if(bl->type == BL_HOM)
rate *= 2;
rate /= 2;
regen->tick.hp += rate;
// Our timer system isn't 100% accurate so make sure we use the closest interval
rate -= NATURAL_HEAL_INTERVAL / 2;
if(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval) {
int val = 0;
do {
val += regen->hp;
regen->tick.hp -= battle_config.natural_healhp_interval;
} while(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval);
if (status_heal(bl, val, 0, 1) < val)
flag &= ~RGN_SHP; // Full.
if(regen->tick.hp + rate <= natural_heal_prev_tick) {
regen->tick.hp = natural_heal_prev_tick;
if (status->hp >= status->max_hp)
flag &= ~(RGN_HP | RGN_SHP);
else if (status_heal(bl, regen->hp, 0, 1) < regen->hp)
flag &= ~RGN_SHP; // Full
}
}
else {
regen->tick.hp = natural_heal_prev_tick;
}
// Natural SP regen
if(flag&RGN_SP) {
rate = (int)(natural_heal_diff_tick * (regen->rate.sp/100. * multi));
// 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)
if(bl->type==BL_HOM)
rate *= 2;
rate /= 2;
#ifdef RENEWAL
if (bl->type == BL_PC && (((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
if (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_MONK &&
sc && sc->data[SC_EXPLOSIONSPIRITS] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK))
rate /= 2; // Tick is doubled in Fury state
rate *= 2; // Tick is doubled in Fury state
#endif
regen->tick.sp += rate;
if(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval) {
int val = 0;
do {
val += regen->sp;
regen->tick.sp -= battle_config.natural_healsp_interval;
} while(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval);
if (status_heal(bl, 0, val, 1) < val)
flag &= ~RGN_SSP; // full.
// Our timer system isn't 100% accurate so make sure we use the closest interval
rate -= NATURAL_HEAL_INTERVAL / 2;
if(regen->tick.sp + rate <= natural_heal_prev_tick) {
regen->tick.sp = natural_heal_prev_tick;
if (status->sp >= status->max_sp)
flag &= ~(RGN_SP | RGN_SSP);
else if (status_heal(bl, 0, regen->sp, 1) < regen->sp)
flag &= ~RGN_SSP; // Full
}
}
else {
regen->tick.sp = natural_heal_prev_tick;
}
if (!regen->sregen)
return flag;
@ -14456,8 +14465,8 @@ static int status_natural_heal(struct block_list* bl, va_list args)
*/
static TIMER_FUNC(status_natural_heal_timer){
natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick);
map_foreachregen(status_natural_heal);
natural_heal_prev_tick = tick;
map_foreachregen(status_natural_heal);
return 0;
}

View File

@ -3012,7 +3012,8 @@ struct regen_data {
//tick accumulation before healing.
struct {
unsigned int hp,sp,shp,ssp;
t_tick hp, sp; //time of last natural recovery
unsigned int shp,ssp;
} tick;
//Regen rates. n/100