
(********************************************************************)
(*                                                                  *)
(*  castle.sd7    Castle Adventure                                  *)
(*  Copyright (C) 2004, 2005  Thomas Mertes                         *)
(*                                                                  *)
(*  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 "seed7_05.s7i";
  include "osfiles.s7i";
  include "float.s7i";
  include "draw.s7i";
  include "keybd.s7i";
  include "dialog.s7i";
  include "pic32.s7i";
  include "time.s7i";
  include "duration.s7i";


const integer: ROOM_LINES        is  18;
const integer: ROOM_COLUMNS      is  24;

const integer: INVENTORY_LINE    is  16;

const integer: MAX_STRENGTH      is 200;

const integer: MAX_INVENTORY     is   6;
const integer: PIXMAP_SIZE       is  32;
const integer: STEP_SIZE         is  16;
const integer: BORDER_DIST       is  20;
const integer: LEGEND_XPOS       is 2 * BORDER_DIST + ROOM_COLUMNS * PIXMAP_SIZE + 16;
const integer: LEGEND_YPOS_MIN   is   8;
const integer: LEGEND_COLUMN     is (LEGEND_XPOS + PIXMAP_SIZE) div 6 + 3;
const integer: BUTTONS_1_XPOS    is LEGEND_XPOS - 3 * 96;
const integer: BUTTONS_2_XPOS    is LEGEND_XPOS - 2 * 96;
const integer: BUTTONS_3_XPOS    is LEGEND_XPOS - 96;
const integer: BUTTONS_1_COLUMN  is (BUTTONS_1_XPOS + PIXMAP_SIZE) div 6 + 3;
const integer: BUTTONS_2_COLUMN  is (BUTTONS_2_XPOS + PIXMAP_SIZE) div 6 + 3;
const integer: BUTTONS_3_COLUMN  is (BUTTONS_3_XPOS + PIXMAP_SIZE) div 6 + 3;
const integer: BUTTON_YPOS_MIN   is 2 * BORDER_DIST + ROOM_LINES * PIXMAP_SIZE + 16;
const integer: BUTTON_TEXT_LINE1 is BUTTON_YPOS_MIN div 16 + 2;

const type: directionType is new enum
    NORTH, SOUTH, EAST, WEST, UP, DOWN
  end enum;

const func string: str (in directionType: direction) is
  return str(ord(direction));

const func directionType: (attr directionType) parse (in string: stri) is
  return directionType conv (integer(stri));

enable_io(directionType);


const type: itemType is new enum
    NO_ITEM, BOOK, CRYSTAL_BALL, GLASSES, HELMET,
    KEY, LAMP, MAGIC_WAND, SWORD, WINE_FLASK,
    CROWN, DIAMOND, FANCY_GOBLET, GOLDBAR, HARP,
    HOLY_CROSS, HOURGLASS, JADE_FIGURINE, LARGE_GEM,
    NECKLACE, RUBYS, SCEPTER, SILVER_BARS
  end enum;

const itemType: MIN_ITEM is succ(itemType.first);
const itemType: MAX_ITEM is itemType.last;


const type: monsterType is new enum
    NO_MONSTER, ANGRY_DEMON1, ANGRY_DEMON2,
    UGLY_OGRE1, UGLY_OGRE2, BIG_SPIDER,
    SMALL_SPIDER, SNAKE, BAT, VAMPIRE,
    FAIRY1, FAIRY2, DOOR1, DOOR2, DOOR3, DOOR4
  end enum;

const monsterType: MIN_MONSTER is succ(monsterType.first);
const monsterType: MAX_MONSTER is monsterType.last;


(* Rooms *)
const integer: NO_ROOM            is  0;
const integer: COURTYARD          is  1;
const integer: WEST_BALLROOM      is  4;
const integer: WEST_DINING_ROOM   is  8;
const integer: KITCHEN            is  9;
const integer: CHEFS_QUARTERS     is 10;
const integer: STORAGE_ROOM       is 11;
const integer: MUSEUM             is 12;
const integer: THRONE_ROOM        is 14;
const integer: GARDEN_NORTH       is 17;
const integer: GARDEN_SOUTH       is 18;
const integer: UPPER_HALL_CENTER  is 19;
const integer: UPPER_HALL_WEST    is 20;
const integer: UPPER_HALL_EAST    is 21;
const integer: GUARDS_HALL        is 22;
const integer: KNIGHTS_HALL       is 24;
const integer: LOWER_BATTLEMENT   is 26;
const integer: CORRIDOR28         is 28;
const integer: GUEST_ROOM         is 29;
const integer: BALCONY            is 33;
const integer: RED_ROOM           is 37;
const integer: YELLOW_ROOM        is 40;
const integer: CORRIDOR52         is 52;
const integer: CORRIDOR54         is 54;
const integer: KINGS_STUDY        is 56;
const integer: EMPTY_ROOM         is 57;
const integer: LIBRARY_WEST_END   is 58;
const integer: LIBRARY_EAST_END   is 59;
const integer: CASTLE_WALL        is 60;
const integer: WEST_TOWER_MIDDLE  is 62;
const integer: EAST_TOWER_TOP     is 65;
const integer: LABORATORY         is 66;
const integer: SORCERERS_QUARTERS is 67;
const integer: WINE_CELLAR        is 68;
const integer: MAZE70             is 70;
const integer: MAZE73             is 73;
const integer: MAZE74             is 74;
const integer: WINDING_PASSAGE1   is 76;
const integer: WINDING_PASSAGE2   is 77;
const integer: WINDING_PASSAGE3   is 78;
const integer: TORTURE_ROOM       is 79;
const integer: DUNGEON_ENTRANCE   is 82;
const integer: SECRET_ROOM        is 83;
const integer: ESCAPED            is 84;
const integer: CASTLE             is 85;
const integer: MAX_ROOM           is 85;


const type: itemSet is set of itemType;

const func string: str (in itemSet: aSet) is
  return str(bitset conv aSet);

const func itemSet: (attr itemSet) parse (in string: stri) is
  return itemSet conv (bitset(stri));

enable_io(itemSet);


const type: graphObj is new struct
    var string: name is "";
    var string: adjective is "";
    var array string: picture is 0 times "";
    var PRIMITIVE_WINDOW: pixmap is PRIMITIVE_WINDOW.value;
    var integer: xPos is 0;
    var integer: yPos is 0;
    var PRIMITIVE_WINDOW: saved_pixmap is PRIMITIVE_WINDOW.value;
    var integer: saved_xPos is 0;
    var integer: saved_yPos is 0;
  end struct;

const type: itemObj is sub graphObj struct
    var itemType: ident is NO_ITEM;
    var integer: room_num is 0;
  end struct;

const type: monsterObj is sub graphObj struct
    var monsterType: ident is NO_MONSTER;
    var integer: attack is 0;
    var integer: strength is 0;
    var integer: room_num is 0;
    var directionType: direction is NORTH;
    var boolean: living is FALSE;
  end struct;

const type: playerObj is sub graphObj struct
    var integer: room is 0;
    var directionType: direction is NORTH;
    var itemSet: inventory is itemSet.EMPTY_SET;
    var integer: strength is 0;
    var boolean: living is TRUE;
  end struct;

const type: roomType is new struct
    var array string: data is ROOM_LINES times "";
    var array string: description is 5 times "";
    var array [directionType] integer: exits is directionType times 0;
  end struct;

const type: scoreType is new struct
    var string: name is "";
    var integer: points is 0;
  end struct;


var text: screen is STD_NULL;
var array [itemType] itemObj: item is itemType times itemObj.value;
var array [monsterType] monsterObj: monster is monsterType times monsterObj.value;
var playerObj: player is playerObj.value;
var array roomType: room is MAX_ROOM times roomType.value;
var scoreType: highScore is scoreType.value;
var graphObj: stair_up is graphObj.value;
var graphObj: stair_down is graphObj.value;

var array string: message is 0 times "";
var boolean: flaskFilled is FALSE;
var boolean: exitGame is FALSE;

var PRIMITIVE_WINDOW: query_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: wall_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: colored_wall1_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: colored_wall2_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: vertical_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: horizontal_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: left_upper_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: right_upper_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: left_lower_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: right_lower_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: railing_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: gate_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: small_bush_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: big_bush_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: statue_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: fountain_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: wood_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: stone_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: gray_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: water_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_left_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_right_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_top_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_bottom_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_white_middle_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_red_middle_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_blue_middle_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_purple_middle_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: bed_yellow_middle_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: chain_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: k_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: b_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: floor_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: eye_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: hand_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: take_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: drop_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: load_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: save_pixmap is PRIMITIVE_WINDOW.value;
var PRIMITIVE_WINDOW: exit_pixmap is PRIMITIVE_WINDOW.value;


const array string: query_pic is [](
  "  WWWW  ",
  " W    W ",
  " W    W ",
  "    WW  ",
  "   WW   ",
  "   WW   ",
  "        ",
  "   WW   ");


const array string: wall_pic is [](
  "WBBBBBBBWBBBBBBB",
  "WBBBBBBBWBBBBBBB",
  "WBBBBBBBWBBBBBBB",
  "WWWWWWWWWWWWWWWW",
  "RRRRWRRRRRRRWRRR",
  "RRRRWRRRRRRRWRRR",
  "RRRRWRRRRRRRWRRR",
  "WWWWWWWWWWWWWWWW",
  "WBBBBBBBWBBBBBBB",
  "WBBBBBBBWBBBBBBB",
  "WBBBBBBBWBBBBBBB",
  "WWWWWWWWWWWWWWWW",
  "RRRRWRRRRRRRWRRR",
  "RRRRWRRRRRRRWRRR",
  "RRRRWRRRRRRRWRRR",
  "WWWWWWWWWWWWWWWW");


const array string: colored_wall1_pic is [](
  "WGGGGGGGWMMMMMMM",
  "WGGGGGGGWMMMMMMM",
  "WGGGGGGGWMMMMMMM",
  "WWWWWWWWWWWWWWWW",
  "RRRRWmmmmmmmWBBB",
  "RRRRWmmmmmmmWBBB",
  "RRRRWmmmmmmmWBBB",
  "WWWWWWWWWWWWWWWW",
  "WgggggggWOOOOOOO",
  "WgggggggWOOOOOOO",
  "WgggggggWOOOOOOO",
  "WWWWWWWWWWWWWWWW",
  "YYYYWbbbbbbbWBBB",
  "YYYYWbbbbbbbWBBB",
  "YYYYWbbbbbbbWBBB",
  "WWWWWWWWWWWWWWWW");


const array string: colored_wall2_pic is [](
  "WgggggggWOOOOOOO",
  "WgggggggWOOOOOOO",
  "WgggggggWOOOOOOO",
  "WWWWWWWWWWWWWWWW",
  "BBBBWbbbbbbbWRRR",
  "BBBBWbbbbbbbWRRR",
  "BBBBWbbbbbbbWRRR",
  "WWWWWWWWWWWWWWWW",
  "WGGGGGGGWMMMMMMM",
  "WGGGGGGGWMMMMMMM",
  "WGGGGGGGWMMMMMMM",
  "WWWWWWWWWWWWWWWW",
  "BBBBWrrrrrrrWYYY",
  "BBBBWrrrrrrrWYYY",
  "BBBBWrrrrrrrWYYY",
  "WWWWWWWWWWWWWWWW");


const array string: vertical_pic is [](
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ");


const array string: horizontal_pic is [](
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ");


const array string: left_upper_pic is [](
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "       WWWWWWWWW",
  "       WWWWWWWWW",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ");


const array string: right_upper_pic is [](
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "WWWWWWWWW       ",
  "WWWWWWWWW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ");


const array string: left_lower_pic is [](
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WWWWWWWWW",
  "       WWWWWWWWW",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ");


const array string: right_lower_pic is [](
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "WWWWWWWWW       ",
  "WWWWWWWWW       ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ");


const array string: railing_pic is [](
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WWWWWWWWW",
  "       WWWWWWWWW",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ",
  "       WW       ");


const array string: gate_pic is [](
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  " bbbbbbbbbbbbbb ",
  "bbbbbrrrrrbbbrrr",
  "brrrrbbbbbrrrbbb",
  "bbbbbbrrrbbbbbbb",
  "rrrrrrbbbrrrrrbb",
  " bbbbbbbbbbbbbb ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ");


const array string: small_bush_pic is [](
  "     G GG       ",
  "   G  GG  GG  G ",
  " G  G  G G   G  ",
  "  GGGG GG G GGG ",
  "      GG  GGG   ",
  " GGG   G G   GG ",
  "   GGG GG GG GGG",
  " G  G GG  G G   ",
  " G  GG G  GG G G",
  "GG GG Gg  g  GG ",
  " G  g  g g   G  ",
  "GGG  g ggGG GGGG",
  "   g  gg   g    ",
  "    g  g  gGG   ",
  "     g g g      ",
  "      ggg       ");


const array string: big_bush_pic is [](
  "   G G GG  GG   ",
  "  GG  GG  gG GG ",
  " G  g  gGg G gG ",
  "  GGgg gg g ggGG",
  "GG    gg  ggg   ",
  " GgGG Gg gG  GgG",
  "   ggG gg gg ggG",
  "GG  g gg  g g   ",
  "GgG gg gG gg G G",
  "Gg gg gb  bG gG ",
  " g  b  b b   g  ",
  "GggG b bbgG ggGG",
  "   b  bb   b    ",
  "  GGb  b  bgGG  ",
  "     b b b      ",
  "      bbb       ");


const array string: wood_pic is [](
  "bbbbbbbbbbbbbbrr",
  "rrbbbbbrrrrbbbbb",
  "bbrrrrrbbbbrrrbb",
  "bbbbbbbbbbbbbbbb",
  "rrrbbbbbbbbbbbbb",
  "bbbrrbbbbrrrrrrb",
  "bbbbbrrrrrbbbbbb",
  "bbbbbbbbbbbbrrrr",
  "rrrrbbbbbrrrbbbb",
  "bbbbrrrrrbbbbbbb",
  "bbbbbbbbbbbbbrrr",
  "rrbbbbbbrrrrrbbb",
  "bbbbbbbbbbbbbbbb",
  "bbbrrrrbbbbbrrrb",
  "rrrbbbbrrrrrbbbb",
  "bbbbbbbbbbbbbbbb");


const array string: stone_pic is [](
  "xxxxxxxxxWxxxxxx",
  "xxxxxxxxxWxxxxxx",
  "xxxxxxxxxWxxxxxx",
  "xxxxxxxxxWxxxxxx",
  "WWWWWWWWWWWWWWWW",
  "xxxWxxxxxxxxxxxx",
  "xxxWxxxxxxxxxxxx",
  "xxxWxxxxxxxxxxxx",
  "xxxWxxxxxxxxxxxx",
  "xxxWxxxxxxxxxxxx",
  "WWWWWWWWWWWWWWWW",
  "xxxxxxxxxxxxWxxx",
  "xxxxxxxxxxxxWxxx",
  "xxxxxxxxxxxxWxxx",
  "xxxxxxxxxxxxWxxx",
  "WWWWWWWWWWWWWWWW");


const array string: gray_pic is [](
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx",
  "xxxxxxxxxxxxxxxx");


const array string: water_pic is [](
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB");


const array string: bed_left_pic is [](
  "            bbbb",
  "            bbbb",
  "            bbbb",
  "            bbbb",
  "             bb ",
  "             bb ",
  "             bb ",
  "             bb ",
  "             bb ",
  "             bb ",
  "             bb ",
  "             bb ",
  "            bbbb",
  "            bbbb",
  "            bbbb",
  "            bbbb");


const array string: bed_right_pic is [](
  "bbbb            ",
  "bbbb            ",
  "bbbb            ",
  "bbbb            ",
  " bb             ",
  " bb             ",
  " bb             ",
  " bb             ",
  " bb             ",
  " bb             ",
  " bb             ",
  " bb             ",
  "bbbb            ",
  "bbbb            ",
  "bbbb            ",
  "bbbb            ");


const array string: bed_top_pic is [](
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "bbbb        bbbb",
  "bbbbbbbbbbbbbbbb",
  "bbbbbbbbbbbbbbbb",
  "bbbb        bbbb");


const array string: bed_bottom_pic is [](
  "bbbb        bbbb",
  "bbbbbbbbbbbbbbbb",
  "bbbbbbbbbbbbbbbb",
  "bbbb        bbbb",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ",
  "                ");


const array string: bed_white_middle_pic is [](
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW",
  "WWWWWWWWWWWWWWWW");


const array string: bed_red_middle_pic is [](
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR",
  "RRRRRRRRRRRRRRRR");


const array string: bed_blue_middle_pic is [](
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB",
  "BBBBBBBBBBBBBBBB");


const array string: bed_purple_middle_pic is [](
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm",
  "mmmmmmmmmmmmmmmm");


const array string: bed_yellow_middle_pic is [](
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY",
  "YYYYYYYYYYYYYYYY");


const array string: chain_pic is [](
  "        BBB   BB",
  "       B   B B  ",
  "       B  BBBBB ",
  "       BB  B B  ",
  "       BBBB   BB",
  "      BB        ",
  "    BBB         ",
  "   B B B        ",
  "   B   B        ",
  "   B B B        ",
  "    BBB         ",
  "      BBBBB   BB",
  "       BB  B B  ",
  "       B  BBBBB ",
  "       B   B B  ",
  "        BBB   BB");


const array string: k_pic is [](
  " WW     ",
  "  W     ",
  "  W     ",
  "  W  WW ",
  "  W WW  ",
  "  WWW   ",
  "  W WW  ",
  " WW  WW ");


const array string: b_pic is [](
  " WW     ",
  "  W     ",
  "  W     ",
  "  WWWW  ",
  "  W   W ",
  "  W   W ",
  "  W   W ",
  " W WWW  ");


const array string: up_pic is [](
  "                  WddW          ",
  "                  WdddW         ",
  "                  WddddW        ",
  "            WWWWWWWdddddW       ",
  "            WW     WdddddW      ",
  "            WdW     WdddddW     ",
  "            WddW     WdddddW    ",
  "            WdddW     WdddddWWWW",
  "            WddddW     WddddW   ",
  "      WWWWWWWdddddW     WdddW   ",
  "      WW     WdddddW     WddW   ",
  "      WdW     WdddddW     WdW   ",
  "      WddW     WdddddW     WW   ",
  "      WdddW     WdddddWWWWWWW   ",
  "      WddddW     WddddW         ",
  "WWWWWWWdddddW     WdddW        W",
  "WW     WdddddW     WddW       W ",
  "WdW     WdddddW     WdW      W  ",
  "WddW     WdddddW     WW     W   ",
  "WdddW     WdddddWWWWWWW    W    ",
  "WddddW     WddddW         W     ",
  "WdddddW     WdddW        W      ",
  " WdddddW     WddW       W       ",
  "  WdddddW     WdW      W        ",
  "   WdddddW     WW     W         ",
  "    WdddddWWWWWWW    W          ",
  "     WddddW         W           ",
  "      WdddW        W            ",
  "       WddW       W             ",
  "        WdW      W              ",
  "         WW     W               ",
  "          WWWWWW                ");


const array string: down_pic is [](
    "           WWWWWWWWWWWWWWWWWWWWW",
    "          WW                    ",
    "         WdW                    ",
    "        WddW                    ",
    "       WdddW           W        ",
    "      WddddW            W       ",
    "     WdddddWWWWWWW       W      ",
    "    WdddddW     WW        W   W ",
    "   WdddddW     WdW         W  W ",
    "  WdddddW     WddW          W W ",
    " WdddddW     WdddW           WW ",
    "WdddddW     WddddW        WWWWW ",
    "WddddW     WdddddWWWWWWW        ",
    "WdddW     WdddddW     WW        ",
    "WddW     WdddddW     WdW        ",
    "WdW     WdddddW     WddW        ",
    "WW     WdddddW     WdddW        ",
    "WWWWWWWdddddW     WddddW        ",
    "      WddddW     WdddddWWWWWWW  ",
    "      WdddW     WdddddW     WW  ",
    "      WddW     WdddddW     WdW  ",
    "      WdW     WdddddW     WddW  ",
    "      WW     WdddddW     WdddW  ",
    "      WWWWWWWdddddW     WddddW  ",
    "            WddddW     WdddddWWW",
    "            WdddW     WdddddW   ",
    "            WddW     WdddddW    ",
    "            WdW     WdddddW     ",
    "            WW     WdddddW      ",
    "            WWWWWWWdddddW       ",
    "                  WddddW        ",
    "                  WdddW         ");


const array string: big_spider_pic is [](
  " B            B ",
  "  B          B  ",
  "   B        B   ",
  "    B      B    ",
  "     B    B     ",
  "BBB   B  B   BBB",
  "   BBBWWWWBBB   ",
  "     WWWWWW     ",
  "     WWWWWW     ",
  "   BBBWWWWBBB   ",
  "BBB   B  B   BBB",
  "     B    B     ",
  "    B      B    ",
  "   B        B   ",
  "  B          B  ",
  " B            B ");


const array string: small_spider_pic is [](
  "                ",
  "  B          B  ",
  "   B        B   ",
  "    B      B    ",
  "     B    B     ",
  " BBB  B  B  BBB ",
  "    BBBWWBBB    ",
  "      WWWW      ",
  "    BBBWWBBB    ",
  " BBB  B  B  BBB ",
  "     B    B     ",
  "    B      B    ",
  "   B        B   ",
  "  B          B  ",
  "                ",
  "                ");


const array string: fairy_pic is [](
  "  xxxx        YYYYY        xxxx ",
  " xxccxx      YYYYYYY      xxccxx",
  " xccccxx    YYWWWWWYY    xxccccx",
  " xcccccxx   YYWBWBWYY   xxcccccx",
  " xxcccccxx  YYWWWWWYY  xxcccccxx",
  "  xccccccxx YYWOWOWYY xxccccccx ",
  " xxccBcBccxx XWWOWWX xxcccccccxx",
  " xcccccccccxxXXWWWXXxxcccccccccx",
  " xccBcWcBcccWWWWWWWWWccccccccccx",
  " xxcccRccccWWRRWWWRRWWccccccccxx",
  "  xccBRBccWWRRRRWRRRRWWcccccccx ",
  " xxcccRccWWWRRRRWRRRRWWWccccccxx",
  " xccccRcWWWcxRRWWWRRxcWWWccccccx",
  " xccccRWWWcxxXWWWWWXxxcWWWWccccx",
  " xxccWWWWcxx XWWYWWX xxcWWWWccxx",
  "  xccWWWcxx XWWWWWWWX xxcWWWccx ",
  " xxcccccxx XRRRRRRRRRX xxcccccxx",
  " xcccccxx  XWWRRRRRWWX  xxcccccx",
  " xccccxx   XWWWRRRWWWX   xxccccx",
  " xxccxx    XWWWWRWWWWX    xxccxx",
  "  xxxx      XWWWXWWWX      xxxx ",
  "            XWWWXWWWX           ",
  "            XWWWXWWWX           ",
  "            XWWWXWWWX           ",
  "             XWWXWWX            ",
  "             XWWXWWX            ",
  "             XWWXWWX            ",
  "             XWWXWWX            ",
  "             XWWXWWX            ",
  "             XWWXWWX            ",
  "             XWWXWWX            ",
  "            XWWWXWWWX           ");


const array string: large_gem_pic is [](
  "                ",
  "  RRRR    RRRR  ",
  " RrrrrR  RrrrrR ",
  "RrrrrrrRRrrrrrrR",
  "RrrrrrrrrrrrrrrR",
  "RrrrrrrrrrrrrrrR",
  "RrrrrrrrrrrrrrrR",
  "RrrrrrrrrrrrrrrR",
  " RrrrrrrrrrrrrR ",
  "  RrrrrrrrrrrR  ",
  "   RrrrrrrrrR   ",
  "    RrrrrrrR    ",
  "     RrrrrR     ",
  "      RrrR      ",
  "       RR       ",
  "                ");


const array string: floor_pic is [](
  "        ",
  "        ",
  "        ",
  "        ",
  "        ",
  "        ",
  "        ",
  "        ");


const array string: player_pic is [](
  "      bbbb      ",
  "     bbbbbb     ",
  "     bBOOBb     ",
  "     OOOOOO     ",
  "     OWWWWO     ",
  "      OOOO      ",
  "   xbbbbbbbbx   ",
  "  xxbbbbbbbbxx  ",
  " xx bbbbbbbb xx ",
  "Ox  bbbbbbbb  xO",
  "OO  bbbbbbbb  OO",
  "     YY  YY     ",
  "     YY  YY     ",
  "     YY  YY     ",
  "     YY  YY     ",
  "   rrrr  rrrr   ");


const func PRIMITIVE_WINDOW: createPixmap (in array string: pattern) is
  return createPixmap(pattern, PIXMAP_SIZE div length(pattern), black);


const proc: initRoomData is func
  local
    var file: dat_file is STD_NULL;
    var integer: number is 0;
    var integer: line is 0;
    var directionType: direction is NORTH;
  begin
    dat_file := open(dir(PROGRAM) & "/castle.dat", "r");
    for number range 1 to MAX_ROOM do
      for line range 1 to ROOM_LINES do
        room[number].data[line] := getln(dat_file);
      end for;
      for line range 1 to 5 do
        room[number].description[line] := getln(dat_file);
      end for;
      for direction range NORTH to DOWN do
        read(dat_file, room[number].exits[direction]);
      end for;
    end for;
    close(dat_file);
  end func;


const proc: initMonster (
    in monsterType: monster_ident,
    in string: monster_adjective,
    in string: monster_name,
    in array string: monster_pic,
    in integer: monster_line,
    in integer: monster_column,
    in integer: monster_attack,
    in integer: monster_strength,
    in integer: monster_room_num) is func
  begin
    monster[monster_ident].ident     := monster_ident;
    monster[monster_ident].adjective := monster_adjective;
    monster[monster_ident].name      := monster_name;
    monster[monster_ident].picture   := monster_pic;
    monster[monster_ident].pixmap    := PRIMITIVE_WINDOW.value;
    monster[monster_ident].yPos      := pred(monster_line) * PIXMAP_SIZE;
    monster[monster_ident].xPos      := pred(monster_column) * PIXMAP_SIZE;
    monster[monster_ident].attack    := monster_attack;
    monster[monster_ident].strength  := monster_strength;
    monster[monster_ident].room_num  := monster_room_num;
    monster[monster_ident].direction := NORTH;
    monster[monster_ident].living    := TRUE;
  end func;


const proc: initMonsterData is func
  begin
    initMonster(ANGRY_DEMON1, "Angry", "Demon",   demon_pic,         7, 13, 5, 160, THRONE_ROOM);
    initMonster(ANGRY_DEMON2, "Angry", "Demon",   demon_pic,        10,  6, 5, 160, KNIGHTS_HALL);
    initMonster(UGLY_OGRE1,   "Ugly",  "Ogre",    ogre_pic,          9, 13, 4,  80, WEST_BALLROOM);
    initMonster(UGLY_OGRE2,   "Ugly",  "Ogre",    ogre_pic,         12, 12, 4,  80, UPPER_HALL_EAST);
    initMonster(BIG_SPIDER,   "Big",   "Spider",  big_spider_pic,   14, 18, 3,  80, MAZE73);
    initMonster(SMALL_SPIDER, "Small", "Spider",  small_spider_pic,  5, 17, 1,  60, MAZE70);
    initMonster(SNAKE,        "",      "Snake",   snake_pic,        14, 17, 3,  60, GARDEN_NORTH);
    initMonster(BAT,          "",      "Bat",     bat_pic,           7, 18, 2,  40, WINE_CELLAR);
    initMonster(VAMPIRE,      "",      "Vampire", vampire_pic,      10, 24, 0,   0, CORRIDOR28);
    initMonster(FAIRY1,       "",      "Fairy",   fairy_pic,        10,  1, 0,   0, CORRIDOR52);
    initMonster(FAIRY2,       "",      "Fairy",   fairy_pic,         1, 12, 0,   0, CORRIDOR54);
    initMonster(DOOR1,        "",      "Door",    grating_pic,      15,  9, 0,   0, WINE_CELLAR);
    initMonster(DOOR2,        "",      "",        grating_pic,      15, 10, 0,   0, WINE_CELLAR);
    initMonster(DOOR3,        "",      "Door",    grating_pic,       9, 14, 0,   0, DUNGEON_ENTRANCE);
    initMonster(DOOR4,        "",      "",        grating_pic,      10, 14, 0,   0, DUNGEON_ENTRANCE);
  end func;


const func itemType: (attr itemType) conv (in char: ch) is func
  result
    var itemType: item_ident is NO_ITEM;
  local
    var integer: number is 0;
  begin
    number := succ(ord(ch) - ord('a'));
    if number >= ord(MIN_ITEM) and number <= ord(MAX_ITEM) then
      item_ident := itemType conv number;
    end if;
  end func;


const func char: (attr char) conv (in itemType: anItem) is
  return chr(pred(ord('a') + ord(anItem)));


const proc: initItem (
    in itemType: item_ident,
    in string: item_name,
    in array string: item_pic,
    in integer: item_line,
    in integer: item_column,
    in integer: item_room_num) is func
  begin
    item[item_ident].ident    := item_ident;
    item[item_ident].name     := item_name;
    item[item_ident].picture  := item_pic;
    item[item_ident].pixmap   := PRIMITIVE_WINDOW.value;
    item[item_ident].yPos     := pred(item_line) * PIXMAP_SIZE;
    item[item_ident].xPos     := pred(item_column) * PIXMAP_SIZE;
    item[item_ident].room_num := item_room_num;
    if item_room_num <> NO_ROOM then
      room[item_room_num].data[item_line] @:= [item_column] char conv item_ident;
    end if;
  end func;


const proc: initItemData is func
  begin
    initItem(BOOK,          "Book",          book_pic,          11, 11, NO_ROOM); # LIBRARY_WEST_END
    initItem(CRYSTAL_BALL,  "Crystal Ball",  crystal_ball_pic,  13, 10, LABORATORY);
    initItem(GLASSES,       "Eye Glasses",   glasses_pic,       12, 18, NO_ROOM); # YELLOW_ROOM
    initItem(HELMET,        "Helmet",        helmet_pic,         5, 12, GUARDS_HALL);
    initItem(KEY,           "Key",           key_pic,            7, 12, NO_ROOM); # KINGS_STUDY
    initItem(LAMP,          "Lamp",          lamp_pic,          14, 19, GARDEN_NORTH);
    initItem(MAGIC_WAND,    "Magic Wand",    magic_wand_pic,     9,  4, KNIGHTS_HALL);
    initItem(SWORD,         "Sword",         sword_pic,          5, 11, MUSEUM);
    initItem(WINE_FLASK,    "Wine Flask",    flask_pic,         11,  8, NO_ROOM); # CHEFS_QUARTERS
    initItem(CROWN,         "Crown",         crown_pic,          5,  9, THRONE_ROOM);
    initItem(DIAMOND,       "Diamond",       diamond_pic,       10, 13, EMPTY_ROOM);
    initItem(FANCY_GOBLET,  "Fancy Goblet",  goblet_pic,        14,  6, WEST_DINING_ROOM);
    initItem(GOLDBAR,       "Goldbar",       goldbar_pic,       14, 23, MAZE74);
    initItem(HARP,          "Harp",          harp_pic,           8, 16, EAST_TOWER_TOP);
    initItem(HOLY_CROSS,    "Holy Cross",    holy_cross_pic,    13, 15, RED_ROOM);
    initItem(HOURGLASS,     "Hourglass",     hourglass_pic,     14,  6, UPPER_HALL_WEST);
    initItem(JADE_FIGURINE, "Jade Figurine", jade_figurine_pic, 10, 13, GUEST_ROOM);
    initItem(LARGE_GEM,     "Large Gem",     large_gem_pic,     10, 12, NO_ROOM); # GARDEN_SOUTH
    initItem(NECKLACE,      "Necklace",      necklace_pic,       9, 12, NO_ROOM); # UPPER_HALL_CENTER
    initItem(RUBYS,         "Ruby",          ruby_pic,          16,  4, CASTLE_WALL);
    initItem(SCEPTER,       "Scepter",       scepter_pic,       10, 12, SECRET_ROOM);
    initItem(SILVER_BARS,   "Silver Bars",   silver_bars_pic,    8,  7, LOWER_BATTLEMENT);
  end func;


const proc: initPlayerData is func
  begin
    player.pixmap := createPixmap(player_pic);
    player.xPos := 13 * PIXMAP_SIZE;
    player.yPos := 14 * PIXMAP_SIZE;
    player.saved_pixmap := PRIMITIVE_WINDOW.value;
    player.saved_xPos := 0;
    player.saved_yPos := 0;
    player.room := COURTYARD;
    player.direction := SOUTH;
    player.inventory := itemSet.EMPTY_SET;
    player.strength := MAX_STRENGTH;
    player.living := TRUE;
  end func;


const proc: loadHighScore is func
  local
    var file: aFile is STD_NULL;
  begin
    aFile := open("castle.scr", "r");
    if aFile <> STD_NULL then
      read(aFile, highScore.points);
      read(aFile, highScore.name);
      close(aFile);
    else
      highScore.name := "";
      highScore.points := 0;
    end if;
  end func;


const proc: saveHighScore is func
  local
    var file: aFile is STD_NULL;
  begin
    aFile := open("castle.scr", "w");
    if aFile <> STD_NULL then
      write(aFile, highScore.points);
      write(aFile, " ");
      writeln(aFile, highScore.name);
      close(aFile);
    end if;
  end func;


const proc: initGame is func
  begin
    query_pixmap := createPixmap(query_pic);
    wall_pixmap := createPixmap(wall_pic);
    colored_wall1_pixmap := createPixmap(colored_wall1_pic);
    colored_wall2_pixmap := createPixmap(colored_wall2_pic);
    vertical_pixmap := createPixmap(vertical_pic);
    horizontal_pixmap := createPixmap(horizontal_pic);
    left_upper_pixmap := createPixmap(left_upper_pic);
    right_upper_pixmap := createPixmap(right_upper_pic);
    left_lower_pixmap := createPixmap(left_lower_pic);
    right_lower_pixmap := createPixmap(right_lower_pic);
    railing_pixmap := createPixmap(railing_pic);
    gate_pixmap := createPixmap(gate_pic);
    small_bush_pixmap := createPixmap(small_bush_pic);
    big_bush_pixmap := createPixmap(big_bush_pic);
    statue_pixmap := createPixmap(statue_pic);
    fountain_pixmap := createPixmap(fountain_pic);
    wood_pixmap := createPixmap(wood_pic);
    stone_pixmap := createPixmap(stone_pic);
    gray_pixmap := createPixmap(gray_pic);
    water_pixmap := createPixmap(water_pic);
    bed_left_pixmap := createPixmap(bed_left_pic);
    bed_right_pixmap := createPixmap(bed_right_pic);
    bed_top_pixmap := createPixmap(bed_top_pic);
    bed_bottom_pixmap := createPixmap(bed_bottom_pic);
    bed_white_middle_pixmap := createPixmap(bed_white_middle_pic);
    bed_red_middle_pixmap := createPixmap(bed_red_middle_pic);
    bed_blue_middle_pixmap := createPixmap(bed_blue_middle_pic);
    bed_purple_middle_pixmap := createPixmap(bed_purple_middle_pic);
    bed_yellow_middle_pixmap := createPixmap(bed_yellow_middle_pic);
    chain_pixmap := createPixmap(chain_pic);
    k_pixmap := createPixmap(k_pic);
    b_pixmap := createPixmap(b_pic);
    floor_pixmap := createPixmap(floor_pic);
    eye_pixmap := createPixmap(eye_pic);
    hand_pixmap := createPixmap(hand_pic);
    take_pixmap := createPixmap(take_pic);
    drop_pixmap := createPixmap(drop_pic);
    load_pixmap := createPixmap(load_pic);
    save_pixmap := createPixmap(save_pic);
    exit_pixmap := createPixmap(exit_pic);

    stair_up.pixmap := createPixmap(up_pic);
    stair_up.name := "Stairs up";
    stair_down.pixmap := createPixmap(down_pic);
    stair_down.name := "Stairs down";

    message := 0 times "";
    exitGame := FALSE;
    flaskFilled := FALSE;

    initRoomData;
    initMonsterData;
    initItemData;
    initPlayerData;
    loadHighScore;
  end func;


const proc: displayBox (in color: boxColor) is func
  local
    const integer: width is 2 * BORDER_DIST + ROOM_COLUMNS * PIXMAP_SIZE;
    const integer: height is 2 * BORDER_DIST + ROOM_LINES * PIXMAP_SIZE;
  begin
    hline(1, 0, width - 2, boxColor);
    vline(0, 1, height - 2, boxColor);
    hline(1, height - 1, width - 2, boxColor);
    vline(width - 1, 1, height - 2, boxColor);
    point(1, 1, boxColor);
    point(width - 2, 1, boxColor);
    point(width - 2, height - 2, boxColor);
    point(1, height - 2, boxColor);
  end func;


const func PRIMITIVE_WINDOW: getPixmap (inout graphObj: currObject) is func
  result
    var PRIMITIVE_WINDOW: pixmap is PRIMITIVE_WINDOW.value;
  begin
    if currObject.pixmap = PRIMITIVE_WINDOW.value then
      currObject.pixmap := createPixmap(currObject.picture,
          PIXMAP_SIZE div length(currObject.picture), black);
    end if;
    pixmap := currObject.pixmap;
  end func;


const proc: displayRoom (in roomType: current_room) is func
  local
    var integer: line is 0;
    var integer: column is 0;
    var char: ch is ' ';
    var PRIMITIVE_WINDOW: field_pixmap is PRIMITIVE_WINDOW.value;
    var itemType: item_ident is NO_ITEM;
    var integer: number is 0;
  begin
    for line range 1 to ROOM_LINES do
      for column range 1 to length(current_room.data[line]) do
        ch := current_room.data[line][column];
        if ch <> ' ' then
          case ch of
            when {'#'}: field_pixmap := wall_pixmap;
            when {':'}: field_pixmap := colored_wall1_pixmap;
            when {';'}: field_pixmap := colored_wall2_pixmap;
            when {'|'}: field_pixmap := vertical_pixmap;
            when {'-'}: field_pixmap := horizontal_pixmap;
            when {','}: field_pixmap := left_upper_pixmap;
            when {'.'}: field_pixmap := right_upper_pixmap;
            when {'`'}: field_pixmap := left_lower_pixmap;
            when {'''}: field_pixmap := right_lower_pixmap;
            when {'>'}: field_pixmap := railing_pixmap;
            when {'='}: field_pixmap := gate_pixmap;
            when {'+'}: field_pixmap := small_bush_pixmap;
            when {'*'}: field_pixmap := big_bush_pixmap;
            when {'$'}: field_pixmap := statue_pixmap;
            when {'?'}: field_pixmap := fountain_pixmap;
            when {'@'}: field_pixmap := wood_pixmap;
            when {'&'}: field_pixmap := stone_pixmap;
            when {'%'}: field_pixmap := gray_pixmap;
            when {'~'}: field_pixmap := water_pixmap;
            when {']'}: field_pixmap := bed_left_pixmap;
            when {'['}: field_pixmap := bed_right_pixmap;
            when {'_'}: field_pixmap := bed_top_pixmap;
            when {'^'}: field_pixmap := bed_bottom_pixmap;
            when {'W'}: field_pixmap := bed_white_middle_pixmap;
            when {'R'}: field_pixmap := bed_red_middle_pixmap;
            when {'N'}: field_pixmap := bed_blue_middle_pixmap;
            when {'P'}: field_pixmap := bed_purple_middle_pixmap;
            when {'Y'}: field_pixmap := bed_yellow_middle_pixmap;
            when {'{'}: field_pixmap := chain_pixmap;
            when {'K'}: field_pixmap := k_pixmap;
            when {'B'}: field_pixmap := b_pixmap;
            when {'U'}: field_pixmap := stair_up.pixmap;
            when {'D'}: field_pixmap := stair_down.pixmap;
            when {' '}: field_pixmap := floor_pixmap;
            when {'a' .. 'z'}:
              item_ident := itemType conv ch;
              if item_ident <> NO_ITEM then
                field_pixmap := getPixmap(item[item_ident]);
              else
                field_pixmap := query_pixmap;
              end if;
            otherwise:
              field_pixmap := query_pixmap;
              write("undefined char no ");
              writeln(ord(ch));
          end case;
          put(curr_win, BORDER_DIST + pred(column) * PIXMAP_SIZE,
              BORDER_DIST + pred(line) * PIXMAP_SIZE, field_pixmap, PSET);
        end if;
      end for;
    end for;
    rect(8, BUTTON_YPOS_MIN, 300, 50, black);
    color(white, black);
    for number range 1 to length(current_room.description) do
      setPos(screen, pred(BUTTON_TEXT_LINE1 + number), 2);
      write(screen, current_room.description[number]);
    end for;
  end func;


const proc: displayObj (inout graphObj: currObject) is func
  begin
    put(curr_win, BORDER_DIST + currObject.saved_xPos,
        BORDER_DIST + currObject.saved_yPos, currObject.saved_pixmap, PSET);
    currObject.saved_pixmap := getPixmap(BORDER_DIST + currObject.xPos,
        BORDER_DIST + currObject.yPos, PIXMAP_SIZE, PIXMAP_SIZE);
    put(curr_win, BORDER_DIST + currObject.xPos,
        BORDER_DIST + currObject.yPos, getPixmap(currObject), PSET);
    currObject.saved_xPos := currObject.xPos;
    currObject.saved_yPos := currObject.yPos;
  end func;


const proc: displayMonster is func
  local
    var monsterType: monster_ident is NO_MONSTER;
  begin
    for monster_ident range MIN_MONSTER to MAX_MONSTER do
      if monster[monster_ident].room_num = player.room then
        displayObj(monster[monster_ident]);
      end if;
    end for;
 end func;


const proc: objectLegend (inout graphObj: currObject, in integer: line) is func
  begin
    put(curr_win, LEGEND_XPOS, LEGEND_YPOS_MIN + pred(line) * PIXMAP_SIZE, getPixmap(currObject), PSET);
    color(white, black);
    setPos(screen, 2 * line, LEGEND_COLUMN);
    if currObject.adjective <> "" then
      write(screen, currObject.adjective & " " & currObject.name);
    else
      write(screen, currObject.name);
    end if;
  end func;


const proc: displayLegend is func
  local
    var monsterType: monster_ident is NO_MONSTER;
    var itemType: item_ident is NO_ITEM;
    var integer: line is 1;
  begin
    rect(LEGEND_XPOS, LEGEND_YPOS_MIN, 200, 576, black);
    for monster_ident range MIN_MONSTER to MAX_MONSTER do
      if monster[monster_ident].room_num = player.room and
          monster[monster_ident].name <> "" then
        objectLegend(monster[monster_ident], line);
        incr(line);
      end if;
    end for;
    for item_ident range MIN_ITEM to MAX_ITEM do
      if item[item_ident].room_num = player.room and
          not item_ident in player.inventory then
        objectLegend(item[item_ident], line);
        incr(line);
      end if;
    end for;
    if room[player.room].exits[UP] <> 0 then
      objectLegend(stair_up, line);
      incr(line);
    end if;
    if room[player.room].exits[DOWN] <> 0 then
      objectLegend(stair_down, line);
      incr(line);
    end if;
  end func;


const func integer: sizeOfLegend is func
  result
    var integer: line is 0;
  local
    var monsterType: monster_ident is NO_MONSTER;
    var itemType: item_ident is NO_ITEM;
  begin
    for monster_ident range MIN_MONSTER to MAX_MONSTER do
      if monster[monster_ident].room_num = player.room and
          monster[monster_ident].name <> "" then
        incr(line);
      end if;
    end for;
    for item_ident range MIN_ITEM to MAX_ITEM do
      if item[item_ident].room_num = player.room and
          not item_ident in player.inventory then
        incr(line);
      end if;
    end for;
    if room[player.room].exits[UP] <> 0 then
      incr(line);
    end if;
    if room[player.room].exits[DOWN] <> 0 then
      incr(line);
    end if;
  end func;


const proc: collectedTreasures is func
  local
    var itemType: item_ident is NO_ITEM;
    var integer: line is 1;
  begin
    rect(LEGEND_XPOS, 0, width(curr_win) - LEGEND_XPOS,
        BORDER_DIST + PIXMAP_SIZE * 14, black);
    color(white, black);
    setPos(screen, 2 * line - 1, LEGEND_COLUMN);
    write(screen, "You have collected");
    setPos(screen, 2 * line, LEGEND_COLUMN);
    write(screen, "these Treasures");
    incr(line);
    for item_ident range CROWN to MAX_ITEM do
      if item_ident in player.inventory or
          item[item_ident].room_num = COURTYARD then
        objectLegend(item[item_ident], line);
        incr(line);
      end if;
    end for;
    if line = 2 then
      color(white, black);
      setPos(screen, 2 * line, LEGEND_COLUMN);
      write(screen, "NONE");
    end if;
  end func;


const proc: killedMonsters is func
  local
    var monsterType: monster_ident is NO_MONSTER;
    var integer: line is 16;
  begin
    rect(LEGEND_XPOS, 15 * PIXMAP_SIZE, width(curr_win) - LEGEND_XPOS,
        BORDER_DIST + PIXMAP_SIZE * 7, black);
    color(white, black);
    setPos(screen, 2 * line - 1, LEGEND_COLUMN);
    write(screen, "You have killed");
    setPos(screen, 2 * line, LEGEND_COLUMN);
    write(screen, "these Monsters");
    incr(line);
    if not monster[ANGRY_DEMON1].living or not monster[ANGRY_DEMON2].living then
      objectLegend(monster[ANGRY_DEMON1], line);
      if not monster[ANGRY_DEMON1].living and not monster[ANGRY_DEMON2].living then
        setPos(screen, 2 * line, LEGEND_COLUMN + 15);
        write(screen, "two times");
      end if;
      incr(line);
    end if;
    if not monster[UGLY_OGRE1].living or not monster[UGLY_OGRE2].living then
      objectLegend(monster[UGLY_OGRE1], line);
      if not monster[UGLY_OGRE1].living and not monster[UGLY_OGRE2].living then
        setPos(screen, 2 * line, LEGEND_COLUMN + 15);
        write(screen, "two times");
      end if;
      incr(line);
    end if;
    for monster_ident range BIG_SPIDER to BAT do
      if not monster[monster_ident].living and
          monster[monster_ident].name <> "" then
        objectLegend(monster[monster_ident], line);
        incr(line);
      end if;
    end for;
    if line = 17 then
      color(white, black);
      setPos(screen, 2 * line, LEGEND_COLUMN);
      write(screen, "NONE");
    end if;
  end func;


const func integer: getInventoryStartLine is func
  result
    var integer: startLine is 0;
  begin
    startLine := succ(sizeOfLegend);
    if startLine < INVENTORY_LINE then
      startLine := INVENTORY_LINE;
    end if;
  end func;


const proc: displayInventoryItem (in integer: item_num) is func
  local
    var integer: item_line is 1;
    var itemType: item_ident is NO_ITEM;
  begin
    for item_ident range MIN_ITEM to MAX_ITEM do
      if item_ident in player.inventory then
        if item_line = item_num then
          objectLegend(item[item_ident], getInventoryStartLine + item_line);
        end if;
        incr(item_line);
      end if;
    end for;
  end func;


const proc: displayInventoryList is func
  local
    var integer: line is 0;
    var itemType: item_ident is NO_ITEM;
  begin
    line := succ(getInventoryStartLine);
    if player.inventory = itemSet.EMPTY_SET then
      color(white, black);
      setPos(screen, 2 * line, LEGEND_COLUMN);
      write(screen, "Nothing");
      incr(line);
    else
      for item_ident range MIN_ITEM to MAX_ITEM do
        if item_ident in player.inventory then
          objectLegend(item[item_ident], line);
          incr(line);
        end if;
      end for;
    end if;
  end func;


const proc: displayInventory is func
  local
    var integer: startLine is 0;
  begin
    startLine := getInventoryStartLine;
    rect(LEGEND_XPOS, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
        182, PIXMAP_SIZE * (card(player.inventory) + 2), black);
    color(white, black);
    setPos(screen, 2 * startLine, LEGEND_COLUMN);
    write(screen, "Inventory");
    displayInventoryList;
  end func;


const proc: displayMessage is func
  local
    var integer: number is 0;
  begin
    rect(186, BUTTON_YPOS_MIN, 346, 70, black);
    color(screen, white, black);
    for number range 1 to length(message) do
      setPos(screen, BUTTON_TEXT_LINE1 + pred(number), 32);
      write(screen, message[number]);
    end for;
  end func;


const proc: displayCommands (in PRIMITIVE_WINDOW: bmp) is func
  begin
    put(curr_win, BUTTONS_1_XPOS, BUTTON_YPOS_MIN, load_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1, BUTTONS_1_COLUMN);
    write(screen, "Load");
    put(curr_win, BUTTONS_1_XPOS, BUTTON_YPOS_MIN + 32, save_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1 + 2, BUTTONS_1_COLUMN);
    write(screen, "Save");
    put(curr_win, BUTTONS_1_XPOS, BUTTON_YPOS_MIN + 64, exit_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1 + 4, BUTTONS_1_COLUMN);
    write(screen, "Exit");
    put(curr_win, BUTTONS_2_XPOS, BUTTON_YPOS_MIN, take_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1, BUTTONS_2_COLUMN);
    write(screen, "Take");
    put(curr_win, BUTTONS_2_XPOS, BUTTON_YPOS_MIN + 32, drop_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1 + 2, BUTTONS_2_COLUMN);
    write(screen, "Drop");
    put(curr_win, BUTTONS_3_XPOS, BUTTON_YPOS_MIN, eye_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1, BUTTONS_3_COLUMN);
    write(screen, "Look");
    put(curr_win, BUTTONS_3_XPOS, BUTTON_YPOS_MIN + 32, hand_pixmap, PSET);
    setPos(screen, BUTTON_TEXT_LINE1 + 2, BUTTONS_3_COLUMN);
    write(screen, "Use");
  end func;


const proc: displayAll is func
  begin
    clear(curr_win, black);
    displayRoom(room[player.room]);
    displayMessage;
    displayMonster;
    displayObj(player);
    displayLegend;
    displayInventory;
    displayCommands(curr_win);
    displayBox(white);
  end func;


const proc: floodTrap is func
  local
    var string: s1 is "";
    var string: s2 is "";
    var integer: number is 0;
  begin
    s1 := room[WINDING_PASSAGE3].data[9];
    s2 := room[WINDING_PASSAGE3].data[10];

    s1 @:= [12] '#';
    s1 @:= [24] '#';
    s2 @:= [12] '#';
    s2 @:= [24] '#';

    room[WINDING_PASSAGE3].data[9] := s1;
    room[WINDING_PASSAGE3].data[10] := s2;
    for number range 13 to 23 do
      s1 @:= [number] '~';
      s2 @:= [number] '~';

      room[WINDING_PASSAGE3].data[9] := s1;
      room[WINDING_PASSAGE3].data[10] := s2;
      displayAll;
    end for;
  end func;


const proc: checkRoom is func
  begin
    case player.room of
      when {WINDING_PASSAGE1}:
        room[WINDING_PASSAGE2].data[18] := "   #################### ";
      when {WINDING_PASSAGE3}:
        room[WINDING_PASSAGE2].data[18] := "   #################### ";
        if player.xPos = 544 and not NECKLACE in player.inventory then
          message := [] ("You have sprung a Trap!!");
          displayAll;
          floodTrap;
          message &:= [] ("The room has filled with water and you drowned!");

          displayAll;

          player.living := FALSE;
        end if;
      when {SECRET_ROOM}:
        room[WINDING_PASSAGE2].data[18] := "   ########  ########## ";
      when {WINDING_PASSAGE2}:
        if room[WINDING_PASSAGE2].data[18][12] = '#' and player.yPos > 512 then
          room[WINDING_PASSAGE2].data[18] := "   ########  ########## ";
        end if;
      when {WEST_TOWER_MIDDLE}:
        room[SORCERERS_QUARTERS].data[8]  := "######            |#    ";
        room[SORCERERS_QUARTERS].data[9]  := "                  |#    ";
        room[SORCERERS_QUARTERS].data[10] := "                  |#    ";
        room[SORCERERS_QUARTERS].data[11] := "######            |#    ";
      when {LABORATORY}:
        room[SORCERERS_QUARTERS].data[8]  := "######             #####";
        room[SORCERERS_QUARTERS].data[9]  := "                        ";
        room[SORCERERS_QUARTERS].data[10] := "                        ";
        room[SORCERERS_QUARTERS].data[11] := "######             #####";
      when {SORCERERS_QUARTERS}:
        if room[SORCERERS_QUARTERS].data[10][20] = '#' and player.xPos > 544 then
          room[SORCERERS_QUARTERS].data[8]  := "######             #####";
          room[SORCERERS_QUARTERS].data[9]  := "                        ";
          room[SORCERERS_QUARTERS].data[10] := "                        ";
          room[SORCERERS_QUARTERS].data[11] := "######             #####";
        end if;
    end case;
  end func;


const proc: hideItem (in itemObj: anItem) is func
  local
    var integer: line is 0;
    var integer: column is 0;
  begin
    if anItem.room_num <> NO_ROOM then
      line   := succ(anItem.yPos div PIXMAP_SIZE);
      column := succ(anItem.xPos div PIXMAP_SIZE);
      room[anItem.room_num].data[line] @:= [column] ' ';
    end if;
  end func;


const proc: showItem (in itemObj: anItem) is func
  local
    var integer: line is 0;
    var integer: column is 0;
  begin
    if anItem.room_num <> NO_ROOM then
      line   := succ(anItem.yPos div PIXMAP_SIZE);
      column := succ(anItem.xPos div PIXMAP_SIZE);
      room[anItem.room_num].data[line] @:= [column] char conv (anItem.ident);
    end if;
  end func;


const proc: loadGame is func
  local
    var file: load_file is STD_NULL;
    var monsterType: monster_ident is NO_MONSTER;
    var itemType: item_ident is NO_ITEM;
  begin
    if fileType("castle.sav") = FILE_REGULAR then
      if isOkay([]("Do you want to load a saved game.", "This will end the current game?")) then
        load_file := open("castle.sav", "r");
        for monster_ident range MIN_MONSTER to MAX_MONSTER do
          readln(load_file, monster[monster_ident].adjective);
          readln(load_file, monster[monster_ident].xPos);
          readln(load_file, monster[monster_ident].yPos);
          readln(load_file, monster[monster_ident].strength);
          readln(load_file, monster[monster_ident].direction);
          readln(load_file, monster[monster_ident].living);
        end for;
        for item_ident range MIN_ITEM to MAX_ITEM do
          hideItem(item[item_ident]);
          readln(load_file, item[item_ident].xPos);
          readln(load_file, item[item_ident].yPos);
          readln(load_file, item[item_ident].room_num);
        end for;
        readln(load_file, player.xPos);
        readln(load_file, player.yPos);
        readln(load_file, player.room);
        readln(load_file, player.direction);
        readln(load_file, player.inventory);
        readln(load_file, player.strength);
        close(load_file);

        message := [] ("Game Loaded Successful!");
        for item_ident range MIN_ITEM to MAX_ITEM do
          if item_ident not in player.inventory then
            showItem(item[item_ident]);
          end if;
        end for;
        player.saved_pixmap := PRIMITIVE_WINDOW.value;
        checkRoom;
        displayAll;
      end if;
    else
      message := [] ("There Is No Saved Game To Load!");
      displayMessage;
    end if;
  end func;


const proc: saveGame is func
  local
    var file: save_file is STD_NULL;
    var monsterType: monster_ident is NO_MONSTER;
    var itemType: item_ident is NO_ITEM;
  begin
    if fileType("castle.sav") = FILE_REGULAR then
      if isOkay([]("Do you want to overwrite the", "game currently saved?")) then
        removeFile("castle.sav");
      end if;
    end if;
    if fileType("castle.sav") = FILE_ABSENT then
      save_file := open("castle.sav", "w");
      for monster_ident range MIN_MONSTER to MAX_MONSTER do
        writeln(save_file, monster[monster_ident].adjective);
        writeln(save_file, monster[monster_ident].xPos);
        writeln(save_file, monster[monster_ident].yPos);
        writeln(save_file, monster[monster_ident].strength);
        writeln(save_file, monster[monster_ident].direction);
        writeln(save_file, monster[monster_ident].living);
      end for;
      for item_ident range MIN_ITEM to MAX_ITEM do
        writeln(save_file, item[item_ident].xPos);
        writeln(save_file, item[item_ident].yPos);
        writeln(save_file, item[item_ident].room_num);
      end for;
      writeln(save_file, player.xPos);
      writeln(save_file, player.yPos);
      writeln(save_file, player.room);
      writeln(save_file, player.direction);
      writeln(save_file, player.inventory);
      writeln(save_file, player.strength);
      close(save_file);

      message := [] ("Game Saved Successful!");
      displayMessage;
    end if;
  end func;


const func monsterType: searchMonster (in integer: xPos, in integer: yPos) is func
  result
    var monsterType: monsterFound is NO_MONSTER;
  local
    var monsterType: monster_ident is NO_MONSTER;
  begin
    for monster_ident range MIN_MONSTER to MAX_MONSTER do
      if monster[monster_ident].room_num = player.room and
          xPos >= monster[monster_ident].xPos and
          xPos < monster[monster_ident].xPos + PIXMAP_SIZE and
          yPos >= monster[monster_ident].yPos and
          yPos < monster[monster_ident].yPos + PIXMAP_SIZE then
        monsterFound := monster_ident;
      end if;
    end for;
  end func;


const func monsterType: hitMonster (in integer: xPos, in integer: yPos) is func
  result
    var monsterType: monsterMet is NO_MONSTER;
  local
    var monsterType: monster_ident is NO_MONSTER;
  begin
    for monster_ident range MIN_MONSTER to MAX_MONSTER do
      if monster[monster_ident].room_num = player.room and monster[monster_ident].living then
        if abs(xPos - monster[monster_ident].xPos) < PIXMAP_SIZE and
            abs(yPos - monster[monster_ident].yPos) < PIXMAP_SIZE then
          monsterMet := monster_ident;
        end if;
      end if;
    end for;
  end func;


const func itemType: selectItem (in PRIMITIVE_WINDOW: symbol, in string: name, in integer: invSize) is func
  result
    var itemType: itemSelected is NO_ITEM;
  local
    var char: command is ' ';
    var integer: startLine is 0;
    var integer: position is 0;
    var itemType: item_ident is NO_ITEM;
    var integer: count is 0;
    var boolean: getCommand is FALSE;
    var integer: xPos is 0;
    var integer: yPos is 0;
  begin
    if card(player.inventory) >= invSize then
      startLine := getInventoryStartLine;
      rect(LEGEND_XPOS, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
          PIXMAP_SIZE, PIXMAP_SIZE, black);
      box(LEGEND_XPOS - 3, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE - 3,
          187, succ(card(player.inventory))  * PIXMAP_SIZE + 6, light_red);
      rect(LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
          150, PIXMAP_SIZE, light_gray);
      put(curr_win, LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
          symbol, PSET);
      color(white, black);
      setPos(screen, 2 * startLine, LEGEND_COLUMN + 5);
      write(screen, name);
      displayInventoryList;
      position := 0;
      command := getc(KEYBOARD);
      while command not in {' ', KEY_NL, KEY_ESC} do
        getCommand := TRUE;
        rect(LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine + position) * PIXMAP_SIZE,
            150, PIXMAP_SIZE, black);
        if position >= 1 then
          displayInventoryItem(position);
        else
          put(curr_win, LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
              symbol, PSET);
          color(white, black);
          setPos(screen, 2 * startLine, LEGEND_COLUMN + 5);
          write(screen, name);
        end if;
        if command = KEY_UP and position > 1 then
          decr(position);
        elsif command = KEY_DOWN and position < card(player.inventory) then
          incr(position);
        elsif command = KEY_MOUSE1 then
          xPos := getxpos(KEYBOARD);
          yPos := getypos(KEYBOARD);
          if xPos >= LEGEND_XPOS and xPos < LEGEND_XPOS + 128 and
              yPos >= LEGEND_YPOS_MIN + startLine * PIXMAP_SIZE and
              yPos <= LEGEND_YPOS_MIN + (startLine + card(player.inventory)) * PIXMAP_SIZE then
            position := (yPos - LEGEND_YPOS_MIN - startLine * PIXMAP_SIZE) div 32 + 1;
          else
            position := 0;
          end if;
          command := ' ';
          getCommand := FALSE;
        end if;
        rect(LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine + position) * PIXMAP_SIZE,
            150, PIXMAP_SIZE, light_gray);
        if position >= 1 then
          displayInventoryItem(position);
        else
          put(curr_win, LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
              symbol, PSET);
          color(white, black);
          setPos(screen, 2 * startLine, LEGEND_COLUMN + 5);
          write(screen, name);
        end if;
        if getCommand then
          command := getc(KEYBOARD);
        end if;
      end while;
      rect(LEGEND_XPOS + PIXMAP_SIZE, LEGEND_YPOS_MIN + pred(startLine + position) * PIXMAP_SIZE,
          150, PIXMAP_SIZE, black);
      displayInventoryItem(position);
      if position >= 1  and command in {' ', KEY_NL} then
        count := 0;
        for item_ident range MIN_ITEM to MAX_ITEM do
          if item_ident in player.inventory then
            incr(count);
            if count = position then
              itemSelected := item_ident;
            end if;
          end if;
        end for;
      end if;
      rect(LEGEND_XPOS, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE,
          182, PIXMAP_SIZE, black);
      setPos(screen, 2 * startLine, LEGEND_COLUMN);
      color(white, black);
      write(screen, "Inventory");
      box(LEGEND_XPOS - 3, LEGEND_YPOS_MIN + pred(startLine) * PIXMAP_SIZE - 3,
          187, succ(card(player.inventory))  * PIXMAP_SIZE + 6, black);
    end if;
  end func;


const proc: dropItem (in itemType: item_ident) is func
  local
    var directionType: direction is NORTH;
    var integer: column is 0;
    var integer: line is 0;

  begin
    direction := player.direction;
    repeat
      if direction = NORTH then
        line :=   player.yPos               div PIXMAP_SIZE;
        column := player.xPos               div PIXMAP_SIZE + 1;
        incr(direction);
      elsif direction = SOUTH then
        line :=   (player.yPos + STEP_SIZE) div PIXMAP_SIZE + 2;
        column := player.xPos               div PIXMAP_SIZE + 1;
        incr(direction);
      elsif direction = EAST then
        line :=   player.yPos               div PIXMAP_SIZE + 1;
        column := (player.xPos + STEP_SIZE) div PIXMAP_SIZE + 2;
        incr(direction);
      elsif direction = WEST then
        line :=   player.yPos               div PIXMAP_SIZE + 1;
        column := player.xPos               div PIXMAP_SIZE;
        direction := NORTH;
      end if;
    until line >= 1 and line <= ROOM_LINES and
        column >= 1 and column <= ROOM_COLUMNS and
        room[player.room].data[line][column] = ' ' and
        hitMonster(pred(column) * PIXMAP_SIZE, pred(line) * PIXMAP_SIZE) = NO_MONSTER or
        direction = player.direction;
    if room[player.room].data[line][column] = ' ' then
      item[item_ident].xPos := pred(column) * PIXMAP_SIZE;
      item[item_ident].yPos := pred(line)   * PIXMAP_SIZE;
      item[item_ident].room_num := player.room;
      showItem(item[item_ident]);
      excl(player.inventory, item_ident);
      put(curr_win, BORDER_DIST + item[item_ident].xPos,
          BORDER_DIST + item[item_ident].yPos, getPixmap(item[item_ident]), PSET);
      displayLegend;
      displayInventory;
    else
      displayInventory;
      message := [] ("Something is in the way!");
      displayMessage;
    end if;
  end func;


const proc: dropItem is func
  local
    var itemType: item_ident is NO_ITEM;
  begin
    item_ident := selectItem(drop_pixmap, "Drop", 1);
    if item_ident <> NO_ITEM then
      dropItem(item_ident);
    end if;
  end func;


const proc: useItem (in itemType: item_ident) is func
  local
    var boolean: done is FALSE;
  begin
    case item_ident of
      when {BOOK}:
        if GLASSES in player.inventory then
          message := [] ("The book reads:",
                         "  Wave Scepter");
        else
          message := [] ("You can't see well enough.",
                         "It's all Blurry.");
        end if;
        done := TRUE;
      when {CRYSTAL_BALL}:
        message := [] ("In the Crystal Ball....",
                       " You see a man in a winding passage, waving a Wand.");
        done := TRUE;
      when {HELMET}:
        message := [] ("Ok. I'm wearing it.");
        done := TRUE;
      when {MAGIC_WAND}:
        if player.room = WINDING_PASSAGE2 then
          message := [] ("As you wave the WAND....",
                         "  There is a *Puff* of smoke Revealing a Secret passage!");
          room[WINDING_PASSAGE2].data[18] := "   ########  ########## ";
        elsif player.room = SORCERERS_QUARTERS then
          message := [] ("As you wave the WAND....",
                         "  There is a *Puff* of smoke Revealing a Secret passage!");
          room[SORCERERS_QUARTERS].data[8]  := "######             #####";
          room[SORCERERS_QUARTERS].data[9]  := "                        ";
          room[SORCERERS_QUARTERS].data[10] := "                        ";
          room[SORCERERS_QUARTERS].data[11] := "######             #####";
        else
          message := [] ("As you wave the WAND....",
                         "  Nothing Happens!");
        end if;
        done := TRUE;
      when {WINE_FLASK}:
        if flaskFilled then
          message := [] ("That was good!",
                         "I feel much better now!");
          player.strength := MAX_STRENGTH;
        else
          message := [] ("You don't have any WATER!");
        end if;
        done := TRUE;
      when {HARP}:
        message := [] ("The Harp makes Beautiful Music!");
        if player.room = CORRIDOR52 or player.room = CORRIDOR54 then
          monster[FAIRY1].room_num := -1;
          monster[FAIRY1].living := FALSE;
          monster[FAIRY2].room_num := -1;
          monster[FAIRY2].living := FALSE;
          message := [] ("The fairy likes the music and leaves!");
        end if;
        done := TRUE;
      when {HOLY_CROSS}:
        if player.room = CORRIDOR28 then
          monster[VAMPIRE].room_num := -1;
          message := [] ("The vampire sees the holy cross and disappears!");
          done := TRUE;
        end if;
      when {SCEPTER}:
        if player.room = COURTYARD then
          message := [] ("As you wave the scepter...",
                         "The Gate opens by itself!");
          room[COURTYARD].data[18] := "##########    ##########";
        else
          message := [] ("As you wave the SCEPTER....",
                         "  Nothing Happens!");
        end if;
        done := TRUE;
    end case;

    if not done then
      message := [] ("You look awful Silly waving that " & item[item_ident].name);
    end if;
    displayAll;
  end func;


const proc: useItem is func
  local
    var itemType: item_ident is NO_ITEM;
  begin
    item_ident := selectItem(hand_pixmap, "Use", 1);
    if item_ident <> NO_ITEM then
      useItem(item_ident);
    end if;
  end func;


const func array string: getMonsterMessage (in monsterType: monster_ident) is func
  result
    var array string: message is 0 times "";
  begin
    case monster_ident of
      when {ANGRY_DEMON1, ANGRY_DEMON2, UGLY_OGRE1, UGLY_OGRE2,
          BIG_SPIDER, SMALL_SPIDER, SNAKE, BAT, VAMPIRE}:
        if monster[monster_ident].living then
          message := [] ("look " & monster[monster_ident].name & ":",
                         "The " & monster[monster_ident].name & " looks Mean!");
        else
          message := [] ("look " & monster[monster_ident].name & ":",
                         "The " & monster[monster_ident].name & " looks Dead!");
        end if;
      when {FAIRY1, FAIRY2}:
        message := [] ("look " & monster[monster_ident].name & ":",
                       "The " & monster[monster_ident].name & " looks pretty but unhappy.");
      when {DOOR1, DOOR2, DOOR3, DOOR4}:
        message := [] ("look door:",
                       "It looks very Strong.");
    end case;
 end func;


const func array string: getItemMessage (in itemType: item_ident) is func
  result
    var array string: message is 0 times "";
  begin
    case item_ident of
      when {BOOK}:
        message := [] ("It is titled",
                       " 'The Gate'");
      when {CRYSTAL_BALL}:
        message := [] ("In the Crystal Ball....",
                       " You see a man in a winding passage, waving a Wand.");
      when {GLASSES}:
        message := [] ("They're Bifocals");
      when {HELMET}:
        message := [] ("It looks Tough!");
      when {KEY}:
        message := [] ("It looks Old!");
      when {LAMP}:
        message := [] ("It's lit Magically");
      when {MAGIC_WAND}:
        message := [] ("It looks Magical!");
      when {SWORD}:
        message := [] ("It looks Sharp!");
      when {WINE_FLASK}:
        if flaskFilled then
          message := [] ("It's filled with water.");
        else
          message := [] ("It's empty.");
        end if;
      when {CROWN}:
        message := [] ("It's made of Gold!");
      when {DIAMOND}:
        message := [] ("It looks Expensive!");
      when {FANCY_GOBLET}:
        message := [] ("It's made of Gold!");
      when {GOLDBAR}:
        message := [] ("It looks Expensive!");
      when {HARP}:
        message := [] ("It's made of Gold!");
      when {HOLY_CROSS}:
        message := [] ("It's made of Gold!");
      when {HOURGLASS}:
        message := [] ("It's made of Gold!");
      when {JADE_FIGURINE}:
        message := [] ("It looks Expensive!");
      when {LARGE_GEM}:
        message := [] ("It looks Expensive!");
      when {NECKLACE}:
        message := [] ("On the back it says:",
                       "Protection from Traps.");
      when {RUBYS}:
        message := [] ("They look Expensive!");
      when {SCEPTER}:
        message := [] ("It looks Expensive!");
      when {SILVER_BARS}:
        message := [] ("They look Expensive!");
    end case;
  end func;


const func integer: getScore is func
  result
    var integer: score is 0;
  local
    var monsterType: monster_ident is NO_MONSTER;
    var itemType: item_ident is NO_ITEM;
  begin
    for monster_ident range MIN_MONSTER to BAT do
      if not monster[monster_ident].living then
        score +:= 100;
      end if;
    end for;
    for item_ident range CROWN to MAX_ITEM do
      if item_ident in player.inventory or
          item[item_ident].room_num = COURTYARD then
        score +:= 50;
      end if;
    end for;
    if player.room = ESCAPED then
      score +:= 100;
    end if;

    if score > highScore.points then
      highScore.points := score;
      highScore.name := player.name;
    end if;
  end func;


const proc: showStatus is func
  local
    var char: command is ' ';
  begin
    rect(BUTTONS_1_XPOS, BUTTON_YPOS_MIN, 272, 128, black);
    box(BUTTONS_1_XPOS, BUTTON_YPOS_MIN, 272, 128, light_red);
    color(screen, white, black);
    if player.room = ESCAPED then
      setPos(screen, BUTTON_TEXT_LINE1, 100);
      write(screen, "You've Escaped the Castle!");
    elsif player.living then
      setPos(screen, BUTTON_TEXT_LINE1, 100);
      write(screen, "  Your current status:");
    else
      setPos(screen, BUTTON_TEXT_LINE1, 100);
      write(screen, "You have failed to Escape!");
    end if;
    rect(LEGEND_XPOS, 0, width(curr_win) - LEGEND_XPOS, height(curr_win), black);
    collectedTreasures;
    killedMonsters;
    setPos(screen, BUTTON_TEXT_LINE1 + 2, 105);
    write(screen, "Your Score: " <& getScore);
    setPos(screen, BUTTON_TEXT_LINE1 + 3, 104);
    write(screen, "(1550 is perfect)");
    saveHighScore;
    setPos(screen, BUTTON_TEXT_LINE1 + 5, 104);
    write(screen, "- Press any key -");
    repeat
      command := upper(getc(KEYBOARD));
      if command in {'Y', 'N'} then
        setPos(screen, BUTTON_TEXT_LINE1 + 6, 105);
        write(screen, "(Except Y or N)");
      end if;
    until command not in {'Y', 'N'};
  end func;


const proc: lookItemInRoom is func
  local
    var integer: xPos is 0;
    var integer: yPos is 0;
    var integer: line is 0;
    var integer: column is 0;
    var itemType: item_ident is NO_ITEM;
    var monsterType: monster_ident is NO_MONSTER;
  begin
    xPos := getxpos(KEYBOARD);
    yPos := getypos(KEYBOARD);
    xPos -:= BORDER_DIST;
    yPos -:= BORDER_DIST;
    line := succ(yPos div PIXMAP_SIZE);
    column := succ(xPos div PIXMAP_SIZE);
    if line >= 1 and line <= ROOM_LINES and
        column >= 1 and column <= ROOM_COLUMNS then
      (* writeln(xPos <& " " <& column);
      writeln(yPos <& " " <& line);
      writeln(" " <& room[player.room].data[line][column] <&
          " " <& ord(room[player.room].data[line][column])); *)
      case room[player.room].data[line][column] of
        when {'#', '&'}:
          if player.room = COURTYARD then
            if highScore.name = "" then
              message := [] ("look wall:",
                             "Writing on the wall says...",
                             "Highscore: " & str(highScore.points));
            else
              message := [] ("look wall:",
                             "Writing on the wall says...",
                             "Highscore: " & str(highScore.points) &
                             " by " & highScore.name);
            end if;
            displayMessage;
            showStatus;
            displayCommands(curr_win);
            displayAll;
            message := 0 times "";
          elsif player.room = MAZE74 then
            message := [] ("look wall:",
                           "The Wall Says:",
                           "Kevin Bales was here!");
          else
            message := [] ("look wall:",
                           "The WALLS are made of Gray Stone.");
          end if;
        when {':', ';'}:
          message := [] ("look wall:",
                         "The walls are many colors!");
        when {'K', 'B'}:
          message := [] ("look wall:",
                         "The Wall Says:",
                         "Kevin Bales was here!");
        when {'W'}:
          message := [] ("look bed:",
                         "It's Old.");
        when {'R'}:
          message := [] ("look bed:",
                         "It's Red.");
        when {'N'}:
          message := [] ("look bed:",
                         "It's Blue.");
        when {'P'}:
          message := [] ("look bed:",
                         "It's Purple.");
        when {'Y'}:
          message := [] ("look bed:",
                         "It's Yellow.");
        when {'U'}:
          message := [] ("look staircase:",
                         "The Staircase leads up.");
        when {'D'}:
          message := [] ("look staircase:",
                         "The Staircase leads down.");
        when {'='}:
          message := [] ("look gate:",
                         "It looks very Strong.");
        when {'>'}:
          message := [] ("look balcony:",
                         "The balcony is made of stone.");
        when {'?'}:
          if player.room = GARDEN_SOUTH then
            message := [] ("look fountain:",
                           "The fountain is filled with water.",
                           "But you can't see In it.");
          elsif player.room = BALCONY then
            if item[LARGE_GEM].room_num = NO_ROOM and not LARGE_GEM in player.inventory then
              message := [] ("look fountain:",
                             "There is a BIG Gem in the Garden Fountain!");
            else
              message := [] ("look fountain:",
                             "The Garden Has A Fountain in the middle.");
            end if;
          end if;
        when {'{'}:
          message := [] ("look chains:",
                         "They look Magical!");
        when {'$'}:
          if item[NECKLACE].room_num = NO_ROOM and not NECKLACE in player.inventory then
            message := [] ("look statue:",
                           "The statue is wearing a Necklace");
          else
            message := [] ("look statue:",
                           "The statue looks like The King!");
          end if;
        when {'%'}:
          if player.room = KITCHEN then
            message := [] ("look table:",
                           "It's made of Stone.");
          elsif player.room = THRONE_ROOM then
            message := [] ("look throne:",
                           "The throne is made of Stone.");
          end if;
        when {'@'}:
          if player.room = CHEFS_QUARTERS then
            if item[WINE_FLASK].room_num = NO_ROOM and not WINE_FLASK in player.inventory then
              put(curr_win, BORDER_DIST + item[WINE_FLASK].xPos,
                  BORDER_DIST + item[WINE_FLASK].yPos, getPixmap(item[WINE_FLASK]), PSET);
              message := [] ("look desk:",
                             "There is a Wine Flask on top.");
            else
              message := [] ("look desk:",
                             "It's made of Wood.");
            end if;
          elsif player.room = YELLOW_ROOM then
            if item[GLASSES].room_num = NO_ROOM and not GLASSES in player.inventory then
              put(curr_win, BORDER_DIST + item[GLASSES].xPos,
                  BORDER_DIST + item[GLASSES].yPos, getPixmap(item[GLASSES]), PSET);
              message := [] ("look desk:",
                             "There is a Pair of Eye Glasses on Top.");
            else
              message := [] ("look desk:",
                             "It's made of Wood.");
            end if;
          elsif player.room = KINGS_STUDY then
            if item[KEY].room_num = NO_ROOM and not KEY in player.inventory then
              put(curr_win, BORDER_DIST + item[KEY].xPos,
                  BORDER_DIST + item[KEY].yPos, getPixmap(item[KEY]), PSET);
              message := [] ("look desk:",
                             "There is a Key on top.");
            else
              message := [] ("look desk:",
                             "It's made of Wood.");
            end if;
          elsif player.room = LIBRARY_WEST_END then
            if item[BOOK].room_num = NO_ROOM and not BOOK in player.inventory then
              put(curr_win, BORDER_DIST + item[BOOK].xPos,
                  BORDER_DIST + item[BOOK].yPos, getPixmap(item[BOOK]), PSET);
              message := [] ("look shelves:",
                             "There's a book on one.");
            else
              message := [] ("look shelves:",
                             "They're made of wood.");
            end if;
          elsif player.room = LIBRARY_EAST_END then
            message := [] ("look shelves:",
                           "They're made of wood.");
          elsif player.room = STORAGE_ROOM then
            message := [] ("look shelves:",
                           "They're made of wood.");
          elsif player.room = WINE_CELLAR then
            message := [] ("look barrels:",
                           "They look Old!");
          elsif player.room = TORTURE_ROOM then
            message := [] ("look table:",
                           "It's covered with Blood!");
          end if;
        when {'|'}:
          if player.room = SORCERERS_QUARTERS then
            message := [] ("look mirror:",
                           "It looks Magical!");
          elsif player.room = BALCONY then
            message := [] ("look balcony:",
                           "The balcony is made of stone.");
          end if;
        when {'+'}:
          message := [] ("look bush:",
                         "This is a small bush.");
        when {'*'}:
          message := [] ("look bush:",
                         "This is a big bush.");
        when {'a' .. 'z'}:
          item_ident := itemType conv (room[player.room].data[line][column]);
          if item_ident <> NO_ITEM then
            message := [] ("look " & item[item_ident].name & ":");
            message &:= getItemMessage(item_ident);
          end if;
        when {' '}:
          monster_ident := searchMonster(xPos, yPos);
          if monster_ident <> NO_MONSTER then
            message := getMonsterMessage(monster_ident);
          elsif xPos >= player.xPos and
              xPos < player.xPos + PIXMAP_SIZE and
              yPos >= player.yPos and
              yPos < player.yPos + PIXMAP_SIZE then
            if player.room = SORCERERS_QUARTERS and
                room[SORCERERS_QUARTERS].data[10][20] = '#' then
              message := [] ("look player:",
                             "You see an ugly face in the mirror.");
            else
              message := [] ("look player:",
                             "This is only possible with a mirror.");
            end if;
          else
            message := [] ("look floor:",
                           "The FLOORS are made of Gray Stone.");
          end if;
      end case;
    end if;
  end func;


const proc: lookItem is func
  local
    var itemType: item_ident is NO_ITEM;
  begin
    displayBox(light_red);
    message := [] ("look");
    displayMessage;
    message := 0 times "";
    item_ident := selectItem(eye_pixmap, "Look at", 0);
    if item_ident <> NO_ITEM then
      message := [] ("look " & item[item_ident].name & ":");
      message &:= getItemMessage(item_ident);
    else
      lookItemInRoom;
    end if;
    displayMessage;
    displayBox(white);
  end func;


const proc: welcomeScreen is func
  begin
    clear(curr_win, black);
    displayRoom(room[CASTLE]);
    setPos(screen, 5, 63);
    writeln(screen, "C A S T L E");
    setPos(screen, 7, 49);
    writeln(screen, "Copyright (C) 2004, 2005  Thomas Mertes");
    setPos(screen, 9, 49);
    writeln(screen, "This program is free software under the");
    setPos(screen, 10, 49);
    writeln(screen, "terms of the GNU General Public License");
    setPos(screen, 12, 43);
    writeln(screen, "Castle is written in the Seed7 programming language");
    setPos(screen, 13, 48);
    writeln(screen, "Homepage:    http://seed7.sourceforge.net");
  end func;


const proc: endOfGame is func
  local
    var char: command is ' ';
  begin
    showStatus;
    setPos(screen, BUTTON_TEXT_LINE1 + 5, 104);
    write(screen, "Play Again (Y/N)?");
    setPos(screen, BUTTON_TEXT_LINE1 + 6, 105);
    write(screen, "               ");
    repeat
      command := upper(getc(KEYBOARD));
    until command in {'Y', 'N'};
    if command = 'Y' then
      initGame;
      displayAll;
    end if;
  end func;


const func char: getField (in integer: xPos, in integer: yPos) is func
  result
    var char: field is ' ';
  local
    var integer: line is 0;
    var integer: column is 0;
  begin
    line := succ(yPos div PIXMAP_SIZE);
    column := succ(xPos div PIXMAP_SIZE);
    field := room[player.room].data[line][column];
    if field = ' ' then
      if xPos rem PIXMAP_SIZE <> 0 then
        field := room[player.room].data[line][succ(column)];
        if field = ' ' then
          if yPos rem PIXMAP_SIZE <> 0 then
            field := room[player.room].data[succ(line)][column];
            if field = ' ' then
              field := room[player.room].data[succ(line)][succ(column)];
            end if;
          end if;
        end if;
      else
        if yPos rem PIXMAP_SIZE <> 0 then
          field := room[player.room].data[succ(line)][column];
        end if;
      end if;
    end if;
  end func;


const proc: checkField (inout char: field) is func
  local
    var itemType: item_ident is NO_ITEM;
    var integer: line is 0;
    var integer: column is 0;
  begin
    if field <> ' ' then
      if field = '=' then
        if player.room = COURTYARD then
          message := [] ("The Gate is locked.");
        end if;
        displayMessage;
      elsif field in {'a' .. 'z'} then
        item_ident := itemType conv field;
        if item_ident <> NO_ITEM then
          if card(player.inventory) < MAX_INVENTORY then
            line := succ(item[item_ident].yPos div PIXMAP_SIZE);
            column := succ(item[item_ident].xPos div PIXMAP_SIZE);
            room[player.room].data[line] @:= [column] ' ';
            field := ' ';
            incl(player.inventory, item_ident);
            put(curr_win, BORDER_DIST + pred(column) * PIXMAP_SIZE,
                BORDER_DIST + pred(line) * PIXMAP_SIZE, floor_pixmap, PSET);
            displayLegend;
            displayInventory;
            message := 0 times "";
          else
            message := [] ("take " & item[item_ident].name & ":",
                           "Can't carry any more!");
          end if;
          displayMessage;
        end if;
      end if;
    end if
  end func;


const func char: hitWall (in integer: xPos, in integer: yPos) is func
  result
    var char: field is ' ';
  begin
    field := getField(xPos, yPos);
    checkField(field);
  end func;


const func boolean: checkCollision (in integer: xPos, in integer: yPos) is func
  result
    var boolean: collision is FALSE;
  local
    var char: ch is ' ';
  begin
    ch := getField(xPos, yPos);
    collision := ch <> ' ';
  end func;


const func boolean: hitPlayer (in graphObj: obj1, in graphObj: obj2) is func
  result
    var boolean: hit is FALSE;
  begin
    if abs(obj1.xPos - obj2.xPos) < PIXMAP_SIZE and
        abs(obj1.yPos - obj2.yPos) < PIXMAP_SIZE then
      hit := TRUE;
    end if;
  end func;


const proc: moveMonsters is func
  local
    var integer: x_diff is 0;
    var integer: y_diff is 0;
    var monsterType: current_monster is NO_MONSTER;
    var monsterType: monster_ident is NO_MONSTER;
  begin
    for monster_ident range MIN_MONSTER to MAX_MONSTER do
      if  monster[monster_ident].room_num = player.room and
          monster[monster_ident].living then
        x_diff := monster[monster_ident].xPos - player.xPos;
        y_diff := monster[monster_ident].yPos - player.yPos;

        if abs(x_diff) >= abs(y_diff) then
          if x_diff >= 0 then
            monster[monster_ident].direction := WEST;
          else
            monster[monster_ident].direction := EAST;
          end if;
        else
          if y_diff >= 0 then
            monster[monster_ident].direction := NORTH;
          else
            monster[monster_ident].direction := SOUTH;
          end if;
        end if;

        if monster_ident <= BAT then
          if monster[monster_ident].yPos > player.yPos then
            monster[monster_ident].yPos -:= 8;
            if hitPlayer(player, monster[monster_ident]) then
              current_monster := monster_ident;
              monster[monster_ident].yPos +:= 8;
            elsif checkCollision(monster[monster_ident].xPos, monster[monster_ident].yPos) then
              monster[monster_ident].yPos +:= 8;
            else
              displayObj(monster[monster_ident]);
            end if;
          end if;
          if monster[monster_ident].yPos < player.yPos then
            monster[monster_ident].yPos +:= 8;
            if hitPlayer(player, monster[monster_ident]) then
              current_monster := monster_ident;
              monster[monster_ident].yPos -:= 8;
            elsif checkCollision(monster[monster_ident].xPos, monster[monster_ident].yPos) then
              monster[monster_ident].yPos -:= 8;
            else
              displayObj(monster[monster_ident]);
            end if;
          end if;
          if monster[monster_ident].xPos > player.xPos then
            monster[monster_ident].xPos -:= 8;
            if hitPlayer(player, monster[monster_ident]) then
              current_monster := monster_ident;
              monster[monster_ident].xPos +:= 8;
            elsif checkCollision(monster[monster_ident].xPos, monster[monster_ident].yPos) then
              monster[monster_ident].xPos +:= 8;
            else
              displayObj(monster[monster_ident]);
            end if;
          end if;
          if monster[monster_ident].xPos < player.xPos then
            monster[monster_ident].xPos +:= 8;
            if hitPlayer(player, monster[monster_ident]) then
              current_monster := monster_ident;
              monster[monster_ident].xPos -:= 8;
            elsif checkCollision(monster[monster_ident].xPos, monster[monster_ident].yPos) then
              monster[monster_ident].xPos -:= 8;
            else
              displayObj(monster[monster_ident]);
            end if;
          end if;
        end if;
      end if;
    end for;

    if current_monster > NO_MONSTER then
      if rand(1, 10) <= 4 then
       player.strength -:= monster[current_monster].attack;
        if player.strength < 1 then
          message &:= [] ("The " & monster[current_monster].name & " has killed you!");
          player.living := FALSE;
        else
          message &:= [] ("The " & monster[current_monster].name & " struck you!");
          if rand(1, 2) = 1 and HELMET in player.inventory then
            player.strength +:= monster[current_monster].attack;
            message &:= [] ("The Helmet helped.");
          end if;
        end if;
      else
        message &:= [] ("The " & monster[current_monster].name & " missed you!");
      end if;
      displayMessage;
      message := 0 times "";
    end if;
  end func;


const proc: enterRoom (in integer: room_number) is func
  begin
    message := 0 times "";
    player.room := room_number;
  end func;


const proc: moveDelta (in integer: delta_x, in integer: delta_y) is func
  local
    var char: field is ' ';
    var directionType: stair is NORTH;
    var monsterType: monster_ident is NO_MONSTER;
  begin
    field := hitWall(player.xPos + delta_x, player.yPos + delta_y);
    if field <> ' ' then
      if field = 'U' then
        stair := UP;
      elsif field = 'D' then
        stair := DOWN;
      end if;
      if stair in {UP, DOWN} and room[player.room].exits[stair] <> 0 then
        if room[player.room].exits[stair] in {WINE_CELLAR, DUNGEON_ENTRANCE} and
            not LAMP in player.inventory then
          message := [] ("It's too dark to go that way!");
          displayMessage;
        else
          enterRoom(room[player.room].exits[stair]);
          player.saved_pixmap := PRIMITIVE_WINDOW.value;
          displayAll;
        end if;
      end if;
    else
      monster_ident := hitMonster(player.xPos + delta_x, player.yPos + delta_y);
      if monster_ident <> NO_MONSTER then
        if monster_ident <= BAT then
          if SWORD in player.inventory then
            if rand(1, 10) <= 4 then
              monster[monster_ident].strength -:= rand(10, 19);
              message := [] ("You struck the " & monster[monster_ident].name & "!");
              if monster[monster_ident].strength < 1 then
                message := [] ("You killed the " & monster[monster_ident].name & "!");
                monster[monster_ident].living := FALSE;
                monster[monster_ident].adjective := "Dead";
                displayLegend;
                displayInventory;
              end if;
            else
              message := [] ("You missed the " & monster[monster_ident].name & "!");
            end if;
          else
            message := [] ("You have no Weapon");
          end if;
          displayMessage;
        elsif monster_ident = VAMPIRE then
          message := [] ("The " & monster[monster_ident].name & " is blocking your way.",
                         "He can't be hurt!");
          displayMessage;
        elsif monster_ident = FAIRY1 or
            monster_ident = FAIRY2 then
          message := [] ("The " & monster[monster_ident].name & " is blocking your way.",
                         "She can't be hurt!");
          displayMessage;
        elsif monster_ident = DOOR1 or
            monster_ident = DOOR2 then
          if KEY in player.inventory then
            monster[DOOR1].living := FALSE;
            monster[DOOR2].living := FALSE;
            monster[DOOR1].adjective := "Open";
            message := [] ("You open the door with the key");
            displayMessage;
          else
            message := [] ("The door is locked");
            displayMessage;
          end if;
        elsif monster_ident = DOOR3 or
            monster_ident = DOOR4 then
          if KEY in player.inventory then
            monster[DOOR3].living := FALSE;
            monster[DOOR4].living := FALSE;
            monster[DOOR3].adjective := "Open";
            message := [] ("You open the door with the key");
            displayMessage;
          else
            message := [] ("The door is locked");
            displayMessage;
          end if;
        end if;
      else
        player.xPos +:= delta_x;
        player.yPos +:= delta_y;
        displayObj(player);
      end if;
    end if;
  end func;


const proc: move (in directionType: direction) is func
  begin
    player.direction := direction;
    if player.direction = WEST then
      if player.xPos - STEP_SIZE < 0 then
        if room[player.room].exits[WEST] <> 0 then
          enterRoom(room[player.room].exits[WEST]);
          player.saved_pixmap := PRIMITIVE_WINDOW.value;
          player.xPos := 736;
          displayAll;
        end if;
      else
        moveDelta(-STEP_SIZE, 0);
      end if;
    elsif player.direction = EAST then
      if player.xPos + STEP_SIZE > 736 then
        if room[player.room].exits[EAST] <> 0 then
          enterRoom(room[player.room].exits[EAST]);
          player.saved_pixmap := PRIMITIVE_WINDOW.value;
          player.xPos := 0;
          displayAll;
        end if;
      else
        moveDelta(STEP_SIZE, 0);
      end if;
    elsif player.direction = NORTH then
      if player.yPos - STEP_SIZE < 0 then
        if room[player.room].exits[NORTH] <> 0 then
          enterRoom(room[player.room].exits[NORTH]);
          player.saved_pixmap := PRIMITIVE_WINDOW.value;
          player.yPos := 544;
          displayAll;
        end if;
      else
        moveDelta(0, -STEP_SIZE);
      end if;
    elsif player.direction = SOUTH then
      if player.yPos + STEP_SIZE > 544 then
        if room[player.room].exits[SOUTH] <> 0 then
          enterRoom(room[player.room].exits[SOUTH]);
          player.saved_pixmap := PRIMITIVE_WINDOW.value;
          player.yPos := 0;
          displayAll;
        end if;
      else
        moveDelta(0, STEP_SIZE);
      end if;
    end if;
  end func;


const proc: moveTo (in integer: xPos, in integer: yPos) is func
  local
    var integer: old_x is 0;
    var integer: old_y is 0;
    var integer: old_room is 0;
    var time: curr_time is time.value;
  begin
    if abs(xPos - player.xPos) > STEP_SIZE div 2 or
        abs(yPos - player.yPos) > STEP_SIZE div 2 then
      old_room := player.room;
      repeat
        curr_time := time(NOW);
        old_x := player.xPos;
        old_y := player.yPos;
        if abs(xPos - player.xPos) > abs(yPos - player.yPos) then
          if xPos > player.xPos then
            move(EAST);
          else
            move(WEST);
          end if;
          if abs(yPos - player.yPos) > STEP_SIZE div 2 and
              old_x = player.xPos and old_y = player.yPos then
            if yPos > player.yPos then
              move(SOUTH);
            else
              move(NORTH);
            end if;
          end if;
        else
          if yPos > player.yPos then
            move(SOUTH);
          else
            move(NORTH);
          end if;
          if abs(xPos - player.xPos) > STEP_SIZE div 2 and
              old_x = player.xPos and old_y = player.yPos then
            if xPos > player.xPos then
              move(EAST);
            else
              move(WEST);
            end if;
          end if;
        end if;
        checkRoom;
        moveMonsters;
        DRAW_FLUSH;
        await(curr_time + 30000 . MICRO_SECONDS);
      until abs(xPos - player.xPos) <= STEP_SIZE div 2 and
          abs(yPos - player.yPos) <= STEP_SIZE div 2 or
          old_x = player.xPos and old_y = player.yPos or
          old_room <> player.room or keypressed(KEYBOARD);
    end if;
  end func;


const proc: takeItem (in itemType: item_ident, in string: name,
    in integer: line, in integer: column) is func
  begin
    if item[item_ident].xPos = pred(column) * PIXMAP_SIZE and
        item[item_ident].yPos = pred(line) * PIXMAP_SIZE and
        item[item_ident].room_num = NO_ROOM and
        not item_ident in player.inventory then
      message := [] ("take " & item[item_ident].name);
      displayMessage;
      message := 0 times "";
      moveTo(pred(column) * PIXMAP_SIZE, pred(line) * PIXMAP_SIZE);
      if abs(succ(player.yPos div PIXMAP_SIZE) - line) <= 2 and
          abs(succ(player.xPos div PIXMAP_SIZE) - column) <= 2 then
        if card(player.inventory) < MAX_INVENTORY then
          incl(player.inventory, item_ident);
          displayAll;
        else
          message := [] ("take " & item[item_ident].name & ":",
                         "Can't carry any more!");
        end if;
      else
        message := [] ("take " & item[item_ident].name & ":",
                       "I can't reach it!");
      end if;
    else
      message := [] ("take " & name & ":",
                     upper(name) & "S are too heavy!");
    end if;
  end func;


const proc: takeItem is func
  local
    var char: command is ' ';
    var integer: xPos is 0;
    var integer: yPos is 0;
    var integer: line is 0;
    var integer: column is 0;
    var itemType: item_ident is NO_ITEM;
    var monsterType: monster_ident is NO_MONSTER;
  begin
    displayBox(light_red);
    message := [] ("take");
    displayMessage;
    message := 0 times "";
    command := getc(KEYBOARD);
    xPos := getxpos(KEYBOARD);
    yPos := getypos(KEYBOARD);
    xPos -:= BORDER_DIST;
    yPos -:= BORDER_DIST;
    line := succ(yPos div PIXMAP_SIZE);
    column := succ(xPos div PIXMAP_SIZE);
    if line >= 1 and line <= ROOM_LINES and
        column >= 1 and column <= ROOM_COLUMNS then
      case room[player.room].data[line][column] of
        when {'#', '&', ':', ';'}:
          message := [] ("take wall:",
                         "Impossible!!");
        when {'W', 'R', 'N', 'P', 'Y'}:
          message := [] ("take bed:",
                         "BEDS are too heavy!");
        when {'U', 'D'}:
          message := [] ("take staircase:",
                         "Impossible!!");
        when {'='}:
          message := [] ("take gate:",
                         "Impossible!!");
        when {'>'}:
          message := [] ("take balcony:",
                         "Impossible!!");
        when {'?'}:
          if player.room = GARDEN_SOUTH then
            takeItem(LARGE_GEM, "fountain", line, column);
          elsif player.room = BALCONY then
            message := [] ("take fountain:",
                           "FOUNTAINS are too heavy!");
          end if;
        when {'{'}:
          message := [] ("take chains:",
                         "They're Connected to the wall.");
        when {'$'}:
          takeItem(NECKLACE, "statue", line, column);
        when {'%'}:
          if player.room = KITCHEN then
            message := [] ("take table:",
                           "TABLES are too heavy!");
          elsif player.room = THRONE_ROOM then
            message := [] ("take throne:",
                           "THRONES are too heavy!");
          end if;
        when {'@'}:
          if player.room = CHEFS_QUARTERS then
            takeItem(WINE_FLASK, "desk", line, column);
          elsif player.room = YELLOW_ROOM then
            takeItem(GLASSES, "desk", line, column);
          elsif player.room = KINGS_STUDY then
            takeItem(KEY, "desk", line, column);
          elsif player.room = LIBRARY_WEST_END then
            takeItem(BOOK, "shelve", line, column);
          elsif player.room = LIBRARY_EAST_END then
            message := [] ("take shelve:",
                           "SHELVES are too heavy!");
          elsif player.room = STORAGE_ROOM then
            message := [] ("take shelve:",
                           "SHELVES are too heavy!");
          elsif player.room = WINE_CELLAR then
            message := [] ("take barrel:",
                           "BARRELS are too heavy!");
          elsif player.room = TORTURE_ROOM then
            message := [] ("take table:",
                           "TABLES are too heavy!");
          end if;
        when {'|'}:
          if player.room = SORCERERS_QUARTERS then
            message := [] ("take mirror:",
                           "MIRRORS are too heavy!");
          elsif player.room = BALCONY then
            message := [] ("take balcony:",
                           "Impossible!!");
          end if;
        when {'+', '*'}:
          message := [] ("take bush:",
                         "Impossible!!");
        when {'a' .. 'z'}:
          item_ident := itemType conv (room[player.room].data[line][column]);
          if item_ident <> NO_ITEM then
            message := [] ("take " & item[item_ident].name);
            displayMessage;
            message := 0 times "";
            moveTo(pred(column) * PIXMAP_SIZE, pred(line) * PIXMAP_SIZE);
          end if;
        when {' '}:
          monster_ident := searchMonster(xPos, yPos);
          if monster_ident <> NO_MONSTER then
            message := [] ("take " & monster[monster_ident].name & ":",
                           "I don't think that would be very safe!");
          elsif xPos >= player.xPos and
              xPos < player.xPos + PIXMAP_SIZE and
              yPos >= player.yPos and
              yPos < player.yPos + PIXMAP_SIZE then
            message := [] ("take player:",
                           "Impossible!!");
          else
            message := [] ("take floor:",
                           "Impossible!!");
          end if;
      end case;
    end if;
    displayMessage;
    displayBox(white);
  end func;


const proc: quitGame is func
  begin
    if isOkay([]("Quit the castle game?")) then
      exitGame := TRUE;
    end if;
  end func;


const proc: playGame is func
  local
    var char: command is ' ';
    var integer: xPos is 0;
    var integer: yPos is 0;
  begin
    displayAll;
    while player.living and not exitGame do

      command := getc(KEYBOARD);
      case command of
        when {KEY_LEFT}:  move(WEST);
        when {KEY_RIGHT}: move(EAST);
        when {KEY_UP}:    move(NORTH);
        when {KEY_DOWN}:  move(SOUTH);
        when {KEY_ESC}:   bossMode(exitGame);
        when {'Q', 'q', KEY_CLOSE}: quitGame;
        when {KEY_MOUSE1}:
          xPos := getxpos(KEYBOARD);
          yPos := getypos(KEYBOARD);
          if xPos >= BUTTONS_1_XPOS and xPos < BUTTONS_1_XPOS + 80 and
            yPos >= BUTTON_YPOS_MIN and yPos <= BUTTON_YPOS_MIN + 3 * 32 then
            case (yPos - BUTTON_YPOS_MIN) div 32 of
              when {0}: loadGame;
              when {1}: saveGame;
              when {2}: quitGame;
            end case;
          end if;
          if xPos >= BUTTONS_2_XPOS and xPos < BUTTONS_2_XPOS + 80 and
              yPos >= BUTTON_YPOS_MIN and yPos <= BUTTON_YPOS_MIN + 2 * 32 then
            case (yPos - BUTTON_YPOS_MIN) div 32 of
              when {0}: takeItem;
              when {1}: dropItem;
            end case;
          end if;
          if xPos >= BUTTONS_3_XPOS and xPos < BUTTONS_3_XPOS + 80 and
              yPos >= BUTTON_YPOS_MIN and yPos <= BUTTON_YPOS_MIN + 2 * 32 then
            case (yPos - BUTTON_YPOS_MIN) div 32 of
              when {0}: lookItem;
              when {1}: useItem;
            end case;
          end if;
          if xPos < 2 * BORDER_DIST + ROOM_COLUMNS * PIXMAP_SIZE and
              yPos < 2 * BORDER_DIST + ROOM_LINES * PIXMAP_SIZE then
            moveTo(xPos - BORDER_DIST - STEP_SIZE, yPos - BORDER_DIST - STEP_SIZE);
          end if;
      end case;

      checkRoom;

      moveMonsters;

      if player.room = ESCAPED or not player.living then
        exitGame := TRUE;
        endOfGame;
      end if;
    end while;
  end func;


const proc: main is func
  local
    var char: command is ' ';
  begin
    screen(1024, 768);
    setCloseAction(curr_win, RETURN_KEY);
    clear(curr_win, white);
    screen := open(curr_win, 16);
    KEYBOARD := GRAPH_KEYBOARD;
    initGame;
    welcomeScreen;
    command := getc(KEYBOARD);
    if upper(command) <> 'Q' and command <> KEY_CLOSE and command <> KEY_ESC then
      playGame;
    end if;
  end func;
