/*- * Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: olc_area.c,v 1.39 1999/04/16 15:52:24 fjoe Exp $ */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include "merc.h" #include "olc.h" #include "db/db.h" #define EDIT_AREA(ch, area) (area = (AREA_DATA*) ch->desc->pEdit) DECLARE_OLC_FUN(areaed_create ); DECLARE_OLC_FUN(areaed_edit ); DECLARE_OLC_FUN(areaed_touch ); DECLARE_OLC_FUN(areaed_show ); DECLARE_OLC_FUN(areaed_list ); DECLARE_OLC_FUN(areaed_name ); DECLARE_OLC_FUN(areaed_file ); DECLARE_OLC_FUN(areaed_flags ); DECLARE_OLC_FUN(areaed_age ); DECLARE_OLC_FUN(areaed_reset ); DECLARE_OLC_FUN(areaed_security ); DECLARE_OLC_FUN(areaed_builders ); DECLARE_OLC_FUN(areaed_resetmsg ); DECLARE_OLC_FUN(areaed_minvnum ); DECLARE_OLC_FUN(areaed_maxvnum ); DECLARE_OLC_FUN(areaed_move ); DECLARE_OLC_FUN(areaed_credits ); DECLARE_OLC_FUN(areaed_minlevel ); DECLARE_OLC_FUN(areaed_maxlevel ); DECLARE_OLC_FUN(areaed_clan ); DECLARE_VALIDATE_FUN(validate_security ); DECLARE_VALIDATE_FUN(validate_minvnum ); DECLARE_VALIDATE_FUN(validate_maxvnum ); DECLARE_VALIDATE_FUN(validate_move ); olc_cmd_t olc_cmds_area[] = { /* { command function arg }, */ { "create", areaed_create }, { "edit", areaed_edit }, { "touch", areaed_touch }, { "show", areaed_show }, { "list", areaed_list }, { "name", areaed_name }, { "filename", areaed_file, validate_filename }, { "area", areaed_flags, area_flags }, { "age", areaed_age }, { "reset", areaed_reset }, { "security", areaed_security, validate_security }, { "builders", areaed_builders }, { "resetmsg", areaed_resetmsg }, { "minvnum", areaed_minvnum, validate_minvnum }, { "maxvnum", areaed_maxvnum, validate_maxvnum }, { "move", areaed_move, validate_move }, { "credits", areaed_credits }, { "minlevel", areaed_minlevel }, { "maxlevel", areaed_maxlevel }, { "clan", areaed_clan }, { "commands", show_commands }, { "version", show_version }, { NULL } }; static AREA_DATA *check_range(AREA_DATA *pArea, int ilower, int iupper); /* * Area Editor Functions. */ OLC_FUN(areaed_create) { AREA_DATA *pArea; if (ch->pcdata->security < SECURITY_AREA_CREATE) { char_puts("AreaEd: Insufficient security.\n", ch); return FALSE; } pArea = new_area(); area_last->next = pArea; area_last = pArea; /* Thanks, Walker. */ ch->desc->pEdit = (void*) pArea; OLCED(ch) = olced_lookup(ED_AREA); touch_area(pArea); char_puts("AreaEd: Area created.\n", ch); return FALSE; } OLC_FUN(areaed_edit) { AREA_DATA *pArea; char arg[MAX_STRING_LENGTH]; one_argument(argument, arg, sizeof(arg)); if (arg[0] == '\0') pArea = ch->in_room->area; else if (!is_number(arg) || (pArea = area_lookup(atoi(arg))) == NULL) { char_puts("AreaEd: That area vnum does not exist.\n", ch); return FALSE; } if (!IS_BUILDER(ch, pArea)) { char_puts("AreaEd: Insufficient security.\n", ch); return FALSE; } ch->desc->pEdit = (void *) pArea; OLCED(ch) = olced_lookup(ED_AREA); return FALSE; } OLC_FUN(areaed_touch) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return touch_area(pArea); } OLC_FUN(areaed_show) { AREA_DATA *pArea; char arg[MAX_STRING_LENGTH]; one_argument(argument, arg, sizeof(arg)); if (arg[0] == '\0') { if (IS_EDIT(ch, ED_AREA)) EDIT_AREA(ch, pArea); else pArea = ch->in_room->area; } else if (!is_number(arg) || (pArea = area_lookup(atoi(arg))) == NULL) { char_puts("AreaEd: That area vnum does not exist.\n", ch); return FALSE; } char_printf(ch, "Name: [%5d] %s\n", pArea->vnum, pArea->name); char_printf(ch, "File: %s\n", pArea->file_name); char_printf(ch, "Vnums: [%d-%d]\n", pArea->min_vnum, pArea->max_vnum); char_printf(ch, "Levels: [%d-%d]\n", pArea->min_level, pArea->max_level); if (pArea->clan) char_printf(ch, "Clan: [%s]\n", clan_name(pArea->clan)); char_printf(ch, "Age: [%d]\n", pArea->age); char_printf(ch, "Players: [%d]\n", pArea->nplayer); char_printf(ch, "Security: [%d]\n", pArea->security); if (!IS_NULLSTR(pArea->builders)) char_printf(ch, "Builders: [%s]\n", pArea->builders); char_printf(ch, "Credits: [%s]\n", pArea->credits); char_printf(ch, "Flags: [%s]\n", flag_string(area_flags, pArea->flags)); return FALSE; } OLC_FUN(areaed_list) { char arg[MAX_STRING_LENGTH]; AREA_DATA *pArea; BUFFER *output = NULL; one_argument(argument, arg, sizeof(arg)); for (pArea = area_first; pArea; pArea = pArea->next) { if (arg[0] != '\0' && !strstr(strlwr(pArea->name), arg)) continue; if (output == NULL) { output = buf_new(-1); buf_printf(output, "[%3s] [%-27s] (%-5s-%5s) [%-10s] %3s [%-10s]\n", "Num", "Area Name", "lvnum", "uvnum", "Filename", "Sec", "Builders"); } buf_printf(output, "[%3d] %-29.29s (%-5d-%5d) %-12.12s [%d] [%-10.10s]\n", pArea->vnum, pArea->name, pArea->min_vnum, pArea->max_vnum, pArea->file_name, pArea->security, pArea->builders); } if (output != NULL) { send_to_char(buf_string(output), ch); buf_free(output); } else char_puts("No areas with that name found.\n", ch); return FALSE; } OLC_FUN(areaed_reset) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); reset_area(pArea); char_puts("Area reset.\n", ch); return FALSE; } OLC_FUN(areaed_name) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_str(ch, argument, cmd, &pArea->name); } OLC_FUN(areaed_credits) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_str(ch, argument, cmd, &pArea->credits); } OLC_FUN(areaed_file) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_str(ch, argument, cmd, &pArea->file_name); } OLC_FUN(areaed_age) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->age); } OLC_FUN(areaed_flags) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_flag32(ch, argument, cmd, &pArea->flags); } OLC_FUN(areaed_security) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->security); } OLC_FUN(areaed_minlevel) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->min_level); } OLC_FUN(areaed_maxlevel) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->max_level); } OLC_FUN(areaed_resetmsg) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_mlstr(ch, argument, cmd, &pArea->resetmsg); } OLC_FUN(areaed_builders) { AREA_DATA *pArea; char name[MAX_STRING_LENGTH]; EDIT_AREA(ch, pArea); one_argument(argument, name, sizeof(name)); if (name[0] == '\0') { do_help(ch, "'OLC AREA BUILDER'"); return FALSE; } name_toggle(&pArea->builders, name, ch, "AreaEd"); return TRUE; } OLC_FUN(areaed_minvnum) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->min_vnum); } OLC_FUN(areaed_maxvnum) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->max_vnum); } OLC_FUN(areaed_move) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_number(ch, argument, cmd, &pArea->min_vnum); } OLC_FUN(areaed_clan) { AREA_DATA *pArea; EDIT_AREA(ch, pArea); return olced_clan(ch, argument, cmd, &pArea->clan); } /* Validators */ VALIDATE_FUN(validate_security) { int sec = *(int*) arg; if (sec > ch->pcdata->security || sec < 0) { if (ch->pcdata->security != 0) char_printf(ch, "AreaEd: Valid security range is 0..%d.\n", ch->pcdata->security); else char_puts("AreaEd: Valid security is 0 only.\n", ch); return FALSE; } return TRUE; } VALIDATE_FUN(validate_minvnum) { int min_vnum = *(int*) arg; AREA_DATA *pArea; EDIT_AREA(ch, pArea); if (min_vnum && pArea->max_vnum) { if (min_vnum > pArea->max_vnum) { char_puts("AreaEd: Min vnum must be less than max vnum.\n", ch); return FALSE; } if (check_range(pArea, min_vnum, pArea->max_vnum)) { char_puts("AreaEd: Range must include only this area.\n", ch); return FALSE; } } return TRUE; } VALIDATE_FUN(validate_maxvnum) { int max_vnum = *(int*) arg; AREA_DATA *pArea; EDIT_AREA(ch, pArea); if (pArea->min_vnum && max_vnum) { if (max_vnum < pArea->min_vnum) { char_puts("AreaEd: Max vnum must be greater than min vnum.\n", ch); return FALSE; } if (check_range(pArea, pArea->min_vnum, max_vnum)) { char_puts("AreaEd: Range must include only this area.\n", ch); return FALSE; } } return TRUE; } #define IN_RANGE(i, l, u) ((l) <= (i) && (i) <= (u)) #define MOVE(i) if (IN_RANGE(i, pArea->min_vnum, pArea->max_vnum)) { \ i += delta; \ touched = TRUE; \ } static void move_mob(MOB_INDEX_DATA *mob, AREA_DATA *pArea, int delta); static void move_obj(OBJ_INDEX_DATA *obj, AREA_DATA *pArea, int delta); static void move_room(ROOM_INDEX_DATA *room, AREA_DATA *pArea, int delta); VALIDATE_FUN(validate_move) { int i; int new_min = *(int*) arg; int delta; bool touched; AREA_DATA *pArea; MPCODE *mpc; clan_t *clan; EDIT_AREA(ch, pArea); if (ch->pcdata->security < SECURITY_AREA_CREATE) { char_puts("AreaEd: Insufficient security.\n", ch); return FALSE; } if (!pArea->min_vnum || !pArea->max_vnum) { char_puts("AreaEd: Both min_vnum and max_vnum must be set " "in order to perform area vnum move.\n", ch); return FALSE; } /* check new region */ delta = new_min - pArea->min_vnum; if (check_range(pArea, new_min, pArea->max_vnum+delta)) { char_puts("AreaEd: New vnum range overlaps other areas.\n", ch); return FALSE; } /* everything is ok -- change vnums of all rooms, objs, mobs in area */ /* fix clan recall, item and altar vnums */ touched = FALSE; for (i = 0; i < clans.nused; i++) { bool touched2 = touched; touched = FALSE; clan = CLAN(i); MOVE(clan->altar_vnum); MOVE(clan->recall_vnum); MOVE(clan->obj_vnum); MOVE(clan->mark_vnum); if (touched) touch_clan(clan); else touched = touched2; } if (touched) { char_puts("AreaEd: Changed clans:\n", ch); for (i = 0; i < clans.nused; i++) { clan = CLAN(i); if (IS_SET(clan->flags, CLAN_CHANGED)) char_printf(ch, "- %s\n", clan->name); } } /* XXX fix mprogs */ for (mpc = mpcode_list; mpc; mpc = mpc->next) MOVE(mpc->vnum); /* fix mobs */ for (i = 0; i < MAX_KEY_HASH; i++) { MOB_INDEX_DATA *mob; for (mob = mob_index_hash[i]; mob; mob = mob->next) move_mob(mob, pArea, delta); } /* fix objs */ for (i = 0; i < MAX_KEY_HASH; i++) { OBJ_INDEX_DATA *obj; for (obj = obj_index_hash[i]; obj; obj = obj->next) move_obj(obj, pArea, delta); } /* fix rooms */ for (i = 0; i < MAX_KEY_HASH; i++) { ROOM_INDEX_DATA *room; for (room = room_index_hash[i]; room; room = room->next) move_room(room, pArea, delta); } /* rebuild mob index hash */ top_vnum_mob = 0; for (i = 0; i < MAX_KEY_HASH; i++) { MOB_INDEX_DATA *mob, *mob_next, *mob_prev = NULL; for (mob = mob_index_hash[i]; mob; mob = mob_next) { int mob_hash = mob->vnum % MAX_KEY_HASH; mob_next = mob->next; if (top_vnum_mob < mob->vnum) top_vnum_mob = mob->vnum; if (mob_hash != i) { if (!mob_prev) mob_index_hash[i] = mob->next; else mob_prev->next = mob->next; mob->next = mob_index_hash[mob_hash]; mob_index_hash[mob_hash] = mob; } else mob_prev = mob; } } /* rebuild obj index hash */ top_vnum_obj = 0; for (i = 0; i < MAX_KEY_HASH; i++) { OBJ_INDEX_DATA *obj, *obj_next, *obj_prev = NULL; for (obj = obj_index_hash[i]; obj; obj = obj_next) { int obj_hash = obj->vnum % MAX_KEY_HASH; obj_next = obj->next; if (top_vnum_obj < obj->vnum) top_vnum_obj = obj->vnum; if (obj_hash != i) { if (!obj_prev) obj_index_hash[i] = obj->next; else obj_prev->next = obj->next; obj->next = obj_index_hash[obj_hash]; obj_index_hash[obj_hash] = obj; } else obj_prev = obj; } } /* rebuild room index hash */ top_vnum_room = 0; for (i = 0; i < MAX_KEY_HASH; i++) { ROOM_INDEX_DATA *room, *room_next, *room_prev = NULL; for (room = room_index_hash[i]; room; room = room_next) { int room_hash = room->vnum % MAX_KEY_HASH; room_next = room->next; if (top_vnum_room < room->vnum) top_vnum_room = room->vnum; if (room_hash != i) { if (!room_prev) room_index_hash[i] = room->next; else room_prev->next = room->next; room->next = room_index_hash[room_hash]; room_index_hash[room_hash] = room; } else room_prev = room; } } pArea->max_vnum += delta; touch_area(pArea); char_puts("AreaEd: Changed areas:\n", ch); for (pArea = area_first; pArea; pArea = pArea->next) if (IS_SET(pArea->flags, AREA_CHANGED)) char_printf(ch, "[%3d] %s (%s)\n", pArea->vnum, pArea->name, pArea->file_name); return TRUE; } /* Local functions */ /***************************************************************************** Name: check_range(lower vnum, upper vnum) Purpose: Ensures the range spans only one area. Called by: areaed_vnum(olc_act.c). ****************************************************************************/ static AREA_DATA *check_range(AREA_DATA *this, int ilower, int iupper) { AREA_DATA *pArea; for (pArea = area_first; pArea; pArea = pArea->next) { if (pArea == this || !pArea->min_vnum || !pArea->max_vnum) continue; if (IN_RANGE(ilower, pArea->min_vnum, pArea->max_vnum) || IN_RANGE(iupper, pArea->min_vnum, pArea->max_vnum) || IN_RANGE(pArea->min_vnum, ilower, iupper) || IN_RANGE(pArea->max_vnum, ilower, iupper)) return pArea; } return NULL; } static void move_mob(MOB_INDEX_DATA *mob, AREA_DATA *pArea, int delta) { bool touched = FALSE; MPTRIG *mp; int old_vnum = mob->vnum; MOVE(mob->vnum); if (mob->pShop) MOVE(mob->pShop->keeper); for (mp = mob->mptrig_list; mp; mp = mp->next) MOVE(mp->vnum); MOVE(mob->fvnum); /* touch area if it is not area being moved */ if (touched && !IN_RANGE(old_vnum, pArea->min_vnum, pArea->max_vnum)) touch_vnum(old_vnum); } static void move_obj(OBJ_INDEX_DATA *obj, AREA_DATA *pArea, int delta) { bool touched = FALSE; int old_vnum = obj->vnum; /* fix containers */ switch (obj->item_type) { case ITEM_CONTAINER: MOVE(obj->value[2]); if (touched) { OBJ_DATA *o; for (o = object_list; o; o = o->next) if (o->pIndexData == obj) o->value[2] += delta; } } MOVE(obj->vnum); /* touch area if it is not area being moved */ if (touched && !IN_RANGE(old_vnum, pArea->min_vnum, pArea->max_vnum)) touch_vnum(old_vnum); } static void move_room(ROOM_INDEX_DATA *room, AREA_DATA *pArea, int delta) { int i; bool touched = FALSE; int old_vnum = room->vnum; RESET_DATA *r; MOVE(room->vnum); for (i = 0; i < MAX_DIR; i++) { EXIT_DATA *pExit = room->exit[i]; if (!pExit || !pExit->to_room.r) continue; if (IN_RANGE(pExit->to_room.r->vnum, pArea->min_vnum+delta, pArea->max_vnum+delta)) touched = TRUE; } for (r = room->reset_first; r; r = r->next) { switch (r->command) { case 'M': case 'O': case 'P': MOVE(r->arg1); MOVE(r->arg3); break; case 'G': case 'E': case 'D': case 'R': MOVE(r->arg1); break; } } /* touch area if it is not area being moved */ if (touched && !IN_RANGE(old_vnum, pArea->min_vnum, pArea->max_vnum)) touch_vnum(old_vnum); }