mirror of
https://github.com/raymoo/progress_tree.git
synced 2025-05-19 17:20:20 +02:00
Initial commit (everything)
This commit is contained in:
commit
2324eaccce
0
depends.txt
Normal file
0
depends.txt
Normal file
1
description.txt
Normal file
1
description.txt
Normal file
@ -0,0 +1 @@
|
||||
Library for tech/skill/whatever advancement trees (DAGs).
|
155
init.lua
Normal file
155
init.lua
Normal file
@ -0,0 +1,155 @@
|
||||
|
||||
progress_tree = {}
|
||||
|
||||
-- A progress tree is a map from string names to nodes in adjacency list
|
||||
-- representation.
|
||||
|
||||
local tree_methods = {}
|
||||
local tree_metatab = { __index = tree_methods }
|
||||
|
||||
function progress_tree.new_tree()
|
||||
local ret = {}
|
||||
setmetatable(ret, tree_metatab)
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
-- A node is a table with these fields:
|
||||
-- - children - A list of child nodes
|
||||
-- - parents - A list of parent nodes
|
||||
|
||||
local node_methods = {}
|
||||
local node_metatab = { __index = node_methods }
|
||||
|
||||
function progress_tree.new_node(parents)
|
||||
local ret = { children = {},
|
||||
parents = parents,
|
||||
}
|
||||
setmetatable(ret, node_metatab)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
-- Adds one node, and takes a list of parents. Each parent must be in the tree
|
||||
-- already.
|
||||
function tree_methods.add(self, name, parents)
|
||||
for i, parent in ipairs(parents) do
|
||||
if not self[parent] then
|
||||
local err_msg = "[progress_tree]"
|
||||
.. " Node " .. name ..
|
||||
" is child of nonexistent parent " .. parent
|
||||
error(err_msg)
|
||||
end
|
||||
end
|
||||
|
||||
if self[name] then
|
||||
error("[progress_tree] Duplicate node " .. name)
|
||||
end
|
||||
|
||||
self[name] = progress_tree.new_node(parents)
|
||||
|
||||
for i, parent in ipairs(parents) do
|
||||
table.insert(self[parent].children, name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Each player can ahve data related to their completion of the tree, namely a table
|
||||
-- with these fields:
|
||||
-- - tree - The original tree this data tracks
|
||||
-- - remaining_deps - A map from node names to the number of unfulfilled
|
||||
-- prerequisite nodes.
|
||||
-- - available - A set { ["node1"] = true, ["node2"] = true, ... } of nodes
|
||||
-- that have no prerequisites left to fulfill, and have not been taken yet
|
||||
-- - learned - A set of nodes the player has taken already.
|
||||
|
||||
local pd_methods = {}
|
||||
local pd_metatab = { __index = pd_methods }
|
||||
|
||||
|
||||
-- Argument is the tree to create the data against. learned is an optional
|
||||
-- argument for a set of initial learned techs.
|
||||
function progress_tree.new_player_data(tree, learned)
|
||||
local data = { tree = tree,
|
||||
remaining_deps = {},
|
||||
available = {},
|
||||
learned = {},
|
||||
}
|
||||
setmetatable(data, pd_metatab)
|
||||
|
||||
for name, node in pairs(tree) do
|
||||
local rem_deps = #(node.parents)
|
||||
data.remaining_deps[name] = rem_deps
|
||||
if rem_deps == 0 then
|
||||
data.available[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
if learned then
|
||||
for k in pairs(learned) do
|
||||
data:learn(k)
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
|
||||
-- Serialization to a string
|
||||
function pd_methods.serialize(self)
|
||||
return minetest.serialize(self.learned)
|
||||
end
|
||||
|
||||
|
||||
-- Deserialization. Data can only be deserialized against a particular tree.
|
||||
-- Returns nil on failure.
|
||||
function progress_tree.deserialize_player_data(tree, learned_string)
|
||||
local learned = minetest.deserialize(learned_string)
|
||||
if not learned then return end
|
||||
|
||||
return progress_tree.new_player_data(tree, learned)
|
||||
end
|
||||
|
||||
|
||||
-- Tells whether the tech is already learned
|
||||
function pd_methods.knows(self, name)
|
||||
return self.learned[name] == true -- Want false when not in
|
||||
end
|
||||
|
||||
|
||||
-- Tells whether the tech is learnable with the current level of techs.
|
||||
function pd_methods.can_learn(self, name)
|
||||
return (not self.learned[name]) and self.available[name] == true
|
||||
end
|
||||
|
||||
|
||||
-- Learn a new tech. Returns true on success, returns false on failure. It can
|
||||
-- fail if the tech doesn't exist or if it is already learned. It does not fail
|
||||
-- if it is learned out of order, so use can_learn if you want to be sure.
|
||||
function pd_methods.learn(self, name)
|
||||
if self:knows(name) then return false end
|
||||
|
||||
local node = self.tree[name]
|
||||
if not node then return false end
|
||||
|
||||
local children = node.children
|
||||
|
||||
-- Decrement the remaining deps for each child
|
||||
for i, child in ipairs(children) do
|
||||
local new_rem = self.remaining_deps[child] - 1
|
||||
print(child, new_rem)
|
||||
-- If the player learned it out of order, don't put it as
|
||||
-- available.
|
||||
if new_rem == 0 and not self:knows(child) then
|
||||
self.available[child] = true
|
||||
end
|
||||
self.remaining_deps[child] = new_rem
|
||||
end
|
||||
|
||||
self.learned[name] = true
|
||||
self.available[name] = nil
|
||||
end
|
||||
|
||||
|
||||
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/test.lua")
|
83
test.lua
Normal file
83
test.lua
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
local test_tree = progress_tree.new_tree()
|
||||
|
||||
test_tree:add("rock", {})
|
||||
test_tree:add("wood", {})
|
||||
test_tree:add("plant", {})
|
||||
test_tree:add("axe", {"rock", "wood"})
|
||||
test_tree:add("plank", {"wood"})
|
||||
test_tree:add("plankaxe", {"axe", "plank"})
|
||||
test_tree:add("wheat", {"plant"})
|
||||
|
||||
local data = progress_tree.new_player_data(test_tree, {"wood"})
|
||||
|
||||
local infos = {
|
||||
rock = {x=0, y=0, texture="default_stone.png", desc="Rock"},
|
||||
wood = {x=1.5, y=0, texture="default_tree.png", desc="Wood"},
|
||||
plant = {x=5, y=0, texture="default_grass.png", desc="Plant"},
|
||||
axe = {x=1, y=1.5, texture="default_tool_stoneaxe.png", desc="Axe"},
|
||||
plank = {x=2.5, y=1.5, texture="default_stick.png", desc="Plank"},
|
||||
plankaxe = {x=2, y=3, texture="default_tool_woodaxe.png", desc="Plank Axe"},
|
||||
wheat = {x=5, y=1.5, texture="farming_wheat.png", desc="Wheat"},
|
||||
}
|
||||
|
||||
|
||||
local function build_formspec()
|
||||
local formspec = "size[8,8]"
|
||||
print(dump(data.available))
|
||||
local nodes = {}
|
||||
|
||||
for k in pairs(data.learned) do
|
||||
local info = infos[k]
|
||||
local texture = info.texture .. "^progress_tree_check.png^[colorize:#00FF00:50"
|
||||
local fs = "image_button[" .. info.x .. "," .. info.y .. ";1,1;"
|
||||
.. minetest.formspec_escape(texture) .. ";" .. k .. ";]"
|
||||
local tooltip = "tooltip[" .. k .. ";" .. info.desc .. "]"
|
||||
table.insert(nodes, fs)
|
||||
table.insert(nodes, tooltip)
|
||||
end
|
||||
|
||||
for k in pairs(data.available) do
|
||||
local info = infos[k]
|
||||
local fs = "image_button[" .. info.x .. "," .. info.y .. ";1,1;" .. info.texture
|
||||
.. ";" .. k .. ";]"
|
||||
local tooltip = "tooltip[" .. k .. ";" .. info.desc .. "]"
|
||||
table.insert(nodes, fs)
|
||||
table.insert(nodes, tooltip)
|
||||
end
|
||||
|
||||
formspec = formspec .. table.concat(nodes)
|
||||
|
||||
return formspec
|
||||
end
|
||||
|
||||
|
||||
local function show(player)
|
||||
minetest.show_formspec(player:get_player_name(), "progress_tree:test", build_formspec())
|
||||
end
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "progress_tree:test" then return end
|
||||
|
||||
for node in pairs(data.available) do
|
||||
if fields[node] then
|
||||
data:learn(node)
|
||||
end
|
||||
end
|
||||
|
||||
if not fields["quit"] then
|
||||
show(player)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_craftitem("progress_tree:test_book", {
|
||||
description = "Ultimate Techs",
|
||||
groups = { not_in_creative_inventory = 1 },
|
||||
inventory_image = "default_book.png",
|
||||
|
||||
on_use = function(itemstack, player)
|
||||
show(player)
|
||||
end,
|
||||
})
|
BIN
textures/progress_tree_check.png
Normal file
BIN
textures/progress_tree_check.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 256 B |
Loading…
x
Reference in New Issue
Block a user