diff --git a/README.md b/README.md index abdf8fa..94f5d1f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Changes from stock tbaMUD 2025 to Cataclysm MUD v1.0.0-alpha: * 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 - aka. hardcore mode +* 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 @@ -65,6 +65,9 @@ Changes in v1.1.0-alpha: * 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 commands "mcreate" and "mset" for builders to modify NPC's + * Fixed issue with msave not saving items in containers on NPC's Features to be implemented in the next few releases: diff --git a/src/genmob.h b/src/genmob.h index e32a0fd..6faf024 100644 --- a/src/genmob.h +++ b/src/genmob.h @@ -15,6 +15,7 @@ int delete_mobile(mob_rnum); int copy_mobile(struct char_data *to, struct char_data *from); int add_mobile(struct char_data *, mob_vnum); +void init_mobile(struct char_data *mob); int copy_mob_strings(struct char_data *to, struct char_data *from); int free_mob_strings(struct char_data *); int free_mobile(struct char_data *mob); diff --git a/src/interpreter.c b/src/interpreter.c index d1f8244..13eef0f 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -212,6 +212,7 @@ cpp_extern const struct command_info cmd_info[] = { { "medit" , "med" , POS_DEAD , do_oasis_medit, LVL_BUILDER, 0 }, { "mlist" , "mlist" , POS_DEAD , do_oasis_list, LVL_BUILDER, SCMD_OASIS_MLIST }, { "mcopy" , "mcopy" , POS_DEAD , do_oasis_copy, LVL_GOD, CON_MEDIT }, + { "mcreate" , "mcreate" , POS_DEAD , do_mcreate , LVL_BUILDER, 0 }, { "msave" , "msav" , POS_DEAD , do_msave, LVL_BUILDER, 0 }, { "msgedit" , "msgedit" , POS_DEAD , do_msgedit, LVL_GOD, 0 }, { "mute" , "mute" , POS_DEAD , do_wizutil , LVL_GOD, SCMD_MUTE }, diff --git a/src/medit.c b/src/medit.c index 65406b3..3acc57a 100644 --- a/src/medit.c +++ b/src/medit.c @@ -31,7 +31,7 @@ /* local functions */ static void medit_setup_new(struct descriptor_data *d); -static void init_mobile(struct char_data *mob); +void init_mobile(struct char_data *mob); static void medit_save_to_disk(zone_vnum zone_num); static void medit_disp_positions(struct descriptor_data *d); static void medit_disp_sex(struct descriptor_data *d); @@ -232,7 +232,7 @@ void medit_setup_existing(struct descriptor_data *d, int rmob_num) } /* Ideally, this function should be in db.c, but I'll put it here for portability. */ -static void init_mobile(struct char_data *mob) +void init_mobile(struct char_data *mob) { int i; clear_char(mob); diff --git a/src/set.c b/src/set.c index a4476e1..d1eb75d 100644 --- a/src/set.c +++ b/src/set.c @@ -2354,6 +2354,139 @@ static struct obj_data *find_obj_vnum_nearby(struct char_data *ch, obj_vnum vnum return NULL; } +ACMD(do_mcreate) +{ + char arg[MAX_INPUT_LENGTH]; + char buf[MAX_STRING_LENGTH]; + char namebuf[MAX_NAME_LENGTH]; + char timestr[64]; + struct char_data *newmob; + struct char_data *mob; + mob_vnum vnum; + zone_rnum znum; + time_t ct; + + if (IS_NPC(ch) || ch->desc == NULL) { + send_to_char(ch, "mcreate is only usable by connected players.\r\n"); + return; + } + + one_argument(argument, arg); + + if (!*arg) { + send_to_char(ch, + "Creates a new unfinished NPC which can be configured.\r\n" + "\r\n" + "Usage:\r\n" + " mcreate \r\n" + "\r\n" + "Examples:\r\n" + " mcreate 1001\r\n"); + return; + } + + if (!is_number(arg)) { + send_to_char(ch, + "Creates a new unfinished NPC which can be configured.\r\n" + "\r\n" + "Usage:\r\n" + " mcreate \r\n" + "\r\n" + "Examples:\r\n" + " mcreate 1001\r\n"); + return; + } + + vnum = atoi(arg); + if (vnum < IDXTYPE_MIN || vnum > IDXTYPE_MAX) { + send_to_char(ch, "That mobile VNUM can't exist.\r\n"); + return; + } + + znum = real_zone_by_thing(vnum); + if (znum == NOWHERE) { + send_to_char(ch, "Sorry, there is no zone for that number!\r\n"); + return; + } + + if (!can_edit_zone(ch, znum)) { + send_cannot_edit(ch, zone_table[znum].number); + return; + } + + if (real_mobile(vnum) != NOBODY) { + mob = read_mobile(vnum, VIRTUAL); + if (mob == NULL) { + send_to_char(ch, "mcreate: failed to instantiate NPC %d.\r\n", vnum); + return; + } + char_to_room(mob, IN_ROOM(ch)); + { + const char *keyword = GET_KEYWORDS(mob); + if (!keyword || !*keyword) + keyword = "npc"; + if (!strn_cmp(keyword, "unfinished ", 11)) + keyword += 11; + act("You form $t from clay.", FALSE, ch, (void *)keyword, 0, TO_CHAR); + act("$n forms $t from clay.", TRUE, ch, (void *)keyword, 0, TO_ROOM); + } + return; + } + + CREATE(newmob, struct char_data, 1); + init_mobile(newmob); + + GET_LEVEL(newmob) = 1; + + GET_NAME(newmob) = strdup("An unfinished NPC"); + GET_KEYWORDS(newmob) = strdup("unfinished npc"); + strlcpy(namebuf, GET_NAME(ch), sizeof(namebuf)); + snprintf(buf, sizeof(buf), "An unfinished NPC made by %.*s", + (int)sizeof(namebuf) - 1, namebuf); + GET_SDESC(newmob) = strdup(buf); + snprintf(buf, sizeof(buf), "An unfinished NPC made by %.*s is here.\r\n", + (int)sizeof(namebuf) - 1, namebuf); + GET_LDESC(newmob) = strdup(buf); + ct = time(0); + strftime(timestr, sizeof(timestr), "%c", localtime(&ct)); + snprintf(buf, sizeof(buf), + "An unfinished NPC made by %.*s on %.*s\r\n", + (int)sizeof(namebuf) - 1, namebuf, + (int)sizeof(timestr) - 1, timestr); + GET_DDESC(newmob) = strdup(buf); + GET_BACKGROUND(newmob) = strdup("No background has been recorded.\r\n"); + + if (add_mobile(newmob, vnum) == NOBODY) { + free_mobile_strings(newmob); + free(newmob); + send_to_char(ch, "mcreate: failed to add NPC %d.\r\n", vnum); + return; + } + + if (in_save_list(zone_table[znum].number, SL_MOB)) + remove_from_save_list(zone_table[znum].number, SL_MOB); + + free_mobile_strings(newmob); + free(newmob); + + mob = read_mobile(vnum, VIRTUAL); + if (mob == NULL) { + send_to_char(ch, "mcreate: failed to instantiate NPC %d.\r\n", vnum); + return; + } + + char_to_room(mob, IN_ROOM(ch)); + { + const char *keyword = GET_KEYWORDS(mob); + if (!keyword || !*keyword) + keyword = "npc"; + if (!strn_cmp(keyword, "unfinished ", 11)) + keyword += 11; + act("You create an unfinished $t from clay.", FALSE, ch, (void *)keyword, 0, TO_CHAR); + act("$n forms an unfinished $t from clay.", TRUE, ch, (void *)keyword, 0, TO_ROOM); + } +} + ACMD(do_ocreate) { char arg[MAX_INPUT_LENGTH]; @@ -2403,11 +2536,6 @@ ACMD(do_ocreate) return; } - if (real_object(vnum) != NOTHING) { - send_to_char(ch, "Object %d already exists.\r\n", vnum); - return; - } - znum = real_zone_by_thing(vnum); if (znum == NOWHERE) { send_to_char(ch, "Sorry, there is no zone for that number!\r\n"); @@ -2419,6 +2547,25 @@ ACMD(do_ocreate) return; } + if (real_object(vnum) != NOTHING) { + obj = read_object(vnum, VIRTUAL); + if (obj == NULL) { + send_to_char(ch, "ocreate: failed to instantiate object %d.\r\n", vnum); + return; + } + obj_to_char(obj, ch); + { + const char *sdesc = obj->short_description; + if (!sdesc || !*sdesc) + sdesc = obj->name; + if (!sdesc || !*sdesc) + sdesc = "object"; + act("You form $t from clay.", FALSE, ch, (void *)sdesc, 0, TO_CHAR); + act("$n forms $t from clay.", TRUE, ch, (void *)sdesc, 0, TO_ROOM); + } + return; + } + CREATE(newobj, struct obj_data, 1); clear_object(newobj); @@ -2454,9 +2601,23 @@ ACMD(do_ocreate) } obj_to_char(obj, ch); - send_to_char(ch, - "Object %d created (temporary). Use osave to write it to disk.\r\n", - vnum); + { + const char *sdesc = obj->short_description; + if (!sdesc || !*sdesc) + sdesc = obj->name; + if (!sdesc || !*sdesc) + sdesc = "object"; + if (!strn_cmp(sdesc, "an ", 3)) + sdesc += 3; + else if (!strn_cmp(sdesc, "a ", 2)) + sdesc += 2; + else if (!strn_cmp(sdesc, "the ", 4)) + sdesc += 4; + if (!strn_cmp(sdesc, "unfinished ", 11)) + sdesc += 11; + act("You create an unfinished $t from clay.", FALSE, ch, (void *)sdesc, 0, TO_CHAR); + act("$n forms an unfinished $t from clay.", TRUE, ch, (void *)sdesc, 0, TO_ROOM); + } } ACMD(do_osave) diff --git a/src/set.h b/src/set.h index 0d687ce..8d04ff2 100644 --- a/src/set.h +++ b/src/set.h @@ -13,6 +13,7 @@ ACMD(do_rset); ACMD(do_rcreate); ACMD(do_ocreate); +ACMD(do_mcreate); ACMD(do_oset); ACMD(do_osave); ACMD(do_msave);