//***************************************************************************** // // oedit.c // // When obj 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 objs. So here it is... // //***************************************************************************** #include "../mud.h" #include "../utils.h" #include "../socket.h" #include "../races.h" #include "../world.h" #include "../object.h" #include "../character.h" #include "../extra_descs.h" #include "../prototype.h" #include "olc.h" #include "olc_submenus.h" //***************************************************************************** // mandatory modules //***************************************************************************** #include "../editor/editor.h" #include "../scripts/scripts.h" #include "../scripts/script_editor.h" #include "../items/items.h" #include "../items/iedit.h" //***************************************************************************** // obj 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? OBJ_DATA *obj; // our object, which holds most of our variables BUFFER *extra_code; // any extra code that should go to our prototype } OBJ_OLC; OBJ_OLC *newObjOLC(void) { OBJ_OLC *data = malloc(sizeof(OBJ_OLC)); data->key = strdup(""); data->parents = strdup(""); data->abstract = TRUE; data->obj = newObj(); objSetWeightRaw(data->obj, -1); data->extra_code = newBuffer(1); return data; } void deleteObjOLC(OBJ_OLC *data) { if(data->key) free(data->key); if(data->parents) free(data->parents); if(data->extra_code) deleteBuffer(data->extra_code); if(data->obj) deleteObj(data->obj); free(data); } const char *objOLCGetKey(OBJ_OLC *data) { return data->key; } const char *objOLCGetParents(OBJ_OLC *data) { return data->parents; } bool objOLCGetAbstract(OBJ_OLC *data) { return data->abstract; } OBJ_DATA *objOLCGetObj(OBJ_OLC *data) { return data->obj; } BUFFER *objOLCGetExtraCode(OBJ_OLC *data) { return data->extra_code; } void objOLCSetKey(OBJ_OLC *data, const char *key) { if(data->key) free(data->key); data->key = strdup(key); } void objOLCSetParents(OBJ_OLC *data, const char *parents) { if(data->parents) free(data->parents); data->parents = strdup(parents); } void objOLCSetAbstract(OBJ_OLC *data, bool abstract) { data->abstract = abstract; } // // takes in an obj prototype, and tries to generate an obj 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... OBJ_OLC *objOLCFromProto(PROTO_DATA *proto) { OBJ_OLC *data = newObjOLC(); OBJ_DATA *obj = objOLCGetObj(data); objOLCSetKey(data, protoGetKey(proto)); objOLCSetParents(data, protoGetParents(proto)); objOLCSetAbstract(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 " objSetName(obj, lptr); } else if(!strncmp(lptr, "me.mname", 8)) { while(*lptr != '\"') lptr++; lptr++; // kill the leading " lptr[strlen(lptr)-1] = '\0'; // kill the ending " objSetMultiName(obj, lptr); } else if(!strncmp(lptr, "me.rdesc", 8)) { while(*lptr != '\"') lptr++; lptr++; // kill the leading " lptr[strlen(lptr)-1] = '\0'; // kill the ending " objSetRdesc(obj, lptr); } else if(!strncmp(lptr, "me.mdesc", 8)) { while(*lptr != '\"') lptr++; lptr++; // kill the leading " lptr[strlen(lptr)-1] = '\0'; // kill the ending " objSetMultiRdesc(obj, 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 " objSetDesc(obj, lptr); // replace our \"s with " bufferReplace(objGetDescBuffer(obj), "\\\"", "\"", TRUE); bufferFormat(objGetDescBuffer(obj), 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 " objSetKeywords(obj, lptr); } else if(!strncmp(lptr, "me.bits", 7)) { while(*lptr != '\"') lptr++; lptr++; // kill the leading " lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending " bitSet(objGetBits(obj), lptr); } else if(!strncmp(lptr, "me.weight", 9)) { while(*lptr != '\0' && !isdigit(*lptr)) lptr++; objSetWeightRaw(obj, atof(lptr)); } else if(!strncmp(lptr, "me.edesc(", 9)) { while(*lptr != '\"') lptr++; lptr++; // kill the leading " char *desc_start = lptr + next_letter_in(lptr, '\"') + 1; lptr[next_letter_in(lptr, '\"')] = '\0'; // kill the ending " while(*desc_start != '\"') desc_start++; desc_start++; // kill start and end desc_start[next_letter_in(desc_start, '\"')] = '\0'; // "s for desc too edescSetPut(objGetEdescs(obj), newEdesc(lptr, desc_start)); } // setting an item type else if(!strncmp(lptr, "me.settype(", 11)) { char type[SMALL_BUFFER]; sscanf(lptr, "me.settype(\"%s", type); // kill our ending ") type[strlen(type)-2] = '\0'; objSetType(obj, type); // parse out all of our type info void *data = objGetTypeData(obj, type); BUFFER *type_buf = newBuffer(1); code = strcpyto(line, code, '\n'); while(*line && strcmp(line, "### end type") != 0) { bprintf(type_buf, "%s\n", line); code = strcpyto(line, code, '\n'); } // parse out our type info item_from_proto_func(type)(data, type_buf); // garbage collection deleteBuffer(type_buf); } 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(objGetTriggers(obj), trigname); } else if(!strcmp(lptr, "### begin extra code")) { code = strcpyto(line, code, '\n'); while(strcmp(line, "### end extra code") != 0) { bprintf(objOLCGetExtraCode(data), "%s\n", line); if(!*code) break; code = strcpyto(line, code, '\n'); } } } while(*code != '\0'); return data; } // // takes in an obj olc and tries to generate a prototype out of it PROTO_DATA *objOLCToProto(OBJ_OLC *data) { PROTO_DATA *proto = newProto(); OBJ_DATA *obj = objOLCGetObj(data); BUFFER *buf = protoGetScriptBuffer(proto); protoSetKey(proto, objOLCGetKey(data)); protoSetParents(proto, objOLCGetParents(data)); protoSetAbstract(proto, objOLCGetAbstract(data)); bprintf(buf, "### The following oproto was generated by oedit\n"); bprintf(buf, "### If you edit this script, adhere to the stylistic\n" "### conventions laid out by oedit, or delete the top line\n"); bprintf(buf, "\n### keywords, short descs, room descs, and look descs\n"); if(*objGetKeywords(obj)) bprintf(buf, "me.keywords = \"%s\" + \", \" + me.keywords\n", objGetKeywords(obj)); if(*objGetName(obj)) bprintf(buf, "me.name = \"%s\"\n", objGetName(obj)); if(*objGetMultiName(obj)) bprintf(buf, "me.mname = \"%s\"\n", objGetMultiName(obj)); if(*objGetRdesc(obj)) bprintf(buf, "me.rdesc = \"%s\"\n", objGetRdesc(obj)); if(*objGetMultiRdesc(obj)) bprintf(buf, "me.mdesc = \"%s\"\n", objGetMultiRdesc(obj)); if(*objGetDesc(obj)) { BUFFER *desc_copy = bufferCopy(objGetDescBuffer(obj)); 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); } // extra descriptions if(listSize(edescSetGetList(objGetEdescs(obj))) > 0) { bprintf(buf, "\n### extra descriptions\n"); LIST_ITERATOR *edesc_i= newListIterator(edescSetGetList(objGetEdescs(obj))); EDESC_DATA *edesc= NULL; ITERATE_LIST(edesc, edesc_i) { BUFFER *desc_copy = bufferCopy(edescGetDescBuffer(edesc)); bufferReplace(desc_copy, "\n", " ", TRUE); bufferReplace(desc_copy, "\r", "", TRUE); bprintf(buf, "me.edesc(\"%s\", \"%s\")\n", edescGetKeywords(edesc), bufferString(desc_copy)); deleteBuffer(desc_copy); } deleteListIterator(edesc_i); } if(*bitvectorGetBits(objGetBits(obj))) { bprintf(buf, "\n### object bits\n"); bprintf(buf, "me.bits = \"%s\" + \", \" + me.bits\n", bitvectorGetBits(objGetBits(obj))); } if(objGetWeight(obj) > 0) { bprintf(buf, "\n### numeric values\n"); bprintf(buf, "me.weight = %1.3lf\n", objGetWeightRaw(obj)); } // item types LIST *item_types = itemTypeList(); LIST_ITERATOR *type_i = newListIterator(item_types); char *type = NULL; ITERATE_LIST(type, type_i) { if(objIsType(obj, type)) { void *data = objGetTypeData(obj, type); bprintf(buf, "\n### set type: %s\n", type); bprintf(buf, "me.settype(\"%s\")\n", type); item_to_proto_func(type)(data, buf); bprintf(buf, "### end type\n"); } } deleteListIterator(type_i); deleteListWith(item_types, free); if(listSize(objGetTriggers(obj)) > 0) { bprintf(buf, "\n### object triggers\n"); LIST_ITERATOR *trig_i = newListIterator(objGetTriggers(obj)); char *trig = NULL; ITERATE_LIST(trig, trig_i) { bprintf(buf, "me.attach(\"%s\")\n", trig); } deleteListIterator(trig_i); } if(bufferLength(objOLCGetExtraCode(data)) > 0) { bprintf(buf, "\n### begin extra code\n"); bprintf(buf, "%s", bufferString(objOLCGetExtraCode(data))); bprintf(buf, "### end extra code\n"); } return proto; } //***************************************************************************** // object editing //***************************************************************************** #define OEDIT_PARENTS 1 #define OEDIT_NAME 2 #define OEDIT_MULTI_NAME 3 #define OEDIT_KEYWORDS 4 #define OEDIT_RDESC 5 #define OEDIT_MULTI_RDESC 6 #define OEDIT_WEIGHT 7 void oedit_menu(SOCKET_DATA *sock, OBJ_OLC *data) { char weight_buf[SMALL_BUFFER]; if(objGetWeightRaw(objOLCGetObj(data)) <= 0) sprintf(weight_buf, "leave unchanged"); else sprintf(weight_buf, "%1.3lf", objGetWeightRaw(objOLCGetObj(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" "{gW) Weight : {c%s\r\n" "{gI) Edit item types: {c%s\r\n" "{gB) Edit bitvector : {c%s\r\n" "{gT) Trigger menu\r\n" "{gX) Extra Descriptions menu\r\n", objOLCGetKey(data), (objOLCGetAbstract(data) ? "yes" : "no"), objOLCGetParents(data), objGetName(objOLCGetObj(data)), objGetMultiName(objOLCGetObj(data)), objGetKeywords(objOLCGetObj(data)), objGetRdesc(objOLCGetObj(data)), objGetMultiRdesc(objOLCGetObj(data)), objGetDesc(objOLCGetObj(data)), weight_buf, objGetTypes(objOLCGetObj(data)), bitvectorGetBits(objGetBits(objOLCGetObj(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(objOLCGetExtraCode(data)), FALSE); } int oedit_chooser(SOCKET_DATA *sock, OBJ_OLC *data, const char *option) { switch(toupper(*option)) { case '1': objOLCSetAbstract(data, (objOLCGetAbstract(data) + 1) % 2); return MENU_NOCHOICE; case '2': text_to_buffer(sock,"Enter comma-separated list of objs to inherit from: "); return OEDIT_PARENTS; case '3': text_to_buffer(sock, "Enter name: "); return OEDIT_NAME; case '4': text_to_buffer(sock, "Enter name for multiple occurences: "); return OEDIT_MULTI_NAME; case '5': text_to_buffer(sock, "Enter keywords: "); return OEDIT_KEYWORDS; case '6': text_to_buffer(sock, "Enter room description: "); return OEDIT_RDESC; case '7': text_to_buffer(sock, "Enter room description for multiple occurences: "); return OEDIT_MULTI_RDESC; case '8': text_to_buffer(sock, "Enter description\r\n"); socketStartEditor(sock, text_editor, objGetDescBuffer(objOLCGetObj(data))); return MENU_NOCHOICE; case 'W': text_to_buffer(sock, "Enter new weight: "); return OEDIT_WEIGHT; case 'X': do_olc(sock, edesc_set_menu, edesc_set_chooser, edesc_set_parser, NULL,NULL, NULL, NULL, objGetEdescs(objOLCGetObj(data))); return MENU_NOCHOICE; case 'I': do_olc(sock, iedit_menu, iedit_chooser, iedit_parser, NULL, NULL, NULL, NULL, objOLCGetObj(data)); return MENU_NOCHOICE; case 'B': do_olc(sock, bedit_menu, bedit_chooser, bedit_parser, NULL, NULL, NULL, NULL, objGetBits(objOLCGetObj(data))); return MENU_NOCHOICE; case 'T': do_olc(sock, trigger_list_menu, trigger_list_chooser, trigger_list_parser, NULL, NULL, NULL, NULL, objGetTriggers(objOLCGetObj(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, objOLCGetExtraCode(data)); return MENU_NOCHOICE; default: return MENU_CHOICE_INVALID; } } bool oedit_parser(SOCKET_DATA *sock, OBJ_OLC *data, int choice, const char *arg){ switch(choice) { case OEDIT_PARENTS: objOLCSetParents(data, arg); return TRUE; case OEDIT_NAME: objSetName(objOLCGetObj(data), arg); return TRUE; case OEDIT_MULTI_NAME: objSetMultiName(objOLCGetObj(data), arg); return TRUE; case OEDIT_KEYWORDS: objSetKeywords(objOLCGetObj(data), arg); return TRUE; case OEDIT_RDESC: objSetRdesc(objOLCGetObj(data), arg); return TRUE; case OEDIT_MULTI_RDESC: objSetMultiRdesc(objOLCGetObj(data), arg); return TRUE; case OEDIT_WEIGHT: { double val = atof(arg); if(val < 0) return FALSE; objSetWeightRaw(objOLCGetObj(data), val); return TRUE; } default: return FALSE; } } void save_obj_olc(OBJ_OLC *data) { PROTO_DATA *old_proto = worldGetType(gameworld, "oproto", objOLCGetKey(data)); PROTO_DATA *new_proto = objOLCToProto(data); if(old_proto == NULL) worldPutType(gameworld, "oproto", protoGetKey(new_proto), new_proto); else { protoCopyTo(new_proto, old_proto); deleteProto(new_proto); } worldSaveType(gameworld, "oproto", objOLCGetKey(data)); } //***************************************************************************** // commands //***************************************************************************** COMMAND(cmd_oedit) { 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 obj 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 obj 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 OBJ_OLC *data = NULL; // try to pull up the prototype proto = worldGetType(gameworld, "oproto", get_fullkey(name, locale)); // if we already have proto data, try to parse an obj olc out of it if(proto != NULL) { // check to make sure the prototype was made by oedit char line[SMALL_BUFFER]; strcpyto(line, protoGetScript(proto), '\n'); if(strcmp(line, "### The following oproto was generated by oedit")!=0){ send_to_char(ch, "This obj was not generated by oedit and potential " "formatting problems prevent oedit from being used. To " "edit, opedit must be used\r\n"); return; } else data = objOLCFromProto(proto); } // otherwise, make a new obj olc and assign its key else { data = newObjOLC(); objOLCSetKey(data, get_fullkey(name, locale)); objOLCSetAbstract(data, TRUE); OBJ_DATA *obj = objOLCGetObj(data); objSetName (obj, "an unfinished object"); objSetKeywords (obj, "object, unfinshed"); objSetRdesc (obj, "an unfinished object is lying here."); objSetDesc (obj, "it looks unfinished.\r\n"); objSetMultiName (obj, "a group of %d unfinished objects"); objSetMultiRdesc(obj, "%d objects lay here, all unfinished."); } do_olc(charGetSocket(ch), oedit_menu, oedit_chooser, oedit_parser, NULL, NULL, deleteObjOLC, save_obj_olc, data); } } }