-- directory/config strings ----------------------------------------------------------
media_dir = "./media/"
src_dir = "./src/"
config_dir = "./config/"

default = "default.lua"
widescreen = "widescreen.lua"
windowed = "windowed.lua"

-- test vars
once = true
debug_time = 0

stripped_version = false
quick_load = false
straight_to_game = false
debug = false

-- global constants -----------------------------------------------------------
main_menu = 1
game_opening = 2
main_game = 3
game_closing = 4

layer_offset = 0.001 -- make sure the number is as small as it can be without confusing the z-buffer

huge = 10000000000000

left_mouse = 1
right_mouse = 3

window_margin_horizontal = 10
window_margin_vertical = 10

text_scale = 14 -- scale how big the text is
cursor_scale = 1 -- scales how big the cursor is
-- player can pick objects where view_distance_near <= [distance to object] <= view_distance_far
-- TODO: these are somehow effected the space in an "absolute" way instead of relative to view
view_distance_far = 70 -- TODO fix bug...these values work well but it seems like this is more like 15ft!
view_distance_near = 5
talk_distance = 20

run = 10 -- running rate in feet per second
walk = 7 -- walking rate in feet per second

animation_rate = .06 -- how quickly animations run in seconds per animation unit (arbitrary)
object_boundary_bias = 1 -- affects how close animation will bring player to paintings
anim_path_curviness = .2 -- affects how exagerated the curve is that the player follows to the paintings

category_none = 0
category_picker = 1
--category_scene = 2
category_person = 4
category_painting = 8
category_player = 16
--category_saying = 32
category_walls = 64
category_floor = 128
category_scene = category_walls + category_floor
category_pickable = category_person + category_painting-- + category_saying
category_all = category_picker + category_scene + category_pickable

-- animation types
anim_go_to_object = 1
anim_dialog_box = 2
anim_open_map = 3
anim_close_map = 4
anim_paused = 5
anim_opening_sequence = 6

-- global game variables ------------------------------------------------------
load_screen = nil

viewport = { }

time_accumulator = 0

camera = nil
light = nil

game_state = game_opening

overlay = { }

world = nil
player = nil

picked_object = nil

-- cursor/picking --
cursor_vec = { }
cursor_vec.x, cursor_vec.y, cursor_vec.z = 0,0,0

cursor = nil
cursor_none = nil
cursor_default = nil
cursor_hover = nil
cursor_picking = nil

cursor_ray = nil

hover_contact = false
hover = false

picking = false
picked = false

-- position/rotation information --
-- previously defined --
-- player_pos = { }
-- player_view = { }
camera_pos = { } -- temporary collector, not persistently accurate
camera_pos.x, camera_pos.y, camera_pos.z = 0,0,0
camera_view = { } -- temporary collector, not persistently accurate
camera_view.x, camera_view.y, camera_view.z = 0,0,0
cursor_pos = { } -- temporary collector, not persistently accurate
cursor_pos.y, cursor_pos.y, cursor_pos.z = 0,0,0

-- player movement --
pace = walk -- walking pace in feet per second
mouse_sensitivity = 64

rotate = false
rot = { } -- temporary collector, not persistently accurate
rot.x, rot.y = 0,0

-- animation --
-- previously defined --
-- time_accumulator = 0
control_lock = false
control_lock_partial = false
animation = nil
anim_units = 0
timing_coeffecient = 0
current_anim_unit = 0

-- anim_go_to_object
cp1 = { }
cp2 = { }
cp3 = { }
cp4 = { }
cp1.x, cp1.z, cp2.x, cp2.z, cp3.x, cp3.z, cp4.x, cp4.z = 0,0,0,0,0,0,0,0
last_pos = { }
last_pos.x, last_pos.y, last_pos.z = 0,0,0
dTheta_x = 0

-- aux functions --------------------------------------------------------------
function make_opaque_image_sprite(filename)
    local image = E.create_image(filename)
    local brush = E.create_brush()
    E.set_brush_flags(brush, E.brush_flag_unlit, true)
    E.set_brush_image(brush, image)

	return E.create_sprite(brush)
end

function make_opaque_image_billboard(filename)
    local sprite = make_opaque_image_sprite(filename)
    E.set_entity_flags(sprite, E.entity_flag_billboard, true)

    return sprite
end

function fit_to_view(entity, percent_scale_x, percent_scale_y, percent_off_x, percent_off_y)
	local x_min, y_min, z_min, x_max, y_max, z_max = E.get_entity_bound(entity)

	E.set_entity_scale(entity, percent_scale_x * viewport.width / (x_max - x_min), percent_scale_y * viewport.height / (y_max - y_min), 1)
	E.set_entity_position(entity, percent_off_x * viewport.width, percent_off_y * viewport.height, viewport.depth_bisect)
end

-- return the sign of a number
function sign(num)
	if num >= 0 then
		return 1
	else
		return -1
	end
end

-- return the dot_3d product of two vectors
function dot_2d(vec1_x, vec1_y, vec2_x, vec2_y)
	return vec1_x * vec2_x + vec1_y * vec2_y
end

-- return the dot_3d product of two vectors
function dot_3d(vec1_x, vec1_y, vec1_z, vec2_x, vec2_y, vec2_z)
	return vec1_x * vec2_x + vec1_y * vec2_y + vec1_z * vec2_z
end

-- return the normalize_2d version of the argument vector
function normalize_2d(vec_x, vec_y)
	if vec_x == 0 and vec_y == 0 then
		return 0, 0
	end
	
	local mag = math.sqrt(math.pow(vec_x, 2) + math.pow(vec_y, 2))
	return vec_x / mag, vec_y / mag
end


-- return the normalize_3d version of the argument vector
function normalize_3d(vec_x, vec_y, vec_z)
	if vec_x == 0 and vec_y == 0 and vec_z == 0 then
		return 0, 0, 0
	end
	
	local mag = math.sqrt(math.pow(vec_x, 2) + math.pow(vec_y, 2) + math.pow(vec_z, 2))
	return vec_x / mag, vec_y / mag, vec_z / mag
end

-- get 2d distance between two points x0, y0, x1, y1
function get_distance_2d(x0, y0, x1, y1)
	return math.sqrt(math.pow(x1 - x0, 2) + math.pow(y1 - y0, 2))
end

--[[function get_distance_3d(x0, y0, z0, x1, y1, z1)
	return get_distance_2d(x0, y0, x1, y1)
end]]

-- get 3d distance between two points x0, y0, z0, x1, y1, z1
function get_distance_3d(x0, y0, z0, x1, y1, z1)
	return math.sqrt(math.pow(x1 - x0, 2) + math.pow(y1 - y0, 2) + math.pow(z1 - z0, 2))
end

function rotate_vector_2d(vec_x, vec_y, deg)
	local theta = math.rad(deg)
	local new_vec_x, new_vec_y = 0, 0
	new_vec_x = vec_x * math.cos(theta) - vec_y * math.sin(theta)
	new_vec_y = vec_y * math.cos(theta) + vec_x * math.sin(theta)
	return new_vec_x, new_vec_y
end

function get_theta_2d(x0, y0, x1, y1)
	local dot_prod = dot_2d(x0, y0, x1, y1)
	return sign(y1-y0) * math.deg(math.acos(dot_prod))
end

function get_theta_3d(x0, y0, z0, x1, y1, z1)
	local dot_prod = dot_3d(x0, y0, z0, x1, y1, z1)
	return sign(y1-y0) * math.deg(math.acos(dot_prod))
end

-- return a sprite with transparency enabled (uses an image with transparency info)
function make_transparent_image_sprite(filename)
	local image = E.create_image(filename)
    local brush = E.create_brush()
    E.set_brush_flags(brush, E.brush_flag_transparent, true)
    E.set_brush_flags(brush, E.brush_flag_unlit,       true)
    E.set_brush_image(brush, image)
    
    return E.create_sprite(brush), E.get_image_size(image)
end

-- return a sprite billboard with transparency enabled (uses an image with transparency info)
function make_transparent_image_billboard(filename)
    local sprite = make_transparent_image_sprite(filename)
    E.set_entity_flags(sprite, E.entity_flag_billboard, true)

    return sprite
end

-- set the current cursor to argument type
function set_cursor(type)
	-- if current cursor is same as argument we don't need to do anything
	if cursor == type then
		return
	end
	
	-- hide all cursors
	E.set_entity_flags(cursor_hover, E.entity_flag_hidden, true)
	E.set_entity_flags(cursor_picking, E.entity_flag_hidden, true)
	E.set_entity_flags(cursor_default, E.entity_flag_hidden, true)
	
	-- show selected cursor
	E.set_entity_flags(type, E.entity_flag_hidden, false)
	
	-- assign it globally for state purposes in other parts of the code
	cursor = type
end

-- update the cursors position on screen, should be called every frame
active_object = nil
function update_cursor()
	-- figure out cursor type needed
	if rotate or animation == anim_go_to_object then
		set_cursor(cursor_blank)
	elseif picking and hover then
		if active_object == hovered_object then
			set_cursor(cursor_picking)
		else
			set_cursor(cursor_default)
		end
	elseif hover then
		set_cursor(cursor_hover)
	else
		set_cursor(cursor_default)
	end

	-- get the camera position and vector from camera to cursor in 3d space
	cursor_vec.x, cursor_vec.y, cursor_vec.z = E.get_camera_vector(camera_ortho, mouse.x, viewport.h - mouse.y)
	
	-- move the cursor to the proper 3d position
	E.set_entity_position(cursor, cursor_vec.x, cursor_vec.y, cursor_vec.z)
	
	-- recompute the cursor_ray used to find pickable objects
	-- geom_type_ray properties can't dynamically change, so we must delete and recreate the ray with new properties
	E.delete_entity(cursor_ray)
	cursor_ray = E.create_object()

	cursor_vec.x, cursor_vec.y, cursor_vec.z = normalize_3d(E.get_camera_vector(camera, mouse.x, mouse.y))
	camera_pos.x, camera_pos.y, camera_pos.z = E.get_entity_position(camera)
	
	-- set cursor_ray to originate at camera and go through cursor for a distance of "view_distance_far"
	-- we need cursor to intersect things in the overlay layer, so we don't account for view_distance_near here
	-- we do this in our do_contact function
	E.set_entity_geom_type(cursor_ray, E.geom_type_ray, camera_pos.x + cursor_vec.x * view_distance_near, --
		camera_pos.y + cursor_vec.y * view_distance_near, camera_pos.z + cursor_vec.z * view_distance_near, --
		cursor_vec.x * view_distance_far, cursor_vec.y * view_distance_far, cursor_vec.z * view_distance_far)
		
	--[[if debug_time > 4 then
		--E.print_console("length: ", get_distance_3d(0,0,0, cursor_vec.x * view_distance_near, cursor_vec.y * view_distance_near, cursor_vec.z * view_distance_near), "\n")
		E.print_console("pos: ", camera_pos.x + cursor_vec.x * view_distance_near, ", ", --
			camera_pos.y + cursor_vec.y * view_distance_near, ", ", camera_pos.z + cursor_vec.z * view_distance_near, "\n")
		debug_time = 0
	end]]
			
	-- set collision category information
	E.set_entity_geom_attr(cursor_ray, E.geom_attr_category, category_picker)
	E.set_entity_geom_attr(cursor_ray, E.geom_attr_collider, category_pickable + category_walls)
	E.set_entity_geom_attr(cursor_ray, E.geom_attr_callback, category_pickable + category_walls)
	--E.set_entity_geom_attr(cursor_ray, E.geom_attr_collider, category_pickable)
	--E.set_entity_geom_attr(cursor_ray, E.geom_attr_callback, category_pickable)
	
	--E.set_entity_flags(cursor_ray, E.entity_flag_visible_geom, debug)
	
	-- set relationships
	E.parent_entity(cursor_ray, camera)
end

-- generate a bezier curve from the given constraints, store results in globals ax,bx,cx,az,bz,cz
ax,bx,cx = 0,0,0
az,bz,cz = 0,0,0
function generate_bezier(cp1, cp2, cp3, cp4)
    cx = 3.0 * (cp2.x - cp1.x);
    bx = 3.0 * (cp3.x - cp2.x) - cx;
    ax = cp4.x - cp1.x - cx - bx;
        
    cz = 3.0 * (cp2.z - cp1.z);
    bz = 3.0 * (cp3.z - cp2.z) - cz;
    az = cp4.z - cp1.z - cz - bz;
end

-- using globals defined by generate_bezier, compute position on curve at "time" dt
-- 0 <= dt <= 1
function get_bezier_pos(dt)
	local dt2 = dt * dt;
    local dt3 = dt2 * dt;
    local pos_x, pos_z = 0,0
        
    pos_x = (ax * dt3) + (bx * dt2) + (cx * dt) + cp1.x;
    pos_z = (az * dt3) + (bz * dt2) + (cz * dt) + cp1.z;
        
    return pos_x, pos_z;
end

-- puts object with its upper left corner at x, y in windows coordinates
function add_to_overlay(entity, x, y, w, h, target, d, pickable, action_num)
	-- modify x and y if they will result in the object going off screen
	local max_x = x + w
	if max_x > viewport.w then
		x = x - (max_x - viewport.w) - window_margin_horizontal
	end
	
	local max_y = y + h
	if max_y > viewport.h then
		y = y - (max_y - viewport.h) - window_margin_vertical
	end
	
	-- make x,y refer to center of our object for placement
	local pos_x, pos_y = x + w / 2, viewport.h - (y + h / 2)
	
	-- get the vector from camera to cursor in 3d space
	--cursor_vec.x, cursor_vec.y, cursor_vec.z = E.get_camera_vector(camera_ortho, pos_x, pos_y)
	
	E.set_entity_position(entity, pos_x, pos_y, -1)--cursor_vec.x, cursor_vec.y, cursor_vec.z)
	
	E.parent_entity(entity, camera_ortho)

	overlay[entity] = {
		["pickable"] = pickable;
		["action_num"] = action_num;
		["target"] = target;
		["d"] = d;
		["min_x"] = x;
		["min_y"] = y;
		["max_x"] = x + w;
		["max_y"] = y + h;
	}
	--E.print_console("min/max: ", overlay[entity].min_x, ", ", overlay[entity].min_y, ", ", overlay[entity].max_x, ", ", overlay[entity].max_y, "\n")
end

function add_to_overlay_timed(entity, x, y, time)
	-- make x,y refer to center of our object for placement
	local pos_x, pos_y = x, viewport.h - y
	
	-- get the vector from camera to cursor in 3d space
	cursor_vec.x, cursor_vec.y, cursor_vec.z = E.get_camera_vector(camera_ortho, pos_x, pos_y)
	
	E.set_entity_position(entity, cursor_vec.x, cursor_vec.y, cursor_vec.z)
	
	E.parent_entity(entity, camera_ortho)

	overlay[entity] = {
		["min_x"] = x;
		["min_y"] = y;
		["time"] = time;
		["stampT"] = 0;
	}
end

cursor_over_question = false
--active_question = 0
function update_overlay(dt)
	--local removed = false
	local i = 1
	--local on_question_now = false
	cursor_over_question = false
	local action_num = nil
	for k,v in pairs(overlay) do
		if v.target then
			local pos_x, tmp, pos_z = E.get_entity_position(v.target)
			if get_distance_2d(pos_x, pos_z, player_pos.x, player_pos.z) > v.d then
				table.remove(overlay,i)
				E.delete_entity(k)
				
				local category = E.get_entity_geom_attr(v.target, E.geom_attr_category)
				if category == category_person then
					npc_go_idle(v.target)
				end
				--removed = true
			end
		end
			
		--if not removed then
		if v.pickable then
			if mouse.x >= v.min_x and mouse.x <= v.max_x and mouse.y <= v.max_y and mouse.y >= v.min_y then
				cursor_over_question = true
				action_num = v.action_num
			end
		end
		--removed = false
		
		if v.time then
			v.stampT = v.stampT + dt
			if v.stampT > v.time then
				table.remove(overlay,i)
				E.delete_entity(k)
			end
		end
		i = i + 1
	end
	
	if control_lock then
		return
	end
	
	if cursor_over_question and not rotate then
		if not hover then
			hover = true
		end
		hover_contact = true
		hovered_object = action_num
		
		for i,v in ipairs(question_masks) do
			if i == action_num then
				E.set_entity_alpha(v, 1.0)
			else
				E.set_entity_alpha(v, 0.0)
			end
		end
	else
		for i,v in ipairs(question_masks) do
			E.set_entity_alpha(v, 0.0)
		end
	end
end

function clear_overlay()
--	local i = 1
	for k,v in pairs(overlay) do
		--table.remove(overlay)
		E.delete_entity(k)
--		i = i + 1
	end
	overlay = { }
end

npc_stored_rot_y = 0
npc2_stored_rot_y = 0
talking_npc = nil
function npc_go_idle(npc)
	if npc ~= Wilhelm_Winckelmann and npc ~= Lacy_Lane and npc ~= Samuel_Harris then		
		E.set_entity_rotation(npc, 0, -npc_stored_rot_y, 0)
	end
	
	if npc == Martha_Stewart then
		E.set_entity_rotation(Abigail_Stewart, 0, -npc2_stored_rot_y, 0)
	elseif npc == Abigail_Stewart then
		E.set_entity_rotation(Martha_Stewart, 0, -npc2_stored_rot_y, 0)
	elseif npc == Bob_Cassady then
		E.set_entity_rotation(Raquel_Ramirez, 0, -npc2_stored_rot_y, 0)
	elseif npc == Raquel_Ramirez then
		E.set_entity_rotation(Bob_Cassady, 0, -npc2_stored_rot_y, 0)
	end
	
	if npc == Wilhelm_Winckelmann then
		E.set_entity_flags(Wilhelm_Winckelmann_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Wilhelm_Winckelmann_Idle, E.entity_flag_hidden, false)
	elseif npc == Lacy_Lane then
		E.set_entity_flags(Lacy_Lane_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Lacy_Lane_Idle, E.entity_flag_hidden, false)
	elseif npc == Samuel_Harris then
		E.set_entity_flags(Samuel_Harris_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Samuel_Harris_Idle, E.entity_flag_hidden, false)
	elseif npc == Martha_Stewart then
		E.set_entity_flags(Martha_Stewart_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Martha_Stewart_Idle, E.entity_flag_hidden, false)
	elseif npc == Dillon_White then
		E.set_entity_flags(Dillon_White_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Dillon_White_Idle, E.entity_flag_hidden, false)
	elseif npc == Bob_Cassady then
		E.set_entity_flags(Bob_Cassady_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Bob_Cassady_Idle, E.entity_flag_hidden, false)
	elseif npc == Raquel_Ramirez then
		E.set_entity_flags(Raquel_Ramirez_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Raquel_Ramirez_Idle, E.entity_flag_hidden, false)
	elseif npc == Chin_Li then
		E.set_entity_flags(Chin_Li_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Chin_Li_Idle, E.entity_flag_hidden, false)
	elseif npc == Claire_dAvignon then
		E.set_entity_flags(Claire_dAvignon_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Claire_dAvignon_Idle, E.entity_flag_hidden, false)
	end
	
	if npc == Abigail_Stewart then
		E.set_entity_flags(Martha_Stewart_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Martha_Stewart_Idle, E.entity_flag_hidden, false)
	elseif npc == Bob_Cassady then
		E.set_entity_flags(Raquel_Ramirez_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Raquel_Ramirez_Idle, E.entity_flag_hidden, false)
	elseif npc == Raquel_Ramirez then
		E.set_entity_flags(Bob_Cassady_Talking, E.entity_flag_hidden, true)
		E.set_entity_flags(Bob_Cassady_Idle, E.entity_flag_hidden, false)
	end
	
	talking_npc = nil
end

function turn_npc_toward_player(npc)
	-- rotate npc to face player, store applied rotation to undo afterwards
	local player_pos_x, tmp, player_pos_z = E.get_entity_position(player)
	local npc_pos_x, tmp, npc_pos_z = E.get_entity_position(npc)
	local npc_z_vec_x, tmp, npc_z_vec_z = E.get_entity_z_vector(npc)
	npc_z_vec_x, npc_z_vec_z = normalize_2d(npc_z_vec_x, npc_z_vec_z)
	local face_player_x, face_player_z = normalize_2d(player_pos_x - npc_pos_x, player_pos_z - npc_pos_z)
	
	local turn_y = get_theta_2d(0, 1, face_player_x, face_player_z)
	npc_stored_rot_y = sign(npc_z_vec_x) * get_theta_2d(0, 1, npc_z_vec_x, npc_z_vec_z)
	
	E.set_entity_rotation(npc, 0, sign(npc_pos_x - player_pos_x) * turn_y, 0)
end

function npc_start_talking(npc)
	if talking_npc then
		npc_go_idle(talking_npc)
	end
	
	if npc == Martha_Stewart then
		turn_npc_toward_player(Abigail_Stewart)
		npc2_stored_rot_y = npc_stored_rot_y
	elseif npc == Abigail_Stewart then
		turn_npc_toward_player(Martha_Stewart)
		npc2_stored_rot_y = npc_stored_rot_y
	elseif npc == Bob_Cassady then
		turn_npc_toward_player(Raquel_Ramirez)
		npc2_stored_rot_y = npc_stored_rot_y
	elseif npc == Raquel_Ramirez then
		turn_npc_toward_player(Bob_Cassady)
		npc2_stored_rot_y = npc_stored_rot_y
	end
	
	if npc ~= Wilhelm_Winckelmann and npc ~= Lacy_Lane and npc ~= Samuel_Harris then
		turn_npc_toward_player(npc)
	end

	if npc == Wilhelm_Winckelmann then
		E.set_entity_flags(Wilhelm_Winckelmann_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Wilhelm_Winckelmann_Idle, E.entity_flag_hidden, true)
	elseif npc == Lacy_Lane then
		E.set_entity_flags(Lacy_Lane_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Lacy_Lane_Idle, E.entity_flag_hidden, true)
	elseif npc == Samuel_Harris then
		E.set_entity_flags(Samuel_Harris_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Samuel_Harris_Idle, E.entity_flag_hidden, true)
	elseif npc == Martha_Stewart then
		E.set_entity_flags(Martha_Stewart_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Martha_Stewart_Idle, E.entity_flag_hidden, true)
	elseif npc == Dillon_White then
		E.set_entity_flags(Dillon_White_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Dillon_White_Idle, E.entity_flag_hidden, true)
	elseif npc == Bob_Cassady then
		E.set_entity_flags(Bob_Cassady_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Bob_Cassady_Idle, E.entity_flag_hidden, true)
	elseif npc == Raquel_Ramirez then
		E.set_entity_flags(Raquel_Ramirez_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Raquel_Ramirez_Idle, E.entity_flag_hidden, true)
	elseif npc == Chin_Li then
		E.set_entity_flags(Chin_Li_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Chin_Li_Idle, E.entity_flag_hidden, true)
	elseif npc == Claire_dAvignon then
		E.set_entity_flags(Claire_dAvignon_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Claire_dAvignon_Idle, E.entity_flag_hidden, true)
	end
	
	if npc == Abigail_Stewart then
		E.set_entity_flags(Martha_Stewart_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Martha_Stewart_Idle, E.entity_flag_hidden, true)
	elseif npc == Bob_Cassady then
		E.set_entity_flags(Raquel_Ramirez_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Raquel_Ramirez_Idle, E.entity_flag_hidden, true)
	elseif npc == Raquel_Ramirez then
		E.set_entity_flags(Bob_Cassady_Talking, E.entity_flag_hidden, false)
		E.set_entity_flags(Bob_Cassady_Idle, E.entity_flag_hidden, true)
	end
	
	talking_npc = npc
end

-- converts the passed saying into a lines of text centered at 0,0,0
function get_lines_of_text(saying)
	--E.print_console("GotString: ", saying, "\n")
	local str = saying
	local beg_ptr, end_ptr = 1,0
	local len = string.len(str)
	--E.print_console("len: ", len, "\n")
	
	local lines_of_text = E.create_pivot()
	local max_width = 0
	local total_height = 0
	local count = 0
	
	local min_x, min_y, tmp, max_x, max_y, width, height
	local line_of_text, text, brush_fill
	while len > max_text_line_width do
		count = count + 1
		line_of_text = E.create_pivot()
		
		-- get nearest previous whitespace from cut-off
		end_ptr = beg_ptr + max_text_line_width
		while not string.find(string.sub(saying, end_ptr, end_ptr),"%s") do
			end_ptr = end_ptr - 1
		end
		
		-- create entities for text rendering
		text = E.create_string(string.sub(saying, beg_ptr, end_ptr))
		brush_fill = E.create_brush()
		
		-- set brush characteristics
		E.set_brush_color(brush_fill, 1.0, 1.0, 0.6, 1.0)
		E.set_brush_flags(brush_fill, E.brush_flag_unlit, true)
		
		-- bind brush to text
		E.set_string_fill(text, brush_fill)
		
		-- center line_of_text
		min_x, min_y, tmp, max_x, max_y = E.get_entity_bound(text)
		width = (max_x - min_x) * text_scale
		height = (max_y - min_y) * text_scale
		E.set_entity_position(text, 0, -height / 2, 0)
		
		-- scale text
		E.set_entity_scale(text, text_scale, text_scale, 1)
		
		-- parent it to our line_of_text
		E.parent_entity(text, line_of_text)
		E.parent_entity(line_of_text, lines_of_text)
		
		max_width = math.max(width, max_width)
		total_height = total_height + height
		
		beg_ptr = end_ptr + 1 -- absorb space
		
		str = string.sub(saying, beg_ptr)
		len = string.len(str)
	end
	
	count = count + 1
	line_of_text = E.create_pivot()
	-- create entities for text rendering
	--E.print_console("laststr: ", str, "\n")
	text = E.create_string(str)
	brush_fill = E.create_brush()
	
	-- set brush characteristics
	E.set_brush_color(brush_fill, 1.0, 1.0, 0.6, 1.0)
	E.set_brush_flags(brush_fill, E.brush_flag_unlit, true)
	
	-- bind brush to text
	E.set_string_fill(text, brush_fill)
	
	-- center line_of_text
	min_x, min_y, tmp, max_x, max_y = E.get_entity_bound(text)
	width = (max_x - min_x) * text_scale
	height = (max_y - min_y) * text_scale
	E.set_entity_position(text, 0, -height / 2, 0)
	
	-- scale text
	E.set_entity_scale(text, text_scale, text_scale, 1)
	
	-- parent it to our line_of_text
	E.parent_entity(text, line_of_text)
	E.parent_entity(line_of_text, lines_of_text)

	max_width = math.max(width, max_width)
	total_height = total_height + height + (count - 1) * text_padding_vertical

	--E.print_console("max_width: ", max_width, ", total_h: ", total_height, "\n")
	if count > 1 then
		local start_height = -total_height / 2 + text_padding_vertical
		local dy = total_height / (count - 1) - text_padding_vertical / (count - 1)
		for i = 0, count - 1, 1 do
			local child = E.get_entity_child(lines_of_text, i)
			E.set_entity_position(child, 0, start_height + dy * i, 0)
		end
	end

	-- return the line_of_text and it's 3d space width/height
	return lines_of_text, max_width, total_height
end

-- create player's dialog box
blocks = { } -- store conceptually pickable regions encompassing each player response
blocks.n = 0
question_masks = { }
function show_questions(questions)
	clear_overlay()

	animation = anim_dialog_box
	anim_units = 8
	time_accumulator = 0
	--E.print_console("show_questions\n")
	
	local max_line_width = 0
	blocks.height = 0
	
	blocks.n = 0 -- reset array size, effectively clearing it
	
	-- create lines_of_text and put them in pickable "blocks"
	for k,v in pairs(questions) do
		-- inc block count
		blocks.n = blocks.n + 1
		
		blocks[blocks.n] = { }
		-- create block pivot that will be our pickable region
		blocks[blocks.n].entity = E.create_pivot()
		--blocks[blocks.n].entity = E.create_clone(dialog_box_mask.sprite)
		
		-- get our line_of_text enitity, string is centered at 0,0,0
		local lines_of_text, width, height = get_lines_of_text(v)
		blocks[blocks.n].height = text_padding_vertical * 2 + height
		blocks.height = blocks.height + blocks[blocks.n].height
		
		-- update max width and height if needed
		max_line_width = math.max(max_line_width, width)
		
		-- parent line_of_text to our current block
		E.parent_entity(lines_of_text, blocks[blocks.n].entity)
	end
	
	-- figure out the width/height of our pickable block regions now that we know the longest/tallest line of text
	blocks.width = text_padding_horizontal * 2 + max_line_width
	
	-- our blocks define how big our dialog_box should be, compute those scale factors now for dialog_box images
	local scale_width = blocks.width / dialog_box.width
	local scale_height = blocks.height / dialog_box.height
	
	-- width of dividers in dialog box should be the same as lines_of_text
	local scale_divider = max_line_width / dialog_box.width
	
	-- store new height and width values of our dialog box for positioning purposes
	local dialog_box_scaled_height = blocks.height -- dialog_box.height * scale_height
	local dialog_box_scaled_width = blocks.width -- dialog_box.width * scale_width
	
	-- create overlay pivot to attach our blocks to, we will also attach our box border to this
	dialog_box_overlay = E.create_pivot()
	
	-- create clone of dialog_box_border
	local top_bar = E.create_clone(dialog_box_divider.sprite)
	E.set_entity_position(top_bar, 0, dialog_box_scaled_height / 2, 0)
	local bottom_bar = E.create_clone(dialog_box_divider.sprite)
	E.set_entity_position(bottom_bar, 0, -dialog_box_scaled_height / 2, 0)
	local left_bar = E.create_clone(dialog_box_bar.sprite)
	E.set_entity_position(left_bar, -dialog_box_scaled_width / 2, 0, 0)
	local right_bar = E.create_clone(dialog_box_bar.sprite)
	E.set_entity_position(right_bar, dialog_box_scaled_width / 2, 0, 0)
	--[[local upper_left = E.create_clone(dialog_box_corner.sprite)
	local upper_right = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(upper_right, 0, 0, 90)
	local lower_right = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(lower_right, 0, 0, 180)
	local lower_left = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(lower_left, 0, 0, 270)]]
	
	-- scale border by compute values
	--E.set_entity_scale(border_clone, scale_width, scale_height , 1)
	E.set_entity_scale(top_bar, scale_width, 1, 1)
	E.set_entity_scale(bottom_bar, scale_width, 1, 1)
	E.set_entity_scale(left_bar, 1, scale_height, 1)
	E.set_entity_scale(right_bar, 1, scale_height, 1)
	--[[E.set_entity_scale(upper_left, scale_width, scale_height, 1)
	E.set_entity_scale(upper_right, scale_width, scale_height, 1)
	E.set_entity_scale(lower_right, scale_width, scale_height, 1)
	E.set_entity_scale(lower_left, scale_width, scale_height, 1)]]
	
	-- position border within the overlay
	--E.set_entity_position(border_clone, dialog_box_scaled_width / 2, -dialog_box_scaled_height / 2, 0)
	
	-- parent border to overlay
	E.parent_entity(top_bar, dialog_box_overlay)
	E.parent_entity(bottom_bar, dialog_box_overlay)
	E.parent_entity(left_bar, dialog_box_overlay)
	E.parent_entity(right_bar, dialog_box_overlay)
	--[[E.parent_entity(upper_left, dialog_box_overlay)
	E.parent_entity(upper_right, dialog_box_overlay)
	E.parent_entity(lower_right, dialog_box_overlay)
	E.parent_entity(lower_left, dialog_box_overlay)]]
	
	--local mask_scale_width = blocks.width / dialog_box_mask.width
	--local mask_scale_height = blocks.height / dialog_box_mask.height
	
	--local dialog_box_mask_clone = E.create_clone(dialog_box_mask.sprite)
	--E.set_entity_scale(dialog_box_mask_clone, scale_width, scale_height, 1)
	--E.set_entity_flags(dialog_box_mask_clone, E.entity_flag_hidden, true)
	
	dialog_box.pivot = E.create_pivot()	

	local dialog_box_background = E.create_clone(dialog_box_background.sprite)
	
	-- set alpha of light background to be slightly transparent, thus lighter and unaccented
	E.set_entity_alpha(dialog_box_background, 0.85)
	
	E.set_entity_scale(dialog_box_background, scale_width, scale_height, 1)
	
	-- create main pivot to contain all elements of dialog_box
	--dialog_box.pivot = E.create_pivot()	
	
	local max_x = active_mouse_x + blocks.width
	if max_x > viewport.w then
		active_mouse_x = active_mouse_x - (max_x - viewport.w) - window_margin_horizontal
	end
	
	local max_y = active_mouse_y + blocks.height
	if max_y > viewport.h then
		active_mouse_y = active_mouse_y - (max_y - viewport.h) - window_margin_vertical
	end

	E.parent_entity(dialog_box_overlay, dialog_box.pivot)
	E.parent_entity(dialog_box_background, dialog_box.pivot)
	
	local first = true
	-- go through each block and add it to our dialog_box_overlay
	local pos_y = 0
	local consumed_y = 0
	question_masks.n = blocks.n
	--local block_scale_width = (blocks.width - text_padding_horizontal) / dialog_box_mask.width
	local block_scale_width = blocks.width / dialog_box_mask.width
	for i = 1, blocks.n, 1 do
		pos_y = dialog_box_scaled_height / 2 - consumed_y - 0.5 * blocks[i].height
		
		-- if this isn't first block, put divider above block
		if not first then
			-- create clone of divider
			local divider_clone = E.create_clone(dialog_box_divider.sprite)
			
			-- scale divider's width to match block entities
			E.set_entity_scale(divider_clone, scale_divider, 1, 1)
			
			-- position divider inside block
			E.set_entity_position(divider_clone, 0, pos_y + blocks[i].height / 2, 0)
			
			-- parent the divider to the current block
			E.parent_entity(divider_clone, dialog_box_overlay)
		else
			first = false
		end
		
		-- set block position within overlay
		E.set_entity_position(blocks[i].entity, 0, pos_y, 0)
		
		-- parent blocks to overlay
		--E.parent_entity(blocks[i], dialog_box_overlay)
		
		E.set_entity_position(E.get_entity_child(blocks[i].entity, 0), -blocks.width / 2 + text_padding_horizontal, 0, 0)
		
		question_masks[i] = E.create_clone(dialog_box_mask.sprite)
		--local block_scale_height = (blocks[i].height - text_padding_vertical / 2) / dialog_box_mask.height
		local block_scale_height = blocks[i].height / dialog_box_mask.height
		E.set_entity_scale(question_masks[i], block_scale_width, block_scale_height, 1)
		E.set_entity_position(question_masks[i], 0, pos_y, -1) -- -1 for layering reasons
		E.set_entity_alpha(question_masks[i], 0.0)
		E.parent_entity(question_masks[i], dialog_box.pivot)
		--add_to_overlay(question_masks[i], 0, 0, blocks.width, blocks[i].height)
		
		-- parent to screen overlay as pickable items
		if questions == dialog.steal_prompt.questions then
			add_to_overlay(blocks[i].entity, active_mouse_x, active_mouse_y + consumed_y, blocks.width, blocks[i].height, painting_to_steal, talk_distance, true, i)
		else
			add_to_overlay(blocks[i].entity, active_mouse_x, active_mouse_y + consumed_y, blocks.width, blocks[i].height, active_npc, talk_distance, true, i)
		end
		
		consumed_y = consumed_y + blocks[i].height
	end
	
	-- create clone of dialog_box_border
	--local border_clone = E.create_clone(dialog_box_border.sprite) -- see all concerns about this process regarding brushes!
	
	-- scale border by compute values
	--E.set_entity_scale(border_clone, scale_width, scale_height, 1)
	
	-- parent border to overlay
	--E.parent_entity(border_clone, dialog_box_overlay)

	E.set_entity_alpha(dialog_box.pivot, 0.0)
	
	-- put dialog box on screen where user clicked TODO: this should be view_distance_far but there is a bug there now
	if questions == dialog.steal_prompt.questions then
		add_to_overlay(dialog_box.pivot, active_mouse_x, active_mouse_y, dialog_box_scaled_width, dialog_box_scaled_height, painting_to_steal, talk_distance, false, nil)
	else
		add_to_overlay(dialog_box.pivot, active_mouse_x, active_mouse_y, dialog_box_scaled_width, dialog_box_scaled_height, active_npc, talk_distance, false, nil)
	end
end

function show_npc_bubble(string)
	clear_overlay()

	animation = anim_dialog_box
	anim_units = 8
	time_accumulator = 0
	
	local text_pivot = E.create_pivot()
	local lines_of_text, width, height = get_lines_of_text(string)
	E.set_entity_position(lines_of_text, -width / 2, 0, 0)	
	
	E.parent_entity(lines_of_text, text_pivot)
	
	width = width + 2 * text_padding_horizontal
	height = height + 4 * text_padding_vertical
	
	--add_to_overlay(text_pivot, active_mouse_x, active_mouse_y, width + 2 * text_padding_horizontal, height + 2 * text_padding_vertical, active_npc, view_distance_far, false, nil)
	add_to_overlay(text_pivot, active_mouse_x, active_mouse_y, width, height, active_npc, view_distance_far, false, nil)

	local scale_width = width / dialog_box.width
	local scale_height = height / dialog_box.height
	
	local dialog_box_background = E.create_clone(dialog_box_npc_background.sprite)
	
	-- set alpha of light background to be slightly transparent, thus lighter and unaccented
	E.set_entity_alpha(dialog_box_background, 0.85)
	
	E.set_entity_scale(dialog_box_background, scale_width, scale_height, 1)

	dialog_box.pivot = E.create_pivot()

-- create overlay pivot to attach our blocks to, we will also attach our box border to this
	dialog_box_overlay = E.create_pivot()
	
	-- create clone of dialog_box_border
	local top_bar = E.create_clone(dialog_box_divider.sprite)
	E.set_entity_position(top_bar, 0, height / 2, 0)
	local bottom_bar = E.create_clone(dialog_box_divider.sprite)
	E.set_entity_position(bottom_bar, 0, -height / 2, 0)
	local left_bar = E.create_clone(dialog_box_bar.sprite)
	E.set_entity_position(left_bar, -width / 2, 0, 0)
	local right_bar = E.create_clone(dialog_box_bar.sprite)
	E.set_entity_position(right_bar, width / 2, 0, 0)
	--[[local upper_left = E.create_clone(dialog_box_corner.sprite)
	local upper_right = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(upper_right, 0, 0, 90)
	local lower_right = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(lower_right, 0, 0, 180)
	local lower_left = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(lower_left, 0, 0, 270)]]
	
	-- scale border by compute values
	--E.set_entity_scale(border_clone, scale_width, scale_height , 1)
	E.set_entity_scale(top_bar, scale_width, 1, 1)
	E.set_entity_scale(bottom_bar, scale_width, 1, 1)
	E.set_entity_scale(left_bar, 1, scale_height, 1)
	E.set_entity_scale(right_bar, 1, scale_height, 1)
	--[[E.set_entity_scale(upper_left, scale_width, scale_height, 1)
	E.set_entity_scale(upper_right, scale_width, scale_height, 1)
	E.set_entity_scale(lower_right, scale_width, scale_height, 1)
	E.set_entity_scale(lower_left, scale_width, scale_height, 1)]]
	
	-- position border within the overlay
	--E.set_entity_position(border_clone, dialog_box_scaled_width / 2, -dialog_box_scaled_height / 2, 0)
	
	-- parent border to overlay
	E.parent_entity(top_bar, dialog_box_overlay)
	E.parent_entity(bottom_bar, dialog_box_overlay)
	E.parent_entity(left_bar, dialog_box_overlay)
	E.parent_entity(right_bar, dialog_box_overlay)
	--[[E.parent_entity(upper_left, dialog_box_overlay)
	E.parent_entity(upper_right, dialog_box_overlay)
	E.parent_entity(lower_right, dialog_box_overlay)
	E.parent_entity(lower_left, dialog_box_overlay)]]

	E.parent_entity(dialog_box_overlay, dialog_box.pivot)
	E.parent_entity(dialog_box_background, dialog_box.pivot)

	--[[local mask_scale_width = width / dialog_box_mask.width
	local mask_scale_height = height / dialog_box_mask.height
	local npc_mask = E.create_clone(dialog_box_npc_mask.sprite)
	E.set_entity_scale(npc_mask, mask_scale_width, mask_scale_height, 1)
	E.parent_entity(npc_mask, dialog_box.pivot)]]

	E.set_entity_alpha(dialog_box.pivot, 1.0)

	add_to_overlay(dialog_box.pivot, active_mouse_x, active_mouse_y, width, height, active_npc, view_distance_far, false, nil)
	--E.print_console("npc_bubble: ", string, "\n")
end

function show_player_bubble(string)
	clear_overlay()

	animation = anim_dialog_box
	anim_units = 8
	time_accumulator = 0
	
	local text_pivot = E.create_pivot()
	local lines_of_text, width, height = get_lines_of_text(string)
	E.set_entity_position(lines_of_text, -width / 2, 0, 0)	
	
	E.parent_entity(lines_of_text, text_pivot)
	
	width = width + 2 * text_padding_horizontal
	height = height + 4 * text_padding_vertical
	
	--add_to_overlay(text_pivot, active_mouse_x, active_mouse_y, width + 2 * text_padding_horizontal, height + 2 * text_padding_vertical, active_npc, view_distance_far, false, nil)
	add_to_overlay(text_pivot, window_margin_horizontal, viewport.h - window_margin_vertical - height, width, height, active_npc, view_distance_far, false, nil)

	local scale_width = width / dialog_box.width
	local scale_height = height / dialog_box.height
	
	local dialog_box_background = E.create_clone(dialog_box_player_background.sprite)
	
	-- set alpha of light background to be slightly transparent, thus lighter and unaccented
	E.set_entity_alpha(dialog_box_background, 0.85)
	
	E.set_entity_scale(dialog_box_background, scale_width, scale_height, 1)

	dialog_box.pivot = E.create_pivot()

-- create overlay pivot to attach our blocks to, we will also attach our box border to this
	dialog_box_overlay = E.create_pivot()
	
	-- create clone of dialog_box_border
	local top_bar = E.create_clone(dialog_box_divider.sprite)
	E.set_entity_position(top_bar, 0, height / 2, 0)
	local bottom_bar = E.create_clone(dialog_box_divider.sprite)
	E.set_entity_position(bottom_bar, 0, -height / 2, 0)
	local left_bar = E.create_clone(dialog_box_bar.sprite)
	E.set_entity_position(left_bar, -width / 2, 0, 0)
	local right_bar = E.create_clone(dialog_box_bar.sprite)
	E.set_entity_position(right_bar, width / 2, 0, 0)
	--[[local upper_left = E.create_clone(dialog_box_corner.sprite)
	local upper_right = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(upper_right, 0, 0, 90)
	local lower_right = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(lower_right, 0, 0, 180)
	local lower_left = E.create_clone(dialog_box_corner.sprite)
	E.set_entity_rotation(lower_left, 0, 0, 270)]]
	
	-- scale border by compute values
	--E.set_entity_scale(border_clone, scale_width, scale_height , 1)
	E.set_entity_scale(top_bar, scale_width, 1, 1)
	E.set_entity_scale(bottom_bar, scale_width, 1, 1)
	E.set_entity_scale(left_bar, 1, scale_height, 1)
	E.set_entity_scale(right_bar, 1, scale_height, 1)
	--[[E.set_entity_scale(upper_left, scale_width, scale_height, 1)
	E.set_entity_scale(upper_right, scale_width, scale_height, 1)
	E.set_entity_scale(lower_right, scale_width, scale_height, 1)
	E.set_entity_scale(lower_left, scale_width, scale_height, 1)]]
	
	-- position border within the overlay
	--E.set_entity_position(border_clone, dialog_box_scaled_width / 2, -dialog_box_scaled_height / 2, 0)
	
	-- parent border to overlay
	E.parent_entity(top_bar, dialog_box_overlay)
	E.parent_entity(bottom_bar, dialog_box_overlay)
	E.parent_entity(left_bar, dialog_box_overlay)
	E.parent_entity(right_bar, dialog_box_overlay)
	--[[E.parent_entity(upper_left, dialog_box_overlay)
	E.parent_entity(upper_right, dialog_box_overlay)
	E.parent_entity(lower_right, dialog_box_overlay)
	E.parent_entity(lower_left, dialog_box_overlay)]]

	E.parent_entity(dialog_box_overlay, dialog_box.pivot)
	E.parent_entity(dialog_box_background, dialog_box.pivot)

	--[[local mask_scale_width = width / dialog_box_mask.width
	local mask_scale_height = height / dialog_box_mask.height
	local player_mask = E.create_clone(dialog_box_npc_mask.sprite)
	E.set_entity_scale(player_mask, mask_scale_width, mask_scale_height, 1)
	E.parent_entity(player_mask, dialog_box.pivot)]]

	E.set_entity_alpha(dialog_box.pivot, 1.0)

	add_to_overlay(dialog_box.pivot, window_margin_horizontal, viewport.h - window_margin_vertical - height, width, height, active_npc, view_distance_far, false, nil)
	--E.print_console("npc_bubble: ", string, "\n")
end

step_through = 0
active_sequence = nil
in_sequence = false
function step_through_sequence(sequence)
	in_sequence = true
	
	if sequence then
		active_sequence = sequence
	else
		sequence = active_sequence
	end
	
	step_through = step_through + 1
	
	local force_npc = false
	local force_player = false
	if sequence.ordering then
		if sequence.ordering[step_through] == 1 then
			force_npc = true
		else
			force_player = true
		end
	end
	
	local mod = math.mod(step_through, 2)
	
	if not force_player and (force_npc or (not sequence.player_starts and mod == 1 or sequence.player_starts and mod == 0)) then
		-- npc bubble
		if sequence[step_through] then
			show_npc_bubble(sequence[step_through])
			--E.print_console("NPC: ", sequence[step_through], "\n")
			--return true
		else
			if sequence.first_talk then
				--E.print_console("made false\n")
				sequence.first_talk = false
			end
			--return false
			in_sequence = false
			repetition_offset = 0
			
			if sequence.knowledge_nugget then
				if debug then
					E.print_console("getting nugget: ", sequence.knowledge_nugget, "\n")
				end
				player_knowledge = math.max(player_knowledge, sequence.knowledge_nugget)
				if debug then
					E.print_console("player_knowledge: ", player_knowledge, "\n")
				end
			end
		end
	else
		-- player bubble
		if sequence[step_through] then
			show_player_bubble(sequence[step_through])
			--E.print_console("Player: ", sequence[step_through], "\n")
			--return true
		else
			if sequence.first_talk then
				--E.print_console("made false\n")
				sequence.first_talk = false
			end
			--return false
			in_sequence = false
			repetition_offset = 0
			
			if sequence.knowledge_nugget then
				if debug then
					E.print_console("getting nugget: ", sequence.knowledge_nugget, "\n")
				end
				player_knowledge = math.max(player_knowledge, sequence.knowledge_nugget)
				if debug then
					E.print_console("player_knowledge: ", player_knowledge, "\n")
				end
			end
		end
	end
	
	control_lock = in_sequence
	return in_sequence
end

stored_sequence_struct = nil
right_painting = false
function start_sequence(sequence_struct)
	if sequence_struct[1] == "START_STEAL_SEQUENCE" then
		right_painting = false
		if painting_to_steal == painting33 then
			right_painting = true
		end
		--E.enable_timer(false)
		--E.nuke()
		--dofile(src_dir .. "game_close.lua")
		dofile(src_dir .. "load_game_close.lua")
		return
	end
	
	if sequence_struct.first_talk then
		stored_sequence_struct = sequence_struct
		sequence_struct = sequence_struct.intro_sequence
	end
	
	if sequence_struct.next then
		if sequence_struct.repeated > sequence_struct.repeat_amount then
			sequence_struct = sequence_struct.next
			start_sequence(sequence_struct)
			return
		end
		sequence_struct.repeated = sequence_struct.repeated + 1
	end
	
	step_through = 0
	step_through_sequence(sequence_struct)
end

--in_dialog = false
dialog_ref = nil
function start_dialog(dialog)
	--in_dialog = true
	
	if dialog then
		dialog_ref = dialog
	else
		dialog = dialog_ref
	end
	
	if dialog.sequences and dialog.sequences.intro and dialog.sequences.intro.first_talk then
		-- begin intro dialog
		start_sequence(dialog.sequences.intro)
	--elseif dialog.is_introduction then
	--	start_sequence(dialog.intro)
	elseif dialog.questions then
		-- show questions
		show_questions(dialog.questions)
	else -- no questions available
		npc_go_idle(active_npc)
    	clear_overlay()
    	in_sequence = false
    	--if dialog.sequences and dialog.sequences.intro then
		--	dialog.sequences.intro.first_talk = true -- reset this so the sequence plays each time
		--end
	end
end

function get_npc_ref(ptr)
	if ptr == Wilhelm_Winckelmann then
		return Wilhelm_Winckelmann
	elseif ptr == Lacy_Lane then
		return Lacy_Lane
	elseif ptr == Bob_Cassady then
		return Bob_Cassady
	elseif ptr == Raquel_Ramirez then
		return Raquel_Ramirez
	elseif ptr == Chin_Li then
		return Chin_Li
	elseif ptr == Claire_dAvignon then
		return Claire_dAvignon
	elseif ptr == Clem_Bergreen then
		return Clem_Bergreen
	elseif ptr == Giorgio_Venturi then
		return Giorgio_Venturi
	elseif ptr == Dillon_White then
		return Dillon_White
	elseif ptr == Martha_Stewart then
		return Martha_Stewart
	elseif ptr == Abigail_Stewart then
		return Abigail_Stewart
	elseif ptr == Shelley_Stevenson then
		return Shelley_Stevenson
	elseif ptr == Samuel_Harris then
		return Samuel_Harris
	elseif ptr == Unknown_Name then
		return Unknown_Name
	end
end

-- animate argument object with the given anim_type
camera_z_vec = { }
camera_z_vec.x, camera_z_vec.y, camera_z_vec.z = 0,0,0
target_pos = { }
target_pos.x, target_pos.y, target_pos.z = 0,0,0
target_vec = { }
target_vec.x, target_vec.y, target_vec.z = 0,0,0
dVec = { }
dVec.x, dVec.y, dVec.z = 0,0,0
dDist = 0
map_target_pos = { }
map_target_pos.x, map_target_pos.y, map_target_pos.z = 0,0,0
map_current_pos = { }
map_current_pos.x, map_current_pos.y, map_current_pos.z = 0,0,0
camera_target_view = { }
camera_target_view.x, camera_target_view.y, camera_target_view.z = 0,0,0
camera_stored_view = { }
camera_stored_view.x, camera_stored_view.y, camera_stored_view.z = 0,0,0
camera_current_view = { }
camera_current_view.x, camera_current_view.y, camera_current_view.z = 0,0,0
npc_dialog_pos_x, npc_dialog_pos_y, npc_dialog_pos_z = 0,0,0
active_npc = nil
active_dialog = nil
active_mouse_x, active_mouse_y = 0, 0
function anim_object(object, anim_type)
	-- only animate if object is defined
	if object then
		-- if there exists dialog boxes open, close them
		clear_overlay()
		
		-- set general global animation properties
		animation = anim_type
		time_accumulator = 0
		
		if animation == anim_go_to_object then -- animate player to painting
			-- lock player control
			control_lock = true
			
			-- hide cursor
			set_cursor(cursor_blank)
			
			-- get camera position and z_vector in world coordinates
			camera_pos.x, camera_pos.y, camera_pos.z = E.get_entity_position(camera)
			camera_z_vec.x, camera_z_vec.y, camera_z_vec.z = E.get_entity_z_vector(camera)
			
			
			target_pos.x, target_pos.y, target_pos.z = E.get_entity_position(object)

			-- get objects local z_vector
			target_vec.x, target_vec.y, target_vec.z = E.get_entity_z_vector(object)
			
			-- get object bounds to formulate a view_bias, the player stops further away from bigger pictures
			local min_x, min_y, min_z, max_x, max_y, max_z = E.get_entity_bound(object)
			local width, height = (max_x - min_x), (max_y - min_y)
			local view_bias = 1
			if width > viewport.w / viewport.h * height then
				view_bias = 1.03 * width
			else
				view_bias = 1.4 * height
			end
			view_bias = math.max(2, view_bias)
			--E.print_console("width,height,widthxheight: ", width, ", ", height, ", ", width * height, "\n")
			
			-- get the distance from the camera to the target
			local d = math.sqrt(math.pow(target_pos.x - camera_pos.x, 2) + math.pow(target_pos.z - camera_pos.z, 2))
			local curviness = anim_path_curviness * d-- the closer the player is to target, the straighter the path will be
			--curviness = 0
			
			-- set anim_units equal to distance and figure out timing_coeffecient to be the fraction of total animation time
			anim_units = d
			timing_coeffecient = 1 / (anim_units * animation_rate)
			
			-- compute control points for bezier curve
			-- cp1: camera position, cp2: somewhere in front of camera, cp3: somewhere in front of target, cp4: target
			-- note: cp3 & cp4 are moved forward by view_bias to stop player infront of painting instead of on it
			cp1.x, cp1.z = camera_pos.x, camera_pos.z
			cp2.x, cp2.z = camera_pos.x - camera_z_vec.x * curviness, camera_pos.z - camera_z_vec.z * curviness
			--cp3.x, cp3.z = target_pos.x + target_vec.x * (curviness + view_bias), target_pos.z + target_vec.z * (curviness + view_bias)
			cp3.x, cp3.z = target_pos.x + target_vec.x * (view_bias + curviness), target_pos.z + target_vec.z * (view_bias + curviness)
			--cp3.x, cp3.z = target_pos.x + target_vec.x * view_bias * curviness, target_pos.z + target_vec.z * view_bias * curviness
			cp4.x, cp4.z = target_pos.x + target_vec.x * view_bias, target_pos.z + target_vec.z * view_bias
			--cp3.x, cp3.z = target_pos.x + target_vec.x * curviness, target_pos.z + target_vec.z * curviness
			--cp4.x, cp4.z = target_pos.x + target_vec.x, target_pos.z + target_vec.z
			
			generate_bezier(cp1, cp2, cp3, cp4)
			
			-- set last_pos variable used in animation to compute new viewing direction
			last_pos.x, last_pos.y, last_pos.z = E.get_entity_position(player)
			
			local final_pos_x, final_pos_z = get_bezier_pos(1.0)
			target_vec.x, target_vec.y, target_vec.z = normalize_3d(target_pos.x - final_pos_x, target_pos.y - camera_pos.y, target_pos.z - final_pos_z)
			
			local view_z, view_y = normalize_2d(-1, -camera_z_vec.y)
			local tar_z, tar_y = normalize_2d(-1 * math.abs(target_vec.x + target_vec.z), target_vec.y)
			dTheta_x = get_theta_2d(view_z, view_y, tar_z, tar_y) / anim_units
			
			--E.print_console("cp1: (", cp1.x, ",", cp1.z, ")\ncp2: (", cp2.x, ",", cp2.z, ")\ncp3: (", cp3.x, ",", cp3.z, ")\ncp4: (", cp4.x, ",", cp4.z, ")\n")
			-- play foot step sound
		    --E.play_sound(walk_sound)
		    E.loop_sound(walk_sound)
		    
		    hit_wall = false
		elseif anim_type == anim_dialog_box then -- animated dialog with NPC
			-- get coordinates of cursor and store it as our top left coordinate for our response box
			--dialog_box_pos.x, dialog_box_pos.y, dialog_box_pos.z = E.get_entity_position(cursor)
			npc_dialog_pos_x, npc_dialog_pos_y, npc_dialog_pos_z = E.get_entity_position(picked_object)
			active_npc = get_npc_ref(picked_object)
			active_mouse_x, active_mouse_y = mouse.x, mouse.y
			
			npc_start_talking(active_npc)
			
			if dialog[active_npc].introduction and dialog[active_npc].introduction.intro.first_talk then
				active_dialog = dialog[active_npc].introduction.intro
				start_sequence(active_dialog)
			else
				active_dialog = dialog[active_npc][player_knowledge]
				start_dialog(active_dialog)
			end
	elseif anim_type == anim_opening_sequence then -- animated dialog with NPC
			-- get coordinates of cursor and store it as our top left coordinate for our response box
			--dialog_box_pos.x, dialog_box_pos.y, dialog_box_pos.z = E.get_entity_position(cursor)
			npc_dialog_pos_x, npc_dialog_pos_y, npc_dialog_pos_z = E.get_entity_position(picked_object)
			active_npc = object
			active_mouse_x, active_mouse_y = 300, 400
			
			npc_start_talking(active_npc)
			
			active_dialog = dialog[active_npc][know_start_dialog]
			start_dialog(active_dialog)
		elseif anim_type == anim_open_map then
			E.set_entity_flags(map, E.entity_flag_hidden, false)
			set_cursor(cursor_blank)
			--E.set_entity_position(map, 0, 1.5, -1)
			control_lock_partial = true
			
			anim_units = 12
			current_anim_unit = 0
			--timing_coeffecient = 1 / (anim_units * animation_rate)
			
			--set_cursor(cursor_blank)
			
			map_current_pos.x, map_current_pos.y, map_current_pos.z = E.get_entity_position(map)
			camera_stored_view.x, camera_stored_view.y, camera_stored_view.z = E.get_entity_z_vector(camera)
			camera_stored_view.x, camera_stored_view.y, camera_stored_view.z = -camera_stored_view.x, -camera_stored_view.y, -camera_stored_view.z
			--E.print_console("x :", camera_stored_view.x, ", ", camera_stored_view.y, ", ", camera_stored_view.z, "\n")
			
			map_target_pos.x, map_target_pos.y, map_target_pos.z = 0, 1.5, -1
			camera_target_view.x, camera_target_view.y, camera_target_view.z = normalize_3d(camera_stored_view.x, -1, camera_stored_view.z)
			
			-- figure out a dTheta_x that will adjust camera on its x-axis gradually so it faces target upon completion of animation
			--dTheta_x = -math.acos(dot_3d(camera_target_view.x, camera_target_view.y, camera_target_view.z, camera_stored_view.x, camera_stored_view.y, camera_stored_view.z)) / anim_units
			--dTheta_x = get_theta_3d(camera_target_view.x, camera_target_view.y, camera_target_view.z, camera_stored_view.x, camera_stored_view.y, camera_stored_view.z) / anim_units
			dTheta_x = get_theta_3d(camera_stored_view.x, camera_stored_view.y, camera_stored_view.z, camera_target_view.x, camera_target_view.y, camera_target_view.z) / anim_units
			
			--E.print_console("dtheat:", dTheta_x)
			
			-- figure out a dDist and dVec that will adjust the map along a vector dVec for dDist each anim loop
			dVec.x, dVec.y, dVec.z = normalize_3d((map_target_pos.x - map_current_pos.x), (map_target_pos.y - map_current_pos.y), (map_target_pos.z - map_current_pos.z))
			dDist = get_distance_3d(map_target_pos.x, map_target_pos.y, map_target_pos.z, map_current_pos.x, map_current_pos.y, map_current_pos.z) / anim_units
			
			--E.print_console("map curr: ", map_current_pos.x, ", ", map_current_pos.y, ", ", map_current_pos.z, "\n")
			--E.print_console("dDist: ", dDist, "\n")
			--E.print_console("dVec: ", dVec.x, ", ", dVec.y, ", ", dVec.z, "\n")
		elseif anim_type == anim_close_map then
			--E.set_entity_position(map, 0, 1.5, -1)
			control_lock_partial = true
			
			anim_units = current_anim_unit
			current_anim_unit = 0
			
			--set_cursor(cursor_blank)			
			map_current_pos.x, map_current_pos.y, map_current_pos.z = E.get_entity_position(map)
			camera_current_view.x, camera_current_view.y, camera_current_view.z = E.get_entity_z_vector(camera)
			camera_current_view.x, camera_current_view.y, camera_current_view.z = -camera_current_view.x, -camera_current_view.y, -camera_current_view.z
			
			map_target_pos.x, map_target_pos.y, map_target_pos.z = 0, 0, 1
			
			-- figure out a dTheta_x that will adjust camera on its x-axis gradually so it faces target upon completion of animation
			--dTheta_x = math.acos(dot_3d(camera_stored_view.x, camera_stored_view.y, camera_stored_view.z, camera_current_view.x, camera_current_view.y, camera_current_view.z)) / anim_units
			--dTheta_x = get_theta_3d(camera_stored_view.x, camera_stored_view.y, camera_stored_view.z, camera_current_view.x, camera_current_view.y, camera_current_view.z) / anim_units
			dTheta_x = get_theta_3d(camera_current_view.x, camera_current_view.y, camera_current_view.z, camera_stored_view.x, camera_stored_view.y, camera_stored_view.z) / anim_units
			
			-- figure out a dDist and dVec that will adjust the map along a vector dVec for dDist each anim loop
			dVec.x, dVec.y, dVec.z = normalize_3d((map_target_pos.x - map_current_pos.x), (map_target_pos.y - map_current_pos.y), (map_target_pos.z - map_current_pos.z))
			dDist = get_distance_3d(map_target_pos.x, map_target_pos.y, map_target_pos.z, map_current_pos.x, map_current_pos.y, map_current_pos.z) / anim_units
			
			--E.print_console("map curr: ", map_current_pos.x, ", ", map_current_pos.y, ", ", map_current_pos.z, "\n")
			--E.print_console("dDist: ", dDist, "\n")
			--E.print_console("dVec: ", dVec.x, ", ", dVec.y, ", ", dVec.z, "\n")
		end
		
		picking = false
		picked_object = nil
	end
end

-- event handling functions ---------------------------------------------------
function my_do_contact()
	if cursor_over_question then
		return
	end
	
	if pickable_obj then
		--E.print_console("we have a pickable object\n")
		local pos_x, tmp, pos_z = E.get_entity_position(pickable_obj)
		local d = get_distance_2d(pos_x, pos_z, player_pos.x, player_pos.z)
	
		local category = E.get_entity_geom_attr(pickable_obj, E.geom_attr_category)
		local d_max = view_distance_far
		if category == category_person then
			d_max = talk_distance
		end
		
		--E.print_console("d, d_max, pickable_obj_d, non_pickable_obj_d, ", d, ", ", d_max, ", ", pickable_obj_d, ", ", non_pickable_obj_d, "\n")
		if d < d_max and pickable_obj_d < non_pickable_obj_d then
			hover = true
			hovered_object = pickable_obj
		else
			hover = false
			hovered_object = nil
		end
	else
		hover = false
		hovered_object = nil
	end
	
	pickable_obj = nil
	pickable_obj_d = huge
	non_pickable_obj_d = huge
end

-- process collision by cursor_ray and pickable objects to update cursor
hovered_object = nil
--in_wall = false
pickable_obj_d = huge
pickable_obj = nil
non_pickable_obj_d = huge
hit_wall = false
function do_contact(entityA, entityB, px, py, pz, nx, ny, nz, d)
	if entityB ~= cursor_ray then
		-- if we are animating, stop, we've hit a wall
		if animation ~= nil then
			hit_wall = true
			--time_accumulator = anim_units * animation_rate
			--animation = nil
			return
		--else
		--	in_wall = true
			--E.print_console("here\n")
			--E.set_entity_body_type(entityB, true)
		end
	end
	
	-- if we are currently moving player, ignore the cursor collision
	if control_lock then
		return
	end
	
	-- TODO: as of now only cursor_ray callsback, if we have animated things doing this later we'd need:
	-- if entityB == cursor_ray
	-- if we aren't already hovering make appropriate changes to state
	-- only change cursor if we aren't rotating, and we're not too close to a world object
	if not rotate then
		local category = E.get_entity_geom_attr(entityA, E.geom_attr_category)
		if category == category_walls then
			non_pickable_obj_d = math.min(non_pickable_obj_d, d)
		elseif d < pickable_obj_d then
			pickable_obj = entityA
			pickable_obj_d = d
		end
	end
end

switch_to_main_game = true
function game_opening_loop(dt)
	if time_accumulator < 2 then
        E.set_entity_alpha(open1, 1 - (2 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 4 then
        E.set_entity_flags(open1_text1, E.entity_flag_hidden, false)
        E.set_entity_flags(open1_text2, E.entity_flag_hidden, false)
		E.set_entity_alpha(open1_text1, 1 - (4 - time_accumulator) / 2)
        E.set_entity_alpha(open1_text2, 1 - (4 - time_accumulator) / 2)
        E.set_entity_alpha(press_enter, 1 - (4 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 5 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 7 then
		E.set_entity_alpha(open1, (7 - time_accumulator) / 2)
		E.set_entity_alpha(open1_text1, (7 - time_accumulator) / 2)
		E.set_entity_alpha(open1_text2, (7 - time_accumulator) / 2)
		E.set_entity_alpha(press_enter, (7 - time_accumulator) / 2)
		E.set_entity_alpha(open2, 1 - (7 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 9 then
	        E.set_entity_flags(open1, E.entity_flag_hidden, true)
                E.set_entity_flags(open1_text1, E.entity_flag_hidden, true)
                E.set_entity_flags(open1_text2, E.entity_flag_hidden, true)
	        E.set_entity_flags(open2_text1, E.entity_flag_hidden, false)
                E.set_entity_flags(open2_text2, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open2_text1, 1 - (9 - time_accumulator) / 2)
	        E.set_entity_alpha(open2_text2, 1 - (9 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, 1 - (9 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 10 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 12 then
		E.set_entity_alpha(open2, (12 - time_accumulator) / 2)
		E.set_entity_alpha(open2_text1, (12 - time_accumulator) / 2)
		E.set_entity_alpha(open2_text2, (12 - time_accumulator) / 2)
		E.set_entity_alpha(press_enter, (12 - time_accumulator) / 2)
		E.set_entity_alpha(open3, 1 - (12 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 14 then
	        E.set_entity_flags(open2, E.entity_flag_hidden, true)
	        E.set_entity_flags(open2_text1, E.entity_flag_hidden, true)
                E.set_entity_flags(open2_text2, E.entity_flag_hidden, true)
	        E.set_entity_flags(open3_text1, E.entity_flag_hidden, false)
                E.set_entity_flags(open3_text2, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open3_text1, 1 - (14 - time_accumulator) / 2)
	        E.set_entity_alpha(open3_text2, 1 - (14 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, 1 - (14 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 15 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 17 then
		E.set_entity_alpha(open3, (17 - time_accumulator) / 2)
		E.set_entity_alpha(open3_text1, (17 - time_accumulator) / 2)
		E.set_entity_alpha(open3_text2, (17 - time_accumulator) / 2)
		E.set_entity_alpha(press_enter, (17 - time_accumulator) / 2)
		E.set_entity_alpha(open4, 1 - (17 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 19 then
	        E.set_entity_flags(open3, E.entity_flag_hidden, true)
	        E.set_entity_flags(open3_text1, E.entity_flag_hidden, true)
                E.set_entity_flags(open3_text2, E.entity_flag_hidden, true)
	        E.set_entity_flags(open4_text, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open4_text, 1 - (19 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, 1 - (19 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 20 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 22 then
		E.set_entity_alpha(open4, (22 - time_accumulator) / 2)
		E.set_entity_alpha(open4_text, (22 - time_accumulator) / 2)
		E.set_entity_alpha(press_enter, (22 - time_accumulator) / 2)
		E.set_entity_alpha(open5, 1 - (22 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 24 then
	        E.set_entity_flags(open4, E.entity_flag_hidden, true)
	        E.set_entity_flags(open4_text, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text1, E.entity_flag_hidden, false)
	        E.set_entity_flags(open5_text2, E.entity_flag_hidden, false)
	        E.set_entity_flags(open5_text3, E.entity_flag_hidden, false)
	        E.set_entity_flags(open5_text4, E.entity_flag_hidden, false)
	        E.set_entity_flags(open5_text5, E.entity_flag_hidden, false)
	        E.set_entity_flags(open5_text6, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open5_text1, 1 - (24 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text2, 1 - (24 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text3, 1 - (24 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text4, 1 - (24 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text5, 1 - (24 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text6, 1 - (24 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, 1 - (24 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 25 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 27 then
		E.set_entity_alpha(open5, (27 - time_accumulator) / 2)
		E.set_entity_alpha(open5_text1, (27 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text2, (27 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text3, (27 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text4, (27 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text5, (27 - time_accumulator) / 2)
	        E.set_entity_alpha(open5_text6, (27 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, (27 - time_accumulator) / 2)
		E.set_entity_alpha(open6, 1 - (27 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 29 then
	        E.set_entity_flags(open5, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text1, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text2, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text3, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text4, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text5, E.entity_flag_hidden, true)
	        E.set_entity_flags(open5_text6, E.entity_flag_hidden, true)
	        E.set_entity_flags(open6_text1, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open6_text1, 1 - (29 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 31 then
	        E.set_entity_flags(open6_text2, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open6_text2, 1 - (31 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, 1 - (31 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 32 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 34 then
		E.set_entity_alpha(open6, (34 - time_accumulator) / 2)
		E.set_entity_alpha(open6_text1, (34 - time_accumulator) / 2)
		--E.set_entity_alpha(open6_text2, (34 - time_accumulator) / 2)
		E.set_entity_alpha(press_enter, (34 - time_accumulator) / 2)
		--E.set_entity_alpha(objective, 1 - (34 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 36 then
	        E.set_entity_flags(open6_text2, E.entity_flag_hidden, false)
	        E.set_entity_alpha(open6_text2, 1.0)
            time_accumulator = time_accumulator + dt
    elseif time_accumulator < 37 then
    	destroy_game_opening()
    	E.set_entity_flags(load_screen, E.entity_flag_hidden, false)
    	time_accumulator = time_accumulator + dt
    elseif switch_to_main_game then
   		--main_game_load_screen()
		--destroy_game_opening()
        --E.nuke()
        init_main_game()
        switch_to_main_game = false
    --dofile(src_dir .. "game.lua")
	end
end

function main_menu_loop(dt)
	if time_accumulator < 3 then
		time_accumulator = time_accumulator + dt
		return false
	elseif time_accumulator < 5 then
		-- begin fade animation
		E.set_entity_alpha(electro_splash, 1 - (5 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 6 then
		time_accumulator = time_accumulator + dt
		return false
	elseif time_accumulator < 8 then
		-- begin fade animation
		E.set_entity_alpha(electro_splash, (8 - time_accumulator) / 2)
		--E.play_sound(menu_music)
		if not menu_music_playing then
			E.loop_sound(menu_music)
			menu_music_playing = true
		end
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 9 then
		time_accumulator = time_accumulator + dt
		return false
	elseif time_accumulator < 11 then
		-- bring next image in with fade
		E.set_entity_alpha(navi_splash, 1 - (11 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 12 then
		time_accumulator = time_accumulator + dt
		return false
	elseif time_accumulator < 14 then
		-- fade out animation
		E.set_entity_alpha(navi_splash, (14 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 15 then
		time_accumulator = time_accumulator + dt
		return false
	elseif time_accumulator < 17 then
		-- bring next image in with fade
		E.set_entity_alpha(menu_img, 1 - (17 - time_accumulator) / 2)
		E.set_entity_alpha(title, 1 - (17 - time_accumulator) / 2)
		E.set_entity_alpha(new_game_txt, 1 - (17 - time_accumulator) / 2)
		E.set_entity_alpha(load_game_txt, 1 - (17 - time_accumulator) / 2)
		E.set_entity_alpha(exit_game_txt, 1 - (17 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 18 then
		time_accumulator = time_accumulator + dt
	else
		--E.set_entity_flags(splash, E.entity_flag_hidden, true)
		E.delete_entity(electro_splash)
		E.delete_entity(navi_splash)
		E.set_entity_flags(menu_img, E.entity_flag_hidden, false)
		--E.enable_timer(false) -- unless we do time-related affect later
		highlight_item(selected_index)
		show_load_screen(show_load_menu)
		highlight_load_menu(load_index)
		return true
	end
end

load_time_accumulator = 0
load_time = 15
load_complete = false
function main_game_loop(dt)
	if load_time_accumulator <= load_time then
		load_time_accumulator = load_time_accumulator + dt
	elseif not load_complete then
		--load_time_accumulator = load_time_accumulator + dt
		dofile(src_dir .. "place_objects.lua") -- done after delay to hopefully load them in video cache
		E.delete_entity(load_screen)
		if start_opening_dialog then
			active_mouse_x, active_mouse_y = 475, 550
			anim_object(Samuel_Harris, anim_opening_sequence)
		end
		load_complete = true
	elseif load_time_accumulator <= load_time + 5 then
		load_time_accumulator = load_time_accumulator + dt
		if intro_music then
			--E.print_console("intro: ", 0.8 - (load_time_accumulator - load_time) / 6.25, "\n")
			E.set_sound_amplitude(intro_music, 0.8 - (load_time_accumulator - load_time) / 6.25)
		elseif menu_music then
			--E.print_console("menu_music: ", 1.0 - (load_time_accumulator - load_time) / 5, "\n")
			E.set_sound_amplitude(menu_music,  1.0 - (load_time_accumulator - load_time) / 5)
		end
	else
		if intro_music then
			E.free_sound(intro_music)
		elseif menu_music then
			E.free_sound(menu_music)
		end
	end
	
	-- if we are currently allowing player control, update according to user input
	if not control_lock and not control_lock_partial then
		-- do any pending animations that don't require exclusive control
		if animation == anim_dialog_box then
			time_accumulator = time_accumulator + dt
			local total_time = anim_units * animation_rate
			if time_accumulator >= total_time then
				animation = nil
				E.set_entity_alpha(dialog_box.pivot, 1.0)
				return
			end
			
			E.set_entity_alpha(dialog_box.pivot, time_accumulator / total_time)
			-- reset all variables specific to this animation TODO: triple check this!
			-- think about deleting saying_box_overlay etc. entities when player moves away
			--saying_box_scale.width, saying_box_scale.height = 0,0
			--saying_count, max_line_width = 0, 0
			--saying_box_overlay_count = 0
		end
		
		-- if hover_contact was not made true by do_contact then we are no longer above pickable object
		-- it may seem that do_timer could be called multiple times between do_contact calls.  I thought so too
		-- I wrote code to relax this rule, but it turned out to work just fine this way!
--	    if not hover_contact and hover and not rotate then
--			hover = false
--			hovered_object = nil
			--closest_object = floor-
--		end	
--		hover_contact = false
		--closest_d = huge
		
		-- TODO add sin curve "walk" motion for camera?
		-- add any rotation that occured to rotation state
		player_view.x, player_view.y = player_view.x + rot.y * dt * mouse_sensitivity, player_view.y + rot.x * dt * mouse_sensitivity

		-- update player with new rotation value
		E.set_entity_rotation(player, 0, player_view.y, 0)
		
		-- if user is applying a force, then update position state, otherwise enforce our previous position
		-- this prevents movement when user is not controlling player, i.e. ODE is making us bounce away from wall
		if player_dir.x ~= 0 or player_dir.y ~= 0 or player_dir.z ~= 0 then
			E.move_entity(player, player_dir.x * dt * pace, player_dir.y * dt * pace, player_dir.z * dt * pace)
			player_pos.x, player_pos.y, player_pos.z = E.get_entity_position(player) -- use get function so that physics can stop player
			player_pos.y = 3.0
		else
			E.set_entity_position(player, player_pos.x, player_pos.y, player_pos.z)
		end
		
		--[[local cur_dir_x, cur_dir_y, cur_dir_z = E.get_entity_z_vector(player)
		cur_dir_x, cur_dir_y, cur_dir_z = -cur_dir_x, -cur_dir_y, -cur_dir_z
		player_pos.x, player_pos.y, player_pos.z = player_pos.x + cur_dir_x * player_dir.x, player_pos.y + cur_dir_y * player_dir.y, player_pos.z + cur_dir_z * player_dir.z
		
		E.set_entity_position(player, player_pos.x, player_pos.y, player_pos.z)
		]]
		-- update camera position and rotation
		E.set_entity_rotation(camera, player_view.x, player_view.y, player_view.z)
		--E.set_entity_rotation(camera, 0, player_view.y, 0)
	    E.set_entity_position(camera, player_pos.x, player_pos.y + 2.5, player_pos.z)
	    
	    -- draw overlay
	    update_overlay(dt)
	
		-- reset rot.x, rot.y so they can be reprocessed in do_point
		rot.x, rot.y = 0, 0
		
		my_do_contact() -- update pickable object info
	else -- do animation that requires exclusive control of player
		time_accumulator = time_accumulator + dt -- update time
		if animation == anim_go_to_object then
			if hit_wall then
				animation = nil
				control_lock = false
				E.stop_sound(walk_sound)
				return
			end
			-- we have completed our animation, unlock player controls force last bit of animation
			local last_time = false
			if time_accumulator >= anim_units * animation_rate then
				dt = anim_units * animation_rate - (time_accumulator - dt)
				last_time = true
			end
			
			local dAnimUnits = dt / animation_rate
			-- multiply total time in seconds with our coeffecient to get our elapsed time between 0 and 1
			local elapsed_time = time_accumulator * timing_coeffecient
			
			-- get x,z position according to bezier curve
			local pos_x, pos_z = get_bezier_pos(elapsed_time)
			
			-- move player and camera to new position
			E.set_entity_position(player, pos_x, player_pos.y, pos_z)
			E.set_entity_position(camera, pos_x, 5.5, pos_z)
			
			-- get new player position with get incase physics has affected it (stuck on something)
			player_pos.x, player_pos.y, player_pos.z = E.get_entity_position(player)
			
			-- get normalize_3dd vector from the difference in last_pos and current pos
			--target_vec.x, target_vec.y, target_vec.z = player_pos.x - last_pos.x, player_pos.y - last_pos.y, player_pos.z - last_pos.z
			--target_vec.x, target_vec.y, target_vec.z = normalize_3d(target_vec.x, target_vec.y, target_vec.z)
			target_vec.x, target_vec.z = player_pos.x - last_pos.x, player_pos.z - last_pos.z
			target_vec.x, target_vec.z = normalize_2d(target_vec.x, target_vec.z)
			
			-- compute rotation of new vector
			--local dir = dot_3d(target_vec.x, target_vec.y, target_vec.z, -1, 0, 0)
			local dir = dot_2d(target_vec.x, target_vec.z, -1, 0)
			--local dTheta_y = sign(dir) * math.acos(dot_3d(0, 0, -1, target_vec.x, target_vec.y, target_vec.z))
			player_view.y = sign(dir) * get_theta_2d(0, -1, target_vec.x, target_vec.z)
			
			-- set player rotation state in degrees
			--player_view.y = math.deg(dTheta_y)
			--player_view.x = player_view.x + math.deg(dTheta_x * dt)
			player_view.x = player_view.x + dTheta_x * dAnimUnits
			
			-- set player and camera to new values
			E.set_entity_rotation(player, 0, player_view.y, 0)
			E.set_entity_rotation(camera, player_view.x, player_view.y, 0)
			
			if last_time then
				animation = nil
				control_lock = false
				E.stop_sound(walk_sound)
				return
				-- TODO: set cursor to proper type
			end
			
			-- store current pos as last_pos for next pass
			--last_pos.x, last_pos.y, last_pos.z = player_pos.x, player_pos.y, player_pos.z
			last_pos.x, last_pos.z = player_pos.x, player_pos.z
		elseif animation == anim_open_map then
			if time_accumulator >= anim_units * animation_rate then
				animation = anim_paused
				return
			end
			--local elapsed_time = time_accumulator * timing_coeffecient
			--local elapsedAnimUnits = time_accumulator / animation_rate
			local dAnimUnits = dt / animation_rate
			--local dAnimUnits = dt * timing_coeffecient
			current_anim_unit = current_anim_unit + dAnimUnits
			
			local map_pos_x, map_pos_y, map_pos_z = E.get_entity_position(map)
			E.set_entity_position(map, map_pos_x + dVec.x * dDist * dAnimUnits, map_pos_y + dVec.y * dDist * dAnimUnits, map_pos_z + dVec.z * dDist * dAnimUnits)
			--E.move_entity(map, dVec.x * dDist * dAnimUnits, dVec.y * dDist * dAnimUnits, dVec.z * dDist * dAnimUnits)
			--E.turn_entity(camera, math.deg(dTheta_x * dAnimUnits), 0, 0)
			E.turn_entity(camera, dTheta_x * dAnimUnits, 0, 0)
			--local cam_view_x, cam_view_y, cam_view_z = E.get_entity_z_vector(camera)
			--cam_view_x, cam_view_y, cam_view_z = -cam_view_x, -cam_view_y, -cam_view_z
			--player_view.x = player_view.x + math.deg(dTheta_x * dAnimUnits)
			--E.set_entity_rotation(camera, player_view.x, player_view.y, 0)
			
			--E.print_console("here: ", anim_units, ", ", time_accumulator, "\n")
		elseif animation == anim_close_map then
			if time_accumulator >= anim_units * animation_rate then
				--[[if once then
					E.print_console(dTheta_x)
					control_lock_partial = false
					animation = nil
					once = false
				end]]
				animation = nil
				control_lock_partial = false
				E.set_entity_flags(map, E.entity_flag_hidden, true)
				set_cursor(cursor_default)
				return
			end
			--local elapsed_time = time_accumulator * timing_coeffecient
			--local elapsedAnimUnits = time_accumulator / animation_rate
			local dAnimUnits = dt / animation_rate
			--local dAnimUnits = dt * timing_coeffecient
			current_anim_unit = current_anim_unit + dAnimUnits
			
			local map_pos_x, map_pos_y, map_pos_z = E.get_entity_position(map)
			E.set_entity_position(map, map_pos_x + dVec.x * dDist * dAnimUnits, map_pos_y + dVec.y * dDist * dAnimUnits, map_pos_z + dVec.z * dDist * dAnimUnits)
			--E.move_entity(map, dVec.x * dDist * dAnimUnits, dVec.y * dDist * dAnimUnits, dVec.z * dDist * dAnimUnits)
			--E.turn_entity(camera, math.deg(dTheta_x * dAnimUnits), 0, 0)
			E.turn_entity(camera, dTheta_x * dAnimUnits, 0, 0)
			--local cam_view_x, cam_view_y, cam_view_z = E.get_entity_z_vector(camera)
			--cam_view_x, cam_view_y, cam_view_z = -cam_view_x, -cam_view_y, -cam_view_z
			--player_view.x = player_view.x + math.deg(dTheta_x * dAnimUnits)
			--E.set_entity_rotation(camera, player_view.x, player_view.y, 0)
			
			--E.print_console("here: ", anim_units, ", ", time_accumulator, "\n")
		end
	end
end

function game_closing_loop(dt)
	if time_accumulator < 3 then
	        E.set_entity_flags(close_text, E.entity_flag_hidden, false)
	        E.set_entity_alpha(close_text, 1 - (3 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, 1 - (3 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 4 then
                -- press enter
                --time_accumulator = time_accumulator + dt
        elseif time_accumulator < 5 then
                E.set_entity_alpha(close_text, (5 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, (5 - time_accumulator) / 2)
                E.set_entity_alpha(close1, 1 - (5 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 7 then
	        E.set_entity_flags(close_text, E.entity_flag_hidden, true)
	        E.set_entity_flags(close1_text, E.entity_flag_hidden, false)
		E.set_entity_alpha(close1_text, 1 - (7 - time_accumulator) / 2)
                E.set_entity_alpha(press_enter, 1 - (7 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 8 then
                -- press enter
                --time_accumulator = time_accumulator + dt
        elseif time_accumulator < 10 then
                E.set_entity_alpha(close1, (10 - time_accumulator) / 2)
                E.set_entity_alpha(close1_text, (10 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, (10 - time_accumulator) / 2)
                E.set_entity_alpha(close2, 1 - (10 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 12 then
	        E.set_entity_flags(close1, E.entity_flag_hidden, true)
	        E.set_entity_flags(close1_text, E.entity_flag_hidden, true)
	        E.set_entity_flags(close2_text, E.entity_flag_hidden, false)
		E.set_entity_alpha(close2_text, 1 - (12 - time_accumulator) / 2)
                E.set_entity_alpha(press_enter, 1 - (12 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 13 then
                -- press enter
                E.stop_sound(close_cricket)
                --E.play_sound(close_music)
                if not close_music_playing then
                	E.loop_sound(close_music)
                	close_music_playing = true
                end
                --time_accumulator = time_accumulator + dt
        elseif time_accumulator < 15 then
                E.set_entity_alpha(close2, (15 - time_accumulator) / 2)
                E.set_entity_alpha(close2_text, (15 - time_accumulator) / 2)
	        E.set_entity_alpha(press_enter, (15 - time_accumulator) / 2)
                if right_painting then
                        E.set_entity_alpha(closeWIN, 1 - (15 - time_accumulator) / 2)
                else
                        E.set_entity_alpha(closeLOSE, 1 - (15 - time_accumulator) / 2)
                end
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 17 then
	        E.set_entity_flags(close2, E.entity_flag_hidden, true)
	        E.set_entity_flags(close2_text, E.entity_flag_hidden, true)
                if right_painting then
	                E.set_entity_flags(closeWIN_text1, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeWIN_text2, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeWIN_text3, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeWIN_text4, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeWIN_text5, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeWIN_text6, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeWIN_text7, E.entity_flag_hidden, false)
		        E.set_entity_alpha(closeWIN_text1, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text2, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text3, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text4, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text5, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text6, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text7, 1 - (17 - time_accumulator) / 2)
                else
                        E.set_entity_flags(closeLOSE_text1, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeLOSE_text2, E.entity_flag_hidden, false)
	                E.set_entity_flags(closeLOSE_text3, E.entity_flag_hidden, false)
		        E.set_entity_alpha(closeLOSE_text1, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeLOSE_text2, 1 - (17 - time_accumulator) / 2)
		        E.set_entity_alpha(closeLOSE_text3, 1 - (17 - time_accumulator) / 2)
                end
                E.set_entity_alpha(press_enter, 1 - (17 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 18 then
                -- press enter
                --time_accumulator = time_accumulator + dt
        elseif time_accumulator < 20 then
                if right_painting then
                        E.set_entity_alpha(closeWIN, (20 - time_accumulator) / 2)
                        E.set_entity_alpha(closeWIN_text1, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text2, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text3, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text4, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text5, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text6, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeWIN_text7, (20 - time_accumulator) / 2)
                else
                        E.set_entity_alpha(closeLOSE, (20 - time_accumulator) / 2)
                        E.set_entity_alpha(closeLOSE_text1, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeLOSE_text2, (20 - time_accumulator) / 2)
		        E.set_entity_alpha(closeLOSE_text3, (20 - time_accumulator) / 2)
                end
                E.set_entity_alpha(press_enter, (20 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 22 then
	        E.set_entity_flags(closeWIN, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text1, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text2, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text3, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text4, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text5, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text6, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeWIN_text7, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeLOSE, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeLOSE_text1, E.entity_flag_hidden, true)
                E.set_entity_flags(closeLOSE_text2, E.entity_flag_hidden, true)
	        E.set_entity_flags(closeLOSE_text3, E.entity_flag_hidden, true)
	        E.set_entity_flags(the_end, E.entity_flag_hidden, false)
                E.set_entity_flags(game_made, E.entity_flag_hidden, false)
                E.set_entity_flags(ben_dombek, E.entity_flag_hidden, false)
                E.set_entity_flags(jason_gorski, E.entity_flag_hidden, false)
                E.set_entity_flags(jon_kinkley, E.entity_flag_hidden, false)
		E.set_entity_alpha(the_end, 1 - (22 - time_accumulator) / 2)
		E.set_entity_alpha(game_made, 1 - (22 - time_accumulator) / 2)
		E.set_entity_alpha(ben_dombek, 1 - (22 - time_accumulator) / 2)
		E.set_entity_alpha(jason_gorski, 1 - (22 - time_accumulator) / 2)
		E.set_entity_alpha(jon_kinkley, 1 - (22 - time_accumulator) / 2)
                E.set_entity_alpha(press_enter, 1 - (22 - time_accumulator) / 2)
		time_accumulator = time_accumulator + dt
	elseif time_accumulator < 23 then
                -- press enter
                --time_accumulator = time_accumulator + dt
	elseif time_accumulator < 24 then
	        E.set_entity_flags(the_end, E.entity_flag_hidden, false)
	        E.set_entity_alpha(the_end, 1.0)
	        destroy_game_closing()
	        os.exit()
	end
end

time_accumulator = 0
-- process player movement/orientation in a time dependant fashion
function do_timer(dt)

	if debug then
		debug_time = debug_time + dt
		if debug_time > 100 then
			debug_time = 0
		end
	end
	
	if game_state == main_menu then
		main_menu_loop(dt)
	elseif game_state == game_opening then
		game_opening_loop(dt)
	elseif game_state == main_game then
		main_game_loop(dt)
	elseif game_state == game_closing then
		game_closing_loop(dt)
	end
	
end

function reset_dialog()
	clear_overlay()
	dialog_ref = nil
	stored_sequence_struct = nil
	right_painting = false
	step_through = 0
	active_sequence = nil
	in_sequence = false
	
	npc_go_idle(active_npc)
	npc_dialog_pos_x, npc_dialog_pos_y, npc_dialog_pos_z = 0,0,0
	active_npc = nil
	active_dialog = nil
	
	picking = false
	picked_object = nil
	active_object = nil
	rotate = false
	
	control_lock = false
	control_lock_partial = false
end

-- process user keyboard input
function game_opening_do_keyboard(key, state)
	if state and key == E.key_return then
        if time_accumulator < 5 then
                time_accumulator = time_accumulator + 1
        elseif time_accumulator < 10 then
                time_accumulator = time_accumulator + 1
        elseif time_accumulator < 15 then
                time_accumulator = time_accumulator + 1
        elseif time_accumulator < 20 then
                time_accumulator = time_accumulator + 1
        elseif time_accumulator < 25 then
                time_accumulator = time_accumulator + 1
        elseif time_accumulator < 32 then
                time_accumulator = time_accumulator + 1
        end
        E.play_sound(menu_click)
    end
end

function main_menu_do_keyboard(key, state)
   if (time_accumulator >= 17)then
      if show_load_menu then
         if state and key == E.key_up then
            load_index = load_menu_move(-1)
            highlight_load_menu(load_index)
         end
         if state and key == E.key_down then
            load_index = load_menu_move(1)
            highlight_load_menu(load_index)
         end
	 if state and key == E.key_return then
	    E.play_sound(menu_click)
	    if load_index == 10 then
               -- go back to main menu
               show_load_menu = false
               show_load_screen(show_load_menu)
            else
            
               -- load a saved game
               	E.set_entity_flags(load_screen, E.entity_flag_hidden, false)
				load_game_num = load_index
				destroy_main_menu()
		       --dofile(src_dir .. "load_game_opening.lua")
		       time_accumulator = 0
		       game_state = nil
               	--E.nuke()
               	E.delete_entity(main_menu_pivot)
				E.free_sound(menu_swap)
               	init_main_game()
	      		--dofile(src_dir .. "game.lua")
            end
         end
      else
         if state and key == E.key_up then
            if selected_index > 0 then
               selected_index = selected_index - 1
               highlight_item(selected_index)
               E.play_sound(menu_swap)
            end
         end
         if state and key == E.key_down then
            if selected_index < 2 then
               selected_index = selected_index + 1
               highlight_item(selected_index)
               E.play_sound(menu_swap)
            end
         end
	 if state and key == E.key_return then
	    E.play_sound(menu_click)
	    if selected_index == 0 then
	       -- start new game
	       destroy_main_menu()
	       --dofile(src_dir .. "load_game_opening.lua")
	       time_accumulator = 0
	       game_state = game_opening
        elseif selected_index == 1 then
               -- load game
               show_load_menu = true
               show_load_screen(show_load_menu)
            else
               -- exit
               os.exit()
            end
         end
      end
   end
end

down = { }
down.w, down.a, down.s, down.d, down.shift, down.ctrl = false,false,false,false,false,false
load_save_num = nil
first_push = true
function main_game_do_keyboard(key, state)
	
	if key == E.key_q and not state then
		reset_dialog()
	end
	
	-- update which keys are down
	if key == E.key_w then
		down.w = state
	elseif key == E.key_s then
		down.s = state
	elseif key == E.key_a then
		down.a = state
	elseif key == E.key_d then
		down.d = state
	elseif key == E.key_r then
		if state then
			pace = run
		else
			pace = walk
		end
	end
	
	if in_sequence and active_sequence and not active_sequence.first_talk then
		if key == E.key_return and not state then
			if not step_through_sequence(nil) then
				-- back to question box
				picking = false
	    		picked_object = nil
	    		active_object = nil
	    		
	    		if stored_sequence_struct and active_dialog.questions then
	    			stored_sequence_struct.first_talk = false
	  			elseif active_dialog.is_introduction then
	  				active_dialog.first_talk = false
	  				--E.print_console("hey wassup")
	  				if not (dialog[active_npc][player_knowledge].sequences.intro and dialog[active_npc][player_knowledge].sequences.intro.first_talk) then
	  					active_dialog = dialog[active_npc][player_knowledge]
	  					start_dialog(active_dialog)
	  				else
	  					reset_dialog()
	  				end
	  				return
	  			elseif stored_sequence_struct then
	  				stored_sequence_struct.first_talk = true
	  				reset_dialog()
	  				return
	  			end
	    		
	    		--if active_dialog.is_introduction then
	    		--	picked_object = active_npc
	    		--	anim_object(picked_object, anim_dialog_box)
	    		--else
	    		if active_dialog.questions then
					start_dialog(nil)
				end
				--end
			end
		end
	end
	
	-- if user controlled is locked then return
	if control_lock then
		return
	end

	-- TODO - make sure player HAS MAP!!! bring up map
	if key == E.key_m and state and player_knowledge >= know_has_map then
		if animation ~= anim_paused and animation ~= anim_open_map then
			anim_object(map, anim_open_map)
		else
			anim_object(map, anim_close_map)
		end
	end
	
	if control_lock_partial then
		return
	end
	
	-- check if shfit key is down
	if key == E.key_rshift or key == E.key_lshift then
       down.shift = state
       return
    end
    
    -- check if ctrl key is down
    if key == E.key_rctrl or key == E.key_lctrl then
       down.ctrl = state
       return
    end
    
    -- save game at save state number
    if down.ctrl and not state then
    	load_save_num = nil
       	if key == E.key_1 then
            load_save_num = 1
       	elseif key == E.key_2 then
            load_save_num = 2
       	elseif key == E.key_3 then
            load_save_num = 3
       	elseif key == E.key_4 then
            load_save_num = 4
       	elseif key == E.key_5 then
            load_save_num = 5
       	elseif key == E.key_6 then
            load_save_num = 6
       	elseif key == E.key_7 then
            load_save_num = 7
       	elseif key == E.key_8 then
            load_save_num = 8
       	elseif key == E.key_9 then
            load_save_num = 9
       	elseif key == E.key_0 then
            load_save_num = 0
       	end
       	
       	if load_save_num then
       		dofile(src_dir .. "save_game.lua")
       		return
       	end
    end
    
    -- load game at save state number
    if down.shift and not state then
    	load_save_num = nil
       	if key == E.key_1 then
            load_save_num = 1
       	elseif key == E.key_2 then
            load_save_num = 2
       	elseif key == E.key_3 then
            load_save_num = 3
       	elseif key == E.key_4 then
            load_save_num = 4
       	elseif key == E.key_5 then
            load_save_num = 5
       	elseif key == E.key_6 then
            load_save_num = 6
       	elseif key == E.key_7 then
            load_save_num = 7
       	elseif key == E.key_8 then
            load_save_num = 8
       	elseif key == E.key_9 then
            load_save_num = 9
       	elseif key == E.key_0 then
            load_save_num = 0
       	end
       	
       	if load_save_num then
       		dofile(src_dir .. "load_game.lua")
       		return
       	end
    end
	
	-- set player_dir in a fashion that allows for multiple directions at once
	player_dir.x, player_dir.y, player_dir.z = 0,0,0
	local play_foot_steps = false
	if down.w then
		player_dir.z = -1
		play_foot_steps = true
	end
	
	if down.s then
		player_dir.z = player_dir.z + 1
		play_foot_steps = true
	end
		
	if down.a then
		player_dir.x = -1
		play_foot_steps = true
	end
		
	if down.d then
		player_dir.x = player_dir.x + 1
		play_foot_steps = true
	end
	
	player_dir.x, player_dir.y, player_dir.z = normalize_3d(player_dir.x, 0, player_dir.z)
	
	if play_foot_steps then
		--my_do_contact() -- refresh pickable object data
		if first_push then
		    --E.play_sound(walk_sound)
		    E.loop_sound(walk_sound)
		    first_push = false
		end
    else
        E.stop_sound(walk_sound)
        first_push = true
    end
end

function game_closing_do_keyboard(key, state)
	if state and key == E.key_return then
            if time_accumulator < 3 then
                    time_accumulator = time_accumulator + 1
            elseif time_accumulator < 8 then
                    time_accumulator = time_accumulator + 1
            elseif time_accumulator < 13 then
                    time_accumulator = time_accumulator + 1
            elseif time_accumulator < 18 then
                    time_accumulator = time_accumulator + 1
            elseif time_accumulator < 23 then
                    time_accumulator = time_accumulator + 1
            end
            E.play_sound(menu_click)
    end
end

function do_keyboard(key, state)
	
	if game_state == main_menu then
		main_menu_do_keyboard(key, state)
	elseif game_state == game_opening then
		game_opening_do_keyboard(key, state)
	elseif game_state == main_game then
		main_game_do_keyboard(key, state)
	elseif game_state == game_closing then
		game_closing_do_keyboard(key, state)
	end
end


function do_joystick(device, button, state)
	if control_lock then
		return
	end
end

painting_to_steal = nil
-- process mouse clicks
function do_click(button, state)
	if game_state ~= main_game then
		return
	end
	-- if user control is disabled return
	
	--if control_lock or control_lock_partial then
	if control_lock then
		if in_sequence then
			hover = true
			picking = state
			if state then
				active_object = E.key_return
				hovered_object = E.key_return
			else
				main_game_do_keyboard(E.key_return, false)
			end
		end
		return
	end
	
	if control_lock_partial then
		return
	end
	
	-- determine proper cursor and/or action based on many state variables
    if button == left_mouse then
    	if state then
    		if hover then
    			picking = true
    			active_object = hovered_object
    		else
    			rotate = state
    		end
    	else
    		if hover then
    			if picking and active_object == hovered_object then
    				--E.print_console("something_picked")
    				picked_object = hovered_object
    				
    				if type(picked_object) == "number" then
    					if active_dialog.sequences[picked_object] then
    						start_sequence(active_dialog.sequences[picked_object])
    					else
    						-- cancel dialog
    						npc_go_idle(active_npc)
    						clear_overlay()
    						
    						if active_dialog.persistent then
    							active_dialog.first_talk = true
    						end
    					end
    				else
	    				--begin animation
	    				local category = E.get_entity_geom_attr(picked_object, E.geom_attr_category)
	    				if category == category_painting then
	    					--E.print_console("anim_painting")
	    					anim_object(picked_object, anim_go_to_object)
	    				elseif category == category_person then
	    					--E.print_console("anim_person")
	    					anim_object(picked_object, anim_dialog_box)
	    				end
	    			end
    			end
    		end
    		picking = false
    		picked_object = nil
    		active_object = nil
    		rotate = false
    	end
    elseif button == right_mouse then
    	if state then
    		if hover then
    			if type(hovered_object) == "number" then
    				return
    			end
    			
    			local category = E.get_entity_geom_attr(hovered_object, E.geom_attr_category)
    			if category == category_painting then
	    			picking = true
	    			active_object = hovered_object
	    		end
    		end
    	else
    		if hover then
    			if picking and active_object == hovered_object then
    				picked_object = hovered_object
    				painting_to_steal = picked_object
    				
    				active_mouse_x, active_mouse_y = mouse.x, mouse.y
    				active_dialog = dialog.steal_prompt
    				
    				show_questions(dialog.steal_prompt.questions)
    			end
    		end
    		
    		picking = false
    		picked_object = nil
    		active_object = nil
    	end
    end
end

-- process mouse movements
count = 0
mouse = { }
mouse.x, mouse.y = 0,0
function do_point(dx, dy)
	-- update stored absolute location of mouse for cursor tracking
	mouse.x, mouse.y = mouse.x + dx, mouse.y + dy
	--my_do_contact() -- refresh pickable object data
	
	-- if user control is disabled return
	if control_lock or control_lock_partial then
	--if control_lock then
		return
	end
	
	-- if user is rotating then update rot.x, rot.y
    if rotate then
    	rot.x = -dx
    	rot.y = -dy
    	return
    end
end

function do_frame()
	if game_state ~= main_game then
		return
	end
	-- update our cursor position/style
	--my_do_contact()
	update_cursor()
end

-- init functions -------------------------------------------------------------
function init_viewport()
    viewport.x_min, viewport.y_min, viewport.z_min, viewport.x_max, viewport.y_max, viewport.z_max = E.get_display_bound()

    viewport.width = (viewport.x_max - viewport.x_min)
    viewport.height = (viewport.y_max - viewport.y_min)
	viewport.aspect = viewport.height / viewport.width

    viewport.depth = (viewport.z_min + viewport.z_max)
    viewport.depth_bisect =  viewport.depth / 2

    viewport.x, viewport.y, viewport.w, viewport.h = E.get_display_union()
end

function init_cursors()
	-- create cursor objects
	--[[cursor_none = make_transparent_image_sprite(media_dir .. "blank.png")
	cursor_hover = make_transparent_image_sprite(media_dir .. "hand.png")
	cursor_picking = make_transparent_image_sprite(media_dir .. "handsm.png")
	cursor_default = make_transparent_image_sprite(media_dir .. "arrow.png")]]
	
	cursor_none = make_transparent_image_sprite(media_dir .. "blank.png")
	cursor_hover = make_opaque_image_sprite(media_dir .. "hand.png")
	cursor_picking = make_opaque_image_sprite(media_dir .. "handsm.png")
	cursor_default = make_opaque_image_sprite(media_dir .. "arrow.png")
	set_cursor(cursor_default)
	
	cursor_ray = E.create_object() -- helps our cursor collide with 3d objects for picking
	
	update_cursor()
	
	-- scale cursors to be a reasonable size
	E.set_entity_scale(cursor_hover, cursor_scale, cursor_scale, 1)
	E.set_entity_scale(cursor_picking, cursor_scale, cursor_scale, 1)
	E.set_entity_scale(cursor_default, cursor_scale, cursor_scale, 1)
	
	-- set relationship
	E.parent_entity(cursor_hover, camera_ortho)
	E.parent_entity(cursor_picking, camera_ortho)
	E.parent_entity(cursor_default, camera_ortho)
	E.parent_entity(cursor_ray, camera_ortho)
end

function main_game_load_screen()
	-- put up load screen before loading scene
	--local screen_img = E.create_image(media_dir .. "Loading%i.raw", 1024, 512, 3, 1, 5, 10, 1)
	local screen_img = E.create_image(media_dir .. "Loading%i.raw", 1024, 1024, 3, 1, 5, 10, 1)
	
	local brush = E.create_brush()
    E.set_brush_flags(brush, E.brush_flag_unlit, true)
    E.set_brush_image(brush, screen_img)
    
    load_screen = E.create_sprite(brush)
    
    --[[local window_ratio = viewport.w / viewport.h
    local picture_ratio = 1024 / 512
    local scale_x, scale_y = window_ratio / picture_ratio, window_ratio / picture_ratio
    E.set_entity_position(load_screen, viewport.w / 2, viewport.h / 2, -1)
    E.set_entity_scale(load_screen, scale_x, scale_y, 1)]]
    local scale1_x, scale1_y = 800 / 1024, 600 / 1024
    local scale2_x, scale2_y = viewport.w / 800, viewport.h / 600
    --local scaled_w, scaled_h = 1024 * scale_x, 1024 * scale_y
    E.set_entity_position(load_screen, viewport.w / 2, viewport.h / 2, -1)
    E.set_entity_scale(load_screen, scale1_x * scale2_x, scale1_y * scale2_y, 1)
	E.parent_entity(load_screen, camera_ortho)
end

function init_main_game()
	E.set_entity_alpha(game_opening_pivot, 0.0)
	dofile(src_dir .. "player_config.lua")
	
	-- update camera position and rotation
	E.set_entity_rotation(camera, player_view.x, player_view.y, player_view.z)
	--E.set_entity_rotation(camera, 0, player_view.y, 0)
    E.set_entity_position(camera, player_pos.x, player_pos.y + 2.5, player_pos.z)
	
	init_cursors() -- must do it here for some occlusion reason not fully understood
	
	-- load text objects
	dofile(src_dir .. "load_dialog_box.lua")
	
	-- load scene objects
	dofile(src_dir .. "load_scene.lua")
	
	dofile(src_dir .. "load_dialog.lua")
	
	-- create player
	player = E.create_object()
	map = E.create_object(media_dir .. "map.obj")
	E.set_entity_flags(map, E.entity_flag_hidden, true)
	local brush = E.get_mesh(map,0)
	E.set_brush_flags(brush, E.brush_flag_unlit, true)

	-- set relationship to world
	E.parent_entity(player, world)
	E.parent_entity(map, player)
	
	-- set collision information
	E.set_entity_body_type(player, true)
	E.set_entity_geom_type(player, E.geom_type_box, 1.6, 6.0, 1.6)
	E.set_entity_geom_attr(player, E.geom_attr_mass, 0.00000000001)
	E.set_entity_geom_attr(player, E.geom_attr_bounce, 0)
	E.set_entity_geom_attr(player, E.geom_attr_soft_cfm, 0)
	E.set_entity_geom_attr(player, E.geom_attr_soft_erp, 1)
	E.set_entity_geom_attr(player, E.geom_attr_friction, 100)
	
	-- set collision category information
	E.set_entity_geom_attr(player, E.geom_attr_category, category_player)
	E.set_entity_geom_attr(player, E.geom_attr_collider, category_all)
	E.set_entity_geom_attr(player, E.geom_attr_response, category_all)
	E.set_entity_geom_attr(player, E.geom_attr_callback, category_walls)
		
	-- make all colliding things visible for debuggin
	E.set_entity_flags(player, E.entity_flag_visible_body, debug)
	E.set_entity_flags(player, E.entity_flag_visible_geom, debug)
	
	E.set_entity_position(map, 0, 0, 1)
	E.set_entity_rotation(map, -70, 0, 0)
	
	E.set_sound_receiver(player, 10)
	
	game_state = main_game
	--destroy_game_opening()
	--destroy_main_menu()
end

function init_world()
	-- create objects
	camera_ortho = E.create_camera(E.camera_type_orthogonal)
	camera = E.create_camera(E.camera_type_perspective)
end
-- main -----------------------------------------------------------------------
-- get setting set by user in config.lua
dofile(config_dir .. "config.lua")
-- run the appropriate setup file
dofile(config_dir .. config)

init_viewport()

init_world()

--if game_state == main_menu then
dofile(src_dir .. "load_main_menu.lua")

main_game_load_screen()
E.set_entity_flags(load_screen, E.entity_flag_hidden, true)
--end
--[[if game_state == main_menu then
	dofile(src_dir .. "load_main_menu.lua")
else
	dofile(src_dir .. "player_config.lua")
	--dofile(src_dir .. "game.lua")
	init_world()
	init_player()
end]]

E.enable_timer(true)

--local mem, threshold = gcinfo()
--collectgarbage()
--collectgarbage(threshold + (mem - threshold) / 2)

