Update gain_skill function with timers

This commit is contained in:
kinther 2025-08-19 13:56:23 -07:00
parent 3560427902
commit cf621e02c1
4 changed files with 66 additions and 11 deletions

View file

@ -23,6 +23,7 @@
#include "fight.h"
#include "screen.h"
#include "mud_event.h"
#include <time.h>
/* local file scope function prototypes */
static int graf(int grafage, int p0, int p1, int p2, int p3, int p4, int p5, int p6);
@ -221,30 +222,65 @@ void run_autowiz(void)
#endif /* CIRCLE_UNIX || CIRCLE_WINDOWS */
}
/* Requires: find_skill_num(), GET_WIS(), wis_app[], GET_SKILL(), SET_SKILL(), rand_number()
* Cooldown: 1 hour - 5 * WIS bonus minutes (floored at 0)
* Rolls: failure -> 1..20, success -> 1..100
* Cap: 90% (change MIN(90, ...) if you want a different cap)
*/
void gain_skill(struct char_data *ch, char *skill, bool success)
{
int skill_num, base, roll, increase;
int wisb, cd_seconds;
time_t now;
if (IS_NPC(ch))
return;
/* Resolve index and validate against table size */
skill_num = find_skill_num(skill);
if (skill_num <= 0)
if (skill_num <= 0 || skill_num > MAX_SKILLS)
return;
base = GET_SKILL(ch, skill_num);
/* Respect per-skill cooldown: do nothing if still cooling down */
now = time(0);
if (GET_SKILL_NEXT_GAIN(ch, skill_num) != 0 &&
now < GET_SKILL_NEXT_GAIN(ch, skill_num)) {
return;
}
if (success) { /* learning from successes is more difficult */
roll = rand_number(1, 400);
if (roll >= (400 - (wis_app[GET_WIS(ch)].bonus) * 4)) {
increase = base + 1;
SET_SKILL(ch, skill_num, MIN(90, increase));
}
} else { /* learning from failures is easier */
base = GET_SKILL(ch, skill_num);
/* If already capped, bail early (and dont start cooldown) */
if (base >= 90)
return;
/* Wisdom bonus from wis_app[] (constants.c). Higher = better learning & shorter cooldown. */
wisb = wis_app[GET_WIS(ch)].bonus;
if (success) {
/* Learning from success is harder: 1..100, threshold scales with WIS */
roll = rand_number(1, 100);
if (roll >= (100 - wis_app[GET_WIS(ch)].bonus)) {
/* Old 1..400 with (400 - wisb*4) ⇒ scaled: (100 - wisb) */
if (roll >= (100 - wisb)) {
increase = base + 1;
SET_SKILL(ch, skill_num, MIN(90, increase));
/* Cooldown only when an increase actually happens */
cd_seconds = 3600 - (wisb * 5 * 60); /* 1 hour - 5 * WIS minutes */
if (cd_seconds < 0) cd_seconds = 0;
GET_SKILL_NEXT_GAIN(ch, skill_num) = now + cd_seconds;
}
} else {
/* Learning from failure is easier: 1..20, threshold scales with WIS */
roll = rand_number(1, 20);
/* Old 1..100 with (100 - wisb) ⇒ scaled: (20 - wisb) */
if (roll >= (20 - wisb)) {
increase = base + 1;
SET_SKILL(ch, skill_num, MIN(90, increase));
/* Cooldown only when an increase actually happens */
cd_seconds = 3600 - (wisb * 5 * 60); /* 1 hour - 5 * WIS minutes */
if (cd_seconds < 0) cd_seconds = 0;
GET_SKILL_NEXT_GAIN(ch, skill_num) = now + cd_seconds;
}
}
}

View file

@ -434,6 +434,14 @@ int load_char(const char *name, struct char_data *ch)
if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line);
else if (!strcmp(tag, "ScrW")) GET_SCREEN_WIDTH(ch) = atoi(line);
else if (!strcmp(tag, "Skil")) load_skills(fl, ch);
else if (!strcmp(tag, "SkGt")) { /* Skill Gain Timers */
for (int i = 1; i <= MAX_SKILLS; i++) {
long t = 0;
if (fscanf(fl, " %ld", &t) != 1)
t = 0;
GET_SKILL_NEXT_GAIN(ch, i) = (time_t)t;
}
}
else if (!strcmp(tag, "Str ")) load_HMVS(ch, line, LOAD_STRENGTH);
break;
@ -673,6 +681,12 @@ void save_char(struct char_data * ch)
fprintf(fl, "0 0\n");
}
/* Write per-skill next gain times as epoch seconds. */
fprintf(fl, "SkGt:"); /* Skill Gain Timer */
for (int i = 1; i <= MAX_SKILLS; i++)
fprintf(fl, " %ld", (long)GET_SKILL_NEXT_GAIN(ch, i));
fputc('\n', fl);
/* Save affects */
if (tmp_aff[0].spell > 0) {
fprintf(fl, "Affs:\n");

View file

@ -14,6 +14,7 @@
#include "protocol.h" /* Kavir Plugin*/
#include "lists.h"
#include <time.h>
/** If you want equipment to be automatically equipped to the same place
* it was when players rented, set the define below to 1 because
@ -968,6 +969,7 @@ struct player_special_data_saved
int quest_counter; /**< Count of targets left to get */
time_t lastmotd; /**< Last time player read motd */
time_t lastnews; /**< Last time player read news */
time_t next_skill_gain[MAX_SKILLS+1]; /* indexed by skill/spell number */
};
/** Specials needed only by PCs, not NPCs. Space for this structure is

View file

@ -130,7 +130,7 @@ void set_title(struct char_data *ch, char *title);
void gain_exp(struct char_data *ch, int gain);
void gain_exp_regardless(struct char_data *ch, int gain);
void gain_condition(struct char_data *ch, int condition, int value);
void gain_skill(struct char_data *ch, char *skill, bool failure);
void gain_skill(struct char_data *ch, char *skill, bool success);
void point_update(void);
void update_pos(struct char_data *victim);
void run_autowiz(void);
@ -617,6 +617,9 @@ do \
#define GET_SKILL(ch, i) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->saved.skills[i]))
/** Copy the current skill level i of ch to pct. */
#define SET_SKILL(ch, i, pct) do { CHECK_PLAYER_SPECIAL((ch), (ch)->player_specials->saved.skills[i]) = pct; } while(0)
/** Per-skill next gain time (epoch seconds). Index with a valid skill number. **/
#define GET_SKILL_NEXT_GAIN(ch, i) \
(CHECK_PLAYER_SPECIAL((ch), (ch)->player_specials->saved.next_skill_gain[(i)]))
/** The player's default sector type when buildwalking */
#define GET_BUILDWALK_SECTOR(ch) CHECK_PLAYER_SPECIAL((ch), ((ch)->player_specials->buildwalk_sector))