
local ns = artifact

local db = minetest.get_mod_storage()

function ns.apply_key(m)
    m.object:set_properties {
        textures = {"artifact_key.png"},
        visual_size = vector.new(1,1,1) *0.88,
        eye_height = 1.6
    }
    m.eye_height = 1.6
    -- Switch hand appearance.
    m.inv:set_stack("main", 1, ItemStack("input_"..m.character))
    if m.healthbar then
        m.object:hud_change(m.healthbar, "text", "artifact_heart.png")
    end
    m.blackrod = minetest.add_entity(m.object:get_pos(), "display")
    m.blackrod:set_properties {
        visual = "mesh",
        mesh = "artifact_blackrod.obj",
        textures = {"artifact_blackrod.png"},
        visual_size = vector.new(1,1,1) *10
    }
    m.blackrod:set_attach(m.object, "RightArm", vector.new(0.25, -5.5, 0), vector.new(90,0,0))
end

function ns.apply_vix(m)
    m.object:set_properties {
        textures = {"artifact_vix.png"},
        visual_size = vector.new(1,1,1) *0.8,
        eye_height = 1.5
    }
    m.eye_height = 1.5
    -- Switch hand appearance.
    m.inv:set_stack("main", 1, ItemStack("input_"..m.character))
    m.object:hud_change(m.healthbar, "text", "artifact_heart_vix.png")
    if m.blackrod then
        m.blackrod:remove()
        m.blackrod = nil
    end
end

function ns._swap_character(m)
    -- If Key was pointing at something Vix shouldn't be able to interact with,
    -- but Vix is also pointing at it, then remove the interaction marker since
    -- it would be misleading.
    if m.pointed_obj and m.interaction_marker and (not m.pointed_obj._can_interact or m.pointed_obj:_can_interact(m)) then
        m.object:hud_remove(m.interaction_marker)
        m.interaction_marker = nil
        m.interaction_start = nil
    end
    
    if m.character == "vix" then
        artifact.sidekick.character = "vix"
        m:set_character("key")
        ns.apply_key(m)
    else
        artifact.sidekick.character = "key"
        m:set_character("vix")
        ns.apply_vix(m)
    end
    
    -- If Vix was pointing at something whackable, and the player then switches
    -- to Key who is pointing at the same thing, we should show the whack icon.
    if m.character == "key" and m.pointed_node and minetest.registered_nodes[m.pointed_node.node_under.name].groups.whackable then
        m.whack_hud = m.object:hud_add {
            type = "image_waypoint",
            world_pos = m.pointed_node.under,
            scale = {x=3,y=3},
            text = "artifact_icon_whack.png"
        }
    end
    
    -- We don't need to have the sidekick entity during testing.
    if artifact.sidekick.pos or not artifact.debug then
        -- `m.pos` includes eye_height, and we don't want that here.
        local pos = m.object:get_pos()
        local yaw = m.yaw
        local pitch = m.pitch
        if not artifact.sidekick.pos then
            artifact.sidekick.pos = pos
        end
        m.object:set_pos(artifact.sidekick.pos)
        m.object:set_look_horizontal(artifact.sidekick.yaw)
        m.object:set_look_vertical(artifact.sidekick.pitch)
        if not artifact.sidekick.ref then
            minetest.add_entity(pos, "artifact:sidekick")
        end
        artifact.sidekick.ref.object:set_pos(pos)
        artifact.sidekick.ref.object:set_yaw(yaw)
        artifact.sidekick.ref.object:set_bone_override("Head", m.object:get_bone_override("Head"))
        artifact.sidekick.ref.object:set_bone_override("root", m.object:get_bone_override("root"))
        artifact.sidekick.pos = pos
        artifact.sidekick.yaw = yaw
        artifact.sidekick.pitch = pitch

        local e = artifact.sidekick.ref
        if artifact.sidekick.character == "vix" then
            artifact.sidekick.ref.object:set_properties {
                textures = {"artifact_vix.png"},
                visual_size = vector.new(1,1,1) *0.8
            }
            if e.blackrod then
                e.blackrod:remove()
                e.blackrod = nil
            end
        else
            e.object:set_properties {
                textures = {"artifact_key.png"},
                visual_size = vector.new(1,1,1) *0.88
            }
            e.blackrod = minetest.add_entity(e.object:get_pos(), "display")
            e.blackrod:set_properties {
                visual = "mesh",
                mesh = "artifact_blackrod.obj",
                textures = {"artifact_blackrod.png"},
                visual_size = vector.new(1,1,1) *10
            }
            e.blackrod:set_attach(e.object, "RightArm", vector.new(0.25, -5.5, 0), vector.new(90,0,0))
        end
    end
    artifact.sidekick.save()
end

function ns.swap_character(m)
    if m._swapping_character then return end
    if artifact.sidekick.ref then
        artifact.sidekick.ref._no_interact = true
    end
    m._swapping_character = true
    local fade = artifact.hud_add(m, {
        type = "image",
        pos = {x=0.5,y=1},
        offset = {x=0,y=0},
        align = {x=0,y=0},
        scale = {x=50000,y=50000},
        opacity = 0,
        image = "[fill:1x1:0,0:#000",
    })
    fade:animate {
        opacity = {
            value = 256,
            duration = 0.3
        }
    }
    artifact.play_sound {
        name = "artifact_character_swap",
        to_player = m.name
    }
    minetest.after(0.3, function()
        ns._swap_character(m)
        fade:animate {
            opacity = {
                value = 0,
                duration = 0.3
            }
        }
        minetest.after(0.3, function()
            m._swapping_character = nil
            if artifact.sidekick.ref then
                artifact.sidekick.ref._no_interact = nil
            end
        end)
    end)
    
end



include "key.lua"
include "vix.lua"

artifact.sidekick = setmetatable(minetest.deserialize(db:get("sidekick") or "return nil") or {
    pitch = 0,
    yaw = 0,
    character = "vix",
}, {
    __index = {
        save = function()
            local ref = artifact.sidekick.ref
            -- Temporarily erase the entity so we can serialize properly.
            artifact.sidekick.ref = nil
            db:set_string("sidekick", minetest.serialize(artifact.sidekick))
            artifact.sidekick.ref = ref
        end
    }
})

minetest.register_entity(":artifact:sidekick", {
    initial_properties = {
        visual = "mesh",
        mesh = "artifact_character.gltf",
        textures = {"artifact_vix.png"},
        visual_size = vector.new(1,1,1) *0.8,
        physical = true,
        collisionbox = {
            -0.3, 0,-0.3,
            0.3, 1.77, 0.3
        },
        collide_with_objects = false
    },
    _interact_marker_offset = function() return vector.new(0, 1.1,0) end,
    _interact_time = 0.4,
    on_activate = function(e, data)
        if data then
            extend(e, minetest.deserialize(data) or {})
        end
        if artifact.sidekick.character == "key" then
            e.object:set_properties {
                textures = {"artifact_key.png"},
                visual_size = vector.new(1,1,1) *0.88
            }
        end
        -- Gravity.
        e.object:set_acceleration(vector.new(0,-9.81,0))
        -- Make quite sure that we only ever have one of these.
        -- Remove this one because the first one we spawned is _probably_ the
        -- right one (e.g. if someone got unloaded, then loaded again after
        -- a replacement was spawned).
        if artifact.sidekick.ref then
            e.object:remove()
            return
        end
        artifact.sidekick.pos = e.object:get_pos()
        artifact.sidekick.ref = e
    end,
    on_deactivate = function(e)
        artifact.sidekick.ref = nil
    end,
    get_staticdata = function(e)
        return minetest.serialize{
            cahracter = e.character
        }
    end,
    -- We need this to ensure that the stored position takes gravity into account.
    on_step = function(e)
        artifact.sidekick.pos = e.object:get_pos()
    end,
    on_interact = function(e, m)
        ns.swap_character(m)
    end
})

