tbamud/src/dg_olc.c
Kevin Fischer b27003e881 Make some string ops bounded and fix bug in editor toggle command (#54)
* Replace a few strcat/sprintf instances with bounded variants

Also cleaned up the whitespace in the parse_edit_action function as it was
not consistent.

Fix bug in editor format command introduced in earlier commit

* Fix bug in editor toggle command when an escaped @ is in the buffer

Previously, toggling between @ and \t would always try to convert
@ to \t, even if already toggled, iff an escaped @ was present in the
buffer (i.e. '@@').
2018-07-16 05:17:45 -04:00

1180 lines
36 KiB
C

/**************************************************************************
* File: dg_olc.c Part of tbaMUD *
* Usage: This source file is used in extending Oasis OLC for trigedit. *
* *
* $Author: Chris Jacobsen/Mark A. Heilpern/egreen/Welcor $ *
* $Date: 2004/10/11 12:07:00$ *
* $Revision: 1.0.14 $ *
**************************************************************************/
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "genolc.h"
#include "interpreter.h"
#include "oasis.h"
#include "dg_olc.h"
#include "dg_event.h"
#include "genzon.h" /* for real_zone_by_thing */
#include "constants.h" /* for the *trig_types */
#include "modify.h" /* for smash_tilde */
/* local functions */
static void trigedit_disp_menu(struct descriptor_data *d);
static void trigedit_disp_types(struct descriptor_data *d);
static void trigedit_create_index(int znum, char *type);
static void trigedit_setup_new(struct descriptor_data *d);
/* Trigedit */
ACMD(do_oasis_trigedit)
{
int number, real_num;
struct descriptor_data *d;
/* No building as a mob or while being forced. */
if (IS_NPC(ch) || !ch->desc || STATE(ch->desc) != CON_PLAYING)
return;
/* Parse any arguments. */
skip_spaces(&argument);
if (!*argument || !isdigit(*argument)) {
send_to_char(ch, "Specify a trigger VNUM to edit.\r\n");
return;
}
number = atoi(argument);
if (number < IDXTYPE_MIN || number > IDXTYPE_MAX) {
send_to_char(ch, "That trigger VNUM can't exist.\r\n");
return;
}
/* Check that it isn't already being edited. */
for (d = descriptor_list; d; d = d->next) {
if (STATE(d) == CON_TRIGEDIT) {
if (d->olc && OLC_NUM(d) == number) {
send_to_char(ch, "That trigger is currently being edited by %s.\r\n",
GET_NAME(d->character));
return;
}
}
}
d = ch->desc;
/* Give descriptor an OLC structure. */
if (d->olc) {
mudlog(BRF, LVL_BUILDER, TRUE,
"SYSERR: do_oasis_trigedit: Player already had olc structure.");
free(d->olc);
}
CREATE(d->olc, struct oasis_olc_data, 1);
/* Find the zone. */
if ((OLC_ZNUM(d) = real_zone_by_thing(number)) == NOWHERE) {
send_to_char(ch, "Sorry, there is no zone for that number!\r\n");
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;
}
OLC_NUM(d) = number;
/* If this is a new trigger, setup a new one, otherwise, setup the a copy of
* the existing trigger. */
if ((real_num = real_trigger(number)) == NOTHING)
trigedit_setup_new(d);
else
trigedit_setup_existing(d, real_num);
trigedit_disp_menu(d);
STATE(d) = CON_TRIGEDIT;
act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM);
SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING);
mudlog(CMP, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE,"OLC: %s starts editing zone %d [trigger](allowed zone %d)",
GET_NAME(ch), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch));
}
/* Called when a mob or object is being saved to disk, so its script can be
* saved. */
void script_save_to_disk(FILE *fp, void *item, int type)
{
struct trig_proto_list *t;
if (type==MOB_TRIGGER)
t = ((struct char_data *)item)->proto_script;
else if (type==OBJ_TRIGGER)
t = ((struct obj_data *)item)->proto_script;
else if (type==WLD_TRIGGER)
t = ((struct room_data *)item)->proto_script;
else {
log("SYSERR: Invalid type passed to script_save_to_disk()");
return;
}
while (t)
{
fprintf(fp,"T %d\n", t->vnum);
t = t->next;
}
}
static void trigedit_setup_new(struct descriptor_data *d)
{
struct trig_data *trig;
/* Allocate a scratch trigger structure. */
CREATE(trig, struct trig_data, 1);
trig->nr = NOWHERE;
/* Set up some defaults. */
trig->name = strdup("new trigger");
trig->trigger_type = MTRIG_GREET;
/* cmdlist will be a large char string until the trigger is saved */
CREATE(OLC_STORAGE(d), char, MAX_CMD_LENGTH);
strncpy(OLC_STORAGE(d),
"%echo% This trigger commandlist is not complete!\r\n", MAX_CMD_LENGTH-1);
trig->narg = 100;
OLC_TRIG(d) = trig;
OLC_VAL(d) = 0; /* Has changed flag. (It hasn't so far, we just made it.) */
}
void trigedit_setup_existing(struct descriptor_data *d, int rtrg_num)
{
struct trig_data *trig;
struct cmdlist_element *c;
/* Allocate a scratch trigger structure. */
CREATE(trig, struct trig_data, 1);
trig_data_copy(trig, trig_index[rtrg_num]->proto);
/* convert cmdlist to a char string */
c = trig->cmdlist;
CREATE(OLC_STORAGE(d), char, MAX_CMD_LENGTH);
strcpy(OLC_STORAGE(d), "");
while (c)
{
strcat(OLC_STORAGE(d), c->cmd);
strcat(OLC_STORAGE(d), "\r\n");
c = c->next;
}
/* Now trig->cmdlist is something to pass to the text editor it will be
* converted back to a real cmdlist_element list later. */
OLC_TRIG(d) = trig;
OLC_VAL(d) = 0; /* Has changed flag. (It hasn't so far, we just made it.) */
}
static void trigedit_disp_menu(struct descriptor_data *d)
{
struct trig_data *trig = OLC_TRIG(d);
char *attach_type;
char trgtypes[256];
get_char_colors(d->character);
if (trig->attach_type==OBJ_TRIGGER) {
attach_type = "Objects";
sprintbit(GET_TRIG_TYPE(trig), otrig_types, trgtypes, sizeof(trgtypes));
} else if (trig->attach_type==WLD_TRIGGER) {
attach_type = "Rooms";
sprintbit(GET_TRIG_TYPE(trig), wtrig_types, trgtypes, sizeof(trgtypes));
} else {
attach_type = "Mobiles";
sprintbit(GET_TRIG_TYPE(trig), trig_types, trgtypes, sizeof(trgtypes));
}
clear_screen(d);
write_to_output(d,
"Trigger Editor [%s%d%s]\r\n\r\n"
"%s1)%s Name : %s%s\r\n"
"%s2)%s Intended for : %s%s\r\n"
"%s3)%s Trigger types: %s%s\r\n"
"%s4)%s Numeric Arg : %s%d\r\n"
"%s5)%s Arguments : %s%s\r\n"
"%s6)%s Commands:\r\n%s%s\r\n"
"%sW%s) Copy Trigger\r\n"
"%sQ)%s Quit\r\n"
"Enter Choice :",
grn, OLC_NUM(d), nrm, /* vnum on the title line */
grn, nrm, yel, GET_TRIG_NAME(trig), /* name */
grn, nrm, yel, attach_type, /* attach type */
grn, nrm, yel, trgtypes, /* greet/drop/etc */
grn, nrm, yel, trig->narg, /* numeric arg */
grn, nrm, yel, trig->arglist?trig->arglist:"",/* strict arg */
grn, nrm, cyn, OLC_STORAGE(d), /* the command list */
grn, nrm, grn, nrm); /* quit colors */
OLC_MODE(d) = TRIGEDIT_MAIN_MENU;
}
static void trigedit_disp_types(struct descriptor_data *d)
{
int i, columns = 0;
const char **types;
char bitbuf[MAX_STRING_LENGTH];
switch(OLC_TRIG(d)->attach_type)
{
case WLD_TRIGGER:
types = wtrig_types;
break;
case OBJ_TRIGGER:
types = otrig_types;
break;
case MOB_TRIGGER:
default:
types = trig_types;
break;
}
get_char_colors(d->character);
clear_screen(d);
for (i = 0; i < NUM_TRIG_TYPE_FLAGS; i++) {
write_to_output(d, "%s%2d%s) %-20.20s %s", grn, i + 1, nrm, types[i],
!(++columns % 2) ? "\r\n" : "");
}
sprintbit(GET_TRIG_TYPE(OLC_TRIG(d)), types, bitbuf, sizeof(bitbuf));
write_to_output(d, "\r\nCurrent types : %s%s%s\r\nEnter type (0 to quit) : ",
cyn, bitbuf, nrm);
}
/****************************************************************************************
DG Scripts Code Syntax Highlighting
Created by Victor Almeida (aka Stoneheart) in Brazil
from BrMUD:Tormenta www.tormenta.com.br
License: Attribution 4.0 International (CC BY 4.0)
http://creativecommons.org/licenses/by/4.0/
You are free to:
Share — copy and redistribute the material in any medium or format
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
Attribution — You must give appropriate credit, provide a link to the license, and indicate
if changes were made. You may do so in any reasonable manner, but not in any way that
suggests the licensor endorses you or your use.
*****************************************************************************************/
// Change a string for another without memory bugs
static char *str_replace(const char *string, const char *substr, const char *replacement) {
char *tok = NULL;
char *newstr = NULL;
char *oldstr = NULL;
char *head = NULL;
// if either substr or replacement is NULL, duplicate string a let caller handle it
if (substr == NULL || replacement == NULL) {
return strdup (string);
}
newstr = strdup (string);
head = newstr;
while ((tok = strstr(head, substr))) {
oldstr = newstr;
newstr = malloc(strlen(oldstr) - strlen(substr) + strlen (replacement) + 1);
// Failed to alloc mem, free old string and return NULL
if (newstr == NULL) {
free (oldstr);
return NULL;
}
memcpy (newstr, oldstr, tok - oldstr);
memcpy (newstr + (tok - oldstr), replacement, strlen (replacement));
memcpy (newstr + (tok - oldstr) + strlen(replacement), tok + strlen (substr), strlen (oldstr) - strlen (substr) - (tok - oldstr));
memset (newstr + strlen (oldstr) - strlen(substr) + strlen (replacement) , 0, 1 );
// move back head right after the last replacement
head = newstr + (tok - oldstr) + strlen( replacement);
free (oldstr);
}
return newstr;
}
// You can easily change the color code (\tn) to the old one (@n or &n)
#define SYNTAX_TERMS 49
static const char *syntax_color_replacement[SYNTAX_TERMS][2] =
{
// script logic (10)
{ "if", "\tcif\tn" }, // 0
{ "elseif", "\tcelseif\tn" },
{ "else", "\tcelse\tn" },
{ "end", "\tcend\tn" },
{ "switch", "\tcswitch\tn" },
{ "case", "\tccase\tY" },
{ "default", "\tcdefault\tn" },
{ "break", "\tcbreak\tn" },
{ "while", "\tcwhile\tn" },
{ "done", "\tcdone\tn" },
// commands (15)
{ "eval ", "\tceval\tY " }, //10
{ "nop ", "\tcnop\tY " },
{ "extract ", "\tcextract\tY " },
{ "dg_letter ", "\tcdg_letter\tY " },
{ "makeuid ", "\tcmakeuid\tY " },
{ "dg_cast ", "\tcdg_cast\tY " },
{ "dg_affect ", "\tcdg_affect\tY " },
{ "global ", "\tcglobal\tY " },
{ "context ", "\tccontext\tY " },
{ "remote ", "\tcremot\tce\tY " },
{ "rdelete ", "\tcrdelete\tY " }, // 20
{ "set ", "\tcset\tY " },
{ "unset ", "\tcunset\tY " },
{ "attach ", "\tcattach\tY " },
{ "detach ", "\tcdetach\tY " },
// stopping (3)
{ "wait", "\trwait" },
{ "return", "\trreturn" },
{ "halt", "\trhalt" },
// operands (12)
{ "||", "\tc||\tY" },
{ "&&", "\tc&&\tY" },
{ "==", "\tc==\tY" }, // 30
{ "!=", "\tc!=\tY" },
{ "<=", "\tc<=\tY" },
{ ">=", "\tc>=\tY" },
{ "< ", "\tc< \tY" },
{ "> ", "\tc> \tY" },
{ "/=", "\tc/=\tY" },
{ "!", "\tc!\tn" },
{ "(", "\tc(\tY" },
{ ")", "\tc)\tn" },
// corrective (4)
{ "\tc!\tn=", "\tc!=\tY" }, // 40
{ "%s\tcend\tn%", "\tm%\tosend%\tn" },
{ "%\tc)", "\tm%\tc)" },
{ ")\tn%", ")\tm%"},
// variables (5)
{ "% ", "\tm%\tn " },
{ "%,", "\tm%\tn," },
{ "%.", "\tm%\tn." },
{ "%:", "\tm%\tn:" },
{ "%", "\tm%\to" } // 48
};
// Here you can include more commands usually used in your triggers
#define COMMAND_TERMS 36
static const char *command_color_replacement[COMMAND_TERMS][2] =
{
// Mob specific commands (25)
{ "mlog", "\tcmlog\tn" }, // 0
{ "masound", "\tcmasound\tn" },
{ "mkill", "\tcmkill\tn" },
{ "mjunk", "\tcmjunk\tn" },
{ "mdamage", "\tcmdamage\tn" },
{ "mdoor", "\tcmdoor\tn" },
{ "mecho", "\tcmecho\tn" },
{ "mrecho", "\tcmrecho\tn" },
{ "mechoaround", "\tcmechoaround\tn" },
{ "msend", "\tcmsend\tn" },
{ "mload", "\tcmload\tn" }, // 10
{ "mpurge", "\tcmpurge\tn" },
{ "mgoto", "\tcmgoto\tn" },
{ "mteleport", "\tcmteleport\tn" },
{ "mforce", "\tcmforce\tn" },
{ "mhunt", "\tcmhunt\tn" },
{ "mremember", "\tcmremember\tn" },
{ "mforget", "\tcmforget\tn" },
{ "mtransform", "\tcmtransform\tn" },
{ "mzoneecho", "\tcmzoneecho\tn" },
{ "mfollow", "\tcmfollow\tn" }, // 20
{ "mquest", "\tcmquest\tn" },
{ "malign", "\tcmalign\tn" },
{ "mcast", "\tcmcast\tn" },
{ "mdismiss", "\tcmdismiss\tn" },
// common commands (10)
{ "drop ", "\tcdrop \tn" },
{ "emote ", "\tcemote \tn" },
{ "give ", "\tcgive \tn" },
{ "say ", "\tcsay \tn" },
{ "tell ", "\tctell \tn" },
{ "unlock ", "\tcunlock \tn" }, // 30
{ "lock ", "\tclock \tn" },
{ "open ", "\tcopen \tn" },
{ "close ", "\tcclose \tn" },
{ "junk ", "\tcjunk \tn" } // 34
};
static void script_syntax_highlighting(struct descriptor_data *d, char *string)
{
ACMD(do_action);
char buffer[MAX_STRING_LENGTH] = "";
char *newlist, *curtok;
size_t i;
// Parse script text line by line
newlist = strdup(string);
for (curtok = strtok(newlist, "\r\n"); curtok; curtok = strtok(NULL, "\r\n")) {
char *line = strdup(curtok);
bool comment = FALSE;
// Find if is a comment
for (i=0;i <= strlen(line);i++) {
// skip initial spaces
if (strncmp(&line[i], " ", 1) == 0) {
continue;
}
// is a comment, highlight
if (strncmp(&line[i], "*", 1) == 0) {
line = str_replace(line, "*", "\tg*");
comment = TRUE;
break;
}
// not a comment
else {
comment = FALSE;
break;
}
}
// Highlight lines
if (!comment) {
// Syntax replacement
for (i=0;i < SYNTAX_TERMS;i++) {
line = str_replace(line, syntax_color_replacement[i][0], syntax_color_replacement[i][1]);
}
// Commands replacement
for (i=0;i < COMMAND_TERMS;i++) {
line = str_replace(line, command_color_replacement[i][0], command_color_replacement[i][1]);
}
// Socials replacement (experimental)
int cmd;
for (cmd = 0; *complete_cmd_info[cmd].command != '\n'; cmd++) {
if (complete_cmd_info[cmd].command_pointer == do_action) {
char replace_social[MAX_INPUT_LENGTH];
snprintf(replace_social, MAX_INPUT_LENGTH, "\tc%s\tn", complete_cmd_info[cmd].command);
line = str_replace(line, complete_cmd_info[cmd].command, replace_social);
}
}
}
strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1);
strncat(buffer, "\tn\r\n", sizeof(buffer) - strlen(buffer) - 1);
}
page_string(d, buffer, TRUE);
}
/****************************************************************************************/
void trigedit_parse(struct descriptor_data *d, char *arg)
{
int i = 0;
switch (OLC_MODE(d)) {
case TRIGEDIT_MAIN_MENU:
switch (tolower(*arg)) {
case 'q':
if (OLC_VAL(d)) { /* Anything been changed? */
if (!GET_TRIG_TYPE(OLC_TRIG(d))) {
write_to_output(d, "Invalid Trigger Type! Answer a to abort quit!\r\n");
}
write_to_output(d, "Do you wish to save your changes? : ");
OLC_MODE(d) = TRIGEDIT_CONFIRM_SAVESTRING;
} else
cleanup_olc(d, CLEANUP_ALL);
return;
case '1':
OLC_MODE(d) = TRIGEDIT_NAME;
write_to_output(d, "Name: ");
break;
case '2':
OLC_MODE(d) = TRIGEDIT_INTENDED;
write_to_output(d, "0: Mobiles, 1: Objects, 2: Rooms: ");
break;
case '3':
OLC_MODE(d) = TRIGEDIT_TYPES;
trigedit_disp_types(d);
break;
case '4':
OLC_MODE(d) = TRIGEDIT_NARG;
write_to_output(d, "Numeric argument: ");
break;
case '5':
OLC_MODE(d) = TRIGEDIT_ARGUMENT;
write_to_output(d, "Argument: ");
break;
case '6':
OLC_MODE(d) = TRIGEDIT_COMMANDS;
write_to_output(d, "Enter trigger commands: (/s saves /h for help)\r\n\r\n");
d->backstr = NULL;
if (OLC_STORAGE(d)) {
clear_screen(d);
script_syntax_highlighting(d, OLC_STORAGE(d));
d->backstr = strdup(OLC_STORAGE(d));
}
d->str = &OLC_STORAGE(d);
d->max_str = MAX_CMD_LENGTH;
d->mail_to = 0;
OLC_VAL(d) = 1;
break;
case 'w':
case 'W':
write_to_output(d, "Copy what trigger? ");
OLC_MODE(d) = TRIGEDIT_COPY;
break;
default:
trigedit_disp_menu(d);
return;
}
return;
case TRIGEDIT_CONFIRM_SAVESTRING:
switch(tolower(*arg)) {
case 'y':
trigedit_save(d);
mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(d->character)), TRUE,
"OLC: %s edits trigger %d", GET_NAME(d->character),
OLC_NUM(d));
/* fall through */
case 'n':
cleanup_olc(d, CLEANUP_ALL);
return;
case 'a': /* abort quitting */
break;
default:
write_to_output(d, "Invalid choice!\r\n");
write_to_output(d, "Do you wish to save your changes? : ");
return;
}
break;
case TRIGEDIT_NAME:
smash_tilde(arg);
if (OLC_TRIG(d)->name)
free(OLC_TRIG(d)->name);
OLC_TRIG(d)->name = strdup((arg && *arg) ? arg : "undefined");
OLC_VAL(d)++;
break;
case TRIGEDIT_INTENDED:
if ((atoi(arg)>=MOB_TRIGGER) || (atoi(arg)<=WLD_TRIGGER))
OLC_TRIG(d)->attach_type = atoi(arg);
OLC_VAL(d)++;
break;
case TRIGEDIT_NARG:
OLC_TRIG(d)->narg = LIMIT(atoi(arg), 0, 100);
OLC_VAL(d)++;
break;
case TRIGEDIT_ARGUMENT:
smash_tilde(arg);
OLC_TRIG(d)->arglist = (*arg?strdup(arg):NULL);
OLC_VAL(d)++;
break;
case TRIGEDIT_TYPES:
if ((i = atoi(arg)) == 0)
break;
else if (!((i < 0) || (i > NUM_TRIG_TYPE_FLAGS)))
TOGGLE_BIT((GET_TRIG_TYPE(OLC_TRIG(d))), 1 << (i - 1));
OLC_VAL(d)++;
trigedit_disp_types(d);
return;
case TRIGEDIT_COPY:
if ((i = real_trigger(atoi(arg))) != NOWHERE) {
trigedit_setup_existing(d, i);
} else
write_to_output(d, "That trigger does not exist.\r\n");
break;
case TRIGEDIT_COMMANDS:
break;
}
OLC_MODE(d) = TRIGEDIT_MAIN_MENU;
trigedit_disp_menu(d);
}
/* save the zone's triggers to internal memory and to disk */
void trigedit_save(struct descriptor_data *d)
{
int i;
trig_rnum rnum;
int found = 0;
char *s;
trig_data *proto;
trig_data *trig = OLC_TRIG(d);
trig_data *live_trig;
struct cmdlist_element *cmd, *next_cmd;
struct index_data **new_index;
struct descriptor_data *dsc;
FILE *trig_file;
int zone, top;
char buf[MAX_CMD_LENGTH];
char bitBuf[MAX_INPUT_LENGTH];
char fname[MAX_INPUT_LENGTH];
if ((rnum = real_trigger(OLC_NUM(d))) != NOTHING) {
proto = trig_index[rnum]->proto;
for (cmd = proto->cmdlist; cmd; cmd = next_cmd) {
next_cmd = cmd->next;
if (cmd->cmd)
free(cmd->cmd);
free(cmd);
}
free(proto->arglist);
free(proto->name);
/* Recompile the command list from the new script */
s = OLC_STORAGE(d);
CREATE(trig->cmdlist, struct cmdlist_element, 1);
if (s) {
char *t = strtok(s, "\n\r"); /* strtok returns NULL if s is "\r\n" */
if (t)
trig->cmdlist->cmd = strdup(t);
else
trig->cmdlist->cmd = strdup("* No script");
cmd = trig->cmdlist;
while ((s = strtok(NULL, "\n\r"))) {
CREATE(cmd->next, struct cmdlist_element, 1);
cmd = cmd->next;
cmd->cmd = strdup(s);
}
} else
trig->cmdlist->cmd = strdup("* No Script");
/* make the prorotype look like what we have */
trig_data_copy(proto, trig);
/* go through the mud and replace existing triggers */
live_trig = trigger_list;
while (live_trig)
{
if (GET_TRIG_RNUM(live_trig) == rnum) {
if (live_trig->arglist) {
free(live_trig->arglist);
live_trig->arglist = NULL;
}
if (live_trig->name) {
free(live_trig->name);
live_trig->name = NULL;
}
if (proto->arglist)
live_trig->arglist = strdup(proto->arglist);
if (proto->name)
live_trig->name = strdup(proto->name);
/* anything could have happened so we don't want to keep these */
if (GET_TRIG_WAIT(live_trig)) {
event_cancel(GET_TRIG_WAIT(live_trig));
GET_TRIG_WAIT(live_trig)=NULL;
}
if (live_trig->var_list) {
free_varlist(live_trig->var_list);
live_trig->var_list=NULL;
}
live_trig->cmdlist = proto->cmdlist;
live_trig->curr_state = live_trig->cmdlist;
live_trig->trigger_type = proto->trigger_type;
live_trig->attach_type = proto->attach_type;
live_trig->narg = proto->narg;
live_trig->data_type = proto->data_type;
live_trig->depth = 0;
}
live_trig = live_trig->next_in_world;
}
} else {
/* this is a new trigger */
CREATE(new_index, struct index_data *, top_of_trigt + 2);
/* Recompile the command list from the new script */
s = OLC_STORAGE(d);
CREATE(trig->cmdlist, struct cmdlist_element, 1);
if (s) {
/* strtok returns NULL if s is "\r\n" */
char *t = strtok(s, "\n\r");
trig->cmdlist->cmd = strdup(t ? t : "* No script");
cmd = trig->cmdlist;
while ((s = strtok(NULL, "\n\r"))) {
CREATE(cmd->next, struct cmdlist_element, 1);
cmd = cmd->next;
cmd->cmd = strdup(s);
}
} else
trig->cmdlist->cmd = strdup("* No Script");
for (i = 0; i < top_of_trigt; i++) {
if (!found) {
if (trig_index[i]->vnum > OLC_NUM(d)) {
found = TRUE;
rnum = i;
CREATE(new_index[rnum], struct index_data, 1);
GET_TRIG_RNUM(OLC_TRIG(d)) = rnum;
new_index[rnum]->vnum = OLC_NUM(d);
new_index[rnum]->number = 0;
new_index[rnum]->func = NULL;
CREATE(proto, struct trig_data, 1);
new_index[rnum]->proto = proto;
trig_data_copy(proto, trig);
new_index[rnum + 1] = trig_index[rnum];
proto = trig_index[rnum]->proto;
proto->nr = rnum + 1;
} else {
new_index[i] = trig_index[i];
}
} else {
new_index[i + 1] = trig_index[i];
proto = trig_index[i]->proto;
proto->nr = i + 1;
}
}
if (!found) {
rnum = i;
CREATE(new_index[rnum], struct index_data, 1);
GET_TRIG_RNUM(OLC_TRIG(d)) = rnum;
new_index[rnum]->vnum = OLC_NUM(d);
new_index[rnum]->number = 0;
new_index[rnum]->func = NULL;
CREATE(proto, struct trig_data, 1);
new_index[rnum]->proto = proto;
trig_data_copy(proto, trig);
}
free(trig_index);
trig_index = new_index;
top_of_trigt++;
/* HERE IT HAS TO GO THROUGH AND FIX ALL SCRIPTS/TRIGS OF HIGHER RNUM */
for (live_trig = trigger_list; live_trig; live_trig = live_trig->next_in_world)
GET_TRIG_RNUM(live_trig) += (GET_TRIG_RNUM(live_trig) != NOTHING && GET_TRIG_RNUM(live_trig) > rnum);
/* Update other trigs being edited. */
for (dsc = descriptor_list; dsc; dsc = dsc->next)
if (STATE(dsc) == CON_TRIGEDIT)
if (GET_TRIG_RNUM(OLC_TRIG(dsc)) >= rnum)
GET_TRIG_RNUM(OLC_TRIG(dsc))++;
}
/* now write the trigger out to disk, along with the rest of the triggers for
* this zone. We write this to disk NOW instead of letting the builder have
* control because if we lose this after having assigned a new trigger to an
* item, we will get SYSERR's upton reboot that could make things hard to
* debug. */
zone = zone_table[OLC_ZNUM(d)].number;
top = zone_table[OLC_ZNUM(d)].top;
#ifdef CIRCLE_MAC
snprintf(fname, sizeof(fname), "%s:%i.new", TRG_PREFIX, zone);
#else
snprintf(fname, sizeof(fname), "%s/%i.new", TRG_PREFIX, zone);
#endif
if (!(trig_file = fopen(fname, "w"))) {
mudlog(BRF, MAX(LVL_GOD, GET_INVIS_LEV(d->character)), TRUE,
"SYSERR: OLC: Can't open trig file \"%s\"", fname);
return;
}
for (i = zone_table[OLC_ZNUM(d)].bot; i <= top; i++) {
if ((rnum = real_trigger(i)) != NOTHING) {
trig = trig_index[rnum]->proto;
if (fprintf(trig_file, "#%d\n", i) < 0) {
mudlog(BRF, MAX(LVL_GOD, GET_INVIS_LEV(d->character)), TRUE,
"SYSERR: OLC: Can't write trig file!");
fclose(trig_file);
return;
}
sprintascii(bitBuf, GET_TRIG_TYPE(trig));
fprintf(trig_file, "%s%c\n"
"%d %s %d\n"
"%s%c\n",
(GET_TRIG_NAME(trig)) ? (GET_TRIG_NAME(trig)) : "unknown trigger", STRING_TERMINATOR,
trig->attach_type,
*bitBuf ? bitBuf : "0", GET_TRIG_NARG(trig),
GET_TRIG_ARG(trig) ? GET_TRIG_ARG(trig) : "", STRING_TERMINATOR);
/* Build the text for the script */
strcpy(buf,""); /* strcpy OK for MAX_CMD_LENGTH > 0*/
for (cmd = trig->cmdlist; cmd; cmd = cmd->next) {
strcat(buf, cmd->cmd);
strcat(buf, "\n");
}
if (!buf[0])
strcpy(buf, "* Empty script");
fprintf(trig_file, "%s%c\n", buf, STRING_TERMINATOR);
*buf = '\0';
}
}
fprintf(trig_file, "$%c\n", STRING_TERMINATOR);
fclose(trig_file);
#ifdef CIRCLE_MAC
snprintf(buf, sizeof(buf), "%s:%d.trg", TRG_PREFIX, zone);
#else
snprintf(buf, sizeof(buf), "%s/%d.trg", TRG_PREFIX, zone);
#endif
remove(buf);
rename(fname, buf);
write_to_output(d, "Trigger saved to disk.\r\n");
trigedit_create_index(zone, "trg");
}
static void trigedit_create_index(int znum, char *type)
{
FILE *newfile, *oldfile;
char new_name[128], old_name[128], *prefix;
char buf[MAX_STRING_LENGTH], buf1[MAX_STRING_LENGTH];
int num, found = FALSE;
prefix = TRG_PREFIX;
snprintf(old_name, sizeof(old_name), "%s/index", prefix);
snprintf(new_name, sizeof(new_name), "%s/newindex", prefix);
if (!(oldfile = fopen(old_name, "r"))) {
mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: DG_OLC: Failed to open %s", old_name);
return;
} else if (!(newfile = fopen(new_name, "w"))) {
mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: DG_OLC: Failed to open %s", new_name);
return;
}
/* Index contents must be in order: search through the old file for the right
* place, insert the new file, then copy the rest over. */
snprintf(buf1, sizeof(buf1), "%d.%s", znum, type);
while (get_line(oldfile, buf)) {
if (*buf == '$') {
fprintf(newfile, "%s\n$\n", (!found ? buf1 : ""));
break;
} else if (!found) {
sscanf(buf, "%d", &num);
if (num == znum)
found = TRUE;
else if (num > znum) {
found = TRUE;
fprintf(newfile, "%s\n", buf1);
}
}
fprintf(newfile, "%s\n", buf);
}
fclose(newfile);
fclose(oldfile);
/* Out with the old, in with the new. */
remove(old_name);
rename(new_name, old_name);
}
void dg_olc_script_copy(struct descriptor_data *d)
{
struct trig_proto_list *origscript, *editscript;
if (OLC_ITEM_TYPE(d)==MOB_TRIGGER)
origscript = OLC_MOB(d)->proto_script;
else if (OLC_ITEM_TYPE(d)==OBJ_TRIGGER)
origscript = OLC_OBJ(d)->proto_script;
else origscript = OLC_ROOM(d)->proto_script;
if (origscript) {
CREATE(editscript, struct trig_proto_list, 1);
OLC_SCRIPT(d) = editscript;
while (origscript) {
editscript->vnum = origscript->vnum;
origscript = origscript->next;
if (origscript)
CREATE(editscript->next, struct trig_proto_list, 1);
editscript = editscript->next;
}
} else
OLC_SCRIPT(d) = NULL;
}
void dg_script_menu(struct descriptor_data *d)
{
struct trig_proto_list *editscript;
int i = 0;
/* make sure our input parser gets used */
OLC_MODE(d) = OLC_SCRIPT_EDIT;
OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_MAIN_MENU;
clear_screen(d);
write_to_output(d, " Triggers Attached:\r\n");
editscript = OLC_SCRIPT(d);
while (editscript) {
write_to_output(d, " %2d) [%s%d%s] %s%s%s", ++i, cyn,
editscript->vnum, nrm, cyn,
trig_index[real_trigger(editscript->vnum)]->proto->name, nrm);
if (trig_index[real_trigger(editscript->vnum)]->proto->attach_type != OLC_ITEM_TYPE(d))
write_to_output(d, " %s** Mis-matched Trigger Type **%s\r\n",grn,nrm);
else
write_to_output(d, "\r\n");
editscript = editscript->next;
}
if (i==0)
write_to_output(d, " <none>\r\n");
write_to_output(d, "\r\n"
" %sN%s) Attach trigger\r\n"
" %sX%s) Detach trigger\r\n"
" %sQ%s) Quit\r\n\r\n"
" Enter choice :",
grn, nrm, grn, nrm, grn, nrm);
}
int dg_script_edit_parse(struct descriptor_data *d, char *arg)
{
struct trig_proto_list *trig, *currtrig;
int count, pos, vnum;
switch(OLC_SCRIPT_EDIT_MODE(d)) {
case SCRIPT_MAIN_MENU:
switch(tolower(*arg)) {
case 'q':
/* This was buggy. First we created a copy of a thing, but maintained
* pointers to scripts, then if we altered the scripts, we freed the
* pointers and added new ones to the OLC_THING. If we then choose NOT
* to save the changes, the pointers in the original pointed to
* garbage. If we saved changes the pointers were updated correctly.
* Solution: Here we just point the working copies to the new
* proto_scripts. We only update the original when choosing to save
* internally, then free the unused memory there. -Welcor
* Thanks to Jeremy Stanley and Torgny Bjers for the bug report.
* After updating to OasisOLC 2.0.3 I discovered some malfunctions
* in this code, so I restructured it a bit. Now things work like
* this: OLC_SCRIPT(d) is assigned a copy of the edited things'
* proto_script. OLC_OBJ(d), etc.. are initalized with proto_script =
* NULL; On save, the saved copy is updated with OLC_SCRIPT(d) as new
* proto_script (freeing the old one). On quit/nosave, OLC_SCRIPT is
* free()'d, and the prototype not touched. */
return 0;
case 'n':
write_to_output(d, "\r\nPlease enter position, vnum (ex: 1, 200):");
OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_NEW_TRIGGER;
break;
case 'x':
write_to_output(d, " Which entry should be deleted? 0 to abort :");
OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_DEL_TRIGGER;
break;
default:
dg_script_menu(d);
break;
}
return 1;
case SCRIPT_NEW_TRIGGER:
vnum = -1;
count = sscanf(arg,"%d, %d",&pos,&vnum);
if (count==1) {
vnum = pos;
pos = 999;
}
if (pos<=0) break; /* this aborts a new trigger entry */
if (vnum==0) break; /* this aborts a new trigger entry */
if (real_trigger(vnum) == NOTHING) {
write_to_output(d, "Invalid Trigger VNUM!\r\n"
"Please enter position, vnum (ex: 1, 200):");
return 1;
}
/* add the new info in position */
currtrig = OLC_SCRIPT(d);
CREATE(trig, struct trig_proto_list, 1);
trig->vnum = vnum;
if (pos==1 || !currtrig) {
trig->next = OLC_SCRIPT(d);
OLC_SCRIPT(d) = trig;
} else {
while (currtrig->next && --pos) {
currtrig = currtrig->next;
}
trig->next = currtrig->next;
currtrig->next = trig;
}
OLC_VAL(d)++;
break;
case SCRIPT_DEL_TRIGGER:
pos = atoi(arg);
if (pos<=0) break;
if (pos==1 && OLC_SCRIPT(d)) {
OLC_VAL(d)++;
currtrig = OLC_SCRIPT(d);
OLC_SCRIPT(d) = currtrig->next;
free(currtrig);
break;
}
pos--;
currtrig = OLC_SCRIPT(d);
while (--pos && currtrig) currtrig = currtrig->next;
/* now curtrig points one before the target */
if (currtrig && currtrig->next) {
OLC_VAL(d)++;
trig = currtrig->next;
currtrig->next = trig->next;
free(trig);
}
break;
}
dg_script_menu(d);
return 1;
}
void trigedit_string_cleanup(struct descriptor_data *d, int terminator)
{
switch (OLC_MODE(d)) {
case TRIGEDIT_COMMANDS:
trigedit_disp_menu(d);
break;
}
}
int format_script(struct descriptor_data *d)
{
char nsc[MAX_CMD_LENGTH], *t, line[READ_SIZE];
char *sc;
size_t len = 0, nlen = 0, llen = 0;
int indent = 0, indent_next = FALSE, found_case = FALSE, i, line_num = 0, ret;
if (!d->str || !*d->str)
return FALSE;
sc = strdup(*d->str); /* we work on a copy, because of strtok() */
t = strtok(sc, "\n\r");
*nsc = '\0';
while (t) {
line_num++;
skip_spaces(&t);
if (!strn_cmp(t, "if ", 3) ||
!strn_cmp(t, "switch ", 7)) {
indent_next = TRUE;
} else if (!strn_cmp(t, "while ", 6)) {
found_case = TRUE; /* so you can 'break' a loop without complains */
indent_next = TRUE;
} else if (!strn_cmp(t, "end", 3) ||
!strn_cmp(t, "done", 4)) {
if (!indent) {
write_to_output(d, "Unmatched 'end' or 'done' (line %d)!\r\n", line_num);
free(sc);
return FALSE;
}
indent--;
indent_next = FALSE;
} else if (!strn_cmp(t, "else", 4)) {
if (!indent) {
write_to_output(d, "Unmatched 'else' (line %d)!\r\n", line_num);
free(sc);
return FALSE;
}
indent--;
indent_next = TRUE;
} else if (!strn_cmp(t, "case", 4) ||
!strn_cmp(t, "default", 7)) {
if (!indent) {
write_to_output(d, "Case/default outside switch (line %d)!\r\n", line_num);
free(sc);
return FALSE;
}
if (!found_case) /* so we don't indent multiple case statements without a break */
indent_next = TRUE;
found_case = TRUE;
} else if (!strn_cmp(t, "break", 5)) {
if (!found_case || !indent ) {
write_to_output(d, "Break not in case (line %d)!\r\n", line_num);
free(sc);
return FALSE;
}
found_case = FALSE;
indent--;
}
*line = '\0';
for (nlen = 0, i = 0;i<indent;i++) {
strncat(line, " ", sizeof(line)-1);
nlen += 2;
}
ret = snprintf(line + nlen, sizeof(line) - nlen, "%s\r\n", t);
llen = (size_t)ret;
if (ret < 0 || llen + nlen + len > d->max_str - 1 ) {
write_to_output(d, "String too long, formatting aborted\r\n");
free(sc);
return FALSE;
}
len = len + nlen + llen;
strcat(nsc, line); /* strcat OK, size checked above */
if (indent_next) {
indent++;
indent_next = FALSE;
}
t = strtok(NULL, "\n\r");
}
if (indent)
write_to_output(d, "Unmatched if, while or switch ignored.\r\n");
free(*d->str);
*d->str = strdup(nsc);
free(sc);
return TRUE;
}