Remplissage du dépôt.

This commit is contained in:
sys4-fr 2018-09-08 14:29:29 +02:00
commit 86fc1975be
12 changed files with 686 additions and 0 deletions

98
README.md Executable file
View File

@ -0,0 +1,98 @@
Player Classes
==============
Yet another class mod for Minetest.
# TODO
- Flush and read system for `pclasses.datas.players`
# Namespaces
## pclasses
- All our stuff
### pclasses.api
- All functions used to declare, get, set classes
### pclasses.api.util
- Some utility functions
### pclasses.conf
- Some configuration values
### pclasses.classes
- All classes and their specs
### pclasses.data
- Miscellaneous data
#### pclasses.data.players
- List of all players' class. Index is player's name and value is the class's name
#### pclasses.data.hud_ids
- Surely useful in the future with a hypothetical hud to show current class
# Functions
### pclasses.api.register_class
- Arguments : cname, def
- Registers a class and its specifications
- Def is a definition table that can contain many functions/values :
- `on_assigned` which is a function, receiving as argument the player name
- `on_unassigned` which is a function, receiving as argument the player name
- `on_update` which is a function, receiving as argument the player name
- `switch_params`, which is a table, containing parameters for the switch pedestal :
- `holo_item` is mandatory. It's the itemstring of the item to be put over the pedestal
- `color` is optional. Default is white. It's a RGB table.
- `tile` is optional. Default is none. It's a string of the texture to be applied over the pedestal
### pclasses.register_class_switch
- Arguments : cname, params
- Used internally to create switch pedestals
- `params` is the `def` table given to `pclasses.api.register_class`, documented above
### pclasses.api.get_class_by_name
- Argument : cname
- Return the class' specs (table) corresponding a class name or nil if not found
### pclasses.api.get_player_class
- Argument : pname (player's name)
- Return the player's current class' name
### pclasses.api.get_class_players
- Argument : cname
- Return a list (table) of all players with class cname
### pclasses.api.set_player_class
- Arguments : pname, cname
- Assign a player the cname class
- Returns true if achieved, false if not
### pclasses.api.util.does_wear_full_armor
- Arguments : pname, material, noshield
- Returns true if player `pname` is wearing the full armor made out of `material`
- `noshield` must be true when the full armor has no shield
### pclasses.api.util.can_have_item
- Arguments : pname, itemname
- Returns true if player `pname` can have items `itemstring` in his main inventory, according to his class
### pclasses.api.util.on_update
- Arguments : pname
- Update player's stats
### pclasses.api.reserve_item
- Arguments : cname, itemstring
- Adds an entry in the reserved items' table. Players will need to belong to class `cname` in order to have items `itemstring` in their main inventory
- Note : You can reserve the same item for two classes, any player of either of both can then have the item
### pclasses.api.create_graveyard_inventory
- Argument : player
- Creates a detached inventory dedicated to 'dead' items (confiscated reserved items)
- Used internally, should not be used outside of pclasses
### pclasses.api.vacuum_graveyard
- Argument : player
- Check all of `player`'s graveyard inventory to get them back items they obtained to right to have

28
adventurer.lua Executable file
View File

@ -0,0 +1,28 @@
-----------------------------
-- Boilerplate class
--
pclasses.api.register_class("adventurer", {
switch_params = {
color = { r = 142, g = 64, b = 00},
tile = "wool_white.png",
holo_item = "unified_inventory:bag_large"
},
on_assigned = function(pname, inform)
if inform then
minetest.chat_send_player(pname, "You are now an adventurer")
end
end,
on_unassigned = function(pname)
end,
on_update = function(pname)
end,
informations = pclasses.api.textify("Adventurer, the casual players, or hardcore players. Whatever end of the spectrum\n" ..
"you're in, adventurer will bring you what you want : no advantages, no help. Maybe you\n" ..
"don't want that if you just began playing. If that's the case.. just pick another tab and\n" ..
"read what's in it. You'll still be able to come back to this boilerplate class whenever you\n" ..
"want to (minus actual cooldown regulation of.. an hour between two changes) if you like\n" ..
"being hurt, or hardcore gaming, which, from afar, look alike.... The pedestal has a backpack\n" ..
"over it, because, yay adventures!") .. "image[2.4,5.6;6,4;pclasses_showcase_adventurer.png]"
})

176
api.lua Executable file
View File

@ -0,0 +1,176 @@
------------------
-- PClasses' API
--
-- Various utility functions
-- Register the class (basic registration)
function pclasses.api.register_class(cname, def)
if not cname then
minetest.log("error", "[PClasses] Error registering unamed class")
return
elseif not def then
minetest.log("error", "[PClasses] Error registering class " ..
cname .. ". Reason : no definition table.")
return
end
if cname == "infos" then
minetest.log("error", "[PClasses] Error registering class with reserved name : infos")
return
end
pclasses.register_class_switch(cname, def.switch_params)
pclasses.classes[cname] = def
return true
end
------------------------
-- Getters and Setters
--
-- Get class specs by name
function pclasses.api.get_class_by_name(cname)
return pclasses.classes[cname]
end
-- Get single player
function pclasses.api.get_player_class(pname)
return pclasses.data.players[pname]
end
-- Get all players for a class
function pclasses.api.get_class_players(cname)
local pnames = {}
if pclasses.api.get_class_by_name(cname) then
for p,c in ipairs(pclasses.data.players) do
if c == cname then
table.insert(pnames, table.getn(pnames)+1)
end
end
end
end
-- Set single player
function pclasses.api.set_player_class(pname, cname, inform)
if pclasses.api.get_class_by_name(cname) then
if pclasses.api.get_player_class(pname) then
pclasses.api.get_class_by_name(pclasses.api.get_player_class(pname)).on_update(pname)
pclasses.api.get_class_by_name(pclasses.api.get_player_class(pname)).on_unassigned(pname)
end
pclasses.data.players[pname] = cname
local newclass = pclasses.api.get_class_by_name(cname)
newclass.on_assigned(pname, inform)
-- Implicit call to on_update because we don't wanna repeat it
if newclass.on_update then
newclass.on_update(pname)
else
newclass.on_update = function(pname) end -- So that it won't annoy us later
end
local ref = minetest.get_player_by_name(pname)
local armor_inv = minetest.get_inventory({type = "detached", name = pname .. "_armor"})
local inv = ref:get_inventory()
vacuum_inventory(pname, inv, "armor", true)
vacuum_inventory(pname, armor_inv, "armor", false) -- Don't move to the graveyard
armor:set_player_armor(ref)
armor:update_inventory(ref)
pclasses.api.vacuum_graveyard(minetest.get_player_by_name(pname))
return true
end
return false
end
-- Util function(s)
pclasses.api.util.does_wear_full_armor = function(pname, material, noshield)
local inv = minetest.get_inventory({type = "detached", name = pname .. "_armor"})
if not inv or inv:is_empty("armor") then
return false
end
local full_armor = true
for _, piece in pairs({"chestplate", "leggings", "boots", "helmet"}) do
full_armor = full_armor and inv:contains_item("armor", "3d_armor:" .. piece .. "_" .. material)
end
return full_armor and (inv:contains_item("armor", "shields:shield_" .. material) or noshield)
end
function pclasses.api.util.can_have_item(pname, itemname)
if not pclasses.data.reserved_items[itemname] or (pclasses.conf.superuser_class and pclasses.api.get_player_class(pname) == pclasses.conf.superuser_class) then
return true
end
for index, class in pairs(pclasses.data.reserved_items[itemname]) do
if pclasses.api.get_player_class(pname) == class then
return true
end
end
return false
end
function pclasses.api.util.on_update(pname)
local cname = pclasses.api.get_player_class(pname)
if cname ~= nil and pclasses.api.get_class_by_name(cname) and pclasses.api.get_class_by_name(cname).on_update then
pclasses.api.get_class_by_name(cname).on_update(pname)
end
end
-- TEMPORARY CLASS SHIFT SYSTEM
-- Used to test on local servers
--
minetest.register_privilege("class_shifter", "Able to shift between classes")
minetest.register_chatcommand("switch_class", {
args = "<class>",
privs = {class_shifter = true},
func = function(name, param)
pclasses.api.set_player_class(name, param)
end
})
-------------------
-- Reserved items
--
function pclasses.api.reserve_item(cname, itemstring)
pclasses.data.reserved_items[itemstring] = pclasses.data.reserved_items[itemstring] or {}
table.insert(pclasses.data.reserved_items[itemstring], cname)
end
-------------------------------------------
-- Determination and reserved items tick --
-------------------------------------------
function vacuum_inventory(name, inv, invname, bury)
local ref = minetest.get_player_by_name(name)
for i = 1, inv:get_size(invname) do
local stack = inv:get_stack(invname, i)
if pclasses.data.reserved_items[stack:get_name()] then
if not pclasses.api.util.can_have_item(name, stack:get_name()) then
inv:set_stack(invname, i, "")
if bury then
local grave_inv = pclasses.api.create_graveyard_inventory(ref)
if grave_inv and grave_inv:room_for_item("graveyard", stack) then
grave_inv:add_item("graveyard", stack)
inv:add_item("graveyard", stack)
-- ^ Because add_item doesn't trigger on_put, nonsense
else
minetest.add_item(ref:getpos(), stack)
end
end
end
end
end
end
local function tick()
for id, ref in ipairs(minetest.get_connected_players()) do
local name = ref:get_player_name()
local inv = ref:get_inventory()
vacuum_inventory(name, inv, "main", true)
end
minetest.after(2, tick)
end
tick()

3
depends.txt Executable file
View File

@ -0,0 +1,3 @@
3d_armor
sprint
unified_inventory

92
init.lua Executable file
View File

@ -0,0 +1,92 @@
-------------------
-- Player Classes
--
-- NOTE: This is a very simple interface for classes, more features will be
-- added depending on the various mechanisms we will need
-- Global namespace
pclasses = {}
-- API
pclasses.api = {}
pclasses.api.util = {}
-- Configuration
pclasses.conf = {}
pclasses.conf.default_class = "adventurer"
pclasses.conf.superuser_class = "admin"
pclasses.conf.save_interval = 3 * 60
pclasses.conf.datafile = minetest.get_worldpath() .. "/pclasses"
pclasses.conf.gravefile = minetest.get_worldpath() .. "/graveyards"
-- Classes
pclasses.classes = {}
-- Data
pclasses.data = {}
pclasses.data.players = {}
pclasses.data.reserved_items = {}
pclasses.data.hud_ids = {} -- HUD maybe?
dofile(minetest.get_modpath("pclasses") .. "/api.lua")
dofile(minetest.get_modpath("pclasses") .. "/inventory.lua")
dofile(minetest.get_modpath("pclasses") .. "/nodes.lua")
function pclasses.data.load()
local file = io.open(pclasses.conf.datafile, "r")
if file then
local loaded = minetest.deserialize(file:read("*all"))
file:close()
if loaded then
pclasses.data.players = loaded.players or pclasses.data.players
minetest.log("action", "[PClasses] Loaded data")
end
end
end
function pclasses.data.save()
local file, err = io.open(pclasses.conf.datafile, "w")
if file then
file:write(minetest.serialize({
players = pclasses.data.players,
}))
file:close()
--minetest.log("action", "[PClasses] Saved data")
else
minetest.log("error", "[PClasses] Data save failed: open failed: " .. err)
end
end
local function data_save_loop()
pclasses.data.save()
minetest.after(pclasses.conf.save_interval, data_save_loop)
end
pclasses.data.load()
------------------
-- Default class
--
if pclasses.conf.default_class then
dofile(minetest.get_modpath("pclasses") .. "/" .. pclasses.conf.default_class .. ".lua")
end
minetest.register_on_joinplayer(function(player)
local pname = player:get_player_name()
pclasses.api.create_graveyard_inventory(player) --create inventory before
local cname = pclasses.api.get_player_class(pname)
if cname ~= nil and pclasses.api.get_class_by_name(cname) then
pclasses.api.set_player_class(pname, cname)
elseif pclasses.api.get_class_by_name(pclasses.conf.default_class) then
pclasses.api.set_player_class(pname, pclasses.conf.default_class)
end
end)
minetest.register_on_shutdown(function()
pclasses.data.save()
end)
data_save_loop()

152
inventory.lua Executable file
View File

@ -0,0 +1,152 @@
------------------------
-- PClasses' inventory
--
-- Inventory for 'dead' items
pclasses.api.create_graveyard_inventory = function(player)
local pname = player:get_player_name()
local grave_inv = minetest.get_inventory({type = "detached", name = pname .. "_graveyard"})
if grave_inv then
return grave_inv
end
local player_inv = minetest.get_inventory({type = "player", name = pname})
grave_inv = minetest.create_detached_inventory(pname .. "_graveyard", {
on_take = function(inv, listname, index, stack, player)
player_inv:set_stack(listname, index, nil)
end,
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
local stack = inv:get_stack(to_list, to_index)
player_inv:set_stack(to_list, to_index, stack)
player_inv:set_stack(from_list, from_index, nil)
end,
allow_take = function(inv, listname, index, stack, player)
player_inv:set_stack(listname, index, nil)
return stack:get_count()
end,
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
return 0
end,
allow_put = function(inv, listname, index, stack, player)
return 0
end,
})
grave_inv:set_size("graveyard", 7*8)
player_inv:set_size("graveyard", 7*8)
for i = 1,56 do
local stack = player_inv:get_stack("graveyard", i)
grave_inv:set_stack("graveyard", i, stack)
end
return grave_inv
end
unified_inventory.register_button("graveyard", {
type = "image",
image = "pclasses_grave_button.png",
tooltip = "Item Graveyard",
})
unified_inventory.register_page("graveyard", {
get_formspec = function(player)
local pname = player:get_player_name()
local form = "label[0,0;Graveyard]" ..
"list[detached:" .. pname .. "_graveyard;graveyard;0.5,0.7;7,8]"
return {formspec = form, draw_inventory = false}
end
})
minetest.register_chatcommand("clear_graveyard", {
description = "Clear Graveyard Inventory",
privs = {},
func = function(name, param)
local grave_inv = minetest.get_inventory({type = "detached", name = name .. "_graveyard"})
grave_inv:set_list("graveyard", {})
minetest.get_player_by_name(name):get_inventory():set_list("graveyard", {})
return true, "Graveyard flushed"
end,
})
function pclasses.api.vacuum_graveyard(player)
local pname = player:get_player_name()
local grave_inv = minetest.get_inventory({type = "detached", name = pname .. "_graveyard"})
local player_inv = minetest.get_inventory({type = "player", name = pname})
if not grave_inv then return end
for i = 1,7*8 do
local stack = grave_inv:get_stack("graveyard", i)
if pclasses.data.reserved_items[stack:get_name()] and pclasses.api.util.can_have_item(pname, stack:get_name()) then
grave_inv:set_stack("graveyard", i, nil)
player_inv:set_stack("graveyard", i, nil)
if player_inv:room_for_item("main", stack) then
player_inv:add_item("main", stack)
else
minetest.add_item(player:getpos(), stack)
end
end
end
end
-- Inventory description buttons
local pbutton_form = "size[10,10]" ..
"button_exit[4.5,9.5;1,0.5;pmenu_leave;Leave]" ..
"tabheader[0,0;pmenu_header;infos"
function pclasses.api.textify(text)
return ("textarea[0.5,0.2;9.6,5.8;pmenu_data;;%s]"):format(text)
end
local pbuttons = {}
local pforms = {}
local pinfo = pclasses.api.textify(
"PClasses (Player Classes) allows you to become a member of specific classes implemented " ..
"with abilities, advantages, and reserved items. Each one of the classes defined grants " ..
"the right to carry items, called reserved items, tied to the abilities of a class. A " ..
"hunter will be able to use arrows, whereas a warrior can own powerful weapons. Each time " ..
"you switch classes, you will lose your stats and items, the latter being transfered into " ..
"a special part of your inventory, the graveyard. Once you return to a class that allows " ..
"you to use those items, they will return in your main inventory.\n" ..
"You can use this menu to navigate between classes and read informations about what " ..
"abilities come with specific classes.\n" ..
"You can see on this man the location of all class buildings available, containing their " ..
"respective class pedestals."
) .. "image[2.4,5.6;6,4;pclasses_buildings.png]"
minetest.after(0, function()
for cname, cdef in pairs(pclasses.classes) do
if cname ~= pclasses.conf.superuser_class then
pbutton_form = pbutton_form .. ',' .. cname
table.insert(pbuttons, cname)
end
end
pbutton_form = pbutton_form .. ";1]"
end)
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "" then return end
if fields.pmenu_header then
if fields.pmenu_header + 0 == 1 then
player:set_inventory_formspec(pbutton_form .. pinfo)
else
player:set_inventory_formspec(string.sub(pbutton_form, 1, -3) .. fields.pmenu_header .. "]" .. (pclasses.classes[pbuttons[fields.pmenu_header-1]].informations or "No informations available"))
end
return
elseif fields.pmenu_leave then
player:set_inventory_formspec(pforms[player:get_player_name()])
pforms[player:get_player_name()] = nil
end
end)
unified_inventory.register_button("pclasses", {
type = "image",
image = "pclasses_inv.png",
tooltip = "Player Classes Descriptions",
action = function(player)
if not pforms[player:get_player_name()] then
pforms[player:get_player_name()] = player:get_inventory_formspec()
player:set_inventory_formspec(pbutton_form .. pinfo)
end
end
})

137
nodes.lua Executable file
View File

@ -0,0 +1,137 @@
minetest.register_entity("pclasses:item", {
initial_properties = {
hp_max = 1,
physical = false,
collisionbox = {-0.17,-0.17,-0.17, 0.17,0.17,0.17},
visual = "sprite",
visual_size = {x=0.5, y=0.5},
textures = {""},
spritediv = {x=1, y=1},
initial_sprite_basepos = {x=0, y=0},
is_visible = false,
},
itemname = '',
class = '',
set_class = function(self, class)
self.class = class
end,
set_item = function(self, itemstring)
self.itemname = itemstring
local itemname = itemstring
local item_texture = nil
local item_type = ""
if minetest.registered_items[itemname] then
item_texture = minetest.registered_items[itemname].inventory_image
item_type = minetest.registered_items[itemname].type
end
local prop = {
is_visible = true,
visual = "sprite",
textures = {"unknown_item.png"}
}
if item_texture and item_texture ~= "" then
prop.visual = "sprite"
prop.textures = {item_texture}
prop.visual_size = {x=0.50, y=0.50}
else
prop.visual = "wielditem"
prop.textures = {itemname}
prop.visual_size = {x=0.25, y=0.25}
prop.automatic_rotate = math.pi * 0.10
end
self.object:set_properties(prop)
end,
on_rightclick = function(self, clicker)
action_timers.wrapper(
clicker:get_player_name(),
"class switch",
"class_switch_" .. clicker:get_player_name(),
3600,
pclasses.api.set_player_class,
{clicker:get_player_name(), self.class, true}
)
end,
on_activate = function(self, staticdata)
local tab = minetest.deserialize(staticdata)
if tab then
self.itemname = tab.itemname
self.class = tab.class
else
self.itemname = staticdata:split("|")[1]
self.class = staticdata:split("|")[2]
end
self.object:set_armor_groups({immortal=1})
self:set_item(self.itemname)
end,
get_staticdata = function(self)
return minetest.serialize({itemname = self.itemname, class = self.class})
end,
})
function pclasses.register_class_switch(cname, params)
local color = params.color or { r = 255, g = 255, b = 255 }
local txtcolor = string.format("#%02x%02x%02x", color.r, color.g, color.b)
local overlay = "pclasses_class_switch_orb_overlay.png"
local holo_item = params.holo_item or "default:diamond"
minetest.register_node(":pclasses:class_switch_" .. cname, {
description = "Class switch orb (" .. cname .. ")",
tiles = {(params.tile or overlay) .. "^[colorize:" .. txtcolor .. ":200"},
drawtype = "nodebox",
node_box = { type = "fixed", fixed = {
{-7/16, -8/16, -7/16, 7/16, -7/16, 7/16}, -- bottom plate
{-6/16, -7/16, -6/16, 6/16, -6/16, 6/16}, -- bottom plate (upper)
{-0.25, -6/16, -0.25, 0.25, 11/16, 0.25}, -- pillar
{-7/16, 11/16, -7/16, 7/16, 12/16, 7/16}, -- top plate
}},
can_dig = function(pos, player) return minetest.get_player_privs(player:get_player_name()).server == true end,
sunlight_propagates = true,
light_source = 10,
sounds = default.node_sound_glass_defaults(),
groups = {unbreakable = 1},
after_place_node = function(pos)
pos.y = pos.y + 1
-- Clean remaining entities
for _,ref in pairs(minetest.get_objects_inside_radius(pos, 0.3)) do
local e = ref:get_luaentity()
if e and e.name == "pclasses:item" then
ref:remove()
end
end
local obj = minetest.add_entity(pos, "pclasses:item")
if obj then
obj:get_luaentity():set_item(holo_item)
obj:get_luaentity():set_class(cname)
end
pos.y = pos.y - 1
local timer = minetest.get_node_timer(pos)
timer:start(3)
end,
on_timer = function(pos)
pos.y = pos.y + 1
for _,ref in pairs(minetest.get_objects_inside_radius(pos, 0.3)) do
local e = ref:get_luaentity()
if e and e.name == "pclasses:item" then
return true
end
end
local obj = minetest.add_entity(pos, "pclasses:item")
if obj then
obj:get_luaentity():set_item(holo_item)
obj:get_luaentity():set_class(cname)
end
return true
end,
on_destruct = function(pos)
pos.y = pos.y + 1
for _,ref in pairs(minetest.get_objects_inside_radius(pos, 0.3)) do
local e = ref:get_luaentity()
if e and e.name == "pclasses:item" then
ref:remove()
end
end
end,
})
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

BIN
textures/pclasses_inv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB