From 8018ed3fcf8a08d969fc359e0cd19efd89b00e32 Mon Sep 17 00:00:00 2001 From: kinther Date: Sun, 28 Dec 2025 16:56:57 -0800 Subject: [PATCH] Species update 1 --- lib/world/mob/1.mob | 57 +++--- src/act.wizard.c | 31 ++- src/class.c | 7 +- src/db.c | 6 + src/genmob.c | 4 + src/interpreter.c | 34 +++- src/medit.c | 75 +++++++- src/oasis.h | 59 +++--- src/pfdefaults.h | 1 + src/players.c | 10 +- src/species.c | 446 ++++++++++++++++++++++++++++++++++++++++++++ src/species.h | 29 +++ src/structs.h | 106 +++++++---- src/utils.h | 3 + 14 files changed, 773 insertions(+), 95 deletions(-) create mode 100644 src/species.c create mode 100644 src/species.h diff --git a/lib/world/mob/1.mob b/lib/world/mob/1.mob index 892c795..948687c 100644 --- a/lib/world/mob/1.mob +++ b/lib/world/mob/1.mob @@ -15,13 +15,14 @@ B + ~ 6218 0 0 0 0 0 0 0 0 E 1 3d20+40 8 8 1 Str: 16 Class: 3 -SaveStr: 3 +Species: 0 Skill 132 5 Skill 134 5 Skill 137 5 @@ -33,17 +34,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~ @@ -62,32 +63,38 @@ B + ~ 6218 0 0 0 0 0 0 0 0 E 1 3d20+40 8 8 2 +Class: 3 +Species: 0 Skill 132 5 Skill 134 5 Skill 137 5 +Skill 140 5 Skill 141 5 Skill 142 5 Skill 143 5 Skill 144 5 Skill 145 5 Skill 146 5 -Skill 147 5 +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~ @@ -106,13 +113,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~ @@ -132,6 +140,7 @@ B + ~ 10 0 0 0 0 0 0 0 0 E 1 3d8+60 @@ -161,6 +170,7 @@ appear thick and ready to propel the animal if it feels threatened. B + It's a rat. ~ 8 0 0 0 0 0 0 0 0 E @@ -172,6 +182,7 @@ Int: 4 Wis: 4 Con: 6 Cha: 3 +Species: 25 AtkT 4 E $ diff --git a/src/act.wizard.c b/src/act.wizard.c index f3943a0..ba56972 100644 --- a/src/act.wizard.c +++ b/src/act.wizard.c @@ -26,6 +26,7 @@ #include "act.h" #include "genzon.h" /* for real_zone_by_thing */ #include "class.h" +#include "species.h" #include "genmob.h" #include "genolc.h" #include "genobj.h" @@ -1818,6 +1819,7 @@ static void do_stat_character(struct char_data *ch, struct char_data *k) } stat_table_row_fmt(ch, "Class", "%s", CLASS_NAME(k)); + stat_table_row_fmt(ch, "Species", "%s", get_species_name(GET_SPECIES(k))); stat_table_row_fmt(ch, "Attributes", "Str %d Int %d Wis %d Dex %d Con %d Cha %d", @@ -3806,7 +3808,8 @@ static struct set_struct { { "weight", LVL_BUILDER, BOTH, NUMBER }, { "wis", LVL_BUILDER, BOTH, NUMBER }, { "questpoints", LVL_GOD, PC, NUMBER }, - { "questhistory", LVL_GOD, PC, NUMBER }, /* 55 */ + { "questhistory", LVL_GOD, PC, NUMBER }, + { "species", LVL_BUILDER, BOTH, MISC }, { "\n", 0, BOTH, MISC } }; @@ -4316,6 +4319,16 @@ static int perform_set(struct char_data *ch, struct char_data *vict, int mode, c } break; } + case 54: /* species */ + if ((i = parse_species(val_arg)) == SPECIES_UNDEFINED) { + send_to_char(ch, "That is not a species.\r\n"); + return (0); + } + update_species(vict, i); + affect_total(vict); + send_to_char(ch, "You set %s's species to %s.\r\n", + get_char_sdesc(vict), get_species_name(GET_SPECIES(vict))); + break; default: send_to_char(ch, "Can't set that!\r\n"); return (0); @@ -4402,7 +4415,21 @@ ACMD(do_set) } else if (!str_cmp(name, "mob")) half_chop(buf, name, buf); - half_chop(buf, field, buf); + if (!is_file && !is_player && !str_cmp(name, "species")) { + char val[MAX_INPUT_LENGTH]; + char target[MAX_INPUT_LENGTH]; + + half_chop(buf, val, target); + if (!*val || !*target) { + send_to_char(ch, "Usage: set species \r\n"); + return; + } + strlcpy(field, "species", sizeof(field)); + strlcpy(name, target, sizeof(name)); + strlcpy(buf, val, sizeof(buf)); + } else { + half_chop(buf, field, buf); + } if (!*name || !*field) { send_to_char(ch, "Usage: set \r\n"); diff --git a/src/class.c b/src/class.c index 02d0719..fafc7a0 100644 --- a/src/class.c +++ b/src/class.c @@ -23,6 +23,7 @@ #include "constants.h" #include "act.h" #include "class.h" +#include "species.h" /* Names first */ const char *class_abbrevs[] = { @@ -231,7 +232,10 @@ void roll_real_abils(struct char_data *ch) ch->real_abils.cha = table[5]; break; } - ch->aff_abils = ch->real_abils; + if (HAS_VALID_SPECIES(ch)) + apply_species_bonuses(ch); + else + ch->aff_abils = ch->real_abils; } /* Per-class skill caps */ @@ -489,6 +493,7 @@ void do_start(struct char_data *ch) GET_MAX_MOVE(ch) = 90; grant_class_skills(ch, TRUE); + grant_species_skills(ch); advance_level(ch); diff --git a/src/db.c b/src/db.c index 8f76516..1cd9b68 100644 --- a/src/db.c +++ b/src/db.c @@ -1624,6 +1624,7 @@ static void parse_simple_mob(FILE *mob_f, int i, int nr) GET_SEX(mob_proto + i) = t[2]; GET_CLASS(mob_proto + i) = CLASS_UNDEFINED; + GET_SPECIES(mob_proto + i) = SPECIES_UNDEFINED; GET_WEIGHT(mob_proto + i) = 200; GET_HEIGHT(mob_proto + i) = 198; @@ -1707,6 +1708,10 @@ static void interpret_espec(const char *keyword, const char *value, int i, int n RANGE(CLASS_UNDEFINED, NUM_CLASSES - 1); mob_proto[i].player.chclass = num_arg; } + CASE("Species") { + RANGE(SPECIES_UNDEFINED, NUM_SPECIES - 1); + mob_proto[i].player.species = num_arg; + } /* --- 5e-style Saving Throws --- */ CASE("SaveStr") { @@ -3766,6 +3771,7 @@ void clear_char(struct char_data *ch) GET_PFILEPOS(ch) = -1; GET_MOB_RNUM(ch) = NOBODY; GET_CLASS(ch) = CLASS_UNDEFINED; + GET_SPECIES(ch) = SPECIES_UNDEFINED; GET_WAS_IN(ch) = NOWHERE; GET_POS(ch) = POS_STANDING; ch->mob_specials.default_pos = POS_STANDING; diff --git a/src/genmob.c b/src/genmob.c index 8722763..2285a5e 100644 --- a/src/genmob.c +++ b/src/genmob.c @@ -359,6 +359,10 @@ int write_mobile_espec(mob_vnum mvnum, struct char_data *mob, FILE *fd) fprintf(fd, "Class: %d\n", (int)GET_CLASS(mob)); count++; } + if (HAS_VALID_SPECIES(mob)) { + fprintf(fd, "Species: %d\n", (int)GET_SPECIES(mob)); + count++; + } /* --- 5e-style saving throws --- */ if (GET_SAVE(mob, ABIL_STR) != 0) { diff --git a/src/interpreter.c b/src/interpreter.c index 33f0938..073aaf0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -27,6 +27,7 @@ #include "act.h" /* ACMDs located within the act*.c files */ #include "ban.h" #include "class.h" +#include "species.h" #include "graph.h" #include "hedit.h" #include "house.h" @@ -47,6 +48,7 @@ static int _parse_name(char *arg, char *name); static bool perform_new_char_dupe_check(struct descriptor_data *d); /* sort_commands utility */ static int sort_commands_helper(const void *a, const void *b); +static void show_species_menu(struct descriptor_data *d); /* globals defined here, used here and elsewhere */ int *cmd_sort_info = NULL; @@ -1303,6 +1305,18 @@ static bool perform_new_char_dupe_check(struct descriptor_data *d) return (found); } +static void show_species_menu(struct descriptor_data *d) +{ + int count = pc_species_count(); + + write_to_output(d, "Select a species:\r\n"); + for (int i = 0; i < count; i++) { + int species = pc_species_list[i]; + write_to_output(d, " %2d) %s\r\n", i + 1, species_types[species]); + } + write_to_output(d, "Species: "); +} + /* load the player, put them in the right room - used by copyover_recover too */ int enter_player_game (struct descriptor_data *d) { @@ -1869,10 +1883,26 @@ void nanny(struct descriptor_data *d, char *arg) return; } - write_to_output(d, "%s\r\nClass: ", class_menu); - STATE(d) = CON_QCLASS; + show_species_menu(d); + STATE(d) = CON_QSPECIES; break; +case CON_QSPECIES: { + int choice = atoi(arg); + int species = species_from_pc_choice(choice); + + if (species == SPECIES_UNDEFINED) { + write_to_output(d, "\r\nThat's not a species.\r\n"); + show_species_menu(d); + return; + } + + GET_SPECIES(d->character) = species; + write_to_output(d, "%s\r\nClass: ", class_menu); + STATE(d) = CON_QCLASS; + break; +} + case CON_QCLASS: load_result = parse_class(*arg); if (load_result == CLASS_UNDEFINED) { diff --git a/src/medit.c b/src/medit.c index 32ada44..637263f 100644 --- a/src/medit.c +++ b/src/medit.c @@ -13,6 +13,7 @@ #include "comm.h" #include "spells.h" #include "class.h" +#include "species.h" #include "db.h" #include "shop.h" #include "genolc.h" @@ -41,6 +42,7 @@ static void medit_disp_mob_flags(struct descriptor_data *d); static void medit_disp_aff_flags(struct descriptor_data *d); static void medit_disp_menu(struct descriptor_data *d); static void medit_disp_class_menu(struct descriptor_data *d); +static void medit_disp_species_menu(struct descriptor_data *d); /* utility functions */ ACMD(do_oasis_medit) @@ -419,13 +421,14 @@ static void medit_disp_menu(struct descriptor_data *d) { struct char_data *mob; char flags[MAX_STRING_LENGTH], flag2[MAX_STRING_LENGTH]; - const char *background, *classname; + const char *background, *classname, *speciesname; mob = OLC_MOB(d); get_char_colors(d->character); clear_screen(d); background = GET_BACKGROUND(mob) ? GET_BACKGROUND(mob) : "\r\n"; classname = HAS_VALID_CLASS(mob) ? pc_class_types[GET_CLASS(mob)] : "Unassigned"; + speciesname = HAS_VALID_SPECIES(mob) ? species_types[GET_SPECIES(mob)] : "Unassigned"; write_to_output(d, "-- Mob Number: [%s%d%s]\r\n" @@ -452,6 +455,7 @@ static void medit_disp_menu(struct descriptor_data *d) "%s8%s) Default : %s%s\r\n" "%s9%s) Attack : %s%s\r\n" "%sD%s) Class : %s%s\r\n" + "%sE%s) Species : %s%s\r\n" "%sK%s) Skinning Menu...\r\n" "%s0%s) Stats Menu...\r\n" "%s-%s) Skills Menu...\r\n" @@ -468,6 +472,7 @@ static void medit_disp_menu(struct descriptor_data *d) grn, nrm, yel, position_types[(int)GET_DEFAULT_POS(mob)], grn, nrm, yel, attack_hit_text[(int)GET_ATTACK(mob)].singular, grn, nrm, yel, classname, + grn, nrm, yel, speciesname, grn, nrm, grn, nrm, grn, nrm, @@ -523,6 +528,46 @@ static void medit_disp_class_menu(struct descriptor_data *d) OLC_MODE(d) = MEDIT_CLASS_MENU; } +static void medit_disp_species_menu(struct descriptor_data *d) +{ + struct char_data *mob = OLC_MOB(d); + const char *current = HAS_VALID_SPECIES(mob) ? species_types[GET_SPECIES(mob)] : "Unassigned"; + + get_char_colors(d->character); + clear_screen(d); + + write_to_output(d, + "-- Mob Number: %s[%s%d%s]%s\r\n" + "Species selection for %s%s%s\r\n\r\n", + cyn, yel, OLC_NUM(d), cyn, nrm, + yel, GET_SDESC(mob), nrm); + + for (int i = 0; i < NUM_SPECIES; i++) { + bool selected = HAS_VALID_SPECIES(mob) && (GET_SPECIES(mob) == i); + write_to_output(d, "%s%2d%s) %s%-12s%s%s\r\n", + cyn, i + 1, nrm, + selected ? grn : yel, + species_types[i], + nrm, + selected ? " (current)" : ""); + } + + write_to_output(d, "%s%2d%s) %sUnassigned%s%s\r\n", + cyn, NUM_SPECIES + 1, nrm, + !HAS_VALID_SPECIES(mob) ? grn : yel, + nrm, + !HAS_VALID_SPECIES(mob) ? " (current)" : ""); + + write_to_output(d, + "\r\nCurrent choice: %s%s%s\r\n" + "%s0%s) Return to main menu\r\n" + "Enter choice : ", + cyn, current, nrm, + cyn, nrm); + + OLC_MODE(d) = MEDIT_SPECIES_MENU; +} + /* Display main menu. */ static void medit_disp_stats_menu(struct descriptor_data *d) { @@ -757,6 +802,10 @@ void medit_parse(struct descriptor_data *d, char *arg) case 'D': medit_disp_class_menu(d); return; + case 'e': + case 'E': + medit_disp_species_menu(d); + return; case '0': OLC_MODE(d) = MEDIT_STATS_MENU; medit_disp_stats_menu(d); @@ -1165,6 +1214,30 @@ void medit_parse(struct descriptor_data *d, char *arg) medit_disp_menu(d); return; + case MEDIT_SPECIES_MENU: + i = atoi(arg); + if (i == 0) { + medit_disp_menu(d); + return; + } + if (i == NUM_SPECIES + 1) { + GET_SPECIES(OLC_MOB(d)) = SPECIES_UNDEFINED; + OLC_VAL(d) = TRUE; + write_to_output(d, "Species cleared.\r\n"); + medit_disp_menu(d); + return; + } + if (i < 1 || i > NUM_SPECIES + 1) { + write_to_output(d, "Invalid choice!\r\n"); + medit_disp_species_menu(d); + return; + } + GET_SPECIES(OLC_MOB(d)) = i - 1; + OLC_VAL(d) = TRUE; + write_to_output(d, "Species set to %s.\r\n", species_types[GET_SPECIES(OLC_MOB(d))]); + medit_disp_menu(d); + return; + case OLC_SCRIPT_EDIT: if (dg_script_edit_parse(d, arg)) return; break; diff --git a/src/oasis.h b/src/oasis.h index db6f738..6697cdb 100644 --- a/src/oasis.h +++ b/src/oasis.h @@ -269,39 +269,40 @@ extern const char *nrm, *grn, *cyn, *yel; #define MEDIT_SKILL_MENU 11 #define MEDIT_SKILL_EDIT 12 #define MEDIT_CLASS_MENU 13 +#define MEDIT_SPECIES_MENU 14 /* Numerical responses. */ -#define MEDIT_NUMERICAL_RESPONSE 14 -#define MEDIT_SEX 15 -#define MEDIT_NUM_HP_DICE 16 -#define MEDIT_SIZE_HP_DICE 17 -#define MEDIT_ADD_HP 18 -#define MEDIT_POS 19 -#define MEDIT_DEFAULT_POS 20 -#define MEDIT_ATTACK 21 -#define MEDIT_LEVEL 22 -#define MEDIT_ALIGNMENT 23 -#define MEDIT_DELETE 24 -#define MEDIT_COPY 25 -#define MEDIT_STR 26 -#define MEDIT_INT 27 -#define MEDIT_WIS 28 -#define MEDIT_DEX 29 -#define MEDIT_CON 30 -#define MEDIT_CHA 31 -#define MEDIT_SAVE_STR 32 -#define MEDIT_SAVE_DEX 33 -#define MEDIT_SAVE_CON 34 -#define MEDIT_SAVE_INT 35 -#define MEDIT_SAVE_WIS 36 -#define MEDIT_SAVE_CHA 37 -#define MEDIT_SKILL_VALUE 38 +#define MEDIT_NUMERICAL_RESPONSE 15 +#define MEDIT_SEX 16 +#define MEDIT_NUM_HP_DICE 17 +#define MEDIT_SIZE_HP_DICE 18 +#define MEDIT_ADD_HP 19 +#define MEDIT_POS 20 +#define MEDIT_DEFAULT_POS 21 +#define MEDIT_ATTACK 22 +#define MEDIT_LEVEL 23 +#define MEDIT_ALIGNMENT 24 +#define MEDIT_DELETE 25 +#define MEDIT_COPY 26 +#define MEDIT_STR 27 +#define MEDIT_INT 28 +#define MEDIT_WIS 29 +#define MEDIT_DEX 30 +#define MEDIT_CON 31 +#define MEDIT_CHA 32 +#define MEDIT_SAVE_STR 33 +#define MEDIT_SAVE_DEX 34 +#define MEDIT_SAVE_CON 35 +#define MEDIT_SAVE_INT 36 +#define MEDIT_SAVE_WIS 37 +#define MEDIT_SAVE_CHA 38 +#define MEDIT_SKILL_VALUE 39 /* Skinning yield editor */ -#define MEDIT_SKIN_MENU 39 -#define MEDIT_SKIN_ADD_VNUM 40 -#define MEDIT_SKIN_ADD_DC 41 -#define MEDIT_SKIN_DELETE 42 +#define MEDIT_SKIN_MENU 40 +#define MEDIT_SKIN_ADD_VNUM 41 +#define MEDIT_SKIN_ADD_DC 42 +#define MEDIT_SKIN_DELETE 43 /* Submodes of SEDIT connectedness. */ #define SEDIT_MAIN_MENU 0 diff --git a/src/pfdefaults.h b/src/pfdefaults.h index afd4ba9..4771360 100644 --- a/src/pfdefaults.h +++ b/src/pfdefaults.h @@ -15,6 +15,7 @@ #define PFDEF_SEX 0 #define PFDEF_CLASS 0 +#define PFDEF_SPECIES SPECIES_UNDEFINED #define PFDEF_LEVEL 0 #define PFDEF_HEIGHT 0 #define PFDEF_WEIGHT 0 diff --git a/src/players.c b/src/players.c index ce06c33..29866e8 100644 --- a/src/players.c +++ b/src/players.c @@ -255,6 +255,7 @@ int load_char(const char *name, struct char_data *ch) ch->player_specials->saved.skills[i] = 0; GET_SEX(ch) = PFDEF_SEX; GET_CLASS(ch) = PFDEF_CLASS; + GET_SPECIES(ch) = PFDEF_SPECIES; GET_LEVEL(ch) = PFDEF_LEVEL; GET_HEIGHT(ch) = PFDEF_HEIGHT; GET_WEIGHT(ch) = PFDEF_WEIGHT; @@ -437,7 +438,13 @@ int load_char(const char *name, struct char_data *ch) break; case 'S': - if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line); + if (!strcmp(tag, "Spec")) { + int val = atoi(line); + if (val < SPECIES_UNDEFINED || val >= NUM_SPECIES) + val = SPECIES_UNDEFINED; + GET_SPECIES(ch) = val; + } + else if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line); else if (!strcmp(tag, "Sdsc")) { /* Clear any existing sdesc to avoid leaks */ if (GET_SHORT_DESC(ch)) @@ -614,6 +621,7 @@ void save_char(struct char_data * ch) if (POOFOUT(ch)) fprintf(fl, "PfOt: %s\n", POOFOUT(ch)); if (GET_SEX(ch) != PFDEF_SEX) fprintf(fl, "Sex : %d\n", GET_SEX(ch)); if (GET_CLASS(ch) != PFDEF_CLASS) fprintf(fl, "Clas: %d\n", GET_CLASS(ch)); + if (GET_SPECIES(ch) != PFDEF_SPECIES) fprintf(fl, "Spec: %d\n", GET_SPECIES(ch)); if (GET_LEVEL(ch) != PFDEF_LEVEL) fprintf(fl, "Levl: %d\n", GET_LEVEL(ch)); fprintf(fl, "Id : %ld\n", GET_IDNUM(ch)); diff --git a/src/species.c b/src/species.c new file mode 100644 index 0000000..c94afde --- /dev/null +++ b/src/species.c @@ -0,0 +1,446 @@ +/** +* @file species.c +* Race/species related configuration, skill bonuses, modifiers, and stat limitations. +* +* This set of code was not originally part of the circlemud distribution. +*/ + +#include "conf.h" +#include "sysdep.h" + +#include "structs.h" +#include "utils.h" +#include "db.h" +#include "spells.h" +#include "species.h" + +struct species_skill_bonus { + int skill; + int start; +}; + +/* Keep species ordering in sync with the SPECIES_* defines. */ +const char *species_types[NUM_SPECIES] = { + "Human", + "City Elf", + "Desert Elf", + "Half-Elf", + "Dwarf", + "Mul", + "Half-Giant", + "Mantis", + "Gith", + "Aarakocra", + "Dray", + "Kenku", + "Jozhal", + "Pterran", + "Tarek", + "Aprig", + "Carru", + "Crodlu", + "Erdlu", + "Inix", + "Jhakar", + "Kank", + "Mekillot", + "Worm", + "Renk", + "Rat", + "Undead", + "Dragon" +}; + +/* PC creation options (1-based ordering in menus). */ +const int pc_species_list[] = { + SPECIES_HUMAN, + SPECIES_CITY_ELF, + SPECIES_DESERT_ELF, + SPECIES_HALF_ELF, + SPECIES_DWARF, + SPECIES_MUL, + SPECIES_HALF_GIANT, + SPECIES_MANTIS, + SPECIES_GITH, + -1 +}; + +static const struct species_skill_bonus species_skill_none[] = { + { -1, 0 } +}; + +/* Per-species skill bonuses. + * Usage: + * 1) Define a bonus list for a species (see commented example below). + * 2) In species_skill_bonuses[], point that species at the new list. + * 3) Keep the { -1, 0 } terminator as the final entry. + * + * Example (commented out): + * + * static const struct species_skill_bonus species_skill_human[] = { + * { SKILL_PERCEPTION, 5 }, + * { -1, 0 } + * }; + */ +static const struct species_skill_bonus *species_skill_bonuses[NUM_SPECIES] = { + /* species_skill_human, */ /* Human (example if you enable the list above) */ + species_skill_none, /* Human */ + species_skill_none, /* City Elf */ + species_skill_none, /* Desert Elf */ + species_skill_none, /* Half-Elf */ + species_skill_none, /* Dwarf */ + species_skill_none, /* Mul */ + species_skill_none, /* Half-Giant */ + species_skill_none, /* Mantis */ + species_skill_none, /* Gith */ + species_skill_none, /* Aarakocra */ + species_skill_none, /* Dray */ + species_skill_none, /* Kenku */ + species_skill_none, /* Jozhal */ + species_skill_none, /* Pterran */ + species_skill_none, /* Tarek */ + species_skill_none, /* Aprig */ + species_skill_none, /* Carru */ + species_skill_none, /* Crodlu */ + species_skill_none, /* Erdlu */ + species_skill_none, /* Inix */ + species_skill_none, /* Jhakar */ + species_skill_none, /* Kank */ + species_skill_none, /* Mekillot */ + species_skill_none, /* Worm */ + species_skill_none, /* Renk */ + species_skill_none, /* Rat */ + species_skill_none, /* Undead */ + species_skill_none /* Dragon */ +}; + +/* Ability minimums by species (STR, DEX, CON, INT, WIS, CHA). Zero = no min. */ +static const int species_ability_mins[NUM_SPECIES][NUM_ABILITIES] = { + { 0, 0, 0, 0, 0, 0 }, /* Human */ + { 0, 0, 0, 0, 0, 0 }, /* City Elf */ + { 0, 0, 0, 0, 0, 0 }, /* Desert Elf */ + { 0, 0, 0, 0, 0, 0 }, /* Half-Elf */ + { 0, 0, 0, 0, 0, 0 }, /* Dwarf */ + { 0, 0, 0, 0, 0, 0 }, /* Mul */ + { 0, 0, 0, 0, 0, 0 }, /* Half-Giant */ + { 0, 0, 0, 0, 0, 0 }, /* Mantis */ + { 0, 0, 0, 0, 0, 0 }, /* Gith */ + { 0, 0, 0, 0, 0, 0 }, /* Aarakocra */ + { 0, 0, 0, 0, 0, 0 }, /* Dray */ + { 0, 0, 0, 0, 0, 0 }, /* Kenku */ + { 0, 0, 0, 0, 0, 0 }, /* Jozhal */ + { 0, 0, 0, 0, 0, 0 }, /* Pterran */ + { 0, 0, 0, 0, 0, 0 }, /* Tarek */ + { 0, 0, 0, 0, 0, 0 }, /* Aprig */ + { 0, 0, 0, 0, 0, 0 }, /* Carru */ + { 0, 0, 0, 0, 0, 0 }, /* Crodlu */ + { 0, 0, 0, 0, 0, 0 }, /* Erdlu */ + { 0, 0, 0, 0, 0, 0 }, /* Inix */ + { 0, 0, 0, 0, 0, 0 }, /* Jhakar */ + { 0, 0, 0, 0, 0, 0 }, /* Kank */ + { 0, 0, 0, 0, 0, 0 }, /* Mekillot */ + { 0, 0, 0, 0, 0, 0 }, /* Worm */ + { 0, 0, 0, 0, 0, 0 }, /* Renk */ + { 0, 0, 0, 0, 0, 0 }, /* Rat */ + { 0, 0, 0, 0, 0, 0 }, /* Undead */ + { 0, 0, 0, 0, 0, 0 } /* Dragon */ +}; + +/* Ability modifiers by species (STR, DEX, CON, INT, WIS, CHA). */ +static const int species_ability_mods[NUM_SPECIES][NUM_ABILITIES] = { + { 0, 0, 0, 0, 0, 0 }, /* Human */ + { 0, 0, 0, 0, 0, 0 }, /* City Elf */ + { 0, 0, 0, 0, 0, 0 }, /* Desert Elf */ + { 0, 0, 0, 0, 0, 0 }, /* Half-Elf */ + { 0, 0, 0, 0, 0, 0 }, /* Dwarf */ + { 0, 0, 0, 0, 0, 0 }, /* Mul */ + { 0, 0, 0, 0, 0, 0 }, /* Half-Giant */ + { 0, 0, 0, 0, 0, 0 }, /* Mantis */ + { 0, 0, 0, 0, 0, 0 }, /* Gith */ + { 0, 0, 0, 0, 0, 0 }, /* Aarakocra */ + { 0, 0, 0, 0, 0, 0 }, /* Dray */ + { 0, 0, 0, 0, 0, 0 }, /* Kenku */ + { 0, 0, 0, 0, 0, 0 }, /* Jozhal */ + { 0, 0, 0, 0, 0, 0 }, /* Pterran */ + { 0, 0, 0, 0, 0, 0 }, /* Tarek */ + { 0, 0, 0, 0, 0, 0 }, /* Aprig */ + { 0, 0, 0, 0, 0, 0 }, /* Carru */ + { 0, 0, 0, 0, 0, 0 }, /* Crodlu */ + { 0, 0, 0, 0, 0, 0 }, /* Erdlu */ + { 0, 0, 0, 0, 0, 0 }, /* Inix */ + { 0, 0, 0, 0, 0, 0 }, /* Jhakar */ + { 0, 0, 0, 0, 0, 0 }, /* Kank */ + { 0, 0, 0, 0, 0, 0 }, /* Mekillot */ + { 0, 0, 0, 0, 0, 0 }, /* Worm */ + { 0, 0, 0, 0, 0, 0 }, /* Renk */ + { 0, 0, 0, 0, 0, 0 }, /* Rat */ + { 0, 0, 0, 0, 0, 0 }, /* Undead */ + { 0, 0, 0, 0, 0, 0 } /* Dragon */ +}; + +/* Ability caps by species (STR, DEX, CON, INT, WIS, CHA). Zero = no cap. */ +static const int species_ability_caps[NUM_SPECIES][NUM_ABILITIES] = { + { 16, 0, 0, 0, 0, 0 }, /* Human */ + { 14, 0, 0, 0, 0, 0 }, /* City Elf */ + { 14, 0, 0, 0, 0, 0 }, /* Desert Elf */ + { 14, 0, 0, 0, 0, 0 }, /* Half-Elf */ + { 18, 0, 0, 0, 0, 0 }, /* Dwarf */ + { 20, 0, 0, 0, 0, 0 }, /* Mul */ + { 24, 0, 0, 0, 0, 0 }, /* Half-Giant */ + { 0, 0, 0, 0, 0, 0 }, /* Mantis */ + { 0, 0, 0, 0, 0, 0 }, /* Gith */ + { 0, 0, 0, 0, 0, 0 }, /* Aarakocra */ + { 0, 0, 0, 0, 0, 0 }, /* Dray */ + { 0, 0, 0, 0, 0, 0 }, /* Kenku */ + { 0, 0, 0, 0, 0, 0 }, /* Jozhal */ + { 0, 0, 0, 0, 0, 0 }, /* Pterran */ + { 0, 0, 0, 0, 0, 0 }, /* Tarek */ + { 0, 0, 0, 0, 0, 0 }, /* Aprig */ + { 0, 0, 0, 0, 0, 0 }, /* Carru */ + { 0, 0, 0, 0, 0, 0 }, /* Crodlu */ + { 0, 0, 0, 0, 0, 0 }, /* Erdlu */ + { 0, 0, 0, 0, 0, 0 }, /* Inix */ + { 0, 0, 0, 0, 0, 0 }, /* Jhakar */ + { 0, 0, 0, 0, 0, 0 }, /* Kank */ + { 0, 0, 0, 0, 0, 0 }, /* Mekillot */ + { 0, 0, 0, 0, 0, 0 }, /* Worm */ + { 0, 0, 0, 0, 0, 0 }, /* Renk */ + { 0, 0, 0, 0, 0, 0 }, /* Rat */ + { 0, 0, 0, 0, 0, 0 }, /* Undead */ + { 0, 0, 0, 0, 0, 0 } /* Dragon */ +}; + +const char *get_species_name(int species) +{ + if (species >= 0 && species < NUM_SPECIES) + return species_types[species]; + return "Unassigned"; +} + +int pc_species_count(void) +{ + int count = 0; + while (pc_species_list[count] != -1) + count++; + return count; +} + +bool species_is_pc_selectable(int species) +{ + int i = 0; + + while (pc_species_list[i] != -1) { + if (pc_species_list[i] == species) + return TRUE; + i++; + } + return FALSE; +} + +int species_from_pc_choice(int choice) +{ + int count = pc_species_count(); + + if (choice < 1 || choice > count) + return SPECIES_UNDEFINED; + return pc_species_list[choice - 1]; +} + +int species_ability_mod(int species, int ability) +{ + if (species < 0 || species >= NUM_SPECIES) + return 0; + if (ability < 0 || ability >= NUM_ABILITIES) + return 0; + return species_ability_mods[species][ability]; +} + +int species_ability_min(int species, int ability) +{ + if (species < 0 || species >= NUM_SPECIES) + return 0; + if (ability < 0 || ability >= NUM_ABILITIES) + return 0; + return species_ability_mins[species][ability]; +} + +int species_ability_cap(int species, int ability) +{ + if (species < 0 || species >= NUM_SPECIES) + return 0; + if (ability < 0 || ability >= NUM_ABILITIES) + return 0; + return species_ability_caps[species][ability]; +} + +static void apply_species_ranges(struct char_data *ch) +{ + int species; + int cap, min; + + if (!ch) + return; + + species = GET_SPECIES(ch); + if (species < 0 || species >= NUM_SPECIES) + return; + + min = species_ability_min(species, ABIL_STR); + if (min > 0 && ch->real_abils.str < min) + ch->real_abils.str = min; + cap = species_ability_cap(species, ABIL_STR); + if (cap > 0 && ch->real_abils.str > cap) + ch->real_abils.str = cap; + + min = species_ability_min(species, ABIL_DEX); + if (min > 0 && ch->real_abils.dex < min) + ch->real_abils.dex = min; + cap = species_ability_cap(species, ABIL_DEX); + if (cap > 0 && ch->real_abils.dex > cap) + ch->real_abils.dex = cap; + + min = species_ability_min(species, ABIL_CON); + if (min > 0 && ch->real_abils.con < min) + ch->real_abils.con = min; + cap = species_ability_cap(species, ABIL_CON); + if (cap > 0 && ch->real_abils.con > cap) + ch->real_abils.con = cap; + + min = species_ability_min(species, ABIL_INT); + if (min > 0 && ch->real_abils.intel < min) + ch->real_abils.intel = min; + cap = species_ability_cap(species, ABIL_INT); + if (cap > 0 && ch->real_abils.intel > cap) + ch->real_abils.intel = cap; + + min = species_ability_min(species, ABIL_WIS); + if (min > 0 && ch->real_abils.wis < min) + ch->real_abils.wis = min; + cap = species_ability_cap(species, ABIL_WIS); + if (cap > 0 && ch->real_abils.wis > cap) + ch->real_abils.wis = cap; + + min = species_ability_min(species, ABIL_CHA); + if (min > 0 && ch->real_abils.cha < min) + ch->real_abils.cha = min; + cap = species_ability_cap(species, ABIL_CHA); + if (cap > 0 && ch->real_abils.cha > cap) + ch->real_abils.cha = cap; +} + +void apply_species_bonuses(struct char_data *ch) +{ + int species; + + if (!ch) + return; + + species = GET_SPECIES(ch); + if (species < 0 || species >= NUM_SPECIES) + return; + + apply_species_ranges(ch); + + ch->real_abils.str += species_ability_mod(species, ABIL_STR); + ch->real_abils.dex += species_ability_mod(species, ABIL_DEX); + ch->real_abils.con += species_ability_mod(species, ABIL_CON); + ch->real_abils.intel += species_ability_mod(species, ABIL_INT); + ch->real_abils.wis += species_ability_mod(species, ABIL_WIS); + ch->real_abils.cha += species_ability_mod(species, ABIL_CHA); + + apply_species_ranges(ch); + ch->aff_abils = ch->real_abils; +} + +void grant_species_skills(struct char_data *ch) +{ + int species; + const struct species_skill_bonus *bonus; + + if (!ch) + return; + + species = GET_SPECIES(ch); + if (species < 0 || species >= NUM_SPECIES) + return; + + for (bonus = species_skill_bonuses[species]; bonus->skill != -1; bonus++) { + if (bonus->skill >= 0 && bonus->skill <= MAX_SKILLS) { + if (GET_SKILL(ch, bonus->skill) < bonus->start) + SET_SKILL(ch, bonus->skill, bonus->start); + } + } +} + +static void remove_species_skills(struct char_data *ch, int species) +{ + const struct species_skill_bonus *bonus; + + if (!ch) + return; + if (species < 0 || species >= NUM_SPECIES) + return; + + for (bonus = species_skill_bonuses[species]; bonus->skill != -1; bonus++) { + if (bonus->skill >= 0 && bonus->skill <= MAX_SKILLS) { + if (GET_SKILL(ch, bonus->skill) <= bonus->start) + SET_SKILL(ch, bonus->skill, 0); + } + } +} + +static void remove_species_bonuses(struct char_data *ch, int species) +{ + if (!ch) + return; + if (species < 0 || species >= NUM_SPECIES) + return; + + ch->real_abils.str -= species_ability_mod(species, ABIL_STR); + ch->real_abils.dex -= species_ability_mod(species, ABIL_DEX); + ch->real_abils.con -= species_ability_mod(species, ABIL_CON); + ch->real_abils.intel -= species_ability_mod(species, ABIL_INT); + ch->real_abils.wis -= species_ability_mod(species, ABIL_WIS); + ch->real_abils.cha -= species_ability_mod(species, ABIL_CHA); +} + +int parse_species(const char *arg) +{ + int i; + + if (!arg || !*arg) + return SPECIES_UNDEFINED; + + for (i = 0; i < NUM_SPECIES; i++) { + if (!str_cmp(arg, species_types[i])) + return i; + } + + return SPECIES_UNDEFINED; +} + +void update_species(struct char_data *ch, int new_species) +{ + int old_species; + + if (!ch) + return; + + old_species = GET_SPECIES(ch); + if (old_species == new_species) + return; + + if (old_species >= 0 && old_species < NUM_SPECIES) { + remove_species_skills(ch, old_species); + remove_species_bonuses(ch, old_species); + } + + GET_SPECIES(ch) = new_species; + + if (new_species >= 0 && new_species < NUM_SPECIES) { + apply_species_bonuses(ch); + grant_species_skills(ch); + } else { + ch->aff_abils = ch->real_abils; + } +} diff --git a/src/species.h b/src/species.h new file mode 100644 index 0000000..34d80d1 --- /dev/null +++ b/src/species.h @@ -0,0 +1,29 @@ +/** +* @file species.h +* Race/species related header. +* +* This set of code was not originally part of the circlemud distribution. +*/ + +#ifndef _SPECIES_H_ +#define _SPECIES_H_ + +struct char_data; + +const char *get_species_name(int species); +int species_from_pc_choice(int choice); +int pc_species_count(void); +bool species_is_pc_selectable(int species); +int species_ability_mod(int species, int ability); +int species_ability_min(int species, int ability); +int species_ability_cap(int species, int ability); + +void apply_species_bonuses(struct char_data *ch); +void grant_species_skills(struct char_data *ch); +int parse_species(const char *arg); +void update_species(struct char_data *ch, int new_species); + +extern const char *species_types[]; +extern const int pc_species_list[]; + +#endif /* _SPECIES_H_ */ diff --git a/src/structs.h b/src/structs.h index 03eca5b..d396bbd 100644 --- a/src/structs.h +++ b/src/structs.h @@ -164,6 +164,38 @@ /** Total number of available PC Classes */ #define NUM_CLASSES 8 +/* Species */ +#define SPECIES_UNDEFINED (-1) /**< Species undefined */ +#define SPECIES_HUMAN 0 +#define SPECIES_CITY_ELF 1 +#define SPECIES_DESERT_ELF 2 +#define SPECIES_HALF_ELF 3 +#define SPECIES_DWARF 4 +#define SPECIES_MUL 5 +#define SPECIES_HALF_GIANT 6 +#define SPECIES_MANTIS 7 +#define SPECIES_GITH 8 +#define SPECIES_AARAKOCRA 9 +#define SPECIES_DRAY 10 +#define SPECIES_KENKU 11 +#define SPECIES_JOZHAL 12 +#define SPECIES_PTERRAN 13 +#define SPECIES_TAREK 14 +#define SPECIES_APRIG 15 +#define SPECIES_CARRU 16 +#define SPECIES_CRODLU 17 +#define SPECIES_ERDLU 18 +#define SPECIES_INIX 19 +#define SPECIES_JHAKAR 20 +#define SPECIES_KANK 21 +#define SPECIES_MEKILLOT 22 +#define SPECIES_WORM 23 +#define SPECIES_RENK 24 +#define SPECIES_RAT 25 +#define SPECIES_UNDEAD 26 +#define SPECIES_DRAGON 27 +#define NUM_SPECIES 28 + /* NPC classes (currently unused - feel free to implement!) */ #define CLASS_OTHER 0 /**< NPC Class Other (or undefined) */ #define CLASS_UNDEAD 1 /**< NPC Class Undead */ @@ -313,42 +345,43 @@ #define CON_NEWPASSWD 5 /**< New character, create password */ #define CON_CNFPASSWD 6 /**< New character, confirm password */ #define CON_QSEX 7 /**< Choose character sex */ -#define CON_QCLASS 8 /**< Choose character class */ -#define CON_QSHORTDESC 9 /**< Enter a new character short description prompt */ -#define CON_RMOTD 10 /**< Reading the message of the day */ -#define CON_MENU 11 /**< At the main menu */ -#define CON_PLR_DESC 12 /**< Enter a new character description prompt */ -#define CON_CHPWD_GETOLD 13 /**< Changing passwd: Get old */ -#define CON_CHPWD_GETNEW 14 /**< Changing passwd: Get new */ -#define CON_CHPWD_VRFY 15 /**< Changing passwd: Verify new password */ -#define CON_DELCNF1 16 /**< Character Delete: Confirmation 1 */ -#define CON_DELCNF2 17 /**< Character Delete: Confirmation 2 */ -#define CON_DISCONNECT 18 /**< In-game link loss (leave character) */ -#define CON_OEDIT 19 /**< OLC mode - object editor */ -#define CON_REDIT 20 /**< OLC mode - room editor */ -#define CON_ZEDIT 21 /**< OLC mode - zone info editor */ -#define CON_MEDIT 22 /**< OLC mode - mobile editor */ -#define CON_SEDIT 23 /**< OLC mode - shop editor */ -#define CON_TEDIT 24 /**< OLC mode - text editor */ -#define CON_CEDIT 25 /**< OLC mode - conf editor */ -#define CON_AEDIT 26 /**< OLC mode - social (action) edit */ -#define CON_TRIGEDIT 27 /**< OLC mode - trigger edit */ -#define CON_HEDIT 28 /**< OLC mode - help edit */ -#define CON_QEDIT 29 /**< OLC mode - quest edit */ -#define CON_PREFEDIT 30 /**< OLC mode - preference edit */ -#define CON_IBTEDIT 31 /**< OLC mode - idea/bug/typo edit */ -#define CON_MSGEDIT 32 /**< OLC mode - message editor */ -#define CON_PLR_BACKGROUND 33 /**< Entering a new character background */ -#define CON_GET_PROTOCOL 33 /**< Used at log-in while attempting to get protocols > */ -#define CON_GET_CONNECT 34 /**< Login connect/disconnect menu */ -#define CON_GET_ACCOUNT 35 /**< Login with account name */ -#define CON_ACCOUNT_CNFRM 36 /**< New account, confirm name */ -#define CON_ACCOUNT_PASSWORD 37 /**< Login with account password */ -#define CON_ACCOUNT_NEWPASSWD 38 /**< New account, create password */ -#define CON_ACCOUNT_CNFPASSWD 39 /**< New account, confirm password */ -#define CON_ACCOUNT_EMAIL 40 /**< New account, optional email */ -#define CON_ACCOUNT_MENU 41 /**< Account main menu */ -#define CON_ACCOUNT_LIST 42 /**< Viewing account character list */ +#define CON_QSPECIES 8 /**< Choose character species */ +#define CON_QCLASS 9 /**< Choose character class */ +#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 */ +#define CON_PLR_DESC 13 /**< Enter a new character description prompt */ +#define CON_CHPWD_GETOLD 14 /**< Changing passwd: Get old */ +#define CON_CHPWD_GETNEW 15 /**< Changing passwd: Get new */ +#define CON_CHPWD_VRFY 16 /**< Changing passwd: Verify new password */ +#define CON_DELCNF1 17 /**< Character Delete: Confirmation 1 */ +#define CON_DELCNF2 18 /**< Character Delete: Confirmation 2 */ +#define CON_DISCONNECT 19 /**< In-game link loss (leave character) */ +#define CON_OEDIT 20 /**< OLC mode - object editor */ +#define CON_REDIT 21 /**< OLC mode - room editor */ +#define CON_ZEDIT 22 /**< OLC mode - zone info editor */ +#define CON_MEDIT 23 /**< OLC mode - mobile editor */ +#define CON_SEDIT 24 /**< OLC mode - shop editor */ +#define CON_TEDIT 25 /**< OLC mode - text editor */ +#define CON_CEDIT 26 /**< OLC mode - conf editor */ +#define CON_AEDIT 27 /**< OLC mode - social (action) edit */ +#define CON_TRIGEDIT 28 /**< OLC mode - trigger edit */ +#define CON_HEDIT 29 /**< OLC mode - help edit */ +#define CON_QEDIT 30 /**< OLC mode - quest edit */ +#define CON_PREFEDIT 31 /**< OLC mode - preference edit */ +#define CON_IBTEDIT 32 /**< OLC mode - idea/bug/typo edit */ +#define CON_MSGEDIT 33 /**< OLC mode - message editor */ +#define CON_PLR_BACKGROUND 34 /**< Entering a new character background */ +#define CON_GET_PROTOCOL 34 /**< Used at log-in while attempting to get protocols > */ +#define CON_GET_CONNECT 35 /**< Login connect/disconnect menu */ +#define CON_GET_ACCOUNT 36 /**< Login with account name */ +#define CON_ACCOUNT_CNFRM 37 /**< New account, confirm name */ +#define CON_ACCOUNT_PASSWORD 38 /**< Login with account password */ +#define CON_ACCOUNT_NEWPASSWD 39 /**< New account, create password */ +#define CON_ACCOUNT_CNFPASSWD 40 /**< New account, confirm password */ +#define CON_ACCOUNT_EMAIL 41 /**< New account, optional email */ +#define CON_ACCOUNT_MENU 42 /**< Account main menu */ +#define CON_ACCOUNT_LIST 43 /**< Viewing account character list */ /* OLC States range - used by IS_IN_OLC and IS_PLAYING */ #define FIRST_OLC_STATE CON_OEDIT /**< The first CON_ state that is an OLC */ @@ -907,6 +940,7 @@ struct char_player_data char *background; /**< PC / NPC background / history text */ byte sex; /**< PC / NPC sex */ byte chclass; /**< PC / NPC class */ + byte species; /**< PC / NPC species */ byte level; /**< PC / NPC level */ struct time_data time; /**< PC AGE in days */ ubyte weight; /**< PC / NPC weight */ diff --git a/src/utils.h b/src/utils.h index faf2d40..2347914 100644 --- a/src/utils.h +++ b/src/utils.h @@ -556,6 +556,8 @@ do \ /** Class of ch. */ #define GET_CLASS(ch) ((ch)->player.chclass) +/** Species of ch. */ +#define GET_SPECIES(ch) ((ch)->player.species) /** Height of ch. */ #define GET_HEIGHT(ch) ((ch)->player.height) /** Weight of ch. */ @@ -936,6 +938,7 @@ do \ /** True if ch has a valid player class assigned. */ #define HAS_VALID_CLASS(ch) ((GET_CLASS(ch) >= CLASS_SORCEROR) && (GET_CLASS(ch) < NUM_CLASSES)) +#define HAS_VALID_SPECIES(ch) ((GET_SPECIES(ch) >= 0) && (GET_SPECIES(ch) < NUM_SPECIES)) /** Return the class abbreviation for ch. */ #define CLASS_ABBR(ch) (HAS_VALID_CLASS(ch) ? class_abbrevs[(int)GET_CLASS(ch)] : "--")