From 3344074ea2df1ad92a67d80a505dbf1f939f4bde Mon Sep 17 00:00:00 2001 From: kinther Date: Mon, 29 Dec 2025 18:08:53 -0800 Subject: [PATCH] Add age and time played functionality --- README.md | 24 +++++++++++++++---- lib/world/mob/1.mob | 54 ++++++++++++++++++++++++------------------- src/act.informative.c | 26 ++++++++++++--------- src/act.wizard.c | 25 ++++++++++---------- src/constants.c | 15 +++++++++++- src/db.c | 21 +++++++++++++++-- src/genmob.c | 8 +++++++ src/interpreter.c | 30 ++++++++++++++++++++++++ src/medit.c | 23 ++++++++++++++++++ src/oasis.h | 1 + src/players.c | 25 ++++++++++++++++++++ src/structs.h | 7 ++++-- src/utils.c | 19 ++++++++++----- src/utils.h | 6 +++++ 14 files changed, 222 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index adc073c..275df22 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -***Files for Miranthas MUD.*** +***Files for Cataclysm MUD.*** -Miranthas MUD is a continuation of tbaMUD/CircleMUD, which is built on DIKU MUD. -The code here is freeware to honor that tradition. +Cataclysm MUD is a continuation of tbaMUD/CircleMUD, which is built on DIKU MUD. +The code here is freeware to honor that tradition. Licensing and use should be based +on what was outlined previously. Any new code added here is released under the same +license. Due to the sensitive nature of topics found in this setting, all characters and players are 18+. The game world is derived from several inspirational sources, @@ -9,7 +11,7 @@ most notably the former Armageddon MUD. Roleplay is highly encouraged, but not enforced. -Features in Miranthas MUD Alpha release: +Features in Cataclysm MUD Alpha release: * The city of Caleran is available for exploration * Experience points and levels are removed in favor of skill based progression @@ -43,12 +45,22 @@ Features in Miranthas MUD Alpha release: * PC's now use a short description for identification instead of name * Backgrounds are now available for PC's and NPC's * Account system for tracking players/characters over long periods of time + +Alpha 1.1 release: + + * Cleaned up legacy practice system code + * Added skill caps for classes to limit ability of everyone to reach skill leve 100 (and respective proficiency) * Race/species selection and stat ranges (elves have higher dex, dwarves have higher str, etc) + * Renamed move to stamina in code to reflect how much energy is used for certain actions + * Species have base hit/mana/stamina now, plus their class modifier rolls * Prioritized stats during character generation * Ability to change ldesc of PC/NPC's in some situations + * Ability to look in certain directions to see what is 1-3 rooms away + * PC's and NPC's can now have an age set between 18-65 Features to be implemented in the next few releases: +* "acaudit" command to be "audit ac", allowing for further audit commands in the future * Subclass selection to personalize character further * Combat is slowed down so it isn't over in < 15 seconds (unless you're far outmatched) * Mounts added to help with long trips @@ -78,6 +90,10 @@ Features to be implemented in the next few releases: * Additional zones/cities based on Miranthas world map * Resources on the world map can be claimed by different city-states or independent factions * Claimed resources improve quality of armor/weapons/food/prices available +* Death from old age if you roll badly on your birthday after the expected lifespan of a species +* Attacks hit different parts of the body and have different damage effects +* Armor degradation based on damage taken per body part +* Weapon degradation based on damage dealt - potentially shattering weapons ...and down the road: diff --git a/lib/world/mob/1.mob b/lib/world/mob/1.mob index da2a3a5..6defa73 100644 --- a/lib/world/mob/1.mob +++ b/lib/world/mob/1.mob @@ -19,6 +19,7 @@ B + ~ 6218 0 0 0 0 0 0 0 0 E 1 3d20+40 @@ -37,17 +38,17 @@ Skill 145 5 Skill 146 5 Skill 147 5 E -L 17 127 1 -L 16 117 1 -L 15 117 1 -L 11 111 1 -L 10 107 1 -L 9 124 1 -L 8 115 1 -L 7 108 1 -L 6 110 1 -L 5 131 1 L 3 118 1 +L 5 131 1 +L 6 110 1 +L 7 108 1 +L 8 115 1 +L 9 124 1 +L 10 107 1 +L 11 111 1 +L 15 117 1 +L 16 117 1 +L 17 127 1 #101 Sally~ slim lanky human soldier guard~ @@ -70,6 +71,7 @@ B + ~ 6218 0 0 0 0 0 0 0 0 E 1 3d20+40 @@ -90,17 +92,17 @@ Skill 152 5 Skill 156 5 Skill 163 5 E -L 3 118 1 -L 5 131 1 -L 6 110 1 -L 7 108 1 -L 8 115 1 -L 9 124 1 -L 10 107 1 -L 11 111 1 -L 15 117 1 -L 16 117 1 L 17 127 1 +L 16 117 1 +L 15 117 1 +L 11 111 1 +L 10 107 1 +L 9 124 1 +L 8 115 1 +L 7 108 1 +L 6 110 1 +L 5 131 1 +L 3 118 1 #102 Baldy~ barkeep stocky bald~ @@ -123,13 +125,14 @@ B + ~ 10 0 0 0 0 0 0 0 0 E 1 3d12+60 8 8 1 E -L 9 112 1 L 14 113 1 +L 9 112 1 #103 Lanky~ woman lanky scarred~ @@ -153,6 +156,7 @@ B + ~ 10 0 0 0 0 0 0 0 0 E 1 3d8+60 @@ -186,18 +190,20 @@ B + It's a rat. ~ 8 0 0 0 0 0 0 0 0 E 1 0d0+10 8 8 2 -Str: 2 +Str: 3 Dex: 7 -Int: 1 +Int: 3 Wis: 8 Con: 8 -Cha: 1 +Cha: 3 Species: 25 +Age: 42 AtkT 4 E $ diff --git a/src/act.informative.c b/src/act.informative.c index 423929e..ef938ea 100644 --- a/src/act.informative.c +++ b/src/act.informative.c @@ -1254,7 +1254,10 @@ ACMD(do_coins) ACMD(do_score) { - struct time_info_data playing_time; + time_t played_seconds; + int played_minutes; + int played_hours; + int played_days; struct ac_breakdown acb; bool ismob = IS_NPC(ch); @@ -1299,12 +1302,9 @@ ACMD(do_score) send_to_char(ch, "Stealth Disadvantage: %s\r\n", has_stealth_disadv(ch) ? "Yes" : "No"); - send_to_char(ch, "You are %d years old.", GET_AGE(ch)); + send_to_char(ch, "You are %d years old.", GET_ROLEPLAY_AGE(ch)); - if (age(ch)->month == 0 && age(ch)->day == 0) - send_to_char(ch, " It's your birthday today.\r\n"); - else - send_to_char(ch, "\r\n"); + send_to_char(ch, "\r\n"); /* Only players have quest data */ if (!ismob) { @@ -1324,11 +1324,15 @@ ACMD(do_score) /* Only players have valid playtime data */ if (!ismob) { - playing_time = *real_time_passed((time(0) - ch->player.time.logon) + - ch->player.time.played, 0); - send_to_char(ch, "You have been playing for %d day%s and %d hour%s.\r\n", - playing_time.day, playing_time.day == 1 ? "" : "s", - playing_time.hours, playing_time.hours == 1 ? "" : "s"); + played_seconds = get_total_played_seconds(ch); + played_minutes = (played_seconds / SECS_PER_REAL_MIN) % 60; + played_hours = (played_seconds / SECS_PER_REAL_HOUR) % 24; + played_days = played_seconds / SECS_PER_REAL_DAY; + send_to_char(ch, + "You have been playing for %d minute%s, %d hour%s, and %d day%s.\r\n", + played_minutes, played_minutes == 1 ? "" : "s", + played_hours, played_hours == 1 ? "" : "s", + played_days, played_days == 1 ? "" : "s"); } /* Position */ diff --git a/src/act.wizard.c b/src/act.wizard.c index 1755b58..4b2ac78 100644 --- a/src/act.wizard.c +++ b/src/act.wizard.c @@ -1793,8 +1793,7 @@ static void do_stat_character(struct char_data *ch, struct char_data *k) zone_table[world[IN_ROOM(k)].zone].number); if (!IS_NPC(k)) { - char created[64], logon[64], olc[64] = ""; - strftime(created, sizeof(created), "%b %d %Y", localtime(&(k->player.time.birth))); + char logon[64], olc[64] = ""; strftime(logon, sizeof(logon), "%b %d %Y", localtime(&(k->player.time.logon))); if (GET_LEVEL(k) >= LVL_BUILDER) { @@ -1810,12 +1809,16 @@ static void do_stat_character(struct char_data *ch, struct char_data *k) snprintf(olc, sizeof(olc), ", OLC %d", GET_OLC_ZONE(k)); } - stat_table_row_fmt(ch, "Account", "Created %s, Last %s%s", - created, logon, olc); - stat_table_row_fmt(ch, "Age/Play", "Age %d, Played %dh %dm", - age(k)->year, - k->player.time.played / 3600, - (k->player.time.played % 3600) / 60); + stat_table_row_fmt(ch, "Account", "Last %s%s", + logon, olc); + { + time_t played_seconds = get_total_played_seconds(k); + int played_minutes = (played_seconds / SECS_PER_REAL_MIN) % 60; + int played_hours = (played_seconds / SECS_PER_REAL_HOUR) % 24; + int played_days = played_seconds / SECS_PER_REAL_DAY; + stat_table_row_fmt(ch, "Age/Playtime", "Age %d, %dd %dh %dm", + GET_ROLEPLAY_AGE(k), played_days, played_hours, played_minutes); + } } stat_table_row_fmt(ch, "Class", "%s", CLASS_NAME(k)); @@ -3864,10 +3867,8 @@ static int perform_set(struct char_data *ch, struct char_data *vict, int mode, c send_to_char(ch, "Ages 2 to 200 accepted.\r\n"); return (0); } - /* NOTE: May not display the exact age specified due to the integer - * division used elsewhere in the code. Seems to only happen for - * some values below the starting age (17) anyway. -gg 5/27/98 */ - vict->player.time.birth = time(0) - ((value - 17) * SECS_PER_MUD_YEAR); + GET_ROLEPLAY_AGE(vict) = LIMIT(value, MIN_CHAR_AGE, MAX_CHAR_AGE); + GET_ROLEPLAY_AGE_YEAR(vict) = time_info.year; break; case 3: /* align */ GET_ALIGNMENT(vict) = RANGE(-1000, 1000); diff --git a/src/constants.c b/src/constants.c index e26e49d..59de100 100644 --- a/src/constants.c +++ b/src/constants.c @@ -326,7 +326,9 @@ const char *connected_types[] = { "Get new PW", "Confirm new PW", "Select sex", + "Select species", "Select class", + "Short description", "Reading MOTD", "Main Menu", "Get descript.", @@ -350,7 +352,18 @@ const char *connected_types[] = { "Preference edit", "IBT edit", "Message edit", - "Protocol Detection", + "Background/Protocol", + "Connect menu", + "Get account", + "Confirm account", + "Account password", + "New account PW", + "Confirm account PW", + "Account email", + "Account menu", + "Account list", + "Stat preference", + "Select age", "\n" }; diff --git a/src/db.c b/src/db.c index 25634a6..9a5c124 100644 --- a/src/db.c +++ b/src/db.c @@ -1713,6 +1713,10 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n RANGE(SPECIES_UNDEFINED, NUM_SPECIES - 1); mob_proto[i].player.species = num_arg; } + CASE("Age") { + RANGE(MIN_CHAR_AGE, MAX_CHAR_AGE); + mob_proto[i].player.roleplay_age = num_arg; + } /* --- 5e-style Saving Throws --- */ CASE("SaveStr") { @@ -1963,6 +1967,13 @@ void parse_mobile(FILE *mob_f, int nr) exit(1); } + if (mob_proto[i].player.time.birth == 0) + mob_proto[i].player.time.birth = time(0); + if (mob_proto[i].player.roleplay_age == 0) + mob_proto[i].player.roleplay_age = MIN_CHAR_AGE; + if (mob_proto[i].player.roleplay_age_year == 0) + mob_proto[i].player.roleplay_age_year = time_info.year; + letter = fread_letter(mob_f); while (letter == 'L') { int wpos = -1, vnum = -1, qty = 1; @@ -2757,7 +2768,8 @@ struct char_data *read_mobile(mob_vnum nr, int type) /* and mob_rnum */ mob->points.mana = mob->points.max_mana; mob->points.stamina = mob->points.max_stamina; - mob->player.time.birth = time(0); + if (mob->player.time.birth == 0) + mob->player.time.birth = time(0); mob->player.time.played = 0; mob->player.time.logon = time(0); @@ -3843,7 +3855,12 @@ void init_char(struct char_data *ch) ch->player_specials->saved.completed_quests = NULL; GET_QUEST(ch) = NOTHING; - ch->player.time.birth = time(0); + if (ch->player.time.birth == 0) + ch->player.time.birth = time(0); + if (GET_ROLEPLAY_AGE(ch) == 0) + GET_ROLEPLAY_AGE(ch) = MIN_CHAR_AGE; + if (GET_ROLEPLAY_AGE_YEAR(ch) == 0) + GET_ROLEPLAY_AGE_YEAR(ch) = time_info.year; ch->player.time.logon = time(0); ch->player.time.played = 0; diff --git a/src/genmob.c b/src/genmob.c index 313ff08..4927b0b 100644 --- a/src/genmob.c +++ b/src/genmob.c @@ -363,6 +363,14 @@ int write_mobile_espec(mob_vnum mvnum, struct char_data *mob, FILE *fd) fprintf(fd, "Species: %d\n", (int)GET_SPECIES(mob)); count++; } + { + int age_years = GET_ROLEPLAY_AGE(mob); + if (age_years >= MIN_CHAR_AGE && age_years <= MAX_CHAR_AGE && + age_years != MIN_CHAR_AGE) { + fprintf(fd, "Age: %d\n", age_years); + count++; + } + } /* --- 5e-style saving throws --- */ if (GET_SAVE(mob, ABIL_STR) != 0) { diff --git a/src/interpreter.c b/src/interpreter.c index 0748ed4..8281f88 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -1323,6 +1323,11 @@ static void show_species_menu(struct descriptor_data *d) write_to_output(d, "Species: "); } +static void show_age_prompt(struct descriptor_data *d) +{ + write_to_output(d, "Age (%d-%d): ", MIN_CHAR_AGE, MAX_CHAR_AGE); +} + static bool is_creation_state(int state) { switch (state) { @@ -1334,6 +1339,7 @@ static bool is_creation_state(int state) case CON_QSEX: case CON_QSPECIES: case CON_QCLASS: + case CON_QAGE: case CON_QSTAT_PREF: case CON_QSHORTDESC: case CON_PLR_DESC: @@ -2019,9 +2025,33 @@ case CON_QCLASS: GET_CLASS(d->character) = load_result; } + show_age_prompt(d); + STATE(d) = CON_QAGE; + return; + +case CON_QAGE: { + if (!is_number(arg)) { + write_to_output(d, "\r\nPlease enter a number between %d and %d.\r\n", + MIN_CHAR_AGE, MAX_CHAR_AGE); + show_age_prompt(d); + return; + } + + int age_years = atoi(arg); + if (age_years < MIN_CHAR_AGE || age_years > MAX_CHAR_AGE) { + write_to_output(d, "\r\nAge must be between %d and %d.\r\n", + MIN_CHAR_AGE, MAX_CHAR_AGE); + show_age_prompt(d); + return; + } + + GET_ROLEPLAY_AGE(d->character) = age_years; + GET_ROLEPLAY_AGE_YEAR(d->character) = time_info.year; + show_stat_pref_prompt(d); STATE(d) = CON_QSTAT_PREF; return; +} case CON_QSTAT_PREF: { ubyte order[NUM_ABILITIES]; diff --git a/src/medit.c b/src/medit.c index e08ae3c..65406b3 100644 --- a/src/medit.c +++ b/src/medit.c @@ -241,6 +241,9 @@ static void init_mobile(struct char_data *mob) GET_MAX_MANA(mob) = GET_MAX_STAMINA(mob) = 100; GET_WEIGHT(mob) = 200; GET_HEIGHT(mob) = 198; + mob->player.time.birth = time(0); + mob->player.roleplay_age = MIN_CHAR_AGE; + mob->player.roleplay_age_year = time_info.year; /* Only assign defaults if the individual stat is unset (zero) */ if (!mob->real_abils.str) mob->real_abils.str = 11; @@ -436,6 +439,7 @@ static void medit_disp_menu(struct descriptor_data *d) "%s1%s) Name: %s%s\r\n" "%s2%s) Keywords: %s%s\r\n" "%s3%s) Sex: %s%-7.7s%s\r\n" + "%sG%s) Age: %s%d%s\r\n" "%s4%s) S-Desc: %s%s\r\n" "%s5%s) L-Desc:-\r\n%s%s\r\n" "%s6%s) D-Desc:-\r\n%s%s\r\n", @@ -444,6 +448,7 @@ static void medit_disp_menu(struct descriptor_data *d) grn, nrm, yel, GET_NAME(mob), grn, nrm, yel, GET_KEYWORDS(mob), grn, nrm, yel, genders[(int)GET_SEX(mob)], nrm, + grn, nrm, yel, GET_ROLEPLAY_AGE(mob), nrm, grn, nrm, yel, GET_SDESC(mob), grn, nrm, yel, GET_LDESC(mob), grn, nrm, yel, GET_DDESC(mob) @@ -764,6 +769,11 @@ void medit_parse(struct descriptor_data *d, char *arg) OLC_MODE(d) = MEDIT_SEX; medit_disp_sex(d); return; + case 'g': + case 'G': + OLC_MODE(d) = MEDIT_AGE; + write_to_output(d, "Enter age (%d-%d): ", MIN_CHAR_AGE, MAX_CHAR_AGE); + return; case '4': OLC_MODE(d) = MEDIT_S_DESC; i--; @@ -1308,6 +1318,19 @@ void medit_parse(struct descriptor_data *d, char *arg) GET_SEX(OLC_MOB(d)) = LIMIT(i - 1, 0, NUM_GENDERS - 1); break; + case MEDIT_AGE: + if (i < MIN_CHAR_AGE || i > MAX_CHAR_AGE) { + write_to_output(d, "Age must be between %d and %d.\r\n", + MIN_CHAR_AGE, MAX_CHAR_AGE); + write_to_output(d, "Enter age (%d-%d): ", MIN_CHAR_AGE, MAX_CHAR_AGE); + return; + } + GET_ROLEPLAY_AGE(OLC_MOB(d)) = i; + GET_ROLEPLAY_AGE_YEAR(OLC_MOB(d)) = time_info.year; + OLC_VAL(d) = TRUE; + medit_disp_menu(d); + return; + case MEDIT_NUM_HP_DICE: GET_HIT(OLC_MOB(d)) = LIMIT(i, 0, 30); OLC_VAL(d) = TRUE; diff --git a/src/oasis.h b/src/oasis.h index 6697cdb..582bcbf 100644 --- a/src/oasis.h +++ b/src/oasis.h @@ -274,6 +274,7 @@ extern const char *nrm, *grn, *cyn, *yel; /* Numerical responses. */ #define MEDIT_NUMERICAL_RESPONSE 15 #define MEDIT_SEX 16 +#define MEDIT_AGE 44 #define MEDIT_NUM_HP_DICE 17 #define MEDIT_SIZE_HP_DICE 18 #define MEDIT_ADD_HP 19 diff --git a/src/players.c b/src/players.c index a83094d..8e50050 100644 --- a/src/players.c +++ b/src/players.c @@ -222,6 +222,20 @@ char *get_name_by_id(long id) return (NULL); } +static void update_roleplay_age(struct char_data *ch) +{ + if (GET_ROLEPLAY_AGE(ch) == 0) + GET_ROLEPLAY_AGE(ch) = MIN_CHAR_AGE; + + if (GET_ROLEPLAY_AGE_YEAR(ch) == 0) + GET_ROLEPLAY_AGE_YEAR(ch) = time_info.year; + + if (time_info.year > GET_ROLEPLAY_AGE_YEAR(ch)) { + GET_ROLEPLAY_AGE(ch) += (time_info.year - GET_ROLEPLAY_AGE_YEAR(ch)); + GET_ROLEPLAY_AGE_YEAR(ch) = time_info.year; + } +} + /* Stuff related to the save/load player system. */ /* New load_char reads ASCII Player Files. Load a char, TRUE if loaded, FALSE * if not. */ @@ -260,6 +274,8 @@ int load_char(const char *name, struct char_data *ch) GET_LEVEL(ch) = PFDEF_LEVEL; GET_HEIGHT(ch) = PFDEF_HEIGHT; GET_WEIGHT(ch) = PFDEF_WEIGHT; + GET_ROLEPLAY_AGE(ch) = 0; + GET_ROLEPLAY_AGE_YEAR(ch) = 0; GET_ALIGNMENT(ch) = PFDEF_ALIGNMENT; for (i = 0; i < NUM_OF_SAVING_THROWS; i++) GET_SAVE(ch, i) = PFDEF_SAVETHROW; @@ -317,6 +333,8 @@ int load_char(const char *name, struct char_data *ch) switch (*tag) { case 'A': if (!strcmp(tag, "Ac ")) GET_AC(ch) = atoi(line); + else if (!strcmp(tag, "AgYr")) GET_ROLEPLAY_AGE_YEAR(ch) = atoi(line); + else if (!strcmp(tag, "Age ")) GET_ROLEPLAY_AGE(ch) = LIMIT(atoi(line), MIN_CHAR_AGE, MAX_CHAR_AGE); else if (!strcmp(tag, "Acct")) { if (GET_ACCOUNT(ch)) free(GET_ACCOUNT(ch)); @@ -515,6 +533,8 @@ int load_char(const char *name, struct char_data *ch) } } + update_roleplay_age(ch); + ch->player.time.birth = time(0) - get_total_played_seconds(ch); affect_total(ch); /* initialization for imms */ @@ -564,6 +584,9 @@ void save_char(struct char_data * ch) } } + update_roleplay_age(ch); + ch->player.time.birth = time(0) - get_total_played_seconds(ch); + if (!get_filename(filename, sizeof(filename), PLR_FILE, GET_NAME(ch))) return; if (!(fl = fopen(filename, "w"))) { @@ -631,6 +654,8 @@ void save_char(struct char_data * ch) fprintf(fl, "Id : %ld\n", GET_IDNUM(ch)); fprintf(fl, "Brth: %ld\n", (long)ch->player.time.birth); + fprintf(fl, "Age : %d\n", GET_ROLEPLAY_AGE(ch)); + fprintf(fl, "AgYr: %d\n", GET_ROLEPLAY_AGE_YEAR(ch)); fprintf(fl, "Plyd: %d\n", ch->player.time.played); fprintf(fl, "Last: %ld\n", (long)ch->player.time.logon); diff --git a/src/structs.h b/src/structs.h index febbccf..fdb09e0 100644 --- a/src/structs.h +++ b/src/structs.h @@ -348,6 +348,7 @@ #define CON_QSPECIES 8 /**< Choose character species */ #define CON_QCLASS 9 /**< Choose character class */ #define CON_QSTAT_PREF 44 /**< Choose character stat preference order */ +#define CON_QAGE 45 /**< Choose character age */ #define CON_QSHORTDESC 10 /**< Enter a new character short description prompt */ #define CON_RMOTD 11 /**< Reading the message of the day */ #define CON_MENU 12 /**< At the main menu */ @@ -906,7 +907,7 @@ struct time_info_data /** Player specific time information. */ struct time_data { - time_t birth; /**< Represents the PCs birthday, used to calculate age. */ + time_t birth; /**< Anchor for calculating mechanical age from played time. */ time_t logon; /**< Time of the last logon, used to calculate time played */ int played; /**< This is the total accumulated time played in secs */ }; @@ -943,7 +944,9 @@ struct char_player_data byte chclass; /**< PC / NPC class */ byte species; /**< PC / NPC species */ byte level; /**< PC / NPC level */ - struct time_data time; /**< PC AGE in days */ + struct time_data time; /**< Playtime tracking */ + int roleplay_age; /**< Roleplay age in years */ + int roleplay_age_year; /**< Last mud year roleplay age was updated */ ubyte weight; /**< PC / NPC weight */ ubyte height; /**< PC / NPC height */ }; diff --git a/src/utils.c b/src/utils.c index 43aaeb1..894c1e9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -570,17 +570,24 @@ time_t mud_time_to_secs(struct time_info_data *now) return (time(NULL) - when); } -/** Calculate a player's MUD age. - * @todo The minimum starting age of 17 is hardcoded in this function. Recommend - * changing the minimum age to a property (variable) external to this function. +time_t get_total_played_seconds(const struct char_data *ch) +{ + time_t played = ch->player.time.played; + + if (ch->desc && STATE(ch->desc) == CON_PLAYING) + played += time(0) - ch->player.time.logon; + + return played; +} + +/** Calculate a player's mechanical age based on total played time. * @param ch A valid player character. */ struct time_info_data *age(struct char_data *ch) { static struct time_info_data player_age; - player_age = *mud_time_passed(time(0), ch->player.time.birth); - - player_age.year += 17; /* All players start at 17 */ + time_t played = get_total_played_seconds(ch); + player_age = *mud_time_passed(time(0), time(0) - played); return (&player_age); } diff --git a/src/utils.h b/src/utils.h index a674a6d..8702064 100644 --- a/src/utils.h +++ b/src/utils.h @@ -54,6 +54,7 @@ void sprintbitarray(int bitvector[], const char *names[], int maxar, char *resul int get_line(FILE *fl, char *buf); int get_filename(char *filename, size_t fbufsize, int mode, const char *orig_name); time_t mud_time_to_secs(struct time_info_data *now); +time_t get_total_played_seconds(const struct char_data *ch); struct time_info_data *age(struct char_data *ch); int num_pc_in_room(struct room_data *room); void core_dump_real(const char *who, int line); @@ -236,6 +237,9 @@ void char_from_furniture(struct char_data *ch); * Current calculation ~= 12.4 real life days */ #define SECS_PER_MUD_YEAR (17*SECS_PER_MUD_MONTH) +#define MIN_CHAR_AGE 18 +#define MAX_CHAR_AGE 65 + /** The number of seconds in a real minute. */ #define SECS_PER_REAL_MIN 60 /** The number of seconds in a real hour. */ @@ -516,6 +520,8 @@ do \ #define GET_WAS_IN(ch) ((ch)->was_in_room) /** How old is PC/NPC, at last recorded time? */ #define GET_AGE(ch) (age(ch)->year) +#define GET_ROLEPLAY_AGE(ch) ((ch)->player.roleplay_age) +#define GET_ROLEPLAY_AGE_YEAR(ch) ((ch)->player.roleplay_age_year) /** Proper name for PCs and NPCs. */ #define GET_NAME(ch) ((ch)->player.name)