local lua_file = nil

--load common functions
lua_file = nuvie_load("common/common.lua"); lua_file();

OBJLIST_OFFSET_HOURS_TILL_NEXT_HEALING  = 0x1cf2
OBJLIST_OFFSET_PREV_PLAYER_X            = 0x1d03
OBJLIST_OFFSET_PREV_PLAYER_Y            = 0x1d04
OBJLIST_OFFSET_1D22_UNK                 = 0x1d22
OBJLIST_OFFSET_DREAM_MODE_FLAG          = 0x1d29

OBJLIST_OFFSET_BERRY_COUNTERS           = 0x1d2f
OBJLIST_OFFSET_DREAM_STAGE              = 0x1d53

function dbg(msg_string)
   --io.stderr:write(msg_string)
end

g_hours_till_next_healing = 0
g_in_dream_mode = false

local g_selected_obj
local g_attack_target

function update_watch_tile()
   local anim_index = get_anim_index_for_tile(616) --616 = watch tile
   if anim_index ~= nil then
      local new_watch_tile = 416 + clock_get_hour() % 12
      anim_set_first_frame(anim_index, new_watch_tile)
   end
end

function load_game()
   g_selected_obj = nil
   g_attack_target = nil

   objlist_seek(OBJLIST_OFFSET_HOURS_TILL_NEXT_HEALING)
   g_hours_till_next_healing = objlist_read1()

   objlist_seek(OBJLIST_OFFSET_DREAM_MODE_FLAG)
   g_in_dream_mode = bit32.btest(objlist_read2(), 0x10)
   map_enable_temp_actor_cleaning(not g_in_dream_mode)

   if g_in_dream_mode then
      lock_inventory_view(Actor.get(0))
   end

   objlist_seek(OBJLIST_OFFSET_DREAM_STAGE)
   g_current_dream_stage = objlist_read2()

   objlist_seek(OBJLIST_OFFSET_PREV_PLAYER_X)
   g_prev_player_x = objlist_read1()

   objlist_seek(OBJLIST_OFFSET_PREV_PLAYER_Y)
   g_prev_player_y = objlist_read1()

   objlist_seek(OBJLIST_OFFSET_1D22_UNK)
   g_objlist_1d22_unk = objlist_read1()

   update_watch_tile()
end

function save_game()
   objlist_seek(OBJLIST_OFFSET_HOURS_TILL_NEXT_HEALING)
   objlist_write1(g_hours_till_next_healing)

   objlist_seek(OBJLIST_OFFSET_DREAM_MODE_FLAG)
   local bytes = objlist_read2()
   if g_in_dream_mode then
      bytes = bit32.bor(bytes, 0x10)
   else
      bytes = bit32.band(bytes, 0xFFEF)
   end
   objlist_seek(OBJLIST_OFFSET_DREAM_MODE_FLAG)
   objlist_write2(bytes)

   objlist_seek(OBJLIST_OFFSET_DREAM_STAGE)
   objlist_write2(g_current_dream_stage)

   objlist_seek(OBJLIST_OFFSET_PREV_PLAYER_X)
   objlist_write1(g_prev_player_x)

   objlist_seek(OBJLIST_OFFSET_PREV_PLAYER_Y)
   objlist_write1(g_prev_player_y)

   objlist_seek(OBJLIST_OFFSET_1D22_UNK)
   objlist_write1(g_objlist_1d22_unk)
end



local g_container_obj_tbl = {
[80]=1, [81]=1, [82]=1,
[83]=1, [85]=1, [86]=1,
[87]=1, [89]=1,[304]=1,
[139]=1,[341]=1,[257]=1,
[104]=1,[284]=1,[285]=1
}

function is_blood(obj_num)
   if obj_num == 334 or obj_num == 335 or obj_num == 336 then --OBJ_BLOOD, OBJ_ICHOR, OBJ_SAP
      return true
   end

   return false
end

function is_container_obj(obj_num)
   if g_container_obj_tbl[obj_num] ~= nil then
      return true
   end
   return false
end

--OBJ_CLOSED_DOOR, OBJ_GLOW_WORM, OBJ_DEVIL_POD, OBJ_DEVIL_PLANT, OBJ_GLASS_PITCHER
local attackable_obj_tbl = {
   [0xB3]=1,
   [0x184]=1,
   [0x133]=1,
   [0x12C]=1,
   [0xD9]=1,
}

function is_obj_attackable(obj)
   return attackable_obj_tbl[obj.obj_n] ~= nil
end

--OBJ_VINE, OBJ_TREE, OBJ_PORCUPOD, OBJ_FLOWER, OBJ_HARD_SHELLED_PLANT
--OBJ_DEVIL_PLANT, OBJ_DEVIL_POD, OBJ_HEDGE
local plant_obj_tbl = {
   [0xCD]=1,
   [0x198]=1,
   [0xA8]=1,
   [0xAA]=1,
   [0xAB]=1,
   [0x12C]=1,
   [0x133]=1,
   [0x12B]=1,
}

function is_plant_obj(obj)
   return plant_obj_tbl[obj.obj_n] ~= nil
end

--OBJ_DOLLAR, OBJ_RUBLE, OBJ_PHOTOGRAPH, OBJ_THREAD, OBJ_BOX_OF_CIGARS
--OBJ_MATCH, OBJ_BOOK, OBJ_MAP, OBJ_NOTE, OBJ_WORMSBANE_SEED
--OBJ_SCROLL, OBJ_LIVE_MARTIAN_SEED, OBJ_PILE_OF_COAL
--OBJ_HUGE_LUMP_OF_COAL, OBJ_CHUNK_OF_ICE
local burnable_obj_tbl = {
   [0x18]=1,
   [0x84]=1,
   [0x0B9]=1,
   [0x3E]=1,
   [0x65]=1,
   [0x6B]=1,
   [0x94]=1,
   [0x96]=1,
   [0x97]=1,
   [0x9E]=1,
   [0x0F3]=1,
   [0x0FC]=1,
   [0x1BC]=1,
   [0x1BF]=1,
   [0x100]=1,
}

function is_obj_burnable(obj)
   return burnable_obj_tbl[obj.obj_n] ~= nil
end

function is_open_water_at_loc(x, y, z)
   local tile_num = map_get_tile_num(x, y, z)
   local is_water = tile_get_flag(tile_num, 1, 0)
   if not is_water then
      return false
   end

   if not tile_get_flag(tile_num, 1, 1) then -- not blocked
      return false
   end

   for obj in objs_at_loc(location) do
      tile_num = obj.tile_num
      --FIXME original does this too
      -- sub     ax, word_40FA2
      if tile_get_flag(tile_num, 3, 1) or tile_get_flag(tile_num, 3, 2) then --SUPPORT_OBJECT or FORCE_PASSABLE
         return false
      end
   end

   return true
end

function search(obj)
   if obj.on_map == false then
      return
   end

   local found_obj = false
   local child
   local first_loop = true
   local prev_obj = nil
   for child in container_objs(obj) do
      if prev_obj ~= nil then
         printfl("SEARCH_NEXT_OBJ", prev_obj.look_string)
         Obj.moveToMap(prev_obj, obj.x, obj.y, obj.z)
      end

      if first_loop == true then
         found_obj = true
         printfl("SEARCHING_HERE_YOU_FIND", child.look_string)
         Obj.moveToMap(child, obj.x, obj.y, obj.z)
      else
         prev_obj = child
      end

      script_wait(50)
      first_loop = false
   end

   if prev_obj ~= nil then
      printfl("SEARCH_LAST_OBJ", prev_obj.look_string)
      Obj.moveToMap(prev_obj, obj.x, obj.y, obj.z)
   end

   if found_obj == false then
      printl("SEARCHING_HERE_YOU_FIND_NOTHING")
   else
      print(".\n")
   end

end

--tile_num, readied location
local g_readiable_objs_tbl = {
-- 0 = head
[512] = 0, --cap
[513] = 0, --cowboy hat
[514] = 0, --pith hat
[515] = 0, --military hat
[516] = 0, --derby
-- 1 = neck
[517] = 1, --kerchief
[518] = 1, --silk scarf
[519] = 1, --muffler
[1048] = 1, --martian jewelry
[1049] = 1, --martian jewelry
[1050] = 1, --martian jewelry
[1051] = 1, --martian jewelry
[1052] = 1, --martian jewelry
[1053] = 1, --martian jewelry
[1054] = 1, --martian jewelry
[1055] = 1, --martian jewelry
-- 3 = 1 handed
[527] = 3, --bloody saber
[552] = 3, --derringer
[553] = 3, --revolver
[560] = 3, --hatchet
[561] = 3, --axe
[562] = 3, --ball-peen hammer
[565] = 3, --knife
[566] = 3, --machete
[567] = 3, --saber
[622] = 3, --pry bar
[650] = 3, --weed sprayer
[667] = 3, --tongs
[1068] = 3, --martian ritual pod knife
-- 2 = torso
[528] = 2, --cloth jacket
[529] = 2, --wool sweater
[530] = 2, --cape
[531] = 2, --duster
[532] = 2, --wool overcoat
[533] = 2, --sheepskin jacket
[534] = 2, --artic parka
[608] = 2, --electric belt
-- 3 = shield hand
[630] = 3, --torch
[631] = 3, --lit torch
[632] = 3, --candlestick
[633] = 3, --lit candlestick
[634] = 3, --candelabra
[635] = 3, --lit candelabra
[636] = 3, --oil lamp
[637] = 3, --lit oil lamp
[638] = 3, --lantern
[639] = 3, --lit lantern
-- 6 = UNK pants or dress FIXME: putting this here for now
[536] = 6, --cotton pants
[537] = 6, --cotton dress
[538] = 6, --denim jeans
[539] = 6, --wool pants
[540] = 6, --wool dress
[541] = 6, --chaps and jeans
-- 7 = feet
[520] = 7, --man's shoes
[521] = 7, --woman's shoes
[522] = 7, --riding boots
[523] = 7, --ruby slippers
[524] = 7, --thigh boots
[525] = 7, --hip boots
[526] = 7, --winged shoes
[542] = 7, --man's shoes
-- 4 = two handed
[551] = 4, --Cupid's bow and arrows
[554] = 4, --shotgun
[555] = 4, --rifle
[556] = 4, --Belgian combine
[557] = 4, --elephant gun
[558] = 4, --sling
[559] = 4, --bow
[563] = 4, --sledge hammer
[576] = 4, --pick
[577] = 4, --shovel
[578] = 4, --hoe
[579] = 4, --rake
[580] = 4, --pitchfork
[581] = 4, --cultivator
[582] = 4, --scythe
[583] = 4, --saw
[717] = 4, --throw rug
[718] = 4, --red cape
[1066] = 4, --heat ray gun
[1067] = 4, --freeze ray gun
[1093] = 4, --spray gun
[1095] = 4, --martian hoe (couldn't be equipped in original)
[1096] = 4, --martian scythe (couldn't be equipped in original)
[1097] = 4, --martian pitchfork (couldn't be equipped in original)
[1098] = 4, --martian rake (couldn't be equipped in original)
[1099] = 4, --martian shovel (couldn't be equipped in original)
[1188] = 4, --M60 machine gun
[1206] = 4, --martian pick (couldn't be equipped in original)
[1897] = 4, --pool cue
-- 5 = finger gloves and bracelets FIXME: putting this here for now
[544] = 5, --lady's silk gloves
[545] = 5, --driving gloves
[546] = 5, --cotton work gloves
[547] = 5, --work gloves
[548] = 5, --wool mittens
[549] = 5, --rubber gloves
[550] = 5 --welding gloves
}

function wrap_coord(coord, level)
   local map_stride = 1024
   if level ~= 0 then
      map_stride = 256
   end

   if coord < 0 then
      return map_stride + coord
   end

   return coord % map_stride
end

function can_move_obj(obj, rel_x, rel_y)
   local z = obj.z
   return map_can_put_obj(wrap_coord(obj.x+rel_x,z), wrap_coord(obj.y+rel_y,z), z)
end

function obj_get_readiable_location(obj)
   if g_readiable_objs_tbl[obj.tile_num] ~= nil then
      return g_readiable_objs_tbl[obj.tile_num]
   end

   return -1
end

function update_lamp_posts()
   --Turn lamps on/off if we have power and it is dark.
   local frame_n = 3
   local hour = clock_get_hour()
   if Actor.get_talk_flag(0x73, 4) and (hour < 6 or hour > 17) then
      frame_n = 7
   end

   local loc = player_get_location()
   for obj in find_obj(loc.z, 228) do --OBJ_LAMP_POST
      if obj ~= nil then
         obj.frame_n = frame_n
      end
   end
end

local PLAY_ASYNC = true
function play_md_sfx(sfx_id, play_async)
   --FIXME
end

function play_door_sfx()
   --FIXME
end

function create_object_needs_quan(obj_n)
-- obj.stackable is already checked
   if obj_n == 196 or obj_n == 311 or obj_n == 312 then --OBJ_LEVER, OBJ_SWITCH, OBJ_SWITCH1
      return true
   else
      return false
   end
end

function input_select_obj_qty(obj)
   if not obj.stackable then
      return 1
   end

   printl("HOW_MANY")
   local qty = input_select_integer("0123456789", true)
   if qty > obj.qty then
      return obj.qty
   end
   return qty
end

--load actor functions
local actor_load = nuvie_load("md/actor.lua");
if type(actor_load) == "function" then
   actor_load()
else
   if type(actor_load) == "string" then
      --io.stderr:write(actor_load);
   end
end

look_init = nuvie_load("md/look.lua"); look_init();

combat_init = nuvie_load("md/combat.lua"); combat_init();

-- init usecode
usecode_init = nuvie_load("md/usecode.lua"); usecode_init();

talk_init = nuvie_load("md/talk.lua"); talk_init();

player_init = nuvie_load("md/player.lua"); player_init();

worktype_init = nuvie_load("md/worktype.lua"); worktype_init();

dreamworld_init = nuvie_load("md/dreamworld.lua"); dreamworld_init();
