Update set commands, add exdescs to npc's

This commit is contained in:
kinther 2026-01-25 10:36:03 -08:00
parent 1a1767b97a
commit b15702aa53
6 changed files with 777 additions and 1316 deletions

123
README.md
View file

@ -2,122 +2,13 @@
Cataclysm MUD is a continuation of tbaMUD/CircleMUD, which is built on DIKU MUD.
Due to licensing issues with tbaMUD, all licensing attributions should remain the
same and not changed from the original, pre-LGPL license model.
same and not changed from the original, pre-LGPL license model. It strives to be
open source and available to everyone just like DIKU/Circle/tba have been in the
past. You're welcome to read the code, make your own changes in a fork, and re-use
it as need be. Just follow the LICENSE.md file and you'll be fine.
TO be clear: **this is not an LGPL licensed fork**.
Changes from stock tbaMUD 2025 to Cataclysm MUD v1.0.0-alpha:
* The city of Caleran is available for exploration
* Experience points and levels are removed in favor of skill based progression
* Initial skills/spells based partly on tbaMUD code and 5e conversion (to be cleaned up in later release)
* Expanded emoting system for roleplay
* Permanent character death
* A hybrid "5e-like" system where:
- [ ] Legacy THAC0 systems are removed in favor of the modern 5e system
- [ ] Your skill level translates to a proficiency bonus on a per-skill level
- [ ] Saving throws are based on 5e rules (if your class has them, you do)
- [ ] Your spell save DC is 8 + skill profiency + ability mod for your class
- [ ] AC and to hit d20 rolls typical of 5e, capped at +10 to hit and +8 to AC
- [ ] Introduction of basic 5e skills which are used for multiple commands
* QUIT room flags to designate a room safe to quit in, as well as quit ooc <message>
* SAVE room flags for players to drop their loot in (such as an apartment or barracks)
* NPC's can be equipped with items and their prototypes saved for quick future loading
* Rooms can be saved with their objects and NPC's to be loaded on the next boot
* Converted action descriptions to main descriptions for items
* Mapping command now supports new terrain types
* Players do not pay to keep their items when they log out
* Furniture items now allow items to be placed on top of them (a mug on top of the bar)
* Furniture items can now be stood at, sat on, rested on, and slept on
* Furniture items in rooms can be reviewed with "look tables"
* Think, feel, and OOC commands for roleplay purposes
* Expanded say, talk, and whisper commands to allow for greater roleplay
* Sneak/Hide have been updated and an opposed roll against observers occurs
* Ability to stealthily put or get items from containers via Palm/Slip commands
* Listening in on conversations nearby is possible
* Modernized stat output for immortals
* NPC's can now be assigned a class like a PC and inherit the relevant skills
* 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
Changes in v1.1.0-alpha:
* Cleaned up legacy practice system code
* Added skill caps for classes to limit ability of everyone to reach skill level 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
* 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
* "audit armor" and "audit melee" commands for immortals (formerly "acaudit") to check non-compliant items
* Minor score output change to only show quest status while on a quest, PC/NPC name, sdesc, and current ldesc
* Added ability to reroll initial stats if they are not to player's liking, and undo reroll if needed
* Removed alignment from game - no more GOOD/EVIL flags or restrictions on shops
* Removed ANTI_ flags related to class restrictions on what objects they can use
* Mounts added to help with long trips, and ability to use them as pack animals
* Introduced AGENTS.md file to ensure code quality
* Migration away from OLC with new commands "rcreate" and "rset" for builders to modify rooms
* Migration away from OLC with new commands "ocreate", "oset", and "osave" for builders to modify objects
* Migration away from OLC with new command "mcreate" and for builders to modify NPC's
* Fixed issue with msave not saving items in containers on NPC's
Changes in v1.2.0-alpha:
* Replaced ASCII files in favor of TOML for ease of reading
Changes in v1.3.0-alpha:
* Migration away from OLC with new command "mset" and for builders to update NPC's
Features that will be implemented in the next few releases:
* Height and weight normalized to species
* Stables allow for purchasing of mounts
* Stables will take mounts and provide tickets to get them out
* Updated door code so that it can be closed/locked/saved with rsave code
* SECTOR/ROOM type changes to make terrain movement easier or more difficult
* Subclass selection to personalize character further
* Combat is slowed down so it isn't over in < 15 seconds (unless you're far outmatched)
* Wagons added to help with caravans
* BUILDING object type created to allow enter/leave
* Updated BUILDING object type so that it can be damaged and no longer enterable (but someone can leave at cost to health)
* Plantlife introduced, allowing a plant object to produce fruit or herbs every few hours/days
* Plantlife can be refreshed to spawn fruits/herbs more frequently by watering it
* Updated lockpicking skill
* Trap as a skill - one focused on city and one focused on desert
* Poisons and antidotes
* New alcohol and drugs/stimulants
* Skimmers/ships to traverse difficult terrain
* New elemental classes
* Introduction of gathering mana/magic for sorceror class
* Highly modified magic system
* Ranged weapons and ammo
* Components for some magical spells
* Reading/writing limited to specific castes of society
* Haggling and bartering system
* New calendar and moon cycles
* Heat based on time of day increases/decreases, changing hunger/thirst levels
* Weather updates and sandstorms limiting visibility
* Shaded rooms providing bonuses to regeneration
* Criminal system for cities and jails
* Basic Psionics
* Basic crafting system
* Apartment rentals for storing your loot
* Enhanced quest system
* Dialogue trees with NPC's
* 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:
* Replace DG Scripts with a Python abstraction layer for modern scripting support
* Replace Oasis OLC with more modern interface for easing builder duties
* Discord server integration for ticketing and community
* Full documentation for admins and easy to follow improvement guides
* ...suggestions from anyone who uses this game
Cataclysm's focus is on building an RPI MUD that takes into account many of the
failings of previous RPI games.

1315
changelog

File diff suppressed because it is too large Load diff

View file

@ -1731,6 +1731,31 @@ static void parse_mobile_toml(toml_table_t *mob_tab)
!str_cmp(fname(mob_proto[i].player.short_descr), "the"))
*mob_proto[i].player.short_descr = LOWER(*mob_proto[i].player.short_descr);
arr = toml_array_in(mob_tab, "extra_desc");
if (arr) {
int n = toml_array_nelem(arr);
for (j = 0; j < n; j++) {
toml_table_t *ed_tab = toml_table_at(arr, j);
struct extra_descr_data *new_descr;
char *keyword, *description;
if (!ed_tab)
continue;
keyword = toml_get_string_dup(ed_tab, "keyword");
description = toml_get_string_dup(ed_tab, "description");
if (!keyword || !description) {
if (keyword) free(keyword);
if (description) free(description);
continue;
}
CREATE(new_descr, struct extra_descr_data, 1);
new_descr->keyword = keyword;
new_descr->description = description;
ensure_newline_terminated(new_descr);
new_descr->next = mob_proto[i].mob_specials.ex_description;
mob_proto[i].mob_specials.ex_description = new_descr;
}
}
for (j = 0; j < AF_ARRAY_MAX; j++) {
MOB_FLAGS(mob_proto + i)[j] = 0;
AFF_FLAGS(mob_proto + i)[j] = 0;

View file

@ -128,6 +128,11 @@ static void extract_mobile_all(mob_vnum vnum)
if (ch->player.background && ch->player.background != mob_proto[i].player.background)
free(ch->player.background);
ch->player.background = NULL;
if (ch->mob_specials.ex_description &&
ch->mob_specials.ex_description != mob_proto[i].mob_specials.ex_description)
free_ex_descriptions(ch->mob_specials.ex_description);
ch->mob_specials.ex_description = NULL;
/* free script proto list if it's not the prototype */
if (ch->proto_script && ch->proto_script != mob_proto[i].proto_script)
@ -208,6 +213,8 @@ int copy_mobile_strings(struct char_data *t, struct char_data *f)
t->player.description = strdup(f->player.description);
if (f->player.background)
t->player.background = strdup(f->player.background);
if (f->mob_specials.ex_description)
copy_ex_descriptions(&t->mob_specials.ex_description, f->mob_specials.ex_description);
return TRUE;
}
@ -223,6 +230,7 @@ int update_mobile_strings(struct char_data *t, struct char_data *f)
t->player.description = f->player.description;
if (f->player.background)
t->player.background = f->player.background;
t->mob_specials.ex_description = f->mob_specials.ex_description;
return TRUE;
}
@ -238,6 +246,10 @@ int free_mobile_strings(struct char_data *mob)
free(mob->player.description);
if (mob->player.background)
free(mob->player.background);
if (mob->mob_specials.ex_description) {
free_ex_descriptions(mob->mob_specials.ex_description);
mob->mob_specials.ex_description = NULL;
}
return TRUE;
}
@ -266,6 +278,9 @@ int free_mobile(struct char_data *mob)
free(mob->player.description);
if (mob->player.background && mob->player.background != mob_proto[i].player.background)
free(mob->player.background);
if (mob->mob_specials.ex_description &&
mob->mob_specials.ex_description != mob_proto[i].mob_specials.ex_description)
free_ex_descriptions(mob->mob_specials.ex_description);
/* free script proto list if it's not the prototype */
if (mob->proto_script && mob->proto_script != mob_proto[i].proto_script)
free_proto_script(mob, MOB_TRIGGER);
@ -407,6 +422,7 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
char ldesc[MAX_STRING_LENGTH];
char ddesc[MAX_STRING_LENGTH];
char bdesc[MAX_STRING_LENGTH];
char edesc_buf[MAX_STRING_LENGTH];
int has_bdesc = 0;
ldesc[MAX_STRING_LENGTH - 1] = '\0';
@ -438,6 +454,22 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd)
if (has_bdesc)
toml_write_kv_string_opt(fd, "background", bdesc);
if (mob->mob_specials.ex_description) {
struct extra_descr_data *xdesc;
for (xdesc = mob->mob_specials.ex_description; xdesc; xdesc = xdesc->next) {
if (!xdesc->keyword || !xdesc->description || !*xdesc->keyword || !*xdesc->description) {
mudlog(BRF, LVL_IMMORT, TRUE, "SYSERR: GenOLC: write_mobile_record: Corrupt ex_desc!");
continue;
}
strncpy(edesc_buf, xdesc->description, sizeof(edesc_buf) - 1);
edesc_buf[sizeof(edesc_buf) - 1] = '\0';
strip_cr(edesc_buf);
fprintf(fd, "\n[[mob.extra_desc]]\n");
toml_write_kv_string(fd, "keyword", xdesc->keyword);
toml_write_kv_string(fd, "description", edesc_buf);
}
}
fprintf(fd, "flags = [%d, %d, %d, %d]\n",
MOB_FLAGS(mob)[0], MOB_FLAGS(mob)[1],
MOB_FLAGS(mob)[2], MOB_FLAGS(mob)[3]);

597
src/set.c
View file

@ -50,7 +50,9 @@ static void rset_show_usage(struct char_data *ch)
" rset add sector <sector>\r\n"
" rset add flags <flag> [flag ...]\r\n"
" rset add exit <direction> <room number>\r\n"
" rset add exitdesc <direction> <text>\r\n"
" rset add door <direction> <name of door>\r\n"
" rset add doorflags <direction> <flag> [flag ...]\r\n"
" rset add key <direction> <key number>\r\n"
" rset add hidden <direction>\r\n"
" rset add forage <object vnum> <dc check>\r\n"
@ -100,7 +102,9 @@ static void rset_show_add_usage(struct char_data *ch)
" rset add sector <sector>\r\n"
" rset add flags <flag> [flag ...]\r\n"
" rset add exit <direction> <room number>\r\n"
" rset add exitdesc <direction> <text>\r\n"
" rset add door <direction> <name of door>\r\n"
" rset add doorflags <direction> <flag> [flag ...]\r\n"
" rset add key <direction> <key number>\r\n"
" rset add hidden <direction>\r\n"
" rset add forage <object vnum> <dc check>\r\n"
@ -116,7 +120,9 @@ static void rset_show_del_usage(struct char_data *ch)
"Usage:\r\n"
" rset del flags <flag> [flag ...]\r\n"
" rset del exit <direction>\r\n"
" rset del exitdesc <direction>\r\n"
" rset del door <direction>\r\n"
" rset del doorflags <direction> <flag> [flag ...]\r\n"
" rset del key <direction>\r\n"
" rset del hidden <direction>\r\n"
" rset del forage <object vnum>\r\n"
@ -208,10 +214,22 @@ static void rset_show_add_exit_usage(struct char_data *ch)
" rset add exit n 101\r\n");
}
static void rset_show_add_exitdesc_usage(struct char_data *ch)
{
send_to_char(ch,
"Adds a description to an existing exit.\r\n"
"\r\n"
"Usage:\r\n"
" rset add exitdesc <direction> <text>\r\n"
"\r\n"
"Examples:\r\n"
" rset add exitdesc n A narrow arch leads north.\r\n");
}
static void rset_show_add_door_usage(struct char_data *ch)
{
send_to_char(ch,
"Adds a door to an existing exit.\r\n"
"Adds a door keyword to an existing exit.\r\n"
"\r\n"
"Usage:\r\n"
" rset add door <direction> <name of door>\r\n"
@ -220,6 +238,26 @@ static void rset_show_add_door_usage(struct char_data *ch)
" rset add door n door\r\n");
}
static void rset_show_add_doorflags_usage(struct char_data *ch)
{
int count = 0;
while (*exit_bits[count] != '\n')
count++;
send_to_char(ch,
"Adds door flags to an existing exit.\r\n"
"\r\n"
"Usage:\r\n"
" rset add doorflags <direction> <flag> [flag ...]\r\n"
"\r\n"
"Examples:\r\n"
" rset add doorflags n closed locked\r\n"
"\r\n"
"Flags:\r\n");
column_list(ch, 0, exit_bits, count, FALSE);
}
static void rset_show_add_key_usage(struct char_data *ch)
{
send_to_char(ch,
@ -280,6 +318,18 @@ static void rset_show_del_exit_usage(struct char_data *ch)
" rset del exit n\r\n");
}
static void rset_show_del_exitdesc_usage(struct char_data *ch)
{
send_to_char(ch,
"Deletes the description from an existing exit.\r\n"
"\r\n"
"Usage:\r\n"
" rset del exitdesc <direction>\r\n"
"\r\n"
"Examples:\r\n"
" rset del exitdesc n\r\n");
}
static void rset_show_del_door_usage(struct char_data *ch)
{
send_to_char(ch,
@ -292,6 +342,26 @@ static void rset_show_del_door_usage(struct char_data *ch)
" rset del door n\r\n");
}
static void rset_show_del_doorflags_usage(struct char_data *ch)
{
int count = 0;
while (*exit_bits[count] != '\n')
count++;
send_to_char(ch,
"Deletes door flags from an existing exit.\r\n"
"\r\n"
"Usage:\r\n"
" rset del doorflags <direction> <flag> [flag ...]\r\n"
"\r\n"
"Examples:\r\n"
" rset del doorflags n locked\r\n"
"\r\n"
"Flags:\r\n");
column_list(ch, 0, exit_bits, count, FALSE);
}
static void rset_show_del_key_usage(struct char_data *ch)
{
send_to_char(ch,
@ -380,6 +450,17 @@ static int rset_find_dir(const char *arg)
return dir;
}
static int rset_find_exit_flag(const char *arg)
{
int i;
for (i = 0; *exit_bits[i] != '\n'; i++)
if (is_abbrev(arg, exit_bits[i]))
return (1 << i);
return -1;
}
static void rset_mark_room_modified(room_rnum rnum)
{
if (rnum == NOWHERE || rnum < 0 || rnum > top_of_world)
@ -436,6 +517,8 @@ static void rset_show_room(struct char_data *ch, struct room_data *room)
exit->keyword ? exit->keyword : "None",
keybuf,
buf);
if (exit->general_description && *exit->general_description)
send_to_char(ch, " desc: %s\r\n", exit->general_description);
count++;
}
if (!count)
@ -831,6 +914,39 @@ ACMD(do_rset)
return;
}
if (is_abbrev(arg2, "exitdesc")) {
int dir;
char *desc;
argument = one_argument(argument, arg3);
skip_spaces(&argument);
desc = argument;
if (!*arg3 || !*desc) {
rset_show_add_exitdesc_usage(ch);
return;
}
dir = rset_find_dir(arg3);
if (dir < 0) {
send_to_char(ch, "Invalid direction.\r\n");
return;
}
if (!room->dir_option[dir] || room->dir_option[dir]->to_room == NOWHERE) {
send_to_char(ch, "That exit does not exist.\r\n");
return;
}
genolc_checkstring(ch->desc, desc);
if (room->dir_option[dir]->general_description)
free(room->dir_option[dir]->general_description);
room->dir_option[dir]->general_description = str_udup(desc);
rset_mark_room_modified(rnum);
send_to_char(ch, "Exit description set for %s.\r\n", dirs[dir]);
return;
}
if (is_abbrev(arg2, "door")) {
int dir;
char *door_name;
@ -865,6 +981,56 @@ ACMD(do_rset)
return;
}
if (is_abbrev(arg2, "doorflags")) {
int dir;
bool any = FALSE;
argument = one_argument(argument, arg3);
if (!*arg3) {
rset_show_add_doorflags_usage(ch);
return;
}
dir = rset_find_dir(arg3);
if (dir < 0) {
send_to_char(ch, "Invalid direction.\r\n");
return;
}
if (!room->dir_option[dir] || room->dir_option[dir]->to_room == NOWHERE) {
send_to_char(ch, "That exit does not exist.\r\n");
return;
}
if (!*argument) {
rset_show_add_doorflags_usage(ch);
return;
}
while (*argument) {
int flag;
argument = one_argument(argument, arg1);
if (!*arg1)
break;
flag = rset_find_exit_flag(arg1);
if (flag < 0) {
send_to_char(ch, "Unknown door flag: %s\r\n", arg1);
continue;
}
SET_BIT(room->dir_option[dir]->exit_info, flag);
any = TRUE;
}
if (any) {
rset_mark_room_modified(rnum);
send_to_char(ch, "Door flags updated on %s.\r\n", dirs[dir]);
}
return;
}
if (is_abbrev(arg2, "key")) {
int dir;
int key_vnum;
@ -1088,6 +1254,36 @@ ACMD(do_rset)
return;
}
if (is_abbrev(arg2, "exitdesc")) {
int dir;
argument = one_argument(argument, arg3);
if (!*arg3) {
rset_show_del_exitdesc_usage(ch);
return;
}
dir = rset_find_dir(arg3);
if (dir < 0) {
send_to_char(ch, "Invalid direction.\r\n");
return;
}
if (!room->dir_option[dir] || room->dir_option[dir]->to_room == NOWHERE) {
send_to_char(ch, "That exit does not exist.\r\n");
return;
}
if (room->dir_option[dir]->general_description) {
free(room->dir_option[dir]->general_description);
room->dir_option[dir]->general_description = NULL;
}
rset_mark_room_modified(rnum);
send_to_char(ch, "Exit description removed from %s.\r\n", dirs[dir]);
return;
}
if (is_abbrev(arg2, "door")) {
int dir;
@ -1123,6 +1319,56 @@ ACMD(do_rset)
return;
}
if (is_abbrev(arg2, "doorflags")) {
int dir;
bool any = FALSE;
argument = one_argument(argument, arg3);
if (!*arg3) {
rset_show_del_doorflags_usage(ch);
return;
}
dir = rset_find_dir(arg3);
if (dir < 0) {
send_to_char(ch, "Invalid direction.\r\n");
return;
}
if (!room->dir_option[dir] || room->dir_option[dir]->to_room == NOWHERE) {
send_to_char(ch, "That exit does not exist.\r\n");
return;
}
if (!*argument) {
rset_show_del_doorflags_usage(ch);
return;
}
while (*argument) {
int flag;
argument = one_argument(argument, arg1);
if (!*arg1)
break;
flag = rset_find_exit_flag(arg1);
if (flag < 0) {
send_to_char(ch, "Unknown door flag: %s\r\n", arg1);
continue;
}
REMOVE_BIT(room->dir_option[dir]->exit_info, flag);
any = TRUE;
}
if (any) {
rset_mark_room_modified(rnum);
send_to_char(ch, "Door flags updated on %s.\r\n", dirs[dir]);
}
return;
}
if (is_abbrev(arg2, "key")) {
int dir;
@ -1309,6 +1555,7 @@ static void oset_show_usage(struct char_data *ch)
" oset add weight <obj> <value>\r\n"
" oset add cost <obj> <value>\r\n"
" oset add oval <obj> <oval number> <value>\r\n"
" oset add edesc <obj> <keyword> <description>\r\n"
" oset del <obj> <field>\r\n"
" oset clear <obj> force\r\n"
" oset validate <obj>\r\n");
@ -1329,7 +1576,8 @@ static void oset_show_add_usage(struct char_data *ch)
" oset add wear <obj> <wear type> [wear types]\r\n"
" oset add weight <obj> <value>\r\n"
" oset add cost <obj> <value>\r\n"
" oset add oval <obj> <oval number> <value>\r\n");
" oset add oval <obj> <oval number> <value>\r\n"
" oset add edesc <obj> <keyword> <description>\r\n");
}
static void oset_show_add_keywords_usage(struct char_data *ch)
@ -1442,6 +1690,18 @@ static void oset_show_add_oval_usage(struct char_data *ch)
" oset add oval sword weapon_type slashing (for weapons)\r\n");
}
static void oset_show_add_edesc_usage(struct char_data *ch)
{
send_to_char(ch,
"Adds an extra description to the object.\r\n"
"\r\n"
"Usage:\r\n"
" oset add edesc <obj> <keyword> <description>\r\n"
"\r\n"
"Examples:\r\n"
" oset add edesc sword rune A rune is etched along the blade.\r\n");
}
static void oset_show_del_usage(struct char_data *ch)
{
send_to_char(ch,
@ -1452,6 +1712,7 @@ static void oset_show_del_usage(struct char_data *ch)
" oset del <obj> flags <flags> [flags]\r\n"
" oset del <obj> wear <wear type> [wear types]\r\n"
" oset del <obj> oval <oval number|oval name>\r\n"
" oset del <obj> edesc <keyword>\r\n"
"\r\n"
"Examples:\r\n"
" oset del sword keywords sword\r\n"
@ -1502,6 +1763,18 @@ static void oset_show_clear_usage(struct char_data *ch)
" oset clear sword force\r\n");
}
static void oset_show_del_edesc_usage(struct char_data *ch)
{
send_to_char(ch,
"Deletes an extra description from the object.\r\n"
"\r\n"
"Usage:\r\n"
" oset del <obj> edesc <keyword>\r\n"
"\r\n"
"Examples:\r\n"
" oset del sword edesc rune\r\n");
}
static struct obj_data *oset_get_target_obj_keyword(struct char_data *ch, char *keyword)
{
struct obj_data *obj;
@ -1718,6 +1991,15 @@ static void oset_show_object(struct char_data *ch, struct obj_data *obj)
const char *label = labels ? labels[i] : "Value";
send_to_char(ch, " [%d] %s: %d\r\n", i, label, GET_OBJ_VAL(obj, i));
}
send_to_char(ch, "Extra Descs:\r\n");
i = 0;
for (struct extra_descr_data *desc = obj->ex_description; desc; desc = desc->next) {
send_to_char(ch, " %s\r\n", desc->keyword ? desc->keyword : "<None>");
i++;
}
if (!i)
send_to_char(ch, " None.\r\n");
}
static void oset_desc_edit(struct char_data *ch, struct obj_data *obj)
@ -1759,7 +2041,7 @@ static void oset_clear_object(struct obj_data *obj)
GET_OBJ_COST(obj) = 0;
GET_OBJ_COST_PER_DAY(obj) = 0;
GET_OBJ_TIMER(obj) = 0;
GET_OBJ_LEVEL(obj) = 0;
GET_OBJ_LEVEL(obj) = 1;
memset(obj->obj_flags.extra_flags, 0, sizeof(obj->obj_flags.extra_flags));
memset(obj->obj_flags.wear_flags, 0, sizeof(obj->obj_flags.wear_flags));
@ -1809,6 +2091,33 @@ static void oset_validate_object(struct char_data *ch, struct obj_data *obj)
errors++;
}
if (GET_OBJ_LEVEL(obj) != 1) {
send_to_char(ch, "Error: object level must be 1.\r\n");
errors++;
}
if (GET_OBJ_TIMER(obj) != 0) {
send_to_char(ch, "Error: object timer must be 0.\r\n");
errors++;
}
if (GET_OBJ_COST_PER_DAY(obj) != 0) {
send_to_char(ch, "Error: cost per day must be 0.\r\n");
errors++;
}
for (struct extra_descr_data *desc = obj->ex_description; desc; desc = desc->next) {
if (!desc->keyword || !*desc->keyword) {
send_to_char(ch, "Error: extra description is missing a keyword.\r\n");
errors++;
}
if (!desc->description || !*desc->description) {
send_to_char(ch, "Error: extra description for %s is missing text.\r\n",
desc->keyword ? desc->keyword : "<None>");
errors++;
}
}
if (!errors)
send_to_char(ch, "Object validates cleanly.\r\n");
else
@ -2160,6 +2469,40 @@ ACMD(do_oset)
return;
}
if (is_abbrev(arg2, "edesc")) {
struct extra_descr_data *desc;
char *keyword;
char *edesc;
argument = one_argument(argument, arg3);
argument = one_argument(argument, arg4);
skip_spaces(&argument);
keyword = arg4;
edesc = argument;
if (!*arg3 || !*keyword || !*edesc) {
oset_show_add_edesc_usage(ch);
return;
}
obj = oset_get_target_obj_keyword(ch, arg3);
if (!obj) {
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg3), arg3);
return;
}
genolc_checkstring(ch->desc, edesc);
genolc_checkstring(ch->desc, keyword);
CREATE(desc, struct extra_descr_data, 1);
desc->keyword = str_udup(keyword);
desc->description = str_udup(edesc);
desc->next = obj->ex_description;
obj->ex_description = desc;
send_to_char(ch, "Extra description added.\r\n");
return;
}
oset_show_add_usage(ch);
return;
}
@ -2289,6 +2632,46 @@ ACMD(do_oset)
return;
}
if (is_abbrev(arg3, "edesc")) {
struct extra_descr_data *desc;
struct extra_descr_data *prev = NULL;
argument = one_argument(argument, arg4);
if (!*arg4) {
oset_show_del_edesc_usage(ch);
return;
}
obj = oset_get_target_obj_keyword(ch, arg2);
if (!obj) {
send_to_char(ch, "You don't seem to have %s %s.\r\n", AN(arg2), arg2);
return;
}
for (desc = obj->ex_description; desc; desc = desc->next) {
if (desc->keyword && isname(arg4, desc->keyword))
break;
prev = desc;
}
if (!desc) {
send_to_char(ch, "No extra description found for %s.\r\n", arg4);
return;
}
if (prev)
prev->next = desc->next;
else
obj->ex_description = desc->next;
if (desc->keyword)
free(desc->keyword);
if (desc->description)
free(desc->description);
free(desc);
send_to_char(ch, "Extra description removed.\r\n");
return;
}
oset_show_del_usage(ch);
return;
}
@ -2359,6 +2742,7 @@ static void mset_show_usage(struct char_data *ch)
" mset add ldesc <npc> <text>\r\n"
" mset add desc <npc> (enters editor)\r\n"
" mset add background <npc> (enters editor)\r\n"
" mset add edesc <npc> <keyword> <description>\r\n"
" mset add attack <npc> <attack type>\r\n"
" mset add sex <npc> <male/female/neutral>\r\n"
" mset add species <npc> <species name>\r\n"
@ -2441,6 +2825,18 @@ static void mset_show_add_background_usage(struct char_data *ch)
" mset add background <npc>\r\n");
}
static void mset_show_add_edesc_usage(struct char_data *ch)
{
send_to_char(ch,
"Adds an extra description to the NPC.\r\n"
"\r\n"
"Usage:\r\n"
" mset add edesc <npc> <keyword> <description>\r\n"
"\r\n"
"Examples:\r\n"
" mset add edesc guard scar A jagged scar cuts across the cheek.\r\n");
}
static void mset_show_add_attack_usage(struct char_data *ch)
{
const char *types[NUM_ATTACK_TYPES];
@ -2597,6 +2993,7 @@ static void mset_show_del_usage(struct char_data *ch)
" mset del <npc> ldesc\r\n"
" mset del <npc> desc\r\n"
" mset del <npc> background\r\n"
" mset del <npc> edesc <keyword>\r\n"
" mset del <npc> attack\r\n"
" mset del <npc> sex\r\n"
" mset del <npc> species\r\n"
@ -2609,6 +3006,18 @@ static void mset_show_del_usage(struct char_data *ch)
" mset del <npc> skinning <vnum>\r\n");
}
static void mset_show_del_edesc_usage(struct char_data *ch)
{
send_to_char(ch,
"Deletes an extra description from the NPC.\r\n"
"\r\n"
"Usage:\r\n"
" mset del <npc> edesc <keyword>\r\n"
"\r\n"
"Examples:\r\n"
" mset del guard edesc scar\r\n");
}
static void mset_show_del_flags_usage(struct char_data *ch)
{
send_to_char(ch,
@ -2754,6 +3163,19 @@ static void mset_update_proto_keywords(mob_rnum rnum, const char *value)
free(old);
}
static void mset_update_proto_edesc(mob_rnum rnum, struct extra_descr_data *old)
{
struct char_data *mob;
if (rnum < 0)
return;
for (mob = character_list; mob; mob = mob->next) {
if (GET_MOB_RNUM(mob) == rnum && mob->mob_specials.ex_description == old)
mob->mob_specials.ex_description = mob_proto[rnum].mob_specials.ex_description;
}
}
static void mset_replace_string(struct char_data *mob, char **field, const char *value, const char *proto_field)
{
if (*field && (!proto_field || *field != proto_field))
@ -2867,6 +3289,15 @@ static void mset_show_mob(struct char_data *ch, struct char_data *mob)
} else {
send_to_char(ch, " None.\r\n");
}
send_to_char(ch, "Extra Descs:\r\n");
i = 0;
for (struct extra_descr_data *desc = mob->mob_specials.ex_description; desc; desc = desc->next) {
send_to_char(ch, " %s\r\n", desc->keyword ? desc->keyword : "<None>");
i++;
}
if (!i)
send_to_char(ch, " None.\r\n");
}
static void mset_desc_edit(struct char_data *ch, char **field, const char *label)
@ -2933,6 +3364,33 @@ static void mset_validate_mob(struct char_data *ch, struct char_data *mob)
errors++;
}
for (struct extra_descr_data *desc = mob->mob_specials.ex_description; desc; desc = desc->next) {
if (!desc->keyword || !*desc->keyword) {
send_to_char(ch, "Error: extra description is missing a keyword.\r\n");
errors++;
}
if (!desc->description || !*desc->description) {
send_to_char(ch, "Error: extra description for %s is missing text.\r\n",
desc->keyword ? desc->keyword : "<None>");
errors++;
}
}
if (GET_LEVEL(mob) != 1) {
send_to_char(ch, "Error: level must be 1.\r\n");
errors++;
}
if (GET_DEFAULT_POS(mob) != POS_STANDING) {
send_to_char(ch, "Error: default position must be standing.\r\n");
errors++;
}
if (GET_EXP(mob) != 0) {
send_to_char(ch, "Error: EXP must be 0.\r\n");
errors++;
}
if (!errors)
send_to_char(ch, "NPC validates cleanly.\r\n");
else
@ -3165,6 +3623,46 @@ ACMD(do_mset)
return;
}
if (is_abbrev(arg2, "edesc")) {
struct extra_descr_data *desc;
struct extra_descr_data *old;
char *keyword;
char *edesc;
argument = one_argument(argument, arg1);
skip_spaces(&argument);
keyword = arg1;
edesc = argument;
if (!*keyword || !*edesc) {
mset_show_add_edesc_usage(ch);
return;
}
genolc_checkstring(ch->desc, edesc);
genolc_checkstring(ch->desc, keyword);
if (rnum != NOBODY) {
old = mob_proto[rnum].mob_specials.ex_description;
CREATE(desc, struct extra_descr_data, 1);
desc->keyword = str_udup(keyword);
desc->description = str_udup(edesc);
desc->next = mob_proto[rnum].mob_specials.ex_description;
mob_proto[rnum].mob_specials.ex_description = desc;
mset_update_proto_edesc(rnum, old);
mset_mark_mob_modified(vnum);
} else {
CREATE(desc, struct extra_descr_data, 1);
desc->keyword = str_udup(keyword);
desc->description = str_udup(edesc);
desc->next = mob->mob_specials.ex_description;
mob->mob_specials.ex_description = desc;
}
send_to_char(ch, "Extra description added.\r\n");
return;
}
if (is_abbrev(arg2, "attack")) {
int atype;
@ -3663,6 +4161,74 @@ ACMD(do_mset)
return;
}
if (is_abbrev(arg3, "edesc")) {
struct extra_descr_data *desc;
struct extra_descr_data *prev = NULL;
struct extra_descr_data *old;
argument = one_argument(argument, arg1);
if (!*arg1) {
mset_show_del_edesc_usage(ch);
return;
}
if (rnum != NOBODY) {
old = mob_proto[rnum].mob_specials.ex_description;
desc = mob_proto[rnum].mob_specials.ex_description;
while (desc) {
if (desc->keyword && isname(arg1, desc->keyword))
break;
prev = desc;
desc = desc->next;
}
if (!desc) {
send_to_char(ch, "No extra description found for %s.\r\n", arg1);
return;
}
if (prev)
prev->next = desc->next;
else
mob_proto[rnum].mob_specials.ex_description = desc->next;
if (desc->keyword)
free(desc->keyword);
if (desc->description)
free(desc->description);
free(desc);
mset_update_proto_edesc(rnum, old);
mset_mark_mob_modified(vnum);
} else {
desc = mob->mob_specials.ex_description;
while (desc) {
if (desc->keyword && isname(arg1, desc->keyword))
break;
prev = desc;
desc = desc->next;
}
if (!desc) {
send_to_char(ch, "No extra description found for %s.\r\n", arg1);
return;
}
if (prev)
prev->next = desc->next;
else
mob->mob_specials.ex_description = desc->next;
if (desc->keyword)
free(desc->keyword);
if (desc->description)
free(desc->description);
free(desc);
}
send_to_char(ch, "Extra description removed.\r\n");
return;
}
if (is_abbrev(arg3, "attack")) {
mob->mob_specials.attack_type = 0;
if (rnum != NOBODY) {
@ -3946,6 +4512,8 @@ ACMD(do_mset)
}
if (rnum != NOBODY) {
struct extra_descr_data *old_edesc = mob_proto[rnum].mob_specials.ex_description;
if (mob_proto[rnum].player.name)
free(mob_proto[rnum].player.name);
if (mob_proto[rnum].player.short_descr)
@ -3956,17 +4524,25 @@ ACMD(do_mset)
free(mob_proto[rnum].player.description);
if (mob_proto[rnum].player.background)
free(mob_proto[rnum].player.background);
if (mob_proto[rnum].mob_specials.ex_description)
free_ex_descriptions(mob_proto[rnum].mob_specials.ex_description);
mob_proto[rnum].player.name = strdup("An unfinished NPC");
mob_proto[rnum].player.short_descr = strdup("the unfinished npc");
mob_proto[rnum].player.long_descr = strdup("An unfinished npc stands here.\r\n");
mob_proto[rnum].player.description = strdup("It looks unfinished.\r\n");
mob_proto[rnum].player.background = strdup("No background has been recorded.\r\n");
mob_proto[rnum].mob_specials.ex_description = NULL;
mset_update_proto_edesc(rnum, old_edesc);
mob_proto[rnum].mob_specials.attack_type = 0;
GET_SEX(&mob_proto[rnum]) = SEX_NEUTRAL;
GET_CLASS(&mob_proto[rnum]) = CLASS_UNDEFINED;
GET_SPECIES(&mob_proto[rnum]) = SPECIES_UNDEFINED;
GET_LEVEL(&mob_proto[rnum]) = 1;
GET_DEFAULT_POS(&mob_proto[rnum]) = POS_STANDING;
GET_POS(&mob_proto[rnum]) = POS_STANDING;
GET_EXP(&mob_proto[rnum]) = 0;
mset_set_stat_value(&mob_proto[rnum], ABIL_STR, 10, FALSE);
mset_set_stat_value(&mob_proto[rnum], ABIL_DEX, 10, FALSE);
@ -3997,11 +4573,19 @@ ACMD(do_mset)
mset_replace_string(mob, &GET_LDESC(mob), "An unfinished npc stands here.\r\n", NULL);
mset_replace_string(mob, &mob->player.description, "It looks unfinished.\r\n", NULL);
mset_replace_string(mob, &mob->player.background, "No background has been recorded.\r\n", NULL);
if (mob->mob_specials.ex_description) {
free_ex_descriptions(mob->mob_specials.ex_description);
mob->mob_specials.ex_description = NULL;
}
mob->mob_specials.attack_type = 0;
GET_SEX(mob) = SEX_NEUTRAL;
GET_CLASS(mob) = CLASS_UNDEFINED;
GET_SPECIES(mob) = SPECIES_UNDEFINED;
GET_LEVEL(mob) = 1;
GET_DEFAULT_POS(mob) = POS_STANDING;
GET_POS(mob) = POS_STANDING;
GET_EXP(mob) = 0;
mset_set_stat_value(mob, ABIL_STR, 10, TRUE);
mset_set_stat_value(mob, ABIL_DEX, 10, TRUE);
@ -4157,6 +4741,9 @@ ACMD(do_mcreate)
init_mobile(newmob);
GET_LEVEL(newmob) = 1;
GET_DEFAULT_POS(newmob) = POS_STANDING;
GET_POS(newmob) = POS_STANDING;
GET_EXP(newmob) = 0;
GET_NAME(newmob) = strdup("An unfinished NPC");
GET_KEYWORDS(newmob) = strdup("unfinished npc");
@ -4289,6 +4876,10 @@ ACMD(do_ocreate)
CREATE(newobj, struct obj_data, 1);
clear_object(newobj);
GET_OBJ_LEVEL(newobj) = 1;
GET_OBJ_TIMER(newobj) = 0;
GET_OBJ_COST_PER_DAY(newobj) = 0;
newobj->name = strdup("unfinished object");
strlcpy(namebuf, GET_NAME(ch), sizeof(namebuf));
snprintf(buf, sizeof(buf), "unfinished object made by %s", namebuf);

View file

@ -1088,6 +1088,7 @@ struct mob_special_data
byte attack_type; /**< The primary attack type (bite, sting, hit, etc.) */
byte default_pos; /**< Default position (standing, sleeping, etc.) */
byte skills[MAX_SKILLS]; /* NPC-specific skill proficiency (0-100) */
struct extra_descr_data *ex_description; /**< Extra descriptions */
};
/** An affect structure. */