math.randomseed(os.time())
bb_player = {}


--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.normal_speed = .5-- the speed players start at
bb_player.speed_boost = .2 -- the speed ech speed boost adds
bb_player.speed_snail = .25


bb_player.textures = {} -- stores the textures that the player will have in-game (out-game, their textures are clear)
bb_player.saved_colors = {} -- stores the color assigned to the player
bb_player.powerup_huds = {} -- stores 


bb_player.die_messages = {
    " was burnt to a crisp.",
    " is flat-out done.",
    " was pancaked by a small explosion.",
    " turned into small specks.",
    " is toast. Literally.",
    " tried biting off more than they could chew, and burnt their mouth.",
    " popped.",
    " was killed by death.",
    " learned explosions aren't powerups.",
    " terminated by THE GAME.",
    " lost their appitite.",
    " didnt think good.",
    " was slightly too slow.",
    " flew on a blast of hot air.",
    " might have lacked intellignce, but compensated with plenty of explosive.",
    ' made a small "pop" and disappeared.',
    " became a star!",
    " had a new experience.",
    " was struck by an interesting thought.",
    " tried, at least.",
    " realized their mistake.",
    " had a bright idea!",
    " was fired.",
    " will rest in pieces.",
    " underestimated their invincibility.",
    " accurately simulated sublimation.",
    " was fully oxidized.",
    " learned the hard way.",
    " reached escape velocity",
    " had an explosion of intelligence",
    " wanted to spectate.",
    " witnessed the Big Bang.",
    " caused an eruption",
    " doesnt feel like it anymore.",
    " went for a coffee.",
    " croaked.",
    " got too excited.",
    " felt slightly discombobulated.",
    " died a little on the inside.",
    " spontaneously combusted.",
    " lived the fast life.",
    " loves particles, I guess.",
    " went out with a bang.",
    " is flashy.",
    "'s hypothesis was incorrect.",
    " theory was disproved by The Universe.",
    " played with fire.",
    " was too eager.",
    " ate something hot.",
    " is well done.",
    " is ready.",
    " looked too far into the oven.",
    "; rest in peices.",
    " checked if the oven was warm enough.",
    " is creating a toxic atmosphere.",
    " is letting their mind wander again.",
    " failed fire safety class.",
    " has some spare parts now.",
    " is no longer with us.",
    " is at large.",
    " is experiencing a chemical imbalence.",
    " didnt know the game was hot potato.",
    " went out with a whimper AND a bang.",
    " didn't really need those organs anyway.",
    " is just fine ... honest!",
    " is having regrets already.",
    " will be sure to return the favor.",
    " decided to keep the game short and to the point.",
    " earned a much-needed break.",
    " was doing really well there for a while.",
    " wants a do-over.",
    " doesn't remember this part from the training video.",
    " is trying to any% their bucket list.",
    " forgot to take their body.",
    " is visiting many exotic places, simultaneously.",
    " turned out for the party.",
    " needs to keep their insides where they belong.",
    " opened up shop, and business is booming!",
    " went out with a bang.",
    " is *not* invincible, after all.",
    " suffered a minor case of death.",
    " popped off.",
    " suddenly discovered the meaning of life, the universe, and everything.",
    " is one with the universe.",

}

bb_player.fall_messages = {

    " forgot to miss the ground.",
    " is off to new horizons.",
    " reached termnal velocity",
    " apparently had too much pride",
    " is flying. No wait, im looking at this backwards...",
    " believed they could fly.",
    " went down to the bottom.",
    " is still falling...",
    " is playing on a whole other level",
    " has embraced his deepest tendency",
    " saw a bird, or a plane, or something... anyhow they're gone now.",
    " went exploring.",
    " slipped.",
    " flopped.",
    " learned flying is for the birds.",
    " tried to find the surface.",
    ' says: "hey guys things look different from here! *zcchzz* "',
    ': "over and out"',
    " went down the rabbithole.",
    " overstepped his boundaries.",
    " zoomed waaay out.",
    " is reaching for the stars.",
    " went off the deep end.",
    " went skydiving.",
    " became smaller and smaller in the distance.",
    " is approching the speed of light.",
    ' says: "hey guys, what happened to the gravity? ... oh."',
    " sprouted wings.",
    " reached for the horizon and slipped.",
    " found the event horizon.",
    " entered the twilight zone.",
    " forgot their chute.",
    " has their head in the clouds.",
    " passed out.",
    " lacked a safety net.",
    " tried living on the edge.",
    " realized the meaning of gravity.",
    " didnt watch their step.",
    " forgot that there was no atmosphere in space.",
    " dident realize that total vacuum kills.",
    " was caught breaking the Law... Of Gravity.",
    " is boldly going where no one has gone before.",
    " is exploring the final frontier.",
    " is assuming a new perspective.",
    " failed to miss the ground.",
    " forgot to pack a parachute",
    " can't fly, and found out the hard way.",
    " is looking up ... with regret.",
    " fellout.",
    " has their feet planted firmly on the ... oh wait.",
    " was once mighty, but is fallen!",
    " thought the floor was invisible. FYI: its not.",
    " and the playing field have had a bit of a falling out.",
    " sailed through the air... AND DIED.",
    " wanted to see what's under the map.",
    " tried to noclip without fly privs.",
    " tried to noclip without fly privs.",
    " is testing the effect of gravity on a mass.",
    " is somewhere... over the rainbow.",
    " is measuring terminal velocity.",
    " likes the feeling of weightlessness.",
    " clearly flies more like a banana than an arrow.",
    " shouldn't have booked the cheap flight.",
    " wanted a window seat, but got more than they bargained for.",
    " confused personal and outer space.",
    " cures insomnia by lying down hard enough.",
    " is enjoying the breeze.",
    " finds it peaceful out here.",
    " de-orbited.",
    " contracted Kessler Syndrome.",
    " wonders if there eventually is a ground below.",
    " thought they were an admin.",
 
}


-- 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



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

-- ents

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



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,

})



minetest.register_entity("bb_player:falling_player",{
    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 > 4 and self._falling then
            self.object:remove()
        end
        if bb_loop.gamestate == "loading" then
            self.object:remove()
        end
        
    end,

    _timer = 0,
    _falling = false,

})

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

-- functions declared

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



-- clears a player's inv
-- input: player objref
function bb_player.clear_inv(player)  return end



-- checks for air in node under player
-- input: player objref
function bb_player.check_for_air_under(player) return end
    


-- returns the number of a kind of item in inv
-- input player objref, itenname, output count (int)
function bb_player.get_count_in_inv(player,item) return end

-- resurrects all players and clears the board of deadplayer markers
function bb_player.resurrect_all_players() return end

-- kills a player who is alive
-- input ObjRef
function bb_player.kill_player(player) return end

function bb_player.loop_theme(song,len,p_name) return end






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

-- 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 = 1})
        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})
        end

    end,
})

minetest.register_chatcommand( "additem" , {
    privs = {
        additem = true,
    },
    description = " additem <itemname> , adds the item to the world at your location (you will pick it up too)",
    func = function( name , param )
        local player = minetest.get_player_by_name( name ) 
        if not player then return end
        local pos = player:get_pos()

        minetest.add_item(pos, param)

    end,
})

minetest.register_chatcommand( "status" , {
    privs = {
        admin = true,
    },
    description = "tells you your player status",
    func = function( name , param )
        local player = minetest.get_player_by_name( name ) 
        if not player then return end
        local meta = player:get_meta()
        return meta:get_string("status")
        
    end,
})





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

-- joinplayer

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


minetest.register_on_joinplayer(function(player)
    -- set position to the spawn pos
    local p_name = player:get_player_name()
    player:set_pos(bb_player.spawn)
    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})
    -- 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 their inv formspec to nada
    player:set_inventory_formspec("")


    -- 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")

    -- set player model
    local player_colors = {
        "#472d3c",
        "#5e3643",
        "#7a444a",
        "#a05b53",
        "#bf7958",
        "#eea160",
        "#b6d53c",
        "#71aa34",
        "#397b44",
        "#3c5956",
        "#5a5353",
        "#dff6f5",
        "#8aebf1",
        "#28ccdf",
        "#3978a8",
        "#394778",
        "#39314b",
        "#564064",
        "#8e478c",
        "#cd6093",
        "#ffaeb6",
        "#f4b41b",
        "#f47e1b",
        "#e6482e",
        "#a93b3b",
        "#827094",
        "#4f546b",
    }

    local randcolor = player_colors[math.random(1,#player_colors)]

    bb_player.saved_colors[p_name] = randcolor

    bb_player.textures[p_name] = {"white.png^[colorize:"..randcolor..":255","player_parts.png"}

    player:set_properties({
        visual = "mesh",
        mesh = "bb_character.b3d",
        textures = {"blank.png","blank.png"},
        visual_size = {x = 4, y = 4},
        collisionbox = {-0.4, 0.0, -0.4, 0.4, 0.9,0.4},
        stepheight = 0.01,
        eye_height = 1.3,
        physical = false,
        collides_with_objects = false,
    })

end)



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

-- player animation

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


minetest.register_globalstep(function(dtime)
    for _, player in pairs(minetest.get_connected_players()) do

        local meta = player:get_meta()
        local p_name = player:get_player_name()

        if bb_loop.gamestate ~= "running" and bb_loop.gamestate ~= "celebrate" then
            -- set player textures to blank if the game is not running
            player:set_properties({
                textures = {"blank.png","blank.png"},
            })

        else
            if meta:get_string("diestatus") ~= "dead" then
                player:set_properties({
                    textures = bb_player.textures[p_name],
                })
            end

        end

        if meta:get_string("status") == "playing" then
            meta:set_string("diestatus", "notdead")
            local controls = player:get_player_control()
            -- if player is pressing movement keys
            if controls.up or controls.down or controls.left or controls.right then
                --walk anim
                player:set_animation({x=60,y=100}, 50, 0, true)
            else
                -- stand anim
                player:set_animation({x=0,y=40}, 20, 0, true)
            end
        elseif meta:get_string("status") == "dead" then
            


            if meta:get_string("diestatus") ~= "dead" then
                
                meta:set_string("diestatus", "dead")
                player:set_properties({
                    textures = {"blank.png","blank.png"},
                })

                

            end

        end

    end
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)
    if control_name ~= "jump" then return end
    bb_player.try_to_place_bomb(player)
end)



-- implement party powerup (players autoplace bombs whenever they can)

local partytimer = 0
minetest.register_globalstep(function(dtime)
    if bb_loop.gamestate ~= "running" then return end 
    partytimer = partytimer + dtime
    if partytimer > .5 then
        partytimer = 0
        for _, player in pairs( minetest.get_connected_players() ) do
            local inv = player:get_inventory()
            if inv:contains_item("main", "bb_powerup:party") then
                bb_player.try_to_place_bomb(player)
            end
        end
    end
end)


-- special other controls (weird_control)

controls.register_on_press(function(player, control_name)
    if not ( control_name == "left" or control_name == "right" or control_name == "up" or control_name == "down") then
        return
    end
    local inv = player:get_inventory()
    if inv:contains_item("main", "bb_powerup:weird_control") then
        local look = player:get_look_horizontal()
        player:set_look_horizontal(look - 3.14)
    end

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
    for _, player in pairs(minetest.get_connected_players()) do

        -- 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

            --%%%%%%%%%%%%%%%%%%
            --set player speed
            --%%%%%%%%%%%%%%%%%%

            local snail = false
            local superfast = false
            local inv = player:get_inventory()
            local num_speed_boosts = bb_player.get_count_in_inv(player,"bb_powerup:extra_speed")
            -- determine the value of the above variables


            if inv:contains_item("main", ItemStack("bb_powerup:slow")) then
                snail = true
            end
            if inv:contains_item("main", ItemStack("bb_powerup:superfast")) then
                superfast = true
            end

            -- if they have a slow modifier then set their speed to slow
            if snail then
                player:set_physics_override({speed = bb_player.speed_snail, gravity = 1})
            -- if they have a super fast modifier then set their speed to very fast
            elseif superfast then
                player:set_physics_override({speed = 30, gravity = 1})
            -- else just set their speed based on the number of speed boosts they have collected
            else
                local normal_speed = bb_player.normal_speed --.75
                local speed_boost = bb_player.speed_boost -- .5
                local newspeed = normal_speed + num_speed_boosts * speed_boost
                player:set_physics_override({speed = newspeed, gravity = 1})
            end

            --%%%%%%%%%%%%%%%%%%
            --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)

                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()

                -- local obj = minetest.add_entity(vector.round(pos),"bb_player:falling_player")
                -- obj:set_properties(p_props)
                -- obj:set_acceleration(vector.new(0,-1,0))

                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)
            end

            --%%%%%%%%%%%%%%%%%%
            --add wear to time limited powerups
            --%%%%%%%%%%%%%%%%%%

            local inv = player:get_inventory()
            local list = inv:get_list("main")
            --for k, v in pairs(list) do
            for  v = 1 , inv:get_size("main") do
                local stack = inv:get_stack("main", v)
                local i_name = stack:get_name()
                if i_name == "bb_powerup:superfast" or
                    i_name == "bb_powerup:slow" or
                    i_name == "bb_powerup:weird_control" or
                    i_name == "bb_powerup:party" or
                    i_name == "bb_powerup:no_placebomb" then

                    stack:add_wear(bb_player.wear_amt)
                    inv:set_stack("main", v, stack)
                end

            end

            --%%%%%%%%%%%%%%%%%%
            --collect nearby items
            --%%%%%%%%%%%%%%%%%%

            local objs = minetest.get_objects_inside_radius(vector.add(pos,vector.new(0,.5,0)), 0.5)
            for k,v in ipairs(objs) do
                if v and not ( v:is_player() ) and v:get_luaentity() and v:get_luaentity().name == '__builtin:item' then
                    local stack = ItemStack(v:get_luaentity().itemstring)
                    local stack_name = stack:get_name()
                    local goodgroup = minetest.get_item_group(stack_name, "good")
                    local badgroup = minetest.get_item_group(stack_name, "bad")

                    


                    v:remove()


                    -- play sounds
                    if goodgroup == 1 then 
                        minetest.sound_play({
                            name = "yippee",
                            gain = 1.0,
                        }, {
                            pos = player:get_pos(),
                            max_hear_distance = 32,  -- default, uses an euclidean metric
                        }, true)
                    end
                    if badgroup == 1 then 
                        minetest.sound_play({
                            name = "eew",
                            gain = .1,
                        }, {
                            pos = player:get_pos(),
                            max_hear_distance = 32,  -- default, uses an euclidean metric
                        }, true)
                    end

                    -- if there is a bad powerup in the inv, remove it, whenever you get any powerup
                    local list = inv:get_list("main")
                    for l, m in ipairs(list) do
                        local stack = m
                        local i_name = stack:get_name()
                        if i_name == "bb_powerup:superfast" or
                            i_name == "bb_powerup:slow" or
                            i_name == "bb_powerup:weird_control" or
                            i_name == "bb_powerup:party" or
                            i_name == "bb_powerup:no_placebomb" then
        
                            stack:take_item()
                            inv:set_stack("main", l, stack)
                        end
                    end

                    if stack_name == "bb_powerup:resurect" then
                        -- resurrect all players!!!
                        bb_player.resurrect_all_players()                   
                    
                    elseif stack_name == "bb_powerup:throw" or
                        stack_name == "bb_powerup:kick" or
                        stack_name == "bb_powerup:extra_life" or
                        stack_name == "bb_powerup:multidir" then

                            -- if the item is one of the pernament powerups then only add the item if the player doesnt already have one
                            
                        if not(inv:contains_item("main", stack_name)) then
                            inv:add_item("main", stack)
                        end

                    else
                        -- for everything else, we can just add the item
                        inv:add_item("main", stack)
                    end
                end
            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})
        end

    end


        

end)





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

-- functions defined

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








-- clears a player's inv
-- input: player objref
function bb_player.clear_inv(player)

    local inv = player:get_inventory()
    local list = inv:get_list("main")
    for k, v in pairs(list) do
        inv:remove_item("main", v)
    end
    
end












-- checks for air in node under player
-- input: player objref
function bb_player.check_for_air_under(player)
    
    local pos = player:get_pos()


    if bb_player.debug then

        --particle to show player pos
        minetest.add_particle({
            pos = pos,
            velocity = vector.new(0,0,0),
            expirationtime = 1,
            size = .3,
            texture = "bb_nodes_breaker.png",
        })
    end

    --player pos is at the floor of the player's hitbox, where they are standing, in the center of the x,z
    local under_pos = vector.add(pos, vector.new(0,-.7,0))
    local undernodename = minetest.get_node(under_pos).name

    if undernodename == "air" then
        
        return true
       
    end
    return false

end















-- returns the number of a kind of item in inv
-- input player objref, itenname, output count (int)
function bb_player.get_count_in_inv(player,item)

    local count = 0
    local inv = player:get_inventory()
    local lists = inv:get_lists()
    for listname,itemstacklist in pairs(lists) do
        for _,stack in pairs( itemstacklist ) do
            if stack:get_name() == item then
                count = count + stack:get_count()
            end
        end
    end
    return count

end



    -- resurrect all players!!!
    -- first remove any deadplayer markers

function bb_player.resurrect_all_players()

    --remove all deadmarkers
    for _,obj in pairs(minetest.get_objects_inside_radius(vector.new(0,0,0), 200)) do

        local ent = obj:get_luaentity()
        if ent then
            local name = ent.name
            if name == "bb_player:falling_player" then
                obj:remove()
            end
        end
    end
    -- resurrect all players
    for _, i_player in pairs( minetest.get_connected_players()) do

        local pmeta = i_player:get_meta()
        local status = pmeta:get_string("status")
        local spawn = minetest.deserialize(pmeta:get_string("spawn"))

        if status == "dead" then
            -- they dont get to keep their stuffz, if they had any
            bb_player.clear_inv(i_player)
            -- if they died from falling, move them to their spawn location
            if bb_player.check_for_air_under(i_player) then
                i_player:set_detach()
                i_player:set_pos(spawn)
            end
            -- give them the basic starting stuff

            local inv = i_player:get_inventory()
            local list = inv:get_list("main")
           
            inv:add_item("main", ItemStack("bb_powerup:extra_bomb")) 
            inv:add_item("main", ItemStack("bb_powerup:extra_power"))


            pmeta:set_string("status", "playing")
        end
    end
end






-- kills a player who is alive
-- input ObjRef
function bb_player.kill_player(player)

    -- 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 = bb_player.check_for_air_under(player)

    local ded_pos = player:get_pos()

    local look_dir = player:get_look_horizontal()

    player:set_physics_override({speed = 0, jump = 0, gravity = 0})

    -- make the deadplayer indicator, falling is a misnomer, send deathmessages
    local player_color = bb_player.saved_colors[p_name]
    local msg
    local p_props = player:get_properties()

    local obj = minetest.add_entity( vector.add(vector.round(ded_pos),vector.new(0,-.5,0)) ,"bb_player:falling_player")
    local ent = obj:get_luaentity()

    obj:set_properties(p_props)
    obj:set_properties({
        textures=bb_player.textures[p_name],
        physical = false,
    })
    obj:set_yaw(look_dir)

    pmeta:set_string("status", "dead") 

    if air_under then

        --obj:set_acceleration(vector.new(0,-1,0))
        obj:set_animation({x=110,y=150}, 20, 2, false)
        ent._falling = true
        math.randomseed(os.time())
        msg = bb_player.fall_messages[math.random(1,#bb_player.fall_messages)]

    else

        obj:set_animation({x=170,y=210}, 90, 2, false)
        ent._falling = false
        math.randomseed(os.time())
        msg = bb_player.die_messages[math.random(1,#bb_player.die_messages)]
    end
    --send the death message
    minetest.chat_send_all(minetest.colorize(player_color, p_name .. msg))

    --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 to the spectator pos
    if bb_loop.current_arena then


        local watch_pos = vector.add(ded_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



function bb_player.loop_theme(song,len,p_name)

    minetest.sound_play({
        name = song,
        gain = 0.1,
    }, {
        to_player = p_name,        
        fade = 0.0,   -- default, change to a value > 0 to fade the sound in
        pitch = 1.0,  -- default
    }, true)
    minetest.after(len,function(song,len,p_name)
        local player = minetest.get_player_by_name(p_name)
        if player then
            bb_player.loop_theme(song,len,p_name)
        end
    end,song,len,p_name)

end