require "brains/fortressbrain"
require "stategraphs/SGfortress"

local assets=
{
	Asset("ANIM", "anim/fortress_build.zip"),
	Asset("SOUND", "sound/chess.fsb"),
}

local prefabs =
{
	"gears",
    "nightmarefuel",
	"trinket_6",
	"fortress_key",
	"knight_nightmare",
	"rook_nightmare",
}

local SLEEP_DIST_FROMHOME = 1
local SLEEP_DIST_FROMTHREAT = 20
local MAX_CHASEAWAY_DIST = 40
local MAX_TARGET_SHARES = 5
local SHARE_TARGET_DIST = 40

local function Retarget(inst)
	local range = 15
	return FindEntity(inst, range, function(guy)
			return inst.components.combat:CanTarget(guy)
		end, nil, {"chess"}, {"character", "monster"})
end

local function KeepTarget(inst, target)
   return target
          and target.components.combat
          and target.components.health
          and not target.components.health:IsDead()
end

local function OnAttacked(inst, data)
    if inst.lightson then
        if data.attacker.components.health then
            if math.random() < 0.33 and (data.weapon == nil or (not data.weapon:HasTag("projectile") and data.weapon.projectile == nil)) and (data.attacker ~= GetPlayer() or (data.attacker == GetPlayer() and not GetPlayer().components.inventory:IsInsulated())) then
                data.attacker.components.health:DoDelta(-5)
                if data.attacker == GetPlayer() then		
					if (IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(REIGN_OF_GIANTS)) then
						data.attacker.sg:GoToState("electrocute")
					end
                end
            end
        end
    end
    local attacker = data and data.attacker
    if attacker and attacker:HasTag("chess") then return end
    inst.components.combat:SetTarget(attacker)
    inst.components.combat:ShareTarget(attacker, 40, function(dude) return dude:HasTag("chess") end, MAX_TARGET_SHARES)
end

local function HitShake()
	TheCamera:Shake("SIDE", 0.5, 0.05, 0.1)
end

local function DoChargeDamage(inst, target)
    if not inst.recentlycharged then
        inst.recentlycharged = {}
    end
    for k,v in pairs(inst.recentlycharged) do
        if v == target then
            return
        end
    end
    inst.recentlycharged[target] = target
    inst:DoTaskInTime(3, function() inst.recentlycharged[target] = nil end)
    inst.components.combat:DoAttack(target, inst.weapon)
    inst.SoundEmitter:PlaySound("dontstarve/creatures/rook/explo") 
end

local function oncollide(inst, other)
    local v1 = Vector3(inst.Physics:GetVelocity())
    if v1:LengthSq() < 1 then return end
    inst:DoTaskInTime(2*FRAMES, function()
        if other and other.components.workable and other.components.workable.workleft > 0 then
            SpawnPrefab("collapse_small").Transform:SetPosition(other:GetPosition():Get())
            other.components.workable:Destroy(inst)
		elseif other and other.components.health and other.components.health:GetPercent() >= 0 and not other:HasTag("chess") and inst.sg:HasStateTag("running") then
			DoChargeDamage(inst, other)	
        end
    end)
end

local function CreateWeapon(inst)
    local weapon = CreateEntity()
    weapon.entity:AddTransform()
    weapon:AddComponent("weapon")
    weapon.components.weapon:SetDamage(250)
    weapon.components.weapon:SetRange(0)
    weapon:AddComponent("inventoryitem")
    weapon.persists = false
    weapon.components.inventoryitem:SetOnDroppedFn(function() weapon:Remove() end)
    weapon:AddComponent("equippable")
    inst.components.inventory:GiveItem(weapon)
    inst.weapon = weapon
end

local function RemoveForcefield(inst, data)
	local pos = inst:GetPosition()
	local ents = TheSim:FindEntities(pos.x, pos.y, pos.z, 20, {"chess"})
	inst.components.health:SetInvincible(false)
	inst:RemoveTag("invincible")
	local fx = FindEntity(inst, 5, function(ent)
		return ent:HasTag("shield")
	end)
	if fx then
		fx.AnimState:PlayAnimation("close")
		fx.components.lighttweener:StartTween(nil, 0, .9, 0.9, nil, .2)
		fx:DoTaskInTime(0.6, function() fx:Remove() end)
	end
	inst:DoTaskInTime(30, function(inst) inst.shielddelay = false end)
end

local function TryForcefield(inst, data)
    local pos = inst:GetPosition()
	if inst.secondphase and not inst.shielddelay then
        if not inst:HasTag("invincible") then
        inst:AddTag("invincible")
        inst.components.health:SetInvincible(true)
        local fx = SpawnPrefab("forcefieldfx")
        fx:AddTag("shield")
        inst.SoundEmitter:PlaySound("dontstarve/common/shadowTentacleAttack_1")
        inst.SoundEmitter:PlaySound("dontstarve/common/shadowTentacleAttack_2")
        fx.entity:SetParent(inst.entity)
        fx.Transform:SetPosition(0, -1.5, 0)
        fx.Transform:SetScale(3, 3, 3)
        end
        local fx_hitanim = function()
            fx.AnimState:PlayAnimation("hit")
            fx.AnimState:PushAnimation("idle_loop")
        end
		inst.shielddelay = true
		inst:DoTaskInTime(8, RemoveForcefield)
    end
end

local function CallGuards(inst, data)
	if not inst.calledguards then
		for k = 1,4 do	
			local pt = Vector3(inst.Transform:GetWorldPosition())
			local theta = math.random() * 2 * PI
			local radius = math.random(20,30)
			local offset = FindWalkableOffset(pt, theta, radius, 12, true)
			if offset then
				local pos = pt + offset
				local ground = GetWorld()
				local tile = GROUND.GRASS
				if ground and ground.Map then
					tile = inst:GetCurrentTileType(pos:Get())
				end
				local guard = SpawnPrefab("knight_nightmare")
				if guard then
					guard.Physics:Teleport(pos:Get())
					inst.components.leader:AddFollower(guard)
					inst.calledguards = true
				end
			end	
		end
	end
end

local function CallRooks(inst, data)
	if not inst.calledrooks then
		for k = 1,3 do	
			local pt = Vector3(inst.Transform:GetWorldPosition())
			local theta = math.random() * 2 * PI
			local radius = math.random(20,30)
			local offset = FindWalkableOffset(pt, theta, radius, 12, true)
			if offset then
				local pos = pt + offset
				local ground = GetWorld()
				local tile = GROUND.GRASS
				if ground and ground.Map then
					tile = inst:GetCurrentTileType(pos:Get())
				end
				local guard = SpawnPrefab("rook_nightmare")
				if guard then
					guard.Physics:Teleport(pos:Get())
					inst.components.leader:AddFollower(guard)
					inst.calledrooks = true
				end
			end	
		end
	end
end

local function LightsOff(inst)
	inst.Light:Enable(false)
	inst.lightson = false
	inst.AnimState:SetBloomEffectHandle("")
end

local function LightsOn(inst)
	inst.Light:Enable(true)
	inst.lightson = true
	inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh")
end

local function CheckTime(inst)
	if GetClock():IsDay() then
		LightsOff(inst)
	elseif GetClock():IsDusk() then
		LightsOff(inst)
	elseif GetClock():IsNight() then
		LightsOn(inst)
	end
end

local function FollowPlayer(inst)
	local init_pos = inst:GetPosition()
	local player_pos = GetPlayer():GetPosition()
	local angle = GetPlayer():GetAngleToPoint(init_pos)
	local offset = FindWalkableOffset(player_pos, angle*DEGREES, 40, 10)
	local pos = player_pos + offset	  
	if pos and distsq(player_pos, init_pos) > 1600 and math.random() > 0.5 then
		if not inst.components.combat:TargetIs(GetPlayer()) then
			inst.components.combat:SetTarget(nil)
			inst.components.combat:SetTarget(GetPlayer())
		end
		inst:DoTaskInTime(.1, function() inst.Transform:SetPosition(pos:Get())end)
	end
end

local function MakeFortress(Sim)
	local inst = CreateEntity()
	local trans = inst.entity:AddTransform()
	local anim = inst.entity:AddAnimState()
	local sound = inst.entity:AddSoundEmitter()
	local shadow = inst.entity:AddDynamicShadow()
	shadow:SetSize(6, 3.5)
    inst.Transform:SetFourFaced()
    inst.Transform:SetScale(1.2, 1.2, 1.2)
    MakeCharacterPhysics(inst, 50, 2.5)
    inst.Physics:SetCollisionCallback(oncollide)
	
	local minimap = inst.entity:AddMiniMapEntity()
	minimap:SetIcon("fortress.tex")

    anim:SetBank("rook")
	anim:SetBuild("fortress_build")

	inst.kind = ""
	inst.soundpath = "dontstarve/creatures/rook/"
	inst.effortsound = "dontstarve/creatures/rook/steam"
	
	local light = inst.entity:AddLight()
    inst.Light:Enable(false)
	inst.Light:SetRadius(4.5)
    inst.Light:SetFalloff(0.7)
    inst.Light:SetIntensity(.65)
    inst.Light:SetColour(223/255, 208/255, 69/255)
	inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh")
    
    inst:AddTag("monster")
    inst:AddTag("hostile")
    inst:AddTag("chess")
    inst:AddTag("fortress")
	inst:AddTag("epic")
    inst:AddTag("scarytoprey")
    inst:AddTag("largecreature")
	inst:AddTag("lightningrod")

    inst:AddComponent("health")
    inst.components.health:SetMaxHealth(5000)
	
	inst:AddComponent("locomotor")
    inst.components.locomotor.walkspeed = 5/1.2
    inst.components.locomotor.runspeed = 14/1.2
	
	inst:AddComponent("sleeper")
    inst.components.sleeper:SetWakeTest(nil)
    inst.components.sleeper:SetSleepTest(nil)
    inst.components.sleeper:SetResistance(3)
	
	inst:AddComponent("combat")
    inst.components.combat.hiteffectsymbol = "spring"
    inst.components.combat:SetRetargetFunction(3, Retarget)
    inst.components.combat:SetKeepTargetFunction(KeepTarget)
    inst.components.combat:SetDefaultDamage(120)
	inst.components.combat.playerdamagepercent = .7
    inst.components.combat:SetAttackPeriod(3)
	inst.components.combat:SetRange(4.5)
	inst.components.combat:SetAreaDamage(4.5,0.33)	
	inst.components.combat:SetHurtSound("dontstarve/creatures/rook/hurt")
	inst.components.combat.notags = {"chess"}

	inst:AddComponent("lootdropper")
	inst.components.lootdropper:SetLoot({"gears","gears","gears","gears","trinket_6","trinket_6","nightmarefuel","fortress_key"})
	inst.components.lootdropper:AddChanceLoot("gears",0.50)
	inst.components.lootdropper:AddChanceLoot("gears",0.50)
		
	if (IsDLCEnabled(CAPY_DLC) or IsDLCEnabled(REIGN_OF_GIANTS)) then
		inst:AddComponent("groundpounder")
		inst.components.groundpounder.destroyer = true
		inst.components.groundpounder.damageRings = 3
		inst.components.groundpounder.destructionRings = 4
		inst.components.groundpounder.numRings = 5
		inst.components.groundpounder.noTags = {"chess"}
	end
			
    inst:AddComponent("follower")
	inst:AddComponent("leader")
    inst:AddComponent("inventory")
    inst:AddComponent("inspectable")
    inst:AddComponent("knownlocations")
    
	local brain = require "brains/fortressbrain"
    inst:SetBrain(brain)
	
    inst:SetStateGraph("SGfortress")
	
	inst:DoPeriodicTask(1, function()
		local target = inst.components.combat.target
		if target then
			local targetPos = Vector3(target.Transform:GetWorldPosition())
			inst.components.knownlocations:RememberLocation("targetpos", targetPos)
		end
		TryForcefield(inst)
		local healthpct = inst.components.health:GetPercent()
		if healthpct < .75 then
			CallGuards(inst)
		end		
		if healthpct < .5 and not inst.secondphase then
			inst.secondphase = true
		end
		if healthpct < .25 then
			CallRooks(inst)
		end	
	end)

    MakeLargeBurnableCharacter(inst, "swap_fire")
    MakeMediumFreezableCharacter(inst, "spring")
    
    inst:ListenForEvent("attacked", OnAttacked)
	inst:ListenForEvent("daytime", function() CheckTime(inst) FollowPlayer(inst) end , GetWorld())
	inst:ListenForEvent("dusktime", function() CheckTime(inst) end , GetWorld())
	inst:ListenForEvent("nighttime", function() CheckTime(inst) end , GetWorld())
	inst:ListenForEvent("lightningstrike", function(inst, data)  inst.components.health:DoDelta(75) end)
	inst.lightningpriority = 15
    CreateWeapon(inst)
	CheckTime(inst)
	
	inst.OnSave = function(inst, data)
		if inst.secondphase then 
			data.secondphase = inst.secondphase
		end
		if inst.calledguards then 
			data.calledguards = inst.calledguards
		end
		if inst.calledrooks then 
			data.calledrooks = inst.calledrooks
		end
    end     
    
    inst.OnLoad = function(inst, data) 
		if data and data.secondphase then
			inst.secondphase = data.secondphase
		end
		if data and data.calledguards then
			inst.calledguards = data.calledguards
		end
		if data and data.calledrooks then
			inst.calledrooks = data.calledrooks
		end
    end
    return inst
end

STRINGS.NAMES.FORTRESS = "Fortress"
STRINGS.CHARACTERS.GENERIC.DESCRIBE.FORTRESS = "An iron giant!"

return Prefab("chessboard/fortress", MakeFortress, assets, prefabs)