//*****************************************************************************
//
// medit.c
//
// When mob prototypes became python scripts, OLC for mobs had to be rethought.
// Ideally, all builders would have a basic grasp on python and thus would be
// able to write scripts. Ideally. Sadly, I don't think this can be expected
// out of most builders, and we still need some sort of non-scripting interface
// for editing mobs. So here it is...
//
//*****************************************************************************
#include "../mud.h"
#include "../utils.h"
#include "../socket.h"
#include "../races.h"
#include "../world.h"
#include "../character.h"
#include "../prototype.h"
#include "olc.h"
//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../editor/editor.h"
#include "../scripts/scripts.h"
#include "../scripts/script_editor.h"
//*****************************************************************************
// mob olc structure, and functions
//*****************************************************************************
typedef struct {
char *key; // the key for our prototype
char *parents; // things we inherit from
bool abstract; // can we be laoded into the game?
CHAR_DATA *ch; // our character, which holds most of our variables
BUFFER *extra_code; // any extra code that should go to our prototype
} CHAR_OLC;
CHAR_OLC *newCharOLC(void) {
CHAR_OLC *data = malloc(sizeof(CHAR_OLC));
data->key = strdup("");
data->parents = strdup("");
data->abstract = TRUE;
data->ch = newMobile();
charSetRace(data->ch, "");
data->extra_code = newBuffer(1);
charSetSex(data->ch, SEX_NONE);
return data;
}
void deleteCharOLC(CHAR_OLC *data) {
if(data->key) free(data->key);
if(data->parents) free(data->parents);
if(data->extra_code) deleteBuffer(data->extra_code);
if(data->ch) deleteChar(data->ch);
free(data);
}
const char *charOLCGetKey(CHAR_OLC *data) {
return data->key;
}
const char *charOLCGetParents(CHAR_OLC *data) {
return data->parents;
}
bool charOLCGetAbstract(CHAR_OLC *data) {
return data->abstract;
}
CHAR_DATA *charOLCGetChar(CHAR_OLC *data) {
return data->ch;
}
BUFFER *charOLCGetExtraCode(CHAR_OLC *data) {
return data->extra_code;
}
void charOLCSetKey(CHAR_OLC *data, const char *key) {
if(data->key) free(data->key);
data->key = strdup(key);
}
void charOLCSetParents(CHAR_OLC *data, const char *parents) {
if(data->parents) free(data->parents);
data->parents = strdup(parents);
}
void charOLCSetAbstract(CHAR_OLC *data, bool abstract) {
data->abstract = abstract;
}
//
// takes in a char prototype, and tries to generate a char olc out of it. This
// function is messy and ugly and icky and yuck. But, alas, I cannot think of
// a better way to do this. Maybe next version...
CHAR_OLC *charOLCFromProto(PROTO_DATA *proto) {
CHAR_OLC *data = newCharOLC();
CHAR_DATA *ch = charOLCGetChar(data);
charOLCSetKey(data, protoGetKey(proto));
charOLCSetParents(data, protoGetParents(proto));
charOLCSetAbstract(data, protoIsAbstract(proto));
// this is a really ugly way to do the conversion, but basically let's
// just look through every line in the buffer and if we recognize some
// token, parse out whatever is assigned to it
char line[MAX_BUFFER];
const char *code = protoGetScript(proto);
do {
code = strcpyto(line, code, '\n');
char *lptr = line;
if(!strncmp(lptr, "me.name", 7)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetName(ch, lptr);
}
else if(!strncmp(lptr, "me.mname", 8)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetMultiName(ch, lptr);
}
else if(!strncmp(lptr, "me.rdesc", 8)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetRdesc(ch, lptr);
}
else if(!strncmp(lptr, "me.mdesc", 8)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetMultiRdesc(ch, lptr);
}
else if(!strncmp(lptr, "me.desc", 7)) {
// we have three "'s to skip by, because this lptr will take the form:
// me.desc = me.desc + " " + "..."
while(*lptr != '\"') lptr++; lptr++;
while(*lptr != '\"') lptr++; lptr++;
while(*lptr != '\"') lptr++; lptr++;
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetDesc(ch, lptr);
// replace our \"s with "
bufferReplace(charGetDescBuffer(ch), "\\\"", "\"", TRUE);
bufferFormat(charGetDescBuffer(ch), SCREEN_WIDTH, PARA_INDENT);
}
else if(!strncmp(lptr, "me.keywords", 11)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending "
charSetKeywords(ch, lptr);
}
else if(!strncmp(lptr, "me.gender", 9)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetSex(ch, sexGetNum(lptr));
}
else if(!strncmp(lptr, "me.race", 7)) {
while(*lptr != '\"') lptr++;
lptr++; // kill the leading "
lptr[strlen(lptr)-1] = '\0'; // kill the ending "
charSetRace(ch, lptr);
}
else if(!strncmp(lptr, "me.attach(\"", 11)) {
char trigname[SMALL_BUFFER];
sscanf(lptr, "me.attach(\"%s", trigname);
// kill our ending ")
trigname[strlen(trigname)-2] = '\0';
triggerListAdd(charGetTriggers(ch), trigname);
}
else if(!strcmp(lptr, "### begin extra code")) {
code = strcpyto(line, code, '\n');
while(strcmp(line, "### end extra code") != 0) {
bprintf(charOLCGetExtraCode(data), "%s\n", line);
if(!*code) break;
code = strcpyto(line, code, '\n');
}
}
// we didn't recognize the line... just ignore it
else
continue;
} while(*code != '\0');
return data;
}
//
// takes in a char olc and tries to generate a prototype out of it
PROTO_DATA *charOLCToProto(CHAR_OLC *data) {
PROTO_DATA *proto = newProto();
CHAR_DATA *ch = charOLCGetChar(data);
BUFFER *buf = protoGetScriptBuffer(proto);
protoSetKey(proto, charOLCGetKey(data));
protoSetParents(proto, charOLCGetParents(data));
protoSetAbstract(proto, charOLCGetAbstract(data));
bprintf(buf, "### The following mproto was generated by medit\n");
bprintf(buf, "### If you edit this script, adhere to the stylistic\n"
"### conventions laid out by medit, or delete the top line\n");
bprintf(buf, "\n### keywords, short descs, room descs, and look descs\n");
if(*charGetKeywords(ch))
bprintf(buf, "me.keywords = \"%s\" + \", \" + me.keywords\n",
charGetKeywords(ch));
if(*charGetName(ch))
bprintf(buf, "me.name = \"%s\"\n", charGetName(ch));
if(*charGetMultiName(ch))
bprintf(buf, "me.mname = \"%s\"\n", charGetMultiName(ch));
if(*charGetRdesc(ch))
bprintf(buf, "me.rdesc = \"%s\"\n", charGetRdesc(ch));
if(*charGetMultiRdesc(ch))
bprintf(buf, "me.mdesc = \"%s\"\n", charGetMultiRdesc(ch));
if(*charGetDesc(ch)) {
BUFFER *desc_copy = bufferCopy(charGetDescBuffer(ch));
bufferReplace(desc_copy, "\n", " ", TRUE);
bufferReplace(desc_copy, "\r", "", TRUE);
bufferReplace(desc_copy, "\"", "\\\"", TRUE);
bprintf(buf, "me.desc = me.desc + \" \" + \"%s\"\n",
bufferString(desc_copy));
deleteBuffer(desc_copy);
}
if(*charGetRace(ch) || charGetSex(ch) != SEX_NONE) {
bprintf(buf, "\n### race and gender\n");
if(*charGetRace(ch))
bprintf(buf, "me.race = \"%s\"\n", charGetRace(ch));
if(charGetSex(ch) != SEX_NONE)
bprintf(buf, "me.gender = \"%s\"\n", sexGetName(charGetSex(ch)));
}
if(listSize(charGetTriggers(ch)) > 0) {
bprintf(buf, "\n### character triggers\n");
LIST_ITERATOR *trig_i = newListIterator(charGetTriggers(ch));
char *trig = NULL;
ITERATE_LIST(trig, trig_i) {
bprintf(buf, "me.attach(\"%s\")\n", trig);
} deleteListIterator(trig_i);
}
if(bufferLength(charOLCGetExtraCode(data)) > 0) {
bprintf(buf, "\n### begin extra code\n");
bprintf(buf, "%s", bufferString(charOLCGetExtraCode(data)));
bprintf(buf, "### end extra code\n");
}
return proto;
}
//*****************************************************************************
// mobile editing
//*****************************************************************************
#define MEDIT_PARENTS 1
#define MEDIT_NAME 2
#define MEDIT_MULTI_NAME 3
#define MEDIT_KEYWORDS 4
#define MEDIT_RDESC 5
#define MEDIT_MULTI_RDESC 6
#define MEDIT_RACE 7
#define MEDIT_SEX 8
void medit_menu(SOCKET_DATA *sock, CHAR_OLC *data) {
send_to_socket(sock,
"{g[{c%s{g]\r\n"
"{g1) Abstract: {c%s\r\n"
"{g2) Inherits from prototypes:\r\n"
"{c%s\r\n"
"{g3) Name\r\n"
"{c%s\r\n"
"{g4) Name for multiple occurences:\r\n"
"{c%s\r\n"
"{g5) Keywords:\r\n"
"{c%s\r\n"
"{g6) Room description:\r\n"
"{c%s\r\n"
"{g7) Room description for multiple occurences:\r\n"
"{c%s\r\n"
"{g8) Description:\r\n"
"{c%s"
"{gT) Trigger menu\r\n"
"{gR) Change race {y[{c%8s{y]\r\n"
"{gG) Change Gender {y[{c%8s{y]\r\n",
charOLCGetKey(data),
(charOLCGetAbstract(data) ? "yes" : "no"),
charOLCGetParents(data),
charGetName(charOLCGetChar(data)),
charGetMultiName(charOLCGetChar(data)),
charGetKeywords(charOLCGetChar(data)),
charGetRdesc(charOLCGetChar(data)),
charGetMultiRdesc(charOLCGetChar(data)),
charGetDesc(charOLCGetChar(data)),
(!*charGetRace(charOLCGetChar(data)) ?
"leave unchanged" :
charGetRace(charOLCGetChar(data))),
(charGetSex(charOLCGetChar(data)) == SEX_NONE ?
"leave unchanged" :
sexGetName(charGetSex(charOLCGetChar(data))))
);
// only allow code editing for people with scripting priviledges
send_to_socket(sock, "{gC) Extra code%s\r\n",
((!socketGetChar(sock) ||
!bitIsOneSet(charGetUserGroups(socketGetChar(sock)),
"scripter")) ? " {y({cuneditable{y){g":""));
script_display(sock, bufferString(charOLCGetExtraCode(data)), FALSE);
}
int medit_chooser(SOCKET_DATA *sock, CHAR_OLC *data, const char *option) {
switch(toupper(*option)) {
case '1':
charOLCSetAbstract(data, (charOLCGetAbstract(data) + 1) % 2);
return MENU_NOCHOICE;
case '2':
text_to_buffer(sock,"Enter comma-separated list of mobs to inherit from: ");
return MEDIT_PARENTS;
case '3':
text_to_buffer(sock, "Enter name: ");
return MEDIT_NAME;
case '4':
text_to_buffer(sock, "Enter name for multiple occurences: ");
return MEDIT_MULTI_NAME;
case '5':
text_to_buffer(sock, "Enter keywords: ");
return MEDIT_KEYWORDS;
case '6':
text_to_buffer(sock, "Enter room description: ");
return MEDIT_RDESC;
case '7':
text_to_buffer(sock, "Enter room description for multiple occurences: ");
return MEDIT_MULTI_RDESC;
case '8':
text_to_buffer(sock, "Enter description\r\n");
socketStartEditor(sock,text_editor,charGetDescBuffer(charOLCGetChar(data)));
return MENU_NOCHOICE;
case 'R':
send_to_socket(sock, "%s\r\n\r\n", raceGetList(FALSE));
text_to_buffer(sock, "Please select a race: ");
return MEDIT_RACE;
case 'G':
olc_display_table(sock, sexGetName, NUM_SEXES, 1);
text_to_buffer(sock, "Pick a gender: ");
return MEDIT_SEX;
case 'T':
do_olc(sock, trigger_list_menu, trigger_list_chooser, trigger_list_parser,
NULL, NULL, NULL, NULL, charGetTriggers(charOLCGetChar(data)));
return MENU_NOCHOICE;
case 'C':
// only scripters can edit extra code
if(!socketGetChar(sock) ||
!bitIsOneSet(charGetUserGroups(socketGetChar(sock)), "scripter"))
return MENU_CHOICE_INVALID;
text_to_buffer(sock, "Edit extra code\r\n");
socketStartEditor(sock,script_editor,charOLCGetExtraCode(data));
return MENU_NOCHOICE;
default: return MENU_CHOICE_INVALID;
}
}
bool medit_parser(SOCKET_DATA *sock, CHAR_OLC *data, int choice,
const char *arg) {
switch(choice) {
case MEDIT_PARENTS:
charOLCSetParents(data, arg);
return TRUE;
case MEDIT_NAME:
charSetName(charOLCGetChar(data), arg);
return TRUE;
case MEDIT_MULTI_NAME:
charSetMultiName(charOLCGetChar(data), arg);
return TRUE;
case MEDIT_KEYWORDS:
charSetKeywords(charOLCGetChar(data), arg);
return TRUE;
case MEDIT_RDESC:
charSetRdesc(charOLCGetChar(data), arg);
return TRUE;
case MEDIT_MULTI_RDESC:
charSetMultiRdesc(charOLCGetChar(data), arg);
return TRUE;
case MEDIT_RACE:
if(!isRace(arg))
return FALSE;
charSetRace(charOLCGetChar(data), arg);
return TRUE;
case MEDIT_SEX: {
int val = atoi(arg);
if(!isdigit(*arg) || val < 0 || val >= NUM_SEXES)
return FALSE;
charSetSex(charOLCGetChar(data), val);
return TRUE;
}
default: return FALSE;
}
}
void save_mob_olc(CHAR_OLC *data) {
PROTO_DATA *old_proto = worldGetType(gameworld, "mproto",charOLCGetKey(data));
PROTO_DATA *new_proto = charOLCToProto(data);
if(old_proto == NULL)
worldPutType(gameworld, "mproto", protoGetKey(new_proto), new_proto);
else {
protoCopyTo(new_proto, old_proto);
deleteProto(new_proto);
}
worldSaveType(gameworld, "mproto", charOLCGetKey(data));
}
//*****************************************************************************
// commands
//*****************************************************************************
COMMAND(cmd_medit) {
ZONE_DATA *zone = NULL;
PROTO_DATA *proto = NULL;
// we need a key
if(!arg || !*arg)
send_to_char(ch, "What is the name of the mob you want to edit?\r\n");
else {
char locale[SMALL_BUFFER];
char name[SMALL_BUFFER];
if(!parse_worldkey_relative(ch, arg, name, locale))
send_to_char(ch, "Which mob are you trying to edit?\r\n");
// make sure we can edit the zone
else if((zone = worldGetZone(gameworld, locale)) == NULL)
send_to_char(ch, "No such zone exists.\r\n");
else if(!canEditZone(zone, ch))
send_to_char(ch, "You are not authorized to edit that zone.\r\n");
else {
// try to make our OLC datastructure
CHAR_OLC *data = NULL;
// try to pull up the prototype
proto = worldGetType(gameworld, "mproto", get_fullkey(name, locale));
// if we already have proto data, try to parse a char olc out of it
if(proto != NULL) {
// check to make sure the prototype was made by medit
char line[SMALL_BUFFER];
strcpyto(line, protoGetScript(proto), '\n');
if(strcmp(line, "### The following mproto was generated by medit")!=0){
send_to_char(ch, "This mob was not generated by medit and potential "
"formatting problems prevent medit from being used. To "
"edit, mpedit must be used\r\n");
return;
}
else
data = charOLCFromProto(proto);
}
// otherwise, make a new char olc and assign its key
else {
data = newCharOLC();
charOLCSetKey(data, get_fullkey(name, locale));
charOLCSetAbstract(data, TRUE);
CHAR_DATA *mob = charOLCGetChar(data);
charSetName(mob, "an unfinished mobile");
charSetMultiName(mob, "%d unfinished mobiles");
charSetKeywords(mob, "mobile, unfinished");
charSetRdesc(mob, "an unfinished mobile is standing here.");
charSetMultiRdesc(mob, "A group of %d mobiles are here, "
"looking unfinished.");
charSetDesc(mob, "It looks unfinished.\r\n");
}
do_olc(charGetSocket(ch), medit_menu, medit_chooser, medit_parser,
NULL, NULL, deleteCharOLC, save_mob_olc, data);
}
}
}