Add mcreate command

This commit is contained in:
kinther 2026-01-02 07:04:52 -08:00
parent c1419f6fe7
commit c6918a1d9c
6 changed files with 178 additions and 11 deletions

View file

@ -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:

View file

@ -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);

View file

@ -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 },

View file

@ -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);

177
src/set.c
View file

@ -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 <vnum>\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 <vnum>\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)

View file

@ -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);