--randomize math.random

math.randomseed(os.time())
bb_player = {}
local storage = minetest.get_mod_storage()



--settings
bb_player.spawn = {x=10,y=10,z=10}
bb_player.killdist = 60 -- no map may be bigger than this distance away from the origin or players will die
bb_player.wear_amt = 300 -- amount of wear to add to bad powerups each globalstep
bb_player.debug = false
-- bb_player.default_speed = .5-- the speed players start at
-- bb_player.speed_boost = .2 -- the speed ech speed boost adds
-- bb_player.speed_snail = .25

-- storage
bb_player.infohuds = {}
bb_player.powerup_huds = {}
bb_player.placebombkeys = minetest.deserialize(storage:get_string("placebombkeys")) or {}

-- api tables
bb_player.registered_before_dieplayers = {}
bb_player.registered_on_dieplayers = {}
-- the current (modifiable) speed of each player, indexed by p_name
bb_player.speeds = {}


local path = minetest.get_modpath("bb_player")
dofile(path.."/api.lua")




-- override the default hand item for shorter reach( so you have to approach a bomb inorder to punch it )



minetest.register_item(":", {
	type = "none",
    range = 1.0,
    groups = {not_in_creative_inventory=1},
})

-- player meta:
-- players can have the following metadata keys:

-- status: can be 
--      "inactive"- they just joined and are waiting for the round to be over. or they have finished a roud and are waiting for some reason
--      "admin" - they are in admin state and cannot play but are not limited as players are
--      "playing" - in-game
--      "dead" - in game but dead and waiting to possibly be respawned if that occurs



--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- attachment ent

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



minetest.register_entity("bb_player:att",{
    initial_properties = {
        physical = false,
        use_texture_alpha = true,        
        collisionbox = {-0.01, -0.01, -0.01, 0.01, 0.01, 0.01},
        visual = "sprite",
        textures = {"blank.png"},
        visual_size = {x = 0.01, y = 0.01, z = 0.01},
        static_save = false,
    },
    on_step = function(self, dtime, moveresult)
        
        self._timer = self._timer + dtime
        if self._timer > 300 then
            self.object:remove()
        end
    
    end,

    _timer = 0,

})






--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- allow admins to become admins and regular players again

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

minetest.register_privilege( "admin" , "allows becoming an admin user" )
minetest.register_privilege( "additem" , "allows adding items at yourpostion for testing purposes" )

minetest.register_chatcommand( "admin" , {
    privs = {
        admin = true,
    },
    description = " /admin args: up = become admin user, down = become regular player",
    func = function( name , param )
        local player = minetest.get_player_by_name( name ) 
        if not player then return end
        if param == "up" then
            local pmeta = player:get_meta()
            pmeta:set_string("status", "admin")  
            player:set_physics_override({speed = 1, jump = 1 ,gravity = 70,sneak=false})
        end
        if param == "down" then
            local pmeta = player:get_meta()
            pmeta:set_string("status", "inactive")  
            player:set_pos(bb_player.spawn)
            player:set_physics_override({speed = 0, jump = 0 ,gravity = 0,sneak=false})
        end

    end,
})



minetest.register_chatcommand( "place" , {
    privs = {
        interact = true,
    },
    description = "/place jump = use jump to place bomb /place sneak = use sneak to place bomb (default). Your choice is remembered.",
    func = function( name , param )
        local player = minetest.get_player_by_name( name ) 
        if not player then return end
        if param ~= "jump" and param ~= "sneak" and param ~= "space" and param ~= "shift" then return "[place] Invalid Input" end
        if param == "jump" or param == "space" then bb_player.placebombkeys[name] = "jump" end
        if param == "sneak" or param == "shift" then bb_player.placebombkeys[name] = "sneak" end
        storage:set_string("placebombkeys",minetest.serialize(bb_player.placebombkeys))
        minetest.chat_send_player(name, minetest.colorize("#f47e1b","[!] Your bomb place key is now set to "..bb_player.placebombkeys[name]))

    end,
})




--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- joinplayer

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


minetest.register_on_joinplayer(function(player)
    
    local p_name = player:get_player_name()

    -- set position to the spawn pos
    player:set_pos(bb_player.spawn)

    
    if not bb_player.placebombkeys[p_name] then 
        bb_player.placebombkeys[p_name] = "sneak"
        storage:set_string("placebombkeys",minetest.serialize(bb_player.placebombkeys))
    end

    -- remove this in future versions, its a warning that the placebomb key has changed.
    --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    minetest.chat_send_player(p_name,minetest.colorize("#f47e1b","[!] ATTN: the new default placebomb key is [sneak]. You can change it back to [jump] with `/place jump`. Your settings are remembered. GUI control coming soon (tm)"))
    --%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    local obj = minetest.add_entity(bb_player.spawn, "bb_player:att")
    player:set_attach(obj)
    -- remove the wielditem and healthbar
    player:hud_set_flags({wielditem=false,healthbar=false,crosshair=false,breathbar=false,minimap=true})
    -- dont allow them to move
    player:set_physics_override({speed = 0, jump = 0 ,gravity = 0,sneak=false})
    --heal fall damage
    player:set_armor_groups({fall_damage_add_percent=-100})
    -- set their status to inactive
    local pmeta = player:get_meta()
    pmeta:set_string("status", "inactive") 
    -- clear their inv
    bb_player.clear_inv(player)

    --start theme
    bb_player.loop_theme("theme",41.1,p_name)



    -- set player privs
    local p_privs = minetest.get_player_privs(p_name)
    if not (p_privs.admin or p_privs.server) then
        p_privs = {
            shout = true,
            interact = true,
        }
    end
    minetest.set_player_privs(p_name, p_privs)

    -- set player skybox
    player:set_sky({
        base_color = "#000000",
        type = "skybox",
        textures = {"Up.jpg", "Down.jpg", "Front.jpg", "Back.jpg", "Right.jpg", "Left.jpg"},
        clouds = false
    })

    player:set_moon({visible = false})
    player:set_sun({visible = false, sunrise_visible = false})

    -- set player hotbar
    player:hud_set_hotbar_selected_image("blank.png")
    player:hud_set_hotbar_image("bb_player_gui_hotbar_2.png")
    player:hud_set_hotbar_itemcount(2)
   

    local status = bb_loop.get_hud_status()

    -- set up the info hud (status and timer/suddendeath)
    bb_player.infohuds[p_name] = player:hud_add({
        hud_elem_type   = "text",
        number          = 0xE6482E,
        position        = { x = .97, y = .03},
        offset          = {x = 0,   y = 0},
        text            = status,
        alignment       = {x = -1, y = 1},  -- change y later!
        scale           = {x = 100, y = 100}, -- covered later
        size            = {x = 2 },
    })

end)






        

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- controls

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function bb_player.try_to_place_bomb(player)

    --get our vars
    if bb_loop.gamestate ~= "running" then return end
	if not player then return end
    local p_name = player:get_player_name()
    local inv = player:get_inventory()
    local pmeta = player:get_meta()
    local p_status = pmeta:get_string("status")
    if p_status ~= "playing" then return end
    local pos = vector.add(player:get_pos(),vector.new(0,.5,0))
    
    
    -- booleans
    -- note: bomb throwing is handled in bomb code
    local can_placebomb = false
    if inv:contains_item("main", "bb_powerup:extra_bomb") and not inv:contains_item("main", "bb_powerup:no_placebomb") then
        can_placebomb = true
    end

    -- if there is a bomb that the player has just placed (they are standing on it) and they have throw ability, then throw it

    local near_bomb = false
    local pos1 = vector.add(pos, vector.new(-.5,0,-.5))
    local pos2 = vector.add(pos, vector.new(.5,0,.5))

    local objs = minetest.get_objects_in_area(pos1, pos2)
    for k,v in ipairs(objs) do
        if v and not ( v:is_player() ) and v:get_luaentity() and v:get_luaentity().name == "bb_bomb:bomb" then
            near_bomb = true
        end
    end


    if can_placebomb and near_bomb ~= true then

        -- place bomb, remove bomb item from inv
        local power = bb_player.get_count_in_inv(player,"bb_powerup:extra_power") 


        if bb_player.debug == true then
            minetest.log("error","power: "..power)
        end


        local multidir = false
        if inv:contains_item("main", ItemStack('bb_powerup:multidir')) then
            multidir = true
            for k,v in pairs(inv:get_list("main")) do 
                if v:get_name() == 'bb_powerup:multidir' then
                    
                    v:add_wear((65535/3)+3)
                    inv:set_stack("main",k,v)
                end
            end
        end


        

        local staticdata = minetest.write_json({
            _power = power,
            _owner = p_name,
            _multidir = multidir,
        })

        minetest.sound_play({
            name = "sfx_sounds_impact4",
            gain = 0.6,
        }, {
            pos = player:get_pos(),
            max_hear_distance = 10,  
        }, true)

        minetest.add_entity(vector.round(pos), "bb_bomb:bomb", staticdata)

        inv:remove_item("main", "bb_powerup:extra_bomb")

    end

end


-- place bomb if no bomb very near and when pressed jump

controls.register_on_press(function(player, control_name)
    local placekey = bb_player.placebombkeys[player:get_player_name()]
    if control_name ~= placekey then return end
    bb_player.try_to_place_bomb(player)
end)





--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- globalstep (player handler)

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- set player speed based on inventory contents
-- check for near explosions and kill player


minetest.register_globalstep(function(dtime)
    -- we are only concerned with the running gamestate
    -- iter through all players, but only affect alive and playing players
    local status = bb_loop.get_hud_status()
    for _, player in pairs(minetest.get_connected_players()) do


        --update the status HUD
        local p_name = player:get_player_name()
        player:hud_change(bb_player.infohuds[p_name],"text",status)
        -- since hitpoints does not factor in this game, we will set hp to max
        player:set_hp(300)
        local p_meta = player:get_meta()
        if p_meta:get_string("status") == "playing" and bb_loop.gamestate == "running" then


            player:set_physics_override({speed = bb_player.speeds[p_name] or .5 , gravity = 70,sneak=false})

            --%%%%%%%%%%%%%%%%%%
            --check for nearby explosions, kill players too close
            --%%%%%%%%%%%%%%%%%%

            local pos = player:get_pos()
            local near_exp = false
            local pos1 = vector.add(pos, vector.new(-.5,-.2,-.5))
            local pos2 = vector.add(pos, vector.new(.5,1,.5))
        
            local objs = minetest.get_objects_in_area(pos1, pos2)
            local o = nil -- we will store the explosion objectref in case we need to remove it
            for k,v in ipairs(objs) do
                if v and not ( v:is_player() ) and v:get_luaentity() and v:get_luaentity().name == "bb_bomb:explosion" then
                    near_exp = true
                    o = v
                end
            end
            if near_exp then
                
                -- if there was a nearby explosion, kill them if they dont have extra life. remove it if they do.
                local inv = player:get_inventory()
                if inv:contains_item("main", "bb_powerup:extra_life") == true then
                    
                    if o then -- remove the killing explosion
                        o:remove()
                    end
                    inv:remove_item("main", "bb_powerup:extra_life")
                else
                    --update the players respawn point
                    
                    local pmeta = player:get_meta()
                    pmeta:set_string("spawn", minetest.serialize( player:get_pos() )) 

                    minetest.sound_play({
                        name = "death",
                        gain = 2.0,
                    }, 
                    {
                        pos = player:get_pos(),
                        max_hear_distance = 32,  -- default, uses an euclidean metric
                    }, true)

                    bb_player.kill_player(player,"explosion")

                end
            end

            --%%%%%%%%%%%%%%%%%%
            --check for air under player, move them to center of block and kill them
            --%%%%%%%%%%%%%%%%%%

            local pos = player:get_pos()
            
            if bb_player.check_for_air_under(player) then
                --player:set_pos(vector.add(vector.round(pos),vector.new(0,2,0)))
                local p_props = player:get_properties()

                if bb_player.debug == true then
                    minetest.log("error","player killed for being over air")
                end

                minetest.sound_play({
                    name = "GameOver",
                    gain = 1.0,
                }, {
                    pos = player:get_pos(),
                    gain = 1,  -- default
                    max_hear_distance = 10,  
                }, true)

                -- TODO: play falling sound
                bb_player.kill_player(player,"falling")
            end


        -- need to set players' physics override for outside running gamestate (eg, dead or not in game)
        elseif p_meta:get_string("status") ~= "admin" then
            player:set_physics_override({speed = 0, jump = 0 ,gravity = 0,sneak=false})
        end

    end


        

end)



--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

-- killplayer stuff

--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bb_player.register_on_dieplayer(function(p_name,cause,death_pos)

    local player = minetest.get_player_by_name(p_name)
    if not player then return end
    -- clear the inv
    bb_player.clear_inv(player)
    -- set meta
    local p_name = player:get_player_name()
    local pmeta = player:get_meta()
    local air_under = false 
    if cause == "falling" then air_under = true end
    
    player:set_physics_override({speed = 0, jump = 0, gravity = 0,sneak=false})
    
    pmeta:set_string("status", "dead") 

    
    --set the player's looking direction to down, so they can watch themselves die >:D
    player:set_look_vertical(0.5 * 3.14)

    -- move the player around: first to watch themselves die from above
    -- then move them to the spectator position
    if bb_loop.current_arena then
        local watch_pos = vector.add(death_pos,vector.new(0,5,0))
        local att = player:get_attach()
        if att then 
            att:set_pos(watch_pos)
        else
            local att = minetest.add_entity(watch_pos, "bb_player:att")
            player:set_attach(att)
        end        
    end

    
    -- after death animation, move them to the spectator position, if the arena is still the same
    minetest.after(3,function(loopcounter,p_name)
        
        if bb_loop.loopcounter == loopcounter then
            --check player exists, they could have ragequit lol
            local player = minetest.get_player_by_name(p_name)

            if player then

                local arena_spectator_pos = bb_loop.current_arena.spectator_pos
                local att = player:get_attach()

                -- we prob. can assume that  att exists, but check, just in case and create it if not
                if att then 
                    att:set_pos(arena_spectator_pos)
                else
                    local att = minetest.add_entity(arena_spectator_pos, "bb_player:att")
                    player:set_attach(att)
                end

            end

        end

    end, bb_loop.loopcounter, p_name )

end)






-- spawn winner particles
bb_loop.register_on_celebrate(function(winner)

    
    minetest.after(0.1,function()
        local player 
        if winner then 
            player = minetest.get_player_by_name(winner)   
        end
        if winner and player then
            local sppos = player:get_pos()
            local yaw = player:get_look_horizontal()
            local att = minetest.add_entity(sppos, "bb_player:att")
            att:set_yaw(yaw)
            player:set_attach(att)
            -- make winner confetti
            sppos = vector.add(sppos,vector.new(0,1,0))
            minetest.add_particlespawner({
                amount = 20,
                time = .5,
                minpos = vector.add(sppos,vector.new(-.7,-.4,-.7)),
                maxpos = vector.add(sppos,vector.new(.7,.4,.7)),
                minvel = vector.new(.1,3,.1),
                maxvel = vector.new(1.5,7,1.5),
                minacc = vector.new(0,-9.8,0),
                maxacc = vector.new(0,-9.8,0),
                minexptime = 1,
                maxexptime = 1,
                minsize = 3,
                maxsize = 4,
                collisiondetection = true,            
                collision_removal = false,
                object_collision = true,
                --attached = ObjectRef,
                vertical = false,
                texture = "spawn_particle.png",
                glow = 2,
            })
            minetest.add_particlespawner({
                amount = 20,
                time = .5,
                minpos = vector.add(sppos,vector.new(-.7,-.4,-.7)),
                maxpos = vector.add(sppos,vector.new(.7,.4,.7)),
                minvel = vector.new(.1,3,.1),
                maxvel = vector.new(1.5,7,1.5),
                minacc = vector.new(0,-9.8,0),
                maxacc = vector.new(0,-9.8,0),
                minexptime = 1,
                maxexptime = 1,
                minsize = 3,
                maxsize = 4,
                collisiondetection = true,            
                collision_removal = false,
                object_collision = true,
                vertical = false,
                texture = "respawn_particle.png",
                glow = 2,
            })

            minetest.sound_play({
                name = "win",
                gain = 1,
            }, {
                pos = sppos,
                max_hear_distance = 40,  
            }, true)


        end

    end)

end)