dofile( 'data/Scripts/Minigames/01_Box/01_Box_anim.lua' )

class 'l_01_Box' (LevelEventHandler)


local ANIMATIONS = { 'clear_sand', 'butterflies_fly', 'open_lock', 'butterfly_blue_idle',
							'butterfly_yellow_idle' }

local BBT_AREAS = { 
	area_drop_area_get_brush = 'BBT_01_MUST_USE_BRUSH',
	area_drop_key_part_bottom_out = 'BBT_01_MUST_FIX_KEY',
	area_drop_reel_out = 'BBT_01_MUST_SET_ROLLER',
	area_missing_reel = 'BBT_01_MUST_SET_ROLLER',
	area_need_combination = 'BBT_01_MUST_FIND_COMBINATION',
	area_drop_area_get_key = 'BBT_01_MUST_UNLOCK_AMBRY',
	area_locked_ambry = 'BBT_01_MUST_UNLOCK_AMBRY'
}
							
local RIGHT_COMBINATION = { 6, 8, 4 }
							
local NUM_REELS = 3


function l_01_Box:__init(name)
	LevelEventHandler.__init(self, name)
	self:GetPanelLuaObject():SetMode( EPanelMode.PM_MINIGAME )
	
	self:initialize()
	
	self:makeCustomCursors()
	
	self:setStartStates()
	
	self:shakeBoatCyclically()
	
	self:registerMoveObjects()
	self:registerAmbriesActiveObjects()
	self:registerAnimObjects()
	self:registerDragObjects()
	
	--self:RegisterToolFindMG( '01_shell' )
	
	self:loadGame()

	if InteractiveHelp.IsActive() and not InteractiveHelp.IsTipShown('SKIP') then
	    InteractiveHelp.ShowTip('SKIP', 0.5)
	end
end

---------------------------------------------------------
---------------- ACTIVE OBJECTS functions ---------------
---------------------------------------------------------

-- Register moving objects -----------------------
function l_01_Box:registerMoveObjects()
	local MOVE_OBJECTS = { 'feather', 'flask', 'cards', 'grape', 'thimble', 'casket' }
	local hideObjects = { reel_out = { 'feather', 'flask', 'cards' },
								 key_part_bottom_out = { 'grape', 'thimble', 'casket' } }
	
	local function onClickObject( event )
		local actTime = event.save and 0 or 0.5
		local distX = -50
		local distY = 0
		
		self:blockMouseOnPeriod( actTime )
		
		if ( not event.save ) then
			self.SOUNDS.move_object_within_ambry:PlaySound( false )
		end
		
		Object( event.target ):Move( distX, distY, actTime )
		
		-- seek and remove hiding objects from table -----------
		for hiddenObj, hidingObj in pairs( hideObjects ) do
			if ( table.has_value( hidingObj, event.target ) ) then
				table.removeValue( hidingObj, event.target )
				
				-- if table empty - do not ignore hidden object --
				if ( #( hidingObj ) == 0 ) then
					play_field:DoNotIgnoreOnTestPoint( hiddenObj )
					ShowObjects( 'area_drop_' .. hiddenObj )
				end
				
				break
			end
		end
	end
	
	for _, objName in ipairs( MOVE_OBJECTS ) do
		RegisterActiveObject( objName, onClickObject )
	end
end

-- Register active ambries objects ---------------
function l_01_Box:registerAmbriesActiveObjects()
	local MOVE_OBJECTS = { 'ambry_left', 'ambry_right' }
	
	local function onClickAmbryButton( event )
		local ambryName = string.gsub( event.target, 'button_', '' )
		
		AddUniqueGameState( 'MG_01_Box', ambryName .. '_activated' )
		
		if ( not event.save ) then
			self.SOUNDS.activate_ambry:PlaySound( false )
		end
		
		play_field:IgnoreOnTestPoint( event.target )
		Object( event.target ):GetAnimation():SetFrame( 2 )
		
		if ( ambryName == 'ambry_right' ) then
			if HasGameState( 'MG_01_Box', 'jar_activated_before_right_ambry' ) then
				self:attachButterfliesIdleInPlace()
			end
			
			if ( not event.save ) then
				if ( Object( 'area_butterfly_yellow' ):IsVisible() ) then
					self:animateButterflyIdleFly( 'area_butterfly_yellow' )
				end
			end
		end
		
		local actTime = event.save and 0 or 0.5
		local upScale = 1.1
		local distX = ( ambryName == 'ambry_left' ) and -180 or 180
		local distY = -40
		
		self:blockMouseOnPeriod( actTime * 2 )
		
		Object( ambryName ):ChangeScale( 1, upScale, actTime )
		Object( ambryName ):Move( 0, distY, actTime )
		
		self.timer:Add(
			function ()
				Object( ambryName ):Move( distX, 0, actTime )
			end,
			actTime )
	end
	
	for _, objName in ipairs( MOVE_OBJECTS ) do
		RegisterActiveObject( 'button_' .. objName, onClickAmbryButton )
	end
end

-- Register animated objects ---------------------
function l_01_Box:registerAnimObjects()
	---- on click on objects areas ----------------	
	-- on click on jar area -----------------------
	local function onClickJarArea( object_name, anim_name, is_event_save, act_time )
		local animTime = is_event_save and 0 or self.ANIM:GetAnimation( anim_name ):GetDuration()
		
		if ( not is_event_save ) then
			self.SOUNDS.open_jar:PlaySound( false )
		end
		
		if ( not HasGameState( 'MG_01_Box', 'ambry_right_activated' ) ) then
			AddUniqueGameState( 'MG_01_Box', 'jar_activated_before_right_ambry' )
			
			self:attachButterfliesIdleInPlace()
		end
		
		self.timer:Add(
			function ()
				self.ANIM:PlayAnimation( 'butterfly_blue_idle' )
				self.ANIM:PlayAnimation( 'butterfly_yellow_idle' )
				ShowObjects( 'area_butterfly_blue', 'area_butterfly_yellow' )
			end,
			animTime )
	end
	
	-- on click on code list ----------------------
	local function onClickCodeList( object_name, anim_name, is_event_save, act_time )
		Object( object_name ):Move( 0, 30, act_time )
		
		AddUniqueGameState( 'MG_01_Box', 'combination_finded' )
		
		if ( not is_event_save ) then
			self.SOUNDS.move_object_within_ambry:PlaySound( false )
		end
		
		HideObjects( 'area_need_combination' )
		ObjectVisible('area_hint_code', true)
		
		if self:isCodeLockReadyToUnlock() then
			for reelNum = 1, NUM_REELS do
				play_field:DoNotIgnoreOnTestPoint( 'reel_in_' .. reelNum )
			end
		end
	end
	
	local ANIM_OBJECTS = {
		jar = { func = onClickJarArea, anim = 'butterflies_fly' },
		code = { func = onClickCodeList, anim = nil }
	}
	
	-- on click on object area --------------------
	local function onDropObjectInTarget( event )
		local actTime = event.save and 0 or 0.5
		
		HideObjects( event.target )
		
		local objName = string.gsub( event.target, 'area_click_', '' )
		local animName = ANIM_OBJECTS[ objName ].anim
		
		ANIM_OBJECTS[ objName ].func( objName, animName, event.save, actTime )  -- call specialized function
		
		if ( animName ~= nil ) then
			local animDuration = event.save and 0 or self.ANIM:GetAnimation( animName ):GetDuration()
			
			if ( animName ~= ANIM_OBJECTS.jar.anim ) then
				self:blockMouseOnPeriod( animDuration )
			end

			self.ANIM:PlayAnimation( animName )  -- play specialized animtion
			
			if event.save then
				self.ANIM:GetAnimation( animName ):Rewind( true )  -- rewind specialized animtion
			end
		else
			self:blockMouseOnPeriod( actTime )
		end
	end
	------ \on drop objects in their target areas -------
	
	for objName in pairs( ANIM_OBJECTS ) do
		RegisterActiveObject( 'area_click_' .. objName, onDropObjectInTarget )
	end
	RegisterMonologObject('area_hint_code', 'BBT_01_MG_CODE')
end


---------------------------------------------------------
---------------- DRAG OBJECTS functions -----------------
---------------------------------------------------------

function l_01_Box:registerDragObjects()
	local DRAG_OBJECTS = { 'reel_out', 'key_part_bottom_out', 'area_get_key', 'area_get_brush' }
	
	local scaleTime = 0.1
	local scaleCoef = 0.3
	
	-- on get drag object -------------------------
	local function onGetObject( event )
		local objScaleX = Object( event.objName ):GetScaleX()
		local downScaleX = objScaleX * ( 1 - scaleCoef )
		local objScaleY = Object( event.objName ):GetScaleY()
		local downScaleY = objScaleY * ( 1 - scaleCoef )
		
		Object( event.objName ):ChangeScaleX( objScaleX, downScaleX, scaleTime )
		Object( event.objName ):ChangeScaleY( objScaleY, downScaleY, scaleTime )
	end
	
	-- on drop drag object ------------------------
	local function onDropObject( event )
		local objScaleX = Object( event.objName ):GetScaleX()
		local upScaleX = objScaleX * ( 1 / ( 1 - scaleCoef ) )
		local objScaleY = Object( event.objName ):GetScaleY()
		local upScaleY = objScaleY * ( 1 / ( 1 - scaleCoef ) )
		
		Object( event.objName ):ChangeScaleX( objScaleX, upScaleX, scaleTime )
		Object( event.objName ):ChangeScaleY( objScaleY, upScaleY, scaleTime )
	end
	
	
	---- on drop objects in their target areas ---------
	-- on drop key part in target area ------------
	local function onDropKeyPartInTarget( act_time, event )
		self:blockMouseOnPeriod( act_time )
		
		if ( not event.save ) then
			self.SOUNDS.set_key_part:PlaySound( false )
		end
		
		ObjectOpacity( 'key_part_bottom_in', 1, act_time )
		
		play_field:DoNotIgnoreOnTestPoint( 'area_get_key' )
	end
	
	-- on drop reel in target area ----------------
	local function onDropReelInTarget( act_time, event )
		AddUniqueGameState( 'MG_01_Box', 'missing_reel_in_place' )
		
		self:blockMouseOnPeriod( act_time )
		
		if ( not event.save ) then
			self.SOUNDS.set_reel:PlaySound( false )
		end
		
		HideObjects( 'area_missing_reel' )
		ObjectOpacity( { 'reel_in_3', 'reel_glass_3' }, 1, act_time )
		
		if self:isCodeLockReadyToUnlock() then
			for reelNum = 1, NUM_REELS do
				play_field:DoNotIgnoreOnTestPoint( 'reel_in_' .. reelNum )
			end
		end
	end
	
	-- on drop key in target area -----------------
	local function onDropKeyInTarget( act_time, event )
		local animDuration = event.save and 0 or self.ANIM:GetAnimation( 'open_lock' ):GetDuration()
		
		self:blockMouseOnPeriod( animDuration )

		self.timer:Add(
			function ()
				self.ANIM:GetAnimation( 'butterflies_fly' ):SetZ( 250 )
				self.ANIM:GetAnimation( 'butterfly_yellow_idle_fly' ):SetZ( 300 )
				self.ANIM:GetAnimation( 'butterfly_blue_idle_fly' ):SetZ( 300 )
			end,
			animDuration
		)
		
		self.ANIM:PlayAnimation( 'open_lock' )
		
		if event.save then
			self.ANIM:GetAnimation( 'open_lock' ):Rewind( true )
		else
			self.SOUNDS.open_lock_by_key:PlaySound( false )
		end
		
		HideObjects( 'area_locked_ambry' )
		play_field:DoNotIgnoreOnTestPoint( 'button_ambry_right' )
	end
	
	-- on drop brush in target --------------------
	local function onDropBrushInTarget( act_time, event )
		AddUniqueGameState( 'MG_01_Box', 'brush_used' )
		
		local animDuration = event.save and 0 or self.ANIM:GetAnimation( 'clear_sand' ):GetDuration()
		
		self:blockMouseOnPeriod( animDuration )
		
		self.ANIM:PlayAnimation( 'clear_sand' )
		
		if event.save then
			self.ANIM:GetAnimation( 'clear_sand' ):Rewind( true )
		else
			self.SOUNDS.use_brush:PlaySound( false )
		end
		
		HideObjects( 'area_drop_area_get_brush' )
		play_field:DoNotIgnoreOnTestPoint( 'area_drop_reel_out' )
	end
	
	
	local DROP_IN_TARGET_FUNCTIONS = { reel_out = onDropReelInTarget, 
												  key_part_bottom_out = onDropKeyPartInTarget,
												  area_get_key = onDropKeyInTarget,
												  area_get_brush = onDropBrushInTarget }
	
	-- on drop object in target area ----------------
	
	local function onDropObjectInTarget( event )
		local actTime = event.save and 0 or 0.5
		
		HideObjects( event.target )
		
		ObjectOpacity( event.objName, 0, actTime )
		
		DROP_IN_TARGET_FUNCTIONS[ event.objName ]( actTime, event )  -- call specialized function
	end
	------ \on drop objects in their target areas -------
	
	
	for _, objName in ipairs( DRAG_OBJECTS ) do
		RegisterDragObject( objName, objName, 'area_drop_' .. objName, onGetObject,
			onDropObject, onDropObjectInTarget, nil, nil, nil, nil )
	end
end


---------------------------------------------------------
------------------- SERVICE functions -------------------
---------------------------------------------------------

---- Remove value from table by it's name -----------
function table.removeValue( rem_table, removing_value )
	for ind, value in pairs( rem_table ) do
		if ( value == removing_value ) then
			table.remove( rem_table, ind )
		end
	end
end
			
---- Initialize variables ---------------------------
function l_01_Box:initialize()
	self.ANIM = AnimationBundle( Loader_01_MG_Animations( Object( 'boat' ) ) )
	
	-- sounds ------------------------------
	self.SOUNDS = { move_object_within_ambry = 0,
						 use_brush = 0,
						 activate_ambry = 0,
						 set_key_part = 0,
						 open_lock_by_key = 0,
						 set_reel = 0,
						 open_jar = 0,
						 change_code_lock_numbers = 0,
						 move_code_lock_panel = 0 }
	
	for soundName in pairs( self.SOUNDS ) do
		self.SOUNDS[ soundName ] = MakeSoundEntity( '01_mg_' .. soundName )
	end
	---- \sounds ---------------------------
	
	-- video -------------------------------
	self.video_water = CreateVideoEntity( 'data/Video/MiniGames/01_Box/water.ogg', '', false )
	self.video_water:AttachTo( Object( 'boat' ) )
	self.video_water:SetLoop( true )
	self.video_water:SetScale( 2 )
	self.video_water:SetZ( -1 )
	self.video_water:SetY( -380 )
	self.video_water:PlayVideo()
	---- \video ----------------------------
	
	self.BUTTONS = {}
	
	self.shakingAnims = {}
	
	self.codeCombination = { 1, 4, 2 }
end

---- Make custom cursors to all interactive objects ---
function l_01_Box:makeCustomCursors()
	for reelNum = 1, NUM_REELS do
		self.custom_cursors[ 'reel_in_' .. reelNum ] = 'CursorActive'
	end
	
	self.custom_cursors[ 'area_find_tool' ] = 'CursorActive'
	
	for areaName in pairs( BBT_AREAS ) do
		self.custom_cursors[ areaName ] = 'CursorGlass'
	end
end

---- Set start states and positions -----------------
function l_01_Box:setStartStates()	
	-- attach objects to ambries -------------------
	local LEFT_AMBRY_OBJECTS = { 'feather', 'flask', 'cards', 'ring', 'reel_out',
										  'button_left_spot', 'button_ambry_left' }
	local RIGHT_AMBRY_OBJECTS = { 'fan', 'oiler','button_right_spot', 'button_ambry_right',
											'area_click_jar', 'area_butterfly_blue',
											'area_butterfly_yellow' }
	
	for _, objName in ipairs( LEFT_AMBRY_OBJECTS ) do
		Helpers.AttachInPlace( Object( objName ), Object( 'ambry_left' ) )
	end
	
	for _, objName in ipairs( RIGHT_AMBRY_OBJECTS ) do
		Helpers.AttachInPlace( Object( objName ), Object( 'ambry_right' ) )
	end
	
	local LEFT_AMBRY_ANIMS = { 'butterflies_fly', 'butterfly_blue_idle', 'butterfly_yellow_idle',
										'open_lock', 'butterfly_blue_idle_fly' }
	
	for _, anim in ipairs( LEFT_AMBRY_ANIMS ) do
		Helpers.AttachInPlace( self.ANIM:GetAnimation( anim ), Object( 'ambry_right' ) )
	end
	---- \attach objects to ambries ----------------
	
	-- attach objects to boat -----------------------
	local SHAKING_OBJECTS = { 'ambry_left', 'ambry_right', 'shell', 'plate', 'plate_overlay_top',
									  'ribbon_overlay', 'key_part_bottom_out', 'grape', 'thimble', 'casket', 
									  'chest_overlay', 'code', 'code_overlay', 'area_get_brush',
									  'area_drop_key_part_bottom_out', 'area_drop_reel_out', 'area_get_key',
									  'area_drop_area_get_brush', 'area_missing_reel', 'area_need_combination',
									  'area_locked_ambry', 'nail_1', 'nail_2' }
	
	for _, objName in pairs( SHAKING_OBJECTS ) do
		Helpers.AttachInPlace( Object( objName ), Object( 'boat' ) )
	end
	---- \attach objects to boat --------------------
	
	-- set start states for code lock objects -------
	for reelNum = 1, NUM_REELS do
		Object( 'reel_in_' .. reelNum ):GetAnimation():Stop()
		
		play_field:IgnoreOnTestPoint( 'reel_glass_' .. reelNum )
		play_field:IgnoreOnTestPoint( 'reel_in_' .. reelNum )
		
		Helpers.AttachInPlace( Object( 'reel_glass_' .. reelNum ), Object( 'plate' ) )
		Helpers.AttachInPlace( Object( 'reel_in_' .. reelNum ), Object( 'plate' ) )
		
		-- set start code combination --
		Object( 'reel_in_' .. reelNum ):GetAnimation():SetFrame( self.codeCombination[ reelNum ] * -1 )
	end
	
	play_field:IgnoreOnTestPoint( 'plate' )
	---- \set start states for code lock objects ----
	
	ObjectVisible('area_hint_code', false)
	
	-- set animations that must be shaken -----------
	for _, animName in ipairs( ANIMATIONS ) do
		table.insert( self.shakingAnims, animName )
	end
	
	Object( 'button_ambry_left' ):GetAnimation():Stop()
	Object( 'button_ambry_left' ):GetAnimation():SetFrame( 0 )
	Object( 'button_ambry_right' ):GetAnimation():Stop()
	Object( 'button_ambry_right' ):GetAnimation():SetFrame( 0 )
	
	-- set animtions visible ------------------------
	self.ANIM:GetAnimation( 'clear_sand' ):SetVisible( true )
	self.ANIM:GetAnimation( 'butterflies_fly' ):SetVisible( true )
	self.ANIM:GetAnimation( 'open_lock' ):SetVisible( true )
	---- \set animtions visible ---------------------
	
	Helpers.AttachInPlace( Object( 'key_part_bottom_in' ), Object( 'area_get_key' ) )
	Helpers.AttachInPlace( Object( 'key_part_top' ), Object( 'area_get_key' ) )
	Helpers.AttachInPlace( Object( 'brush' ), Object( 'area_get_brush' ) )
	
	ObjectOpacity( { 'reel_in_3', 'reel_glass_3', 'key_part_bottom_in' }, 0 )
	
	HideObjects( 'area_drop_reel_out', 'area_butterfly_blue', 'area_butterfly_yellow', 'area_find_tool' )
	
	play_field:IgnoreOnTestPoint( 'button_ambry_right' )
	play_field:IgnoreOnTestPoint( 'reel_out' )
	play_field:IgnoreOnTestPoint( 'key_part_bottom_out' )
	play_field:IgnoreOnTestPoint( 'key_part_bottom_in' )
	play_field:IgnoreOnTestPoint( 'key_part_top' )
	play_field:IgnoreOnTestPoint( 'brush' )
	play_field:IgnoreOnTestPoint( 'area_get_key' )
	play_field:IgnoreOnTestPoint( 'area_drop_reel_out' )
end

---- Block mouse on some period ---------------------
function l_01_Box:blockMouseOnPeriod( period )
	self.mouseBlocker:Push()
	
	self.timer:Add(
		function ()
			self.mouseBlocker:Pop()
		end,
		period )
end

---- Attach butterflies idle objects to current place ---
function l_01_Box:attachButterfliesIdleInPlace()
	Helpers.AttachInPlace( self.ANIM:GetAnimation( 'butterfly_blue_idle' ), Object( 'boat' ) )
	Helpers.AttachInPlace( self.ANIM:GetAnimation( 'butterfly_yellow_idle' ), Object( 'boat' ) )
	Helpers.AttachInPlace( self.ANIM:GetAnimation( 'butterfly_blue_idle_fly' ), Object( 'boat' ) )
	Helpers.AttachInPlace( Object( 'area_butterfly_blue' ), play_field )
	Helpers.AttachInPlace( Object( 'area_butterfly_yellow' ), play_field )
end

---- Animate idle fly of the specified butterfly ---
function l_01_Box:animateButterflyIdleFly( area_name )
	local butterflyName = string.gsub( area_name, 'area_', '' )
	local animName = butterflyName .. '_idle'
	
	self.ANIM:StopAnimation( animName )
	self.ANIM:PlayAnimation( animName .. '_fly' )
	
	HideObjects( area_name )
	
	local animDuration = self.ANIM:GetAnimation( animName .. '_fly' ):GetDuration()
	
	self.timer:Add(
		function ()
			self.ANIM:PlayAnimation( animName )
			ShowObjects( area_name )
		end,
		animDuration )
end

---- Shake boat cyclically -------------------------
function l_01_Box:shakeBoatCyclically()
	local TIME_STEP = 2
	local SHAKE_ANGLE = 0.25
	
	Object( 'boat' ):SetScale( 1.05 )
	Object( 'boat' ):SetAngle( math.rad( -SHAKE_ANGLE ) )
	
	local function shakeBoat()
		Object( 'boat' ):RotateTo( math.rad( SHAKE_ANGLE ), TIME_STEP )
		
		self.timer:Add(
			function ()
				Object( 'boat' ):RotateTo( math.rad( -SHAKE_ANGLE ), TIME_STEP )
			end,
			TIME_STEP )
		
		self.timer:Add(
			function ()
				shakeBoat()
			end,
			TIME_STEP * 2 )
	end
	
	shakeBoat()
end

---- Check if code lock ready for the unlocking ----
function l_01_Box:isCodeLockReadyToUnlock()
	local LOCK_STATES = { 'combination_finded', 'missing_reel_in_place' }
	
	for _, state in ipairs( LOCK_STATES ) do
		if ( not HasGameState( 'MG_01_Box', state ) ) then
			return false
		end
	end
	
	return true
end

---- Change reel number info -----------------------
function l_01_Box:changeReelCodeNumber( reel_name )
	local reelInd = tonumber( string.gsub( reel_name, "[ %a, _ ]", '' ) )
	
	self.codeCombination[ reelInd ] = self.codeCombination[ reelInd ] < 9 and
												 ( self.codeCombination[ reelInd ] + 1 ) or 0
end

---- Query to show black bar text ------------------
function l_01_Box:queryToShowBBT( clicked_obj_name )
	for areaName, bbt in pairs( BBT_AREAS ) do
		if ( areaName == clicked_obj_name ) then
		    if not IsHardPlayMode() then
			    ShowBlackBar( bbt )
		    end
		end
	end
end

---- Check if current combination - right ----------
function l_01_Box:isRightCombination()
	for ind, rightNum in ipairs( RIGHT_COMBINATION ) do
		if ( rightNum ~= self.codeCombination[ ind ] ) then
			return false
		end
	end
	
	return true
end

---- Do mini-game final actions --------------------
function l_01_Box:doFinalActions( delay, on_load )
	self:GetPanelLuaObject():SetMode( EPanelMode.PM_QUEST )
	
	SetCursor( 'CursorDefault' )
	
	local actTime = on_load and 0 or 0.5
		
	self.timer:Add(
		function ()
			if ( not on_load ) then
				self.SOUNDS.move_code_lock_panel:PlaySound( false )
			end
			Object( 'plate' ):Move( 0, -230, actTime )
			
			self.timer:Add(
				function ()
					ShowObjects( 'area_find_tool' )
				end,
				actTime )
		end,
		delay )
end

---- Complete mini-game ----------------------------
function l_01_Box:completeMinigame( complete_time, on_load )
	local actTime = on_load and 0 or 0.5
	
	ObjectOpacity( 'shell', 0, actTime )
	
	Object( 'area_find_tool' ):SetVisible( false )
	
	CompleteMinigame( complete_time )
end

---- Set only one active (unignored) object -----------
function l_01_Box:setOneActiveObject( not_ignore_obj_name )
	for object in play_field:GetObjects() do
		local objName = object:GetName()
		
		if ( objName ~= not_ignore_obj_name ) and ( not string.find( objName, 'boat' ) ) then
			play_field:IgnoreOnTestPoint( objName )
		end
	end
end -- \setOneActiveObject


---------------------------------------------------------
--------------- EVENT HANDLING functions ----------------
---------------------------------------------------------

function l_01_Box:OnScreenObjectMouseDown( object, key )
	if ( key == HGEK_LBUTTON ) then
		local objName = object:GetName()
		
		if string.find( objName, 'reel_in' ) then
			local nextFrame = Object( objName ):GetAnimation():GetFrame() - 1
			Object( objName ):GetAnimation():SetFrame( nextFrame )
			
			self.SOUNDS.change_code_lock_numbers:StopSound()
			self.SOUNDS.change_code_lock_numbers:PlaySound( false )
			
			self:changeReelCodeNumber( objName )
			
			if self:isRightCombination() then
				self:doFinalActions( 1 )
				self:setOneActiveObject( 'area_find_tool' )
			end
			
		self:saveGame()
		
		elseif ( objName == 'area_find_tool' ) then
			self:completeMinigame( 1 )
			
		elseif ( not IsMouseBlocked() ) then
			self:queryToShowBBT( objName )
		end
	end
end

-----------------------

function l_01_Box:OnObjectMouseEnter( object )
	local objName = object:GetName()
	
	if string.find( objName, 'area_butterfly' ) then
		local butterflyName = string.gsub( objName, 'area_', '' )
		
		self:animateButterflyIdleFly( objName )
	elseif string.find( objName, 'button_ambry' ) then
		if ( not IsMouseBlocked() ) then
			local ambryName = string.gsub( objName, 'button_', '' )
		
			if ( not HasGameState( 'MG_01_Box', ambryName .. '_activated' ) ) then
				Object( objName ):GetAnimation():SetFrame( 1 )
			end
		end
	end
end

-----------------------

function l_01_Box:OnObjectMouseLeave( object )
	local objName = object:GetName()
	
	if string.find( objName, 'button_ambry' ) then
		local ambryName = string.gsub( objName, 'button_', '' )
		
		if ( not HasGameState( 'MG_01_Box', ambryName .. '_activated' ) ) then
			Object( objName ):GetAnimation():SetFrame( 0 )
		end
	end
end

-----------------------

function l_01_Box:OnUpdate( dt )
end


---------------------------------------------------------
------------ STANDARD MINI-GAME functions ---------------
---------------------------------------------------------

function l_01_Box:SkipMiniGame( on_load )
	AddUniqueGameState( 'MG_01_Box', 'skipped' )
	
	self.mouseBlocker:Push()
	
	local actTime = on_load and 0 or 0.5
	
	if ( not HasGameState( 'MG_01_Box', 'brush_used' ) ) then
		ObjectOpacity( 'brush', 0, actTime )
		
		self.ANIM:PlayAnimation( 'clear_sand' )
		
		if on_load then
			self.ANIM:GetAnimation( 'clear_sand' ):Rewind( true )
		else
			self.SOUNDS.use_brush:PlaySound( false )
		end
	end
	
	local animTime = self.ANIM:GetAnimation( 'clear_sand' ):GetDuration()
		- self.ANIM:GetAnimation( 'clear_sand' ):GetCurrentTime()
		
	if ( not HasGameState( 'MG_01_Box', 'missing_reel_in_place' ) ) then
		self.timer:Add(
			function ()
				ObjectOpacity( 'reel_in_3', 1, actTime )
				ObjectOpacity( 'reel_out', 0, actTime )
				
				if ( not on_load ) then
					self.SOUNDS.set_reel:PlaySound( false )
				end
			end,
			animTime )
	end
	
	self.timer:Add(
		function ()
			-- set final states for code lock reels -------
			for reelNum = 1, NUM_REELS do
				Object( 'reel_in_' .. reelNum ):GetAnimation():SetFrame( RIGHT_COMBINATION[ reelNum ] * -1 )
				
				if ( not on_load ) then
					self.SOUNDS.change_code_lock_numbers:PlaySound( false )
				end
			end
			
			self:doFinalActions( actTime, on_load )
			self:setOneActiveObject( 'area_find_tool' )
			
			self.timer:Add(
					function ()
						self.mouseBlocker:Pop()
					end,
					actTime )
		end,
		actTime + animTime )
end

-----------------------

function l_01_Box:saveGame()
	Helpers.SaveObjectToGameState( self.name .. '_save_code_combination', self.codeCombination )
end

-----------------------

function l_01_Box:loadGame()
	if HasGameState( self.name .. '_save_code_combination' )
		or HasGameState( 'MG_01_Box', 'skipped' ) then
		if HasGameState( 'MG_01_Box', 'skipped' ) then
			self:GetPanelLuaObject():SetMode( EPanelMode.PM_QUEST )
			self:SkipMiniGame( true )
		else
			-- load code combination
			self.codeCombination = Helpers.RestoreObjectFromGameState( self.name .. '_save_code_combination' )
			
			-- set loaded states for code lock reels -------
			for reelNum = 1, NUM_REELS do
				Object( 'reel_in_' .. reelNum ):GetAnimation():SetFrame( self.codeCombination[ reelNum ] * -1 )
			end
			
			if self:isRightCombination() then
				self:doFinalActions( 0, true )
				self:setOneActiveObject( 'area_find_tool' )
			end
		end
		
		if IsMiniGameCompleted( self.name ) then
			self:completeMinigame( 1, true )
		end
	end
end


--------------------------------------------------------------------------------
-- CLEAR_ON_SAVE_BEGIN
-- Do not edit code listed below (generated by the editor on each save)
function l_01_Box:LoadData(screen)
	local anim
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/ambry_1.png',0,0,290,364,1,0)
	anim:SetHotSpot(145,182)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('ambry_left',anim,314,435,70,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/ambry_2.png',0,0,310,364,1,0)
	anim:SetHotSpot(155,182)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('ambry_right',anim,724,433,70,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/cards.png',0,0,164,158,1,0)
	anim:SetHotSpot(82,79)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('cards',anim,309,506,76,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/casket.png',0,0,156,144,1,0)
	anim:SetHotSpot(78,72)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('casket',anim,368,419,51,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/chest_overlay.png',0,0,768,84,1,0)
	anim:SetHotSpot(384,42)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('chest_overlay',anim,518,621,150,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/code.png',0,0,116,118,1,0)
	anim:SetHotSpot(58,59)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('code',anim,720,489,35,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/code_overlay.png',0,0,120,74,1,0)
	anim:SetHotSpot(60,37)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('code_overlay',anim,721,468,50,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/fan.png',0,0,254,218,1,0)
	anim:SetHotSpot(127,109)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('fan',anim,750,425,76,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/feather.png',0,0,182,242,1,0)
	anim:SetHotSpot(91,121)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('feather',anim,281,435,78,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/flask.png',0,0,144,118,1,0)
	anim:SetHotSpot(72,59)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('flask',anim,292,362,77,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/grape.png',0,0,190,134,1,0)
	anim:SetHotSpot(95,67)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('grape',anim,352,521,53,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/key_part_bottom_in.png',0,0,68,116,1,0)
	anim:SetHotSpot(34,58)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('key_part_bottom_in',anim,443,198,84,1,1,1)
	
	anim = CreateAnimation('data/Texture/Minigames/01_Box/key_part_bottom_out.png',0,0,64,78,1,0)
	anim:SetHotSpot(32,39)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('key_part_bottom_out',anim,401,459,50,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/key_part_top.png',0,0,74,94,1,0)
	anim:SetHotSpot(37,47)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('key_part_top',anim,421,117,85,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/oiler.png',0,0,146,118,1,0)
	anim:SetHotSpot(73,59)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('oiler',anim,746,498,75,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/plate.png',0,0,138,228,1,0)
	anim:SetHotSpot(69,114)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('plate',anim,524,472,31,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel.png',0,0,82,48,10,1)
	anim:SetHotSpot(41,24)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_in_1',anim,523,399,30,0,0.92,0.92)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel_out.png',0,0,108,92,1,0)
	anim:SetHotSpot(54,46)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_out',anim,346,444,75,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel_glass.png',0,0,82,50,1,0)
	anim:SetHotSpot(41,25)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_glass_3',anim,524,540,35,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/ribbon_overlay.png',0,0,34,34,1,0)
	anim:SetHotSpot(17,17)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('ribbon_overlay',anim,407,81,90,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/ring.png',0,0,62,46,1,0)
	anim:SetHotSpot(31,23)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('ring',anim,390,367,74,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/shell.png',0,0,116,96,1,0)
	anim:SetHotSpot(58,48)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('shell',anim,524,474,28,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/thimble.png',0,0,108,68,1,0)
	anim:SetHotSpot(54,34)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('thimble',anim,356,464,52,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel.png',0,0,82,48,10,1)
	anim:SetHotSpot(41,24)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_in_2',anim,523,467,30,0,0.96,0.96)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel.png',0,0,82,48,10,1)
	anim:SetHotSpot(41,24)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_in_3',anim,524,540,30,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel_glass.png',0,0,82,50,1,0)
	anim:SetHotSpot(41,25)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_glass_2',anim,522,467,35,0,0.96,0.96)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/reel_glass.png',0,0,82,50,1,0)
	anim:SetHotSpot(41,25)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('reel_glass_1',anim,523,399,35,0,0.92,0.92)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/button_left.png',0,0,86,84,3,0)
	anim:SetHotSpot(43,42)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_ambry_left',anim,425,430,181,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/button_left_spot.png',0,0,64,128,1,0)
	anim:SetHotSpot(32,64)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_left_spot',anim,421,431,80,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/button_right.png',0,0,86,84,3,0)
	anim:SetHotSpot(43,42)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_ambry_right',anim,635,430,81,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Minigames/01_Box/button_right_spot.png',0,0,74,134,1,0)
	anim:SetHotSpot(37,67)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('button_right_spot',anim,625,429,80,0,1,1)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_drop_reel_out',anim,523,541,201,0,15,8)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_get_brush',anim,624,114,157,0,15,28.38)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_click_jar',anim,703,338,79,0,20,23)
	
	anim = CreateAnimation('data/Texture/Minigames/01_Box/boat.png',0,0,1026,770,1,0)
	anim:SetHotSpot(513,800)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('boat',anim,512,799,25,0,1,1)
	
	anim = CreateAnimation('data/Texture/Common/area_round.png',0,0,128,128,1,0)
	anim:SetHotSpot(64,64)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_drop_area_get_key',anim,746,394,200,0,1,1)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_get_key',anim,436,159,200,-14,10,21.5)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_click_code',anim,740,494,69,-20,14,9)
	
	anim = CreateAnimation('data/Texture/Common/area_round.png',0,0,128,128,1,0)
	anim:SetHotSpot(64,64)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_butterfly_blue',anim,125,584,177,0,1,1)
	
	anim = CreateAnimation('data/Texture/Common/area_round.png',0,0,128,128,1,0)
	anim:SetHotSpot(64,64)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_butterfly_yellow',anim,939,468,177,0,1,1)
	
	anim = CreateAnimation('data/Texture/Minigames/01_Box/plate_overlay_top.png',0,0,136,248,1,0)
	anim:SetHotSpot(68,124)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('plate_overlay_top',anim,523,238,83,0,1,1)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_find_tool',anim,524,454,300,0,20,20)
	
	-- BLOCKED
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_drop_key_part_bottom_out',anim,436,159,201,-14,10,21.5)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_missing_reel',anim,526,469,200,0,15,28)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_need_combination',anim,526,469,199,0,15,28)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_locked_ambry',anim,732,434,199,0,35,18)
	
	anim = CreateAnimation('data/Texture/Common/area.png',0,0,8,8,1,0)
	anim:SetHotSpot(4,4)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_drop_area_get_brush',anim,526,469,202,0,15,28)
	
	anim = CreateAnimation('data/Texture/Minigames/01_Box/brush.png',0,0,116,234,1,0)
	anim:SetHotSpot(58,117)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('brush',anim,621,115,101,0,1,1)
	
	anim = CreateAnimation('data/Texture/Minigames/01_Box/nail.png',0,0,22,18,1,0)
	anim:SetHotSpot(11,9)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('nail_1',anim,615,71,102,0,1,1)
	
	anim = CreateAnimation('data/Texture/Minigames/01_Box/nail.png',0,0,22,18,1,0)
	anim:SetHotSpot(11,9)
	anim:SetBlendMode(2)
	anim:SetColor('0xFFFFFFFF')
	screen:CreateObject('nail_2',anim,647,71,102,0,1,1)
	
	anim = CreateAnimation('data/Texture/Common/area_round.png',0,0,128,128,1,0)
	anim:SetHotSpot(64,64)
	anim:SetBlendMode(2)
	anim:SetColor('0x01FFFFFF')
	screen:CreateObject('area_hint_code',anim,731,498,100,0,1,1)
	
end
-- CLEAR_ON_SAVE_END
