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:
parent
630ca789c8
commit
414d43dd05
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user