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

function init_level(level)
	log("")
	log("")
	log('INIT LEVEL '..level)

	collectgarbage(0)
	PurgeShapes()

	local w = g_Game:GetWidget("lualayerwidget")
	if w then g_Game:RemoveWidget(w) end


	local w = g_State:GetWidget("game_darken_effect")
	if w then
		g_State:RemoveWidget(w)
		CreateDarkenEffect(g_State, g_State:GetRect(), g_Game:GetLayer()+1, 
			CColor(0, 0, 0, get_number("level", "dark_alpha")), CColor(0, 0, 0, 0), 
			get_number("level", "dark_time"), get_number("level", "dark_time"))
	end

	trial_controller():Store()

	g_StripeArea:EndPlay()
	g_StripeArea:Clear()
	g_Pipeline:Clear()
	g_Cursor:ClearShape()
	g_AmuletPlace:ClearAmulet()
	g_AmuletPlace:ClearScore(true)

	g_ScoreView:SetColor(get_color('score_view', 'start_color'))

	local config = get_level_config(level)

	g_GameVars.amulets_num = config.amulet_count
	g_GameVars.amulets = {}

	local start = g_Player:EnumAmulets() - g_GameVars.amulets_num
	if start<0 then start = 0 end
	local cur_amulet = 1
	local cur_pl_amulet = start
	while cur_amulet<=g_GameVars.amulets_num do
		g_GameVars.amulets[cur_amulet] = g_Player:GetAmulet(cur_pl_amulet)
		cur_amulet = cur_amulet + 1
		cur_pl_amulet = cur_pl_amulet + 1
		if cur_pl_amulet >= g_Player:EnumAmulets() then cur_pl_amulet = 0 end
	end
	if g_Player:LastAddedAmulet()~=-1 and g_GameVars.amulets[1] ~= g_Player:LastAddedAmulet() then
		local i = 2
		while i<=g_GameVars.amulets_num do
			if g_GameVars.amulets[i] == g_Player:LastAddedAmulet() then break end
			i = i + 1
		end
		if g_GameVars.amulets[i] == g_Player:LastAddedAmulet() then
			while i>1 do
				g_GameVars.amulets[i] = g_GameVars.amulets[i - 1] 
				i = i - 1
			end
		end
		g_GameVars.amulets[1] = g_Player:LastAddedAmulet() 
	end

	g_GameVars.start_fill_amulet = false
	g_GameVars.start_satisfaction = config.start_satisfaction
--	g_GameVars.satisfaction = g_GameVars.start_satisfaction
	g_GameVars.satisfaction = 0
	g_GameVars.middle_sat = 0
	g_GameVars.level_score = 0
	g_GameVars.goal_score = config.goal_score
	g_GameVars.sat_speed = config.sat_speed
	g_GameVars.color_num = config.colors_count
	g_GameVars.current_amulet = 0
	g_GameVars.current_amulet_value = 0
	g_GameVars.amulet_price = 0
	g_GameVars.freeze_satisfaction = false
	

	g_GameVars.goal_reached = false

	g_GameVars.morph_chance = config.morph_chance --из 10000
	g_GameVars.switch_chance = config.switch_chance --из 10000
	g_GameVars.score_chance = config.score_chance --из 10000

	g_GameVars.pause_chance = config.pause_chance --из 10000
	g_GameVars.revealer_chance = config.revealer_chance --из 10000
	g_GameVars.gem_chance = config.gem_chance --из 10000

	g_GameVars.stripe_speed = config.stripe_speed
	g_GameVars.shapes_count_in_stripe = config.shapes_count_in_stripe

	g_GameVars.shapes = get_shapes(config.shapes, config.allowed_shapes)
	g_GameVars.shapes_num = table.getn(g_GameVars.shapes)
	generate_shape_distribution(config.shapes)

	g_GameVars.scene = config.image_setting

	if not g_GameVars.location or g_GameVars.location~=config.location then
		-- сменили локацию
		--g_Player:Save("saves/save.loc")
		save_player("loc")
		g_Player:SetLives(3)
		g_GameVars.easy_lvl1 = false
		g_GameVars.easy_lvl2 = false
		g_GameVars.location_loses = 0
		log("location loses = "..g_GameVars.location_loses)
	end
	g_GameVars.StatePause = 0
	g_GameVars.BonusPause = 1000
	g_GameVars.Melted = false
	g_GameVars.WinLevel = false
	g_GameVars.location = config.location
	g_GameVars.level = config.level

	g_GameVars.level_gem_count = 0

	g_LevelView:SetText(g_GameVars.location.." - "..config.level)
	add_score(0)
	add_lives(0)
	add_coins(0)
	update_bonuses()

	g_GoalScoreView:SetText(tostring(g_GameVars.goal_score))

	local speed = g_GameVars.stripe_speed
	local num = 5
	while num>0 do
		create_stripe(g_StripeArea, speed)
		speed = -speed
		num = num - 1
	end

	g_LifeScene:ShowScene(scene_loader():GetScene(g_GameVars.scene), g_GameVars.force_change)
	if g_GameVars.force_change then
		local melt = g_GameVars.melt_count
		while melt>0 do g_LifeScene:InstantMelt() melt = melt - 1 end
	end

	g_GameVars.melt_count = g_LifeScene:GetMeltCount()

	g_GameVars.LevelState = g_GameVars.state_before_level
	g_GameVars.StatePause = get_number("time", "begin_level_pause")

	g_GameVars.force_change = false

	if (not g_PanelEffectStarted) then
		PanelGemStars(g_Game:GetWidget('show_gem'))
	end

	event_on_init_level()

	g_GodPlace:StartLevel()

	
	fade_game_music()

	--g_App:Save("saves/save")
	--g_Player:Save("saves/save.lev")
	save_game()
	save_player("lev")

	local lvl1 = "false"
	if g_GameVars.easy_lvl1 then lvl1="true" end
	local lvl2 = "false"
	if g_GameVars.easy_lvl2 then lvl2="true" end
	local lvl3 = "false"
	if g_GameVars.easy_lvl3 then lvl3="true" end
	log("Ease game : lvl1="..lvl1.." lvl2="..lvl2.." lvl3="..lvl3)
end

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

function update_level(game, dt)

	if g_GameVars.LevelState==g_GameVars.state_before_level then
		if g_LifeScene:GetState()==0 then
			if not g_LifeScene:IsDark() then
				g_GameVars.StatePause = g_GameVars.StatePause - dt
				if g_GameVars.StatePause<=0 then 
					g_LifeScene:ChangeDark()
					local fxcenter = g_StripeArea:GetAbsPos()
					fxcenter.x = fxcenter.x + g_StripeArea:GetRect().mWidth/2
					fxcenter.y = fxcenter.y + g_StripeArea:GetRect().mHeight/2
					
					local str = string.format(string_table():GetString(168), g_GameVars.location, g_GameVars.level)
					LevelTextEffect(g_Game, fxcenter, str, "font_big_text", 50, 150, 3500)
				end
				return
			end
			g_GameVars.LevelState = g_GameVars.state_playing_level

			show_amulet()

			local scene = scene_loader():GetScene(g_GameVars.scene)
			if scene then
				g_GodPlace:ShowGod(scene.god_image, 1500)
			end
			g_SatisfactionView:ShowSatisfaction(1500, g_GameVars.start_satisfaction)
	
			g_LifeScene:CacheImage(true)
			g_StripeArea:StartPlay()	
			
			local num = 6
			while num>0 do
				add_shape(g_Pipeline)
				num = num - 1
			end

			event_on_start_play()
			change_game_music()
		end
	elseif g_GameVars.LevelState==g_GameVars.state_playing_level then

		if g_Player:GetLevel()==1 then
			g_StripeArea:FlashHoles(10000, get_number('revealer', 'flash_period'), 
				get_color('revealer', 'flash_acolor'), get_color('revealer', 'flash_bcolor')) 
		end

		if g_Cursor:GetShape().__ok and g_Cursor:GetShape():GetType()=="CFillShape" then
			local found = false
			local shape = g_Cursor:GetShape()
			local stripe_num = g_StripeArea:EnumStripes() - 1
			local hole_num = 0
			while stripe_num>=0 do
				local stripe = g_StripeArea:GetStripe(stripe_num)
				local x = shape:GetRect().mX + shape:GetRect().mWidth/2
				local y = shape:GetRect().mY + shape:GetRect().mHeight/2
				if x>=stripe:GetAbsPos().x and x<=stripe:GetAbsPos().x + stripe:GetRect().mWidth
				and y>=stripe:GetAbsPos().y and y<=stripe:GetAbsPos().y + stripe:GetRect().mHeight then
					hole_num = stripe:EnumHoles() - 1
					while hole_num>=0 do
						local hole = stripe:GetHole(hole_num)
						if x>=hole:GetAbsPos().x and x<=hole:GetAbsPos().x + hole:GetRect().mWidth
						and y>=hole:GetAbsPos().y and y<=hole:GetAbsPos().y + hole:GetRect().mHeight 
						and hole:Empty() and not hole:IsMarked() then
							found = true
							break
						end

						hole_num = hole_num - 1
					end
				end
				if found then break end
				stripe_num = stripe_num - 1
			end

			if g_GameVars.last_flash and not (found and g_GameVars.last_flash.stripe==stripe_num and g_GameVars.last_flash.hole==hole_num) then
				g_StripeArea:GetStripe(g_GameVars.last_flash.stripe):GetHole(g_GameVars.last_flash.hole):StopFlash(true)
				g_GameVars.last_flash = nil
			end
			
			if found then
				g_GameVars.last_flash = {}
				g_GameVars.last_flash.stripe = stripe_num
				g_GameVars.last_flash.hole = hole_num
				g_StripeArea:GetStripe(g_GameVars.last_flash.stripe):GetHole(g_GameVars.last_flash.hole):Flash(
					get_number("combo", "flash_speed"), CColor(255, 255, 0, 0), CColor(255, 255, 255, 255))

			end
		end

		-- отнимаем сатисфакцию
		if g_GameVars.start_fill_amulet and not g_StripeArea:IsPaused() and g_AmuletPlace:IsValidAmulet() then
			local sat = g_GameVars.satisfaction
			if sat>100 then sat = 100 end
			if sat<20 then sat = 20 end
			local dsat = -(sat*get_number("satisfaction", "k") + g_GameVars.sat_speed)*dt/1000.0

			-- применяем упрощение
			if g_GameVars.easy_lvl1 then
				dsat = dsat * get_number("ease_game", "lvl1_satisfaction_percent")
			end
			if g_GameVars.easy_lvl2 then
				dsat = dsat * get_number("ease_game", "lvl2_satisfaction_percent")
			end
			if g_GameVars.easy_lvl3 then
				dsat = dsat * get_number("ease_game", "lvl3_satisfaction_percent")
			end

			add_satisfaction(dsat)
		end

		update_game_music(g_GameVars.satisfaction)
		update_game_events()

		
		g_GameVars.BonusPause = g_GameVars.BonusPause - dt
		if g_GameVars.BonusPause <= 0 then 
			g_GameVars.BonusPause = 1000

			if not g_StripeArea:IsPaused() then
				local id = ""
				local rnd = math.random(10000)
				if  g_GameVars.enable_gem_bonus and rnd>0 and rnd<=g_GameVars.gem_chance then id = "Gem" end
				rnd = rnd - g_GameVars.gem_chance
				if  rnd>0 and rnd<=g_GameVars.pause_chance then id = "Pause" end
				rnd = rnd - g_GameVars.pause_chance 
				if  rnd>0 and rnd<=g_GameVars.revealer_chance then id = "Revealer" end

				if id~='' then create_hole_bonus(id) end
			end
		end

		if g_AmuletPlace:IsCompleted() then 
			change_amulet() 
		end
		
		if g_GameVars.current_amulet>g_GameVars.amulets_num then end_level() end

	elseif g_GameVars.LevelState == g_GameVars.state_after_level then
		if not g_GameVars.WinLevel  then 
			if g_GameVars.ThunderTime>0 then
				local t = g_GameVars.ThunderTime / 100
				g_Game:MoveTo(5*math.sin(t*6.29), 5*math.cos(t*6.29))
				g_GameVars.ThunderTime = g_GameVars.ThunderTime - dt
			else
				g_Game:MoveTo(0, 0)
			end
		end
		if g_LifeScene:GetState()~=0 then return end

		if not g_GameVars.Melted then
			g_GameVars.EndMelt = false
			if g_GameVars.WinLevel then 
				play_sound("sound_stars_emit")
				StripeMeltEffect(g_Game)
			else
				g_StripeArea:PlayGrayscaleEffect(700)
			end
			g_GameVars.Melted = true
			return
		end
		if not g_GameVars.EndMelt or g_Player:GetLevel()<=100 then
			g_GameVars.StatePause = g_GameVars.StatePause - dt
		else
			if g_GameVars.EndGameMusicPause then
				g_GameVars.EndGameMusicPause = g_GameVars.EndGameMusicPause - dt
				if g_GameVars.EndGameMusicPause<0 then
					g_GameVars.EndGameMusicPause = nil
					update_game_music(100.0)
				end
			end
		end

		local pause = 0
		if g_GameVars.WinLevel then
			pause = get_number("time", "end_win_level_pause")
			if not g_GameVars.EndMelt and g_GameVars.StatePause<pause - get_number("life_scene", "melt_time") then 
				g_GameVars.EndMelt = true
			 
				if g_GameVars.WinLevel then
					if g_Player:GetLevel()>=100 then 
						play_sound("sound_game_win") 
						CreateFinalGameEffect(g_Game)
					end
					local w = CLuaLayerWidget("lualayerwidget", 0)
					w:Resize(g_Game:GetRect().mWidth, g_Game:GetRect().mHeight)
					w:SetModal(true)
					g_Game:AddWidget(w)

					g_GameVars.EndGameMusicPause = 9400
				end
			end

		else
			pause = get_number("time", "end_lose_level_pause")
			if g_GameVars.StatePause<pause - 700 then 
				g_StripeArea:EndPlay()
			end
		end
		
		if g_GameVars.StatePause<=0 then
			
			g_GodPlace:HideGod(300)
			g_SatisfactionView:HideSatisfaction(300)
			g_GameVars.LevelState = g_GameVars.state_between_levels
			show_statistics()
		end

	end
end

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

function end_level()
	print('end_level')
	g_Pipeline:HideShapes(1200.0)

	g_Cursor:ClearShape()

	g_GodPlace:EndLevel()

	play_sound("sound_thunder")

	g_GameVars.ThunderTime = 300

	local number = 5
	while number>=0 do
		local shape = g_Pipeline:GetShape(number)
		local pwidget = shape:GetWidget("additional_score_pwidget")
		if pwidget ~= nil then
			ConvertToCParticleWidget(pwidget):Destroy()
		end
		number = number - 1
	end

	update_game_music(100.0)

	g_LifeScene:CacheImage(false)

	g_LifeScene:ChangeDark()

	g_GameVars.middle_sat = g_GameVars.middle_sat / g_GameVars.amulets_num

	if g_GameVars.goal_score <= g_GameVars.level_score then
		-- win	
		g_GameVars.StatePause = get_number("time", "end_win_level_pause")

		g_Player:AddLevelSatisfaction(g_GameVars.middle_sat)
		g_GameVars.WinLevel = true
		g_Player:SetLevel(g_Player:GetLevel() + 1)
		g_StripeArea:EndPlay()

		local fxcenter = g_StripeArea:GetAbsPos()
		fxcenter.x = fxcenter.x + g_StripeArea:GetRect().mWidth/2
		fxcenter.y = fxcenter.y + g_StripeArea:GetRect().mHeight/2
		local config = get_level_config(g_Player:GetLevel())

		local text = string_table():GetString(187)
		if g_Player:GetLevel()>100 then
			text = string_table():GetString(215)

--[[			local music = g_App.mMusicInterface
			music:FadeOut(1, false, 0.004)
			music:FadeOut(2, false, 0.004)]]
			fade_game_music()


		elseif g_GameVars.location~=config.location then
			text = string_table():GetString(188)
		end
		-- уровень пройден
		if g_GameVars.goal_show == true then
			fxcenter.y = fxcenter.y - 40
		end
		LevelTextEffect(g_Game, fxcenter, text, "font_big_text", 50, 150, 3000)
	else
		--lose
		g_GameVars.StatePause = get_number("time", "end_lose_level_pause")

		g_GameVars.WinLevel = false
		if g_Player:GetLives()==2 and not g_GameVars.easy_lvl2 then
			g_GameVars.easy_lvl1 = true
		end
		add_lives(-1)
		g_StripeArea:Pause(480, 20000)
	end

	g_GameVars.LevelState = g_GameVars.state_after_level
end

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

function change_amulet()
	if g_AmuletPlace:IsValidAmulet() then
		local center = g_StripeArea:GetAbsPos()
		center.x = center.x + g_StripeArea:GetRect().mWidth/2
		center.y = center.y + g_StripeArea:GetRect().mHeight/2

		local point = g_AmuletPlace:GetAbsPos()
		point.x = point.x + 59
		point.y = point.y + 63

		AmuletFlyAction(g_Game, center, point, g_AmuletPlace:GetAmuletImage())
	end

	g_AmuletName:SetText('')
	g_AmuletCost:SetText('')
	g_AmuletPlace:ClearAmulet()
end

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

function show_amulet()
	g_GameVars.freeze_satisfaction = false
	g_GameVars.current_amulet = g_GameVars.current_amulet + 1

	if g_GameVars.current_amulet == (g_GameVars.amulets_num) then
		local from = g_Game:GetAbsPos()
		from.x = from.x + g_Game:GetRect().mWidth/2
		from.y = from.y + g_Game:GetRect().mHeight/2
		
		if g_GameVars.goal_show then 
			from.y = from.y + 40
		end

		LastAmuletBonusEffect(g_Game, from, CColor(255, 255, 255, 255), string_table():GetString(165), "font_big_text")
	end

	if g_GameVars.current_amulet > g_GameVars.amulets_num then 
		g_AmuletName:SetText('')
		g_AmuletCost:SetText('')
		return 
	end
	g_AmuletPlace:ClearScore(true)
	g_AmuletPlace:SetAmulet(CAmulet(g_GameVars.amulets[g_GameVars.current_amulet]))

	g_GameVars.amulet_price = g_AmuletPlace:GetAmulet():GetPrice()

	g_AmuletName:SetText(g_AmuletPlace:GetAmulet():GetAmuletName())
	g_AmuletCost:SetText(g_GameVars.amulet_price..'')

	g_GameVars.satisfaction = g_GameVars.start_satisfaction
	add_satisfaction(0)
	g_GameVars.start_fill_amulet = false

	g_AmuletsCount:SetText(string.format(string_table():GetString(11), g_GameVars.current_amulet, g_GameVars.amulets_num) )

	play_sound("sound_amulet_show")
end

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

function create_stripe(area, speed)
	local stripe = CStripe(speed)
	stripe:SetLua(g_Lua)
	local num = g_GameVars.shapes_count_in_stripe
	stripe:Resize(80*6, 90)
	while num>0 do
		insert_hole(stripe)
		num = num - 1
	end
	stripe:GenerateImage()
	area:InsertStripe(stripe)
end

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

function insert_hole(stripe)
	local hole = CHole(get_random_shape_id())
	hole:Resize(80, 90)
	stripe:InsertHole(hole)
end

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

function add_shape(pipe, force)
	local rnd = math.random(10000)
	if  force==1 or rnd <= g_GameVars.switch_chance and not force then
		local changeshape = CSwitchShape(get_number("switch_shape", "switch_time"), get_random_color())
		changeshape:Resize(80, 90)
		local num = 1
		while num <= g_GameVars.shapes_num do
			changeshape:AddID(g_GameVars.shapes[num])
			num = num + 1
		end
		pipe:AddShape(changeshape)
	elseif force==2 or rnd - g_GameVars.switch_chance <= g_GameVars.morph_chance and not force then
		local filler = CFillShape(get_number("fill_shape", "color_time"))
		filler:Resize(80, 90)
		local num = 1
		while num <= g_GameVars.color_num do
			filler:AddColor(g_Colors[num])
			num = num + 1
		end
		pipe:AddShape(filler)
	else
		local id = get_random_shape_id()
		if g_Player:GetLevel()==1 and pipe:EnumShapes()<5 then
			while (not g_StripeArea:HasFreeHoles(id, true))  do 
				id = get_random_shape_id()
			end
		else
			local try = 50
			while try>0  do 
				id = get_random_shape_id()
				local free = g_StripeArea:CountFreeHoles(id)
				if not (free==1 and g_Cursor:GetShape().__ok and g_Cursor:GetShape():GetShapeID()==id
				         or free<=0) then break end
				try = try - 1
			end
		end
		local shape = CShape(id, get_random_color())
		shape:Resize(80, 90)
		if force==3 or rnd - g_GameVars.switch_chance - g_GameVars.morph_chance <= g_GameVars.score_chance and not force then  
			shape:SetBonusScore(get_number("scores","bonus_score"))
			AdditionalScoreStarsEffect(shape, shape:GetShapeID())
		end
		pipe:AddShape(shape)
	end
end

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

function create_hole_bonus(id)
	local candidate_min_dist = get_number("bonuses", "min_hole_distance")

	local candidates = {}
	local stripe_size = g_StripeArea:EnumStripes()
	local stripe_num = 0
	while stripe_num<stripe_size do
		local stripe = g_StripeArea:GetStripe(stripe_num)

		local hole_size = stripe:EnumHoles()
		local hole_num = 0
		while hole_num<hole_size do
			local h = stripe:GetHole(hole_num)
			if h and h:Empty()  and not h:GetBonus():IsEnabled() 
			and  (	(stripe:GetSpeed() < 0 and h:GetRect().mX>=candidate_min_dist and h:GetRect().mX<g_StripeArea:GetRect().mWidth - h:GetRect().mWidth) 
				or (stripe:GetSpeed() > 0 and h:GetRect().mX + h:GetRect().mWidth <= stripe:GetRect().mWidth - candidate_min_dist and h:GetRect().mX>=0)) 
			then
				candidates[table.getn(candidates) + 1] = { [1] = stripe_num, [2] = hole_num}
				break 
			end
			hole_num = hole_num + 1
		end
		stripe_num = stripe_num + 1
	end

	if table.getn(candidates)>0 then 
		if id=="Gem" then 
			if g_GameVars.level_gem_count < get_number("bonuses", "max_level_gem_count") then
				g_GameVars.level_gem_count = g_GameVars.level_gem_count + 1
			else
				return false
			end
		end

		local num = math.random(table.getn(candidates))
		local hole = g_StripeArea:GetStripe(candidates[num][1]):GetHole(candidates[num][2])
		local bonus = CBonus(get_number('bonuses', 'show_time'), get_number('bonuses', 'hide_time'), get_number('bonuses', 'view_time') , id)
		hole:SetBonus(bonus) 
		if bonus:GetBonusID()=="Gem" then
			GemStarsEffect(hole)
		end

		return true
	end
	return false
end

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

function on_pickup_shape(pipeline, shape)
	if g_GameVars.LevelState~=g_GameVars.state_playing_level or not g_StripeArea:IsPlaying() then return end
	if g_Cursor:GetShape().__ok then
		play_sound_shape_wrong()
		return
	end

	event_on_pickup_shape(shape)

	shape:OnPickupShape()
	pipeline:RemoveShape(shape)
	g_Cursor:SetShape(shape)
	
	add_shape(pipeline)
	play_sound("sound_shape_pickup")

	g_StripeArea:ChangeFlashID(shape:GetShapeID())
end

-------------------------------------------------------------------------------------------------
function on_mouse_enter_hole(hole)
		if not g_Cursor:GetShape().__ok then return end
		local shape = g_Cursor:GetShape()
		
		if hole:Empty()==true and shape:GetType()=="CFillShape" then
			hole:Flash(get_number("combo", "flash_speed"), CColor(255, 255, 0, 0), CColor(255, 255, 255, 255))
		end
end

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

function on_mouse_leave_hole(hole)
		if not g_Cursor:GetShape().__ok then return end
		
		if hole:Empty()==true and g_Cursor:GetShape():GetType()=="CFillShape" then 
			hole:StopFlash(true)
		end
end

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

function on_insert_shape_in_stripe(stripe, hole)
	g_GameVars.TextOffset = 0

	if (not g_Cursor:GetShape().__ok or not g_StripeArea:IsPlaying()
	or g_GameVars.LevelState~=g_GameVars.state_playing_level) then return end

	if g_Cursor:GetShape():IsEqualID(hole:GetShapeID())==false or (not hole:Empty()) then 
		play_sound_shape_wrong()
		return
	end

	g_GameVars.last_flash = nil

	g_GameVars.start_fill_amulet = true

	local score = get_number("scores", "insert_shape")
	
	local from = hole:GetAbsPos()
	local fromr = hole:GetRect()

	local shape = g_Cursor:GetShape()

	local from_shape = shape:GetAbsPos()
	from_shape.x = from_shape.x + shape:GetRect().mWidth/2
	from_shape.y = from_shape.y + shape:GetRect().mHeight/2

	local to = g_AmuletPlace:GetAmuletBarRect()
	local top = g_AmuletPlace:GetAbsPos()
	to.mX = to.mX + top.x
	to.mY = to.mY + top.y

	local extrapoints = false
	local extrascore = 0
	if g_Cursor:GetShape():GetBonusScore()~=0 then
		play_sound("sound_shape_insert_bonus")
		AdditionalScoreFlyEffect(g_Game,
					g_Cursor:GetShape(),
					g_Cursor:GetShape():GetShapeID(),
		   		    	from,
				    	to)
		extrapoints = true
		extrascore = g_Cursor:GetShape():GetBonusScore() + 15
	else
		play_sound("sound_stars_emit")
	end
	
	score = score + g_Cursor:GetShape():GetBonusScore()

	g_Cursor:GetShape():SetBonusScore(0)
	g_Cursor:GetShape():OnInsertShape()

	local immediate_insert = true

	if g_Cursor:GetShape():GetType()=="CSwitchShape" then
		score = score + get_number("scores", "use_switch_shape")
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(148), get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 20
	elseif g_Cursor:GetShape():GetType()=="CFillShape" then
		score = score + get_number("scores", "use_fill_shape")
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(147), get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 20
		
		immediate_insert = false
	end

	local gem = hole:GetWidget("gem_pwidget")
	if gem then
		hole:RemoveWidget(gem)
	end

	add_satisfaction(get_number("satisfaction", "insert_shape"))

	if immediate_insert == true then
		hole:InsertShape(g_Cursor:GetShape())
	end

	local bonus = hole:GetBonus()
	local id = bonus:GetBonusID()

	if (id == "Gem") and (bonus:IsEnabled()) then
		local to = g_Game:GetWidget("show_gem"):GetAbsPos()
		to.x = to.x + g_Game:GetWidget("show_gem"):GetRect().mWidth/2
		to.y = to.y + g_Game:GetWidget("show_gem"):GetRect().mHeight/2
		
		local from_gem = CFloatVector(from.x + hole:GetRect().mWidth/2, from.y + hole:GetRect().mHeight/2)
		
		GemFlyEffect(g_Game, from_gem, to, "image_game_bonuses_gem")
		
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(145), get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 20		
		play_sound("sound_bonus_gem")
	end
	if (id == "Pause") and (bonus:IsEnabled()==true) then 
		g_StripeArea:Pause(get_number('pause',  'move_time'), get_number('pause', 'stay_time')) 
		score = score + get_number("scores", "use_pause")

		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(144), get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 20
		play_sound("sound_bonus_pause")
	end
	if (id == "Revealer") and (bonus:IsEnabled()==true) then 
		local from = hole:GetAbsPos()
		from.x = from.x + hole:GetRect().mWidth/2
		from.y = from.y + hole:GetRect().mHeight/2	
	
		g_StripeArea:FlashHoles(get_number('revealer', 'flash_time'), get_number('revealer', 'flash_period'), 
			get_color('revealer', 'flash_acolor'), get_color('revealer', 'flash_bcolor'))
		score = score + get_number("scores", "use_revealer")
		
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(143), get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 20
		play_sound("sound_bonus_revealer")
	end
	
	g_StripeArea:ChangeFlashID(-1)
	
	hole:SetBonus(CBonus())

	-- подсчитываем комбу
	local color = g_Cursor:GetShape():GetShapeColor()
	local pos = stripe:GetHoleNumber(hole)
	local combo = 0
	-- смотрим слева от дыры
	local size = stripe:EnumHoles()
	local i = pos - 1
	if i<0 then i = size - 1 end
	while i~=pos do
		cur_hole = stripe:GetHole(i)
		if cur_hole:Empty() or cur_hole:GetShapeColor()~=color then break end
		combo = combo + 1
		i = i - 1
		local ccolor = cur_hole:GetShapeColor()
		cur_hole:Flash(get_number("combo", "flash_speed"), CColor(ccolor.mRed, ccolor.mGreen, ccolor.mBlue, 0), CColor(255, 255, 255, 255))
		cur_hole:StopFlash(false)
		if i<0 then i = size - 1 end
	end
	if i~=pos then
		-- смотрим справа от дыры
		local i = pos + 1
		if i>=size then i = 0 end
		while i~=pos do
			cur_hole = stripe:GetHole(i)
			if cur_hole:Empty() or cur_hole:GetShapeColor()~=color then break end
			combo = combo + 1
			i = i + 1
			local ccolor = cur_hole:GetShapeColor()
			cur_hole:Flash(get_number("combo", "flash_speed"), CColor(ccolor.mRed, ccolor.mGreen, ccolor.mBlue, 0), CColor(255, 255, 255, 255))
			cur_hole:StopFlash(false)
			if i>=size then i = 0 end
		end
	end
	if combo>0 then
		play_sound("sound_shape_insert_combo")
		local ccolor = hole:GetShapeColor()
		
		if immediate_insert == false then
			ccolor = g_Cursor:GetShape():GetShapeColor()
		end
		
		hole:Flash(get_number("combo", "flash_speed"), CColor(ccolor.mRed, ccolor.mGreen, ccolor.mBlue, 0), CColor(255, 255, 255, 255))
		hole:StopFlash(false)
		score = score + get_number("combo", "score_multiplier")*combo
		add_satisfaction(get_number("combo", "satisfaction_multiplier")*combo)
	end
	
	event_on_insert_shape(g_Cursor:GetShape(), combo)

	if immediate_insert == false then
		hole:Mark()
		
		local from1 = CFloatVector(g_Cursor:GetShape():GetRect().mX, g_Cursor:GetShape():GetRect().mY)
		from1.x = from1.x + g_Cursor:GetShape():GetRect().mWidth/2
		from1.y = from1.y + g_Cursor:GetShape():GetRect().mHeight/2
		ShapeInsertEffect(g_StripeArea, from1, hole, stripe, hole:GetShapeID(), g_Cursor:GetShape():GetShapeColor(), score)		
	end

	local check = stripe:IsFullStripe()
	if not check then play_sound("sound_shape_insert") end
	if (check == false) and (combo > 0) then
		local ccolor = hole:GetShapeColor()
		
		if immediate_insert == false then
			ccolor = g_Cursor:GetShape():GetShapeColor()
		end
		if extrapoints==false then
			ComboBonusEffect(g_Game, from_shape, ccolor, combo, true, score)
		else
			ComboBonusEffect(g_Game, from_shape, ccolor, combo, false, 0)
		end
		from_shape.y = from_shape.y - 30		
	end
	
	if (check == true) and (combo > 0) then
		if immediate_insert == true then 
			check_stripe(stripe, score)
		end
		
		local ccolor = g_Cursor:GetShape():GetShapeColor()
		ComboBonusEffect(g_Game, from_shape, ccolor, combo, false, 0)
		from_shape.y = from_shape.y - 30		
	end

	if (check == true) and (combo == 0) then
		check_stripe(stripe, score)
	end

	if (extrapoints == true) and (check == false) and (combo == 0) then
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(146).." +"..extrascore, get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 25
	elseif (extrapoints == true) and (check == false) and (combo > 0) then
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(146).." +"..score, get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 25
	elseif (extrapoints == true) and (check == true) then
		TextBonusEffect(g_Game, from_shape, get_color('fx_color', 'text_color1'), get_color('fx_color', 'text_color2'), get_number('fx_color', 'text_period'), string_table():GetString(146).."", get_string('fonts', 'bonus_font'))
		from_shape.y = from_shape.y - 25
	end

	InsertShapeStarsEffect(g_Game,
				    CFloatVector(from.x  + fromr.mWidth/2, from.y  + fromr.mHeight/2),
				    to,
				    g_Cursor:GetShape():GetShapeColor(),
				    score)
	g_Cursor:ClearShape()
	
end

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

function check_stripe(stripe, score)
	if stripe:IsFullStripe() then
	
		local to = g_AmuletPlace:GetAmuletBarRect()	
		local top = g_AmuletPlace:GetAbsPos()
		to.mX = to.mX + top.x
		to.mY = to.mY + top.y
		
		DestroyStripeEffect(g_Game,
				           stripe,
				           to,
				           get_number("scores", "fill_stripe"))
				           
		local stripepos = stripe:GetAbsPos()
		stripepos.x = g_StripeArea:GetAbsPos().x + stripe:GetRect().mWidth/2
		stripepos.y = g_StripeArea:GetAbsPos().y + stripe:GetRect().mY + stripe:GetRect().mHeight/2
		
		local add_scores = get_number("scores", "fill_stripe") + score
		g_GameVars.stripescore_show = true
		TextStripeBonusEffect(g_Game, stripepos, CColor(255, 255, 255, 255), CColor(255, 255, 255, 255), 1000, "+"..add_scores, get_string('fonts', 'stripe_font'))
		
		g_StripeArea:RemoveStripe(stripe)
		create_stripe(g_StripeArea, -g_StripeArea:GetStripe(0):GetSpeed())

		play_sound("sound_stripe_destroy")
		return true
	end
end

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

function discard_shape(shape)
	local s = shape
	shape:OnInsertShape()
	g_Discard:DiscardShape(shape)
	add_satisfaction(-get_number("satisfaction", "discard_penalty"))
	g_GameVars.start_fill_amulet = true
	play_sound("sound_discard_use")
	g_StripeArea:ChangeFlashID(-1)
end

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

function on_discard_mouse_up()
	if g_GameVars.LevelState~=g_GameVars.state_playing_level or not g_StripeArea:IsPlaying() then return end
	if g_Cursor:GetShape().__ok then
		local shape = g_Cursor:GetShape()
		g_Cursor:ClearShape()
		discard_shape(shape)
	end
end

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

function on_discard_mouse_down()
	if g_GameVars.LevelState~=g_GameVars.state_playing_level or not g_StripeArea:IsPlaying()  then return end
	if g_Cursor:GetShape().__ok
	then 	
		local shape = g_Cursor:GetShape()
		g_Cursor:ClearShape()
		discard_shape(shape)
	else 
		local shape = g_Pipeline:GetShape(0)
		g_Pipeline:RemoveFirstShape()
		discard_shape(shape)
		add_shape(g_Pipeline)
	end
end

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

function on_lotus_pressed(window, action, widget)
	if g_Player:GetGiftCount(CPlayer.gift_lotus) <= 0 then return end

	event_show_gift_info()
	play_sound("sound_gift_lotus_press")
	local sat = get_number("gifts", "lotus")
	
--	эффект вылетания амулета
	GiftFlyEffect(g_Game, widget, "image_game_gui_lotus", sat, "sound_gift_lotus_apply")
		
--	add_satisfaction(sat)
	g_Player:SetGiftCount(CPlayer.gift_lotus, g_Player:GetGiftCount(CPlayer.gift_lotus) - 1)
	update_bonuses()
end

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

function on_food_pressed(window, action, widget)
	if g_Player:GetGiftCount(CPlayer.gift_food) <= 0 then return end

	event_show_gift_info()
	play_sound("sound_gift_food_press")
	local sat = get_number("gifts", "food")
	
--	эффект вылетания амулета
	GiftFlyEffect(g_Game, widget, "image_game_gui_food", sat, "sound_gift_food_apply")	
	
--	add_satisfaction(sat)
	g_Player:SetGiftCount(CPlayer.gift_food, g_Player:GetGiftCount(CPlayer.gift_food) - 1)
	update_bonuses()
end

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

function on_aroma_pressed(game, action, widget)
	if g_Player:GetGiftCount(CPlayer.gift_aroma) <= 0 then return end

	event_show_gift_info()
	play_sound("sound_gift_aroma_press")

	local sat = get_number("gifts", "aroma")
--	add_satisfaction(sat)
	
--	эффект вылетания амулета
	GiftFlyEffect(g_Game, widget, "image_game_gui_aroma", sat, "sound_gift_aroma_apply")	
	
	g_Player:SetGiftCount(CPlayer.gift_aroma, g_Player:GetGiftCount(CPlayer.gift_aroma) - 1)
	update_bonuses()
end

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

function to_2dnumber(num)
	local s = num..''
	if num<10 then s = '0'..s end
	return s
end

function update_bonuses()
	local aroma = g_Player:GetGiftCount(CPlayer.gift_aroma)
	local food = g_Player:GetGiftCount(CPlayer.gift_food)
	local lotus = g_Player:GetGiftCount(CPlayer.gift_lotus)

	g_AromaView:SetText('x'..to_2dnumber(aroma))
	g_FoodView:SetText('x'..to_2dnumber(food))
	g_LotusView:SetText('x'..to_2dnumber(lotus))

	local button = ConvertToCButtonWidget(g_Game:GetWidget("aroma"))
	if button then button:DisableButton(aroma==0) end
	local button = ConvertToCButtonWidget(g_Game:GetWidget("food"))
	if button then button:DisableButton(food==0) end
	local button = ConvertToCButtonWidget(g_Game:GetWidget("lotus"))
	if button then button:DisableButton(lotus==0) end
end

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


g_Colors = { 	CColor(0, 246, 20), 
		CColor(52, 255, 249),
		CColor(255,239,42),
		CColor(240, 39, 216),
		CColor(255,3,3),
		CColor(255,151,19),
		CColor(51, 83, 255) }

function get_random_color()
	local color = nil
	if g_GameVars.events.combo_info or g_Player:GetLevel()~=2 then color = g_Colors[math.random(g_GameVars.color_num)]
	else color = g_Colors[1] end
	return color
end

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

function generate_shape_distribution(shapes)
	g_GameVars.distribution = {}
	local distrib = 1000 / g_GameVars.shapes_num
	if shapes[1]>0 and table.getn(shapes)>1 and (g_GameVars.easy_lvl2 or g_GameVars.easy_lvl3) then
		-- рассчитываем распределение с упрощением

		local dif_distrib = shapes[1] * distrib

		-- применяем действующие упрощения
		if g_GameVars.easy_lvl2 then
			dif_distrib = dif_distrib + get_number("ease_game", "lvl2_diff_shapes_percent_boost")
		end
		if g_GameVars.easy_lvl3 then
			dif_distrib = dif_distrib + get_number("ease_game", "lvl3_diff_shapes_percent_boost")
		end

		local sim_shapes = g_GameVars.shapes_num - shapes[1]
		local sim_distrib = math.floor( (1000 - dif_distrib) /  sim_shapes )
		
		dif_distrib = math.floor(dif_distrib/shapes[1])

		distrib = sim_distrib

		log("sim shapes = "..sim_shapes)
		log("sim distrib = "..sim_distrib)
		log("dif distrib = "..dif_distrib)

		local cur_distrib = 1
		local shape = 1
		while shape<=g_GameVars.shapes_num do
			g_GameVars.distribution[shape] = {}
			g_GameVars.distribution[shape].shape = g_GameVars.shapes[shape]
			g_GameVars.distribution[shape].min_range = cur_distrib
			g_GameVars.distribution[shape].max_range = cur_distrib + distrib - 1
			cur_distrib  = cur_distrib + distrib

			shape = shape + 1
			if shape>sim_shapes then distrib = dif_distrib end
		end	
	else
		-- рассчитываем равномерное распределение
		distrib = math.floor(distrib)
		local shape = 1
		local cur_distrib = 0
		while shape<=g_GameVars.shapes_num do
			g_GameVars.distribution[shape] = {}
			g_GameVars.distribution[shape].shape = g_GameVars.shapes[shape]
			g_GameVars.distribution[shape].min_range = cur_distrib
			g_GameVars.distribution[shape].max_range = cur_distrib + distrib - 1
			cur_distrib  = cur_distrib + distrib

			shape = shape + 1
		end	

	end

	log("generated:")
	local shape = 1
	while shape<=g_GameVars.shapes_num do
		log("chance "..g_GameVars.distribution[shape].shape.." = "..(g_GameVars.distribution[shape].max_range - g_GameVars.distribution[shape].min_range + 1))
		shape = shape + 1
	end
end

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

function get_random_shape_id()
	local rnd = math.random(1000)
	local size = table.getn(g_GameVars.distribution)
	local i = 1
	while i<=size do
		if rnd>=g_GameVars.distribution[i].min_range and rnd<=g_GameVars.distribution[i].max_range then
			break
		end
		i = i + 1
	end
	if i>size then i = size end
	return g_GameVars.distribution[i].shape
end

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


function add_satisfaction(sat)
	if ((not g_AmuletPlace:IsValidAmulet()) and (sat<0)) or (g_GameVars.freeze_satisfaction) then return end

	g_GameVars.satisfaction = g_GameVars.satisfaction + sat
	if g_GameVars.satisfaction < 0 then g_GameVars.satisfaction = 0 end

	g_SatisfactionView:SetSatisfaction(g_GameVars.satisfaction, false)
	g_GodPlace:SetSatisfaction(g_GameVars.satisfaction)

	event_on_satisfaction_changed()

	local sat = g_GameVars.satisfaction
	if sat > 100 then sat = 100 end
	local val =  g_GameVars.amulet_price*sat/100.0

	val = math.ceil(val / 10)*10
	if val<100 then val = 100 end
	if val>g_GameVars.amulet_price then val = g_GameVars.amulet_price end
	g_GameVars.current_amulet_value = val
	g_CurAmuletValue:SetText(val..'')
end

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

function add_score(score)
	g_Player:SetGlobalScore(g_Player:GetGlobalScore() + score)
	g_GameVars.level_score = g_GameVars.level_score + score

	g_ScoreView:SetText(g_GameVars.level_score..'')
	g_GlobalScoreView:SetText(g_Player:GetGlobalScore().."")

	event_on_score_changed()

	if (not g_GameVars.goal_reached) and g_GameVars.level_score >= g_GameVars.goal_score then
		play_sound("sound_goal_reached")
		g_GameVars.goal_reached = true
		g_ScoreView:SetColor(get_color("score_view", "reach_color"))
		ScoreFlashEffect(g_Game, g_ScoreView)
		
		local from = g_Game:GetAbsPos()
		from.x = from.x + g_Game:GetRect().mWidth/2
		from.y = from.y + g_Game:GetRect().mHeight/2
				
		g_GameVars.goal_show = true
		GoalScoreBonusEffect(g_Game, from, CColor(255, 255, 255, 255), string_table():GetString(190), "font_big_text")		
	end


	if g_AmuletPlace:IsValidAmulet() then 
		g_AmuletPlace:AddScore(score)
	end
end

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

function add_coins(coins)
	g_Player:AddCoins(coins)
	g_GemsView:SetText("x"..g_Player:GetCoins())
end

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

function add_lives(lives)
	g_Player:SetLives(g_Player:GetLives() + lives)
	g_LivesView:SetText(g_Player:GetLives()..'')
end

-------------------------------------------------------------------------------------------------
function play_sound_shape_wrong()
	if g_GameVars.wrong_shape_pause >0 then return end
	g_GameVars.wrong_shape_pause = 300
	play_sound("sound_shape_wrong")
end
