local USE_EVENT_USE = 0x01
local USE_EVENT_MOVE = 0x40
local USE_EVENT_READY = 0x0100

function search_container(obj)
	local child
	for child in container_objs(obj) do
		Obj.moveToMap(child, obj.x, obj.y, obj.z)
	end
end

function use_door(obj, actor)
	if bit32.btest(obj.quality, 0x80) then
	  printl("IT_IS_LOCKED")
	  return
	end

   if map_get_actor(obj.x, obj.y, obj.z) ~= nil then
      printl("BLOCKED")
      return
   end
	
   obj.frame_n = bit32.bxor(obj.frame_n, 2)

end

function use_crate(obj, target_obj, actor)
	if target_obj.frame_n == 2 then
		target_obj.frame_n = 1
		printl("YOU_PRIED_THE_NAILS_OUT_OF_THE_CRATE")
	else
		if target_obj.frame_n == 1 and obj.obj_n == 273 then
			target_obj.frame_n = 2
			printl("YOU_NAIL_THE_CRATE_SHUT")
		else
			printl("IT_HAS_NO_EFFECT")
		end
	end
end

function use_container(obj, actor)
	if obj.frame_n == 2 then
		if obj.obj_n == 86 or obj.obj_n == 284 then
			printl("IT_IS_NAILED_SHUT")
		else
			printl("IT_IS_LOCKED")
		end
	else
		if obj.frame_n == 1 then
			obj.frame_n = 0
			search_container(obj)
		else
			obj.frame_n = 1
		end
	end
end

function use_hammer_on_oxium_geode(obj, target_obj, actor)
   if target_obj.frame_n ~= 0 then
      printl("IT_HAS_NO_EFFECT")
      return
   end

   play_md_sfx(0x20)
   target_obj.frame_n = 1 --break the geode open

   if target_obj.on_map then
      local oxium = Obj.new(131) --OBJ_BLOB_OF_OXIUM
      oxium.qty = math.random(1, 50) + math.random(1, 50)
      Obj.moveToMap(oxium, target_obj.x, target_obj.y, target_obj.z)
   else
      --FIXME add to actor's inventory
      local oxium = Actor.inv_get_obj_n(actor, 131) --OBJ_BLOB_OF_OXIUM
      local qty = math.random(1, 50) + math.random(1, 50)
      if oxium ~= nil then
         oxium.qty = oxium.qty + qty
      else
         oxium = Obj.new(131, 0, 0, qty) --OBJ_BLOB_OF_OXIUM
         Actor.inv_add_obj(actor, oxium)
      end
   end
   
end

function use_prybar_on_hatch(obj, target_obj, actor)

	if actor.actor_num ~= 1 then
		printfl("IS_NOT_STRONG_ENOUGH", actor.name)
		return
	end
	
	local tesla = Actor.get(16)
	if Actor.get_talk_flag(tesla, 4) == false then
		Actor.set_talk_flag(tesla, 2)
		Actor.talk(tesla)
	else
		play_midgame_sequence(1)
		
		Actor.set_talk_flag(tesla, 5)
		target_obj.obj_n = 428
		target_obj.frame_n = 0;
		target_obj.x = target_obj.x + 1
		target_obj.y = target_obj.y + 1
		
		local blood = Actor.get(18)
		Actor.set_talk_flag(blood, 3)
		Actor.set_talk_flag(blood, 6)
		Actor.talk(blood)
	end
end

function use_sextant(obj, actor)
	if actor.z ~= 0 then
		printl("NOT_WHILE_UNDERGROUND")
		return
	end

	local lat_str = "N"
	local long_str = "W"
	
	local lat = math.modf(((actor.y - 512) * 240) / 1024)
	local long = math.modf(((actor.x - 512) * 360) / 1024)
	
	if lat > 0 then
		lat_str = "S"
	else
		if lat == 0 then
			lat_str = " "
		end
	end
	
	if long == 180 or long == -180 or long == 0 then
		long_str = " "
	else
		if long < 0 then
			long_str = "E"
		end
	end
	
	lat = math.abs(lat)
	long = 180 - math.abs(long)
	printl("YOU_ARE_SOMEWHERE_NEAR")
	print(" \nLat:" ..lat.." "..lat_str.."\nLong:"..long.." "..long_str.."\n")
end

function use_berry(obj, actor)
   local actor_num = actor.actor_num
   if actor_num == 6 then
      printl("A_MECHANICAL_PERSON_CANT_EAT_BERRIES")
      return
   end
   
   play_md_sfx(0x32)
   local berry_type = obj.obj_n - 73 --OBJ_BERRY
   local first_berry = true
   
   if (berry_type == 0 and actor_is_affected_by_purple_berries(actor_num))
      or (berry_type == 1 and actor_is_affected_by_green_berries(actor_num))
      or (berry_type == 2 and actor_is_affected_by_brown_berries(actor_num)) then
      printl("YOU_EAT_A_MARTIAN_BERRY_YOU_FEEL_AN_INCREASE_IN_THE_STRANGE")
   else
      printl("YOU_EAT_A_MARTIAN_BERRY_YOU_FEEL_A_STRANGE")
   end

   if berry_type == 0 then
      printl("RELATIONSHIP_WITH_THINGS_AROUND_YOU")
      actor_increment_purple_berry_count(actor_num)
   elseif berry_type == 1 then
      printl("SENSITIVITY_TO_THE_FEELINGS_OF_OBJECTS_AROUND_YOU")
      actor_increment_green_berry_count(actor_num)
   elseif berry_type == 2 then
      printl("SENSE_OF_YOUR_SPATIAL_LOCATION")
      actor_increment_brown_berry_count(actor_num)
   elseif berry_type == 3 then
      printl("SUDDEN_FLUSH_DIZZINESS_AND_NAUSEA")
      actor.poisoned = false
      Actor.hit(actor, math.random(5, 0x14) + math.random(5, 0x14))
      local counter = actor_get_blue_berry_counter()
      actor_set_blue_berry_counter(counter + math.random(1, 2))
   end
   
   if obj.qty == 1 then
      Obj.removeFromEngine(obj)
   else
      obj.qty = obj.qty - 1
   end
end

function use_misc_text(obj)

	local obj_n = obj.obj_n
	if obj_n == 65 or obj_n == 66 or obj_n == 67 or obj_n == 263 or obj_n == 267 or obj_n == 327 then
		printl("YOU_CANT_DIG_ANY_DEEPER")
	elseif obj_n == 427 then
		printl("IT_IS_STUCK")
	elseif obj_n == 288 then
		printl("THE_CONTROL_PANEL_OPERATES_THE_DREAM_MACHINE")
	elseif obj_n == 199 then
		printl("YOU_NEED_TO_USE_PLIERS_TO_ATTACH_THE_CABLE")
	elseif obj_n == 131 then
		printl("YOU_WILL_CHEW_IT_INSTINCTIVELY")
	elseif obj_n == 184 then
		printl("THE_PLATE_IN_THE_CAMERA_HAS_ALREADY_BEEN_EXPOSED")
	elseif obj_n == 442 or obj_n == 443 or obj_n == 444 then
		printl("YOU_MUST_USE_A_SHOVEL_TO_MOVE_THAT")
	elseif obj_n == 293 then
		printl("YOU_CANT_DETERMINE_HOW_TO_READ_THE_TIME")
	elseif obj_n == 77 or obj_n == 78 then
		printl("THE_BERRIES_ARE_NOT_EDIBLE")
	elseif obj_n == 323 then
		printl("YOU_DONT_KNOW_HOW_IT_WORKS")
	else
		printl("IT_HAS_NO_EFFECT")
	end
end

function get_pile_obj_num(map_tile)
	if (map_tile >= 32 and map_tile <= 36) or map_tile == 12 or map_tile == 13 or (map_tile >= 40 and map_tile <= 63) then
		return 258 --OBJ_DIRT_PILE
	elseif map_tile == 124 or map_tile == 125 or map_tile == 127 then
		return 256 --OBJ_CHUNK_OF_ICE
	end

	return 0 --CANNOT DIG HERE 
end

function get_free_location_around_actor(actor)
	local x_tbl = {-1,0,1,1,1,0,-1,-1}
	local y_tbl = {-1,-1,-1,0,1,1,1,0}
	local pos = {}
	local i

	pos.z = actor.z
	
	for i=1,8 do
		pos.x = actor.x + x_tbl[i]
		pos.y = actor.y + y_tbl[i]
		if map_can_put(pos.x,pos.y,pos.z) == true and map_get_obj(pos.x,pos.y,pos.z) == nil then
			return pos
		end
	end
	
	return nil
end

function use_tool_on_ground(obj, target_obj, actor, target_x, target_y, target_z)
	if target_obj ~= nil then
		--check tile flag here.
		printl("THE_GROUND_IS_NOT_CLEAR_FOR_DIGGING")
		return
	end
		
	local map_tile = map_get_tile_num(target_x, target_y, target_z)
	
	local pile_obj_num = get_pile_obj_num(map_tile)
	
	if pile_obj_num == 0 then
		printl("IT_HAS_NO_EFFECT")
		return
	end

	local hole_obj_num = 257
	
	if pile_obj_num == 256 then
		hole_obj_num = 255
	end
	
	local hole = Obj.new(hole_obj_num)
	hole.temporary = true
	Obj.moveToMap(hole, target_x, target_y, target_z)

	local loc = get_free_location_around_actor(actor)
	
	if loc ~= nil then
		local pile = Obj.new(pile_obj_num)
		pile.temporary = true
		Obj.moveToMap(pile, loc.x, loc.y, loc.z)
	else
		Obj.removeFromEngine(hole)
		printl("YOU_CANT_DIG_HERE")
	end
end

function use_shovel_on_pile_to_hole(obj, target_obj, to_obj, actor)
	Obj.removeFromEngine(target_obj)
	Obj.removeFromEngine(to_obj)

	play_md_sfx(0x1b)
   play_md_sfx(0x1b)
	printl("YOU_FILLED_IN_THE_HOLE")
end

function use_shovel_on_ore_to_container(obj, target_obj, to_obj, actor)
   print("\n")
   local ore_quality = get_ore_container_quality(target_obj.obj_n)
   if to_obj.obj_n == 268 then --OBJ_MARTIAN_WHEEL_BARROW
      if to_obj.qty ~= 1 then
         play_md_sfx(0x1b)
         to_obj.qty = 1
         to_obj.quality = ore_quality
         printfl("YOU_PUT_THE_ORE_INTO_THE_WHEELBARROW", target_obj.name)
         Obj.removeFromEngine(target_obj)
      else
         printl("THERE_IS_NO_MORE_ROOM")
         play_md_sfx(5)
         return false
      end
   elseif to_obj.obj_n == 410 then --OBJ_RAIL_CAR
      local qty = to_obj.qty
      if to_obj.qty < 7 then
         if to_obj.qty > 0 and to_obj.quality ~= ore_quality then
            printl("YOU_CANT_MIX_DIFFERENT_THINGS_IN_THE_SAME_LOAD")
            play_md_sfx(5)
            return false
         else
            to_obj.quality = ore_quality
            to_obj.qty = to_obj.qty + 1
            if to_obj.qty == 1 or to_obj.qty == 7 then
               to_obj.frame_n = to_obj.frame_n + 2
            end
            printfl("YOU_PUT_THE_ORE_INTO_THE_RAIL_CAR", target_obj.name)
            Obj.removeFromEngine(target_obj)
         end
      else
         printl("THERE_IS_NO_MORE_ROOM")
         play_md_sfx(5)
         return false
      end
   end
   
   return true
end

function use_shovel_to_unload_container(obj, target_obj, to_obj, actor)
   if target_obj.qty == 0 then
      printl("THERE_IS_NOTHING_TO_UNLOAD")
      play_md_sfx(5)
      return
   end
   
   local ore = Obj.new(get_obj_num_from_ore_quality(target_obj.quality))
   
   --FIXME if to_obj == nil do something
   
   local success_flag = false
   if to_obj.obj_n == 268 or to_obj.obj_n == 410 then --OBJ_MARTIAN_WHEEL_BARROW
      success_flag = use_shovel_on_ore_to_container(obj, ore, to_obj, actor)
   elseif to_obj.obj_n == 233 then --OBJ_FURNACE
      success_flag = use_shovel_on_ore_to_furnace(obj, ore, to_obj, actor)
   elseif to_obj.obj_n == 257 then --OBJ_HOLE
      --FIXME need to wire up this logic.
   else
      --FIXME need to implement burying logic
   end
   
   if success_flag then
      target_obj.qty = target_obj.qty - 1
      if target_obj.obj_n == 410 then --OBJ_RAIL_CAR
         if target_obj.qty == 6 or target_obj.qty == 0 then
            target_obj.frame_n = target_obj.frame_n - 2
         end
      end
   end 
end

function use_shovel_on_ore_to_furnace(obj, target_obj, to_obj, actor)
   local obj_n = target_obj.obj_n
   
   play_md_sfx(0x1b)
   
   Obj.removeFromEngine(target_obj)
   if obj_n == 444 then --OBJ_PILE_OF_COAL
      if to_obj.frame_n < 4 then
         activate_power_system()
      end
   else
      printl("IT_HAS_NO_EFFECT")
   end
   
   return true
end

function activate_power_system()
   printl("THE_COAL_BEGINS_MOVING_DOWN_THE_BELT")
   Actor.set_talk_flag(0x73, 2)
   Actor.set_talk_flag(0x71, 3)
   activate_power_update_tiles()
   update_conveyor_belt(false)
end

function activate_power_update_tiles()
   anim_play(9)
   anim_play(10)
   anim_play(11)
   anim_play(12)
   local loc = player_get_location()
   for obj in find_obj(loc.z, 208) do --OBJ_CRACK
      if obj ~= nil then
         if map_get_obj(obj.x, obj.y, obj.z, 209) == nil then
            local steam = Obj.new(209, 1)
            Obj.moveToMap(steam, obj.x, obj.y+1, obj.z)
         end
      end
   end

   for obj in find_obj(loc.z, 233) do --OBJ_FURNACE
      if obj ~= nil then
         local frame_n = obj.frame_n
         if frame_n == 1 or frame_n == 3 then
            obj.frame_n = frame_n + 4
         end
      end
   end

   for obj in find_obj(loc.z, 154) do --OBJ_LAMP
      if obj ~= nil then
         obj.frame_n = 1
      end
   end
   for obj in find_obj(4, 154) do --OBJ_LAMP
      if obj ~= nil then
         obj.frame_n = 1
      end
   end
end

function shutdown_power_update_tiles()
   anim_stop(9)
   anim_stop(10)
   anim_stop(11)
   anim_stop(12)
   local loc = player_get_location()
   for obj in find_obj(loc.z, 209) do --OBJ_STEAM_LEAK
      if obj ~= nil then
         Obj.removeFromEngine(obj)
      end
   end

   for obj in find_obj(loc.z, 233) do --OBJ_FURNACE
      if obj ~= nil then
         local frame_n = obj.frame_n
         if frame_n == 5 or frame_n == 7 then
            obj.frame_n = frame_n - 4
         end
      end
   end

   for obj in find_obj(loc.z, 154) do --OBJ_LAMP
      if obj ~= nil then
         obj.frame_n = 0
      end
   end
   for obj in find_obj(4, 154) do --OBJ_LAMP
      if obj ~= nil then
         obj.frame_n = 0
      end
   end
end

function update_conveyor_belt(can_stop)
   if Actor.get_talk_flag(0x73, 2) == false then
      return
   end
   local player_loc = player_get_location()
   
   if player_loc.z ~= 5 then
      return
   end

   --NOTE. The original game used generalised logic to search for conveyors.
   -- I haven't used that here as there is only one conveyor in the game.

   local x = 0x3c
   local y = 0x63
   local z = 5
   
   local conveyor = map_get_obj(x, y, z, 188) --OBJ_CONVEYOR_BELT
   
   while conveyor ~= nil do
      if conveyor.frame_n == 2 then
         local seam = map_get_obj(x, y, z, 189) --OBJ_CONVEYOR_BELT1
         if seam ~= nil then
            Obj.removeFromEngine(seam)
         end
      elseif conveyor.frame_n == 0 then
         if conveyor.qty == 0 then
            local seam = Obj.new(189)
            Obj.moveToMap(seam, x, y, z)
            conveyor.qty = 4
         end
         conveyor.qty = conveyor.qty - 1
      end
      
      local seam = map_get_obj(x, y, z, 189) --OBJ_CONVEYOR_BELT1
      if seam ~= nil then
         seam.x = seam.x + 1
      end

      for obj in objs_at_loc(x, y, z) do
         if obj.obj_n == 447 or obj.weight > 0 then --OBJ_HUGE_LUMP_OF_COAL
            obj.x = obj.x + 1
         end
      end

      local actor = map_get_actor(x, y, z)
      if actor ~= nil then
         Actor.move(actor, x+1, y, z)
      end

      x = x - 1
      conveyor = map_get_obj(x, y, z, 188) --OBJ_CONVEYOR_BELT
   end
   
   
   if can_stop and Actor.get_talk_flag(0x71, 3) then
      if math.random(0, 6) == 0 then
         printl("THE_CONVEYOR_BELT_STOPS")
         Actor.clear_talk_flag(0x73, 2)
         Actor.clear_talk_flag(0x71, 3)
         shutdown_power_update_tiles()
      end
   end
   
   
end

function midgame_cutscene_2()
   play_midgame_sequence(2)
   
   for tower in find_obj(0, 201) do --OBJ_TOWER_TOP
      if tower.x >= 0x3d0 and tower.x <= 0x3f0 and tower.y >= 0x1d0 and tower.y <= 0x1e7 then
         tower.frame_n = 4 + (tower.frame_n % 4)
      end
   end
   
   for cable in find_obj(0, 214) do --OBJ_POWER_CABLE
      if cable.x >= 0x3d0 and cable.x <= 0x3f0 and cable.y >= 0x1d0 and cable.y <= 0x1e7 then
         cable.obj_n = 215
      end
   end   
end

function use_fixed_belt_on_bare_rollers(obj, target_obj, actor)
   local start_obj = nil
   local rollers = target_obj
   while rollers ~= nil do
      if rollers.frame_n == 0 then
         start_obj = rollers
         break
      end
      rollers = map_get_obj(rollers.x-1,rollers.y,rollers.z, rollers.obj_n)
   end
   
   if start_obj == nil then
      printl("OOOPS_THESE_ROLLERS_CAN_NEVER_BE_FIXED")
      return
   end
   
   rollers = start_obj
   local i = 4
   while rollers ~= nil do
      rollers.obj_n = 188
      if i == 0 then
         i = 4
         local belt_join_obj = Obj.new(189)
         Obj.moveToMap(belt_join_obj, rollers.x, rollers.y, rollers.z)
      else
         i = i - 1
      end
      rollers = map_get_obj(rollers.x+1,rollers.y,rollers.z, 192) --OBJ_BARE_ROLLERS
   end
   
   Obj.removeFromEngine(obj)
   Actor.set_talk_flag(0x72, 2)
end

function use_ruby_slippers(obj, actor)
   if obj.readied == false then
      --FIXME check that we can ready this object.
      Obj.removeFromEngine(obj)
      Actor.inv_ready_obj(actor, obj)
      return
   end
   
   if obj.quality == 2 then
      printl("YOU_MAY_USE_THE_RUBY_SLIPPERS_TO_GO_HOME")
      local input = input_select("yn", false)
      if input == "Y" then
         play_end_sequence()
      else
         play_midgame_sequence(13)
      end
   else
      printl("CLICK")
      obj.quality = obj.quality + 1
   end
end

function foes_are_nearby()
   local loc = player_get_location()
   local i
   for i=1,0xff do
      local actor = Actor.get(i)
      if actor.alive and (actor.align == ALIGNMENT_EVIL or actor.align == ALIGNMENT_CHAOTIC) and actor.z == loc.z then
         if get_wrapped_dist(actor.x, loc.x) < 40 and get_wrapped_dist(actor.y, loc.y) < 40 then
            return true
         end
      end
   end
   
   return false
end

function is_target_visible_to_actor(actor, target)
   --FIXME
   return true
end

function npcs_are_nearby()
   local loc = player_get_location()
   local i
   local player = Actor.get_player_actor()
   for i=1,0xff do
      local actor = Actor.get(i)
      if actor.alive and actor.asleep == false and actor.in_party == false and actor.z == loc.z then
         if get_wrapped_dist(actor.x, loc.x) < 6 and get_wrapped_dist(actor.y, loc.y) < 6 and is_target_visible_to_actor(player, actor) then
            return actor
         end
      end
   end

   return nil
end

function is_actor_able_to_talk_to_player(actor)
   local player = Actor.get_player_actor()
   local loc = player_get_location()
   if actor.alive and actor.asleep == false and actor.z == loc.z then
      if get_wrapped_dist(actor.x, loc.x) < 6 and get_wrapped_dist(actor.y, loc.y) < 6 and is_target_visible_to_actor(player, actor) then
         return true
      end
   end
   
   return false
end

function rest_heal_party(hours_to_rest)
   local actor
   for actor in party_members() do
      local hp = math.random(3,8) + math.random(3,8) * math.floor(hours_to_rest / 2)
      local max_hp = actor_get_max_hp(actor)
      if actor.hp + hp > max_hp then
         actor.hp = max_hp
      else
         actor.hp = actor.hp + hp
      end
   end
end

function rest_level_up_actor(actor)
   if actor.actor_num > 15 then
      return
   end
   
   local exp_level_tbl = {
   [0] = 0,
   [1] = 100,
   [2] = 200,
   [3] = 400,
   [4] = 800,
   [5] = 1600,
   [6] = 3200,
   [7] = 6400,
   [8] = 32767,
}

   if actor.exp <= exp_level_tbl[actor.level] then
      return
   end
   
   actor.level = actor.level + 1
   
   local max_hp = actor_get_max_hp(actor)
   if actor.hp + 30 > max_hp then
      actor.hp = max_hp
   else
      actor.hp = actor.hp + 30
   end
   
   Actor.show_portrait(actor)
   
   local obj_n = actor.obj_n
   local gender = math.random(0,1)
   if obj_n == 342 or obj_n == 343 or obj_n == 345 or (obj_n >= 347 and obj_n <= 353) then
      gender = 0
   elseif obj_n == 344 or obj_n == 346 or (obj_n >= 354 and obj_n <= 357) then
      gender = 1
   end
   
   local gender_pronoun = "He"
   if gender == 1 then
      gender_pronoun = "She"
   end
   
   printfl("HAS_A_DREAM", actor.name)
   printfl("SEES_THREE_STONE_OBELISKS", gender_pronoun)
   printfl("FEELS_DRAWN_TO_ONE_OF_THE_OBELISKS", gender_pronoun)
   printfl("DOES_TOUCH_THE_OBELISK", actor.name)
   printl("WHICH_BHS")

   local answer = input_select("bhs", false)
   
   if answer == "B" then
      if actor.int < 30 then
         actor.int = actor.int + 1
      end
   elseif answer == "H" then
      if actor.dex < 30 then
         actor.dex = actor.dex + 1
      end   
   elseif answer == "S" then
      if actor.str < 30 then
         actor.str = actor.str + 1
      end  
   end
   
end

function use_tent(obj, actor)
   if player_is_in_solo_mode() then
      printl("NOT_WHILE_IN_SOLO_MODE")
      play_md_sfx(5)
      return
   end

   if g_in_dream_mode then
     printl("YOU_CANT_SLEEP_IN_A_DREAM")
     play_md_sfx(5)
     return
  end
  
   local tent_loc = {}
  
   if obj.on_map then
      tent_loc.x = obj.x
      tent_loc.y = obj.y
      tent_loc.z = obj.z
   else
      tent_loc = player_get_location()
   end
   
   local x, y
   
   for y = tent_loc.y - 2, tent_loc.y do
      for x = tent_loc.x - 1, tent_loc.x + 1 do
         local map_obj = map_get_obj(x,y,tent_loc.z)
         if map_obj ~= nil and map_obj.obj_n ~= 106 then
            if tile_get_flag(map_obj.tile_num, 3, 4) == false then
               printfl("TENT_OBJ_IN_THE_WAY", map_obj.name)
               play_md_sfx(5)
               return
            end
         end
      end
   end

   for y = tent_loc.y - 2, tent_loc.y do
      for x = tent_loc.x - 1, tent_loc.x + 1 do
         if tile_get_flag(map_get_tile_num(x,y,tent_loc.z), 1, 1) == true then --if map tile is impassible
            printl("THE_GROUND_IS_NOT_FLAT_ENOUGH")
            play_md_sfx(5)
            return
         end
      end
   end
   
   printl("REST")
   
   if party_is_in_combat_mode() then
      print(" - ")
      printl("NOT_WHILE_IN_COMBAT_MODE")
      play_md_sfx(5)
      return
   end
   
   if foes_are_nearby() then
      printl("NOT_WHILE_FOES_ARE_NEAR")
      play_md_sfx(5)
      return
   end

   local npc = npcs_are_nearby()
   if npc ~= nil then
      printfl("IS_TOO_NEAR_TO_SETUP_CAMP", npc.name)
      play_md_sfx(5)
      return
   end   
   
   --poison check

   local actor
   local poisoned = false
   for actor in party_members() do
      if actor.poisoned then
         poisoned = true
         printfl("IS_POISONED", actor.name)
      end
   end
   
   if poisoned then
      printl("DO_YOU_REALLY_WANT_TO_SLEEP")
      local answer = input_select("yn", false)
      if answer == "N" or answer == "n" then
         return
      end
   end

   local party_is_using_berries = false
   for actor in party_members() do
      local actor_num = actor.actor_num
      local green = actor_is_affected_by_green_berries(actor_num)
      local brown = actor_is_affected_by_brown_berries(actor_num)
      
      if brown or green then
         party_is_using_berries = true
         if brown and green then
            printfl("COMPLAINS_OF_TOO_MUCH_LIGHT_AND_INANIMATE", actor.name)
         elseif brown then
            printfl("COMPLAINS_OF_TOO_MUCH_LIGHT", actor.name)
         else --green
            printfl("COMPLAINS_OF_INANIMATE_THINGS_TALKING", actor.name)            
         end  
      end
      
   end
   
   if party_is_using_berries then
      if party_get_size() == 1 then
         printl("YOU_CANT_SLEEP")
      else
         printl("NOBODY_CAN_SLEEP")
      end
      
      return
   end
   
   local player = Actor.get_player_actor()
   player.x = tent_loc.x
   player.y = tent_loc.y
   
   local tent = Obj.new(134, 3)
   Obj.moveToMap(tent, player.x, player.y-1, player.z)
   
   tent = Obj.new(134, 5)
   Obj.moveToMap(tent, player.x+1, player.y-1, player.z)

   tent = Obj.new(134, 6)
   Obj.moveToMap(tent, player.x-1, player.y, player.z)

   tent = Obj.new(134, 9)
   Obj.moveToMap(tent, player.x+1, player.y, player.z)

   tent = Obj.new(134, 8)
   Obj.moveToMap(tent, player.x, player.y, player.z)
            
   party_move(player.x, player.y, player.z)
   
   script_wait(500)
   
   party_hide_all()
   
   tent.frame_n = 7
   
   local hour = clock_get_hour()
   local time
   local hours_to_rest
   
   if hour < 7 or hour > 16 then
      time = i18n("SUNRISE")
      if hour < 7 then
         hours_to_rest = 7 - hour
      else
         hours_to_rest = 24 - hour + 7
      end
   elseif hour <= 16 then
      time = i18n("SUNSET")
      hours_to_rest = 18 - hour
   end
   
   printfl("REST_UNTIL", time)
   local answer = input_select("yn", false)
   
   if answer == "N" or answer == "n" then
      printl("HOW_MANY_HOURS")
      hours_to_rest = input_select_integer("0123456789", true)
   end
   
   g_party_is_warm = true
   
   if g_hours_till_next_healing == 0 and hours_to_rest > 4 then
      rest_heal_party(hours_to_rest)
      g_hours_till_next_healing = 6
   end
   
   local can_level_up = false
   if hours_to_rest * 3 > party_get_size() then
      can_level_up = true
   end
   
   local i
   for i=0,hours_to_rest*3-1 do
      advance_time(20)
      script_wait(100)
      if i < party_get_size() then
         local actor = party_get_member(i)
         rest_level_up_actor(actor)
      end
   end

   local actor
   local poisoned = false
   for actor in party_members() do
      if actor.poisoned then
         if math.random(1, 8) + math.random(1, 8) >= 15 then
            actor.poisoned = false
            printfl("FEELS_BETTER", actor.name)
         else
            if actor.hp < hours_to_rest * 2 + 5 then
               actor.hp = 5
            else
               actor.hp = actor.hp - (hours_to_rest * 2 + 5)
            end
         end
      end
   end


   tent.frame_n = 8 --Open the tent flap
   party_show_all()
         
   party_move(player.x, player.y + 1, player.z)

   script_wait(500)
   
   --remove tent from map
   local z = player.z
   for tent in find_obj(z, 134) do
      if tent ~= nil and 
      ((tent.x == tent_loc.x and tent.y == tent_loc.y-1) or
       (tent.x == wrap_coord(tent_loc.x+1,z) and tent.y == tent_loc.y-1) or
       (tent.x == wrap_coord(tent_loc.x-1,z) and tent.y == tent_loc.y) or
       (tent.x == wrap_coord(tent_loc.x+1,z) and tent.y == tent_loc.y) or
       (tent.x == tent_loc.x and tent.y == tent_loc.y))
      then
         Obj.removeFromEngine(tent)
      end
   end

   g_party_is_warm = false
end

function use_red_berry(obj, actor)
   if not party_is_in_combat_mode() then
      printl("THAT_WOULD_BE_A_WASTE_OUTSIDE_OF_COMBAT")
      return
   end
   
   if actor.frenzy == false then
      printfl("ENTERS_A_BATTLE_FRENZY", actor.name)
      play_md_sfx(0x32)
   end
   
   actor.frenzy = true
   
   local qty = obj.qty
   
   if qty > 1 then
      obj.qty = qty - 1
   else
      Obj.removeFromEngine(obj)
   end
end

function use_spittoon(obj, actor)
   if not actor.hypoxia then
      printl("YOU_SPIT_INTO_THE_SPITTOON")
   else
      printl("YOUR_MOUTH_IS_TOO_DRY")
   end
end

function use_gong(obj, target_obj, actor)
   printl("GONG")
   play_md_sfx(0xf) 
end

function use_musical_instrument(obj, actor)

   local obj_n = obj.obj_n
   
   if obj_n == 280 then --OBJ_CYMBALS
      printl("CHING")
      play_md_sfx(0x36)
   elseif obj_n == 281 then --OBJ_TAMBORINE
      printl("SHING")
      play_md_sfx(0x35)
   elseif obj_n == 282 then --OBJ_DRUM
      printl("THUMP_THUMP")
      play_md_sfx(0x34)
      script_wait(100)
      play_md_sfx(0x34)
   elseif obj_n == 283 then --OBJ_ACCORDION
      printl("WHEEEEZE")
      play_md_sfx(0x37)
   else
      printfl("YOU_PLAY_THE", obj.name)
   end
end

function use_wrench_on_switchbar(obj, target_obj, actor)
   if target_obj.quality == 1 then
      if target_obj.on_map then
         local turntable = map_get_obj(target_obj.x-1, target_obj.y-1, target_obj.z, 413)
         if turntable ~= nil then
            target_obj.frame_n = turntable.frame_n
            target_obj.quality = 0
            printl("THE_SWITCH_IS_FASTENED")
            play_md_sfx(0x1f)
            return
         end   
      end
      printl("THIS_SWITCH_CANNOT_BE_FIXED")
      play_md_sfx(0x5)
   else
      printl("THE_SWITCH_IS_LOOSE")
      play_md_sfx(0x1f)
      target_obj.quality = 1
   end
end

function use_wrench_on_drill(obj, target_obj, actor)
   local drill_cart
   if target_obj.on_map then
      drill_cart = map_get_obj(target_obj.x, target_obj.y, target_obj.z, 439)
   end
   
   if drill_cart ~= nil then
      local drill = Obj.new(441,1) --assembled drill
      Obj.moveToMap(drill, target_obj.x, target_obj.y, target_obj.z)
      Obj.removeFromEngine(target_obj)
      Obj.removeFromEngine(drill_cart)
      printl("THE_DRILLS_POWER_IS_CONNECTED")
      play_md_sfx(0x1f)
   else
      printl("THE_DRILL_MUST_BE_INSTALLED_ONTO_A_DRILL_CART")
   end
end

function use_wrench_on_panel(obj, target_obj, actor)
   if target_obj.on_map == false then
      printl("IT_HAS_NO_EFFECT")
      return
   end
   
   local quality = target_obj.quality
   local panel_qty = target_obj.qty
   if quality == 0 then
      target_obj.quality = 1
      printl("THE_PANEL_IS_LOOSE")
      play_md_sfx(0x1f)
   elseif bit32.btest(quality, 2) then
      printl("IT_MUST_BE_REPAIRED_FIRST")
      play_md_sfx(0x5)
   else
      local cabinet = map_get_obj(target_obj.x, target_obj.y, target_obj.z, 457)
      if cabinet == nil then
         printl("PANELS_ARE_ONLY_INSTALLED_ONTO_CABINETS")
         play_md_sfx(0x5)
      else
         quality = cabinet.quality
         if (quality == 0 and panel_qty ~= 0) or
            (quality ~= 0 and quality <= 3 and panel_qty == 0 and target_obj.frame_n ~= (quality - 1) ) or
            (quality ~= 0 and (quality > 3 or panel_qty ~= 0) and quality ~= panel_qty) then
            printl("THIS_CABINET_REQUIRES_A_DIFFERENT_TYPE_OF_PANEL")
            play_md_sfx(0x5)
         else
            target_obj.quality = target_obj.quality - 1
            printl("THE_PANEL_IS_FASTENED")
            play_md_sfx(0x1f)
            if target_obj.quality == 3 then
               Actor.set_talk_flag(0x74, 3)
               if Actor.get_talk_flag(0x74, 0) then
                  Actor.set_talk_flag(0x60, 2)
               end
            end
         end
      end
   end
   
end

function use_oxium_bin(obj, actor)
   if map_can_reach_point (actor.x, actor.y, obj.x, obj.y, obj.z) == false then
      printl("BLOCKED")
      return
   end
   
   local oxium = Obj.new(131) --OBJ_BLOB_OF_OXIUM
   oxium.qty = 20
   
   if Actor.can_carry_obj(actor, oxium) then
      Actor.inv_add_obj(actor, oxium, STACK_OBJECT_QTY)
      printl("YOU_GET_TWO_HANDFULS_OF_OXIUM_FROM_THE_BIN")
      Actor.set_talk_flag(0x12, 4)
   else
      printl("YOU_ARE_CARRYING_TOO_MUCH_ALREADY")
   end
end

function use_sledge_hammer_on_replacement_track_to_broken_track(obj, target_obj, to_obj, actor)
   play_md_sfx(0x20)
   printl("WHICH_SECTION_OF_RAIL_NEEDS_FIXING")
   Obj.removeFromEngine(target_obj)
   to_obj.obj_n = 414 --OBJ_TRACK
end

function use_pliers_on_spool_to_tower(obj, target_obj, to_obj, actor)

    if actor_find_max_wrapped_xy_distance(actor, to_obj.x, to_obj.y) > 1 then
      printl("THE_WORK_IS_TO_PRECISE_TO_PERFORM_TELEKINETICALLY")
      return
   end

   if Actor.get_talk_flag(0x73, 2) == false or Actor.get_talk_flag(0x71, 3) == true then
      printl("THE_CABLE_DOES_NOT_NEED_REPLACEMENT")
      return
   end
   
   if actor_is_holding_obj(actor, 38) == false then --OBJ_RUBBER_GLOVES
      Actor.hit(actor, math.random(0, math.floor(actor.max_hp/2)))
      local spector = Actor.get(2)
      if is_actor_able_to_talk_to_player(spector) then
         Actor.set_talk_flag(spector, 2)
         Actor.talk(spector)
      end
   else
      play_md_sfx(0x1f)
      Obj.removeFromEngine(target_obj)
      Actor.set_talk_flag(0x73, 4)
      play_midgame_sequence(3)
      for obj in find_obj_from_area(0x3d0, 0x1d0, 0, 32, 17) do
         if obj.obj_n == 215 then -- OBJ_POWER_CABLE1
            obj.obj_n = 214 -- OBJ_POWER_CABLE
         end
      end

      for obj in find_obj(0, 315) do --OBJ_CHAMBER1
         Obj.removeFromEngine(obj)
      end
   end
   
end

function use_gate(obj, actor)
   if bit32.btest(obj.quality, 128) == true then
      printl("IT_IS_APPARENTLY_LOCKED")
      return
   end

   local frame_n = obj.frame_n
   if frame_n == 0 or frame_n == 1 then
      obj.frame_n = 3
      obj.x = obj.x - 1
   elseif frame_n == 2 or frame_n == 3 then
      obj.frame_n = 1
      obj.x = obj.x + 1
   elseif frame_n == 4 or frame_n == 5 then
      obj.frame_n = 7
   elseif frame_n == 6 or frame_n == 7 then
      obj.frame_n = 5
   end
   
   
end

function use_switch_bar(obj, actor)
   if obj.frame_n == 0 then
      obj.frame_n = 1
   else
      obj.frame_n = 0
   end
   
   if obj.on_map == false or map_get_obj(obj.x-1,obj.y-1,obj.z, 413) == nil then
      printl("IT_HAS_NO_EFFECT")
      return
   end
   
   if obj.quality == 1 then
      printl("IT_TURNS_LOOSELY")
      return
   end
   
   local turntable = map_get_obj(obj.x-1,obj.y-1,obj.z, 413)
   turntable.frame_n = obj.frame_n
   
   local railcar = map_get_obj(obj.x-1,obj.y-1,obj.z, 410)
   if railcar ~= nil then
      railcar.frame_n = railcar.frame_n - (railcar.frame_n % 2)
      railcar.frame_n = railcar.frame_n + turntable.frame_n
   end
end

function use_reading_material(obj, actor)
   if obj.quality == 0 then
      local signatures = ""
      if Actor.get_talk_flag(5, 3) == true then
         signatures = signatures .. " Richard Sherman"
      end
      signatures = signatures .. "\n"

      if Actor.get_talk_flag(0x19, 3) == true then
         signatures = signatures .. "  Capt. Gregory Duprey"
      end
      signatures = signatures .. "\n"

      if Actor.get_talk_flag(0x1a, 3) == true then
         signatures = signatures .. "  Doctor David Yellin"
      end
      signatures = signatures .. "\n"

      display_text_in_scroll_gump(i18nf("AFFIDAVIT", player_get_name(), signatures))
   else
      local text = load_text_from_lzc("mdtext.lzc", obj.quality - 1)
      if text ~= nil then
         display_text_in_scroll_gump(text)
      else
         printl("YOU_CANT_READ_IT")
      end
   end

end

function use_pool_table(obj, actor)
   if Actor.inv_get_readied_obj_n(actor, ARM) ~= 401 then --OBJ_POOL_CUE
      printl("YOU_NEED_A_POOL_CUE")
      return
   end
   local pool_table1 = map_get_obj(wrap_coord(obj.x - 1, obj.z), obj.y, obj.z, 400) -- OBJ_UNK_400
   local rand = math.random
   if pool_table1 == nil then
      pool_table1 = Obj.new(400, rand(0, 6))
      Obj.moveToMap(pool_table1, wrap_coord(obj.x - 1, obj.z), obj.y, obj.z)
   end

   local pool_table2 = map_get_obj(obj.x, obj.y, obj.z, 400) -- OBJ_UNK_400
   if pool_table2 == nil then
      pool_table2 = Obj.new(400, rand(0, 6))
      Obj.moveToMap(pool_table2, wrap_coord(obj.x - 1, obj.z), obj.y, obj.z)
   end

   for i=1,10 do
      if i~= 1 then
         script_wait(rand(200,500))
      end
      play_md_sfx(0x1c + rand(0, 2))
      script_wait(rand(10,200))
      play_md_sfx(0x1c + rand(0, 2))

      pool_table1.frame_n = rand(0, 6)
      pool_table2.frame_n = rand(0, 6)

      if rand(0, 40) >= actor_dex_adj(actor) then
         break
      end
   end

   if rand(0, 40) < actor_dex_adj(actor) then
      printl("GOOD_SHOT_OLD_BEAN")
   end
end

function use_ready_obj(obj, actor)
   if not Actor.can_carry_obj(actor, obj) then
      printl("YOU_ARE_CARRYING_TOO_MUCH_ALREADY")
      return
   end
   if obj.readied == true then
      return
   end

   Obj.removeFromEngine(obj)
   Obj.moveToInv(obj, actor.actor_num)

   Actor.inv_ready_obj(actor, obj)
end

function use_heartstone_on_metal_woman(obj, target_obj, actor)
   if target_obj.quality == 1 then
      printl("THE_ROBOT_ALREADY_HAS_A_HEARTSTONE")
   else
      target_obj.quality = 1
      Obj.removeFromEngine(obj)
      play_md_sfx(0x1f)
      printl("THE_HEARTSTONE_IS_INSTALLED")
   end

end

function use_manuscript_on_mailbox(obj, target_obj, actor)
   local twain = Actor.get(0x57)

   finish_dream_quest(twain)
   Actor.set_talk_flag(twain, 6)
   Actor.talk(twain)
   wake_from_dream()
end

function use_assembled_drill(obj, actor)

   play_md_sfx(0x10)
   
   local x = obj.x
   local y = obj.y
   local z = obj.z
   
   if obj.frame_n == 1 then
      x = x - 1
   elseif obj.frame_n == 3 then
      y = y - 1
   elseif obj.frame_n == 4 then
      y = y + 1
   else
      x = x + 1
   end
   
   local target_obj
   for obj in objs_at_loc(x, y, z) do
      if obj.obj_n == 445 --OBJ_IRON_ORE
         or obj.obj_n == 446 -- OBJ_VEIN_OF_COAL
         or obj.obj_n == 213 then --OBJ_CAVE_IN
         target_obj = obj
         break
      end
   end
   
   if target_obj == nil then
      target_obj = map_get_obj(x, y, z, 213, true)
   end
   
   local drilled_matterial
   
   if target_obj == nil then
      if can_drill_at_loc(x, y, z) == true then
         drilled_matterial = 442 --OBJ_PILE_OF_ROCKS
      else
         local target_actor = map_get_actor(x, y, z)
         if target_actor ~= nil then
            --FIXME attack actor here. 40 points of damage
         else
            printl("THERE_IS_NOTHING_TO_DRILL_INTO")
         end
         return
      end
   elseif target_obj.obj_n == 445 then --OBJ_IRON_ORE
      drilled_matterial = 443 --OBJ_PILE_OF_IRON_ORE
   elseif target_obj.obj_n == 446 then --OBJ_VEIN_OF_COAL
      drilled_matterial = 444 --OBJ_PILE_OF_COAL
   end
   
   if drilled_matterial ~= nil then
      local spoil_location = get_free_location_around_drill(obj)
      if spoil_location ~= nil then
         local spoil_obj = Obj.new(drilled_matterial)
         Obj.moveToMap(spoil_obj, spoil_location)
      else
         printl("THERE_IS_NO_ROOM_LEFT_FOR_THE_ORE")
      end
   end
   
   if target_obj then
      if target_obj.quality > 1 then
         target_obj.quality = target_obj.quality - 1
      else
         Obj.removeFromEngine(target_obj)
      end  
   end
   
   if drilled_matterial == nil then
      Obj.removeFromEngine(target_obj)
   end
   
end

function get_free_location_around_drill(drill)
   local x_tbl = {-1,0,1,1,1,0,-1,-1}
   local y_tbl = {-1,-1,-1,0,1,1,1,0}
   local pos = {}
   local i

   pos.z = drill.z
   
   for i=1,8 do
      pos.x = drill.x + x_tbl[i]
      pos.y = drill.y + y_tbl[i]
      local obj = map_get_obj(pos.x, pos.y, pos.z)
      if obj == nil or is_blood(obj.obj_num) then
         if tile_get_flag(map_get_tile_num(pos), 1, 1) == false then --not blocked.
            return pos
         end
      end
   end
   
   return nil
end

function get_ore_container_quality(ore_obj_num)
   local tbl = {[258]=1,[442]=2,[443]=3,[444]=4} --OBJ_DIRT_PILE, OBJ_PILE_OF_ROCKS, OBJ_PILE_OF_IRON_ORE, OBJ_PILE_OF_COAL
   local quality = tbl[ore_obj_num]
   if quality == nil then
      quality = 1
   end
   
   return quality   
end

function get_obj_num_from_ore_quality(ore_quality)
   if ore_quality == 2 then
      return 442 --OBJ_PILE_OF_ROCKS
   elseif ore_quality == 3 then
      return 443 --OBJ_PILE_OF_IRON_ORE
   elseif ore_quality == 4 then
      return 444 --OBJ_PILE_OF_COAL
   end

   return 258 --OBJ_DIRT_PILE
end

function can_drill_at_loc(x,y,z)
   local tile_num = map_get_tile_num(x, y, z)
   
   if tile_num >= 0xf0 and tile_num <= 0xfb then
      return true
   end
   
   return false
end

local use_shovel_on_tbl = {
--on
   [255]=use_misc_text,
   [257]=use_misc_text,
   [258]={[257]=use_shovel_on_pile_to_hole},
   [268]={
      [233]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_MARTIAN_WHEEL_BARROW to OBJ_FURNACE
      [257]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_MARTIAN_WHEEL_BARROW to OBJ_HOLE
      [268]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_MARTIAN_WHEEL_BARROW to OBJ_MARTIAN_WHEEL_BARROW
      [410]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_MARTIAN_WHEEL_BARROW to OBJ_RAIL_CAR
   },
   [410]={
      [233]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_RAIL_CAR to OBJ_FURNACE
      [257]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_RAIL_CAR to OBJ_HOLE
      [268]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_RAIL_CAR to OBJ_MARTIAN_WHEEL_BARROW
      [410]=use_shovel_to_unload_container, --use OBJ_SHOVEL on OBJ_RAIL_CAR to OBJ_RAIL_CAR
   },
   [442]={
      [268]=use_shovel_on_ore_to_container, --use OBJ_SHOVEL on OBJ_PILE_OF_ROCKS to OBJ_MARTIAN_WHEEL_BARROW
      [410]=use_shovel_on_ore_to_container}, --use OBJ_SHOVEL on OBJ_PILE_OF_ROCKS to OBJ_RAIL_CAR
   [443]={
      [268]=use_shovel_on_ore_to_container, --use OBJ_SHOVEL on OBJ_PILE_OF_IRON_ORE to OBJ_MARTIAN_WHEEL_BARROW
      [410]=use_shovel_on_ore_to_container}, --use OBJ_SHOVEL on OBJ_PILE_OF_IRON_ORE to OBJ_RAIL_CAR
   [444]={
      [268]=use_shovel_on_ore_to_container, --use OBJ_SHOVEL on OBJ_PILE_OF_COAL to OBJ_MARTIAN_WHEEL_BARROW
      [410]=use_shovel_on_ore_to_container}, --use OBJ_SHOVEL on OBJ_PILE_OF_COAL to OBJ_RAIL_CAR
   [0]=use_tool_on_ground, --hole in ice, hole
}

function use_oil_on_door(obj, target_obj, actor)
   if obj.obj_n == 235 and obj.frame_n ~= 4 then
      printl("IT_HAS_NO_EFFECT")
      return
   end

   if target_obj ~= nil then
      if bit32.band(target_obj.quality, 0x80) == 0 then
         printl("THIS_DOOR_IS_NOT_RUSTED")
      else
         target_obj.quality = bit32.band(target_obj.quality, 0x7f) --unset bit 7
         printl("THE_DOOR_IS_UNSTUCK")
         play_md_sfx(4)
         if obj.stackable then
            if obj.qty == 1 then
               Obj.removeFromEngine(obj)
            else
               obj.qty = obj.qty - 1
            end
         else
            Obj.removeFromEngine(obj)
         end
      end
   end
end

function use_oil_on_dream_door(obj, target_obj, actor, target_x, target_y, target_z)
   if map_get_tile_num(target_x, target_y, target_z) == 8 then
      local melies = Actor.get(0x51)
      Actor.set_talk_flag(melies, 6)
      Actor.talk(melies)
      finish_dream_quest(melies)
      wake_from_dream()
   else
      printl("IT_HAS_NO_EFFECT")
   end
end

function use_hand_mirror(obj, actor)
   if actor.x >= 0x76 and actor.x <= 0x78 and actor.y >= 0xca and actor.y <= 0xcc and actor.z == 2 then
      local lowell = Actor.get(0x50)

      Actor.set_talk_flag(lowell, 6)
      Actor.talk(lowell)
      finish_dream_quest(lowell)
      wake_from_dream()
   else
      printl("YOU_SEE_YOURSELF")
   end

end

function use_radium(obj, target_obj, actor)
   actor_radiation_check(actor, obj)
   if obj.obj_n == 448 then --OBJ_BLOCK_OF_RADIUM
      if target_obj.obj_n == 448 and target_obj.on_map then
         local power_unit = map_get_obj(obj.x, obj.y, obj.z, 290) --OBJ_POWER_UNIT
         if power_unit ~= nil then
            target_obj = power_unit
         end
      end

      if target_obj.obj_n == 290 then --OBJ_POWER_UNIT
         if target_obj.frame_n == 1 then
            printl("RADIUM_HAS_ALREADY_BEEN_INSTALLED")
         else
            Obj.removeFromEngine(obj)
            play_md_sfx(4)
            printl("THE_RADIUM_HAS_BEEN_INSTALLED")
            target_obj.frame_n = 1
            Actor.set_talk_flag(0x74, 2)
            if Actor.get_talk_flag(0x74, 0) then
               Actor.set_talk_flag(0x60, 2)
            end
         end

      else
         printl("IT_HAS_NO_EFFECT")
      end

   elseif obj.obj_n == 449 then --OBJ_CHIP_OF_RADIUM
      if target_obj.qty < 0xf0 then
         local qty = input_select_obj_qty(obj)
         if qty == 0 then
            return
         end

         if target_obj.qty + qty * 30 > 0xf0 then
            qty = math.ceil((0xf0 - target_obj.qty) / 30)
            printfl("THE_OBJ_ONLY_NEEDED_N_RADIUM_BLOCKS", target_obj.name, qty)
            printfl("THE_OBJ_IS_FULLY_CHARGED", target_obj.name)
            target_obj.qty = 0xf0
         else
            target_obj.qty = target_obj.qty + qty * 30
         end


         if obj.qty == qty then
            Obj.removeFromEngine(obj)
         else
            obj.qty = obj.qty - qty
         end
      else
         printfl("THE_OBJ_IS_FULLY_CHARGED", target_obj.name)
      end

   end

end

function use_head_gear(obj, target_obj, actor)
   local machine
   if target_obj.obj_n == 289 then --OBJ_DREAM_MACHINE2
      local machine = map_get_obj(target_obj.x, target_obj.y, target_obj.z, 288)
      if machine == nil then
         printl("IT_HAS_NO_EFFECT")
         return
      end
   else
      machine = target_obj
   end

   local loc = machine.xyz

   machine = Obj.new(100)
   machine.frame_n = 1
   Obj.moveToMap(machine, loc)

   printl("THE_HEADGEAR_IS_INSTALLED")

   Actor.set_talk_flag(0x74, 4)
   if Actor.get_talk_flag(0x74, 0) then
      Actor.set_talk_flag(0x60, 2)
   end

end

function use_dream_machine(panel_obj)
   if Actor.get_talk_flag(0x46, 3) then
      printl("THE_DREAM_MACHINES_SEEM_TO_HAVE_CEASED_FUNCTIONING")
      return
   end

   local z = panel_obj.z
   local power_unit
   local dream_quality
   local headgear_installed = false
   local seat_x, seat_y
   for obj in find_obj_from_area(panel_obj.x - 5, panel_obj.y - 5, z, 11, 11) do
      local obj_n = obj.obj_n
      if obj_n == 290 and obj.frame_n == 1 then --OBJ_POWER_UNIT
         power_unit = obj
      elseif obj_n == 100 then --OBJ_DREAM_MACHINE
         headgear_installed = true
      elseif obj_n == 289 then --OBJ_DREAM_MACHINE2
         seat_x = obj.x
         seat_y = obj.y
      elseif obj_n == 288 then --OBJ_DREAM_MACHINE2
         dream_quality = obj.quality
      end
   end

   local actor_in_seat = map_get_actor(seat_x, seat_y, z)

   if power_unit == nil or dream_quality == nil or not headgear_installed then
      printl("THE_MACHINE_DOES_NOT_WORK")
      return
   end

   local martian_obj = map_get_obj(seat_x, seat_y, z, 291) --OBJ_UNCONSCIOUS_MARTIAN
   if martian_obj ~= nil then
      if Actor.get_talk_flag(0x21, 2) or not Actor.get_talk_flag(0x61, 4) then
         printl("IT_HAS_NO_EFFECT")
      else
         play_midgame_sequence(5)
         local martian = Actor.get(0x21)
         Actor.talk(martian)
         Actor.set_talk_flag(martian, 2)
         martian.x = seat_x
         martian.y = seat_y + 1
         martian.z = panel_obj.z
         kill_actor(martian)
         Obj.removeFromEngine(martian_obj)
      end

      return
   end

   local metal_woman_obj = map_get_obj(seat_x, seat_y, z, 287) --OBJ_METAL_WOMAN
   if metal_woman_obj ~= nil then
      if metal_woman_obj.quality == 0 then
         printl("IT_HAS_NO_EFFECT")
      else
         --FIXME implement metal woman activation logic
      end
   elseif actor_in_seat ~= nil then
      if z ~= 0 then
         wake_from_dream()
      else
         if dream_quality < 2 then
            Actor.set_talk_flag(0x60, 3)
         end
         if dream_quality == 3 then
            if Actor.get_talk_flag(0x46, 0) then
               --FIXME call sub_3F624
            else
               printl("THE_MACHINE_DOES_NOT_WORK")
            end
         else
            actor_use_dream_machine(actor_in_seat, dream_quality)
         end

      end
   else
      printl("THERE_IS_NOBODY_SITTING_IN_THE_MACHINE")
   end

end

function use_sprayer_system(panel_obj)
end

function use_lens_controls(panel_obj)
end

function use_pump_controls(panel_obj)
end

local use_panel_tbl = {
   [0]=function() printl("YOU_ARE_COMPLETELY_UNSURE_WHAT_YOU_JUST_DID") end,
   [1]=function() printl("YOU_ACTUATE_THE_MECHANISM_TO_NO_APPARENT_EFFECT") end,
   [2]=function() printl("STATUS_LIGHTS_CHANGE_BUT_YOU_SEE_NO_OTHER_EFFECT") end,
   [3]=function() printl("LIGHTS_FLASH_AND_CHANGE_COLOR_BUT_NOTHING_ELSE_HAPPENS") end,
   [4]=use_dream_machine,
   [5]=use_sprayer_system,
   [6]=use_lens_controls,
   [7]=use_pump_controls,
}

function use_panel(obj, actor)
   if bit32.band(obj.quality, 2) ~= 0 then
      printl("THE_PANEL_IS_BROKEN")
      return
   end
   if bit32.band(obj.quality, 1) ~= 0 then
      printl("THE_PANEL_IS_NOT_INSTALLED")
      return
   end

   local cabinet = map_get_obj(obj.x, obj.y, obj.z, 457) --OBJ_CABINET
   if cabinet == nil then
      printl("PANELS_ARE_ONLY_INSTALLED_ONTO_CABINETS")
      return
   end

   local quality = cabinet.quality
   if use_panel_tbl[quality] ~= nil then
      use_panel_tbl[quality](obj)
   else
      printl("IT_HAS_NO_EFFECT")
   end
end

function has_minotaur_left_the_shop(cur_z)
   for i=1,0xff do
      local actor = Actor.get(i)
      if actor.obj_n == 398 then --OBJ_MINOTAUR
         if actor.z == cur_z and not Actor.get_talk_flag(0x54, 6)
                 and actor.x >= 0x37 and actor.y <= 0x37 then
            return true
         end
      end
   end
   return false
end

function use_switch(obj, actor)
   local switch_qty = obj.qty
   local switch_quality = obj.quality
   local target_obj

   local num_switches = 0
   local switches = {}

   for obj in find_obj_from_area(obj.x - 32, obj.y - 32, obj.z, 64, 64) do
      if obj.obj_n == 179 or obj.obj_n == 227 then --OBJ_CLOSED_DOOR, OBJ_DOOR3
         if bit32.band(obj.quality, 0x7f) == switch_qty then
            target_obj = obj
         end
      elseif (obj.obj_n == 311 or obj.obj_n == 312 or obj.obj_n == 196) then --and obj.quality < 2 then
         if num_switches < 4 then
            num_switches = num_switches + 1
            switches[num_switches] = obj
         end
      end
   end

   if target_obj == nil then
      printl("STRANGELY_NOTHING_HAPPENS")
      return
   end

   if num_switches == 0 then
      printl("STRANGELY_IT_DOESNT_MOVE")
      return
   end

   local frame_n = 0
   if switch_quality == 1 then
      if bit32.band(target_obj.quality, 0x80) == 0 then
         target_obj.quality = bit32.bor(target_obj.quality, 0x80)
      else
         target_obj.quality = bit32.band(target_obj.quality, 0x7f)
         frame_n = 1
      end
   else
      local old_frame_n = target_obj.frame_n
      target_obj.frame_n = bit32.band(old_frame_n, 2) + 5
      play_door_sfx()
      if old_frame_n < 4 then
         target_obj.frame_n = bit32.band(old_frame_n, 2) + 9
         frame_n = 1
      else
         target_obj.frame_n = bit32.band(old_frame_n, 2) + 1

         if g_in_dream_mode and g_current_dream_stage == 0x44 and has_minotaur_left_the_shop(target_obj.z) then
            complete_tiffany_stage()
            return
         end
      end
      play_door_sfx()
   end

   for i=1,num_switches do
      switches[i].frame_n = frame_n
   end
   play_md_sfx(0x11)
end

function use_drawbridge_lever(obj, actor)
   --FIXME
end

function use_cheat_lever(obj, actor)
   --FIXME
end

function use_cheat_lever2(obj, actor)
   --FIXME
end

local switch_qual_tbl = {
   [1] = use_switch,
   [2] = use_switch,
   [10] = use_drawbridge_lever,
   [20] = use_cheat_lever,
   [21] = use_cheat_lever2,
}

function use_switch_device(obj, actor)
   if switch_qual_tbl[obj.quality] ~= nil then
      switch_qual_tbl[obj.quality](obj, actor)
   else
      printl("WHAT_AN_ODD_LEVER")
   end
end

function use_dreamstuff(obj, actor)
   printl("YOU_IMAGINE")

   for item in container_objs(obj) do
      if item.obj_n < 342 then -- OBJ_POOR_MONK
         --object
         print(item.look_string.."\n")
         if obj.on_map then
            Obj.moveToMap(item, obj.xyz)
         else
            local parent = obj.parent
            if parent.luatype == "actor" then
               Obj.moveToInv(item, parent.actor_num)
            else
               Obj.moveToCont(item, parent)
            end
         end
      else
         --actor
         print(item.name.."\n")
         local spawned_actor = Actor.new(item.obj_n, actor.x, actor.y, actor.z)

         actor_init(spawned_actor, obj.quality + 1) -- alignment
         toss_actor(spawned_actor, actor.x, actor.y, actor.z)
         spawned_actor.wt = item.quality
      end
      Obj.removeFromEngine(obj)
      return
   end

   printl("NOTHING")
end

function use_obj_on_spray_gun(obj, target_obj, actor)
   if obj.obj_n == 119 and target_obj.quality == 0 then --OBJ_BOTTLE_OF_GREEN_PAINT
      target_obj.quality = 1
      target_obj.qty = 20
      printl("SPRAY_GUN_GREEN_PAINT")
   elseif obj.obj_n == 128 and target_obj.quality ~= 0 then --OBJ_WEED_KILLER
      target_obj.quality = 0
      target_obj.qty = 10
      printl("SPRAY_GUN_WEED_KILLER")
   else
      local spray_gun_qty = target_obj.qty + 10
      if spray_gun_qty > 0x1e then
         spray_gun_qty = 0x1e
      end
      target_obj.qty = spray_gun_qty
      printl("SPRAY_GUN_10_MORE_CHARGES")
   end

   Obj.removeFromEngine(obj)
end

function use_spray_gun(obj, target_obj, actor)
   if obj.qty == 0 then
      printl("THERE_IS_NOTHING_IN_THE_GUN")
      return
   end

   if obj.quality ~= 0 then
      obj.qty = obj.qty - 1
      if target_obj.luatype == "actor" and target_obj.obj_n == 145 then --OBJ_MONSTER_FOOTPRINTS
         target_obj.obj_n = 364 --OBJ_PROTO_MARTIAN
         printfl("BECOMES_VISIBLE", target_obj.name)
      else
         if target_obj.luatype == "actor" and not target_obj.visible then
            target_obj.visible = true
            printfl("BECOMES_VISIBLE", target_obj.name)
         elseif target_obj.luatype == "obj" and target_obj.invisible then
            target_obj.invisible = false
            printfl("BECOMES_VISIBLE", target_obj.name)
         else
            printl("IT_HAS_NO_EFFECT")
         end
      end
   elseif target_obj.luatype == "actor" then
      attack_target_with_weapon(actor, target_obj.x, target_obj.y, obj)
   elseif is_plant_obj(target_obj) then
      hit_target(target_obj, RED_HIT_TILE)
      printl("YOU_KILLED_THE_PLANT")
      if target_obj.obj_n == 205 then --OBJ_VINE
         Actor.set_talk_flag(0x30, 7)
         Obj.removeFromEngine(target_obj)
      elseif target_obj.obj_n == 408 then --OBJ_TREE
         target_obj.obj_n = 166 --OBJ_DEAD_TREE
      else
         Obj.removeFromEngine(target_obj)
      end
   else
      obj.qty = obj.qty - 1
      printl("IT_HAS_NO_EFFECT")
   end
end

local usecode_table = {
--OBJ_RUBY_SLIPPERS
[12]=use_ruby_slippers,
--OBJ_RUBBER_GLOVES
[38]=use_ready_obj,
--OBJ_SLEDGE_HAMMER
[52]={
--on
   [404]={ --OBJ_REPLACEMENT_TRACK
   --to
      [405]=use_sledge_hammer_on_replacement_track_to_broken_track
   }
},
--OBJ_PLIERS
[53]={
--on
   [199]={--OBJ_CABLE_SPOOL
   --to
      [201]=use_pliers_on_spool_to_tower, --OBJ_TOWER_TOP
      [215]=use_pliers_on_spool_to_tower, --OBJ_POWER_CABLE1
   }
},
--OBJ_PICK
[65]={[255]=use_misc_text,[257]=use_misc_text}, --hole in ice, hole
--OBJ_SHOVEL
[66]=use_shovel_on_tbl, 
--OBJ_HOE 
[67]={[255]=use_misc_text,[257]=use_misc_text}, --hole in ice, hole
--OBJ_BERRY
[73]=use_berry,
--OBJ_BERRY1
[74]=use_berry,
--OBJ_BERRY2
[75]=use_berry,
--OBJ_BERRY3
[76]=use_berry,
--OBJ_BERRY4
[77]=use_red_berry,
--OBJ_CLUMP_OF_ROUGE_BERRIES
[78]=use_misc_text, 
[86]=use_container,
[87]=use_container,
--OBJ_MANUSCRIPT
[88]={
   --on
   [84]=use_manuscript_on_mailbox, --OBJ_MAILBOX
},
[96]=use_sextant,
[102]={[86]=use_crate,[427]=use_prybar_on_hatch},
[104]=use_container,
--OBJ_FOLDED_TENT
[106]=use_tent,
--OBJ_BOTTLE_OF_GREEN_PAINT
[119]={
   --on
   --OBJ_WEED_SPRAYER
   [129]=use_obj_on_spray_gun,
   --OBJ_SPRAY_GUN
   [261]=use_obj_on_spray_gun,
},
--OBJ_CAN_OF_LAMP_OIL
[124]={
   --on
   --OBJ_CLOSED_DOOR
   [179]=use_oil_on_door,
   --OBJ_DOOR3
   [227]=use_oil_on_door,
   --OBJ_CLOSED_HATCH
   [421]=use_oil_on_door,
   --OBJ_DOOR
   [152]=use_oil_on_door,
   --OBJ_DOOR1
   [219]=use_oil_on_door,
   --OBJ_DOOR2
   [222]=use_oil_on_door,
   [0]=use_oil_on_dream_door,
},
--OBJ_WEED_KILLER
[128]={
   --on
   --OBJ_WEED_SPRAYER
   [129]=use_obj_on_spray_gun,
   --OBJ_SPRAY_GUN
   [261]=use_obj_on_spray_gun,
},
--OBJ_WEED_SPRAYER
[129]={
   --on
   [0]=use_spray_gun
},
--OBJ_BLOB_OF_OXIUM
[131]=use_misc_text,
--OBJ_WRENCH
[135]={
--on
   [411]=use_wrench_on_switchbar,
   [440]=use_wrench_on_drill,
   [458]=use_wrench_on_panel
},
--OBJ_TONGS
[136]=use_ready_obj,
--OBJ_REPAIRED_BELT
[144]={
--on
   [192]=use_fixed_belt_on_bare_rollers,
},
--OBJ_HAND_MIRROR
[147]=use_hand_mirror,
--OBJ_BOOK
[148]=use_reading_material,
--OBJ_NOTE
[151]=use_reading_material,
--OBJ_DOOR 
[152]=use_door,
[181]=use_gate,
--OBJ_CAMERA
[184]=use_misc_text,
--OBJ_LEVER
[196]=use_switch_device,
--OBJ_CABLE_SPOOL
[199]=use_misc_text,
[212]=use_oxium_bin,
[222]=use_door,
--OBJ_GONG_HAMMER
[231]={
--on
   [130]=use_hammer_on_oxium_geode, --OBJ_OXIUM_GEODE
   [298]=use_gong, --OBJ_GONG
   --FIXME OBJ_BRASS_CHEST, OBJ_OBSIDIAN_BOX, OBJ_STEAMER_TRUNK, OBJ_OPEN_BRASS_TRUNK use_crate
   },
--OBJ_POTASH (this is actually oil when frame_n == 4)
[235]={
--on
   --OBJ_CLOSED_DOOR
   [179]=use_oil_on_door,
   --OBJ_DOOR3
   [227]=use_oil_on_door,
   --OBJ_CLOSED_HATCH
   [421]=use_oil_on_door,
   --OBJ_DOOR
   [152]=use_oil_on_door,
   --OBJ_DOOR1
   [219]=use_oil_on_door,
   --OBJ_DOOR2
   [222]=use_oil_on_door,
   [0]=use_oil_on_dream_door,
},
--OBJ_SCROLL
[243]=use_reading_material,
--OBJ_SPRAY_GUN
[261]={
   --on
   [0]=use_spray_gun
},
--OBJ_MARTIAN_HOE
[263]={[255]=use_misc_text,[257]=use_misc_text}, --hole in ice, hole
--OBJ_MARTIAN_SHOVEL
[267]=use_shovel_on_tbl,
[273]={[86]=use_crate}, --Hammer needs more codes
--OBJ_CYMBALS
[280]=use_musical_instrument,
--OBJ_TAMBORINE
[281]=use_musical_instrument,
--OBJ_DRUM
[282]=use_musical_instrument,
--OBJ_ACCORDION
[283]=use_musical_instrument,
[284]=use_container,
--OBJ_SPITTOON
[286]=use_spittoon,
--OBJ_DREAM_MACHINE1
[288]=use_misc_text,
--OBJ_MARTIAN_CLOCK
[293]=use_misc_text,
--OBJ_HEADGEAR
[296]={
   --on
   [288]=use_head_gear, --OBJ_DREAM_MACHINE1
   [289]=use_head_gear, --OBJ_DREAM_MACHINE2
},
--OBJ_SWITCH
[311]=use_switch_device,
--OBJ_SWITCH1
[312]=use_switch_device,
--OBJ_OXYGENATED_AIR_MACHINE
[323]=use_misc_text,
--OBJ_MARTIAN_PICK
[327]={[255]=use_misc_text,[257]=use_misc_text}, --hole in ice, hole
[331]=use_dreamstuff,
[399]=use_pool_table,
--OBJ_POOL_QUE
[401]=use_ready_obj,
[411]=use_switch_bar,
--OBJ_CLOSED_HATCH
[421]=use_door,
--OBJ_HEART_STONE
[426]={
   --on
   [287]= use_heartstone_on_metal_woman, --OBJ_METAL_WOMAN
},
[427]=use_misc_text,
--OBJ_ASSEMBLED_DRILL
[441]=use_assembled_drill,
--OBJ_PILE_OF_ROCKS
[442]=use_misc_text,
--OBJ_PILE_OF_IRON_ORE
[443]=use_misc_text,
--OBJ_PILE_OF_COAL
[444]=use_misc_text,
--OBJ_BLOCK_OF_RADIUM
[448]={
   --on
   [290]=use_radium, --OBJ_POWER_UNIT
   [448]=use_radium, --OBJ_BLOCK_OF_RADIUM
},
--OBJ_CHIP_OF_RADIUM
[449]={
   --on
   [240]=use_radium, --OBJ_HEAT_RAY_GUN
   [241]=use_radium, --OBJ_FREEZE_RAY_GUN
},
--OBJ_PANEL
[458]=use_panel,
}

function ready_winged_shoes(obj, actor)
   local player_loc = player_get_location()
   if player_loc.z == 2 then
      local bridge = Obj.new(146)
      bridge.temporary = false
      Obj.moveToMap(bridge, 0xc9, 0x9b, 2)
   end
   
   return true
end

function ready_tongs(obj, actor)
   printl("THE_TONGS_WILL_NOW_PROTECT_YOUR_HANDS")
   return true
end

function ready_throw_rug(obj, actor)
   obj.obj_n = 162 --Change to red cape
   return true
end

local usecode_ready_obj_table = {
[15]=ready_winged_shoes,
[136]=ready_tongs,
[161]=ready_throw_rug,
}

function unready_winged_shoes(obj, actor)
   printl("THEY_WONT_COME_OFF")
   return false
end

local usecode_unready_obj_table = {
[15]=unready_winged_shoes,
}

function move_drill(obj, rel_x, rel_y)
  if rel_x ~= 0 and rel_y ~= 0 then
    return false
  end

  if rel_x < 0 then
    obj.frame_n = 1
  elseif rel_x > 0 then
      obj.frame_n = 4
  elseif rel_y < 0 then
      obj.frame_n = 3
  elseif rel_y > 0 then
      obj.frame_n = 5
  end
    
  return true
end

function move_wheelbarrow(obj, rel_x, rel_y)
  if rel_x ~= 0 and rel_y ~= 0 then
    return false
  end
  
  if rel_x < 0 then
    obj.frame_n = 3
  elseif rel_x > 0 then
      obj.frame_n = 1
  elseif rel_y < 0 then
      obj.frame_n = 0
  elseif rel_y > 0 then
      obj.frame_n = 2
  end
  
  return true
end

function move_rail_cart(obj, rel_x, rel_y)
   local frame_n = obj.frame_n
   if rel_x ~= 0 and rel_y ~= 0 then
      printl("IT_WONT_GO_IN_THAT_DIRECTION")
      return false
   end
   if (rel_x < 0 or rel_x > 0) and (frame_n == 1 or frame_n == 3 or frame_n == 5) then
      printl("IT_WONT_GO_IN_THAT_DIRECTION")
      return false
   end
   if (rel_y < 0 or rel_y > 0) and (frame_n == 0 or frame_n == 2 or frame_n == 4) then
      printl("IT_WONT_GO_IN_THAT_DIRECTION")
      return false
   end
   if check_for_track(obj, rel_x, rel_y) == false then
      printl("IT_WONT_GO_IN_THAT_DIRECTION")
      return false
   end

   move_car_obj(obj, rel_x, rel_y)
   return false     
end

function check_for_track(car, rel_x, rel_y)
   local x = car.x + rel_x
   local y = car.y + rel_y
   
   for obj in objs_at_loc(x, y, car.z) do
   	if (obj.obj_n >= 412 and obj.obj_n <= 414) or obj.obj_n == 419 or obj.obj_n == 175 or obj.obj_n == 163 then --track object

   	  local track_frame_n = obj.frame_n
   	  if (car.frame_n % 2) == track_frame_n or track_frame_n == 2 or obj.obj_n == 412 or obj.obj_n == 175 or obj.obj_n == 163 then
   	     return true
   	  else
   	     return false
   	  end
   	elseif is_blood(obj.obj_n) == false and obj.obj_n ~= 417 then --MARTIAN_BARGE
   	  return false
   	end
   end

   if map_get_obj(x, y, car.z, 175, true) then
      return true
   end

   if map_get_obj(x, y, car.z, 412, true) then
      return true
   end
      
   local tile_num = map_get_tile_num(x,y, car.z)
   if is_track_tile(tile_num) then
      return true
   end
   
   return false
end

function move_car_obj(obj, rel_x, rel_y)
   obj.x = obj.x + rel_x
   obj.y = obj.y + rel_y
   return true
end

function move_plank(obj, rel_x, rel_y)
   obj.x = obj.x + rel_x
   obj.y = obj.y + rel_y
   return true
end

function is_track_tile(tile_num)
   if tile_num == 108 or tile_num == 109 or tile_num == 110 or tile_num == 77 or tile_num == 79 then
      return true
   end
   return false
end

local usecode_move_obj_table = {
[268]=move_wheelbarrow,
[395]=move_plank,
[410]=move_rail_cart,
[441]=move_drill,
}

function has_usecode(obj, usecode_type)
      --print("has_usecode("..obj.obj_n..", "..usecode_type..")\n")
   if usecode_type == USE_EVENT_USE and usecode_table[obj.obj_n] ~= nil then
      return true
   elseif usecode_type == USE_EVENT_READY and (usecode_ready_obj_table[obj.obj_n] ~= nil or usecode_unready_obj_table[obj.obj_n] ~= nil)then
      return true
   elseif usecode_type == USE_EVENT_MOVE and usecode_move_obj_table[obj.obj_n] ~= nil then
      return true
   end
   
   return false
end

function use_obj_on_to(obj, target_obj, actor, use_to_tbl)
	local dir = get_direction(i18n("TO"))
	if dir == nil then
		printl("NOTHING")
		return
	end
	
	local to_x, to_y = direction_get_loc(dir, actor.x, actor.y)
	
	local to_obj = map_get_obj(to_x, to_y, actor.z)
	
	if to_obj ~= nil then
		print(to_obj.name.."\n\n")
		local func = use_to_tbl[to_obj.obj_n]
		if func ~= nil then
			func(obj, target_obj, to_obj, actor)
		end
	else
		printl("SOMETHING")
	end
end

function use_obj_on(obj, actor, use_on_tbl)
	local dir = get_direction(i18n("ON"))
	if dir == nil then
      printl("NOTHING")
		return
	end

	local target_x, target_y = direction_get_loc(dir, actor.x, actor.y)

   local target_entity = map_get_actor(target_x, target_y, actor.z)
   if target_entity == nil then
      target_entity = map_get_obj(target_x, target_y, actor.z)
   end
	
	if target_entity ~= nil then
		print(target_entity.name.."\n\n")
		local on = use_on_tbl[target_entity.obj_n]
		if on ~= nil then
			if type(on) == "function" then
				local func = on
				func(obj, target_entity, actor)
			else
				use_obj_on_to(obj, target_entity, actor, on)
			end
		else
			local func = use_on_tbl[0]
			if func ~= nil then
				func(obj, target_entity, actor)
			else
				printl("NO_EFFECT")
			end
		end
	else
		local func = use_on_tbl[0]
		if func ~= nil then
			func(obj, nil, actor, target_x, target_y, actor.z)
		else
			printl("NOTHING")
		end
	end
end

function can_interact_with_obj(actor, obj)
    if obj.on_map then
        --FIXME get_combat_range()
        local distance = actor_find_max_wrapped_xy_distance(actor, obj.x, obj.y)
        if actor_is_affected_by_purple_berries(actor.actor_num) and distance <= actor.level then
            return true
        end

        if distance > 1 then
            printl("OUT_OF_RANGE")
            return false
        end
    end

    return true
end

function use_obj(obj, actor)
    if not can_interact_with_obj(actor, obj) then
        return
    end

	if type(usecode_table[obj.obj_n]) == "function" then
		local func = usecode_table[obj.obj_n]
		if func ~= nil then
			print("\n")
			func(obj, actor)
		end
	else
		use_obj_on(obj, actor, usecode_table[obj.obj_n])
   end

   update_objects_around_party()
end

function ready_obj(obj, actor)
   if type(usecode_ready_obj_table[obj.obj_n]) == "function" and obj.readied == false then
      local func = usecode_ready_obj_table[obj.obj_n]
      if func ~= nil then
         print("\n")
         return func(obj, actor)
      end
   end

   if type(usecode_unready_obj_table[obj.obj_n]) == "function" and obj.readied == true then
      local func = usecode_unready_obj_table[obj.obj_n]
      if func ~= nil then
         print("\n")
         return func(obj, actor)
      end
   end
   
   return true
end

function move_obj(obj, rel_x, rel_y)
  if usecode_move_obj_table[obj.obj_n] ~= nil then
    return usecode_move_obj_table[obj.obj_n](obj, rel_x, rel_y)
  end
	return true
end

function is_ranged_select(operation)
    return actor_is_affected_by_purple_berries(Actor.get_player_actor().actor_num)
end