code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Declarations for EDIT.CPP */

#ifndef __EDITOR_EDIT_H
#define __EDITOR_EDIT_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

EDITOR_LIBSPEC void load_editor_config(struct world *mzx_world, int *argc,
 char *argv[]);
EDITOR_LIBSPEC void editor_init(void);
EDITOR_LIBSPEC bool is_editor(void);

int place_current_at_xy(struct world *mzx_world, enum thing id, int color,
 int param, int x, int y, struct robot *copy_robot,
 struct scroll *copy_scroll, struct sensor *copy_sensor, int overlay_edit);

#define EC_MAIN_BOX           25
#define EC_MAIN_BOX_DARK      16
#define EC_MAIN_BOX_CORNER    24
#define EC_MENU_NAME          27
#define EC_CURR_MENU_NAME     159
#define EC_MODE_STR           30
#define EC_MODE_HELP          31
#define EC_CURR_THING         31
#define EC_CURR_PARAM         23
#define EC_OPTION             26
#define EC_HIGHLIGHTED_OPTION 31
#define EC_NA_FILL            1

__M_END_DECLS

#endif // __EDITOR_EDIT_H

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

// Dialogs for the world editor.

#include <string.h>

#include "../helpsys.h"
#include "../intake.h"
#include "../graphics.h"
#include "../window.h"
#include "../data.h"
#include "../idput.h"
#include "../const.h"
#include "../world_struct.h"
#include "../error.h"
#include "../counter.h"
#include "../board.h"
#include "../world.h"

#include "board.h"
#include "edit.h"
#include "edit_di.h"
#include "robo_ed.h"
#include "window.h"

// The 8th bit set indicates that it's a color, not a char
static const int char_values[8][24] =
{
  {
    0,
    1,
    2,
    3,
    6,
    8,
    9,
    11,
    13,
    14,
    15,
    16,
    20,
    21,
    22,
    23,
    24,
    27,
    28,
    29,
    30,
    31,
    32,
    33
  },
  {
    34,
    36,
    38,
    39,
    40,
    43,
    44,
    47,
    48,
    50,
    55,
    57,
    58,
    60,
    65,
    67,
    68,
    69,
    70,
    71,
    73,
    80,
    81,
    82
  },
  {
    83,
    84,
    85,
    86,
    87,
    88,
    89,
    90,
    91,
    94,
    95,
    125,
    126,
    160,
    161,
    162,
    163,
    164,
    165,
    166,
    167,
    168,
    169, // Special
    176 | 512
  },
  {
    177 | 512,
    178 | 512,
    179 | 512,
    180 | 512,
    181 | 512,
    182 | 512,
    183 | 512,
    184 | 512,
    185 | 512,
    186 | 512,
    187 | 512,
    188, // Special
    189, // Special
    198, // Special
    200, // Special
    190,
    191,
    192,
    193,
    194,
    195,
    196,
    197,
    230
  },
  {
    231,
    232,
    233,
    234,
    235,
    236,
    237,
    238,
    239,
    240,
    241,
    242,
    243,
    244,
    245,
    246,
    247,
    248,
    249,
    250,
    251,
    252,
    253,
    254
  },
  {
    255,
    256,
    257,
    258,
    259,
    260,
    261,
    262,
    263,
    264,
    265,
    266,
    267,
    268,
    269,
    270,
    271,
    272 | 512,
    273 | 512,
    274 | 512,
    275 | 512,
    276 | 512,
    277 | 512,
    278
  },
  {
    279,
    280,
    281,
    282 | 512,
    283 | 512,
    284 | 512,
    285 | 512,
    286,
    287,
    288,
    289,
    290,
    291,
    292 | 512,
    293 | 512,
    294,
    295,
    296,
    297,
    298 | 512,
    299 | 512,
    300 | 512,
    301 | 512,
    302 | 512
  },
  {
    303 | 512,
    304 | 512,
    305 | 512,
    306,
    307,
    308,
    309,
    310,
    311,
    312,
    313,
    314,
    315,
    316,
    317,
    318,
    319,
    320,
    321,
    322 | 512,
    323 | 512,
    324 | 512,
    325 | 512,
    326 | 512
  }
};

// Info for char funcs, 192 selections (24 per func, 8 funcs)
static const char *const char_strs[8][24] =
{
  {
    "Space (000)-",
    "Normal (001)-",
    "Solid (002)-",
    "Tree (003)-",
    "Breakaway (006)-",
    "Boulder (008)-",
    "Crate (009)-",
    "Box (011)-",
    "Fake (013)-",
    "Carpet (014)-",
    "Floor (015)-",
    "Tiles (016)-",
    "Still Water (020)-",
    "N Water (021)-",
    "S Water (022)-",
    "E Water (023)-",
    "W Water (024)-",
    "Chest (027)-",
    "Gem (028)-",
    "Magic Gem (029)-",
    "Health (030)-",
    "Ring (031)-",
    "Potion (032)-",
    "Energizer (033)-"
  },
  {
    "Goop (034)-",
    "Bomb (036)-",
    "Explosion (038)-",
    "Key (039)-",
    "Lock (040)-",
    "Stairs (043)-",
    "Cave (044)-",
    "Gate (047)-",
    "Open Gate (048)-",
    "Coin (050)-",
    "Pouch (055)-",
    "Slider NS (057)-",
    "Slider EW (058)-",
    "Lazer Gun (060)-",
    "Forest (065)-",
    "Whirlpool 1 (067)-",
    "Whirlpool 2 (068)-",
    "Whirlpool 3 (069)-",
    "Whirlpool 4 (070)-",
    "Invis. Wall (071)-",
    "Ricochet (073)-",
    "Snake (080)-",
    "Eye (081)-",
    "Thief (082)-"
  },
  {
    "Slime Blob (083)-",
    "Runner (084)-",
    "Ghost (085)-",
    "Dragon (086)-",
    "Fish (087)-",
    "Shark (088)-",
    "Spider (089)-",
    "Goblin (090)-",
    "Spitting Tiger (091)-",
    "Bear (094)-",
    "Bear Cub (095)-",
    "Sign (125)-",
    "Scroll (126)-",
    "Blank Ice (160)-",
    "Ice Anim 1 (161)-",
    "Ice Anim 2 (162)-",
    "Ice Anim 3 (163)-",
    "Lava Anim 1 (164)-",
    "Lava Anim 2 (165)-",
    "Lava Anim 3 (166)-",
    "Small Ammo (167)-",
    "Large Ammo (168)-",
    "Lit Bomb Anim 1 (169)-", // Special
    "Energizer Color 1 (176)-"
  },
  {
    "Energizer Color 2 (177)-",
    "Energizer Color 3 (178)-",
    "Energizer Color 4 (179)-",
    "Energizer Color 5 (180)-",
    "Energizer Color 6 (181)-",
    "Energizer Color 7 (182)-",
    "Energizer Color 8 (183)-",
    "Explosion Stage 1 (184)-",
    "Explosion Stage 2 (185)-",
    "Explosion Stage 3 (186)-",
    "Explosion Stage 4 (187)-",
    "Horizontal Door (\?\?\?)-",//Special
    "Vertical Door (\?\?\?)-",//Special
    "Diagonal Door / (\?\?\?)-",//Special
    "Diagonal Door \\ (\?\?\?)-",//Special
    "CW Anim 1 (190)-",
    "CW Anim 2 (191)-",
    "CW Anim 3 (192)-",
    "CW Anim 4 (193)-",
    "CCW Anim 1 (194)-",
    "CCW Anim 2 (195)-",
    "CCW Anim 3 (196)-",
    "CCW Anim 4 (197)-",
    "N Transport Anim 1 (230)-"
  },
  {
    "N Transport Anim 2 (231)-",
    "N Transport Anim 3 (232)-",
    "N Transport Anim 4 (233)-",
    "S Transport Anim 1 (234)-",
    "S Transport Anim 2 (235)-",
    "S Transport Anim 3 (236)-",
    "S Transport Anim 4 (237)-",
    "E Transport Anim 1 (238)-",
    "E Transport Anim 2 (239)-",
    "E Transport Anim 3 (240)-",
    "E Transport Anim 4 (241)-",
    "W Transport Anim 1 (242)-",
    "W Transport Anim 2 (243)-",
    "W Transport Anim 3 (244)-",
    "W Transport Anim 4 (245)-",
    "A.Transport Anim 1 (246)-",
    "A.Transport Anim 2 (247)-",
    "A.Transport Anim 3 (248)-",
    "A.Transport Anim 4 (249)-",
    "N Thick Arrow (250)-",
    "S Thick Arrow (251)-",
    "E Thick Arrow (252)-",
    "W Thick Arrow (253)-",
    "N Thin Arrow (254)-"
  },
  {
    "S Thin Arrow (255)-",
    "E Thin Arrow (256)-",
    "W Thin Arrow (257)-",
    "Horiz Lazer Anim 1 (258)-",
    "Horiz Lazer Anim 2 (259)-",
    "Horiz Lazer Anim 3 (260)-",
    "Horiz Lazer Anim 4 (261)-",
    "Vert Lazer Anim 1 (262)-",
    "Vert Lazer Anim 2 (263)-",
    "Vert Lazer Anim 3 (264)-",
    "Vert Lazer Anim 4 (265)-",
    "Fire Anim 1 (266)-",
    "Fire Anim 2 (267)-",
    "Fire Anim 3 (268)-",
    "Fire Anim 4 (269)-",
    "Fire Anim 5 (270)-",
    "Fire Anim 6 (271)-",
    "Fire Color 1 (272)-",
    "Fire Color 2 (273)-",
    "Fire Color 3 (274)-",
    "Fire Color 4 (275)-",
    "Fire Color 5 (276)-",
    "Fire Color 6 (277)-",
    "Life Anim 1 (278)-"
  },
  {
    "Life Anim 2 (279)-",
    "Life Anim 3 (280)-",
    "Life Anim 4 (281)-",
    "Life Color 1 (282)-",
    "Life Color 2 (283)-",
    "Life Color 3 (284)-",
    "Life Color 4 (285)-",
    "Ricochet Panel \\ (286)-",
    "Ricochet Panel / (287)-",
    "Mine Anim 1 (288)-",
    "Mine Anim 2 (289)-",
    "Spit Fire Anim 1 (290)-",
    "Spit Fire Anim 2 (291)-",
    "Spit Fire Color 1 (292)-",
    "Spit Fire Color 2 (293)-",
    "Seeker Anim 1 (294)-",
    "Seeker Anim 2 (295)-",
    "Seeker Anim 3 (296)-",
    "Seeker Anim 4 (297)-",
    "Seeker Color 1 (298)-",
    "Seeker Color 2 (299)-",
    "Seeker Color 3 (300)-",
    "Seeker Color 4 (301)-",
    "Whirlpool Color 1 (302)-"
  },
  {
    "Whirlpool Color 2 (303)-",
    "Whirlpool Color 3 (304)-",
    "Whirlpool Color 4 (305)-",
    "N Player Bullet (306)-",
    "S Player Bullet (307)-",
    "E Player Bullet (308)-",
    "W Player Bullet (309)-",
    "N Neutral Bullet (310)-",
    "S Neutral Bullet (311)-",
    "E Neutral Bullet (312)-",
    "W Neutral Bullet (313)-",
    "N Enemy Bullet (314)-",
    "S Enemy Bullet (315)-",
    "E Enemy Bullet (316)-",
    "W Enemy Bullet (317)-",
    "N Player Char (318)-",
    "S Player Char (319)-",
    "E Player Char (320)-",
    "W Player Char (321)-",
    "Player Color (322)-",
    "Missile Color (323)-",
    "Player Bullet Color (324)-",
    "Neutral Bullet Color (325)-",
    "Enemy Bullet Color (326)-"
  }
};

// Info for damage editing
static const char dmg_ids[22] =
{
  26,
  38,
  59,
  61,
  62,
  63,
  75,
  76,
  78,
  79,
  80,
  83,
  84,
  85,
  86,
  87,
  88,
  89,
  90,
  91,
  94,
  95
};

static const char *const dmg_strs[22] =
{
  "Lava-",
  "Explosion-",
  "Lazer-",
  "Bullet-",
  "Missile-",
  "Fire-",
  "Spike-",
  "Custom Hurt-",
  "Shooting Fire-",
  "Seeker-",
  "Snake-",
  "Slime Blob-",
  "Runner-",
  "Ghost-",
  "Dragon-",
  "Fish-",
  "Shark-",
  "Spider-",
  "Goblin-",
  "Spitting Tiger-",
  "Bear-",
  "Bear Cub-"
};

void set_confirm_buttons(struct element **elements)
{
  elements[0] = construct_button(15, 15, "OK", 0);
  elements[1] = construct_button(37, 15, "Cancel", 1);
}

void status_counter_info(struct world *mzx_world)
{
  struct dialog di;
  struct element *elements[2 + NUM_STATUS_COUNTERS];
  const char *status_counters_strings[NUM_STATUS_COUNTERS] =
  {
    "Status counter 1: ", "2: ", "3: ",
    "4: ", "5: ", "6: "
  };
  int i;

  set_confirm_buttons(elements);
  set_context(82);

  elements[2] = construct_input_box(12, 5,
   status_counters_strings[0], COUNTER_NAME_SIZE - 1, 0,
   mzx_world->status_counters_shown[0]);

  for(i = 1; i < NUM_STATUS_COUNTERS; i++)
  {
    elements[i + 2] = construct_input_box(27, 5 + i,
     status_counters_strings[i], COUNTER_NAME_SIZE - 1, 0,
     mzx_world->status_counters_shown[i]);
  }

  construct_dialog(&di, "Status Counters", 10, 4, 60, 18,
   elements, 2 + NUM_STATUS_COUNTERS, 2);

  run_dialog(mzx_world, &di);
  destruct_dialog(&di);

  pop_context();
}

void board_exits(struct world *mzx_world)
{
  struct dialog di;
  struct element *elements[6];
  struct board *src_board = mzx_world->current_board;
  int dialog_result;
  int exits[4];

  memcpy(exits, src_board->board_dir, sizeof(int) * 4);

  set_confirm_buttons(elements);
  set_context(83);

  elements[2] = construct_board_list(12, 4, "Board to north:",
   1, exits + 0);
  elements[3] = construct_board_list(12, 6, "Board to south:",
   1, exits + 1);
  elements[4] = construct_board_list(12, 8, "Board to east:",
   1, exits + 2);
  elements[5] = construct_board_list(12, 10, "Board to west:",
   1, exits + 3);
  construct_dialog(&di, "Board Exits", 10, 4, 60, 18,
   elements, 6, 2);

  dialog_result = run_dialog(mzx_world, &di);
  destruct_dialog(&di);

  if(!dialog_result)
  {
    memcpy(src_board->board_dir, exits, sizeof(int) * 4);
  }

  pop_context();
}

// Size/pos of board/viewport
void size_pos(struct world *mzx_world)
{
  struct board *src_board = mzx_world->current_board;
  int dialog_result;
  struct element *elements[8];
  struct dialog di;

  int redo = 1;

  int results[6] =
  {
    src_board->viewport_x, src_board->viewport_y,
    src_board->viewport_width, src_board->viewport_height,
    src_board->board_width, src_board->board_height
  };

  set_context(84);

  do
  {
    elements[0] = construct_button(15, 15, "OK", 0);
    elements[1] = construct_button(37, 15, "Center", 1);
    elements[2] = construct_number_box(15, 4, "Viewport X pos: ",
     0, 79, 0, results + 0);
    elements[3] = construct_number_box(15, 5, "Viewport Y pos: ",
     0, 24, 0, results + 1);
    elements[4] = construct_number_box(15, 6, "Viewport Width: ",
     1, 80, 0, results + 2);
    elements[5] = construct_number_box(15, 7, "Viewport Height:",
     1, 25, 0, results + 3);
    elements[6] = construct_number_box(15, 11, "Board Width:    ",
     1, 32767, 0, results + 4);
    elements[7] = construct_number_box(15, 12, "Board Height:   ",
     1, 32767, 0, results + 5);

    construct_dialog(&di, "Board Sizes/Positions", 10, 4, 60, 18,
     elements, 8, 2);

    dialog_result = run_dialog(mzx_world, &di);
    destruct_dialog(&di);

    // Fix sizes
    if(results[2] > results[4])
      results[2] = results[4];
    if(results[3] > results[5])
      results[3] = results[5];
    if((results[2] + results[0]) > 80)
      results[2] = 80 - results[0];
    if((results[3] + results[1]) > 25)
      results[3] = 25 - results[1];

    switch(dialog_result)
    {
      default:
      {
        // Center
        results[0] = 40 - (results[2] / 2);
        results[1] = 12 - (results[3] / 2);
        break;
      }

      case -1:
      {
        redo = 0;
        break;
      }

      case 0:
      {
        // Change the size; if it's not an enlargement, make sure
        // the player confirms first.

        // Hack to prevent multiplies of 256, for now - only relevant
        // with overlay off, but we want to avoid complications if
        // the user turns the overlay off later
        if((results[4] % 256) == 0)
          results[4]++;

        if((results[4] * results[5]) > MAX_BOARD_SIZE)
        {
          if(results[4] > results[5])
            results[5] = MAX_BOARD_SIZE / results[4];
          else
            results[4] = MAX_BOARD_SIZE / results[5];
        }

        if(((results[4] >= src_board->board_width) &&
          (results[5] >= src_board->board_height)) ||
          !confirm(mzx_world, "Reduce board size- Are you sure?"))
        {
          redo = 0;
          change_board_size(src_board, results[4], results[5]);
          src_board->viewport_x = results[0];
          src_board->viewport_y = results[1];
          src_board->viewport_width = results[2];
          src_board->viewport_height = results[3];
        }
        break;
      }
    }
  } while(redo);

  pop_context();
}

//Dialog- (board info)
//----------------------------------------------------------
//        Board name- __________________________
//    [ ] Can shoot               ( ) Explosions to space
//    [ ] Can bomb                ( ) Explosions to ash
//    [ ] Fire burns space        ( ) Explosions to fire
//    [ ] Fire burns fakes
//    [ ] Fire burns trees        ( ) Can save
//    [ ] Fire burns brown        ( ) Can't save
//    [ ] Forest to floor         ( ) Can save on sensors
//    [ ] Collect bombs
//    [ ] Fire burns forever      ( ) No overlay
//    [ ] Restart if hurt         ( ) Normal overlay
//                                ( ) Static overlay
//    Time limit- _00000__!__!_   ( ) Transparent overlay
//
//              _OK_                  _Cancel_
//
//----------------------------------------------------------

// Board info
void board_info(struct world *mzx_world)
{
  struct board *src_board = mzx_world->current_board;
  int dialog_result;
  struct element *elements[8];
  struct dialog di;
  int check_box_results[13] =
  {
    src_board->can_shoot, src_board->can_bomb,
    src_board->fire_burn_space, src_board->fire_burn_fakes,
    src_board->fire_burn_trees, src_board->fire_burn_brown,
    src_board->forest_becomes, src_board->collect_bombs,
    src_board->fire_burns, src_board->restart_if_zapped,
    src_board->player_ns_locked, src_board->player_ew_locked,
    src_board->player_attack_locked
  };
  const char *check_box_strings[] =
  {
    "Can shoot", "Can bomb", "Fire burns space",
    "Fire burns fakes", "Fire burns trees", "Fire burns brown",
    "Forest to floor", "Collect bombs", "Fire burns forever",
    "Restart if hurt", "Player locked N/S", "Player locked E/W",
    "Player attack locked"
  };
  const char *radio_strings_1[] =
  {
    "Explosions to space", "Explosions to ash",
    "Explosions to fire"
  };
  const char *radio_strings_2[] =
  {
    "Can save", "Can't save", "Can save on sensors"
  };
  const char *radio_strings_3[] =
  {
    "No overlay", "Normal overlay", "Static overlay",
    "Transparent overlay"
  };
  int radio_result_1 = src_board->explosions_leave;
  int radio_result_2 = src_board->save_mode;
  int radio_result_3 = src_board->overlay_mode;
  int time_limit = src_board->time_limit;
  char title_string[BOARD_NAME_SIZE];

  set_context(85);

  strcpy(title_string, src_board->board_name);

  elements[0] = construct_button(15, 18, "OK", 0);
  elements[1] = construct_button(37, 18, "Cancel", 1);
  elements[2] = construct_input_box(9, 1, "Board name- ",
   BOARD_NAME_SIZE - 1, 0, title_string);
  elements[3] = construct_check_box(5, 2, check_box_strings,
   13, 20, check_box_results);
  elements[4] = construct_number_box(5, 16, "Time limit- ",
   0, 32767, 0, &time_limit);
  elements[5] = construct_radio_button(33, 3, radio_strings_1,
   3, 19, &radio_result_1);
  elements[6] = construct_radio_button(33, 8, radio_strings_2,
   3, 19, &radio_result_2);
  elements[7] = construct_radio_button(33, 13, radio_strings_3,
   4, 19, &radio_result_3);

  construct_dialog(&di, "Board Settings", 10, 2, 60, 21,
   elements, 8, 2);

  dialog_result = run_dialog(mzx_world, &di);

  if(!dialog_result)
  {
    strcpy(src_board->board_name, title_string);
    src_board->can_shoot = check_box_results[0];
    src_board->can_bomb = check_box_results[1];
    src_board->fire_burn_space = check_box_results[2];
    src_board->fire_burn_fakes = check_box_results[3];
    src_board->fire_burn_trees = check_box_results[4];
    src_board->fire_burn_brown = check_box_results[5];
    src_board->forest_becomes = check_box_results[6];
    src_board->collect_bombs = check_box_results[7];
    src_board->fire_burns = check_box_results[8];
    src_board->restart_if_zapped = check_box_results[9];
    src_board->player_ns_locked = check_box_results[10];
    src_board->player_ew_locked = check_box_results[11];
    src_board->player_attack_locked = check_box_results[12];
    src_board->explosions_leave = radio_result_1;
    src_board->save_mode = radio_result_2;

    if(src_board->overlay_mode != radio_result_3)
    {
      setup_overlay(src_board, radio_result_3);
      src_board->overlay_mode = radio_result_3;
    }

    src_board->time_limit = time_limit;
  }

  destruct_dialog(&di);
  pop_context();
}

// Chars #1-8
static void global_chars(struct world *mzx_world)
{
  int i;
  int current_id, dialog_result;
  int current_menu = 0;
  int results[24];
  struct element *elements[27];
  struct dialog di;
  set_context(89);

  do
  {
    elements[0] = construct_button(15, 15, "Next", 1);
    elements[1] = construct_button(25, 15, "Previous", 2);
    elements[2] = construct_button(39, 15, "Done", 3);

    for(i = 0; i < 12; i++)
    {
      if(char_values[current_menu][i] & 512)
      {
        elements[i + 3] = construct_color_box(2 +
         (25 - (int)strlen(char_strs[current_menu][i])), 2 + i,
         char_strs[current_menu][i], 0, results + i);
      }
      else
      {
        elements[i + 3] = construct_char_box(2 +
         (25 - (int)strlen(char_strs[current_menu][i])), 2 + i,
         char_strs[current_menu][i],
         char_values[current_menu][i] > 127, results + i);
      }

      current_id = char_values[current_menu][i] & 511;
      if(current_id == 323)
        results[i] = missile_color;
      else

      if((current_id >= 324) && (current_id <= 326))
        results[i] = bullet_color[current_id - 324];
      else
        results[i] = id_chars[current_id];
    }

    for(; i < 24; i++)
    {
      if(char_values[current_menu][i] & 512)
      {
        elements[i + 3] = construct_color_box(32 +
         (25 - (int)strlen(char_strs[current_menu][i])), i - 10,
         char_strs[current_menu][i], 0, results + i);
      }
      else
      {
        elements[i + 3] = construct_char_box(32 +
         (25 - (int)strlen(char_strs[current_menu][i])), i - 10,
         char_strs[current_menu][i],
         char_values[current_menu][i] > 127, results + i);
      }

      current_id = char_values[current_menu][i] & 511;
      if(current_id == 323)
        results[i] = missile_color;
      else

      if((current_id >= 324) && (current_id <= 326))
        results[i] = bullet_color[current_id - 324];
      else
        results[i] = id_chars[current_id];
    }

    construct_dialog(&di, "Edit Characters", 9, 4, 62, 18,
     elements, 27, 3);

    // Run
    dialog_result = run_dialog(mzx_world, &di);
    destruct_dialog(&di);

    if(dialog_result == -1)
      break;

    // Get from storage
    for(i = 0; i < 24; i++)
    {
      current_id = char_values[current_menu][i] & 511;
      if(current_id == 323)
        missile_color = results[i];
      else

      if((current_id >= 324) && (current_id <= 326))
        bullet_color[current_id - 324] = results[i];
      else
        id_chars[current_id] = results[i];
    }

    // Setup lit bomb sequence or doors
    if(current_menu == 2)
    {
      id_chars[170] = id_chars[169] - 1;
      id_chars[171] = id_chars[170] - 1;
      id_chars[172] = id_chars[171] - 1;
      id_chars[173] = id_chars[172] - 1;
      id_chars[174] = id_chars[173] - 1;
      id_chars[175] = id_chars[174] - 1;
    }
    else

    if(current_menu == 3)
    {
      // Doors
      id_chars[199] = id_chars[198]; // '/'
      id_chars[201] = id_chars[200]; // '\'
      id_chars[202] = id_chars[200]; // '\'
      id_chars[203] = id_chars[200]; // '\'
      id_chars[204] = id_chars[198]; // '/'
      id_chars[205] = id_chars[198]; // '/'
      id_chars[206] = id_chars[189]; // '|'
      id_chars[207] = id_chars[188]; // '-'
      id_chars[208] = id_chars[189]; // '|'
      id_chars[209] = id_chars[188]; // '-'
      id_chars[210] = id_chars[189]; // '|'
      id_chars[211] = id_chars[188]; // '-'
      id_chars[212] = id_chars[189]; // '|'
      id_chars[213] = id_chars[188]; // '-'
      id_chars[214] = id_chars[189]; // '|'
      id_chars[215] = id_chars[188]; // '-'
      id_chars[216] = id_chars[189]; // '|'
      id_chars[217] = id_chars[188]; // '-'
      id_chars[218] = id_chars[189]; // '|'
      id_chars[219] = id_chars[188]; // '-'
      id_chars[220] = id_chars[189]; // '|'
      id_chars[221] = id_chars[188]; // '-'
      id_chars[222] = id_chars[198]; // '/'
      id_chars[223] = id_chars[198]; // '/'
      id_chars[224] = id_chars[200]; // '\'
      id_chars[225] = id_chars[200]; // '\'
      id_chars[226] = id_chars[200]; // '\'
      id_chars[227] = id_chars[200]; // '\'
      id_chars[228] = id_chars[198]; // '/'
      id_chars[229] = id_chars[198]; // '/'
    }

    // Next, prev, or done
    if(dialog_result == 1)
    {
      current_menu++;
      if((current_menu) > 7)
        current_menu = 0;
    }
    else

    if(dialog_result == 2)
    {
      current_menu--;

      if((current_menu) < 0)
        current_menu = 7;
    }
  } while((dialog_result == 1) || (dialog_result == 2));

  pop_context();
}

static void global_dmg(struct world *mzx_world)
{
  int dialog_result;
  struct element *elements[24];
  struct dialog di;
  int results[22];
  int i;

  set_context(90);
  set_confirm_buttons(elements);

  for(i = 0; i < 11; i++)
  {
    results[i] = id_dmg[(int)dmg_ids[i]];
    elements[i + 2] = construct_number_box(2 +
     (14 - (int)strlen(dmg_strs[i])), 2 + i,
     dmg_strs[i], 0, 255, 0, results + i);
  }

  for(; i < 22; i++)
  {
    results[i] = id_dmg[(int)dmg_ids[i]];
    elements[i + 2] = construct_number_box(31 +
     (14 - (int)strlen(dmg_strs[i])), i - 9,
     dmg_strs[i], 0, 255, 0, results + i);
  }

  construct_dialog(&di, "Edit Damage", 10, 4, 60, 18,
   elements, 24, 2);

  dialog_result = run_dialog(mzx_world, &di);
  destruct_dialog(&di);

  if(!dialog_result)
  {
    for(i = 0; i < 22; i++)
    {
      id_dmg[(int)dmg_ids[i]] = results[i];
    }
  }
  pop_context();
}

void global_info(struct world *mzx_world)
{
  int death_board = mzx_world->death_board;
  int endgame_board = mzx_world->endgame_board;
  int death_x = mzx_world->death_x;
  int death_y = mzx_world->death_y;
  int endgame_x = mzx_world->endgame_x;
  int endgame_y = mzx_world->endgame_y;
  int first_board = mzx_world->first_board;
  int edge_color = mzx_world->edge_color;
  int starting_lives = mzx_world->starting_lives;
  int lives_limit = mzx_world->lives_limit;
  int starting_health = mzx_world->starting_health;
  int health_limit = mzx_world->health_limit;
  int radio_result_1 = 2;
  int radio_result_2 = 1;
  int check_box_results_1[1] = { mzx_world->game_over_sfx };
  int check_box_results_2[3] =
  {
    mzx_world->enemy_hurt_enemy, mzx_world->clear_on_exit,
    mzx_world->only_from_swap
  };
  const char *radio_strings_1[] =
  {
    "Death- Same position", "Death- Restart board",
    "Death- Teleport"
  };
  const char *radio_strings_2[] =
  {
    "Endgame- Game over", "Endgame- Teleport"
  };
  const char *check_box_strings_1[] = { "Play game over sfx" };
  const char *check_box_strings_2[] =
  {
    "Enemies' bullets hurt other enemies",
    "Clear messages and projectiles on exit",
    "Can only play world from a 'SWAP WORLD'"
  };
  struct dialog a_di;
  struct dialog b_di;
  struct element *a_elements[15];
  struct element *b_elements[12];
  int dialog_result;
  int redo = 0;

  do
  {
    set_confirm_buttons(a_elements);
    a_elements[2] = construct_board_list(5, 2, "First board-",
     0, &first_board);
    a_elements[3] = construct_color_box(38, 3, "Edging color- ",
     0, &edge_color);
    a_elements[4] = construct_label(5+10+4, 5, "Lives"),
    a_elements[5] = construct_label(32+10+3, 5, "Health"),
    a_elements[6] = construct_number_box(5, 6, "Starting- ",
     1, 32767, 0, &starting_lives);
    a_elements[7] = construct_number_box(5, 7, "Maximum-  ",
     1, 32767, 0, &lives_limit);
    a_elements[8] = construct_number_box(32, 6, "Starting- ",
     1, 32767, 0, &starting_health);
    a_elements[9] = construct_number_box(32, 7, "Maximum-  ",
     1, 32767, 0, &health_limit);
    a_elements[10] = construct_check_box(7, 9, check_box_strings_2,
     3, 39, check_box_results_2);
    a_elements[11] = construct_button(5, 13, "More", 2);
    a_elements[12] = construct_button(12, 13, "Edit Chars", 3);
    a_elements[13] = construct_button(25, 13, "Edit Dmg", 4);
    a_elements[14] = construct_button(36, 13, "Edit Global Robot", 5);

    construct_dialog(&a_di, "Global Settings", 10, 4, 60, 18,
     a_elements, 15, 2);

    redo = 0;
    set_context(86);

    dialog_result = run_dialog(mzx_world, &a_di);
    destruct_dialog(&a_di);

    switch(dialog_result)
    {
      case 2:
      {
        // Returns 1 for previous
        set_context(88);

        if(death_board == DEATH_SAME_POS)
        {
          death_board = 0;
          radio_result_1 = 0;
        }

        if(death_board == NO_DEATH_BOARD)
        {
          death_board = 0;
          radio_result_1 = 1;
        }

        if(endgame_board == NO_ENDGAME_BOARD)
        {
          endgame_board = 0;
          radio_result_2 = 0;
        }

        set_confirm_buttons(b_elements);
        b_elements[2] = construct_board_list(1, 2, "Death board-",
         0, &death_board);
        b_elements[3] = construct_number_box(1, 5, "Death X- ",
         0, 32767, 0, &death_x);
        b_elements[4] = construct_number_box(1, 6, "Death Y- ",
         0, 32767, 0, &death_y);
        b_elements[5] = construct_radio_button(1, 8, radio_strings_1,
         3, 20, &radio_result_1);
        b_elements[6] = construct_board_list(30, 2, "Endgame board-",
         0, &endgame_board);
        b_elements[7] = construct_number_box(30, 5, "Endgame X- ",
         0, 32767, 0, &endgame_x);
        b_elements[8] = construct_number_box(30, 6, "Endgame Y- ",
         0, 32767, 0, &endgame_y);
        b_elements[9] = construct_radio_button(30, 8, radio_strings_2,
         2, 18, &radio_result_2);
        b_elements[10] = construct_check_box(30, 11, check_box_strings_1,
         1, 18, check_box_results_1);

        b_elements[11] = construct_button(25, 13, "Previous", 2); //38, 9

        construct_dialog(&b_di, "Global Settings (Continued)", 10, 4,
         60, 18, b_elements, 12, 2);

        dialog_result = run_dialog(mzx_world, &b_di);
        destruct_dialog(&b_di);

        pop_context();

        if(dialog_result == 2)
        {
          redo = 1;
          break;
        }

        // If okay, fall through and apply settings
        if(dialog_result == 1)
          break;
      }

      case 0:
      {
        if(radio_result_1 == 2)
          mzx_world->death_board = death_board;
        else

        if(radio_result_1 == 1)
          mzx_world->death_board = NO_DEATH_BOARD;

        else
          mzx_world->death_board = DEATH_SAME_POS;

        if(radio_result_2 == 1)
          mzx_world->endgame_board = endgame_board;
        else
          mzx_world->endgame_board = NO_ENDGAME_BOARD;

        mzx_world->death_x = death_x;
        mzx_world->endgame_x = endgame_x;
        mzx_world->death_y = death_y;
        mzx_world->endgame_y = endgame_y;
        mzx_world->game_over_sfx = check_box_results_1[0];

        mzx_world->first_board = first_board;
        mzx_world->edge_color = edge_color;

        // These are the only parts of the default global data
        // that the editor can change, so it has to be updated
        // so testing won't screw things up.
        set_counter(mzx_world, "LIVES", starting_lives, 0);
        set_counter(mzx_world, "HEALTH", starting_health, 0);

        mzx_world->starting_lives = starting_lives;
        mzx_world->lives_limit = lives_limit;
        mzx_world->starting_health = starting_health;
        mzx_world->health_limit = health_limit;
        mzx_world->enemy_hurt_enemy = check_box_results_2[0];
        mzx_world->clear_on_exit = check_box_results_2[1];
        mzx_world->only_from_swap = check_box_results_2[2];

        if(starting_lives > lives_limit)
          mzx_world->starting_lives = lives_limit;

        if(starting_health > health_limit)
          mzx_world->starting_health = health_limit;

        break;
      }

      case 3:
      {
        global_chars(mzx_world); // Chars
        break;
      }

      case 4:
      {
        global_dmg(mzx_world); // Dmg
        break;
      }

      case 5:
      {
        global_robot(mzx_world);
        break;
      }
    }
  } while(redo);

  pop_context();
}

void global_robot(struct world *mzx_world)
{
  struct robot *cur_robot = &(mzx_world->global_robot);
  // First get name...
  m_hide();
  save_screen();
  draw_window_box(16, 12, 50, 14, DI_DEBUG_BOX, DI_DEBUG_BOX_DARK,
  DI_DEBUG_BOX_CORNER, 1, 1);
  write_string("Name for robot:", 18, 13, DI_DEBUG_LABEL, 0);
  m_show();

  if(intake(mzx_world, cur_robot->robot_name,
   14, 34, 13, 15, 1, 0, NULL, 0, NULL) != IKEY_ESCAPE)
  {
    restore_screen();
    set_context(87);
    robot_editor(mzx_world, cur_robot);
    pop_context();
  }
  else
  {
    restore_screen();
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

//EDIT_DI.CPP prototypes

#ifndef __EDITOR_EDIT_DI_H
#define __EDITOR_EDIT_DI_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"
#include "../window.h"

void board_info(struct world *mzx_world);
void board_exits(struct world *mzx_world);
void global_info(struct world *mzx_world);
void global_robot(struct world *mzx_world);
void size_pos(struct world *mzx_world);
void set_confirm_buttons(struct element **elements);
void status_counter_info(struct world *mzx_world);

__M_END_DECLS

#endif // __EDITOR_EDIT_DI_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Fill function. */

#include "fill.h"

#include "edit.h"

/* Pseudo-code algorithm-

  (MATCHES means that the id/color at that id is what we are
   filling over. PLACING includes deleting the old, deleting programs,
   and copying current program, if any. The STACK is an array for
   keeping track of areas we need to continue filling at, and the
   direction to fill in at that point. A PUSH onto the STACK moves the
   stack pointer up one and inserts info, unless the STACK is full, in
   which case the operation is cancelled. A STACK size of about 500
   elements is usally enough for a fairly complex 100 by 100 section.)

  (Note that the usual struct robot#0 code is NOT performed- If current is a
   robot, it is already #0 and must stay that way for proper filling.)

  1) Initialize fill stack and record what we are filling over.
  2) Push current position going left.
  3) Loop:

    1) Take top element off of stack.
    1.5) Verify it still matches (in case another branch got here)
    2) Start at noted position. Set ABOVE_MATCH and BELOW_MATCH to 0.
    3a) If direction is left, push onto stack-

      1) Id above, going left, if matches. Set ABOVE_MATCH to 1.
      2) Id below, going left, if matches. Set BELOW_MATCH to 1.
      3) Id to the right, going right, if matches.

    3b) If direction is right, push nothing.
    4) Place at position.
    6) Note what is above- If it DOESN'T match, set ABOVE_MATCH to 0.
    7) Note what is below- If it DOESN'T match, set BELOW_MATCH to 0.
    8) If above DOES match and ABOVE_MATCH is 0-

      1) Push onto stack Id above, going left.
      2) Set ABOVE_MATCH to 1.

    9) If below DOES match and BELOW_MATCH is 0-

      1) Push onto stack Id below, going left.
      2) Set BELOW_MATCH to 1.

    10) Go one step in direction. (left/right)
    11) If position matches, jump to #4.
    12) If any elements remain on stack, loop.

  4) Done.
*/

#define match_position(off, new_x, new_y, d, check)     \
  new_offset = off;                                     \
  if((id_check[new_offset] == fill_over_id) &&          \
   (param_check[new_offset] == fill_over_param) &&      \
   (color_check[new_offset] == fill_over_color))        \
  {                                                     \
    if(stack_pos < STACK_SIZE)                          \
    {                                                   \
      stack_pos++;                                      \
      stack[stack_pos].x = new_x;                       \
      stack[stack_pos].y = new_y;                       \
      stack[stack_pos].dir = d;                         \
      check = 1;                                        \
    }                                                   \
  }                                                     \

#define match_no_check(off, new_x, new_y, d)            \
  new_offset = off;                                     \
  if((id_check[new_offset] == fill_over_id) &&          \
   (param_check[new_offset] == fill_over_param) &&      \
   (color_check[new_offset] == fill_over_color))        \
  {                                                     \
    if(stack_pos < STACK_SIZE)                          \
    {                                                   \
      stack_pos++;                                      \
      stack[stack_pos].x = new_x;                       \
      stack[stack_pos].y = new_y;                       \
      stack[stack_pos].dir = d;                         \
    }                                                   \
  }                                                     \

#define no_match(off, check)                            \
  new_offset = off;                                     \
  if((id_check[new_offset] != fill_over_id) ||          \
   (param_check[new_offset] != fill_over_param) ||      \
   (color_check[new_offset] != fill_over_color))        \
    check = 0;                                          \

#define match_position_ov(off, new_x, new_y, d, check)  \
  new_offset = off;                                     \
  if((param_check[new_offset] == fill_over_param) &&    \
   (color_check[new_offset] == fill_over_color))        \
  {                                                     \
    if(stack_pos < STACK_SIZE)                          \
    {                                                   \
      stack_pos++;                                      \
      stack[stack_pos].x = new_x;                       \
      stack[stack_pos].y = new_y;                       \
      stack[stack_pos].dir = d;                         \
      check = 1;                                        \
    }                                                   \
  }                                                     \

#define match_no_check_ov(off, new_x, new_y, d)         \
  new_offset = off;                                     \
  if((param_check[new_offset] == fill_over_param) &&    \
   (color_check[new_offset] == fill_over_color))        \
  {                                                     \
    if(stack_pos < STACK_SIZE)                          \
    {                                                   \
      stack_pos++;                                      \
      stack[stack_pos].x = new_x;                       \
      stack[stack_pos].y = new_y;                       \
      stack[stack_pos].dir = d;                         \
    }                                                   \
  }                                                     \

#define no_match_ov(off, check)                         \
  new_offset = off;                                     \
  if((param_check[new_offset] != fill_over_param) ||    \
   (color_check[new_offset] != fill_over_color))        \
    check = 0;                                          \

// Structure for one element of the stack
struct stack_elem
{
  short int x;
  short int y;
  // -1 = Left, 1 = Right.
  signed char dir;
};

// The stack
#define STACK_SIZE 4096

void fill_area(struct world *mzx_world, enum thing id, int color, int param,
 int x, int y, struct robot *copy_robot, struct scroll *copy_scroll, struct sensor *copy_sensor,
 int overlay_edit)
{
  struct board *src_board = mzx_world->current_board;
  enum thing fill_over_id;
  int fill_over_param, fill_over_color;
  int dir, above_match, below_match;
  int new_offset;
  int board_width = src_board->board_width;
  int board_height = src_board->board_height;
  char *id_check;
  char *param_check;
  char *color_check;
  int offset = x + (y * board_width);

  int stack_pos; // Current element. -1 = empty.
  struct stack_elem *stack = cmalloc(sizeof(struct stack_elem) * (STACK_SIZE + 1));

  if(overlay_edit)
  {
    param_check = src_board->overlay;
    color_check = src_board->overlay_color;

    // 1) Initialize fill stack and record what we are filling over.
    fill_over_color = color_check[offset];
    fill_over_param = param_check[offset];

    if((fill_over_param == param) && (fill_over_color == color))
      goto out_free_stack;

    stack_pos = 0;
    // 2) Push current position going left.
    stack[0].x = x;
    stack[0].y = y;
    stack[0].dir = -1;

    // 3) Loop:
    do
    {
      // 1) Take top element off of stack.
      x = stack[stack_pos].x;
      y = stack[stack_pos].y;
      dir = stack[stack_pos].dir;
      offset = x + (y * board_width);
      stack_pos--;

      // 2) Start at noted position. Set ABOVE_MATCH and BELOW_MATCH to 0.
      above_match = 0;
      below_match = 0;
      // 3a) If direction is left, push onto stack-
      if(dir == -1)
      {
        // 1) Id above, going left, if matches. Set ABOVE_MATCH to 1.
        if(y > 0)
        {
          match_position_ov(offset - board_width, x, y - 1, -1, above_match);
        }
        // 2) Id below, going left, if matches. Set BELOW_MATCH to 1.
        if(y < (board_height - 1))
        {
          match_position_ov(offset + board_width, x, y + 1, -1, below_match);
        }
        // 3) Id to the right, going right, if matches.
        if(x < (board_width - 1))
        {
          match_no_check_ov(offset + 1, x + 1, y, 1);
        }
      }
      //  3b) If direction is right, push nothing.
      //  4) Place at position.

      do
      {
        if(place_current_at_xy(mzx_world, id, color, param, x, y, copy_robot,
         copy_scroll, copy_sensor, overlay_edit) == -1)
          goto out_free_stack;

        // 6) Note what is above- If it DOESN'T match, set ABOVE_MATCH to 0.
        if(y > 0)
        {
          no_match_ov(offset - board_width, above_match);
        }

        // 7) Note what is below- If it DOESN'T match, set BELOW_MATCH to 0.
        if(y < (board_height -1))
        {
          no_match_ov(offset + board_width, below_match);
        }

        // 8) If above DOES match and ABOVE_MATCH is 0-
        if((y > 0) && (above_match == 0))
        {
          match_position_ov(offset - board_width, x, y - 1, -1, above_match);
        }
        // 9) If below DOES match and BELOW_MATCH is 0-
        if((y < (board_height - 1)) && (below_match == 0))
        {
          match_position_ov(offset + board_width, x, y + 1, -1, below_match);
        }

        // 10) Go one step in direction. (left/right)
        x += dir;
        offset += dir;
        // 11) If position matches, jump to #4.

        if((x >= 0) && (x < board_width))
        {
          if((param_check[offset] != fill_over_param) ||
           (color_check[offset] != fill_over_color))
          {
            break;
          }
        }
        else
        {
          break;
        }
      } while(1);

      //  12) If any elements remain on stack, loop.
    } while(stack_pos >= 0);
  }
  else
  {
    if(id == PLAYER)
      goto out_free_stack;

    id_check = src_board->level_id;
    param_check = src_board->level_param;
    color_check = src_board->level_color;

    // 1) Initialize fill stack and record what we are filling over.
    fill_over_id = (enum thing)id_check[offset];
    fill_over_color = color_check[offset];
    fill_over_param = param_check[offset];

    if((fill_over_id == id) && (fill_over_param == param) &&
     (fill_over_color == color))
      goto out_free_stack;

    stack_pos = 0;
    // 2) Push current position going left.
    stack[0].x = x;
    stack[0].y = y;
    stack[0].dir = -1;

    // 3) Loop:
    do
    {
      // 1) Take top element off of stack.
      x = stack[stack_pos].x;
      y = stack[stack_pos].y;
      dir = stack[stack_pos].dir;
      offset = x + (y * board_width);
      stack_pos--;

      // 2) Start at noted position. Set ABOVE_MATCH and BELOW_MATCH to 0.
      above_match = 0;
      below_match = 0;
      // 3a) If direction is left, push onto stack-
      if(dir == -1)
      {
        // 1) Id above, going left, if matches. Set ABOVE_MATCH to 1.
        if(y > 0)
        {
          match_position(offset - board_width, x, y - 1, -1, above_match);
        }
        // 2) Id below, going left, if matches. Set BELOW_MATCH to 1.
        if(y < (board_height - 1))
        {
          match_position(offset + board_width, x, y + 1, -1, below_match);
        }
        // 3) Id to the right, going right, if matches.
        if(x < (board_width - 1))
        {
          match_no_check(offset + 1, x + 1, y, 1);
        }
      }
      //  3b) If direction is right, push nothing.
      //  4) Place at position.

      do
      {
        if(place_current_at_xy(mzx_world, id, color, param, x, y, copy_robot,
         copy_scroll, copy_sensor, overlay_edit) == -1)
          goto out_free_stack;

        // 6) Note what is above- If it DOESN'T match, set ABOVE_MATCH to 0.
        if(y > 0)
        {
          no_match(offset - board_width, above_match);
        }

        // 7) Note what is below- If it DOESN'T match, set BELOW_MATCH to 0.
        if(y < (board_height -1))
        {
          no_match(offset + board_width, below_match);
        }

        // 8) If above DOES match and ABOVE_MATCH is 0-
        if((y > 0) && (above_match == 0))
        {
          match_position(offset - board_width, x, y - 1, -1, above_match);
        }
        // 9) If below DOES match and BELOW_MATCH is 0-
        if((y < (board_height - 1)) && (below_match == 0))
        {
          match_position(offset + board_width, x, y + 1, -1, below_match);
        }

        // 10) Go one step in direction. (left/right)
        x += dir;
        offset += dir;
        // 11) If position matches, jump to #4.

        if((x >= 0) && (x < board_width))
        {
          if((id_check[offset] != fill_over_id) ||
           (param_check[offset] != fill_over_param) ||
           (color_check[offset] != fill_over_color))
          {
            break;
          }
        }
        else
        {
          break;
        }
      } while(1);

      //  12) If any elements remain on stack, loop.
    } while(stack_pos >= 0);
  }

out_free_stack:
  free(stack);
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Declaration for FILL.CPP */

#ifndef __EDITOR_FILL_H
#define __EDITOR_FILL_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

void fill_area(struct world *mzx_world, enum thing id, int color, int param,
 int x, int y, struct robot *copy_robot, struct scroll *copy_scroll,
 struct sensor *copy_sensor, int overlay_edit);

__M_END_DECLS

#endif // __EDITOR_FILL_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004-2006 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * YUV Renderers:
 *   Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * OpenGL #2 Renderer:
 *   Copyright (C) 2007 Joel Bouchard Lamontagne <logicow@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "graphics.h"

#include "../graphics.h"
#include "../util.h"

#include <string.h>
#include <stdio.h>

static Uint8 ascii_charset[CHAR_SIZE * CHARSET_SIZE];
static Uint8 blank_charset[CHAR_SIZE * CHARSET_SIZE];
static Uint8 smzx_charset[CHAR_SIZE * CHARSET_SIZE];

void save_editor_palette(void)
{
  if(graphics.screen_mode < 2)
    memcpy(graphics.editor_backup_palette, graphics.palette,
     sizeof(struct rgb_color) * SMZX_PAL_SIZE);
}

void load_editor_palette(void)
{
  memcpy(graphics.palette, graphics.editor_backup_palette,
   sizeof(struct rgb_color) * SMZX_PAL_SIZE);
  set_gui_palette();
}

void save_palette(char *fname)
{
  FILE *pal_file = fopen_unsafe(fname, "wb");

  if(pal_file)
  {
    int num_colors = SMZX_PAL_SIZE;
    int i;

    if(!graphics.screen_mode)
      num_colors = PAL_SIZE;

    for(i = 0; i < num_colors; i++)
    {
      fputc(get_red_component(i), pal_file);
      fputc(get_green_component(i), pal_file);
      fputc(get_blue_component(i), pal_file);
    }

    fclose(pal_file);
  }
}

void draw_char_linear(Uint8 color, Uint8 chr, Uint32 offset)
{
  draw_char_linear_ext(color, chr, offset, 256, 16);
}

void clear_screen_no_update(void)
{
  struct char_element *dest = graphics.text_video;
  Uint32 i;

  for(i = 0; i < (SCREEN_W * SCREEN_H); i++)
  {
    // use protected charset and palette
    dest->char_value = 177 + 256;
    dest->fg_color = 1 + 16;
    dest->bg_color = 0 + 16;
    dest++;
  }
}

void ec_save_set_var(char *name, Uint8 offset, Uint32 size)
{
  FILE *fp = fopen_unsafe(name, "wb");

  if(fp)
  {
    if(size + offset > CHARSET_SIZE)
    {
      size = CHARSET_SIZE - offset;
    }

    fwrite(graphics.charset + (offset * CHAR_SIZE), CHAR_SIZE, size, fp);
    fclose(fp);
  }
}

void load_editor_charsets(void)
{
  ec_load_set_secondary(mzx_res_get_by_id(MZX_ASCII_CHR), ascii_charset);
  ec_load_set_secondary(mzx_res_get_by_id(MZX_BLANK_CHR), blank_charset);
  ec_load_set_secondary(mzx_res_get_by_id(MZX_SMZX_CHR),  smzx_charset);
}

void ec_load_smzx(void)
{
  ec_mem_load_set(smzx_charset);
}

void ec_load_blank(void)
{
  ec_mem_load_set(blank_charset);
}

void ec_load_ascii(void)
{
  ec_mem_load_set(ascii_charset);
}

void ec_load_char_ascii(Uint32 char_number)
{
  memcpy(graphics.charset + (char_number * CHAR_SIZE),
   ascii_charset + (char_number * CHAR_SIZE), CHAR_SIZE);

  // some renderers may want to map charsets to textures
  if(graphics.renderer.remap_charsets)
    graphics.renderer.remap_charsets(&graphics);
}

void ec_load_char_mzx(Uint32 char_number)
{
  memcpy(graphics.charset + (char_number * CHAR_SIZE),
   graphics.default_charset + (char_number * CHAR_SIZE), CHAR_SIZE);

  // some renderers may want to map charsets to textures
  if(graphics.renderer.remap_charsets)
    graphics.renderer.remap_charsets(&graphics);
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_GRAPHICS_H
#define __EDITOR_GRAPHICS_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../platform.h"

void save_editor_palette(void);
void load_editor_palette(void);
void save_palette(char *fname);
void draw_char_linear(Uint8 color, Uint8 chr, Uint32 offset);
void clear_screen_no_update(void);
void ec_save_set_var(char *name, Uint8 offset, Uint32 size);
void load_editor_charsets(void);
void ec_load_smzx(void);
void ec_load_blank(void);
void ec_load_ascii(void);
void ec_load_char_ascii(Uint32 char_number);
void ec_load_char_mzx(Uint32 char_number);

__M_END_DECLS

#endif // __EDITOR_GRAPHICS_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

// Extended macro support

#include "configure.h"
#include "macro.h"
#include "../rasm.h"
#include "../util.h"

#include <string.h>
#include <stdlib.h>
#include <ctype.h>

static int cmp_variables(const void *dest, const void *src)
{
  struct macro_variable *m_src = *((struct macro_variable **)src);
  struct macro_variable *m_dest = *((struct macro_variable **)dest);

  return strcasecmp(m_dest->name, m_src->name);
}

void free_macro(struct ext_macro *macro_src)
{
  int i, i2;

  free(macro_src->name);
  free(macro_src->label);

  for(i = 0; i < macro_src->num_lines; i++)
  {
    free(macro_src->lines[i]);
    free(macro_src->variable_references[i]);
  }

  free(macro_src->lines);
  free(macro_src->variable_references);

  free(macro_src->line_element_count);

  for(i = 0; i < macro_src->num_types; i++)
  {
    if(macro_src->types[i].type == string)
    {
      for(i2 = 0; i2 < macro_src->types[i].num_variables; i2++)
      {
        free(macro_src->types[i].variables[i2].storage.str_storage);

        if(macro_src->types[i].variables[i2].def.str_storage)
          free(macro_src->types[i].variables[i2].def.str_storage);
      }
    }

    free(macro_src->types[i].variables_sorted);
    free(macro_src->types[i].variables);
  }

  free(macro_src->text);
  free(macro_src);
}

#ifdef CONFIG_DEBYTECODE
static
#endif
char *skip_to_next(char *src, char t, char a, char b)
{
  char *current = src;
  char current_char = *current;

  while(current_char && (current_char != t) && (current_char != '\n') &&
   (current_char != a) && (current_char != b))
  {
    if(current_char == '\r')
      *current = 0;
    current++;
    current_char = *current;
  }

  return current;
}

#ifdef CONFIG_DEBYTECODE
static
#endif
char *skip_whitespace(char *src)
{
  char *current = src;
  char current_char = *current;

  while(isspace((int)current_char))
  {
    current++;
    current_char = *current;
  }

  return current;
}

#ifdef CONFIG_DEBYTECODE
static
#endif
union variable_storage *find_macro_variable(char *name, struct macro_type *m)
{
  int bottom = 0, top = m->num_variables - 1, middle = 0;
  int cmpval = 0;
  struct macro_variable **base = m->variables_sorted;
  struct macro_variable *current;

  while(bottom <= top)
  {
    middle = (top + bottom) / 2;
    current = base[middle];
    cmpval = strcasecmp(name, current->name);

    if(cmpval > 0)
      bottom = middle + 1;
    else

    if(cmpval < 0)
      top = middle - 1;
    else
      return &(current->storage);
  }

  return NULL;
}

static struct ext_macro *process_macro(char *line_data, char *name, char *label)
{
  char *line_position, *line_position_old;
  struct macro_variable *variables;
  char ***text_lines;
  char **line_text_segments;
  struct macro_variable_reference *line_variable_references;
  union variable_storage *current_storage;
  struct macro_variable_reference **variable_references;
  int *line_variables_count;
  int num_variables;
  int num_types = 0;
  int num_line_variables;
  int num_lines = 0;
  int num_lines_allocated = 32;
  int total_variables = 0;
  struct macro_type *current_type;
  char current_char;
  struct ext_macro *macro_dest;
  int i;
  int def_val;

  line_text_segments = ccalloc(256, sizeof(char *));
  variables = ccalloc(256, sizeof(struct macro_variable));
  line_variable_references = ccalloc(256,
   sizeof(struct macro_variable_reference));

  text_lines = ccalloc(num_lines_allocated, sizeof(char **));
  variable_references =
   ccalloc(num_lines_allocated, sizeof(struct macro_variable_reference *));
  line_variables_count = ccalloc(num_lines_allocated, sizeof(int));

  macro_dest = cmalloc(sizeof(struct ext_macro));
  macro_dest->name = cmalloc(strlen(name) + 1);
  strcpy(macro_dest->name, name);

  macro_dest->label = cmalloc(strlen(label) + 1);
  strcpy(macro_dest->label, label);

  current_type = macro_dest->types;

  macro_dest->text = cmalloc(strlen(line_data) + 1);
  strcpy(macro_dest->text, line_data);
  line_position = macro_dest->text;

  do
  {
    if(*line_position == '(')
    {
      // Line is for variables
      line_position++;

      // Determine type
      if(!strncmp(line_position, "number", 6))
      {
        // Number
        current_type->type = number;
        current_type->type_attributes[1] =
         strtol(line_position + 6, &line_position, 10);

        if(*line_position == '-')
        {
          current_type->type_attributes[0] =
           current_type->type_attributes[1];
          current_type->type_attributes[1] =
           strtol(line_position + 1, &line_position, 10);
        }
        else
        {
          current_type->type_attributes[0] = 0;
        }
      }

      if(!strncmp(line_position, "string", 6))
      {
        // Number
        current_type->type = string;
        current_type->type_attributes[0] =
         strtol(line_position + 6, &line_position, 10);
      }

      if(!strncmp(line_position, "char", 4))
      {
        // Character
        current_type->type = character;
        line_position += 4;
      }

      if(!strncmp(line_position, "color", 5))
      {
        // Character
        current_type->type = color;
        line_position += 5;
      }

      num_variables = 0;

      // Parse names
      while(1)
      {
        line_position = skip_whitespace(line_position);
        line_position_old = line_position;
        line_position = skip_to_next(line_position, ',', ')', '=');
        variables[num_variables].name = line_position_old;

        if(*line_position == '=')
        {
          // Initialize default value
          def_val = 1;
          *line_position = 0;
          line_position++;

          switch(current_type->type)
          {
            case number:
            {
              variables[num_variables].def.int_storage =
               strtol(line_position, &line_position, 10);
              break;
            }

            case string:
            {
              line_position_old = line_position;
              break;
            }

            case character:
            {
              if(*line_position == '\'')
              {
                variables[num_variables].def.int_storage =
                 *(line_position + 1);
                line_position += 2;
              }
              else
              {
                variables[num_variables].def.int_storage =
                 (char)strtol(line_position, &line_position, 10);
              }

              break;
            }

            case color:
            {
              variables[num_variables].def.int_storage =
               get_color(line_position);
              line_position += 3;
              break;
            }

            default:
            {
              break;
            }
          }

          line_position = skip_to_next(line_position, ',', ')', 0);
        }
        else
        {
          switch(current_type->type)
          {
            case number:
            {
              variables[num_variables].def.int_storage =
               current_type->type_attributes[0];
              break;
            }

            case character:
            {
              variables[num_variables].def.int_storage = 0;
              break;
            }

            case color:
            {
              variables[num_variables].def.int_storage = 288;
              break;
            }

            default:
            {
              break;
            }
          }
          def_val = 0;
        }

        current_char = *line_position;
         *line_position = 0;

        if(current_type->type == string)
        {
          variables[num_variables].storage.str_storage =
           cmalloc(current_type->type_attributes[0] + 1);

          if(def_val)
          {
            variables[num_variables].def.str_storage =
             cmalloc(current_type->type_attributes[0] + 1);
            memcpy(variables[num_variables].def.str_storage,
             line_position_old, current_type->type_attributes[0]);
            variables[num_variables].def.
             str_storage[current_type->type_attributes[0]] = 0;
            memcpy(variables[num_variables].storage.str_storage,
             variables[num_variables].def.str_storage,
             current_type->type_attributes[0] + 1);
          }
          else
          {
            variables[num_variables].def.str_storage = NULL;
            variables[num_variables].storage.str_storage[0] = 0;
          }
        }
        else
        {
          memcpy(&(variables[num_variables].storage),
           &(variables[num_variables].def), sizeof(union variable_storage));
        }

        num_variables++;

        if(current_char == ')')
        {
          line_position++;
          break;
        }
        else

        if(current_char)
        {
          line_position++;
        }
        else
        {
          break;
        }
      }

      current_type->num_variables = num_variables;
      current_type->variables =
       ccalloc(num_variables, sizeof(struct macro_variable));
      current_type->variables_sorted =
       ccalloc(num_variables, sizeof(struct macro_variable *));
      memcpy(current_type->variables, variables,
       sizeof(struct macro_variable) * num_variables);

      for(i = 0; i < num_variables; i++)
      {
        current_type->variables_sorted[i] = current_type->variables + i;
      }

      total_variables += num_variables;

      qsort(current_type->variables_sorted,
       num_variables, sizeof(struct macro_variable *), cmp_variables);

      current_type++;
      num_types++;

      line_position = skip_whitespace(line_position);
    }
    else
    {
      current_char = *line_position;

      while((current_char == '\n') || (current_char == '\r'))
      {
        line_position++;
        current_char = *line_position;
      }

      num_line_variables = 0;
      line_text_segments[0] = line_position;

      while(current_char && (current_char != '\n'))
      {
        current_char = *line_position;
        while(current_char && (current_char != '\n'))
        {
          line_position = skip_to_next(line_position, '!', 0, 0);
          current_char = *line_position;

          if(current_char == '!')
          {
            // Embedded variable, close off
            *line_position = 0;
            line_position_old = line_position + 1;

            if(*line_position_old == '#')
            {
              line_position_old++;
              line_variable_references[num_line_variables].ref_mode =
               hexidecimal;
            }
            else
            {
              line_variable_references[num_line_variables].ref_mode =
               decimal;
            }

            // Get name
            line_position = skip_to_next(line_position_old, '!', 0, 0);
            // Close off end
            *line_position = 0;
            line_position++;

            // Find macro element this corresponds to and link.
            for(i = 0; i < num_types; i++)
            {
              current_storage = find_macro_variable(line_position_old,
               macro_dest->types + i);
              if(current_storage)
              {
                line_variable_references[num_line_variables].storage =
                 current_storage;
                line_variable_references[num_line_variables].type =
                 macro_dest->types + i;
                break;
              }
            }

            if(i == num_types)
            {
              line_variable_references[num_line_variables].type =
               NULL;
            }

            line_text_segments[num_line_variables + 1] = line_position;
            num_line_variables++;
          }

          current_char = *line_position;
        }
        *line_position = 0;
        line_position++;
      }

      line_variables_count[num_lines] = num_line_variables;

      variable_references[num_lines] =
       ccalloc(num_line_variables, sizeof(struct macro_variable_reference));
      memcpy(variable_references[num_lines], line_variable_references,
       sizeof(struct macro_variable_reference) * num_line_variables);

      text_lines[num_lines] =
       ccalloc(num_line_variables + 1, sizeof(char *));
      memcpy(text_lines[num_lines], line_text_segments,
       sizeof(char *) * (num_line_variables + 1));

      num_lines++;

      if(num_lines == num_lines_allocated)
      {
        num_lines_allocated *= 2;

        text_lines = crealloc(text_lines,
         sizeof(char **) * num_lines_allocated);
        variable_references = crealloc(variable_references,
         sizeof(struct macro_variable_reference *) * num_lines_allocated);
        line_variables_count = crealloc(line_variables_count,
         sizeof(int) * num_lines_allocated);
      }
    }
  } while(*line_position);

  macro_dest->num_lines = num_lines;
  macro_dest->total_variables = total_variables;

  macro_dest->lines = ccalloc(num_lines, sizeof(char **));
  memcpy(macro_dest->lines, text_lines, sizeof(char **) * num_lines);

  macro_dest->variable_references =
   ccalloc(num_lines, sizeof(struct macro_variable_reference *));
  memcpy(macro_dest->variable_references, variable_references,
   sizeof(struct macro_variable_reference *) * num_lines);

  macro_dest->line_element_count = ccalloc(num_lines, sizeof(int));
  memcpy(macro_dest->line_element_count, line_variables_count,
   sizeof(int) * num_lines);

  macro_dest->num_types = num_types;

  free(line_variable_references);
  free(line_variables_count);
  free(line_text_segments);
  free(variable_references);
  free(variables);
  free(text_lines);

  return macro_dest;
}

struct ext_macro *find_macro(struct editor_config_info *conf, char *name,
 int *next)
{
  int bottom = 0, top = (conf->num_extended_macros) - 1, middle = 0;
  int cmpval = 0;
  struct ext_macro **base = conf->extended_macros;
  struct ext_macro *current;

  while(bottom <= top)
  {
    middle = (top + bottom) / 2;
    current = base[middle];
    cmpval = strcasecmp(name, current->name);

    if(cmpval > 0)
      bottom = middle + 1;
    else

    if(cmpval < 0)
      top = middle - 1;
    else
      return current;
  }

  if(cmpval > 0)
    *next = middle + 1;
  else
    *next = middle;

  return NULL;
}

void add_ext_macro(struct editor_config_info *conf, char *name,
 char *line_data, char *label)
{
  struct ext_macro *macro_dest;
  struct ext_macro **macro_list;
  int next = 0;

  if(!(conf->num_macros_allocated))
  {
    conf->extended_macros = cmalloc(sizeof(struct ext_macro *));
    conf->extended_macros[0] = process_macro(line_data, name, label);
    conf->num_extended_macros = 1;
    conf->num_macros_allocated = 1;
  }
  else
  {
    macro_dest = find_macro(conf, name, &next);

    if(macro_dest)
    {
      free_macro(macro_dest);
      conf->extended_macros[next] =
       process_macro(line_data, name, label);
    }
    else
    {
      if(conf->num_extended_macros == conf->num_macros_allocated)
      {
        conf->num_macros_allocated *= 2;
        conf->extended_macros =
         crealloc(conf->extended_macros,
         sizeof(struct ext_macro *) * conf->num_macros_allocated);
      }

      macro_list = conf->extended_macros;

      if(next != conf->num_extended_macros)
      {
        macro_list += next;
        memmove((char *)(macro_list + 1),
         (char *)macro_list, (conf->num_extended_macros - next) *
         sizeof(struct ext_macro *));
      }

      conf->extended_macros[next] =
       process_macro(line_data, name, label);

      conf->num_extended_macros++;
    }
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_MACRO_H
#define __EDITOR_MACRO_H

#include "../compat.h"

__M_BEGIN_DECLS

enum variable_type
{
  number,
  string,
  character,
  color
};

enum reference_mode
{
  decimal,
  hexidecimal
};

union variable_storage
{
  int int_storage;
  char *str_storage;
};

struct macro_variable
{
  union variable_storage storage;
  union variable_storage def;
  char *name;
};

struct macro_type
{
  int type_attributes[16];
  enum variable_type type;
  int num_variables;
  struct macro_variable *variables;
  struct macro_variable **variables_sorted;
};

struct macro_variable_reference
{
  struct macro_type *type;
  union variable_storage *storage;
  enum reference_mode ref_mode;
};

struct ext_macro
{
  char *name;
  char *label;
  int num_lines;
  char ***lines;
  struct macro_variable_reference **variable_references;
  int *line_element_count;
  int num_types;
  int total_variables;
  struct macro_type types[32];
  char *text;
};

struct editor_config_info;

void free_macro(struct ext_macro *macro_src);
void add_ext_macro(struct editor_config_info *conf, char *name,
 char *line_data, char *label);
struct ext_macro *find_macro(struct editor_config_info *conf, char *name,
 int *next);

#ifndef CONFIG_DEBYTECODE
union variable_storage *find_macro_variable(char *name, struct macro_type *m);
char *skip_whitespace(char *src);
char *skip_to_next(char *src, char t, char a, char b);
#endif

__M_END_DECLS

#endif // __EDITOR_MACRO_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Palette editor */

#include <stdlib.h>
#include <string.h>

#include "../helpsys.h"
#include "../window.h"
#include "../data.h"
#include "../graphics.h"
#include "../event.h"

#include "pal_ed.h"

//---------------------------------------------
//
// ##..##..##..##..##..##..##..##.. Color #00-
// ##..##..##..##..##..#1.1#1.1#1.1  Red 00/63
// #0.1#2.3#4.5#6.7#8.9#0.1#2.3#4.5  Grn 00/63
// ##..##..##..##..##..##..##..##..  Blu 00/63
// ^^
//
// %- Select color   Alt+D- Default palette
// R- Increase Red   Alt+R- Decrease Red
// G- Increase Grn   Alt+G- Decrease Grn
// B- Increase Blu   Alt+B- Decrease Blu
// A- Increase All   Alt+A- Decrease All
// 0- Blacken color     F2- Store color
// Q- Quit editing      F3- Retrieve color
//
//---------------------------------------------

//Mouse- Color=Change color
//       Menu=Do command
//       RGB #'s=Raise/Lower R/G/B

static int saved_r = -1;
static int saved_g = -1;
static int saved_b = -1;

void palette_editor(struct world *mzx_world)
{
  int i, i2, key, chr, color;
  int current_color = 0;
  unsigned char current_r, current_g, current_b;

  cursor_off();
  set_context(93);
  save_screen();

  // Draw window
  draw_window_box(17, 3, 63, 19, 128, 143, 135, 1, 1);
  // Draw palette bars
  for(i = 0; i < 32; i++)
  {
    for(i2 = 0; i2 < 4; i2++)
    {
      chr = ' ';
      if((i & 1) && (i2 == 1) && (i > 19))
        chr = '1';
      if((i & 1) && (i2 == 2))
        chr = '0' + ((i / 2) % 10);
      color = ((i & 30) * 8);
      if(i < 20)
        color += 15;
      draw_char_ext(chr, color, i + 19, i2 + 5, 256, 0);
    }
  }

  // Write rgb info stuff
  write_string("Color #00-\n Red 00/63\n Grn 00/63\n Blu 00/63",
   52, 5, 143, 0);

  // Write menu
  write_string
  (
    "\x1d- Select color   Alt+D- Default palette\n"
    "R- Increase Red   Alt+R- Decrease Red\n"
    "G- Increase Grn   Alt+G- Decrease Grn\n"
    "B- Increase Blu   Alt+B- Decrease Blu\n"
    "A- Increase All   Alt+A- Decrease All\n"
    "0- Blacken color\tF2- Store color\n"
    "Q- Quit editing\t F3- Retrieve color", 19, 11, 143, 1
  );

  do
  {
    // Write rgb and color #
    get_rgb(current_color, &current_r, &current_g, &current_b);
    write_number(current_color, 143, 60, 5, 2, 1, 10);
    write_number(current_r, 143, 58, 6, 2, 1, 10);
    write_number(current_g, 143, 58, 7, 2, 1, 10);
    write_number(current_b, 143, 58, 8, 2, 1, 10);
    // Draw '^^', get key, erase '^^'
    write_string("\x1e\x1e", (current_color * 2) + 19, 9, 143, 0);

    update_screen();
    update_event_status_delay();

    key = get_key(keycode_internal);
    write_string("\x20\x20", (current_color * 2) + 19, 9, 143, 0);
    // Process

    if(get_mouse_press())
    {
      int mouse_x, mouse_y;
      get_mouse_position(&mouse_x, &mouse_y);

      if((mouse_x > 18) && (mouse_x < 51) &&
       (mouse_y > 4) && (mouse_y < 9))
      {
        current_color = (mouse_x - 19) * 2;
        break;
      }
    }

    switch(key)
    {
      case IKEY_LEFT:
      case IKEY_MINUS:
      case IKEY_KP_MINUS:
      {
        if(current_color > 0)
          current_color--;
        break;
      }

      case IKEY_RIGHT:
      case IKEY_EQUALS:
      case IKEY_KP_PLUS:
      {
        if(current_color < 15)
          current_color++;
        break;
      }

      case IKEY_q:
      {
        key = IKEY_ESCAPE;
        break;
      }

      case IKEY_r:
      {
        if(get_alt_status(keycode_internal))
        {
          if(current_r)
          {
            current_r--;
          }
        }
        else
        {
          if(current_r < 63)
          {
            current_r++;
          }
        }

        set_red_component(current_color, current_r);
        update_palette();
        break;
      }

      case IKEY_g:
      {
        if(get_alt_status(keycode_internal))
        {
          if(current_g)
          {
            current_g--;
          }
        }
        else
        {
          if(current_g < 63)
          {
            current_g++;
          }
        }

        set_green_component(current_color, current_g);
        update_palette();
        break;
      }

      case IKEY_b:
      {
        if(get_alt_status(keycode_internal))
        {
          if(current_b)
          {
            current_b--;
          }
        }
        else
        {
          if(current_b < 63)
          {
            current_b++;
          }
        }

        set_blue_component(current_color, current_b);
        update_palette();
        break;
      }

      case IKEY_a:
      {
        if(get_alt_status(keycode_internal))
        {
          if(current_r)
            current_r--;
          if(current_g)
            current_g--;
          if(current_b)
            current_b--;
        }
        else
        {
          if(current_r < 63)
            current_r++;
          if(current_g < 63)
            current_g++;
          if(current_b < 63)
            current_b++;
        }

        set_rgb(current_color, current_r, current_g, current_b);
        update_palette();
        break;
      }

      case IKEY_0:
      {
        current_r = 0;
        current_g = 0;
        current_b = 0;
        set_rgb(current_color, current_r, current_g, current_b);
        update_palette();
        break;
      }

      case IKEY_d:
      {
        if(get_alt_status(keycode_internal))
        {
          default_palette();
        }
        break;
      }

      case IKEY_F2:
      {
        saved_r = current_r;
        saved_g = current_g;
        saved_b = current_b;

        break;
      }

      case IKEY_F3:
      {
        if(saved_r != -1)
        {
          current_r = saved_r;
          current_g = saved_g;
          current_b = saved_b;
        }
        set_rgb(current_color, current_r, current_g, current_b);
        update_palette();
        break;
      }

#ifdef CONFIG_HELPSYS
      case IKEY_F1: // F1
      {
        m_show();
        help_system(mzx_world);
        break;
      }
#endif
    }
  } while(key != IKEY_ESCAPE);
  restore_screen();
  pop_context();
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Declaration for PAL_ED.CPP */

#ifndef __EDITOR_PAL_ED_H
#define __EDITOR_PAL_ED_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

void palette_editor(struct world *mzx_world);

__M_END_DECLS

#endif // __EDITOR_PAL_ED_H