/*
 *  U6UseCode.cpp
 *  Nuvie
 *
 *  Created by Eric Fry on Fri May 30 2003.
 *  Copyright (c) 2003. All rights reserved.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
#include <cstdlib>
#include <cassert>
#include <cstdio>
#include "nuvieDefs.h"
#include "U6LList.h"
#include "U6misc.h"
#include "MapEntity.h"

#include "Game.h"
#include "View.h"
#include "ViewManager.h"
#include "ActorManager.h"
#include "Actor.h"
#include "U6Actor.h"
#include "Party.h"
#include "Player.h"
#include "MsgScroll.h"
#include "Map.h"
#include "GameClock.h"
#include "Book.h"
#include "Event.h"
#include "MapWindow.h"
#include "TimedEvent.h"
#include "EggManager.h"
#include "AnimManager.h"
#include "SoundManager.h"
#include "Effect.h"
#include "Weather.h"
#include "Script.h"
#include "Keys.h"
#include "Background.h"
#include "CommandBar.h"

#include "U6UseCode.h"
#include "U6ObjectTypes.h"
#include "U6WorkTypes.h"

#include <math.h> // floorf (used in part which I think ought to be moved out of here)

#define MESG_ANIM_HIT_WORLD  ANIM_CB_HIT_WORLD
#define MESG_ANIM_HIT        ANIM_CB_HIT
#define MESG_TEXT_READY      MSGSCROLL_CB_TEXT_READY
#define MESG_DATA_READY      CB_DATA_READY
#define MESG_EFFECT_COMPLETE EFFECT_CB_COMPLETE
#define MESG_TIMED           CB_TIMED
#define MESG_INPUT_CANCELED  CB_INPUT_CANCELED

// numbered by entrance quality, "" = no name
static const char *u6_dungeons[21] =
{
    "",
    "Deceit",
    "Despise",
    "Destard",
    "Wrong",
    "Covetous",
    "Shame",
    "Hythloth",
    "GSA",
    "Control",
    "Passion",
    "Diligence",
    "Tomb of Kings",
    "Ant Mound",
    "Swamp Cave",
    "Spider Cave",
    "Cyclops Cave",
    "Heftimus Cave",
    "Heroes' Hole",
    "Pirate Cave",
    "Buccaneer's Cave"
};

// Red moongate teleport locations.
static const struct { uint16 x; uint16 y; uint8 z; } red_moongate_tbl[] =
{ {0x0,   0x0,   0x0},
  {0x383, 0x1f3, 0x0},
  {0x3a7, 0x106, 0x0},
  {0x1b3, 0x18b, 0x0},
  {0x1f7, 0x166, 0x0},
  {0x93,  0x373, 0x0},
  {0x397, 0x3a6, 0x0},
  {0x44,  0x2d,  0x5},
  {0x133, 0x160, 0x0},
  {0xbc,  0x2d,  0x5},
  {0x9f,  0x3ae, 0x0},
  {0x2e3, 0x2bb, 0x0},
  {0x0,   0x0,   0x0},
  {0x0,   0x0,   0x0},
  {0x0,   0x0,   0x0},
  {0xe3,  0x83,  0x0},
  {0x17,  0x16,  0x1},
  {0x80,  0x56,  0x5},
  {0x6c,  0xdd,  0x5},
  {0x39b, 0x36c, 0x0},
  {0x127, 0x26,  0x0},
  {0x4b,  0x1fb, 0x0},
  {0x147, 0x336, 0x0},
  {0x183, 0x313, 0x0},
  {0x33f, 0xa6,  0x0},
  {0x29b, 0x43,  0x0}
 };

#define USE_U6_POTION_BLUE   0x0
#define USE_U6_POTION_RED    0x1
#define USE_U6_POTION_YELLOW 0x2
#define USE_U6_POTION_GREEN  0x3
#define USE_U6_POTION_ORANGE 0x4
#define USE_U6_POTION_PURPLE 0x5
#define USE_U6_POTION_BLACK  0x6
#define USE_U6_POTION_WHITE  0x7

// numbered by potion object frame number
static const char *u6_potions[8] =
{
    "an awaken", // blue
    "a cure", // red
    "a heal", // yellow
    "a poison", // green
    "a sleep", // orange
    "a protection", // purple
    "an invisibility", // black
    "an xray vision" // white
};

// convenient macro to grab a target object - when first called the player will
// be prompted with P, and upon target selection the object will be set to O
#define USECODE_SELECT_OBJ(O, P) \
{ \
    static bool selected_obj = false; \
    if(!selected_obj) \
    { \
        game->get_event()->get_direction(P); \
        game->get_event()->request_input(this, obj); \
        selected_obj = true; \
        return false; \
    } \
    else { O = items.obj_ref; selected_obj = false; } \
}
#define USECODE_SELECT_ACTOR(O, P) \
{ \
    static bool selected_obj = false; \
    if(!selected_obj) \
    { \
        game->get_event()->get_target(P); \
        game->get_event()->request_input(this, obj); \
        selected_obj = true; \
        return false; \
    } \
    else { A = items.actor2_ref; selected_obj = false; } \
}
#define USECODE_SELECT_TARGET(T, P) \
{ \
    static bool selected_obj = false; \
    if(!selected_obj) \
    { \
        game->get_event()->get_target(P); \
        game->get_event()->request_input(this, obj); \
        selected_obj = true; \
        return false; \
    } \
    else { T = items.mapcoord_ref; selected_obj = false; } \
}
#define USECODE_SELECT_DIRECTION(T, P) \
{ \
    static bool selected_obj = false; \
    if(!selected_obj) \
    { \
        game->get_event()->get_target(P); \
        game->get_event()->request_input(this, obj); \
        selected_obj = true; \
        return false; \
    } \
    else { T = items.mapcoord_ref; selected_obj = false; } \
}


U6UseCode::U6UseCode(Game *g, Configuration *cfg) : UseCode(g, cfg)
{
}

U6UseCode::~U6UseCode()
{
}


/* Is the object a food (or drink) item? */
bool U6UseCode::is_food(Obj *obj)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n);
    return(type && (type->flags & OBJTYPE_FOOD));
}


bool U6UseCode::is_container(Obj *obj)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n);
    return(type && (type->flags & OBJTYPE_CONTAINER));
}

bool U6UseCode::is_container(uint16 obj_n, uint8 frame_n)
{
    const U6ObjectType *type = get_object_type(obj_n, frame_n);
    return(type && (type->flags & OBJTYPE_CONTAINER));
}

bool U6UseCode::is_readable(Obj *obj)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n);
    return((type && (type->flags & OBJTYPE_BOOK)) || obj->obj_n == OBJ_U6_CLOCK
           || obj->obj_n == OBJ_U6_SUNDIAL);
}


/* Is there `ev' usecode for an object? */
bool U6UseCode::has_usecode(Obj *obj, UseCodeEvent ev)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, ev);
    if(!type && !UseCode::has_usecode(obj, ev))
        return(false);
    return(true);
}

bool U6UseCode::has_usecode(Actor *actor, UseCodeEvent ev)
{
    const U6ObjectType *type = get_object_type(actor->get_obj_n(), actor->get_frame_n(), ev);
    if(!type || type->flags == OBJTYPE_CONTAINER)
        return(false);
    return(true);
}

/* USE object. Actor is the actor using the object. */
bool U6UseCode::use_obj(Obj *obj, Actor *actor)
{
    if(UseCode::has_usecode(obj, USE_EVENT_USE)) //use script based usecode.
    {
      return UseCode::use_obj(obj, actor);
    }

    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_USE);
    set_itemref(actor, items.actor2_ref);
    return(uc_event(type, USE_EVENT_USE, obj));
}

/* LOOK at object. Actor is the actor looking at the object. */
bool U6UseCode::look_obj(Obj *obj, Actor *actor)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_LOOK);
    set_itemref(actor);
    return(uc_event(type, USE_EVENT_LOOK, obj));
}

/* PASS object. Actor is the actor trying to pass the object. It takes
   target coordinates in case the object has multiple tiles. */
bool U6UseCode::pass_obj(Obj *obj, Actor *actor, uint16 x, uint16 y)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_PASS);
    static MapCoord loc;
    loc.x = x; loc.y = y;
    loc.z = obj->z;
    set_itemref(actor);
    set_itemref(&loc);
    return(uc_event(type, USE_EVENT_PASS, obj));
}

/* SEARCH nearby object. Actor is the actor searching. */
bool U6UseCode::search_obj(Obj *obj, Actor *actor)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_SEARCH);
    set_itemref(actor);
    return(uc_event(type, USE_EVENT_SEARCH, obj));
}


/* Callback from timer or other class. User_data is the object which will
 * receive the message (if applicable). */
uint16 U6UseCode::callback(uint16 msg, CallBack *caller, void *msg_data)
{
    Obj *obj = (Obj *)callback_user_data;
    if(!obj)
    {
        DEBUG(0,LEVEL_ERROR, "UseCode: internal message %d sent to NULL object\n", msg);
        return(0);
    }
    return(message_obj(obj, (CallbackMessage)msg, msg_data));
}


/* Call MESSAGE function for an object. Msg_data is assigned to the appropriate
 * itemref. The USE function is called in response to a DATA_READY message.
 * Returns false if there is no usecode for that object.
 */
bool U6UseCode::message_obj(Obj *obj, CallbackMessage msg, void *msg_data)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_MESSAGE);
    items.msg_ref = &msg;
    switch(msg) // set itemref from msgdata
    {
        case MESG_TIMED:
            items.uint_ref = (uint32 *)msg_data;
            break;
        case MESG_ANIM_HIT_WORLD:
            items.mapcoord_ref = (MapCoord *)msg_data;
            break;
        case MESG_ANIM_HIT:
            items.ent_ref = (MapEntity *)msg_data;
            break;
        case MESG_TEXT_READY:
            items.string_ref = (string *)msg_data;
            break;
        case MESG_DATA_READY:
            items.data_ref = (char *)msg_data; // pointer to EventInput structure
            items.obj_ref = ((EventInput*)items.data_ref)->obj;
            items.actor2_ref = ((EventInput*)items.data_ref)->actor;
            items.mapcoord_ref = ((EventInput*)items.data_ref)->loc;
            items.string_ref = ((EventInput*)items.data_ref)->str;
            return uc_event(get_object_type(obj->obj_n,obj->frame_n),
                            USE_EVENT_USE, obj);
        case MESG_INPUT_CANCELED:
            return uc_event(get_object_type(obj->obj_n,obj->frame_n),
        	                USE_EVENT_INPUT_CANCEL, obj);
        default : break;
    }

 return uc_event(type, USE_EVENT_MESSAGE, obj);
}


/* MOVE nearby object in a relative direction. */
bool U6UseCode::move_obj(Obj *obj, sint16 rel_x, sint16 rel_y)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_MOVE);
    static MapCoord dir;
    dir.sx = rel_x;
    dir.sy = rel_y;
    set_itemref(&dir);
    return(uc_event(type, USE_EVENT_MOVE, obj));
}


/* Call post-LOAD or UNLOAD usecode for an object.
 * Returns false if there is no usecode for that object. */
bool U6UseCode::load_obj(Obj *obj)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_LOAD);
    return(uc_event(type, USE_EVENT_LOAD, obj));
}


/* Call READY or UNREADY usecode for an object. */
bool U6UseCode::ready_obj(Obj *obj, Actor *actor)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_READY);
    set_itemref(actor);
    return(uc_event(type, USE_EVENT_READY, obj));
}


/* Call GET usecode for an object. */
bool U6UseCode::get_obj(Obj *obj, Actor *actor)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_GET);
    set_itemref(actor);
    return(uc_event(type, USE_EVENT_GET, obj));
}


/* Call DROP usecode for an object. */
bool U6UseCode::drop_obj(Obj *obj, Actor *actor, uint16 x, uint16 y, uint16 qty)
{
    const U6ObjectType *type = get_object_type(obj->obj_n, obj->frame_n, USE_EVENT_DROP);
    static MapCoord loc; // use statics for pointers
    static uint32 obj_qty;
    loc.x = x;
    loc.y = y;
    set_itemref(actor);
    set_itemref(&loc);
    items.uint_ref = &obj_qty;
    return(uc_event(type, USE_EVENT_DROP, obj));
}


/* Return pointer to object-type in list for object N:F, or NULL if none. */
inline const U6ObjectType *U6UseCode::get_object_type(uint16 n, uint8 f, UseCodeEvent ev)
{
    const U6ObjectType *type = U6ObjectTypes;
    while(type->obj_n != OBJ_U6_NOTHING)
    {
        if(type->obj_n == n && (type->frame_n == 0xFF || type->frame_n == f)
           && ((type->trigger & ev) || ev == 0))
            return(type);
        ++type;
    }
    return(NULL);
}


/* Call usecode function of the U6ObjectType, with event `ev', for `obj'.
 * The meaning of the return value is different for each event.
 * Returns false if the type is invalid or doesn't respond to the event.
 */
bool U6UseCode::uc_event(const U6ObjectType *type, UseCodeEvent ev, Obj *obj)
{
    if(!type || type->obj_n == OBJ_U6_NOTHING)
        return(false);
    if(type->trigger & ev)
    {
        dbg_print_event(ev, obj);
        bool ucret = (this->*type->usefunc)(obj, ev);
        clear_items(); // clear references for next call
        return(ucret); // return from usecode function
    }
    return(false); // doesn't respond to event
}


void U6UseCode::lock_door(Obj *obj)
{
    if(is_unlocked_door(obj))
        obj->frame_n += 4;
}

void U6UseCode::unlock_door(Obj *obj)
{
    if(is_locked_door(obj))
        obj->frame_n -= 4;
}

void U6UseCode::unlock(Obj *obj)
{
	if(is_locked_door(obj))
	{
		unlock_door(obj);
	}
	else if(is_locked_chest(obj))
	{
		unlock_chest(obj);
	}
}

void U6UseCode::lock(Obj *obj)
{
	if(is_magically_locked(obj) || is_locked(obj))
		return;

	if(is_closed_door(obj))
	{
		lock_door(obj);
	}
	else if(is_closed_chest(obj))
	{
		lock_chest(obj);
	}
}

// USE: unlock locked doors, open/close other doors
bool U6UseCode::use_door(Obj *obj, UseCodeEvent ev)
{
 Obj *key_obj;
 bool print = (items.actor_ref == player->get_actor());

 if(is_magically_locked_door(obj))
   {
    if(print) scroll->display_string("\nmagically locked\n");
    return true;
   }

 if(is_locked_door(obj)) // locked door
   {
    key_obj = player->get_actor()->inventory_get_object(OBJ_U6_KEY, obj->quality);
    if(obj->quality != 0 && key_obj != NULL) // we have the key for this door so lets unlock it.
      {
       unlock_door(obj);
       if(print) scroll->display_string("\nunlocked\n");
      }
    else
       if(print) scroll->display_string("\nlocked\n");

    return true;
   }

 if(obj->frame_n <= 3) //the door is open
   {
    if(!map->is_passable(obj->x,obj->y,obj->z) || map->actor_at_location(obj->x, obj->y, obj->z)) //don't close door if blocked
      {
       if(print) scroll->display_string("\nNot now!\n");
      }
    else //close the door
      {
       obj->frame_n += 4;
       if(print) scroll->display_string("\nclosed!\n");
      }
   }
 else
   {
	process_effects(obj, items.actor_ref); //process traps.
    obj->frame_n -= 4;
    if(print) scroll->display_string("\nopened!\n");
   }

 return true;
}

// USE: climb up or down a ladder (entire party)
bool U6UseCode::use_ladder(Obj *obj, UseCodeEvent ev)
{
 uint16 x = obj->x, y = obj->y;
 uint8 z;
 if(!player->in_party_mode())
  {
    scroll->display_string("\nNot in solo mode.\n");
    return true;
  }

  if(UseCode::out_of_use_range(obj, true))
    return true;

 if(obj->frame_n == 0) // DOWN
   {
    if(obj->z == 0)//handle the transition from the surface to the first dungeon level
      {
       x = (obj->x & 0x07) | (obj->x >> 2 & 0xF8);
       y = (obj->y & 0x07) | (obj->y >> 2 & 0xF8);
      }
    z = obj->z + 1;
   }
 else //UP
   {
    if(obj->z == 1)//we use obj->quality to tell us which surface chunk to come up in.
      {
       x = obj->x / 8 * 8 * 4 + ((obj->quality & 0x03) * 8) + (obj->x - obj->x / 8 * 8);
       y = obj->y / 8 * 8 * 4 + ((obj->quality >> 2 & 0x03) * 8) + (obj->y - obj->y / 8 * 8);
      }
    z = obj->z - 1;
   }

 party->dismount_from_horses();

 MapCoord ladder(obj->x, obj->y, obj->z), destination(x, y, z);
 party->walk(&ladder, &destination, 100);
 if(z != 0 && z != 5)
     game->get_weather()->set_wind_dir(NUVIE_DIR_NONE);
 return true;
}


// USE: Open the passhtrough! Close the passthrough!
bool U6UseCode::use_passthrough(Obj *obj, UseCodeEvent ev)
{
 uint16 new_x, new_y;
 uint8 new_frame_n;
 char action_string[6]; // either 'Open' or 'Close'
 bool print = (items.actor_ref == player->get_actor());

 new_x = obj->x;
 new_y = obj->y;
 new_frame_n = obj->frame_n;

 if(obj->frame_n < 2) // the pass through is currently closed.
  {
   if(obj->obj_n == OBJ_U6_V_PASSTHROUGH)
     new_y--;
   else // OBJ_U6_H_PASSTHROUGH
     new_x--;

   new_frame_n = 2; // open the pass through
   strcpy(action_string,"Open");
  }
 else // the pass through is currently open.
  {
   if(obj->obj_n == OBJ_U6_V_PASSTHROUGH)
     new_y++;
   else // OBJ_U6_H_PASSTHROUGH
     new_x++;

   new_frame_n = 0; // close the pass through
   strcpy(action_string,"Close");
  }

 if(!map->actor_at_location(new_x, new_y, obj->z))
    {
     obj_manager->move(obj, new_x, new_y, obj->z);
     obj->frame_n = new_frame_n;
     if(print)
       {
        scroll->display_string("\n");
        scroll->display_string(action_string);
        scroll->display_string(" the passthrough.\n");
       }
    }
 else if(print)
    scroll->display_string("\nNot now!\n");

 return true;
}

// for use with levers and switches, target_obj_n is either OBJ_U6_PORTCULLIS or OBJ_U6_ELECTRIC_FIELD
bool U6UseCode::use_switch(Obj *obj, UseCodeEvent ev)
{
 Obj *doorway_obj;
 Obj *portc_obj;
 U6LList *obj_list;
 U6Link *link;
 uint16 target_obj_n = 0;
 const char *message = NULL;
 const char *fail_message = NULL;
 bool success = false;
 bool print = (items.actor_ref == player->get_actor());

 if(obj->obj_n == OBJ_U6_LEVER)
 {
     target_obj_n = OBJ_U6_PORTCULLIS;
     message = "\nSwitch the lever, you hear a noise.\n";
     fail_message = "\nSwitch the lever, strange, nothing happened.\n";
 }
 else if(obj->obj_n == OBJ_U6_SWITCH)
 {
     if(obj->quality == 113 && obj->x == 139 && obj->y == 0 && obj->z == 1) // hack for Covetous
     {
         doorway_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DOORWAY, 0, 0, 160, 3, 1);
         if(doorway_obj)
             doorway_obj->quality = 113;
     }
     target_obj_n = OBJ_U6_ELECTRIC_FIELD;
     message = "\nOperate the switch, you hear a noise.\n";
     fail_message = "\nOperate the switch, strange, nothing happened.\n";
 }

 doorway_obj = obj_manager->find_obj(obj->z, OBJ_U6_DOORWAY, obj->quality);
 for(;doorway_obj != NULL;doorway_obj = obj_manager->find_next_obj(obj->z, doorway_obj))
   {
    obj_list = obj_manager->get_obj_list(doorway_obj->x,doorway_obj->y,doorway_obj->z);

    for(portc_obj=NULL,link=obj_list->start();link != NULL;link=link->next) // find existing portcullis.
     {
      if(((Obj *)link->data)->obj_n == target_obj_n)
        {
         portc_obj = (Obj *)link->data;
         break;
        }
     }
    success = true;

    if(portc_obj == NULL) //no barrier object, so lets create one.
     {
      portc_obj = obj_manager->copy_obj(doorway_obj);
      portc_obj->obj_n = target_obj_n;
      portc_obj->quality = 0;
      if(target_obj_n == OBJ_U6_PORTCULLIS)
        {
         if(portc_obj->frame_n == 9) //FIX Hack for cream buildings might need one for virt wall.
           portc_obj->frame_n = 1;
        }
      else
         portc_obj->frame_n = 0;

      obj_manager->add_obj(portc_obj,true);
     }
    else //delete barrier object.
     {
      obj_list->remove(portc_obj);
      delete_obj(portc_obj);
     }
   }

 toggle_frame(obj);
 if(print)
   scroll->display_string(success ? message : fail_message);
 return true;
}


/* USE: light or douse various fire objects (toggling their frame number)
 */
bool U6UseCode::use_firedevice(Obj *obj, UseCodeEvent ev)
{
    if(obj->obj_n == OBJ_U6_BRAZIER && obj->frame_n == 2)
        return(true); // holy flames can't be doused
    if(obj->obj_n == OBJ_U6_FIREPLACE)
    {
        if(obj->frame_n == 1 || obj->frame_n == 3)
        {
            use_firedevice_message(obj,false);
            obj->frame_n--;
        }
        else
        {
            use_firedevice_message(obj,true);
            obj->frame_n++;
        }
    }
    else
    {
        toggle_frame(obj);
        use_firedevice_message(obj,(bool)obj->frame_n);
    }
    return(true);
}


/* SEARCH: discover and open door
 * USE: open or close door
 */
bool U6UseCode::use_secret_door(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_USE)
    {
        if(obj->frame_n == 1 || obj->frame_n == 3)
            obj->frame_n--;
        else
            obj->frame_n++;
        return(true);
    }
    else if(ev == USE_EVENT_SEARCH)
    {
        scroll->display_string("a secret door");
        if(obj->frame_n == 0 || obj->frame_n == 2)
            obj->frame_n++;
        return(true);
    }
    return(true);
}


/* Use: Open/close container. If container is open, Search.
 * Search: Dump container contents.
 */
bool U6UseCode::use_container(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_USE)
    {
    	if(is_locked_chest(obj) || is_magically_locked_chest(obj))
    	{
    		if(is_locked_chest(obj) && obj->quality != 0)
    		{
    		    Obj *key_obj = player->get_actor()->inventory_get_object(OBJ_U6_KEY, obj->quality);
    		    if(key_obj != NULL) // we have the key for this chest so lets unlock it.
    		      {
    		       unlock_chest(obj);
    		       scroll->display_string("\nunlocked\n");
    		       return true;
    		      }
    		}

    		scroll->display_string("\nNo effect\n");
    		return true;
    	}
        if((obj->obj_n == OBJ_U6_CHEST && !obj->is_in_inventory()) || obj->obj_n == OBJ_U6_CRATE || obj->obj_n == OBJ_U6_BARREL)
            toggle_frame(obj); //open / close object
        if(obj->frame_n == 0 || (obj->obj_n != OBJ_U6_CHEST && obj->obj_n != OBJ_U6_CRATE && obj->obj_n != OBJ_U6_BARREL)
           || (obj->obj_n == OBJ_U6_CHEST && obj->frame_n < 2 && obj->is_in_inventory()))
        {
            process_effects(obj, items.actor_ref); //run any effects that might be stored in this container. Eg Poison explosion.

            if(Game::get_game()->doubleclick_opens_containers() && obj->obj_n != OBJ_U6_DEER
               && obj->obj_n != OBJ_U6_SHIP && obj->obj_n != OBJ_U6_STONE_LION) // just search for these
            {
            	game->get_view_manager()->open_container_view(obj);
            }
            else if(obj->is_in_inventory())
            {
            	scroll->display_string("\nNot usable\n");
            	return true;
            }
            else
            {
            	scroll->display_string("\nSearching here, you find ");
            	bool found_objects = search_obj(obj, items.actor_ref);
            	scroll->display_string(found_objects ? ".\n" : "nothing.\n");
            }
        }
        return(true);
    }
    else if(ev == USE_EVENT_SEARCH) // search message already printed
    {
        return(UseCode::search_container(obj));
//        if(obj->container && obj->container->end())
//        {
//            new TimedContainerSearch(obj);
//            return(true);
//        }
    }
    else if(ev == USE_EVENT_GET)
    {
    	if(is_chest(obj) && obj->frame_n == 0) //open chest
    		obj->frame_n = 1; //close the chest
    	return true;
    }
    return(false);
}

/* Use rune to free shrine */
bool U6UseCode::use_rune(Obj *obj, UseCodeEvent ev)
{
 char mantras[][8] = {"AHM", "MU", "RA", "BEH", "CAH", "SUMM", "OM", "LUM"};
 Obj *force_field = NULL;
 uint16 rune_obj_offset = obj->obj_n - OBJ_U6_RUNE_HONESTY;
 MapCoord player_location = player->get_actor()->get_location();


 scroll->cancel_input_request();

 if(ev == USE_EVENT_USE)
   {
    scroll->display_string("Mantra: ");

    scroll->set_input_mode(true, NULL, true);
    scroll->request_input(this, obj);

    return(false);
   }
 else if(ev == USE_EVENT_MESSAGE && items.string_ref)
   {
    scroll->display_string("\n");

    char *mantra = new char[items.string_ref->size() + 1];
    strcpy(mantra, items.string_ref->c_str());

    if(strcasecmp(mantra,  mantras[rune_obj_offset]) == 0)
      {
       // find the matching force field for this shrine. match rune offset against force field quality
       force_field = obj_manager->find_obj(player_location.z, OBJ_U6_FORCE_FIELD, rune_obj_offset);

       // make sure the player is right next to the force field.
       if(force_field && abs(player_location.x - force_field->x) < 2 && abs(player_location.y - force_field->y) < 2)
         {
    	  game->get_sound_manager()->playSfx(NUVIE_SFX_CASTING_MAGIC_P1, SFX_PLAY_ASYNC);
    	  game->get_sound_manager()->playSfx(NUVIE_SFX_CASTING_MAGIC_P2, SFX_PLAY_SYNC);
    	  AsyncEffect *e = new AsyncEffect(new XorEffect(1000));
    	  e->run();

          remove_gargoyle_egg(force_field->x,force_field->y,force_field->z);

          obj_manager->remove_obj_from_map(force_field);
          delete force_field;


          scroll->display_string("\nDone!\n");
         }
       else
          scroll->display_string("\nNo effect!\n");
      }
    else
       scroll->display_string("\nWrong mantra!\n");

    scroll->display_string("\n");
    scroll->display_prompt();
    delete[] mantra;
   }

 return true;
}

void U6UseCode::remove_gargoyle_egg(uint16 x, uint16 y, uint8 z)
{
	std::list<Egg *> *egg_list;
	std::list<Egg *>::iterator egg_itr;

	 egg_list = game->get_egg_manager()->get_egg_list();

	 for(egg_itr = egg_list->begin(); egg_itr != egg_list->end();)
	 {
		 Egg *egg = *egg_itr;
		 egg_itr++;

		 Obj *egg_obj = egg->obj;

		 if(abs(x - egg_obj->x) < 20 && abs(y - egg_obj->y) < 20 && z == egg_obj->z)
		 {
			 if(egg_obj->find_in_container(OBJ_U6_GARGOYLE, 0, false, 0, false) || egg_obj->find_in_container(OBJ_U6_WINGED_GARGOYLE, 0, false, 0, false))
			 {
				 DEBUG(0, LEVEL_DEBUGGING, "Removed egg at (%x,%x,%x)", egg_obj->x, egg_obj->y, egg_obj->z);
				 game->get_egg_manager()->remove_egg(egg_obj, false);
				 obj_manager->unlink_from_engine(egg_obj);
				 delete_obj(egg_obj);

			 }
		 }
	 }
}

bool U6UseCode::use_vortex_cube(Obj *obj, UseCodeEvent ev)
{
 if(ev == USE_EVENT_SEARCH)
    return UseCode::search_container(obj);

 Obj *britannian_lens, *gargoyle_lens;
 Obj *container_obj;
 Obj *codex;
 U6Link *link;
 uint8 moonstone_check = 0;
 MapCoord player_location = player->get_actor()->get_location();
 codex = obj_manager->find_obj(player_location.z, OBJ_U6_CODEX, 128); // 128 = codex's book id

 if(Game::get_game()->doubleclick_opens_containers() && (obj->is_in_inventory() || !codex
     || abs(player_location.x - codex->x) > 11 || abs(player_location.y - codex->y) > 11)) //FIXME this should probably be mapwindow size)
 {
    game->get_view_manager()->open_container_view(obj);
    return true;
 }
 if(obj->container != NULL || player_location.z == 0)  // make sure we've got all 8 moonstones in our vortex cube.
   {
    britannian_lens = obj_manager->find_obj(player_location.z, OBJ_U6_BRITANNIAN_LENS, 0, OBJ_NOMATCH_QUALITY);
    gargoyle_lens = obj_manager->find_obj(player_location.z, OBJ_U6_GARGOYLE_LENS, 0, OBJ_NOMATCH_QUALITY);
        
    // make sure the player is close to the codex
    if(codex && abs(player_location.x - codex->x) < 11 && abs(player_location.y - codex->y) < 11) //FIXME this should probably be mapwindow size
      {
       // check that the lenses are in the correct place.
       if(britannian_lens && gargoyle_lens && 
          britannian_lens->x == 0x399 && britannian_lens->y == 0x353 && britannian_lens->z == 0 &&
          gargoyle_lens->x == 0x39d && gargoyle_lens->y == 0x353 && gargoyle_lens->z == 0)
         {
          for(link = obj->container->start(); link != NULL; link = link->next)
            {
             container_obj = (Obj *)link->data;
             if(container_obj->obj_n == OBJ_U6_MOONSTONE)
               {
                moonstone_check |= 1 << container_obj->frame_n;
               }
            }

          if(moonstone_check == 0xff) // have we got all 8 moonstones?
            {
             obj_manager->remove_obj_from_map(codex);
             delete codex;
          
             scroll->display_string("\nThe Codex has vanished!\n");

             //FIXME put weird swirl effect in here.

             game->get_map_window()->Hide();
             game->get_scroll()->Hide();
             game->get_background()->Hide();
             game->get_command_bar()->Hide();
             game->get_event()->close_gumps();
             if(game->get_view_manager()->get_current_view())
                 game->get_view_manager()->get_current_view()->Hide();

             game->get_script()->play_cutscene("/ending.lua");
             game->quit();

             return(true);
            }
         }
      }
   }

 DEBUG(0,LEVEL_DEBUGGING,"moonstone_check = %d\n", moonstone_check);  
 scroll->display_string("\nNo Effect!\n");
 
 return true;
}


/* Use bell or pull-chain, ring and animate nearby bell.
 */
bool U6UseCode::use_bell(Obj *obj, UseCodeEvent ev)
{
    Obj *bell = NULL;
    if(ev != USE_EVENT_USE)
        return(false);
    if(obj->obj_n == OBJ_U6_BELL)
        bell = obj;
    else
        bell = bell_find(obj);
    if(bell)
    {
        obj_manager->animate_forwards(bell, 2);
    }
    Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_BELL);

    return(true);
}


/* Find bell near its pull-chain.
 */
Obj *U6UseCode::bell_find(Obj *chain_obj)
{
    Obj *bell = NULL;
    for(uint16 x = chain_obj->x-8; x <= chain_obj->x+8; x++)
        for(uint16 y = chain_obj->y-8; y <= chain_obj->y+8 && !bell; y++)
            bell = obj_manager->get_obj_of_type_from_location(OBJ_U6_BELL, x, y, chain_obj->z);
    return(bell);
}


//cranks control drawbridges.
bool U6UseCode::use_crank(Obj *obj, UseCodeEvent ev)
{
 uint16 x,y;
 uint8 level;
 uint16 b_width;
 bool bridge_open;
 Obj *start_obj;

 start_obj = drawbridge_find(obj);

 if(start_obj->frame_n == 3) // bridge open
    bridge_open = true;
 else
    bridge_open = false;

 x = start_obj->x;
 y = start_obj->y;
 level = start_obj->z;
 drawbridge_remove(x, y, level, &b_width);

 // find and animate chain
 if(!(start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_CHAIN, obj->x+1, obj->y, obj->z)))
   start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_CHAIN, obj->x-1, obj->y, obj->z);
 if(start_obj)
   obj_manager->animate_forwards(start_obj, 3);

 if(bridge_open)
 {
   obj_manager->animate_backwards(obj, 3); // animate crank
   drawbridge_close(x, y, level, b_width);
 }
 else
 {
   obj_manager->animate_forwards(obj, 3);
   drawbridge_open(x, y, level, b_width);
 }

 return true;
}

Obj *U6UseCode::drawbridge_find(Obj *crank_obj)
{
 uint16 i,j;
 Obj *start_obj, *tmp_obj;

 for(i=0;i<6;i++) // search on right side of crank.
  {
   start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DRAWBRIDGE, crank_obj->x+1, crank_obj->y+i,  crank_obj->z);
   if(start_obj != NULL) // this means we are using the left crank.
     return start_obj;
  }

 for(i=0;i<6;i++) // search on left side of crank.
  {
   tmp_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DRAWBRIDGE, crank_obj->x-1, crank_obj->y+i,  crank_obj->z);

   if(tmp_obj != NULL) // this means we are using the right crank.
     {
      //find the start of the drawbridge on the left.
      // we do this by searching to the left of the crank till we hit the crank on the otherside.
      // we then move right 1 tile and down 'i' tiles to the start object. :)
      for(j=1; j < crank_obj->x; j++)
        {
         tmp_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_CRANK, crank_obj->x-j, crank_obj->y,  crank_obj->z);
         if(tmp_obj && tmp_obj->obj_n == OBJ_U6_CRANK)
           {
            start_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_DRAWBRIDGE, tmp_obj->x+1, tmp_obj->y+i,  tmp_obj->z);
            return start_obj;
           }
        }
     }
  }

 return NULL;
}

void U6UseCode::drawbridge_open(uint16 x, uint16 y, uint8 level, uint16 b_width)
{
 uint16 i,j;
 Obj *obj;

 y++;

 for(i=0;;i++)
  {
   obj = new_obj(OBJ_U6_DRAWBRIDGE,3,x,y+i,level); //left side chain
   obj_manager->add_obj(obj,true);

   obj = new_obj(OBJ_U6_DRAWBRIDGE,5,x+b_width-1,y+i,level); //right side chain
   obj_manager->add_obj(obj,true);

   for(j=0;j<b_width-2;j++)
    {
     obj = new_obj(OBJ_U6_DRAWBRIDGE,4,x+1+j,y+i,level);
     obj_manager->add_obj(obj,true);
    }

   if(map->is_passable(x,y+i+1,level)) //we extend the drawbridge until we hit a passable tile.
    break;
  }

 i++;

 for(j=0;j<b_width-2;j++) //middle bottom tiles
  {
   obj = new_obj(OBJ_U6_DRAWBRIDGE,1,x+1+j,y+i,level);
   obj_manager->add_obj(obj,true);
  }

 obj = new_obj(OBJ_U6_DRAWBRIDGE,0,x,y+i,level); //bottom left
 obj_manager->add_obj(obj,true);

 obj = new_obj(OBJ_U6_DRAWBRIDGE,2,x+b_width-1,y+i,level);  // bottom right
 obj_manager->add_obj(obj,true);

 scroll->display_string("\nOpen the drawbridge.\n");

 return;
}

void U6UseCode::drawbridge_close(uint16 x, uint16 y, uint8 level, uint16 b_width)
{
 uint16 i;
 Obj *obj;

 y--;

 obj = new_obj(OBJ_U6_DRAWBRIDGE,6,x-1,y,level);  //left side
 obj_manager->add_obj(obj,true);

 obj = new_obj(OBJ_U6_DRAWBRIDGE,8,x+b_width-1,y,level);  //right side
 obj_manager->add_obj(obj,true);

 for(i=0;i<b_width-1;i++)
  {
   obj = new_obj(OBJ_U6_DRAWBRIDGE,7,x+i,y,level);  //middle
   obj_manager->add_obj(obj,true);
  }

 scroll->display_string("\nClose the drawbridge.\n");
}

/* GET: clear buried moonstone location
 *      (we never know if it is buried or just lying there)
 * USE: drop at user's current location, and update buried location 
 */
bool U6UseCode::use_moonstone(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_GET) 
    {
      Weather *weather = game->get_weather();
       /* FIXME: need to check weights here already? 
        * Check original's behavior when moonstone cannot be gotten due 
        * to weight limitations. Moving it normally doesn't unbury it,
        * so probably failing to get it shouldn't either.
        */ 
      weather->set_moonstone(obj->frame_n,MapCoord(0,0,0)) ;
      //scroll->display_string("\nMoonstone dug up. (FIXME)\n");
      weather->update_moongates();
      return true;
    } else if(ev == USE_EVENT_USE)
    {
		Weather *weather = game->get_weather();
		MapCoord loc=Game::get_game()->get_player()->get_actor()->get_location();
		Tile *map_tile = map->get_tile(loc.x,loc.y,loc.z);

		if( (map_tile->tile_num < 1 || map_tile->tile_num > 7) && (map_tile->tile_num < 0x10 || map_tile->tile_num > 0x6f) )
		{
			scroll->display_string("Cannot be buried here!\n");
			return true;
		}

		weather->set_moonstone(obj->frame_n,loc) ;
		scroll->display_string("buried.\n");

		obj_manager->moveto_map(obj, loc);
			obj->status |= OBJ_STATUS_OK_TO_TAKE;
		weather->update_moongates();
		return true;
    }
  return false;
}

/* USE: select location on ground to bury orb; when location is passed, open
 *      a red moongate to a new location
 */
bool U6UseCode::use_orb(Obj *obj, UseCodeEvent ev)
{
 Obj *gate;
 uint16 x,y,ox,oy;
 uint8 px,py,z,oz;
 uint8 position;
 Actor *lord_british;
 MapCoord *mapcoord_ref = items.mapcoord_ref;

 // can't use obj->is_on_map() since new container gumps will
 // have corpse items in an npc's inventoty
 if(!party->has_obj(87, 0, false)) // make sure orb of moons is in party inventory
 {
	scroll->display_string("\nNot usable\n");
	return true;
 }

 player->get_actor()->get_location(&x,&y,&z);
 lord_british = actor_manager->get_actor(U6_LORD_BRITISH_ACTOR_NUM);

 // The player must ask Lord British about the orb before it can be used.
 // This sets the flag 0x20 in LB's flags field which allows the orb to be used.

 if((lord_british->get_talk_flags() & U6_LORD_BRITISH_ORB_CHECK_FLAG) == 0)
   {
    scroll->display_string("\nYou can't figure out how to use it.\n");
    return true;
   }

// if(!orb_activated)
//   {
//    scroll->display_string("\nYou must recharge it first!\n");
//    return true;
//   }

 if(ev == USE_EVENT_INPUT_CANCEL ||
    (items.obj_ref && !items.obj_ref->is_on_map())) // selected item in inventory
 {
	 scroll->display_string("Failed\n");
	 return true;
 }

 if(!mapcoord_ref)
  {
   game->get_event()->get_target(MapCoord(x,y,z), "Where: ");
   game->get_event()->request_input(this, obj);
   return false; // no prompt
  }

 ox=mapcoord_ref->x;
 oy=mapcoord_ref->y;
 oz=mapcoord_ref->z;

 px=3+ox-x;
 py=2+oy-y;

 if( px > 5 || py > 4 ||           // Moongate out of range.
     items.actor2_ref ||           // Actor at location.
     !map->is_passable(ox,oy,oz))  // Location not passable.
   {
    scroll->display_string("Failed.\n");
    return true;
   }

 position=px+py*5;

 if(position >= 12 && position <= 14) // The three middle positions go noware.
    position = 0;

 gate=new_obj(OBJ_U6_RED_GATE,1,ox,oy,z);
 gate->quality=position;
 gate->set_temporary();

 new VanishEffect(VANISH_WAIT);
 obj_manager->add_obj(gate, true);
 game->get_map_window()->updateBlacking(); // next update not until Effect completes
 scroll->display_string("a red moon gate appears.\n");

 return true;
}


void U6UseCode::drawbridge_remove(uint16 x, uint16 y, uint8 level, uint16 *bridge_width)
{
 uint16 w,h;

 //remove end of closed drawbridge.
 // if present.
 if(x > 0)
   obj_manager->remove_obj_type_from_location(OBJ_U6_DRAWBRIDGE,x-1,y,level);

 *bridge_width = 0;

 //remove the rest of the bridge.
 for(h=0,w=1;w != 0;h++)
  {
   for(w=0;;w++)
    {
     if(obj_manager->remove_obj_type_from_location(OBJ_U6_DRAWBRIDGE,x+w,y+h,level) == false)
       {
        if(w != 0)
          *bridge_width = w;
        break;
       }
    }
  }

 return;
}

// USE: fishing pole. Attempt to catch a fish from an adjacent water square.
bool U6UseCode::use_fishing_pole(Obj *obj, UseCodeEvent ev)
{
 ViewManager *view_manager = game->get_view_manager();
 Actor *player_actor;
 Obj *fish;
 uint16 x, y;
 uint8 z;
 
 player_actor = player->get_actor();

 player_actor->get_location(&x, &y, &z);
 
 if(use_find_water(&x, &y, &z) == false)
  {
   scroll->display_string("\nYou need to stand next to water.\n");
   return true;
  }

 if(NUVIE_RAND() % 100 <= 20)
   {
    fish = new Obj();

    fish->obj_n = OBJ_U6_FISH;
    if(!player_actor->can_carry_object(fish))
      {
       scroll->display_string("\nGot it, but can't carry it.\n");
       if(use_boat_find_land(&x,&y,&z) == false) //we couldn't find an empty spot for the fish.
         {                                       //so back into the water with thee.
          delete fish;
          return true;
         }

       fish->x = x;
       fish->y = y;
       fish->z = z;
       fish->set_ok_to_take(true);
       obj_manager->add_obj(fish);
       return true;
      }

    player_actor->inventory_add_object(fish);

    if(!game->is_new_style())
        view_manager->set_inventory_mode();

    view_manager->update(); //FIX this should be moved higher up in UseCode

    scroll->display_string("\nGot it!\n");
   }
 else
   scroll->display_string("\nDidn't get a fish.\n");

 return true;
}

inline bool U6UseCode::use_find_water(uint16 *x, uint16 *y, uint8 *z)
{
 if(map->is_water(*x, *y-1, *z))  //UP
   {
    *y = *y - 1;
    return true;
   }
 if(map->is_water(*x+1, *y, *z)) //RIGHT
   {
    *x = *x + 1;
    return true;
   }
 if(map->is_water(*x, *y+1, *z)) //DOWN
   {
    *y = *y + 1;
    return true;
   }
 if(map->is_water(*x-1, *y, *z)) //LEFT
   {
    *x = *x - 1;
    return true;
   }

 return false;
}


/* Use shovel in one of 8 directions. If used in a dungeon level get a chance of
 * finding gold or a fountain (to make a wish).
 */
bool U6UseCode::use_shovel(Obj *obj, UseCodeEvent ev)
{
    Obj *dug_up_obj = NULL;
    Obj *ladder_obj;
    MapCoord from, dig_at, ladder;

    if(ev == USE_EVENT_INPUT_CANCEL)
    {
    	scroll->display_string("nowhere.\n");
    	return true;
    }

    if(!items.mapcoord_ref) // get direction (FIXME: should return relative dir)
    {
        if(!obj->is_readied())
        {
            scroll->display_string("\nNot readied.\n");
            return(true);
        }
        if(items.actor_ref == NULL) // happens when you use on a widget
        {
            scroll->display_string("nowhere.\n");
            return true;
        }
        Actor *parent = obj->get_actor_holding_obj();
        from = parent->get_location();

//        game->get_event()->useselect_mode(obj, "Direction: ");
        game->get_event()->get_direction(from, "Direction: ");
        if(game->get_map_window()->get_interface() == INTERFACE_NORMAL)
            game->get_event()->do_not_show_target_cursor = true;
        game->get_event()->request_input(this, obj);
        return(false);
    }

    Actor *parent = obj->get_actor_holding_obj();
    from = parent->get_location();

    dig_at = *items.mapcoord_ref;

    if(game->get_map_window()->get_interface() < INTERFACE_FULLSCREEN)
    {
        dig_at.sx = (dig_at.sx == 0) ? 0 : (dig_at.sx < 0) ? -1 : 1;
        dig_at.sy = (dig_at.sy == 0) ? 0 : (dig_at.sy < 0) ? -1 : 1;
    }

    scroll->display_string(get_direction_name(dig_at.x, dig_at.y));
    if(dig_at.sx == 0 && dig_at.sy == 0)
    {
        scroll->display_string(".\n");
        return(true); // display prompt
    }
    scroll->display_string(".\n\n");

    dig_at.x += from.x;
    dig_at.y += from.y;
    dig_at.z = from.z;
    if(!dig_at.is_visible())
    {
        scroll->display_string("Not on screen.\n");
        return true; //  display prompt
    }
    else if(!from.is_visible() && from.distance(dig_at) > 5)
    {
        scroll->display_string("Out of range.\n");
        return true; //  display prompt
    }
    else if(game->get_map_window()->get_interface() != INTERFACE_IGNORE_BLOCK)
    {
        LineTestResult lt;
        if(map->lineTest(from.x, from.y, dig_at.x, dig_at.y, dig_at.z, LT_HitUnpassable, lt))
        {
            MapCoord hit_loc = MapCoord(lt.hit_x, lt.hit_y, lt.hit_level);
            if(hit_loc != dig_at)
            {
                scroll->display_string("Blocked\n");
                return true; // display prompt
            }
        }
    }
    Obj *hole = obj_manager->get_obj_of_type_from_location(OBJ_U6_HOLE,dig_at.x,dig_at.y,dig_at.z);
    if(hole || dig_at.z == 5 // we can't go anywhere from the gargoyle world.
       || game->get_map_window()->tile_is_black(dig_at.x, dig_at.y)
       || (dig_at.z == 0 && (dig_at.x != 0x2c3 || dig_at.y != 0x343)))
    {
        scroll->display_string("No effect\n");
        return(true); // ??
    }

    // try to conenct with a ladder on a lower level.
    ladder = dig_at;
// This is to inacurate. It will detect an extra ladder 8 tiles east
// plus another one 8 tiles north and 8 tiles east
    if(dig_at.z == 0)//handle the transition from the surface to the first dungeon level
      {
       ladder.x = (dig_at.x & 0x07) | (dig_at.x >> 2 & 0xF8);
       ladder.y = (dig_at.y & 0x07) | (dig_at.y >> 2 & 0xF8);
      }
      
    ladder.z = dig_at.z + 1;

//    if(dig_at.z != 5) already checked
      {
       ladder_obj = obj_manager->get_obj_of_type_from_location(OBJ_U6_LADDER, ladder.x, ladder.y, ladder.z);
       if(ladder_obj && ladder_obj->frame_n == 1) // ladder up.
         {
          scroll->display_string("You dig a hole.\n");
          dug_up_obj = new_obj(OBJ_U6_HOLE, 0, dig_at.x, dig_at.y,dig_at.z); //found a connecting ladder, dig a hole
         }
      }
    Tile *tile = map->get_tile(dig_at.x, dig_at.y, dig_at.z, true);
// uncomment first check if the coord conversion gets added back
    if(/*(!dug_up_obj && dig_at.z == 0) ||*/ !tile // original might have checked for earth desc and no wall mask
       || !((tile->tile_num <= 111 && tile->tile_num >= 108) || tile->tile_num == 540))
      {
       scroll->display_string("No Effect.\n");
       return(true);
      }
    
    if(!dug_up_obj)
      {
       // 10% chance of anything
       if(NUVIE_RAND() % 10)
         {
          scroll->display_string("Failed\n");
          return(true);
         }
         
       // Door #1 or Door #2?
       Obj *fountain = obj_manager->get_obj_of_type_from_location(OBJ_U6_FOUNTAIN,dig_at.x,dig_at.y,dig_at.z);
       if((NUVIE_RAND() % 2)) // original lets you stack fountains
         {
          scroll->display_string("You find a water fountain.\n");
          if(!fountain) // don't actually add another one
              dug_up_obj = new_obj(OBJ_U6_FOUNTAIN,0, dig_at.x,dig_at.y,dig_at.z);
         }
       else
         {
          scroll->display_string("You find a gold nugget.\n");
          dug_up_obj = new_obj(OBJ_U6_GOLD_NUGGET,0, dig_at.x,dig_at.y,dig_at.z);
          dug_up_obj->status |= OBJ_STATUS_OK_TO_TAKE;
         }
      }
    if(dug_up_obj)
    {
        dug_up_obj->set_temporary();
        obj_manager->add_obj(dug_up_obj, true);
    }
    return(true);
}


/* USE: Magic fountain. Make a wish!
 */
bool U6UseCode::use_fountain(Obj *obj, UseCodeEvent ev)
{
    static bool get_wish = false;
    static Actor *wish_actor = NULL; // person receiving gift

    scroll->cancel_input_request();
    if(ev == USE_EVENT_USE)
    {
        scroll->display_string("Make a wish? ");
        // get Y/N single char, no ENTER (FIXME: no printing)
        scroll->set_input_mode(true, "yn", false);
        scroll->request_input(this, obj);
        wish_actor = items.actor_ref;
        assert(wish_actor);
        return(false);
    }
    else if(ev == USE_EVENT_MESSAGE && items.string_ref)
    {
        scroll->display_string("\n");
        if(!get_wish) // answered with Y/N
        {
            // Y:
            if(*items.string_ref == "y" || *items.string_ref == "Y")
            {
                scroll->display_string("Wish for: ");
                // get string
                scroll->set_input_mode(true);
                scroll->request_input(this, obj);
                get_wish = true;
            }
            else // N: won't wish
            {
                scroll->display_string("\n");
                scroll->display_prompt();
            }
        }
        else // answered with wish
        {
            get_wish = false;
            bool wished_for_food = false;
            char *wish = (char*)malloc(items.string_ref->size() + 1);
            strcpy(wish, items.string_ref->c_str());
            if(strcasecmp(wish, "Food") == 0 || strcasecmp(wish, "Mutton") == 0
               || strcasecmp(wish, "Wine") == 0 || strcasecmp(wish, "Fruit") == 0
               || strcasecmp(wish, "Mead") == 0)
                wished_for_food = true;
            free(wish);
            if(!wished_for_food)
            {
                scroll->display_string("\nFailed\n\n");
                scroll->display_prompt();
                return(true);
            }
            // 25% chance of anything
            if((NUVIE_RAND() % 4) != 0)
            {
                scroll->display_string("\nNo effect\n\n");
                scroll->display_prompt();
                return(true);
            }
            scroll->display_string("\nYou got food");
            // must be able to carry it
            if(!wish_actor->can_carry_object(OBJ_U6_MEAT_PORTION, 1))
            {
                scroll->display_string(", but you can't carry it.\n\n");
                scroll->display_prompt();
                return(true);
            }
            scroll->display_string(".\n\n");
            scroll->display_prompt();
            assert(wish_actor);
            wish_actor->inventory_new_object(OBJ_U6_MEAT_PORTION, 1);
        }
    }
    else
        get_wish = false;
    return(false);
}


/* USE: Make a rubber ducky sound. */
bool U6UseCode::use_rubber_ducky(Obj *obj, UseCodeEvent ev)
{
    if(items.actor_ref == player->get_actor())
        scroll->display_string("\nSqueak!\n");
    Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_RUBBER_DUCK); //FIXME towns says "Quack! Quack!" and plays sfx twice.
    return(true);
}

sint16 U6UseCode::parseLatLongString(U6UseCodeLatLonEnum mode, std::string *input)
{
	uint16 len = input->length();
	sint16 val = 0;
	for(uint16 i=0;i<len;i++)
	{
		char c = (*input)[i];
		if(c < '0' || c > '9')
		{
			c = toupper(c);
			if(mode == LAT)
			{
				if(c == 'N' || c == 'S')
				{
					if(c == 'N')
						val = -val;
				}
				else
				{
					val = 100;
				}
			}
			else
			{
				if(c == 'E' || c == 'W')
				{
					if(c == 'W')
						val = -val;
				}
				else
				{
					val = 100;
				}
			}
			break;
		}

		val = val * 10 + (*input)[i] - 48;
	}

	return val;
}

/* USE: Crystal ball
 */
bool U6UseCode::use_crystal_ball(Obj *obj, UseCodeEvent ev)
{
    static enum { GET_LAT, GET_LON} mode = GET_LAT;
    static MapCoord loc;
    static Actor *actor = NULL;

    scroll->cancel_input_request();
    if(ev == USE_EVENT_USE)
    {
    	actor = items.actor_ref;
    	if(NUVIE_RAND()%30 < (45 - actor->get_intelligence()) / 2) //use crystal ball saving roll.
    	{
    		game->get_script()->call_actor_hit(actor, (NUVIE_RAND()%10)+1, SCRIPT_DISPLAY_HIT_MSG);
    		scroll->display_string("\n");
    		scroll->display_prompt();
    		return false;
    	}

    	mode = GET_LAT;
    	scroll->display_string("Enter degrees followed by N, S, E or W.\n\nAt latitude=");
    	scroll->set_input_mode(true);
    	scroll->request_input(this, obj);

        return(false);
    }
    else if(ev == USE_EVENT_MESSAGE && items.string_ref)
    {
		if(mode == GET_LAT)
		{
			sint16 lat = parseLatLongString(LAT, items.string_ref);
			if(lat > 80 || lat < -44)
			{
				scroll->display_string("\n\n");
				scroll->display_prompt();
				return false;
			}

			loc.y = lat * 8 + 360;
			scroll->display_string("\n");
			scroll->display_string("  longitude=");
    		scroll->set_input_mode(true);
    		scroll->request_input(this, obj);
    		mode = GET_LON;
		}
		else if(mode == GET_LON)
		{
			scroll->display_string("\n");
			sint16 lon = parseLatLongString(LON, items.string_ref);
			if(lon > 88 || lon < -37)
			{
				scroll->display_string("\n\n");
				scroll->display_prompt();
				return false;
			}

			loc.x = lon * 8 + 304;

			actor->get_location(NULL, NULL, &loc.z);
			if(loc.z != 0)
			{
				loc.x = loc.x / 4;
				loc.y = loc.y / 4;
			}

			AsyncEffect *e = new AsyncEffect(new WizardEyeEffect(loc, 0x28));
			e->run(EFFECT_PROCESS_GUI_INPUT);
			scroll->display_string("\nDone\n\n");
			scroll->display_prompt();
		}

    }

    return(false);
}

/* USE: Enter instrument playing mode, with sound for used object. */
bool U6UseCode::play_instrument(Obj *obj, UseCodeEvent ev)
{
// FIXME: need intrument sounds AND a config option to simply change music
// track when an instrument is played. Maybe NORTH_KEY and SOUTH_KEY can cycle through sounds/music and DO_ACTION_KEY can play it.
/// FIXME: also some floating music note icons like in U7
    game->get_event()->close_gumps(); // gumps will steal input
    const char *musicmsg = (obj->obj_n == OBJ_U6_PANPIPES) ? "panpipes"
                 : (obj->obj_n == OBJ_U6_HARPSICHORD) ? "harpsichord"
                 : (obj->obj_n == OBJ_U6_HARP) ? "harp"
                 : (obj->obj_n == OBJ_U6_LUTE) ? "lute"
                 : (obj->obj_n == OBJ_U6_XYLOPHONE) ? "xylophone"
                 : "musical instrument";
    if(items.data_ref)
    {
            SDL_Keycode key = ((EventInput*)items.data_ref)->key;
            ActionKeyType key_type = ((EventInput*)items.data_ref)->action_key_type;
            if(key == SDLK_0) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 0\n", musicmsg);
            if(key == SDLK_1) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 1\n", musicmsg);
            if(key == SDLK_2) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 2\n", musicmsg);
            if(key == SDLK_3) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 3\n", musicmsg);
            if(key == SDLK_4) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 4\n", musicmsg);
            if(key == SDLK_5) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 5\n", musicmsg);
            if(key == SDLK_6) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 6\n", musicmsg);
            if(key == SDLK_7) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 7\n", musicmsg);
            if(key == SDLK_8) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 8\n", musicmsg);
            if(key == SDLK_9) DEBUG(0,LEVEL_WARNING,"FIXME: %s: modulate 9\n", musicmsg);
            return(key_type != DO_ACTION_KEY && key_type != CANCEL_ACTION_KEY);
    }
    else
        game->get_event()->key_redirect(this, obj);
    return false;
}


// use_firedevice()
bool U6UseCode::use_firedevice_message(Obj *obj, bool lit)
{
 if(items.actor_ref != player->get_actor())
   return true;
 scroll->display_string("\n");
 scroll->display_string(obj_manager->get_obj_name(obj));
 if(lit)
   scroll->display_string(" is lit.\n");
 else
   scroll->display_string(" is doused.\n");

 return true;
}

/* USE: Eat/drink food object. Hic!
 */
bool U6UseCode::use_food(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_USE)
    {
        if(items.actor_ref == player->get_actor())
        {
            if(obj->obj_n == OBJ_U6_WINE || obj->obj_n == OBJ_U6_MEAD
               || obj->obj_n == OBJ_U6_ALE)
            {
                scroll->display_string("\nYou drink it.\n");

                player->add_alcohol(); // add to drunkeness
            }
            else
                scroll->display_string("\nYou eat the food.\n");
        }
        destroy_obj(obj, 1);
    }
    return(true);
}


/* USE: Use potion. If actor2 is passed, give them the potion, else select
 * actor2. */
bool U6UseCode::use_potion(Obj *obj, UseCodeEvent ev)
{
    ActorManager *am = actor_manager;

    if(ev == USE_EVENT_USE)
    {
        if(!items.actor2_ref && !items.obj_ref && !items.mapcoord_ref)
        {
            game->get_event()->get_target(items.actor_ref->get_location(), "On whom: ");
            game->get_event()->request_input(this, obj);
        }
        else if(!items.actor2_ref) // no selection
        {
            scroll->display_string("nobody\n");
            return(true);
        }
        else // use potion
        {
            sint8 party_num = party->get_member_num(items.actor2_ref);
            scroll->display_string(party_num >= 0 ? party->get_actor_name(party_num)
                                   : am->look_actor(items.actor2_ref));
            scroll->display_string("\n");

            if(party_num < 0) // can't force potions on non-party members
                scroll->display_string("No effect\n");
            else
            {
                switch(obj->frame_n)
                {
                    case USE_U6_POTION_RED: ((U6Actor *)items.actor2_ref)->set_poisoned(false);
                        destroy_obj(obj); break;
                    case USE_U6_POTION_YELLOW: ((U6Actor *)items.actor2_ref)->heal();
                        destroy_obj(obj); break;
                    case USE_U6_POTION_GREEN: ((U6Actor *)items.actor2_ref)->set_poisoned(true);
                        destroy_obj(obj); break;
                    case USE_U6_POTION_BLUE: ((U6Actor *)items.actor2_ref)->set_asleep(false);
                        destroy_obj(obj); break;
                    case USE_U6_POTION_PURPLE: ((U6Actor *)items.actor2_ref)->set_protected(true);
                        destroy_obj(obj); break;
                    case USE_U6_POTION_WHITE: new U6WhitePotionEffect(2500, 6000, obj);
                        break; // wait for message to delete potion
                    case USE_U6_POTION_BLACK:
                        //new SpellTargetEffect(items.actor2_ref, obj);
                        // or effect_mgr->wait_for_effect(new SpellTargetEffect(items.actor2_ref), this, obj);
                        items.actor2_ref->set_invisible(true);
                        destroy_obj(obj); break;
                    case USE_U6_POTION_ORANGE:
                        //items.actor2_ref->set_worktype(WORKTYPE_U6_SLEEP);
                        items.actor2_ref->set_asleep(true);
                        //party->set_active(party_num, !(items.actor2_ref->is_sleeping() || items.actor2_ref->is_paralyzed()));
                        player->set_actor(party->get_leader_actor());
                        player->set_mapwindow_centered(true);
                        destroy_obj(obj);
                        break;

                    default:
                        if(obj->frame_n <= 7)
                        {
                            scroll->display_string("Drink %s potion!\n", u6_potions[obj->frame_n]);
                            //scroll->display_string(u6_potions[obj->frame_n]);
                            //scroll->display_string(" potion!\n");
                        }
                        else
                            scroll->display_string("\nNo effect\n");
                        destroy_obj(obj);
                }
            }
            return true;
        }
    }
    else if(ev == USE_EVENT_INPUT_CANCEL)
    {
    	scroll->display_string("No effect\n");
    	return true;
    }
    else if(ev == USE_EVENT_MESSAGE) // assume message is from potion effect
    {
        if(*items.msg_ref == MESG_EFFECT_COMPLETE && obj->frame_n == USE_U6_POTION_WHITE) // white
        {
            destroy_obj(obj);
        }
    }
    return false;
}

bool U6UseCode::lock_pick_dex_check()
{
	int dex = player->get_actor()->get_dexterity();
	if(player->get_actor()->is_cursed())
	{
		if(dex <= 3)
			dex = 1;
		else
			dex -= 3;
	}

	if(NUVIE_RAND() % 30 < (45 - dex) / 2)
		return true;

	return false;
}

/* Use a key on obj_ref (a door). */
bool U6UseCode::use_key(Obj *obj, UseCodeEvent ev)
{
    Obj *door_obj = NULL;
    if(ev == USE_EVENT_USE)
    {
        USECODE_SELECT_OBJ(door_obj, "On "); // door_obj <- items.obj_ref or from user
        if(!door_obj)
        {
            scroll->display_string("nothing\n");
            return true;
        }
        else
        {
            if(UseCode::out_of_use_range(door_obj, false))
                return true;

            scroll->display_string(obj_manager->get_obj_name(door_obj));
            scroll->display_string("\n");

            if(!is_door(door_obj) && !is_chest(door_obj))
            {
            	scroll->display_string("No effect\n");
            	return true;
            }

            if(obj->obj_n == OBJ_U6_LOCK_PICK && lock_pick_dex_check() == true)
            {
                Game::get_game()->get_sound_manager()->playSfx(NUVIE_SFX_FAILURE);
            	scroll->display_string("\nKey broke.\n");
            	if(obj->qty > 1)
            	{
            		obj->qty -= 1;
            	}
            	else
            	{
            		UseCode::search_container(obj, false); //need to remove rolling pin if there is one
            		obj_manager->unlink_from_engine(obj);
            		delete_obj(obj);
            	}

            	return true;
            }

//FIXME need to handle locked chests.
            if(( (obj->obj_n == OBJ_U6_KEY && door_obj->quality != 0 && door_obj->quality == obj->quality)
            		|| (obj->obj_n == OBJ_U6_LOCK_PICK && door_obj->quality == 0) )
            	&& (is_closed_door(door_obj) || is_closed_chest(door_obj))
            	&& !is_magically_locked(door_obj))
            {
                if(is_locked(door_obj))
                {
                    unlock(door_obj);
                    scroll->display_string("\nunlocked!\n");
                }
                else
                {
                    lock(door_obj);
                    scroll->display_string("\nlocked!\n");
                }
            }
            else if(is_door(door_obj) && door_obj->frame_n <= 3
                    && ((obj->obj_n == OBJ_U6_KEY && door_obj->quality != 0 && door_obj->quality == obj->quality)
                    || (obj->obj_n == OBJ_U6_LOCK_PICK && door_obj->quality == 0)))
                scroll->display_string("\nCan't (Un)lock an opened door\n");
            else
                scroll->display_string("\nNo effect\n");
            return true;
        }
    }
    else if(ev == USE_EVENT_INPUT_CANCEL)
    {
    	scroll->display_string("nothing\n");
    	return true;
    }
    else if(ev == USE_EVENT_GET && obj->obj_n == OBJ_U6_LOCK_PICK)  //need to remove rolling pin if there is one
	{
		UseCode::search_container(obj, false);
        return true;
	}
    else if(ev == USE_EVENT_SEARCH && obj->obj_n == OBJ_U6_LOCK_PICK)  //need to remove rolling pin if there is one
        return(UseCode::search_container(obj));

    return false;
}


/* USE: Enter and exit sea-going vessels. (entire party)
 */
bool U6UseCode::use_boat(Obj *obj, UseCodeEvent ev)
{
 Actor *ship_actor;
 uint16 lx, ly;
 uint8 lz;

 if(ev == USE_EVENT_SEARCH)
    return(UseCode::search_container(obj));
 else if(ev == USE_EVENT_USE && obj->has_container())
    return use_container(obj, USE_EVENT_USE);
 else if(ev == USE_EVENT_LOOK || ev == USE_EVENT_GET)
 {
    if(obj->quality != 0 && party->has_obj(OBJ_U6_SHIP_DEED, obj->quality))
    {
      if(obj->obj_n == OBJ_U6_SKIFF)
        obj->set_ok_to_take(true);
      obj->quality = 0;
    }
    if(ev == USE_EVENT_GET)
      return true;
 }

 if(ev != USE_EVENT_USE)
    return(false);
 ship_actor = actor_manager->get_actor(0); //get the vehicle actor.

 // get out of boat
 if(party->is_in_vehicle())
  {
   ship_actor->get_location(&lx,&ly,&lz); //retrieve actor position for land check.

   if(use_boat_find_land(&lx,&ly,&lz)) //we must be next to land to disembark
     {
      Obj *obj = ship_actor->make_obj();
      obj->qty = ship_actor->get_hp(); // Hull Strength

      party->exit_vehicle(lx, ly, lz);

      obj_manager->add_obj(obj);
     }
   else
     {
      scroll->display_string("\nOnly next to land.\n");
      return true;
     }

   return true;
  }

  if(obj->is_on_map() == false)
   {
    scroll->display_string("\nNot usable\n");
    return(true);
   }
  if((obj->obj_n == OBJ_U6_SKIFF || obj->obj_n == OBJ_U6_RAFT)
     && !map->is_water(obj->x, obj->y, obj->z, true))
  {
        scroll->display_string("\nYou must place it in water first.\n");
        return true;
  }
  if(!player->in_party_mode())
   {
    scroll->display_string("\nNot in solo mode.\n");
    return(true);
   }

  if(obj->obj_n == OBJ_U6_SHIP) //If we are using a ship we need to use its center object.
   {
    obj = use_boat_find_center(obj); //return the center object
    if(obj == NULL)
     {
      scroll->display_string("\nShip not usable\n");
      return true;
     }
   }

 if(obj->quality != 0)    //deed check
   {
    if(party->has_obj(OBJ_U6_SHIP_DEED, obj->quality) == false)
      {
       scroll->display_string("\nA deed is required.\n");
       return true;
      }
    if(obj->obj_n == OBJ_U6_SKIFF)
       obj->set_ok_to_take(true);
    obj->quality = 0;
   }

 if(UseCode::out_of_use_range(obj, true))
    return true;

 // walk to vehicle if necessary
 if(!party->is_at(obj->x, obj->y, obj->z))
   {
    party->enter_vehicle(obj);
    return(true);
   }

 // use it (replace ship with vehicle actor)
 ship_actor->init_from_obj(obj, ACTOR_CHANGE_BASE_OBJ_N);
 if(obj->obj_n == OBJ_U6_SHIP)
    ship_actor->set_hp(obj->qty); // Hull Strength
 ship_actor->show(); // Swift!
 obj_manager->remove_obj_from_map(obj);
 delete_obj(obj);

 party->hide(); // set in-vehicle
 player->set_actor(ship_actor);
 party->set_in_vehicle(true);
 return(true);
}

inline Obj *U6UseCode::use_boat_find_center(Obj *obj)
{
 Obj *new_obj;
 uint16 x, y;
 uint8 ship_direction = (obj->frame_n % 8) / 2; //find the direction based on the frame_n

 if(obj->frame_n >= 8 && obj->frame_n < 16) //center obj
  return obj;

 x = obj->x;
 y = obj->y;

 if(obj->frame_n < 8) //front obj
   {
    switch(ship_direction)
      {
       case NUVIE_DIR_N : y++; break;
       case NUVIE_DIR_E : x--; break;
       case NUVIE_DIR_S : y--; break;
       case NUVIE_DIR_W : x++; break;
      }
   }
 else
   {
    if(obj->frame_n >= 16 && obj->frame_n < 24) //back obj
      {
       switch(ship_direction)
         {
          case NUVIE_DIR_N : y--; break;
          case NUVIE_DIR_E : x++; break;
          case NUVIE_DIR_S : y++; break;
          case NUVIE_DIR_W : x--; break;
         }
      }
   }

 new_obj = obj_manager->get_objBasedAt(x,y,obj->z,true);

 if(new_obj != NULL && new_obj->obj_n == OBJ_U6_SHIP)
   return new_obj;

 return NULL;
}

inline bool U6UseCode::use_boat_find_land(uint16 *x, uint16 *y, uint8 *z)
{
 if(map->is_passable(*x, *y-1, *z))  //UP
   {
    *y = *y - 1;
    return true;
   }
 if(map->is_passable(*x+1, *y, *z)) //RIGHT
   {
    *x = *x + 1;
    return true;
   }
 if(map->is_passable(*x, *y+1, *z)) //DOWN
   {
    *y = *y + 1;
    return true;
   }
 if(map->is_passable(*x-1, *y, *z)) //LEFT
   {
    *x = *x - 1;
    return true;
   }

 return false;
}

/* construct a balloon using the balloon plans */
bool U6UseCode::use_balloon_plans(Obj *obj, UseCodeEvent ev)
{
 if(ev == USE_EVENT_LOOK)
    return look_sign(obj, ev);

 MapCoord player_location = player->get_actor()->get_location();
 bool missing_obj = false;
 Obj *balloon;
 
 if(ev != USE_EVENT_USE)
    return(false);

 scroll->display_string("\n");
 
 //make sure the party is carrying the required parts.
 
 if(!party->has_obj(OBJ_U6_MAMMOTH_SILK_BAG, 0))
   {
    scroll->display_string("Missing a mammoth silk bag.\n");
    missing_obj = true;
   }

 if(!party->has_obj(OBJ_U6_BALLOON_BASKET, 0))
   {
    scroll->display_string("Missing a balloon basket.\n");
    missing_obj = true;
   }
    
 if(!party->has_obj(OBJ_U6_CAULDRON, 0))
   {
    scroll->display_string("Missing a cauldron.\n");
    missing_obj = true;
   }
 
 if(!party->has_obj(OBJ_U6_ROPE, 0))
   {
    scroll->display_string("Missing a rope.\n");  
    missing_obj = true;
   }

 // Make the balloon if we have all the parts.
 if(!missing_obj)
   {
    party->remove_obj(OBJ_U6_MAMMOTH_SILK_BAG, 0);
    party->remove_obj(OBJ_U6_BALLOON_BASKET, 0);
    party->remove_obj(OBJ_U6_CAULDRON, 0);
    party->remove_obj(OBJ_U6_ROPE, 0);
            
    balloon = new_obj(OBJ_U6_BALLOON, 0, player_location.x, player_location.y, player_location.z);

    if(balloon && obj_manager->add_obj(balloon))
    {
      balloon->set_ok_to_take(true);
      scroll->display_string("Done!\n");
    }
   }
   
 return true;
}


/* USE: balloon. (entire party)
 */
bool U6UseCode::use_balloon(Obj *obj, UseCodeEvent ev)
{
 Actor *balloon_actor;
 Actor *balloonist;
 MapCoord spot(0,0,0);
 uint16 lx, ly;
 uint8 lz;

 if(ev != USE_EVENT_USE)
    return(false);

 if (Game::get_game()->get_player()->in_party_mode())
 {
   balloonist=Game::get_game()->get_party()->get_leader_actor();
 } else
 {
   balloonist=Game::get_game()->get_player()->get_actor();
 }
 spot=balloonist->get_location();
 if ((spot.z>0) && (spot.z<5)) 
 {
   scroll->display_string("\nNot usable\n");
   return(true);
 }
 
 if(obj->obj_n == OBJ_U6_BALLOON)
   {
     if (!obj->is_on_map()) 
     {
       // if in party mode, find a spot around the avatar that is_passable,
       // else a spot around the person using it.
       // drop the balloon there, and inflate it.
       
       uint16 x,y;
       x=spot.x;
       y=spot.y;
       
       bool dropped=false;
       for (sint8 iy=-1;iy<2;iy++) // FIXME scan order
       {
	 for (sint8 ix=-1;ix<2;ix++) 
	 {
	   DEBUG(0,LEVEL_DEBUGGING,"can drop at %d %d?\n",ix,iy); 
	   if(Game::get_game()->get_map_window()->can_drop_or_move_obj(x+ix,y+iy,balloonist, obj) == MSG_SUCCESS)
	   {
	     DEBUG(0,LEVEL_DEBUGGING,"yes, can drop at %d %d.\n",x+ix,y+iy); 
	     obj_manager->unlink_from_engine(obj);
	     obj->x=x+ix;
	     obj->y=y+iy;
	     obj->z=spot.z;
	     
	     dropped=true;
	     iy=2;ix=2;
	   }
	 }
       }
       if (!dropped) 
       {
// drop on 'spot' instead. 
	     obj_manager->unlink_from_engine(obj);
	     obj->x=spot.x;
	     obj->y=spot.y;;
	     obj->z=spot.z;
	     dropped=true;
       }
       obj->status |= OBJ_STATUS_OK_TO_TAKE;
       obj_manager->add_obj(obj, OBJ_ADD_TOP);
     }

    obj->obj_n = OBJ_U6_INFLATED_BALLOON;
    obj->frame_n = 3;
    scroll->display_string("\nDone!\n");
    return true;
	}
	
 balloon_actor = actor_manager->get_actor(0); //get the vehicle actor.

 // get out of balloon
 if(party->is_in_vehicle()) // FIXME: use balloon when in skiff...
  {
   balloon_actor->get_location(&lx,&ly,&lz); //retrieve actor position for land check.

   if(use_boat_find_land(&lx,&ly,&lz)) //we must be next to land to disembark
     {
      Obj *obj;

      party->show();
      balloon_actor->hide();
      balloon_actor->set_worktype(0);

      player->set_actor(party->get_actor(0));
      player->move(lx,ly,lz, false);
      balloon_actor->obj_n = OBJ_U6_NO_VEHICLE;
      balloon_actor->frame_n = 0;
      balloon_actor->init();
      balloon_actor->move(0,0,0,ACTOR_FORCE_MOVE);

      obj = new_obj(OBJ_U6_BALLOON, 0, lx, ly, lz);
      obj->status|=OBJ_STATUS_OK_TO_TAKE; 
      obj_manager->add_obj(obj, OBJ_ADD_TOP);
     }
   else
     {
      scroll->display_string("\nOnly next to land.\n");
      return true;
     }

   party->set_in_vehicle(false);

   return true;
  }

  if(!player->in_party_mode())
   {
    scroll->display_string("\nNot in solo mode.\n");
    return(true);
   }

  if(UseCode::out_of_use_range(obj, true))
    return true;
 // walk to vehicle if necessary
 if(!party->is_at(obj->x, obj->y, obj->z))
   {
    party->enter_vehicle(obj);
    return(true); // display prompt
   }

 // use it (replace ship with vehicle actor)
 balloon_actor->init_from_obj(obj, ACTOR_CHANGE_BASE_OBJ_N);
 balloon_actor->show(); // Swift!
 obj_manager->remove_obj_from_map(obj);
 delete_obj(obj);

 party->hide(); // set in-vehicle
 player->set_actor(balloon_actor);
 party->set_in_vehicle(true);
 return(true);
}


/* using a cow fills an empty bucket in the player's inventory with milk */
bool U6UseCode::use_cow(Obj *obj, UseCodeEvent ev)
{
 if(ev != USE_EVENT_USE)
    return(false);

// return fill_bucket(OBJ_U6_BUCKET_OF_MILK);
 fill_bucket(OBJ_U6_BUCKET_OF_MILK);
 return true;
}

/* using a well fills an empty bucket in the player's inventory with water */
bool U6UseCode::use_well(Obj *obj, UseCodeEvent ev)
{
 if(ev != USE_EVENT_USE)
    return(false);

// return fill_bucket(OBJ_U6_BUCKET_OF_WATER);
 fill_bucket(OBJ_U6_BUCKET_OF_WATER);
 return true;
}

// fill an empty bucket in the player actor's inventory with some liquid
bool U6UseCode::fill_bucket(uint16 filled_bucket_obj_n)
{
 Actor *player_actor;
 Obj *bucket;

 player_actor = player->get_actor();

 if(!player_actor->inventory_has_object(OBJ_U6_BUCKET))
   {
    if(player_actor->inventory_has_object(OBJ_U6_BUCKET_OF_MILK) ||
       player_actor->inventory_has_object(OBJ_U6_BUCKET_OF_WATER) )
      {
       scroll->display_string("\nYou need an empty bucket.\n");
       return true;
      }
    else
      {
       scroll->display_string("\nYou need a bucket.\n");
       return true;
      }
   }

 // Fill the first empty bucket in player's inventory.

 bucket = player_actor->inventory_get_object(OBJ_U6_BUCKET);
 player_actor->inventory_remove_obj(bucket);

 bucket->obj_n = filled_bucket_obj_n;

 player_actor->inventory_add_object(bucket);

 scroll->display_string("\nDone\n");

 return true;
}


// USE: replace a bucket of milk in the player's inventory with butter
bool U6UseCode::use_churn(Obj *obj, UseCodeEvent ev)
{
 ViewManager *view_manager = game->get_view_manager();
 Actor *player_actor;
 Obj *bucket;
 Obj *butter;

 player_actor = player->get_actor();

 if(!player_actor->inventory_has_object(OBJ_U6_BUCKET_OF_MILK))
   {
    scroll->display_string("\nYou need some milk.\n");
	return true;
   }

 bucket = player_actor->inventory_get_object(OBJ_U6_BUCKET_OF_MILK);
 player_actor->inventory_remove_obj(bucket);

 bucket->obj_n = OBJ_U6_BUCKET;

 butter = new Obj();

 butter->obj_n = OBJ_U6_BUTTER;
 player_actor->inventory_add_object(butter);
 player_actor->inventory_add_object(bucket);
 if(!game->is_new_style())
    view_manager->set_inventory_mode();

 view_manager->update(); //FIX this should be moved higher up in UseCode

 scroll->display_string("\nDone\n");
 return true;
}


// USE: fill an empty honey jar in the player's inventory
bool U6UseCode::use_beehive(Obj *obj, UseCodeEvent ev)
{
 ViewManager *view_manager = game->get_view_manager();
 Actor *player_actor;
 Obj *honey_jar;

 player_actor = player->get_actor();

 if(!player_actor->inventory_has_object(OBJ_U6_HONEY_JAR))
   {
    if(player_actor->inventory_has_object(OBJ_U6_JAR_OF_HONEY))
      {
       scroll->display_string("\nYou need an empty honey jar.\n");
	  }
else
	  {
       scroll->display_string("\nYou need a honey jar.\n");
      }

	return true;
   }

 honey_jar = player_actor->inventory_get_object(OBJ_U6_HONEY_JAR);
 player_actor->inventory_remove_obj(honey_jar);

 honey_jar->obj_n = OBJ_U6_JAR_OF_HONEY; //fill the empty jar with honey.

 player_actor->inventory_add_object(honey_jar);   // add the filled jar back to the player's inventory
 if(!game->is_new_style())
    view_manager->set_inventory_mode();

 view_manager->update(); //FIX this should be moved higher up in UseCode

 scroll->display_string("\nDone\n");
 return true;
}


/* USE: Mount or dismount from a horse. Don't allow using another horse if
 * already riding one.
 */
bool U6UseCode::use_horse(Obj *obj, UseCodeEvent ev)
{
 Actor *actor, *player_actor;
 Obj *actor_obj;

 if(ev != USE_EVENT_USE)
    return(false);

 actor = actor_manager->get_actor(obj->quality); // horse or horse with rider
 if(!actor)
   return false;

 player_actor = items.actor_ref;
 if(player_actor->get_actor_num() == U6_SHERRY_ACTOR_NUM)
   {
    scroll->display_string("Sherry says: \"Eeek!!! I'm afraid of horses!\"\n");
    return true;
   }
 else if(player_actor->get_actor_num() == U6_BEHLEM_ACTOR_NUM)
   {
    scroll->display_string("BehLem says: \"Horses are for food!\"\n");
    return true;
   }
 else if(obj->obj_n == OBJ_U6_HORSE && player_actor->obj_n == OBJ_U6_HORSE_WITH_RIDER)
   {
    scroll->display_string("You're already on a horse!\n");
    return true;
   }
 else if(party->is_in_vehicle())
   {
    Game::get_game()->get_event()->display_not_aboard_vehicle(false);
    return true;
   }

 actor_obj = actor->make_obj();

 //dismount from horse. revert to original actor type.
 //Add a temporary horse actor onto the map.
 if(obj->obj_n == OBJ_U6_HORSE_WITH_RIDER)
   {
    actor->clear();
    if(actor == player_actor)
        actor->set_worktype(0x02); // PLAYER

    actor_obj->obj_n = actor->base_obj_n; //revert to normal actor type
    actor_obj->frame_n = actor->old_frame_n;

	actor->init_from_obj(actor_obj);

    // create a temporary horse on the map.
	actor_manager->create_temp_actor(OBJ_U6_HORSE, NO_OBJ_STATUS, obj->x, obj->y, obj->z, ACTOR_ALIGNMENT_DEFAULT, WORKTYPE_U6_ANIMAL_WANDER);
   }
 else if(!actor_manager->is_temp_actor(actor))// Try to mount horse. Don't use permenant Actors eg Smith, push-me pull-you
   {
    scroll->display_string("\nHorse not boardable!\n");
   }
 else // mount up.
   {
    if(UseCode::out_of_use_range(obj, true))
        return true;
    actor_manager->clear_actor(actor); //clear the temp horse actor from the map.

    actor_obj->obj_n = OBJ_U6_HORSE_WITH_RIDER;

	player_actor->move(actor_obj->x,actor_obj->y,actor_obj->z); //this will center the map window
    player_actor->init_from_obj(actor_obj);

	delete_obj(actor_obj);
   }

 return true;
}

bool U6UseCode::use_fan(Obj *obj, UseCodeEvent ev)
{
	uint8 wind_tbl[] = {4,5,6,7,1,2,3,0};
	uint8 wind_dir;
	Weather *weather = game->get_weather();
	scroll->display_string("\nYou feel a breeze.\n");

	wind_dir = weather->get_wind_dir();

	if(wind_dir == NUVIE_DIR_NONE)
		wind_dir = NUVIE_DIR_NW;
	
	//cycle through the wind directions.
	weather->set_wind_dir(wind_tbl[wind_dir]);

 return true;
}

/* USE: Sextant. Display Latitude/Longitude coords centered on LB's castle.
 */
bool U6UseCode::use_sextant(Obj *obj, UseCodeEvent ev)
{
 MapCoord location;
 char buf[18]; // "\nxxoS, xxoE\n"
 char lat, lon;
 uint16 x, y;

 if(ev != USE_EVENT_USE)
   return false;

 location = player->get_actor()->get_location();

 //only use sextant on surface level.
 if(location.z == 0)
   {
    x = location.x / 8;
    if(x > 38)
     {
      lon = 'E';
      x -= 38;
     }
    else
     {
      x = 38 - x;
      lon = 'W';
     }

    y = location.y / 8;
    if(y > 45)
     {
      lat = 'S';
      y -= 45;
     }
    else
     {
      y = 45 - y;
      lat = 'N';
     }

    sprintf(buf, "\n%d{%c, %d{%c\n", y, lat, x, lon);
    scroll->display_string(buf);
   }
 else
   scroll->display_string("\nNot usable\n");

 return(true);
}

bool U6UseCode::use_staff(Obj *obj, UseCodeEvent ev)
{
	 if(ev != USE_EVENT_USE)
	   return false;

	 if(obj->is_readied() == false)
	 {
		 scroll->display_string("\nNot readied.\n");
		 return true;
	 }

	 Obj *charge = obj->find_in_container(OBJ_U6_CHARGE, 0, OBJ_NOMATCH_QUALITY);

	 if(charge)
	 {
		 uint8 spell_num = charge->quality;
		 obj_manager->unlink_from_engine(charge);
		 delete_obj(charge);

		 Game::get_game()->get_event()->cast_spell_directly(spell_num);
	 }

	 return true;
}

/* Pass: Allow normal move if player's Quest Flag is set.
 */
bool U6UseCode::pass_quest_barrier(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_PASS)
        if(player->get_quest_flag() == 0)
        {
            // block everyone, only print message when player attempts to pass
            if(items.actor_ref == player->get_actor())
                scroll->message("\n\"Thou art not upon a Sacred Quest!\n"
                                "Passage denied!\"\n\n");
            return(false);
        }
    return(true);
}


/* LOOK: Get (possibly translate) book data for readable object. Disallow search
 * (return true) if book data was displayed.
 */
bool U6UseCode::look_sign(Obj *obj, UseCodeEvent ev)
{
    char *data;
    Book *book = game->get_book(); // ??

    if(ev == USE_EVENT_LOOK)
    {
        MapCoord obj_loc = MapCoord(obj->x, obj->y, obj->z);
        MapCoord player_loc = player->get_actor()->get_location();
        InterfaceType interface = game->get_map_window()->get_interface();
        bool too_far = (player_loc.distance(obj_loc) > 1 && interface == INTERFACE_NORMAL);
        bool blocked = (interface != INTERFACE_IGNORE_BLOCK
                        && !game->get_map_window()->can_get_obj(player->get_actor(), obj));
        if((obj->quality == 0 && obj->obj_n != OBJ_U6_BOOK) || (!obj->is_in_inventory()
          && (obj->obj_n == OBJ_U6_BOOK || obj->obj_n == OBJ_U6_SCROLL) && (too_far || blocked)))
        {
            scroll->display_string("\n");
            return true; // display prompt
        }
        // read
        if(items.actor_ref == player->get_actor())
        {
            scroll->display_string(":\n\n");
            uint8 book_num = obj->quality - 1;
            if(obj->quality == 0)
                book_num = 126;
            if((data = book->get_book_data(book_num)))
            {
/*
             // FIX Any alternate-font text is in < >, Runic is capitalized,
             // Gargish is lower-case. Translations follow untranslated text and
             // are wrapped in & &.
             if(data[0] == '<' && data[strlen(data)-1] == '>') //Britannian text is wrapped in '<' '>' chars
                {
                 scroll->display_string(&data[1],strlen(data)-2, 1); // 1 for britannian font.
                 scroll->display_string("\n",1);
                }
             else
                {
*/
             bool using_gump = game->is_using_text_gumps();
             if(using_gump)
             {
                 switch(obj->obj_n)
                 {
                     case OBJ_U6_BOOK:
                     case OBJ_U6_PICTURE:
                     case OBJ_U6_SCROLL:
                     case OBJ_U6_GRAVE:
                     case OBJ_U6_CROSS: // wooden cross used as grave marker (text like grave)
                     case OBJ_U6_BALLOON_PLANS:
                     case OBJ_U6_BOOK_OF_CIRCLES:
                     case OBJ_U6_CODEX:
                         game->get_view_manager()->open_scroll_gump(data,strlen(data));
                         break;
                     case OBJ_U6_SIGN:
                         if(strlen(data) > 20) // FIXME sign text needs to fit on multiple lines
                         {
                             using_gump = false;
                             break;
                         }
                         game->get_view_manager()->open_sign_gump(data,strlen(data)); break;
                     case OBJ_U6_SIGN_ARROW:

                     default:
                        using_gump = false;
                 }
             }
             if(!using_gump)
             {
            	 scroll->set_autobreak(true);
            	 scroll->display_string(data,strlen(data)); //normal font
            	 scroll->display_string("\n\t"); // '\t' = auto break off.
             }
             //scroll->set_autobreak(false);
//                }
             free(data);
            }

        }
        return true;
    }
    return false;
}


/* LOOK: Display the current time. Disallow search.
 */
bool U6UseCode::look_clock(Obj *obj, UseCodeEvent ev)
{
    GameClock *clock = game->get_clock();
    if(obj->obj_n == OBJ_U6_SUNDIAL
       && (clock->get_hour() < 5 || clock->get_hour() > 19))
        return(true); // don't get time from sundial at night
    if(ev == USE_EVENT_LOOK && items.actor_ref == player->get_actor())
    {
        scroll->display_string("\nThe time is ");
        scroll->display_string(clock->get_time_string());
        scroll->display_string("\n");
    }
    return(true);
}


/* test (need to determine use of true/false return)
 */
bool U6UseCode::look_mirror(Obj *obj, UseCodeEvent ev)
{
//    ViewManager *view_manager = game->get_view_manager();
    if(ev == USE_EVENT_LOOK && items.actor_ref == player->get_actor())
    {
        uint16 x, y;
        uint8 z;
        items.actor_ref->get_location(&x, &y, &z);
        if(x == obj->x && y > obj->y && y <= (obj->y + 2))
        {
            scroll->display_string("\nYou can see yourself!");
            game->get_event()->display_portrait(items.actor_ref);
        }
        scroll->display_string("\n");
        return(true);
    }
    return(false);
}


/* PASS: if not in party mode, say that you cannot enter and do normal move
 * else walk all party members to cave, give dungeon name, and move to dungeon
 */
bool U6UseCode::enter_dungeon(Obj *obj, UseCodeEvent ev)
{
    if(!party->contains_actor(items.actor_ref))
        return false;

    const char *prefix = "", *dungeon_name = "";
    uint16 x = obj->x, y = obj->y;
    uint8 z = obj->z;

    if(party->is_in_vehicle()) //don't enter if in a balloon.
      return true;
      
    if(!player->in_party_mode())
    {
        scroll->display_string("\n\nNot in solo mode.\n");
        return(true);
    }

    if(ev == USE_EVENT_USE && UseCode::out_of_use_range(obj, true))
        return true;

    if(obj->quality < 21)
        dungeon_name = (char *)u6_dungeons[obj->quality];
    if(obj->quality >= 1 && obj->quality <= 7)
        prefix = "dungeon ";
    else if(obj->quality >= 9 && obj->quality <= 11)
        prefix = "shrine of ";
    else
        prefix = "";

    party->dismount_from_horses();

    // don't activate if autowalking from linking exit
    if((ev == USE_EVENT_PASS || ev == USE_EVENT_USE) && items.actor_ref == player->get_actor() && !party->get_autowalk())
    {
        ActorManager *actor_manager = Game::get_game()->get_actor_manager();
        if(obj->quality != 0 && party->contains_actor(3) && actor_manager->get_actor(3)->is_alive())
        {
            // scroll->printf("%s says, \"This is the %s%s.\"\n\n",blah->name, prefix, dungeon_name);
            scroll->display_string("Shamino says, \"This is the ");
            scroll->display_string(prefix);
            scroll->display_string(dungeon_name);
            scroll->display_string(".\"\n\n");
            scroll->display_prompt();
        }
        MapCoord entrance(x, y, z);
        // going down
        if(z == 0) // from surface, do superchunk translation
        {
            x = (x & 0x07) | (x >> 2 & 0xF8);
            y = (y & 0x07) | (y >> 2 & 0xF8);
        }
        if(z < 5)
          z += 1;
        else
          z -= 1;

        MapCoord exit(x, y, z);
//        if(obj->obj_n == OBJ_U6_HOLE) // fall down hole faster
//            party->walk(&entrance, &exit, 100);
//        else
//            party->walk(&entrance, &exit);
        party->walk(&entrance, &exit, 100);
        game->get_weather()->set_wind_dir(NUVIE_DIR_NONE);
        return(true);
    }
    else if((ev == USE_EVENT_PASS || ev == USE_EVENT_USE) && party->get_autowalk()) // party can use now
        return(true);
    return(false);
}

bool U6UseCode::enter_moongate(Obj *obj, UseCodeEvent ev)
{
 /* shared between blue and red gates */
/* PASS: if not in party mode, say that you cannot enter and do normal move
 * else walk all party members to moongate and teleport.
 */
    uint16 x = obj->x, y = obj->y;
    uint8 z = obj->z;
    MapCoord exit(0,0,0);

    if(party->is_in_vehicle())
      return true;
    if(items.mapcoord_ref->x != x)
      return true; // don't step onto the left tile of a moongate

    if(!player->in_party_mode())
    {
        scroll->display_string("\nYou must be in party mode to enter.\n\n");
        scroll->display_prompt();
        return(true);
    }

    // don't activate if autowalking from linking exit
    if(ev == USE_EVENT_PASS && items.actor_ref == player->get_actor() && !party->get_autowalk())
    {
	if (obj->obj_n == OBJ_U6_RED_GATE ) 
	{
	  if(obj->quality > 25)
	  {
	    DEBUG(0,LEVEL_ERROR,"invalid moongate destination %d\n",obj->quality);
	    return false;
	  }
	  if(!party->has_obj(87, 0, false)) // make sure orb of moons is in party inventory
	  {
	  	scroll->display_string("\nYou forgot the Orb of the Moons!\n");
	  	return true;
	  }
	  if((obj->quality > 0 && obj->quality < 12) ||
	      (obj->quality > 14 && obj->quality < 26) ) //positions 0, 12, 13 and 14 go nowhere.
	  {
	    x = red_moongate_tbl[obj->quality].x; // set our moongate destination from the lookup table.
	    y = red_moongate_tbl[obj->quality].y;
	    z = red_moongate_tbl[obj->quality].z;
	  }
	  exit=MapCoord(x,y,z);
	} 
	else if (obj->obj_n == OBJ_U6_MOONGATE )
	{ 
	  // FIXME: Duplication from PartyView, this ought to be separated
	  /* we don't care if the moons are in the sky, 
	   * (to make permanent moongates work)
	   * if the moongate is there, it goes somewhere
	   */
	  Weather *weather = game->get_weather();
	  GameClock *clock = Game::get_game()->get_clock();
	  uint8 day=clock->get_day();
	  uint8 hour=clock->get_hour();
	  uint8 phaseTrammel=uint8(nearbyint((day-1)/TRAMMEL_PHASE)) % 8;
	  sint8 phaseb = (day-1) % uint8(nearbyint(FELUCCA_PHASE*8)) - 1;
	  uint8 phaseFelucca = (phaseb >= 0) ? phaseb : 0;
	  uint8 posTrammel = ((hour + 1) + 3*phaseTrammel) % 24;
	  uint8 posFelucca = ((hour - 1) + 3*phaseFelucca) % 24;
	  uint8 absTrammel=abs(posTrammel-12);
	  uint8 absFelucca=abs(posFelucca-12);
	  if (absTrammel<absFelucca) 
	  {
	    // Trammel wins. 
	    exit=weather->get_moonstone(8-phaseTrammel);
	  }
	  else
	  {
	    // Feluccality!
	    exit=weather->get_moonstone(8-phaseFelucca);
	  }
	  if (exit.x==0&&exit.y==0&&exit.z==0)
	  {
	    exit=MapCoord(x,y,z); // stay put.
	  }
	}
	party->walk(obj, &exit);
        return(true);
    }
    else if(ev == USE_EVENT_PASS && party->get_autowalk()) // party can use now
        if(party->contains_actor(items.actor_ref))
            return(true);
    return(true);
}


/* USE: Light powder keg if unlit
 * MESSAGE: Timed: Explode; Effect complete: delete powder keg
 */
bool U6UseCode::use_powder_keg(Obj *obj, UseCodeEvent ev)
{
	if(ev == USE_EVENT_USE)
	{
		game->get_script()->call_use_keg(obj);
	}
	return true;
}


/* Use: Fire! (block input, start cannonball anim, release input on hit)
 * Message: Effect complete. Return to prompt.
 * Move: Change direction if necessary
 */
bool U6UseCode::use_cannon(Obj *obj, UseCodeEvent ev)
{
    MapCoord *mapcoord_ref = items.mapcoord_ref;

    if(ev == USE_EVENT_USE)
    {
        scroll->display_string("\nFire!\n");
// FIXME: new UseCodeEffect(obj, cannonballtile, dir) // sets WAIT mode
        new CannonballEffect(obj); // sets WAIT mode
        // Note: waits for effect to complete and sends MESG_EFFECT_COMPLETE
        return(false);
    }
    else if(ev == USE_EVENT_MESSAGE)
    {
        if(*items.msg_ref == MESG_EFFECT_COMPLETE)
        {
            scroll->display_string("\n");
            scroll->display_prompt();
        }
        return(true);
    }
    else if(ev == USE_EVENT_MOVE)
    {
        // allow normal move
        if((obj->frame_n == 0 && mapcoord_ref->sy < 0)
           || (obj->frame_n == 1 && mapcoord_ref->sx > 0)
           || (obj->frame_n == 2 && mapcoord_ref->sy > 0)
           || (obj->frame_n == 3 && mapcoord_ref->sx < 0))
            return(true);
        else // aim cannon in new direction
        {
            if(mapcoord_ref->sy < 0)
                obj->frame_n = 0;
            else if(mapcoord_ref->sy > 0)
                obj->frame_n = 2;
            else if(mapcoord_ref->sx < 0)
                obj->frame_n = 3;
            else if(mapcoord_ref->sx > 0)
                obj->frame_n = 1;
            return(false);
        }
    }
    return(false);
}


/* USE: Hatch egg.
 */
bool U6UseCode::use_egg(Obj *obj, UseCodeEvent ev)
{
    EggManager *egg_manager = obj_manager->get_egg_manager();
    bool success = egg_manager->spawn_egg(obj, NUVIE_RAND() % 100);
    if(items.actor_ref)
        scroll->display_string(success ? "\nSpawned!\n" : "\nNo effect.\n");
    return(true);
}

/* USE: Open spellbook for casting, if equipped.
 * LOOK: Open for spell inspection.
 */
bool U6UseCode::use_spellbook(Obj *obj, UseCodeEvent ev)
{
  if(ev == USE_EVENT_USE)
  {
    Game::get_game()->get_event()->endAction(); // FIXME: this should call Magic directly
    Game::get_game()->get_event()->newAction(CAST_MODE);
    if(obj->is_readied()) 
    {
      /* TODO open spellbook for casting */

    }
  }
  else
    if(ev == USE_EVENT_LOOK)
  {
      scroll->display_string("\n");
    /* TODO open spellbook for reading */

  }
  return(true);
}

/* Use: Light torch if readied or on the ground.
 * Ready: Get a torch from a stack and equip it.
 * Unready: Unlight torch.
 * Get: Equip torch if lit
 * Drop: Unlight torch
 */
bool U6UseCode::torch(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_USE)
    {
        if(obj->frame_n == 1)
        {
            extinguish_torch(obj);
            return(true);
        }
        // light
        if(obj->is_on_map())
        {
            Obj *torch = obj_manager->get_obj_from_stack(obj, 1);
            if(torch != obj)
                obj_manager->add_obj(torch, true); // keep new one on map
            light_torch(torch);
            return true;
        }
        else // so is readied or in inventory
        {
            Obj *torch = obj;
            Actor *actor;
            if(obj->is_in_inventory() == false) // container on map
                actor = actor_manager->get_player();
            else
                actor = actor_manager->get_actor_holding_obj(obj);
            bool can_light_it = true; // only set FALSE on some error
            bool in_container = obj->is_in_container();

            if(!obj->is_readied())
            {
                torch = obj_manager->get_obj_from_stack(obj, 1);
                if(torch != obj) // keep new one in inventory
                    actor->inventory_add_object_nostack(torch);

                // ready it
//                actor = actor_manager->get_actor_holding_obj(torch);
                can_light_it = actor->add_readied_object(torch);
            }

            if(can_light_it) // assume torch is readied
            {
                assert(torch->is_readied());
                light_torch(torch);
            }
            else
            {
                assert(torch->qty == 1);
                if(in_container) // need old location
                    obj_manager->moveto_container(torch, obj->get_container_obj());
                else if(torch->is_in_inventory()) //  assume it's not stacked
                {
                    actor->inventory_remove_obj(torch);
                    actor->inventory_add_object(torch); // restack here
                }
                scroll->display_string("\nNo free hand to hold the torch.\n");
            }
        }
    }
    else if(ev == USE_EVENT_READY)
    {
        if(obj->is_readied()) // remove
        {
            if(obj->frame_n == 1)
            {
                extinguish_torch(obj);
                return(false); // destroyed
            }
        }
        else // equip (get one from the stack)
        {
            if(obj->qty > 1 && obj->frame_n == 0) // don't change the quantity of lit torches
            {
                Obj *torch = obj_manager->get_obj_from_stack(obj, obj->qty - 1);
                assert(torch != obj); // got a new object from the obj stack
                if(obj->is_in_container())
                    obj_manager->moveto_container(torch, obj->get_container_obj(), false);
                else if(obj->is_in_inventory()) // keep extras in inventory
                {
                    actor_manager->get_actor_holding_obj(torch)->inventory_add_object_nostack(torch);
                }
            }
        }
        return(true); // equip or remove to inventory
    }
    else if(ev == USE_EVENT_GET)
    {
        if(obj->frame_n == 0) // unlit: may get normally
            return(true);
        toggle_frame(obj); // unlight
        obj->qty=1;
        obj_manager->remove_obj_from_map(obj); // add to inventory and USE
        items.actor_ref->inventory_add_object(obj); // will unstack in USE
        scroll->display_string("\n");
        torch(obj, USE_EVENT_USE);
        return(false); // ready or not, handled by usecode
    }
    else if(ev == USE_EVENT_DROP)
    {
        if(obj->frame_n == 0) // unlit: normal drop
            return(true);
        extinguish_torch(obj);
        return(false); // destroyed
    }

    return(true);
}

/* Torches disappear when extinguished. */
void U6UseCode::extinguish_torch(Obj *obj)
{
    assert(obj->frame_n == 1);

//  handled by Actor::inventory_remove_obj()
//    if(obj->is_in_inventory_old())
//        actor_manager->get_actor_holding_obj(obj)->subtract_light(TORCH_LIGHT_LEVEL);
    if(obj->is_readied())
    {
        Actor *owner = actor_manager->get_actor_holding_obj(obj);
        if((owner->is_in_party() || owner == player->get_actor()) && owner->is_alive()) 
        {
            if(owner->get_hp() == 0) // Avatar during Kal Lor item removal
            {
                owner->remove_readied_object(obj, false);
                party->subtract_light_source();
                game->get_map_window()->updateBlacking();
                return;
            }
        }
        else // don't extinguish on death or leaving the party
        {
            game->get_map_window()->updateBlacking(); // might need this on death
            return;
        }
    }

    scroll->display_string("\nA torch burned out.\n");
    destroy_obj(obj, 0, false);
    game->get_map_window()->updateBlacking();
}

void U6UseCode::light_torch(Obj *obj)
{
    assert(obj->qty == 1);
    assert(obj->frame_n == 0);
    assert(obj->is_readied() || obj->is_on_map());
    toggle_frame(obj); // light
    obj->status |= OBJ_STATUS_LIT;
    Actor *owner = NULL;
    if(obj->is_readied())
    {
        owner= actor_manager->get_actor_holding_obj(obj);
        owner->add_light(TORCH_LIGHT_LEVEL);
    }

    obj->qty = 0xc8; //torch duration. updated in lua advance_time()
    if(!owner || owner->is_in_party() || owner == player->get_actor())
        scroll->display_string("\nTorch is lit.\n");
    game->get_map_window()->updateBlacking();
}

bool U6UseCode::process_effects(Obj *container_obj, Actor *actor)
{
 Obj *temp_obj;
 U6Link *obj_link, *temp_link;

 /* Test whether this object has items inside it. */
 if(container_obj->container != NULL)
    {
     for(obj_link = container_obj->container->end(); obj_link != NULL; )
       {
        temp_obj = (Obj*)obj_link->data;

        if(temp_obj->obj_n == OBJ_U6_EFFECT)
          {
           temp_link = obj_link->prev;
           game->get_script()->call_actor_use_effect(temp_obj, actor); //Note this call unlinks the effect object.
           obj_link = temp_link;
          }
        else
           obj_link = obj_link->prev;
      }
    }

 return true;
}

/* Use: Display Peer effect, showing a map of the area around the player.
   Message: Delete 1 gem. */
bool U6UseCode::use_peer_gem(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_MESSAGE && *items.msg_ref == MESG_EFFECT_COMPLETE)
    {
        destroy_obj(obj, 1);
        scroll->display_string("\n");
        scroll->display_prompt();
        return true;
    }

    if(ev != USE_EVENT_USE)
        return true;

    uint16 x, y;
    uint8 z;
    player->get_location(&x, &y, &z);
    game->get_event()->close_gumps();
    new PeerEffect(x-(x%8)-18,y-(y%8)-18,z, obj); // wrap to chunk boundary,
                                                  // and center in 11x11 MapWindow
    return false; // no prompt
}

/* Ready: Apply ring's status effect to actor.
   Unready: Cancel status effect. */
bool U6UseCode::magic_ring(Obj *obj, UseCodeEvent ev)
{
    Actor *actor = obj->get_actor_holding_obj();
    if(!actor)
        actor = player->get_actor();
    if(actor->inventory_get_readied_object(ACTOR_HAND) != NULL
       && actor->inventory_get_readied_object(ACTOR_HAND) != obj
       && actor->inventory_get_readied_object(ACTOR_HAND_2) != NULL
       && actor->inventory_get_readied_object(ACTOR_HAND_2) != obj)
        return true;
    uint8 num_readied = actor->count_readied_objects(obj->obj_n, 0);
//    if(obj->obj_n == OBJ_U6_REGENERATION_RING)
//        actor_manager->get_actor_holding_obj(obj)->??? no visual effect
//    if(obj->obj_n == OBJ_U6_PROTECTION_RING)
//        actor_manager->get_actor_holding_obj(obj)->??? no visual effect
    if(obj->obj_n == OBJ_U6_INVISIBILITY_RING)
        actor->set_invisible((obj->is_readied()&&num_readied==1)?false:true);
    return true; // do normal ready/unready
}

bool U6UseCode::storm_cloak(Obj *obj, UseCodeEvent ev)
{
    Actor *actor = obj->get_actor_holding_obj();
    if(!actor)
        actor = player->get_actor();
    if(actor->inventory_get_readied_object(ACTOR_BODY) != NULL
       && actor->inventory_get_readied_object(ACTOR_BODY) != obj)
        return true;

    AsyncEffect *e = new AsyncEffect(new TileBlackFadeEffect(actor, 9, 20)); //FIXME hardcoded values.
	e->run();

    if(obj->is_readied() == false)
    {
    	Game::get_game()->get_clock()->set_timer(GAMECLOCK_TIMER_U6_STORM, 0x14);
    }
    else
    {
    	Game::get_game()->get_clock()->set_timer(GAMECLOCK_TIMER_U6_STORM, 0);
    }

    return true;
}

/* Unready/Drop/Move: Don't allow removal. */
bool U6UseCode::amulet_of_submission(Obj *obj, UseCodeEvent ev)
{
    if(obj->is_readied())
    {
        scroll->display_string("\nMagical energy prevents you from removing the amulet.\n");
        return false;
    }
    return true;
}

/* Use: Learn Gargish! */
bool U6UseCode::gargish_vocabulary(Obj *obj, UseCodeEvent ev)
{
    if(ev == USE_EVENT_USE)
    {
        scroll->display_string("\n");
        scroll->display_string("You study the scroll!\n");
        player->set_gargish_flag(true);
    }
    return true;
}

/* LOOK: Print the name of a holy brazier, and not the normal description. */
bool U6UseCode::holy_flame(Obj *obj, UseCodeEvent ev)
{
    if(obj->quality == 0 || obj->quality > 3)
        return true; // use normal description
    scroll->display_string("\nThe flame of ");
    if(obj->quality == 1)
        scroll->display_string("truth");
    if(obj->quality == 2)
        scroll->display_string("love");
    if(obj->quality == 3)
        scroll->display_string("courage");
    scroll->display_string(".\n");
    return false;
}

bool U6UseCode::cannot_unready(Obj *obj)
{
	if(!obj->is_readied())
		return false;
	if(obj->obj_n == OBJ_U6_AMULET_OF_SUBMISSION
	   || (obj->obj_n == OBJ_U6_TORCH && obj->frame_n == 1))
		return true;

	return false;
}
bool U6UseCode::use_harpsichord(Obj *obj, UseCodeEvent ev) {
  if(ev == USE_EVENT_SEARCH) {
    return search_container(obj);
  }
  return play_instrument(obj, ev);
}
