tbamud/src/oedit.c
2025-09-15 16:34:06 -07:00

1332 lines
39 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**************************************************************************
* File: oedit.c Part of tbaMUD *
* Usage: Oasis OLC - Objects. *
* *
* By Levork. Copyright 1996 Harvey Gilpin. 1997-2001 George Greer. *
**************************************************************************/
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "spells.h"
#include "db.h"
#include "boards.h"
#include "constants.h"
#include "shop.h"
#include "genolc.h"
#include "genobj.h"
#include "genzon.h"
#include "oasis.h"
#include "improved-edit.h"
#include "dg_olc.h"
#include "fight.h"
#include "modify.h"
/* local functions */
static void oedit_setup_new(struct descriptor_data *d);
static void oedit_disp_container_flags_menu(struct descriptor_data *d);
static void oedit_disp_extradesc_menu(struct descriptor_data *d);
static void oedit_disp_prompt_apply_menu(struct descriptor_data *d);
static void oedit_liquid_type(struct descriptor_data *d);
static void oedit_disp_apply_menu(struct descriptor_data *d);
static void oedit_disp_weapon_menu(struct descriptor_data *d);
static void oedit_disp_spells_menu(struct descriptor_data *d);
static void oedit_disp_type_menu(struct descriptor_data *d);
static void oedit_disp_extra_menu(struct descriptor_data *d);
static void oedit_disp_wear_menu(struct descriptor_data *d);
static void oedit_disp_menu(struct descriptor_data *d);
static void oedit_disp_perm_menu(struct descriptor_data *d);
static void oedit_save_to_disk(int zone_num);
/* handy macro */
#define S_PRODUCT(s, i) ((s)->producing[(i)])
/* Label tables for object values by type */
/* Light */
static const char *light_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"unused0", "unused1", "hours_left", "unused3",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Scroll, Potion */
static const char *scroll_potion_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"spell_level", "spell1", "spell2", "spell3",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Wand, Staff */
static const char *wand_staff_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"level", "max_charges", "remaining_charges", "spell",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Weapon */
static const char *weapon_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"dice_num", "dice_size", "weapon_type", "message_type",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Armor */
static const char *armor_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"piece_ac", "bulk", "magic_bonus", "stealth_disadv",
"durability", "str_requirement", "Value[6]", "Value[7]"
};
/* Container */
static const char *container_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"capacity", "flags", "key_vnum", "corpse",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Drinkcon / Fountain */
static const char *drink_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"capacity", "contains", "liquid_type", "poisoned",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Food */
static const char *food_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"hours_full", "unused1", "unused2", "poisoned",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Money */
static const char *money_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"coins", "unused1", "unused2", "unused3",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Furniture */
static const char *furniture_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"max_seats", "current_occupants", "allowed_pos", "Value[3]",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
/* Generic fallback */
static const char *generic_val_labels[NUM_OBJ_VAL_POSITIONS] = {
"Value[0]", "Value[1]", "Value[2]", "Value[3]",
"Value[4]", "Value[5]", "Value[6]", "Value[7]"
};
static const char **get_val_labels(struct obj_data *obj)
{
switch (GET_OBJ_TYPE(obj)) {
case ITEM_LIGHT: return light_val_labels;
case ITEM_SCROLL:
case ITEM_POTION: return scroll_potion_val_labels;
case ITEM_WAND:
case ITEM_STAFF: return wand_staff_val_labels;
case ITEM_WEAPON: return weapon_val_labels;
case ITEM_ARMOR: return armor_val_labels;
case ITEM_CONTAINER: return container_val_labels;
case ITEM_DRINKCON:
case ITEM_FOUNTAIN: return drink_val_labels;
case ITEM_FOOD: return food_val_labels;
case ITEM_MONEY: return money_val_labels;
case ITEM_FURNITURE: return furniture_val_labels;
default: return generic_val_labels;
}
}
/* Utility and exported functions */
ACMD(do_oasis_oedit)
{
int number = NOWHERE, save = 0, real_num;
struct descriptor_data *d;
char buf1[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
/* No building as a mob or while being forced. */
if (IS_NPC(ch) || !ch->desc || STATE(ch->desc) != CON_PLAYING)
return;
/* Parse any arguments. */
two_arguments(argument, buf1, buf2);
/* If there aren't any arguments they can't modify anything. */
if (!*buf1) {
send_to_char(ch, "Specify an object VNUM to edit.\r\n");
return;
} else if (!isdigit(*buf1)) {
if (str_cmp("save", buf1) != 0) {
send_to_char(ch, "Yikes! Stop that, someone will get hurt!\r\n");
return;
}
save = TRUE;
if (is_number(buf2))
number = atoi(buf2);
else if (GET_OLC_ZONE(ch) > 0) {
zone_rnum zlok;
if ((zlok = real_zone(GET_OLC_ZONE(ch))) == NOWHERE)
number = NOWHERE;
else
number = genolc_zone_bottom(zlok);
}
if (number == NOWHERE) {
send_to_char(ch, "Save which zone?\r\n");
return;
}
}
/* If a numeric argument was given, get it. */
if (number == NOWHERE)
number = atoi(buf1);
if (number < IDXTYPE_MIN || number > IDXTYPE_MAX) {
send_to_char(ch, "That object VNUM can't exist.\r\n");
return;
}
/* Check that whatever it is isn't already being edited. */
for (d = descriptor_list; d; d = d->next) {
if (STATE(d) == CON_OEDIT) {
if (d->olc && OLC_NUM(d) == number) {
send_to_char(ch, "That object is currently being edited by %s.\r\n",
PERS(d->character, ch));
return;
}
}
}
/* Point d to the builder's descriptor (for easier typing later). */
d = ch->desc;
/* Give the descriptor an OLC structure. */
if (d->olc) {
mudlog(BRF, LVL_IMMORT, TRUE,
"SYSERR: do_oasis: Player already had olc structure.");
free(d->olc);
}
CREATE(d->olc, struct oasis_olc_data, 1);
/* Find the zone. */
OLC_ZNUM(d) = save ? real_zone(number) : real_zone_by_thing(number);
if (OLC_ZNUM(d) == NOWHERE) {
send_to_char(ch, "Sorry, there is no zone for that number!\r\n");
/* Free the descriptor's OLC structure. */
free(d->olc);
d->olc = NULL;
return;
}
/* Everyone but IMPLs can only edit zones they have been assigned. */
if (!can_edit_zone(ch, OLC_ZNUM(d))) {
send_cannot_edit(ch, zone_table[OLC_ZNUM(d)].number);
/* Free the OLC structure. */
free(d->olc);
d->olc = NULL;
return;
}
/* If we need to save, save the objects. */
if (save) {
send_to_char(ch, "Saving all objects in zone %d.\r\n",
zone_table[OLC_ZNUM(d)].number);
mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(ch)), TRUE,
"OLC: %s saves object info for zone %d.", GET_NAME(ch),
zone_table[OLC_ZNUM(d)].number);
/* Save the objects in this zone. */
save_objects(OLC_ZNUM(d));
/* Free the descriptor's OLC structure. */
free(d->olc);
d->olc = NULL;
return;
}
OLC_NUM(d) = number;
/* If a new object, setup new, otherwise setup the existing object. */
if ((real_num = real_object(number)) != NOTHING)
oedit_setup_existing(d, real_num);
else
oedit_setup_new(d);
oedit_disp_menu(d);
STATE(d) = CON_OEDIT;
/* Send the OLC message to the players in the same room as the builder. */
act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM);
SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING);
/* Log the OLC message. */
mudlog(CMP, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "OLC: %s starts editing zone %d allowed zone %d",
GET_NAME(ch), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch));
}
static void oedit_setup_new(struct descriptor_data *d)
{
CREATE(OLC_OBJ(d), struct obj_data, 1);
clear_object(OLC_OBJ(d));
OLC_OBJ(d)->name = strdup("unfinished object");
OLC_OBJ(d)->description = strdup("An unfinished object is lying here.");
OLC_OBJ(d)->short_description = strdup("an unfinished object");
SET_BIT_AR(GET_OBJ_WEAR(OLC_OBJ(d)), ITEM_WEAR_TAKE);
OLC_VAL(d) = 0;
OLC_DIRTY(d) = 0;
OLC_VAL_SLOT(d) = -1;
OLC_ITEM_TYPE(d) = OBJ_TRIGGER;
SCRIPT(OLC_OBJ(d)) = NULL;
OLC_OBJ(d)->proto_script = OLC_SCRIPT(d) = NULL;
}
void oedit_setup_existing(struct descriptor_data *d, int real_num)
{
struct obj_data *obj;
/* Allocate object in memory. */
CREATE(obj, struct obj_data, 1);
copy_object(obj, &obj_proto[real_num]);
/* Attach new object to player's descriptor. */
OLC_OBJ(d) = obj;
OLC_VAL(d) = 0;
OLC_DIRTY(d) = 0;
OLC_VAL_SLOT(d) = -1;
OLC_ITEM_TYPE(d) = OBJ_TRIGGER;
dg_olc_script_copy(d);
/* The edited obj must not have a script. It will be assigned to the updated
* obj later, after editing. */
SCRIPT(obj) = NULL;
OLC_OBJ(d)->proto_script = NULL;
}
void oedit_save_internally(struct descriptor_data *d)
{
int i;
obj_rnum robj_num;
struct descriptor_data *dsc;
struct obj_data *obj;
i = (real_object(OLC_NUM(d)) == NOTHING);
if ((robj_num = add_object(OLC_OBJ(d), OLC_NUM(d))) == NOTHING) {
log("oedit_save_internally: add_object failed.");
return;
}
/* Update triggers and free old proto list */
if (obj_proto[robj_num].proto_script &&
obj_proto[robj_num].proto_script != OLC_SCRIPT(d))
free_proto_script(&obj_proto[robj_num], OBJ_TRIGGER);
/* this will handle new instances of the object: */
obj_proto[robj_num].proto_script = OLC_SCRIPT(d);
/* this takes care of the objects currently in-game */
for (obj = object_list; obj; obj = obj->next) {
if (obj->item_number != robj_num)
continue;
/* remove any old scripts */
if (SCRIPT(obj))
extract_script(obj, OBJ_TRIGGER);
free_proto_script(obj, OBJ_TRIGGER);
copy_proto_script(&obj_proto[robj_num], obj, OBJ_TRIGGER);
assign_triggers(obj, OBJ_TRIGGER);
}
/* end trigger update */
if (!i) /* If it's not a new object, don't renumber. */
return;
/* Renumber produce in shops being edited. */
for (dsc = descriptor_list; dsc; dsc = dsc->next)
if (STATE(dsc) == CON_SEDIT)
for (i = 0; S_PRODUCT(OLC_SHOP(dsc), i) != NOTHING; i++)
if (S_PRODUCT(OLC_SHOP(dsc), i) >= robj_num)
S_PRODUCT(OLC_SHOP(dsc), i)++;
/* Update other people in zedit too. From: C.Raehl 4/27/99 */
for (dsc = descriptor_list; dsc; dsc = dsc->next)
if (STATE(dsc) == CON_ZEDIT)
for (i = 0; OLC_ZONE(dsc)->cmd[i].command != 'S'; i++)
switch (OLC_ZONE(dsc)->cmd[i].command) {
case 'P':
OLC_ZONE(dsc)->cmd[i].arg3 += (OLC_ZONE(dsc)->cmd[i].arg3 >= robj_num);
/* Fall through. */
case 'E':
case 'G':
case 'O':
OLC_ZONE(dsc)->cmd[i].arg1 += (OLC_ZONE(dsc)->cmd[i].arg1 >= robj_num);
break;
case 'R':
OLC_ZONE(dsc)->cmd[i].arg2 += (OLC_ZONE(dsc)->cmd[i].arg2 >= robj_num);
break;
default:
break;
}
}
static void oedit_save_to_disk(int zone_num)
{
save_objects(zone_num);
}
/* Menu functions */
/* For container flags. */
static void oedit_disp_container_flags_menu(struct descriptor_data *d)
{
int i, columns = 0;
clear_screen(d);
write_to_output(d, "-- Container Flags Menu --\r\n");
for (i = 0; i < NUM_CONTAINER_FLAGS; i++) {
write_to_output(d, "%2d) %-15s%s", i, container_bits[i],
(++columns % 3 ? "" : "\r\n"));
}
if (columns % 3)
write_to_output(d, "\r\n");
write_to_output(d, "Enter container flag number (toggles on/off): ");
}
/* For extra descriptions. */
static void oedit_disp_extradesc_menu(struct descriptor_data *d)
{
struct extra_descr_data *extra_desc = OLC_DESC(d);
get_char_colors(d->character);
clear_screen(d);
write_to_output(d,
"Extra desc menu\r\n"
"%s1%s) Keywords: %s%s\r\n"
"%s2%s) Description:\r\n%s%s\r\n"
"%s3%s) Goto next description: %s\r\n"
"%s0%s) Quit\r\n"
"Enter choice : ",
grn, nrm, yel, (extra_desc->keyword && *extra_desc->keyword) ? extra_desc->keyword : "<NONE>",
grn, nrm, yel, (extra_desc->description && *extra_desc->description) ? extra_desc->description : "<NONE>",
grn, nrm, !extra_desc->next ? "Not set." : "Set.", grn, nrm);
OLC_MODE(d) = OEDIT_EXTRADESC_MENU;
}
/* Ask for *which* apply to edit. */
static void oedit_disp_prompt_apply_menu(struct descriptor_data *d)
{
char apply_buf[MAX_STRING_LENGTH];
int counter;
get_char_colors(d->character);
clear_screen(d);
for (counter = 0; counter < MAX_OBJ_AFFECT; counter++) {
if (OLC_OBJ(d)->affected[counter].modifier) {
sprinttype(OLC_OBJ(d)->affected[counter].location, apply_types, apply_buf, sizeof(apply_buf));
write_to_output(d, " %s%d%s) %+d to %s\r\n", grn, counter + 1, nrm,
OLC_OBJ(d)->affected[counter].modifier, apply_buf);
} else {
write_to_output(d, " %s%d%s) None.\r\n", grn, counter + 1, nrm);
}
}
write_to_output(d, "\r\nEnter affection to modify (0 to quit) : ");
OLC_MODE(d) = OEDIT_PROMPT_APPLY;
}
/* Ask for liquid type. */
static void oedit_liquid_type(struct descriptor_data *d)
{
int i, columns = 0;
clear_screen(d);
write_to_output(d, "-- Liquid Types Menu --\r\n");
for (i = 0; i < NUM_LIQ_TYPES; i++) {
write_to_output(d, "%2d) %-10s%s", i, drinks[i],
(++columns % 4 ? "" : "\r\n"));
}
if (columns % 4)
write_to_output(d, "\r\n");
write_to_output(d, "Enter liquid type number : ");
}
/* The actual apply to set. */
static void oedit_disp_apply_menu(struct descriptor_data *d)
{
get_char_colors(d->character);
clear_screen(d);
column_list(d->character, 0, apply_types, NUM_APPLIES, TRUE);
write_to_output(d, "\r\nEnter apply type (0 is no apply) : ");
OLC_MODE(d) = OEDIT_APPLY;
}
/* Weapon type. */
static void oedit_disp_weapon_menu(struct descriptor_data *d)
{
int i;
clear_screen(d);
write_to_output(d, "-- Weapon Types Menu --\r\n");
for (i = 0; i < NUM_ATTACK_TYPES; i++) {
write_to_output(d, "%2d) %s\r\n", i, attack_hit_text[i].singular);
}
write_to_output(d, "Enter weapon type number : ");
}
/* Spell type. */
static void oedit_disp_spells_menu(struct descriptor_data *d)
{
int i, columns = 0;
clear_screen(d);
write_to_output(d, "-- Spells Menu --\r\n");
for (i = 0; i < NUM_SPELLS; i++) {
if (*spell_info[i].name != '!')
write_to_output(d, "%3d) %-20s%s", i, spell_info[i].name,
(++columns % 3 ? "" : "\r\n"));
}
if (columns % 3)
write_to_output(d, "\r\n");
write_to_output(d, "Enter spell number (0 = none): ");
}
/* Display all object values dynamically, with labels for known slots */
/* Display all object values dynamically with labels based on type */
static void oedit_disp_values_menu(struct descriptor_data *d)
{
int i;
struct obj_data *obj = OLC_OBJ(d);
const char **labels;
/* Select appropriate labels for this item type */
switch (GET_OBJ_TYPE(obj)) {
case ITEM_ARMOR: labels = armor_val_labels; break;
case ITEM_WEAPON: labels = weapon_val_labels; break;
case ITEM_DRINKCON:
case ITEM_FOUNTAIN: labels = drink_val_labels; break;
case ITEM_CONTAINER: labels = container_val_labels; break;
case ITEM_FURNITURE: labels = furniture_val_labels; break;
default: labels = generic_val_labels; break;
}
write_to_output(d, "\r\n-- Object Values Menu --\r\n");
for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) {
/* Hide current_occupants field for furniture - it's managed by the game engine */
if (GET_OBJ_TYPE(obj) == ITEM_FURNITURE && i == 1) {
write_to_output(d, "%2d) %-12s : %d (managed by game engine)\r\n",
i+1, labels[i], GET_OBJ_VAL(obj, i));
} else {
write_to_output(d, "%2d) %-12s : %d\r\n",
i+1, labels[i], GET_OBJ_VAL(obj, i));
}
}
write_to_output(d, "Q) Quit to main menu\r\nEnter choice : ");
OLC_MODE(d) = OEDIT_VALUES_MENU;
}
/* Object type. */
static void oedit_disp_type_menu(struct descriptor_data *d)
{
int counter, columns = 0;
get_char_colors(d->character);
clear_screen(d);
for (counter = 0; counter < NUM_ITEM_TYPES; counter++) {
write_to_output(d, "%s%2d%s) %-20.20s %s", grn, counter, nrm,
item_types[counter], !(++columns % 2) ? "\r\n" : "");
}
write_to_output(d, "\r\nEnter object type : ");
}
/* Object extra flags. */
static void oedit_disp_extra_menu(struct descriptor_data *d)
{
char buf[MAX_STRING_LENGTH];
int i, columns = 0;
clear_screen(d);
write_to_output(d, "-- Extra Flags Menu --\r\n");
for (i = 0; i < NUM_EXTRA_FLAGS; i++) {
/* Menu is 1-based for builders */
write_to_output(d, "%2d) %-20s%s", i + 1, extra_bits[i],
(++columns % 2 ? "" : "\r\n"));
}
if (columns % 2)
write_to_output(d, "\r\n");
/* Show current flags nicely */
sprintbitarray(GET_OBJ_EXTRA(OLC_OBJ(d)), extra_bits, EF_ARRAY_MAX, buf);
write_to_output(d, "\r\nObject flags: %s\r\n", buf);
write_to_output(d, "Enter object extra flag (0 to quit) : ");
}
/* Object perm flags. */
static void oedit_disp_perm_menu(struct descriptor_data *d)
{
char bits[MAX_STRING_LENGTH];
int counter, columns = 0;
get_char_colors(d->character);
clear_screen(d);
for (counter = 1; counter < NUM_AFF_FLAGS; counter++) {
write_to_output(d, "%s%2d%s) %-20.20s %s", grn, counter, nrm, affected_bits[counter], !(++columns % 2) ? "\r\n" : "");
}
sprintbitarray(GET_OBJ_AFFECT(OLC_OBJ(d)), affected_bits, EF_ARRAY_MAX, bits);
write_to_output(d, "\r\nObject permanent flags: %s%s%s\r\n"
"Enter object perm flag (0 to quit) : ", cyn, bits, nrm);
}
/* Object wear flags. */
static void oedit_disp_wear_menu(struct descriptor_data *d)
{
char bits[MAX_STRING_LENGTH];
int counter, columns = 0;
get_char_colors(d->character);
clear_screen(d);
for (counter = 0; counter < NUM_ITEM_WEARS; counter++) {
write_to_output(d, "%s%2d%s) %-20.20s %s", grn, counter + 1, nrm,
wear_bits[counter], !(++columns % 2) ? "\r\n" : "");
}
sprintbitarray(GET_OBJ_WEAR(OLC_OBJ(d)), wear_bits, TW_ARRAY_MAX, bits);
write_to_output(d, "\r\nWear flags: %s%s%s\r\n"
"Enter wear flag, 0 to quit : ", cyn, bits, nrm);
}
/* Display main menu. */
static void oedit_disp_menu(struct descriptor_data *d)
{
struct obj_data *obj;
char buf1[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH];
obj = OLC_OBJ(d);
get_char_colors(d->character);
clear_screen(d);
/* Build buffers for type/flags */
sprinttype(GET_OBJ_TYPE(obj), item_types, buf1, sizeof(buf1));
sprintbitarray(GET_OBJ_EXTRA(obj), extra_bits, EF_ARRAY_MAX, buf2);
/* First part of menu */
write_to_output(d,
"-- Item Number : [%s%d%s]\r\n"
"%s1%s) Keywords : %s%s\r\n"
"%s2%s) S-Desc : %s%s\r\n"
"%s3%s) L-Desc :-\r\n%s%s\r\n"
"%s4%s) M-Desc :-\r\n%s%s"
"%s5%s) Type : %s%s\r\n"
"%s6%s) Extra flags : %s%s\r\n",
cyn, OLC_NUM(d), nrm,
grn, nrm, yel, (obj->name && *obj->name) ? obj->name : "undefined",
grn, nrm, yel, (obj->short_description && *obj->short_description) ? obj->short_description : "undefined",
grn, nrm, yel, (obj->description && *obj->description) ? obj->description : "undefined",
grn, nrm, yel, (obj->main_description && *obj->main_description) ? obj->main_description : "Not Set.\r\n",
grn, nrm, cyn, buf1,
grn, nrm, cyn, buf2
);
/* Wear flags and affects */
sprintbitarray(GET_OBJ_WEAR(obj), wear_bits, TW_ARRAY_MAX, buf1);
sprintbitarray(GET_OBJ_AFFECT(obj), affected_bits, AF_ARRAY_MAX, buf2);
/* Second half of menu */
write_to_output(d,
"%s7%s) Wear flags : %s%s\r\n"
"%s8%s) Weight : %s%d\r\n"
"%s9%s) Cost : %s%d\r\n"
"%sA%s) Cost/Day : %s%d\r\n"
"%sB%s) Timer : %s%d\r\n"
"%sM%s) Min Level : %s%d\r\n"
"%sP%s) Perm Affects: %s%s\r\n"
"%sS%s) Script : %s%s\r\n"
"%sW%s) Copy object\r\n"
"%sX%s) Delete object\r\n",
grn, nrm, cyn, buf1,
grn, nrm, cyn, GET_OBJ_WEIGHT(obj),
grn, nrm, cyn, GET_OBJ_COST(obj),
grn, nrm, cyn, GET_OBJ_RENT(obj),
grn, nrm, cyn, GET_OBJ_TIMER(obj),
grn, nrm, cyn, GET_OBJ_LEVEL(obj),
grn, nrm, cyn, buf2,
grn, nrm, cyn, OLC_SCRIPT(d) ? "Set." : "Not Set.",
grn, nrm,
grn, nrm
);
/* Unified values menu */
write_to_output(d,
"%sV%s) Edit object values (%d slots total)\r\n",
grn, nrm, NUM_OBJ_VAL_POSITIONS);
/* Quit */
write_to_output(d,
"%sQ%s) Quit\r\n"
"Enter choice : ",
grn, nrm);
OLC_MODE(d) = OEDIT_MAIN_MENU;
}
/* main loop (of sorts).. basically interpreter throws all input to here. */
void oedit_parse(struct descriptor_data *d, char *arg)
{
int number;
char *oldtext = NULL;
switch (OLC_MODE(d)) {
case OEDIT_CONFIRM_SAVESTRING:
switch (*arg) {
case 'y':
case 'Y':
oedit_save_internally(d);
mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(d->character)), TRUE,
"OLC: %s edits obj %d", GET_NAME(d->character), OLC_NUM(d));
if (CONFIG_OLC_SAVE) {
oedit_save_to_disk(real_zone_by_thing(OLC_NUM(d)));
write_to_output(d, "Object saved to disk.\r\n");
} else
write_to_output(d, "Object saved to memory.\r\n");
cleanup_olc(d, CLEANUP_ALL);
return;
case 'n':
case 'N':
OLC_OBJ(d)->proto_script = OLC_SCRIPT(d);
free_proto_script(OLC_OBJ(d), OBJ_TRIGGER);
cleanup_olc(d, CLEANUP_ALL);
return;
case 'a':
case 'A':
oedit_disp_menu(d);
return;
default:
write_to_output(d, "Invalid choice!\r\n");
write_to_output(d, "Do you wish to save your changes? : \r\n");
return;
}
case OEDIT_MAIN_MENU:
switch (*arg) {
case 'q': case 'Q':
if (OLC_DIRTY(d)) {
write_to_output(d, "Do you wish to save your changes? : ");
OLC_MODE(d) = OEDIT_CONFIRM_SAVESTRING;
} else
cleanup_olc(d, CLEANUP_ALL);
return;
case '1':
write_to_output(d, "Enter keywords : ");
OLC_MODE(d) = OEDIT_KEYWORD;
break;
case '2':
write_to_output(d, "Enter short desc : ");
OLC_MODE(d) = OEDIT_SHORTDESC;
break;
case '3':
write_to_output(d, "Enter long desc :-\r\n| ");
OLC_MODE(d) = OEDIT_LONGDESC;
break;
case '4':
OLC_MODE(d) = OEDIT_MAINDESC;
send_editor_help(d);
write_to_output(d, "Enter action description:\r\n\r\n");
if (OLC_OBJ(d)->main_description) {
write_to_output(d, "%s", OLC_OBJ(d)->main_description);
oldtext = strdup(OLC_OBJ(d)->main_description);
}
string_write(d, &OLC_OBJ(d)->main_description,
MAX_MESSAGE_LENGTH, 0, oldtext);
OLC_DIRTY(d) = 1;
break;
case '5':
oedit_disp_type_menu(d);
OLC_MODE(d) = OEDIT_TYPE;
break;
case '6':
oedit_disp_extra_menu(d);
OLC_MODE(d) = OEDIT_EXTRAS;
break;
case '7':
oedit_disp_wear_menu(d);
OLC_MODE(d) = OEDIT_WEAR;
break;
case '8':
write_to_output(d, "Enter weight : ");
OLC_MODE(d) = OEDIT_WEIGHT;
break;
case '9':
write_to_output(d, "Enter cost : ");
OLC_MODE(d) = OEDIT_COST;
break;
case 'a': case 'A':
write_to_output(d, "Enter cost per day : ");
OLC_MODE(d) = OEDIT_COSTPERDAY;
break;
case 'b': case 'B':
write_to_output(d, "Enter timer : ");
OLC_MODE(d) = OEDIT_TIMER;
break;
case 'd': case 'D':
oedit_disp_prompt_apply_menu(d);
break;
case 'e': case 'E':
if (OLC_OBJ(d)->ex_description == NULL) {
CREATE(OLC_OBJ(d)->ex_description, struct extra_descr_data, 1);
OLC_OBJ(d)->ex_description->next = NULL;
}
OLC_DESC(d) = OLC_OBJ(d)->ex_description;
oedit_disp_extradesc_menu(d);
break;
case 'm': case 'M':
write_to_output(d, "Enter new minimum level: ");
OLC_MODE(d) = OEDIT_LEVEL;
break;
case 'p': case 'P':
oedit_disp_perm_menu(d);
OLC_MODE(d) = OEDIT_PERM;
break;
case 's': case 'S':
OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_MAIN_MENU;
OLC_MODE(d) = OLC_SCRIPT_EDIT;
dg_script_menu(d);
return;
case 'V': case 'v':
oedit_disp_values_menu(d);
return;
case 'w': case 'W':
write_to_output(d, "Copy what object (vnum or 0 to cancel): ");
OLC_MODE(d) = OEDIT_COPY;
break;
case 'x': case 'X':
write_to_output(d, "Are you sure you want to delete this object? ");
OLC_MODE(d) = OEDIT_DELETE;
break;
default:
oedit_disp_menu(d);
break;
}
return;
case OLC_SCRIPT_EDIT:
{
/* Optional: clean, immediate quit without extra DG reprint */
if (arg && (arg[0] == 'q' || arg[0] == 'Q') && arg[1] == '\0') {
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
/* Let DG handle input first. It returns non-zero to STAY in editor. */
if (dg_script_edit_parse(d, arg)) {
/* Still inside DGs triggers UI (possibly mid-prompt like “pos, vnum”) */
return;
}
/* Return value 0 means DG editor is finished -> go back to OEDIT menu */
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
case OEDIT_KEYWORD:
if (!genolc_checkstring(d, arg)) {
write_to_output(d, "Invalid keywords. Try again: ");
return; /* stay in OEDIT_KEYWORD waiting for a valid line */
}
if (OLC_OBJ(d)->name)
free(OLC_OBJ(d)->name);
OLC_OBJ(d)->name = str_udup(arg);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_SHORTDESC:
if (!genolc_checkstring(d, arg)) {
write_to_output(d, "Invalid short desc. Try again: ");
return; /* stay in OEDIT_SHORTDESC */
}
if (OLC_OBJ(d)->short_description)
free(OLC_OBJ(d)->short_description);
OLC_OBJ(d)->short_description = str_udup(arg);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_LONGDESC:
if (!genolc_checkstring(d, arg)) {
write_to_output(d, "Invalid long desc. Try again: ");
return; /* stay in OEDIT_LONGDESC */
}
if (OLC_OBJ(d)->description)
free(OLC_OBJ(d)->description);
OLC_OBJ(d)->description = str_udup(arg);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_MAINDESC:
/* Multi-line editor is correct here, requires '@' to finish */
send_editor_help(d);
write_to_output(d, "Enter action description:\r\n\r\n");
if (OLC_OBJ(d)->main_description) {
write_to_output(d, "%s", OLC_OBJ(d)->main_description);
oldtext = strdup(OLC_OBJ(d)->main_description);
}
string_write(d, &OLC_OBJ(d)->main_description, MAX_MESSAGE_LENGTH, 0, oldtext);
OLC_DIRTY(d) = 1;
return;
case OEDIT_TYPE:
number = atoi(arg);
if (number < 0 || number >= NUM_ITEM_TYPES) {
write_to_output(d, "Invalid choice, try again : ");
return;
}
GET_OBJ_TYPE(OLC_OBJ(d)) = number;
/* Reset values when type changes */
for (int i = 0; i < NUM_OBJ_VAL_POSITIONS; i++)
GET_OBJ_VAL(OLC_OBJ(d), i) = 0;
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_EXTRAS:
number = atoi(arg);
if (number < 0 || number > NUM_EXTRA_FLAGS) {
oedit_disp_extra_menu(d);
return;
} else if (number == 0) {
/* exit extras submenu */
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
} else {
/* Toggle: user picks 1..N, bit index is 0..N-1 */
TOGGLE_BIT_AR(GET_OBJ_EXTRA(OLC_OBJ(d)), (number - 1));
OLC_DIRTY(d) = 1;
oedit_disp_extra_menu(d);
return;
}
case OEDIT_WEAR:
number = atoi(arg);
if (number < 0 || number > NUM_ITEM_WEARS) {
write_to_output(d, "That's not a valid choice!\r\n");
oedit_disp_wear_menu(d);
return;
} else if (number == 0) {
/* exit wear submenu */
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
} else {
TOGGLE_BIT_AR(GET_OBJ_WEAR(OLC_OBJ(d)), (number - 1));
OLC_DIRTY(d) = 1;
oedit_disp_wear_menu(d);
return;
}
case OEDIT_WEIGHT:
GET_OBJ_WEIGHT(OLC_OBJ(d)) = LIMIT(atoi(arg), 0, MAX_OBJ_WEIGHT);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_COST:
GET_OBJ_COST(OLC_OBJ(d)) = LIMIT(atoi(arg), 0, MAX_OBJ_COST);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_COSTPERDAY:
GET_OBJ_RENT(OLC_OBJ(d)) = LIMIT(atoi(arg), 0, MAX_OBJ_RENT);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_TIMER:
GET_OBJ_TIMER(OLC_OBJ(d)) = LIMIT(atoi(arg), 0, MAX_OBJ_TIMER);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_LEVEL:
GET_OBJ_LEVEL(OLC_OBJ(d)) = LIMIT(atoi(arg), 0, LVL_IMPL);
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
case OEDIT_PERM:
if ((number = atoi(arg)) == 0) {
/* exit perm affects submenu */
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
if (number > 0 && number < NUM_AFF_FLAGS) {
if (number != AFF_CHARM) {
TOGGLE_BIT_AR(GET_OBJ_AFFECT(OLC_OBJ(d)), number);
}
OLC_DIRTY(d) = 1;
}
oedit_disp_perm_menu(d);
return;
/* === Values menu unified === */
case OEDIT_VALUES_MENU:
if (*arg == 'Q' || *arg == 'q') {
oedit_disp_menu(d);
return;
} else {
int i = atoi(arg) - 1;
if (i >= 0 && i < NUM_OBJ_VAL_POSITIONS) {
/* Prevent editing current_occupants for furniture - it's managed by the game engine */
if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_FURNITURE && i == 1) {
write_to_output(d, "The current_occupants field is managed by the game engine and cannot be edited manually.\r\n");
oedit_disp_values_menu(d);
return;
}
OLC_VAL_SLOT(d) = i;
const char **labels = get_val_labels(OLC_OBJ(d));
if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_WEAPON && i == 2) {
oedit_disp_weapon_menu(d);
OLC_MODE(d) = OEDIT_VALUE_X;
return;
}
if ((GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_SCROLL ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_POTION ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_WAND ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_STAFF) &&
(i == 1 || i == 2 || i == 3)) {
oedit_disp_spells_menu(d);
OLC_MODE(d) = OEDIT_VALUE_X;
return;
}
if ((GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_DRINKCON ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_FOUNTAIN) &&
i == 2) {
oedit_liquid_type(d);
OLC_MODE(d) = OEDIT_VALUE_X;
return;
}
if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_CONTAINER && i == 1) {
oedit_disp_container_flags_menu(d);
OLC_MODE(d) = OEDIT_VALUE_X;
return;
}
write_to_output(d, "Enter new integer for %s : ", labels[i]);
OLC_MODE(d) = OEDIT_VALUE_X;
} else {
write_to_output(d, "Invalid choice.\r\n");
oedit_disp_values_menu(d);
}
}
break;
case OEDIT_VALUE_X:
{
int i = OLC_VAL_SLOT(d);
int number = atoi(arg);
/* --- Armor-specific semantics --- */
if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_ARMOR) {
if (i == VAL_ARMOR_STEALTH_DISADV /* 3 */) {
GET_OBJ_VAL(OLC_OBJ(d), i) = (number != 0) ? 1 : 0;
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
if (i == VAL_ARMOR_STR_REQ /* 5 */) {
/* 0 disables the requirement; otherwise accept a sane STR range */
if (number < 0 || number > 25) {
write_to_output(d, "Enter STR requirement (0 disables, 3..25 typical): ");
return; /* stay in OEDIT_VALUE_X for a valid number */
}
GET_OBJ_VAL(OLC_OBJ(d), i) = number;
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
}
/* --- Existing special cases (weapon/liquid/spells/container) remain here --- */
if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_WEAPON && i == 2) {
if (number < 0 || number >= NUM_ATTACK_TYPES) {
oedit_disp_weapon_menu(d);
return;
}
GET_OBJ_VAL(OLC_OBJ(d), i) = number;
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
else if ((GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_SCROLL ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_POTION ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_WAND ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_STAFF) &&
(i == 1 || i == 2 || i == 3)) {
if (number < 0 || number >= NUM_SPELLS) {
oedit_disp_spells_menu(d);
return;
}
GET_OBJ_VAL(OLC_OBJ(d), i) = number;
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
else if ((GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_DRINKCON ||
GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_FOUNTAIN) &&
i == 2) {
if (number < 0 || number >= NUM_LIQ_TYPES) {
oedit_liquid_type(d);
return;
}
GET_OBJ_VAL(OLC_OBJ(d), i) = number;
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
else if (GET_OBJ_TYPE(OLC_OBJ(d)) == ITEM_CONTAINER && i == 1) {
extern const int NUM_CONTAINER_FLAGS;
if (number < 0 || number >= NUM_CONTAINER_FLAGS) {
oedit_disp_container_flags_menu(d);
return;
}
TOGGLE_BIT(GET_OBJ_VAL(OLC_OBJ(d), i), 1 << number);
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
/* --- Default assignment for other slots/types --- */
GET_OBJ_VAL(OLC_OBJ(d), i) = number;
OLC_DIRTY(d) = 1;
oedit_disp_values_menu(d);
return;
}
/* === Apply editing === */
case OEDIT_PROMPT_APPLY:
if ((number = atoi(arg)) == 0) break;
else if (number < 0 || number > MAX_OBJ_AFFECT) {
oedit_disp_prompt_apply_menu(d);
return;
}
OLC_VAL(d) = number - 1;
OLC_MODE(d) = OEDIT_APPLY;
oedit_disp_apply_menu(d);
return;
case OEDIT_APPLY:
if (((number = atoi(arg)) == 0) || ((number = atoi(arg)) == 1)) {
OLC_OBJ(d)->affected[OLC_VAL(d)].location = 0;
OLC_OBJ(d)->affected[OLC_VAL(d)].modifier = 0;
oedit_disp_prompt_apply_menu(d);
} else if (number < 0 || number > NUM_APPLIES)
oedit_disp_apply_menu(d);
else {
int counter;
if (GET_LEVEL(d->character) < LVL_IMPL) {
for (counter = 0; counter < MAX_OBJ_AFFECT; counter++) {
if (OLC_OBJ(d)->affected[counter].location == number) {
write_to_output(d, "Object already has that apply.");
return;
}
}
}
OLC_OBJ(d)->affected[OLC_VAL(d)].location = number - 1;
write_to_output(d, "Modifier : ");
OLC_DIRTY(d) = 1;
OLC_MODE(d) = OEDIT_APPLYMOD;
}
return;
case OEDIT_APPLYMOD:
OLC_OBJ(d)->affected[OLC_VAL(d)].modifier = atoi(arg);
OLC_DIRTY(d) = 1;
oedit_disp_prompt_apply_menu(d);
return;
/* === Extra descriptions === */
case OEDIT_EXTRADESC_KEY:
if (genolc_checkstring(d, arg)) {
if (OLC_DESC(d)->keyword) free(OLC_DESC(d)->keyword);
OLC_DESC(d)->keyword = str_udup(arg);
}
oedit_disp_extradesc_menu(d);
return;
case OEDIT_EXTRADESC_MENU:
switch ((number = atoi(arg))) {
case 0:
if (!OLC_DESC(d)->keyword || !OLC_DESC(d)->description) {
struct extra_descr_data *temp;
if (OLC_DESC(d)->keyword) free(OLC_DESC(d)->keyword);
if (OLC_DESC(d)->description) free(OLC_DESC(d)->description);
REMOVE_FROM_LIST(OLC_DESC(d), OLC_OBJ(d)->ex_description, next);
free(OLC_DESC(d));
OLC_DESC(d) = NULL;
OLC_DIRTY(d) = 1;
}
break;
case 1:
OLC_MODE(d) = OEDIT_EXTRADESC_KEY;
write_to_output(d, "Enter keywords, separated by spaces :-\r\n| ");
return;
case 2:
OLC_MODE(d) = OEDIT_EXTRADESC_DESCRIPTION;
send_editor_help(d);
write_to_output(d, "Enter the extra description:\r\n\r\n");
if (OLC_DESC(d)->description) {
write_to_output(d, "%s", OLC_DESC(d)->description);
oldtext = strdup(OLC_DESC(d)->description);
}
string_write(d, &OLC_DESC(d)->description, MAX_MESSAGE_LENGTH, 0, oldtext);
OLC_DIRTY(d) = 1;
return;
case 3:
if (OLC_DESC(d)->keyword && OLC_DESC(d)->description) {
struct extra_descr_data *new_extra;
if (OLC_DESC(d)->next)
OLC_DESC(d) = OLC_DESC(d)->next;
else {
CREATE(new_extra, struct extra_descr_data, 1);
OLC_DESC(d)->next = new_extra;
OLC_DESC(d) = OLC_DESC(d)->next;
}
}
default:
oedit_disp_extradesc_menu(d);
return;
}
break;
/* === Copy object === */
case OEDIT_COPY:
{
/* Trim leading spaces if you have a helper; otherwise simple checks below handle it */
/* skip_spaces(&arg); */
/* Treat empty input as cancel */
if (!arg || *arg == '\0') {
write_to_output(d, "Copy cancelled.\r\n");
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
/* Allow 0 or q/Q to cancel */
if ((arg[0] == '0' && arg[1] == '\0') ||
(arg[0] == 'q' && arg[1] == '\0') ||
(arg[0] == 'Q' && arg[1] == '\0')) {
write_to_output(d, "Copy cancelled.\r\n");
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
/* Require a number */
if (!is_number(arg)) {
write_to_output(d, "Please enter a vnum or 0 to cancel: ");
return; /* stay in OEDIT_COPY */
}
int vnum = atoi(arg);
if (vnum <= 0) {
write_to_output(d, "Copy cancelled.\r\n");
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
int rnum = real_object(vnum);
if (rnum == NOTHING) {
write_to_output(d, "That object does not exist. Enter vnum or 0 to cancel: ");
return; /* stay in OEDIT_COPY */
}
/* Success: clone into editor */
oedit_setup_existing(d, rnum);
OLC_MODE(d) = OEDIT_MAIN_MENU;
oedit_disp_menu(d);
return;
}
/* === Delete object === */
case OEDIT_DELETE:
if (*arg == 'y' || *arg == 'Y') {
if (delete_object(GET_OBJ_RNUM(OLC_OBJ(d))) != NOTHING)
write_to_output(d, "Object deleted.\r\n");
else
write_to_output(d, "Couldn't delete the object!\r\n");
cleanup_olc(d, CLEANUP_ALL);
} else if (*arg == 'n' || *arg == 'N') {
oedit_disp_menu(d);
OLC_MODE(d) = OEDIT_MAIN_MENU;
} else
write_to_output(d, "Please answer 'Y' or 'N': ");
return;
default:
mudlog(BRF, LVL_BUILDER, TRUE,
"SYSERR: OLC: Reached default case in oedit_parse()!");
write_to_output(d, "Oops...\r\n");
break;
}
/* Only redisplay main menu if we are in main menu mode */
if (OLC_MODE(d) == OEDIT_MAIN_MENU) {
oedit_disp_menu(d);
}
}
void oedit_string_cleanup(struct descriptor_data *d, int terminator)
{
switch (OLC_MODE(d)) {
case OEDIT_MAINDESC:
OLC_DIRTY(d) = 1;
oedit_disp_menu(d);
break;
case OEDIT_EXTRADESC_DESCRIPTION:
OLC_DIRTY(d) = 1;
oedit_disp_extradesc_menu(d);
break;
}
}