#include <iostream>
#include <strstream>
#include <string>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <set>
#include <stdio.h>
#include <stdlib.h>
#include "../src/sectors.h"
#include "../src/colors.h"
#include "commands.h"

#define VERSION "1.3"
#define MIL 256
#define MSL 4608
#define MAX_ROOMS 150001
#define DIR_NORTH 	0
#define DIR_EAST 	1
#define DIR_SOUTH 	2
#define DIR_WEST 	3
#define DIR_NE		4
#define DIR_NW		5
#define DIR_SE		6
#define DIR_SW		7
#define LOWER(c) ((c) >= 'A' && (c) <= 'Z' ? (c)+'a'-'A' : (c))
#define MAPSIZE		35
#define ROOM_NOWALK             0
#define ROOM_NOFLY              1
#define ROOM_WATER              2
#define MAX_ROOM_FLAGS		3

void cprintf args((const string str));
int get_max_sectors args(());
string header args((const string str, const bool center = false));
string f_str_box args((const string str, const bool line_t = true, const bool line_b = true, const string c_box = "{0"));
void split args((string &args, string &arg));
bool find args((const string str1, const string str2, const bool case_ = false));

struct {
  string name;
  long room;
  bool viewCeiling;
} ch;

struct {
  string name, filename, author, version;
  int width, height, grav;
  bool town, wrap, weather, solo;
} map;

struct {
  int x, y;
  short sector, ceiling;
  bool flags[MAX_ROOM_FLAGS];
  unsigned long to[2], up[2], down[2];
} room[MAX_ROOMS];

int max_sectors;

main() {
  string input, command;
  bool found, running = false;
  max_sectors = get_max_sectors();
  cprintf(header("DRAGONBALL Z MUD Map Editor v" + (string)VERSION));
  cprintf(f_str_box("Email finished maps and suggestions to damdai@hotmail.com. Type '?' for a complete list of commands.", false));
  cout << "Name: ";
  cin >> ch.name;
  char buf[256];

  while(1) {
    found = false;
    
    if(!running) {
      cin.getline(buf, 256);
      running = true;
      continue;
    }
    
    printf("mapeditor> ");
    cin.getline(buf, 256);
    input = buf + '\0';
    split(input, command);
    
    if(command[0] == '\0')
      continue;
    
    for(short x = 1; cmd_table[x].cmd; ++x) {
      if(find(command, cmd_table[x].cmd)) {
	found = true;
	(*cmd_table[x].function)(input);
	break;
      }
    }
    
    if(!found)
      cout << command << ": ERROR: Command not found. Type '?' for a list of commands.\n";
  }
  
  exit(0);
  return 0;
}

string colour(const char type) {
  switch(type) {
  default:
  case 'x':
  case '0':	return CLEAR;
  case 'b':
  case '4':     return C_BLUE;
  case 'c':
  case '6':     return C_CYAN;
  case 'g':
  case '2':     return C_GREEN;
  case 'm':
  case '5':     return C_MAGENTA;
  case 'r':
  case '1':     return C_RED;
  case 'w':
  case '7':     return C_WHITE;
  case 'y':
  case '3':     return C_YELLOW;
  case 'B':
  case '$':     return C_B_BLUE;
  case 'C':
  case '^':     return C_B_CYAN;
  case 'G':
  case '@':     return C_B_GREEN;
  case 'M':
  case '%':     return C_B_MAGENTA;
  case 'R':
  case '!':     return C_B_RED;
  case 'W':
  case '&':     return C_B_WHITE;
  case 'Y':
  case '#':     return C_B_YELLOW;
  case 'D':
  case '8':     return C_D_GREY;
  case '{':     return "{";
  }
}

void cprintf(const string str) {
  string new_str = str;
  string::size_type pos;

  while((pos = new_str.find('{')) != string::npos)
    new_str.replace(pos, 2, colour(new_str[pos + 1]));

  cout << new_str;
  return;
}

void split(string &args, string &arg) {
  if(args.empty())
    return;
    
  args.erase(0, args.find_first_not_of(' '));  
  string::size_type pos;
    
  if(args[0] == '"') {
    if((pos = args.find('"', 1)) != string::npos) {
      arg = args.substr(0, pos);
      args.erase(0, pos + 1);
      return;
    }
  }
 
  if((pos = args.find(' ')) == string::npos) {
    arg = args;
    args = "";   
    return;
  }
  
  arg = args.substr(0, pos);
  args.erase(0, pos + 1);
  return;
}

bool find(const string str1, const string str2, const bool case_ = false) {
  string str_temp = str2, str = str1;
  string::size_type pos;
    
  if(!case_) {
    transform(str.begin(), str.end(), str.begin(), tolower);
    transform(str_temp.begin(), str_temp.end(), str_temp.begin(), tolower);
  }
    
  if((pos = str_temp.find(str, 0)) != string::npos && (!pos || str_temp[pos - 1] == ' '))
    return true;

  return false;
}

bool is_num(const string str) {
  if(str.empty())
    return false;

  for(unsigned short x = 0; x < str.length(); ++x)
    if(!isdigit(str[x]))
      return false;

  return true;
}

int strLen(const string str) {
  unsigned int len = 0, x;
    
  for(x = 0; x < str.length(); ++x) {
    if(str[x] == '{')
      ++x;
    else
      ++len;
  } 
    
  return len;
}     

string header(const string str, const bool center = false) {
  ostrstream ost;
  
  if(!center)
    ost << "+-{#" << str << "{0" << setfill('-') << setw((70 - str.length())) << "-" << CLEAR;
  else {
    ost << "+";
    short len = (70 - strLen(str)) / 2;
    
    for(short x = 0; x < len; ++x)
      ost << "-";

    ost << C_B_YELLOW << str << CLEAR;
  
    for(short x = 0; x < len; ++x)
      ost << "-";

    if(len % 2 == 0)
      ost << "-";
  }
  
  ost << "+\n\r" << ends;
  return ost.str();
}

string f_str_box(const string str, const bool line_t = true, const bool line_b = true, const string c_box = "{0") {
  ostrstream ost;
  string str_temp = str, str_temp2, color;
  string::size_type pos;
  short cnt;

  if(line_t)
    ost << c_box << '+' << setfill('-') << setw(71) << "-" << "+\n\r";

  ost.fill(' ');

  while(!str_temp.empty()) {   
    if(str_temp.length() > 69) {
      str_temp2 = str_temp.substr(0, 69);
      str_temp2 = str_temp2.substr(0, str_temp2.rfind(' '));
      str_temp.erase(0, str_temp2.length() + 1);  
    }
    else {
      str_temp2 = str_temp;
      str_temp.erase(str_temp.begin(), str_temp.end());
    }

    cnt = 0;
    pos = 0;

    while((pos = str_temp2.find('{', pos + 1)) != string::npos)
      cnt += 2;
    
    if(str_temp2[0] == '{')
      cnt += 2;
      
    if(!color.empty())
      ost << "| " << color << setiosflags(ios::left) << setw(69 + cnt) << str_temp2.c_str() << " {0|\n\r";
    else
      ost << "| " << setiosflags(ios::left) << setw(69 + cnt) << str_temp2.c_str() << " {0|\n\r";
  
    if((pos = str_temp2.rfind('{')) != string::npos && str_temp2[pos + 1] != '0')
      color = str_temp2.substr(pos, 2);
    else
      color = "";
  }
  
  if(line_b)
    ost << c_box << '+' << setfill('-') << setw(71) << "-" << "+\n\r{0";
 
  ost << ends;
  return ost.str();
}

long number_mm() {
  return random() >> 6;
}

int number_percent() {
  int percent;
  
  while((percent = number_mm() & (128-1)) > 99)
    ;
  
  return 1 + percent;
}

long map_room(const long fvnum, const short door) {
  long tvnum = 0, size = map.width * map.height;
  
  if(door == DIR_NORTH)       tvnum = fvnum - map.width;
  else if(door == DIR_SOUTH)  tvnum = fvnum + map.width;
  else if(door == DIR_EAST)   tvnum = fvnum + 1;
  else if(door == DIR_WEST)   tvnum = fvnum - 1;
  
  if(tvnum > size)
    tvnum -= size;
  else if(tvnum < 1)
    tvnum += size;
  
  return tvnum;
}

string print_sector(const long croom, const bool color) {
  if(room[croom].ceiling) {
    if(room[ch.room].ceiling && !ch.viewCeiling)
      return sector_table[room[croom].sector].print;
    else
      return sector_table[room[croom].ceiling].print;
  }

  string str_sector = sector_table[room[croom].sector].print;
  return (color ? str_sector : str_sector.substr(2, 2));
}

void print_map() {
  short x, y, width = map.width < MAPSIZE ? map.width : MAPSIZE, height = map.height < MAPSIZE ? map.height : MAPSIZE;
  long croom = ch.room, lroom;
  ostrstream ost;
  bool beginning = true, blank, was_blank = false;
  
  cout << "AUTHOR: " << map.author << " / MAP: " << map.name << " (" << map.filename << ".area) / SIZE: " << map.width * map.height << endl;
  cout << "TOWN: " << map.town << " / GRAVITY: " << map.grav << " / WRAP: " << map.wrap << " / WEATHER: " << map.weather << " / SOLO: " << map.solo << endl;
  
  for(y = 1; y <= height / 2; ++y)
    croom = map_room(croom, DIR_NORTH);
  
  for(x = 1; x <= width / 2; ++x)
    croom = map_room(croom, DIR_WEST);
  
  for(y = 1; y <= height; ++y) {
    for(x = 1; x <= width; ++x) {
      blank = false;
      
      if((abs)(room[croom].x - room[ch.room].x) > width / 2 || (abs)(room[croom].y - room[ch.room].y) > height / 2) {
	ost << "  ";
	blank = true;
      }
      else if(ch.room == croom)
	ost << "{0X ";
      else
	ost << print_sector(croom, (beginning || croom - 1 == ch.room || was_blank || (lroom && room[croom].sector != room[lroom].sector) || (lroom && room[croom].ceiling != room[lroom].ceiling) ? true : false));
      
      was_blank = blank;
      lroom = croom;
      croom = map_room(croom, DIR_EAST);
      beginning = false;
    }
    
    ost << endl;
    croom = map_room(croom, DIR_SOUTH);
    
    for(x = 1; x <= width; ++x)
      croom = map_room(croom, DIR_WEST);
    
    beginning = true;
  }
  
  ost << CLEAR << ends;
  cprintf(ost.str());
  cout << "LOCATION: " << ch.room << " (" << room[ch.room].x << ", " << room[ch.room].y << ") / SECTOR: " << room[ch.room].sector << " / WIDTH: " << map.width << " / HEIGHT " << map.height << endl;
  cout << "NOWALK: " << room[ch.room].flags[ROOM_NOWALK] << " / NOFLY: " << room[ch.room].flags[ROOM_NOFLY] << " / WATER: " << room[ch.room].flags[ROOM_WATER] << " / CEILING: " << room[ch.room].ceiling << endl;
  cout << "TO: " << room[ch.room].to[0] << " " << room[ch.room].to[1] << " / UP: " << room[ch.room].up[0] << " " << room[ch.room].up[1] << " / DOWN: " << room[ch.room].down[0] << " " << room[ch.room].down[1] << endl;
  return;
}

void createmap(int sector) {
  int x = 1, y = 1;
  long size = map.width * map.height;
  
  for(long vnum = 1; vnum <= size; ++vnum) {
    room[vnum].x = x;
    room[vnum].y = y;
    room[vnum].sector = sector;
    
    if(++x > map.width) {
      ++y;
      x = 1;
    }
  }
  
  ch.room = size / 2;
  return;
}

void do_quit(string argument = "") {
  exit(0);
  return;
}

short getCmdID(const string cmd) {
  for(short x = 1; cmd_table[x].cmd; ++x)
    if(find(cmd, cmd_table[x].cmd))
      return x;
}

void do_help(string argument = "") {
  short cnt = 0, cmd = 0;
  ostrstream ost;

  if(argument.empty() || !(cmd = getCmdID(argument))) {
	cout << "Usage: ? [command]\n";
    set<string> cmd_set;
    set<string>::iterator x;
    string str;
      
    for(cmd = 1; cmd_table[cmd].cmd; cmd++)
      cmd_set.insert(cmd_table[cmd].cmd);
      
    for(x = cmd_set.begin(); x != cmd_set.end(); ++x) {
      str = "{@" + *x + "{0*";
      ost << setiosflags(ios::left) << setw(16) << str.c_str() << ' ';
    
      if(++cnt % 5 == 0)
        ost << endl;
    }
   
    if(cnt % 5 != 0)
      ost << endl;  
    
    ost << ends;    
    cprintf(ost.str());     
    return;  
  }

  ost << f_str_box("Keyword: {2" + (string)cmd_table[cmd].cmd + "{0");
      
  if(cmd_table[cmd].usage) {
    ost << f_str_box("Usage: {2" + (string)cmd_table[cmd].usage + "{0", false);
    ost << f_str_box("Example: {2" + (string)cmd_table[cmd].example + "{0", false);
  }
  
  ost << f_str_box(cmd_table[cmd].help, false) << ends;
  cprintf(ost.str());
  return;
}

void do_newmap(string argument = "") {
  string arg1, arg2, arg3;
  int width, height, sector;
  split(argument, arg1);
  split(argument, arg2);
  split(argument, arg3);
  
  if(arg1.empty() || arg2.empty() || arg3.empty() || !is_num(arg2) || !is_num(arg3)) {
    cout << "Usage: newmap [name] [width] [height] -[default sector]\n";
    return;
  }
  
  width = atoi(arg2.c_str());
  height = atoi(arg3.c_str());
  sector = atoi(argument.c_str());
  
  if(arg1.length() > 20) {
    printf("name: ERROR: [name] must be less than 20 characters.\n");
    return;
  }
  
  if(width < 21 || height < 21) {
    printf("newmap: ERROR: [#width] and [#height] must be greater than 21.\n");
    return;
  }
  
  if(width * height > MAX_ROOMS) {
    printf("newmap: ERROR: [#width] * [#height] must be less than %ld.\n", MAX_ROOMS);
    return;
  }

  map.author = map.name = arg1;
  map.width = width;
  map.height = height;
  createmap(sector);
  print_map();
  return;
}

void do_look(string argument = "") {
  if(map.name.empty()) {
    printf("look: ERROR: No map loaded.\n");
    return;
  }
  
  print_map();
  return;
}

void move_char(const short door) {
  short from_door = 0;
  long vnum = 0, size = map.width * map.height;
  
  if(door == DIR_NORTH)       vnum = ch.room - map.width;
  else if(door == DIR_SOUTH)  vnum = ch.room + map.width;
  else if(door == DIR_EAST)   vnum = ch.room + 1;
  else if(door == DIR_WEST)   vnum = ch.room - 1;
  else if(door == DIR_NW)     vnum = ch.room - map.width - 1;
  else if(door == DIR_NE)     vnum = ch.room - map.width + 1;
  else if(door == DIR_SW)     vnum = ch.room + map.width - 1; 
  else if(door == DIR_SE)     vnum = ch.room + map.width + 1; 
  
  if(vnum > size)
    vnum -= size;
  else if(vnum < 1)
    vnum += size;
  
  ch.room = vnum;
  return;
}

short get_dir_id(string arg) {
  return find(arg, "north") ? DIR_NORTH : find(arg, "south") ? DIR_SOUTH : find(arg, "east") ? DIR_EAST : find(arg, "west") ? DIR_WEST : find(arg, "ne") ? DIR_NE : find(arg, "nw") ? DIR_NW : find(arg, "se") ? DIR_SE : DIR_SW;
}

void do_move(string argument = "") {
  string arg;
  split(argument, arg);
  
  if(arg.empty() || !find(arg, "n e s w ne nw se sw")) {
    cout << "Usage: move [direction] -[distance]\n";
    return;
  }
  
  short distance = argument.empty() || !is_num(argument) ? 1 : atoi(argument.c_str());
  
  if(distance > 100 || distance < 0) {
    cout << "move: ERROR: [distance] must be >= 0 and < 100.\n";
    return;
  }
  
  for(short x = 0; x < distance; ++x)
    move_char(get_dir_id(arg));
  
  do_look();
  return;
}

void do_sector(string argument = "") {
  short id = 0;

  if(map.name.empty()) {
    printf("sector: ERROR: No map loaded.\n");
    return;
  }
  
  if(argument.empty() || (id = atoi(argument.c_str())) < 0 || id > max_sectors) {
    int x, cnt = 0;
    char buf[256];
    
    printf("Usage: sector [name]\n");
    
    for(x = 0; sector_table[x].print; ++x) {
      sprintf(buf, "%3d) %s{0  ", x, sector_table[x].print);
      cprintf(buf);
      
      if(++cnt % 8 == 0)
	printf("\n");
    }
    
    if(cnt % 8 != 0)
      printf("\n");
    
    return;
  }

  room[ch.room].sector = id;
  return;
}

void do_ceiling(string argument = "") {
  short id = 0;
  
  if(map.name.empty()) {
    printf("ceiling: ERROR: No map loaded.\n");
    return;
  }
  
  if(argument.empty() || (id = atoi(argument.c_str())) < 0 || id > max_sectors) {
    int x, cnt = 0;
    char buf[256];
    
    printf("Usage: ceiling [name]\n");
   
    for(x = 0; sector_table[x].print; ++x) {
      sprintf(buf, "%3d) %s{0  ", x, sector_table[x].print);
      cprintf(buf);
    
      if(++cnt % 8 == 0)
        printf("\n");
    }
    
    if(cnt % 8 != 0)
      printf("\n");
     
    return;
  }
   
  room[ch.room].ceiling = id;
  return;
}

void do_save(string argument = "") {
  FILE *fp;
  string filename;
  long size = map.width * map.height, x;
  
  if(map.name.empty()) {
    printf("save: ERROR: No map loaded.\n");
    return;
  }
  
  if(map.filename.empty() && argument.empty()) {
    printf("Usage: save -[filename]\n");
    return;
  }
  
  if(!argument.empty()) {
    if(argument.length() > 20) {
      printf("save: ERROR: [filename] must be less than 20 characters.\n");
      return;
    }
    
    map.filename = argument;
  }

  filename = map.filename + ".area";
  fp = fopen(filename.c_str(), "w");
  fprintf(fp, "%s\n%s\n%s\n%d %d %d %d %d %d %d\n", VERSION, map.name.c_str(), map.author.c_str(), map.width, map.height, map.town, map.grav, map.wrap, map.weather, map.solo);
  
  for(x = 1; x <= size; ++x)
    fprintf(fp, "%d %d %d %d %ld %ld %ld %ld %ld %ld %d\n", room[x].sector, room[x].flags[ROOM_NOWALK], room[x].flags[ROOM_NOFLY], room[x].flags[ROOM_WATER], room[x].to[0], room[x].to[1], room[x].up[0], room[x].up[1], room[x].down[0], room[x].down[1], room[x].ceiling);
  
  fclose(fp);
  return;    
}

void do_load(string argument = "") {
  FILE *fp;
  unsigned long size, x;
  char temp_str[5], arg3[3], c;
  short arg, cnt;

  if(argument.empty()) {
    printf("Usage: load [filename]\n");
    return;
  }
  
  string filename = argument +  ".area";
  ifstream file(filename.c_str());

  if(!file.is_open()) {
    cout << "load: ERROR: " << filename << " not found.\n";
    return;
  }

  char c_temp[MIL];
  file.getline(c_temp, 10);
  map.version = c_temp + '\0';
  file.getline(c_temp, 50);
  map.name = c_temp + '\0';

  if(map.version == "1.1" || map.version == "1.2")
    map.author = ch.name;
  else {
    file.getline(c_temp, 50);
    map.author = c_temp + '\0';
  }

  map.filename = filename.substr(0, filename.length() - 5);
  file >> map.width >> map.height >> map.town >> map.grav >> map.wrap >> map.weather >> map.solo;
  size = map.width * map.height;
  createmap(0);

  for(x = 1; x <= size; ++x) {
    file >> room[x].sector >> room[x].flags[ROOM_NOWALK] >> room[x].flags[ROOM_NOFLY] >> room[x].flags[ROOM_WATER] >> room[x].to[0] >> room[x].to[1] >> room[x].up[0] >> room[x].up[1] >> room[x].down[0] >> room[x].down[1];

    if(map.version == "1.1")
      room[x].ceiling = 0;
    else
      file >> room[x].ceiling;
  }

  file.close();
  print_map();
  return;
}

void do_replace(string argument = "") {
  string arg1, arg2;
  long x, size = map.width * map.height;
  short chance;
  int sector1, sector2;
  
  if(map.name.empty()) {
    printf("replace: ERROR: No map loaded.\n");
    return;
  }
  
  split(argument, arg1);
  split(argument, arg2);
  
  if(arg1.empty() || arg2.empty()) {
    cout << "Usage: replace [sector1] [sector2] -[%]\n";
    return;
  }
  
  sector1 = atoi(arg1.c_str());
  sector2 = atoi(arg2.c_str());
  chance = argument.empty() || !is_num(argument) ? 100 : atoi(argument.c_str());
  
  for(x = 1; x <= size; ++x)
    if(room[x].sector == sector1 && number_percent() <= chance)
      room[x].sector = sector2;
  
  return;
}

void do_creplace(string argument = "") {
  string arg1, arg2;
  long x, size = map.width * map.height;
  short chance;
  int sector1, sector2;
  
  if(map.name.empty()) {
    printf("creplace: ERROR: No map loaded.\n");
    return;
  }
    
  split(argument, arg1);
  split(argument, arg2);
  
  if(arg1.empty() || arg2.empty()) {
    cout << "Usage: creplace [ceiling1] [ceiling2] -[%]\n";
    return;
  }
  
  sector1 = atoi(arg1.c_str());
  sector2 = atoi(arg2.c_str());
  chance = argument.empty() || !is_num(argument) ? 100 : atoi(argument.c_str());
    
  for(x = 1; x <= size; ++x)
    if(room[x].ceiling == sector1 && number_percent() <= chance)
      room[x].ceiling = sector2;
      
  return;
}

void do_copy(string argument = "") {
  string arg;
  
  if(map.name.empty()) {
    printf("copy: ERROR: No map loaded.\n");
    return;
  }
  
  split(argument, arg);
  
  if(arg.empty() || argument.empty() || !is_num(arg) || !find(argument, "n e s w ne nw se sw")) {
    printf("Usage: copy [distance] [direction]\n");
    return;
  }
  
  short distance = atoi(arg.c_str()), direction = get_dir_id(argument), sector = room[ch.room].sector, ceiling = room[ch.room].ceiling, y, x;
  bool flags[MAX_ROOM_FLAGS];

  for(x = 0; x < MAX_ROOM_FLAGS; ++x)
    flags[x] = room[ch.room].flags[x];

  if(distance) {
    for(x = 0; x < distance; ++x) {
      move_char(direction);
      room[ch.room].sector = sector;
      room[ch.room].ceiling = ceiling;

      for(y = 0; y < MAX_ROOM_FLAGS; ++y)
	room[ch.room].flags[y] = flags[y];
    }
  }
  else {
    move_char(direction);
    
    while(room[ch.room].sector != sector) {
      room[ch.room].sector = sector;
      room[ch.room].ceiling = ceiling;

      for(y = 0; y < MAX_ROOM_FLAGS; ++y)
        room[ch.room].flags[y] = flags[y];

      move_char(direction);
    }
  }
  
  do_look();
  return;
}

void do_ccopy(string argument = "") {
  string arg;
     
  if(map.name.empty()) {
    printf("ccopy: ERROR: No map loaded.\n");
    return;
  }
    
  split(argument, arg);
      
  if(arg.empty() || argument.empty() || !is_num(arg) || !find(argument, "n e s w ne nw se sw")) {
    printf("Usage: ccopy [distance] [direction]\n");
    return;
  }

  short distance = atoi(arg.c_str()), direction = get_dir_id(argument), ceiling = room[ch.room].ceiling;

  if(distance) {
    for(short x = 0; x < distance; ++x) { 
      move_char(direction);
      room[ch.room].ceiling = ceiling;
    }
  }
  else {
    move_char(direction);
   
    while(room[ch.room].ceiling != ceiling) {
      room[ch.room].ceiling = ceiling;
      move_char(direction);
    }
  }
  
  do_look();
  return;
}

void do_scopy(string argument = "") { 
  string arg;
     
  if(map.name.empty()) {
    printf("scopy: ERROR: No map loaded.\n");
    return; 
  }
 
  split(argument, arg);

  if(arg.empty() || argument.empty() || !is_num(arg) || !find(argument, "n e s w ne nw se sw")) {
    printf("Usage: scopy [distance] [direction]\n");
    return;
  }
    
  short distance = atoi(arg.c_str()), direction = get_dir_id(argument), sector = room[ch.room].sector;
     
  if(distance) {
    for(short x = 0; x < distance; ++x) { 
      move_char(direction);
      room[ch.room].sector = sector;
    }
  }
  else {
    move_char(direction);
    
    while(room[ch.room].sector != sector) {
      room[ch.room].sector = sector;
      move_char(direction);
    }
  }
  
  do_look();
  return;
}

void do_goto(string argument = "") {
  long vnum;
  
  if(map.name.empty()) {
    printf("goto: ERROR: No map loaded.\n");
    return;
  }
  
  if(argument.empty() || !is_num(argument)) {
    printf("Usage: goto [#room]\n");
    return;
  }
  
  vnum = atoi(argument.c_str());
  
  if(vnum < 1 || vnum > map.width * map.height) {
    printf("goto: ERROR: [#room] must be between 1 and %ld.\n", map.width * map.height);
    return;
  }
  
  ch.room = vnum;
  do_look();
  return;
}

void do_name(string argument = "") {
  if(map.name.empty()) {
    printf("name: ERROR: No map loaded.\n");
    return;
  }
  
  if(argument.empty()) {
    printf("Usage: name [name]\n");
    return;
  }
  
  if(argument.length() > 20) {
    printf("name: ERROR: [name] must be less than 20 characters.\n");
    return;
  }
  
  map.name =  argument;
  return;
}

char *add_html_color(char *string) {
  static char buf[MSL];
  int x;
  
  buf[0] = '\0';
  
  for(x = 0; x < strlen(string); ++x) {
    if(string[x] == '{') {
      ++x;
      
      switch(string[x]) {
      case '1': strcat(buf, "<font color=darkred>");  	break;
      case '!': strcat(buf, "<font color=red>");      	break;
      case '2': strcat(buf, "<font color=green>");    	break;
      case '@': strcat(buf, "<font color=lime>");     	break;
      case '3': strcat(buf, "<font color=808000>");   	break;
      case '#': strcat(buf, "<font color=yellow>");   	break;
      case 'b':
      case '4': strcat(buf, "<font color=darkblue>"); 	break;
      case 'B': strcat(buf, "<font color=blue>");     	break;
      case 'm':
      case '5':
      case 'M':	strcat(buf, "<font color=purple>");   	break;
      case '6': strcat(buf, "<font color=darkcyan>"); 	break;
      case '^': strcat(buf, "<font color=cyan>");     	break;
      case '7':	strcat(buf, "<font color=lightgrey>");	break;
      case '8': strcat(buf, "<font color=gray>");     	break;
      default:  strcat(buf, "<font color=white>");    	break;
      }
    }
    else
      sprintf(buf + strlen(buf), "%c", string[x]);
  }
  
  return buf;
}

void do_savehtml(string argument = "") {
  FILE *fp;
  char filename[MIL], buf[MSL];
  long size = map.width * map.height, x; 
  bool newline = true;

  if(map.name.empty()) {
    printf("savehtml: ERROR: No map loaded.\n");
    return;
  }

  if(map.filename.empty() && argument.empty()) {
    printf("Usage: savehtml -[filename]\n");
    return;
  }

  if(!argument.empty()) {
    if(argument.length() > 12) {
      printf("savehtml: ERROR: [filename] must be less than 12 characters.\n");
      return;
    }
    
    map.filename = argument;
  }
  
  sprintf(filename, "%s.html", map.filename.c_str());
  fp = fopen(filename, "w");
  fprintf(fp, "<html><head><title>DRAGONBALL Z MUD Map Editor v%s : %s</title></head><body bgcolor=black><font size=1><pre>\n", VERSION, map.name.c_str());
  
  for(x = 1; x <= size; ++x) {
    if(newline) {
      newline = false;
      sprintf(buf, "{0%6d %s", x, print_sector(x, true).c_str());//(!(x - 1) || room[x].sector != room[x - 1].sector) ? true : false));
    }
    else
      sprintf(buf + strlen(buf), "%s", print_sector(x, (!(x - 1) || room[x].sector != room[x - 1].sector || room[x].ceiling != room[x - 1].ceiling) ? true : false).c_str());
    
    if(x % map.width == 0) {
      newline  = true;
      fprintf(fp, "%s\n", add_html_color(buf));
    }
  }
  
  fprintf(fp, "</pre></body></html>");
  fclose(fp);
  return;
}

int get_max_sectors() {
  short x;

  for(x = 0; sector_table[x].print; ++x) ;
  return x - 1;
}

void reset(const unsigned long id) {
  for(short x = 0; x < MAX_ROOM_FLAGS; ++x)
    room[id].flags[x] = false;
}

void setRoom(const unsigned long id, string argument) {
  string arg, arg2;
  split(argument, arg);

  if(arg.empty() || !find(arg, "water nofly nowalk reset to up down")) {
    printf("Usage: set room [water | nofly | nowalk | reset | [to | up | down] [area] [id | [room]]]\n");
    return;
  }

  split(argument, arg2);

  if(find(arg, "water"))
    room[id].flags[ROOM_WATER] = true;
  else if(find(arg, "nofly"))
    room[id].flags[ROOM_NOFLY]= true;
  else if(find(arg, "nowalk"))
    room[id].flags[ROOM_NOWALK] = true;
  else if(find(arg, "reset"))
    reset(id);
  else if(find(arg, "to")) {
    room[id].to[0] = atoi(arg2.c_str());
    room[id].to[1] = (argument[0] != '\0' ? atoi(argument.c_str()) : id);
  }
  else if(find(arg, "up")) {
    room[id].up[0] = atoi(arg2.c_str());
    room[id].up[1] = (argument[0] != '\0' ? atoi(argument.c_str()) : id);
  }
  else if(find(arg, "down")) {
    room[id].down[0] = atoi(arg2.c_str());
    room[id].down[1] = (argument[0] != '\0' ? atoi(argument.c_str()) : id);
  }

  return;
}

void rset(string argument) {
  long x, sector;
  string arg, arg2;
  int size = map.width * map.height;
  split(argument, arg);

  if(arg.empty() || !find(arg, "sector id")) {
    printf("Usage: set room [sector | id] [water | nofly | nowalk | reset | [to | up | down] [area] [id | [room]]]\n");
    return;
  }

  split(argument, arg2);

  if(find(arg, "sector")) {
    short sector = atoi(arg2.c_str());

    if(!sector) {
      printf("ERROR: Invalid sector.\n");
      return;
    }

    for(x = 1; x <= size; ++x)
      if(room[x].sector == sector)
	setRoom(x, argument);
  }
  else {
    x = atoi(arg2.c_str());

    if(!x || x > MAX_ROOMS) {
      printf("ERROR: Invalid room ID.\n");
      return;
    }

    setRoom(x, argument);
  }

  return;
}

void aset(string argument) {
  string arg;
  split(argument, arg);

  if(arg.empty() || !find(arg, "town grav wrap weather solo")) {
    printf("Usage: set area [town | grav | wrap | weather | solo]\n");
    return;
  }

  if(find(arg, "town"))
    map.town = map.town ? false : true;
  else if(find(arg, "grav"))
    map.grav = atoi(argument.c_str());
  else if(find(arg, "wrap"))
    map.wrap = map.wrap ? false : true;
  else if(find(arg, "weather"))
    map.weather = map.weather ? false : true;
  else if(find(arg, "solo"))
    map.solo = map.solo ? false : true;

  return;
}

void do_set(string argument = "") {
  string arg;
  int size = map.width * map.height;
  split(argument, arg); 

  if(arg.empty() || !find(arg, "room area")) {
    printf("Usage: set [room | area]\n");
    return;
  }
 
  if(find(arg, "room"))
    rset(argument);
  else
    aset(argument);
 
  return;
}

void do_viewceiling(string argument = "") {
  ch.viewCeiling = !ch.viewCeiling;
  return;
}