local assets=
{
	Asset("ANIM", "anim/rakkyboomerang.zip"),
	Asset("ANIM", "anim/rakkyboomerang_carrot.zip"),
	Asset("ANIM", "anim/rakkyboomerang_feather.zip"),
	Asset("ANIM", "anim/rakkyboomerang_bat.zip"),
	Asset("ANIM", "anim/rakkyboomerang_ruins.zip"),
	Asset("ANIM", "anim/rakkyboomerang_subclimax.zip"),
	Asset("ANIM", "anim/rakkyboomerang_climax.zip"),
	Asset("ANIM", "anim/rakkyboomerang_sleep.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_carrot.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_feather.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_bat.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_ruins.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_subclimax.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_climax.zip"),
	Asset("ANIM", "anim/swap_rakkyboomerang_sleep.zip"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_carrot.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_feather.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_bat.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_ruins.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_subclimax.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_climax.xml"),
	Asset("ATLAS", "images/inventoryimages/rakkyboomerang_sleep.xml")
}

    
local prefabs =
{
}

-- Sets Catastrorang build to subclimax phase
local function setSubclimaxImage(inst)
	inst.AnimState:SetBuild("rakkyboomerang_subclimax")
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_subclimax.xml"
	inst.components.inventoryitem:ChangeImageName("rakkyboomerang_subclimax")    	
end

-- Sets equipped item build of Catastrorang to the appropriate phase
local function setSwap(inst,owner)
    local cattracker = inst.components.catastrophetracker
	local swap = "swap_rakkyboomerang_ruins"
	if cattracker then
		if cattracker:IsCalm() then
		elseif cattracker:IsClimax() then
			swap = "swap_rakkyboomerang_climax"
		else
			swap = "swap_rakkyboomerang_subclimax"
		end
	end
    owner.AnimState:OverrideSymbol("swap_object", swap, "swap_boomerang")
end
local states =
{
    calm = function(inst)
		inst.AnimState:SetBuild("rakkyboomerang_ruins")
		inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_ruins.xml"
		inst.components.inventoryitem:ChangeImageName("rakkyboomerang_ruins")    	
    end,

    warn = function(inst)
		setSubclimaxImage(inst) 
    end,

    climax = function(inst)
		inst.AnimState:SetBuild("rakkyboomerang_climax")
		inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_climax.xml"
		inst.components.inventoryitem:ChangeImageName("rakkyboomerang_climax")    	  
    end,

    cool = function(inst)
		setSubclimaxImage(inst)   
    end,
	
	subclimax = function(inst)
		setSubclimaxImage(inst) 
    end,
}

-- Change the appearance of the Catastrorang
local function phasechange(inst, data)
    local statefn = states[data.newphase]

    if statefn then
        statefn(inst)
    end
	local equippable = inst.components.equippable
	if equippable and equippable:IsEquipped() then
		local owner = inst.components.inventoryitem and inst.components.inventoryitem.owner
		if owner then
			setSwap(inst,owner)
		end
	end
end

-- Remove the boomerang once used up
local function OnFinished(inst)
    inst.AnimState:PlayAnimation("used")
    inst:ListenForEvent("animover", function() inst:Remove() end)
end

-- Updates the damage of boomerangs with dynamic damage values
local function UpdateDamage(inst)
	if inst.components.weapon then
		local dmg
		if inst.components.perishable then
			-- Set damage of the Carrotrang
			dmg = TUNING.SPEAR_DAMAGE*1.5 * inst.components.perishable:GetPercent()
			dmg = Remap(dmg, 0, TUNING.SPEAR_DAMAGE*1.5, TUNING.SPEAR_DAMAGE*1.5*0.5, TUNING.SPEAR_DAMAGE*1.5)
			inst.components.weapon:SetDamage(dmg)
		elseif inst.components.catastrophetracker then
			-- Set damage of the Catastrorang
			dmg = TUNING.SPEAR_DAMAGE*3 * inst.components.catastrophetracker:GetCatastrorangMultiplier()
			inst.components.weapon:SetDamage(dmg)
		end
	end
end

-- Equip functions
local function OnEquip(inst, owner) 
    owner.AnimState:OverrideSymbol("swap_object", "swap_rakkyboomerang", "swap_boomerang")
    owner.AnimState:Show("ARM_carry") 
    owner.AnimState:Hide("ARM_normal") 
end
local function OnEquip_carrot(inst, owner) 
	UpdateDamage(inst)
    owner.AnimState:OverrideSymbol("swap_object", "swap_rakkyboomerang_carrot", "swap_boomerang")
    owner.AnimState:Show("ARM_carry") 
    owner.AnimState:Hide("ARM_normal") 
end
local function OnEquip_sleep(inst, owner) 
    owner.AnimState:OverrideSymbol("swap_object", "swap_rakkyboomerang_sleep", "swap_boomerang")
    owner.AnimState:Show("ARM_carry") 
    owner.AnimState:Hide("ARM_normal") 
end
local function OnEquip_bat(inst, owner) 
    owner.AnimState:OverrideSymbol("swap_object", "swap_rakkyboomerang_bat", "swap_boomerang")
    owner.AnimState:Show("ARM_carry") 
    owner.AnimState:Hide("ARM_normal") 
end
local function OnEquip_feather(inst, owner) 
    owner.AnimState:OverrideSymbol("swap_object", "swap_rakkyboomerang_feather", "swap_boomerang")
    owner.AnimState:Show("ARM_carry") 
    owner.AnimState:Hide("ARM_normal") 
end
local function OnEquip_ruins(inst, owner) 
	UpdateDamage(inst)
	setSwap(inst,owner)
    owner.AnimState:Show("ARM_carry") 
    owner.AnimState:Hide("ARM_normal") 
end

local function OnDropped(inst)
    inst.AnimState:PlayAnimation("idle")
end

local function OnUnequip(inst, owner) 
	UpdateDamage(inst)
    owner.AnimState:Hide("ARM_carry") 
    owner.AnimState:Show("ARM_normal") 
end

local function OnThrown(inst, owner, target)
    if target ~= owner then
        owner.SoundEmitter:PlaySound("dontstarve/wilson/boomerang_throw")
    end
    inst.AnimState:PlayAnimation("spin_loop", true)
end

local function OnCaught(inst, catcher)
    if catcher then
        if catcher.components.inventory then
            if inst.components.equippable and not catcher.components.inventory:GetEquippedItem(inst.components.equippable.equipslot) then
				catcher.components.inventory:Equip(inst)
			else
                catcher.components.inventory:GiveItem(inst)
            end
            catcher:PushEvent("catch")
        end
    end
end

local function ReturnToOwner(inst, owner)
    if owner and not (inst.components.finiteuses and inst.components.finiteuses:GetUses() < 1) then
        owner.SoundEmitter:PlaySound("dontstarve/wilson/boomerang_return")
        inst.components.projectile:Throw(owner, owner)
    end
end

local function OnHit(inst, owner, target)
    if owner == target or owner:HasTag("playerghost") then
        OnDropped(inst)
    else
        ReturnToOwner(inst, owner)
    end
    local impactfx = SpawnPrefab("impact")
    if impactfx then
	    local follower = impactfx.entity:AddFollower()
	    follower:FollowSymbol(target.GUID, target.components.combat.hiteffectsymbol, 0, 0, 0 )
        impactfx:FacePoint(inst.Transform:GetWorldPosition())
    end
end

local function OnLoad(inst, data)
    UpdateDamage(inst)
end

-- Get status of Catastrorang for dynamic inspection responses
local function GetStatus(inst)
    local cattracker = inst.components.catastrophetracker
    if cattracker then
		if cattracker:IsClimax() then
            return "CLIMAX"
        elseif cattracker:IsCool() then
            return "COOL"
        elseif cattracker:IsWarn() then
            return "WARN"
        elseif cattracker:IsSubclimax() then
            return "SUBCLIMAX"
        end
    end

    return "CALM"
end

-- Batarang attack function
local function onattack(inst, owner, target)
    if owner.components.health and owner.components.health:GetPercent() < 1 and not target:HasTag("wall") then
        owner.components.health:DoDelta(TUNING.BATBAT_DRAIN,false,"batbat")
        owner.components.sanity:DoDelta(-TUNING.BATBAT_DRAIN * 0.5)
    end
end
-- Catastrorang attack function
local function onattack_ruins(inst, owner, target)
	UpdateDamage(inst)
	local catastrophetracker = inst.components.catastrophetracker
	if catastrophetracker and target:HasTag("epic") then
		-- Force subclimax phase if attacking a giant
		catastrophetracker:EnterSubclimax(30)
	end
end

local function fn(Sim)
	local inst = CreateEntity()
	inst.entity:AddTransform()
	inst.entity:AddAnimState()
    inst.entity:AddNetwork()
	
    MakeInventoryPhysics(inst)
    RemovePhysicsColliders(inst)
	
	-- Tag this so that I can add a response to being hit with any of Rakky's boomerangs
    inst:AddTag("rakkyboomerang")
	
	-- Proceeds only if host
    if not TheWorld.ismastersim then
        return inst
    end
	
    inst.entity:SetPristine()
    
    inst:AddTag("projectile")
    inst:AddTag("thrown")
    
    inst:AddComponent("weapon")
	inst.components.weapon:SetDamage(TUNING.SPEAR_DAMAGE)
	inst.components.weapon:SetRange(TUNING.BOOMERANG_DISTANCE, TUNING.BOOMERANG_DISTANCE+2)
    -------

    inst:AddComponent("inspectable")
    
    inst:AddComponent("projectile")
    inst.components.projectile:SetSpeed(10)
    inst.components.projectile:SetCanCatch(true)
    inst.components.projectile:SetOnThrownFn(OnThrown)
    inst.components.projectile:SetOnHitFn(OnHit)
    inst.components.projectile:SetOnMissFn(ReturnToOwner)
    inst.components.projectile:SetOnCaughtFn(OnCaught)
    
    inst:AddComponent("inventoryitem")
    inst.components.inventoryitem:SetOnDroppedFn(OnDropped)
    
    inst:AddComponent("equippable")
    inst.components.equippable:SetOnUnequip(OnUnequip)
	
	
    MakeHauntableLaunch(inst)
    AddHauntableCustomReaction(inst, function(inst, haunter)
        local target = FindEntity(inst, 25, function(guy)
            return not guy:HasTag("playerghost") and guy.components.combat
        end)

        if target and math.random() <= TUNING.HAUNT_CHANCE_HALF then
            inst.components.projectile:Throw(haunter, target, haunter)
            inst.components.hauntable.hauntvalue = TUNING.HAUNT_SMALL
            return true
        end
        return false
    end, true, false, true)
    
    
    return inst
end

local function fn_adventure(Sim)
	local inst = fn(Sim)
	
	-- Changes the hit sound to sound sharp. Also makes Zinnia insane.
    inst:AddTag("sharp")
	
	-- Proceeds only if host
	if not TheWorld.ismastersim then
        return inst
    end
	
	inst.AnimState:SetBank("boomerang")
    inst.AnimState:SetBuild("rakkyboomerang")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetRayTestOnBB(true)
	
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang.xml"
	
    inst.components.equippable:SetOnEquip(OnEquip)
	
	inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(TUNING.BOOMERANG_USES*10)
    inst.components.finiteuses:SetUses(TUNING.BOOMERANG_USES*10)
    
    inst.components.finiteuses:SetOnFinished(OnFinished)
	
    return inst
end

local function fn_carrot(Sim)
	local inst = fn(Sim)
	
	inst:AddTag("show_spoilage")
    inst:AddTag("icebox_valid")
    inst:AddTag("show_basic_spoilage") -- Tag is used to use normal spoilage meter.
	
	-- Proceeds only if host
	if not TheWorld.ismastersim then
        return inst
    end
	
	inst.AnimState:SetBank("boomerang")
    inst.AnimState:SetBuild("rakkyboomerang_carrot")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetRayTestOnBB(true)
	
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_carrot.xml"
	
    inst.components.equippable:SetOnEquip(OnEquip_carrot)
	
	-- Attracts rabbits
	inst:AddComponent("bait")
	
	inst:AddComponent("perishable")
	inst.components.perishable:SetPerishTime(TUNING.PERISH_MED)
	inst.components.perishable:StartPerishing()
	inst.components.perishable.onperishreplacement = "spoiled_food"
	
    inst.components.weapon:SetDamage(TUNING.SPEAR_DAMAGE*1.5)
    inst.components.weapon:SetOnAttack(UpdateDamage)
	
    MakeHauntableLaunchAndPerish(inst)
	
	-- Set this as something edible to non-human vegetarians, such as rabbits
	inst:AddComponent("edible")
	inst.components.edible.foodtype = FOODTYPE.VEGGIEHORRIBLE
	inst:AddTag("edible_"..FOODTYPE.VEGGIEHORRIBLE)
	
    inst.OnLoad = OnLoad
	
    return inst
end
local function fn_feather(Sim)
	local inst = fn(Sim)
	
	-- Changes the hit sound to sound sharp. Also makes Zinnia insane.
    inst:AddTag("sharp")
	
	-- Proceeds only if host
	if not TheWorld.ismastersim then
        return inst
    end
	
	inst.AnimState:SetBank("boomerang")
    inst.AnimState:SetBuild("rakkyboomerang_feather")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetRayTestOnBB(true)
	
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_feather.xml"
	
    inst.components.equippable:SetOnEquip(OnEquip_feather)
	
	inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(TUNING.BOOMERANG_USES*10)
    inst.components.finiteuses:SetUses(TUNING.BOOMERANG_USES*10)
    
    inst.components.finiteuses:SetOnFinished(OnFinished)
	
	-- Make it faster
    inst.components.projectile:SetSpeed(15)
	
    return inst
end


local function sleepattack(inst, attacker, target)
    if target.components.sleeper and not (inst.components.freezable and inst.components.freezable:IsFrozen() ) then
        --target.SoundEmitter:PlaySound("dontstarve/wilson/blowdart_impact_sleep")
        target.components.sleeper:AddSleepiness(1, 25)
        if target.components.combat then
            target.components.combat:SuggestTarget(attacker)
        end
        if target.sg and not target.sg:HasStateTag("sleeping") and target.sg.sg.states.hit then
            target.sg:GoToState("hit")
        end
    elseif target.components.grogginess and not (inst.components.freezable and inst.components.freezable:IsFrozen()) then
        --target.SoundEmitter:PlaySound("dontstarve/wilson/blowdart_impact_sleep")
        target:PushEvent("attacked", {attacker = attacker, damage = 0, weapon = inst})
        target.components.grogginess:AddGrogginess(1, 15)
    end
end
local function fn_sleep(Sim)
	local inst = fn(Sim)
	
	-- Proceeds only if host
	if not TheWorld.ismastersim then
        return inst
    end
	
	inst.AnimState:SetBank("boomerang")
    inst.AnimState:SetBuild("rakkyboomerang_sleep")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetRayTestOnBB(true)
	
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_sleep.xml"
	
    inst.components.equippable:SetOnEquip(OnEquip_sleep)
	
	inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(TUNING.BOOMERANG_USES*5)
    inst.components.finiteuses:SetUses(TUNING.BOOMERANG_USES*5)
    
    inst.components.finiteuses:SetOnFinished(OnFinished)
	
	-- Deals no damage
    inst.components.weapon:SetDamage(0)
	inst.components.weapon:SetOnAttack(sleepattack)
	
	-- Make a bit slower
    inst.components.projectile:SetSpeed(8)
	
	AddHauntableCustomReaction(inst, function(inst, haunter)
        local target = FindEntity(inst, 25, function(guy)
            return not guy:HasTag("playerghost") and guy.components.sleeper
        end)

        if target and math.random() <= TUNING.HAUNT_CHANCE_HALF then
            inst.components.projectile:Throw(haunter, target, haunter)
            inst.components.hauntable.hauntvalue = TUNING.HAUNT_SMALL
            return true
        end
        return false
    end, true, false, true)
	
    return inst
end

local function fn_bat(Sim)
	local inst = fn(Sim)
	
	-- Proceeds only if host
	if not TheWorld.ismastersim then
        return inst
    end
	
	inst.AnimState:SetBank("boomerang")
    inst.AnimState:SetBuild("rakkyboomerang_bat")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetRayTestOnBB(true)
	
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_bat.xml"
	
    inst.components.equippable:SetOnEquip(OnEquip_bat)
	
	inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(TUNING.BOOMERANG_USES*10)
    inst.components.finiteuses:SetUses(TUNING.BOOMERANG_USES*10)
    
    inst.components.finiteuses:SetOnFinished(OnFinished)
	
	-- Set custom onattack function
    inst.components.weapon.onattack = onattack
	
    return inst
end
local function fn_ruins(Sim)
	local inst = fn(Sim)
	
	-- Proceeds only if host
	if not TheWorld.ismastersim then
        return inst
    end
	
	inst.AnimState:SetBank("boomerang")
    inst.AnimState:SetBuild("rakkyboomerang_ruins")
    inst.AnimState:PlayAnimation("idle")
    inst.AnimState:SetRayTestOnBB(true)
	
	inst.components.inventoryitem.atlasname = "images/inventoryimages/rakkyboomerang_ruins.xml"
	
    inst.components.equippable:SetOnEquip(OnEquip_ruins)
	
	inst:AddComponent("finiteuses")
    inst.components.finiteuses:SetMaxUses(TUNING.BOOMERANG_USES*20)
    inst.components.finiteuses:SetUses(TUNING.BOOMERANG_USES*20)

    inst.components.finiteuses:SetOnFinished(OnFinished)
	
	-- Add catastrophe tracker, to track events such as hounds, giants, and earthquakes
	inst:AddComponent("catastrophetracker")
	local catastrophetracker = inst.components.catastrophetracker
	catastrophetracker:AddHoundedTracker()
	--[[
	catastrophetracker:AddBaseHasslerTracker()
	catastrophetracker:AddQuakerTracker()
	if IsDLCEnabled(REIGN_OF_GIANTS) then
		catastrophetracker:AddMooseEggTracker()
	end
	]]
	inst.components.weapon:SetDamage(TUNING.SPEAR_DAMAGE)
    inst.components.weapon:SetOnAttack(onattack_ruins)
    UpdateDamage(inst)
	
    inst.OnLoad = OnLoad
	
	-- Set dynamic inspectable responses based on phase
	inst.components.inspectable.getstatus = GetStatus
	
	-- Change build if event is pushed by catastrophe tracker
    inst:ListenForEvent("catastrophe_phase_change", phasechange)
	-- Change build if event is pushed by nightmare clock
	inst:ListenForEvent("phasechange", function(world, data) inst:DoTaskInTime(math.random() * 2, function()catastrophetracker:NewPhase()end) end, TheWorld)
	
        
    return inst
end

return Prefab( "common/inventory/rakkyboomerang", fn_adventure, assets), 
Prefab( "common/inventory/rakkyboomerang_carrot", fn_carrot, assets), 
Prefab( "common/inventory/rakkyboomerang_feather", fn_feather, assets), 
Prefab( "common/inventory/rakkyboomerang_sleep", fn_sleep, assets), 
Prefab( "common/inventory/rakkyboomerang_bat", fn_bat, assets), 
Prefab( "common/inventory/rakkyboomerang_ruins", fn_ruins, assets)
