Jordan Irwin 2021-04-25 05:47:46 -07:00
parent f950f53351
commit e7f0dadb0a
24 changed files with 1160 additions and 0 deletions

View File

@ -84,6 +84,7 @@ The game includes the mods from the default [minetest_game](https://github.com/m
* [creatures][mob-engine] ([zlib][lic.creatures] / [CC BY-SA][lic.ccbysa3.0]) -- version: [c2bedc3 Git][ver.mob-engine] *2018-04-23* ([patched][patch.mob-engine])
* [mobkit][] ([MIT][lic.mobkit]) -- version: [ddea141 Git][ver.mobkit] *2021-02-01*
* [mobs_redo][] ([MIT][lic.mobs_redo] / [CC BY][lic.ccby3.0] / [CC0][lic.cc0]) -- version: [f6e16a5 Git][ver.mobs_redo] *2021-04-11* ([patched][patch.mobs_redo])
* [folks][] ([GPL][lic.gpl3.0]) -- version: [0.2.0][ver.folks] *2021-02-23*
* general/
* [mobs_animal][] ([MIT][lic.mobs_animal]) -- version: [80e72a4 Git][ver.mobs_animal] *2021-04-13* ([patched][patch.mobs_animal])
* [monsters_aggressive][] (see individual mods for licensing) -- version: [89a8187 Git][ver.monsters_aggressive] *2017-08-30*
@ -264,6 +265,7 @@ The game includes the mods from the default [minetest_game](https://github.com/m
[ethereal]: https://forum.minetest.net/viewtopic.php?t=14638
[farlands]: https://forum.minetest.net/viewtopic.php?t=16921
[farming_plus]: https://forum.minetest.net/viewtopic.php?t=2787
[folks]: https://forum.minetest.net/viewtopic.php?t=26121
[fort_spikes]: https://forum.minetest.net/viewtopic.php?t=14574
[gems_encrustable]: https://forum.minetest.net/viewtopic.php?t=2596
[gems_tools]: https://forum.minetest.net/viewtopic.php?t=4294
@ -482,6 +484,7 @@ The game includes the mods from the default [minetest_game](https://github.com/m
[ver.equip_exam]: https://github.com/AntumMT/mod-equip_exam/releases/tag/v1.0
[ver.ethereal]: https://notabug.org/TenPlus1/ethereal/src/9d6103d9bb2be7b7ff761c85a6e7285bfe7700a0
[ver.farming_plus]: https://github.com/PilzAdam/farming_plus/tree/7e0d976
[ver.folks]: https://gitlab.com/SonoMichele/folks/tags/v0.2.0
[ver.fort_spikes]: https://github.com/xeranas/fort_spikes/tree/3b98b46
[ver.gems_encrustable]: https://github.com/wowiamdiamonds/gems/tree/81d513d
[ver.gems_tools]: https://github.com/captainLAD/gems/tree/b375432

View File

@ -0,0 +1,6 @@
# Folks [folks]
This mod is still a WIP. Check the [forum thread](https://forum.minetest.net/viewtopic.php?f=9&t=26121&p=389081#p389081) for more info.
## Texture
The folks_default.png was made by [Zughy](https://content.minetest.net/users/Zughy/)

100
mods/mobiles/folks/api.lua Normal file
View File

@ -0,0 +1,100 @@
function folks.edit_npc_name(npc_id, new_name)
folks.backend.get_npcs()[npc_id]._npc_name = new_name
local npc_objs = folks.backend.get_npcs_obj(npc_id)
for _, npc_obj in pairs(npc_objs) do
if npc_obj then
npc_obj:set_properties({
nametag = new_name,
})
end
end
end
function folks.edit_npc_name_color(npc_id, new_color)
folks.backend.get_npcs()[npc_id]._npc_name_color = new_color
local npc_objs = folks.backend.get_npcs_obj(npc_id)
for _, npc_obj in pairs(npc_objs) do
if npc_obj then
npc_obj:set_properties({
nametag_color = new_color,
})
end
end
end
function folks.edit_npc_texture(npc_id, new_texture)
-- add .png if not found in new_texture
if string.find(new_texture, ".png", 0, true) == nil then
new_texture = new_texture .. ".png"
end
folks.backend.get_npcs()[npc_id]._npc_textures = {new_texture,}
local npc_objs = folks.backend.get_npcs_obj(npc_id)
for _, npc_obj in pairs(npc_objs) do
if npc_obj then
npc_obj:set_properties({
textures = {new_texture,},
})
end
end
end
-- needs skins_collectible
-- returns false if couldn't retrieve player texture (ex. is offline)
function folks.bind_npc_to_player(npc_id, bind_to)
local new_texture_obj = skins_collectible.get_player_skin(bind_to)
if new_texture_obj == nil then return false end
local new_texture = new_texture_obj.texture
folks.backend.get_npcs()[npc_id]._npc_textures = {new_texture,}
folks.backend.get_npcs()[npc_id]._npc_name = bind_to
folks.backend.get_npcs()[npc_id]._bound_player = bind_to
local npc_objs = folks.backend.get_npcs_obj(npc_id)
for _, npc_obj in pairs(npc_objs) do
if npc_obj then
npc_obj:set_properties({
nametag = bind_to,
textures = {new_texture,},
_bound_player = bind_to,
})
end
end
return true
end
function folks.edit_npc_messages(npc_id, messages)
folks.backend.get_npcs()[npc_id]._npc_messages = messages
end
function folks.get_npc_message(npc_id, index)
local npc = folks.backend.get_npc(npc_id)
if npc then
if npc._npc_messages[index] == nil then return nil end
return npc._npc_messages[index]
end
end
function folks.spawn_npc(npc_id, position)
local npc = folks.backend.get_npc(npc_id)
if npc then
local spawned_npc = minetest.add_entity(position, "folks:npc", minetest.serialize({_npc_id = npc_id}))
if spawned_npc then
local entity = spawned_npc:get_luaentity()
if entity then
entity._npc_id = npc_id
folks.edit_npc_name(npc_id, npc._npc_name)
folks.edit_npc_name_color(npc_id, npc._npc_name_color)
folks.edit_npc_texture(npc_id, table.concat(npc._npc_textures))
folks.edit_npc_messages(npc_id, npc._npc_messages)
folks.backend.set_npc_obj(spawned_npc)
return true
end
end
end
return false
end

View File

View File

@ -0,0 +1,136 @@
local storage = minetest.get_mod_storage()
local backend = {}
local npcs = {} -- id: obj
local npcs_objects = {} -- id: {obj1, obj2}, more objects of the same NPC
local msg_for_players = {} -- npc_id: {p_name: msg_index}
function backend.load_npcs()
npcs = minetest.deserialize(storage:get_string("npcs")) or {}
for npc_id, _ in pairs(npcs) do
msg_for_players[npc_id] = {}
end
return npcs
end
function backend.get_npcs()
return npcs
end
function backend.get_npcs_objs()
return npcs_objects
end
function backend.get_unique_id()
local id
repeat
id = folks.util.randomString(16)
until npcs[id] == nil
return id
end
function backend.add_npc(ref)
local def = folks.util.deepcopy(minetest.registered_entities[ref:get_luaentity().name])
local npc = {}
local entity = ref:get_luaentity()
npc._npc_pos = ref:get_pos()
npc._npc_textures = entity.textures or def.initial_properties.textures
npc._npc_name = entity._npc_name or def.initial_properties.nametag
npc._npc_name_color = entity._npc_name_color or def.initial_properties.nametag_color
npc._npc_id = entity._npc_id
npc._npc_type = entity.name
npc._bound_player = entity._bound_player
npc._npc_messages = entity._npc_messages
npcs[entity._npc_id] = npc
msg_for_players[entity._npc_id] = {}
-- that way I can serialize npcs without problems
backend.set_npc_obj(entity._npc_id, entity._npc_object)
backend.save_npcs()
end
function backend.remove_npc(npc_id)
npcs[npc_id] = nil
npcs_objects[npc_id] = nil
backend.save_npcs()
end
function backend.save_npcs()
storage:set_string("npcs", minetest.serialize(npcs))
end
function backend.get_npc(npc_id)
return npcs[npc_id]
end
function backend.get_npcs_obj(npc_id)
return npcs_objects[npc_id]
end
function backend.set_npc_obj(npc_id, obj)
if not npcs_objects[npc_id] then
npcs_objects[npc_id] = {obj}
else
table.insert(npcs_objects[npc_id], obj)
end
end
function backend.update_npc_texture(p_name, texture)
for npc_id, npc in pairs(npcs) do
if npc._bound_player == p_name then
if npcs_objects[npc_id] then
for _, npc_obj in pairs(npcs_objects[npc_id]) do
npc_obj:set_properties({
textures = {texture,},
})
end
npcs[npc_id]._npc_textures = {texture}
end
end
end
end
-- custom Messages
function backend.get_next_message_index(npc_id, p_name)
if msg_for_players[npc_id][p_name] then
local id = msg_for_players[npc_id][p_name]
if id > #npcs[npc_id]._npc_messages then id = 1 end
msg_for_players[npc_id][p_name] = id + 1
return id
end
msg_for_players[npc_id][p_name] = 2 -- set the next id
return 1 -- if p_name is not found means it's the first message
end
-- function backend.spawn_npc(ref)
-- local obj = minetest.add_entity(ref._npc_pos, ref._npc_type)
-- local npc = obj:get_properties()
-- npc._npc_pos = ref._npc_pos
-- npc._npc_textures = ref._npc_textures or npc.initial_properties.textures
-- npc._npc_name = ref._npc_name or npc.initial_properties.nametag
-- npc._npc_id = ref._npc_id
-- npc._npc_type = ref._npc_type
-- npc._npc_object = obj
--
-- npcs[ref._npc_id] = npc
--
-- end
--
-- function backend.despawn_npc(ref)
-- local npc = npcs[ref._npc_id]
-- npc._npc_object:remove()
--
-- npcs[ref._npc_id]["_npc_object"] = "heyo"
-- minetest.log("Despawning: " .. ref._npc_id)
-- end
return backend

View File

@ -0,0 +1,306 @@
ChatCmdBuilder = {}
function ChatCmdBuilder.new(name, func, def)
def = def or {}
local cmd = ChatCmdBuilder.build(func)
cmd.def = def
def.func = cmd.run
minetest.register_chatcommand(name, def)
return cmd
end
local STATE_READY = 1
local STATE_PARAM = 2
local STATE_PARAM_TYPE = 3
local bad_chars = {}
bad_chars["("] = true
bad_chars[")"] = true
bad_chars["."] = true
bad_chars["%"] = true
bad_chars["+"] = true
bad_chars["-"] = true
bad_chars["*"] = true
bad_chars["?"] = true
bad_chars["["] = true
bad_chars["^"] = true
bad_chars["$"] = true
local function escape(char)
if bad_chars[char] then
return "%" .. char
else
return char
end
end
local dprint = function() end
ChatCmdBuilder.types = {
pos = "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?",
text = "(.+)",
number = "(%-?[%d.]+)",
int = "(%-?[%d]+)",
word = "([^ ]+)",
alpha = "([A-Za-z]+)",
modname = "([a-z0-9_]+)",
alphascore = "([A-Za-z_]+)",
alphanumeric = "([A-Za-z0-9]+)",
username = "([A-Za-z0-9-_]+)",
}
function ChatCmdBuilder.build(func)
local cmd = {
_subs = {}
}
function cmd:sub(route, func, def)
dprint("Parsing " .. route)
def = def or {}
if string.trim then
route = string.trim(route)
end
local sub = {
pattern = "^",
params = {},
func = func
}
-- End of param reached: add it to the pattern
local param = ""
local param_type = ""
local should_be_eos = false
local function finishParam()
if param ~= "" and param_type ~= "" then
dprint(" - Found param " .. param .. " type " .. param_type)
local pattern = ChatCmdBuilder.types[param_type]
if not pattern then
error("Unrecognised param_type=" .. param_type)
end
sub.pattern = sub.pattern .. pattern
table.insert(sub.params, param_type)
param = ""
param_type = ""
end
end
-- Iterate through the route to find params
local state = STATE_READY
local catching_space = false
local match_space = " " -- change to "%s" to also catch tabs and newlines
local catch_space = match_space.."+"
for i = 1, #route do
local c = route:sub(i, i)
if should_be_eos then
error("Should be end of string. Nothing is allowed after a param of type text.")
end
if state == STATE_READY then
if c == ":" then
dprint(" - Found :, entering param")
state = STATE_PARAM
param_type = "word"
catching_space = false
elseif c:match(match_space) then
print(" - Found space")
if not catching_space then
catching_space = true
sub.pattern = sub.pattern .. catch_space
end
else
catching_space = false
sub.pattern = sub.pattern .. escape(c)
end
elseif state == STATE_PARAM then
if c == ":" then
dprint(" - Found :, entering param type")
state = STATE_PARAM_TYPE
param_type = ""
elseif c:match(match_space) then
print(" - Found whitespace, leaving param")
state = STATE_READY
finishParam()
catching_space = true
sub.pattern = sub.pattern .. catch_space
elseif c:match("%W") then
dprint(" - Found nonalphanum, leaving param")
state = STATE_READY
finishParam()
sub.pattern = sub.pattern .. escape(c)
else
param = param .. c
end
elseif state == STATE_PARAM_TYPE then
if c:match(match_space) then
print(" - Found space, leaving param type")
state = STATE_READY
finishParam()
catching_space = true
sub.pattern = sub.pattern .. catch_space
elseif c:match("%W") then
dprint(" - Found nonalphanum, leaving param type")
state = STATE_READY
finishParam()
sub.pattern = sub.pattern .. escape(c)
else
param_type = param_type .. c
end
end
end
dprint(" - End of route")
finishParam()
sub.pattern = sub.pattern .. "$"
dprint("Pattern: " .. sub.pattern)
table.insert(self._subs, sub)
end
if func then
func(cmd)
end
cmd.run = function(name, param)
for i = 1, #cmd._subs do
local sub = cmd._subs[i]
local res = { string.match(param, sub.pattern) }
if #res > 0 then
local pointer = 1
local params = { name }
for j = 1, #sub.params do
local param = sub.params[j]
if param == "pos" then
local pos = {
x = tonumber(res[pointer]),
y = tonumber(res[pointer + 1]),
z = tonumber(res[pointer + 2])
}
table.insert(params, pos)
pointer = pointer + 3
elseif param == "number" or param == "int" then
table.insert(params, tonumber(res[pointer]))
pointer = pointer + 1
else
table.insert(params, res[pointer])
pointer = pointer + 1
end
end
if table.unpack then
-- lua 5.2 or later
return sub.func(table.unpack(params))
else
-- lua 5.1 or earlier
return sub.func(unpack(params))
end
end
end
return false, "Invalid command"
end
return cmd
end
local function run_tests()
if not (ChatCmdBuilder.build(function(cmd)
cmd:sub("bar :one and :two:word", function(name, one, two)
if name == "singleplayer" and one == "abc" and two == "def" then
return true
end
end)
end)).run("singleplayer", "bar abc and def") then
error("Test 1 failed")
end
local move = ChatCmdBuilder.build(function(cmd)
cmd:sub("move :target to :pos:pos", function(name, target, pos)
if name == "singleplayer" and target == "player1" and
pos.x == 0 and pos.y == 1 and pos.z == 2 then
return true
end
end)
end).run
if not move("singleplayer", "move player1 to 0,1,2") then
error("Test 2 failed")
end
if not move("singleplayer", "move player1 to (0,1,2)") then
error("Test 3 failed")
end
if not move("singleplayer", "move player1 to 0, 1,2") then
error("Test 4 failed")
end
if not move("singleplayer", "move player1 to 0 ,1, 2") then
error("Test 5 failed")
end
if not move("singleplayer", "move player1 to 0, 1, 2") then
error("Test 6 failed")
end
if not move("singleplayer", "move player1 to 0 ,1 ,2") then
error("Test 7 failed")
end
if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then
error("Test 8 failed")
end
if move("singleplayer", "move player1 to abc,def,sdosd") then
error("Test 9 failed")
end
if move("singleplayer", "move player1 to abc def sdosd") then
error("Test 10 failed")
end
if not (ChatCmdBuilder.build(function(cmd)
cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three)
if name == "singleplayer" and one + two == three then
return true
end
end)
end)).run("singleplayer", "does 1 plus 2 equal 3") then
error("Test 11 failed")
end
local checknegint = ChatCmdBuilder.build(function(cmd)
cmd:sub("checknegint :x:int", function(name, x)
return x
end)
end).run
if checknegint("checker","checknegint -2") ~= -2 then
error("Test 12 failed")
end
local checknegnumber = ChatCmdBuilder.build(function(cmd)
cmd:sub("checknegnumber :x:number", function(name, x)
return x
end)
end).run
if checknegnumber("checker","checknegnumber -3.3") ~= -3.3 then
error("Test 13 failed")
end
local checknegpos = ChatCmdBuilder.build(function(cmd)
cmd:sub("checknegpos :pos:pos", function(name, pos)
return pos
end)
end).run
local negpos = checknegpos("checker","checknegpos (-13.3,-4.6,-1234.5)")
if negpos.x ~= -13.3 or negpos.y ~= -4.6 or negpos.z ~= -1234.5 then
error("Test 14 failed")
end
local checktypes = ChatCmdBuilder.build(function(cmd)
cmd:sub("checktypes :int:int :number:number :pos:pos :word:word :text:text", function(name, int, number, pos, word, text)
return int, number, pos.x, pos.y, pos.z, word, text
end)
end).run
local int, number, posx, posy, posz, word, text
int, number, posx, posy, posz, word, text = checktypes("checker","checktypes -1 -2.4 (-3,-5.3,6.12) some text to finish off with")
--dprint(int, number, posx, posy, posz, word, text)
if int ~= -1 or number ~= -2.4 or posx ~= -3 or posy ~= -5.3 or posz ~= 6.12 or word ~= "some" or text ~= "text to finish off with" then
error("Test 15 failed")
end
dprint("All tests passed")
end
if not minetest then
run_tests()
end

View File

@ -0,0 +1,105 @@
local S = minetest.get_translator("folks")
ChatCmdBuilder.new("folks", function(cmd)
-- command for editing selected npc name
cmd:sub("edit name :name:text", function(pname, new_name)
local player = minetest.get_player_by_name(pname)
if player then
local meta = player:get_meta()
if meta then
local editing_npc = meta:get_string("folks_editing_npc")
if editing_npc == "" then
minetest.chat_send_player(pname, minetest.colorize("#ff0000", S("You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.")))
return
end
local npc = folks.backend.get_npc(editing_npc)
if npc then
folks.edit_npc_name(editing_npc, new_name)
meta:set_string("folks_editing_npc", "")
minetest.chat_send_player(pname, minetest.colorize("#00ff00", S("Edited NPC: @1", editing_npc)))
end
end
end
end)
-- command for editing selected npc name color
cmd:sub("edit name_color :color:text", function(pname, new_color)
local player = minetest.get_player_by_name(pname)
if player then
local meta = player:get_meta()
if meta then
local editing_npc = meta:get_string("folks_editing_npc")
if editing_npc == "" then
minetest.chat_send_player(pname, minetest.colorize("#ff0000", S("You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.")))
return
end
local npc = folks.backend.get_npc(editing_npc)
if npc then
folks.edit_npc_name_color(editing_npc, new_color)
meta:set_string("folks_editing_npc", "")
minetest.chat_send_player(pname, minetest.colorize("#00ff00", S("Edited NPC: @1", editing_npc)))
end
end
end
end)
-- command for editing selected npc texture
cmd:sub("edit texture :name:text", function(pname, new_texture)
local player = minetest.get_player_by_name(pname)
if player then
local meta = player:get_meta()
if meta then
local editing_npc = meta:get_string("folks_editing_npc")
if editing_npc == "" then
minetest.chat_send_player(pname, minetest.colorize("#ff0000", S("You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.")))
return
end
local npc = folks.backend.get_npc(editing_npc)
if npc then
folks.edit_npc_texture(editing_npc, new_texture)
meta:set_string("folks_editing_npc", "")
minetest.chat_send_player(pname, minetest.colorize("#00ff00", S("Edited NPC: @1", editing_npc)))
end
end
end
end)
-- command to bind name and texture of npc to a player, needs skins_collectible
cmd:sub("bind :name:text", function(pname, bind_to)
if folks.skins_c then
local player = minetest.get_player_by_name(pname)
if player then
local meta = player:get_meta()
if meta then
local editing_npc = meta:get_string("folks_editing_npc")
if editing_npc == "" then
minetest.chat_send_player(pname, minetest.colorize("#ff0000", S("You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.")))
return
end
local npc = folks.backend.get_npc(editing_npc)
if npc then
if folks.bind_npc_to_player(editing_npc, bind_to) then
minetest.chat_send_player(pname, minetest.colorize("#00ff00", S("Edited NPC: @1", editing_npc)))
else
minetest.chat_send_player(pname, minetest.colorize("#ff0000", S("Couldn't retrieve player texture (is it online?)")))
end
meta:set_string("folks_editing_npc", "")
end
end
end
else
minetest.chat_send_player(pname, minetest.colorize("#ff0000", S("You need skins_collectible to use this feature")))
end
end)
end, {
description = S("Manage folks npcs"),
privs = {
folks_admin = true
}
})

View File

@ -0,0 +1,117 @@
local S = minetest.get_translator("folks")
function folks.get_edit_formspec(npc_id)
local npc = folks.backend.get_npc(npc_id)
local escape = minetest.formspec_escape
local formspec = {}
if npc then
formspec = {
"formspec_version[3]",
"size[11,11]",
"label[4.85,1;", S("Edit Folk"), "]",
"field[2,2;3,0.75;folk_name;", S("Folk name"), ";", escape(npc._npc_name), "]",
"field[6,2;3,0.75;folk_name_color;", S("Folk Name Color"), ";", escape(npc._npc_name_color), "]",
"field[2,3.5;7,0.75;folk_texture;", S("Folk Texture (with or without .png)"), ";", escape(table.concat(npc._npc_textures)), "]",
"textarea[2,5;7,4;folk_messages;", S("Messages (every line is a message)"), ";", escape(table.concat(npc._npc_messages, "\n")), "]",
"button_exit[4,9.5;3,0.75;folk_save_edit;", S("Save"), "]",
"button_exit[9.5,0.2;1,0.75;folk_close_edit;X]",
}
end
return table.concat(formspec)
end
function folks.get_spawn_formspec()
local escape = minetest.formspec_escape
local npcs = folks.backend.get_npcs()
local dropdown_items = {}
for npc_id, npc in pairs(npcs) do
table.insert(dropdown_items, escape(npc._npc_name .. " - " .. npc_id))
end
local formspec = {
"formspec_version[3]",
"size[9,8.5]",
"label[3.7,1;", S("Spawn NPC"), "]",
"button_exit[7.5,0.5;1,0.75;folks_close_spawner;X]",
"dropdown[1,3;7,1;folks_select_npc;", table.concat(dropdown_items, ",") ,";1]",
"button_exit[3,7;3,0.75;folks_spawn_npc;", S("Spawn"), "]",
}
return table.concat(formspec)
end
local function handle_edit_formspec(player, formname, fields)
if fields.folk_save_edit then
local p_name = player:get_player_name()
if not minetest.check_player_privs(player:get_player_name(), { folks_admin=true }) then return end
if player then
local meta = player:get_meta()
if meta then
local editing_npc = meta:get_string("folks_editing_npc")
if editing_npc == "" then
minetest.chat_send_player(p_name, minetest.colorize("#ff0000", S("You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.")))
return
end
local npc = folks.backend.get_npc(editing_npc)
if npc then
local msgs = string.split(fields.folk_messages, "\n")
folks.edit_npc_name(editing_npc, fields.folk_name)
folks.edit_npc_name_color(editing_npc, fields.folk_name_color)
folks.edit_npc_texture(editing_npc, fields.folk_texture)
folks.edit_npc_messages(editing_npc, msgs)
meta:set_string("folks_editing_npc", "")
minetest.chat_send_player(p_name, minetest.colorize("#00ff00", S("Edited NPC: @1", editing_npc)))
end
end
end
end
if fields.folk_close_edit then
if player then
local meta = player:get_meta()
if meta then
minetest.chat_send_player(player:get_player_name(), minetest.colorize("#00ff00", S("Exited from NPC: @1", meta:get_string("folks_editing_npc"))))
meta:set_string("folks_editing_npc", "")
end
end
end
end
local function handle_spawn_formspec(player, formname, fields)
if fields.folks_spawn_npc then
local p_name = player:get_player_name()
if not minetest.check_player_privs(p_name, { folks_admin=true }) then return end
if fields.folks_select_npc ~= "" then
local npc_id = string.split(fields.folks_select_npc, " - ")[2]
if folks.spawn_npc(npc_id, player:get_pos()) then
minetest.chat_send_player(p_name, minetest.colorize("#00ff00", S("Spawned NPC: @1", npc_id)))
return
end
else
minetest.chat_send_player(p_name, minetest.colorize("#ff0000", S("No NPC selected")))
return
end
end
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "folks:edit_npc_formspec" then
handle_edit_formspec(player, formname, fields)
return
end
if formname == "folks:spawn_npc_formspec" then
handle_spawn_formspec(player, formname, fields)
return
end
return
end)

View File

@ -0,0 +1,54 @@
folks = {}
local version = "0.2.0"
local modpath = minetest.get_modpath("folks")
-- check for skins_collectible
if minetest.get_modpath("skins_collectible") then
folks.skins_c = true
else
folks.skins_c = false
end
folks.util = dofile(modpath .. "/util.lua")
dofile(modpath .. "/settings.lua")
if folks.backend_type == "storage" then
folks.backend = dofile(modpath .. "/backend_storage.lua")
elseif folks.backend_type == "sqlite" then
folks.backend = dofile(modpath .. "/backend_sqlite.lua")
else
minetest.log("error", "[FOLKS] Invalid storage type")
return
end
dofile(modpath .. "/api.lua")
dofile(modpath .. "/privs.lua")
dofile(modpath .. "/chatcmdbuilder.lua")
dofile(modpath .. "/commands.lua")
dofile(modpath .. "/formspecs.lua")
dofile(modpath .. "/items.lua")
dofile(modpath .. "/npc.lua")
minetest.after(0, function()
local npcs = folks.backend.load_npcs()
-- if npcs then
-- for id, npc in pairs(npcs) do
-- minetest.log("action", minetest.serialize(npc))
-- folks.backend.spawn_npc(npc)
-- end
-- end
end)
-- callback for skins_collectible
if folks.skins_c then
skins_collectible.register_on_set_skin(function(p_name, skin_ID)
local texture = skins_collectible.get_skin(skin_ID)
folks.backend.update_npc_texture(p_name, texture.texture)
end)
end
minetest.register_on_shutdown(function()
folks.backend.save_npcs()
end)
minetest.log("action", "[FOLKS] Mod initialised. Running version " .. version)

View File

@ -0,0 +1,112 @@
local S = minetest.get_translator("folks")
minetest.register_tool("folks:npc_creator", {
description = S("Use this to create a new NPC at your position"),
inventory_image = "npc_creator.png",
groups = {oddly_breakable_by_hand = "2"},
on_place = function() end,
on_drop = function() end,
on_use = function(itemstack, player, pointed_thing)
if not minetest.check_player_privs(player:get_player_name(), { folks_admin=true }) then return end
local npc_id = folks.backend.get_unique_id()
local new_npc = minetest.add_entity(player:get_pos(), "folks:npc", minetest.serialize({_npc_id = npc_id}))
if new_npc then
local entity = new_npc:get_luaentity()
if entity then
entity._npc_id = npc_id
folks.backend.add_npc(new_npc)
end
end
return
end
})
minetest.register_tool("folks:npc_editor", {
description = S("Use this to edit the NPC you click"),
inventory_image = "npc_editor.png",
groups = {oddly_breakable_by_hand = "2"},
on_place = function() end,
on_drop = function() end,
on_use = function(itemstack, player, pointed_thing)
if pointed_thing.type == "nothing" or pointed_thing.type == "node" then return end
if not minetest.check_player_privs(player:get_player_name(), { folks_admin=true }) then return end
local entity = pointed_thing.ref:get_luaentity()
if entity._isfolk then
if mobkit.is_alive(entity) and not entity._isremoved then
-- TODO: show formspec to edit clicked npc
local meta = player:get_meta()
meta:set_string("folks_editing_npc", entity._npc_id)
-- minetest.log(dump(entity._npc_object))
minetest.chat_send_player(player:get_player_name(), minetest.colorize("#00ff00", S("You are now editing NPC: @1", entity._npc_id)))
-- minetest.log(entity._npc_id or "none")
-- formspec
minetest.show_formspec(player:get_player_name(), "folks:edit_npc_formspec", folks.get_edit_formspec(entity._npc_id))
-- end formspec
end
end
return
end
})
minetest.register_tool("folks:npc_remover", {
description = S("Use this to remove the NPC you click"),
inventory_image = "npc_remover.png",
groups = {oddly_breakable_by_hand = "2"},
on_place = function() end,
on_drop = function() end,
on_use = function(itemstack, player, pointed_thing)
if pointed_thing.type == "nothing" or pointed_thing.type == "node" then return end
if not minetest.check_player_privs(player:get_player_name(), { folks_admin=true }) then return end
local entity = pointed_thing.ref:get_luaentity()
if entity._isfolk and not entity._isremoved then
if folks.backend.get_npc(entity._npc_id) then
for _, npc_obj in pairs(folks.backend.get_npcs_obj(entity._npc_id)) do
npc_obj:remove()
end
folks.backend.remove_npc(entity._npc_id)
end
return
else
return
end
end
})
minetest.register_tool("folks:npc_spawner", {
description = S("Use this to spawn an NPC that you had already created"),
inventory_image = "npc_spawner.png",
groups = {oddly_breakable_by_hand = "2"},
on_place = function() end,
on_drop = function() end,
on_use = function(itemstack, player, pointed_thing)
if not minetest.check_player_privs(player:get_player_name(), { folks_admin=true }) then return end
minetest.show_formspec(player:get_player_name(), "folks:spawn_npc_formspec", folks.get_spawn_formspec())
return
end
})
minetest.register_tool("folks:npc_despawner", {
description = S("Use this to despawn without deleting the NPC you click"),
inventory_image = "npc_despawner.png",
groups = {oddly_breakable_by_hand = "2"},
on_place = function() end,
on_drop = function() end,
on_use = function(itemstack, player, pointed_thing)
if pointed_thing.type == "nothing" or pointed_thing.type == "node" then return end
if not minetest.check_player_privs(player:get_player_name(), { folks_admin=true }) then return end
pointed_thing.ref:remove()
return
end
})

View File

@ -0,0 +1,40 @@
# textdomain: folks
### commands.lua ###
Couldn't retrieve player texture (is it online?)=Impossibile ottenere la skin del giocatore (è online?)
You need skins_collectible to use this feature=Hai bisogno di skins_collectible per usare questa funzione
Manage folks npcs=Gestisci gli npc di folks
### commands.lua ###
### formspecs.lua ###
You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.=Non stai modificando nessun NPC. Clicca l'NPC che vuoi modificare con l'oggetto NPC editor.
Edited NPC: @1=Modificato NPC: @1
### formspecs.lua ###
Edit Folk=Modifica Folk
Folk name=Nome Folk
Folk Name Color=Colore del nome Folk
Folk Texture (with or without .png)=Texture del Folk (con o senza .png)
Messages (every line is a message)=Messaggi (ogni riga è un messaggio)
Save=Salva
Spawn NPC=Piazza NPC
Spawn=Piazza
Exited from NPC: @1=Uscito dall'NPC: @1
Spawned NPC: @1=Piazzato NPC: @1
No NPC selected=Nessun NPC selezionato
### items.lua ###
Use this to create a new NPC at your position=Usa questo per creare un NPC nella tua posizione
Use this to edit the NPC you click=Usa questo per modificare l'NPC che clicchi
You are now editing NPC: @1=Stai modificando l'NPC: @1
Use this to remove the NPC you click=Usa questo per rimuovere l'NPC che clicchi
Use this to spawn an NPC that you had already created=Usa questo per piazzare un NPC che hai già creato
Use this to despawn without deleting the NPC you click=Usa questo per rimuovere l'NPC che clicchi senza eliminarlo
### NPC messages ###
Hey, I'm a Folk!=Ciao, sono un Folk!

View File

@ -0,0 +1,40 @@
# textdomain: folks
### commands.lua ###
Couldn't retrieve player texture (is it online?)=
You need skins_collectible to use this feature=
Manage folks npcs=
### commands.lua ###
### formspecs.lua ###
You are not editing an NPC. Click the NPC you want to edit with the NPC editor item.=
Edited NPC: @1=
### formspecs.lua ###
Edit Folk=
Folk name=
Folk Name Color=
Folk Texture (with or without .png)=
Messages (every line is a message)=
Save=
Spawn NPC=
Spawn=
Exited from NPC: @1=
Spawned NPC: @1=
No NPC selected=
### items.lua ###
Use this to create a new NPC at your position=
Use this to edit the NPC you click=
You are now editing NPC: @1=
Use this to remove the NPC you click=
Use this to spawn an NPC that you had already created=
Use this to despawn without deleting the NPC you click=
### NPC messages ###
Hey, I'm a Folk!=

View File

@ -0,0 +1,4 @@
name = folks
description = Adds npcs for RPG games
depends = mobkit
optional_depends = skins_collectible

Binary file not shown.

View File

@ -0,0 +1,98 @@
local S = minetest.get_translator("folks")
folks.default_npc = {
initial_properties = {
hp_max = 9999,
physical = true,
collide_with_objects = false,
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
visual_size = {x = 1, y = 1},
visual = "mesh",
mesh = "npc.b3d",
textures = {
folks.default_npc_texture,
},
pushable = false,
nametag = "Folk",
nametag_color = "#ffffff",
},
-- mobkit properties
timeout = 0,
max_hp = 9999,
buoyancy = -1,
lung_capacity = 200,
on_step = mobkit.stepfunc,
-- on_activate = mobkit.actfunc,
on_activate = function(self, staticdata, dtime_s)
mobkit.actfunc(self, staticdata, dtime_s)
self._npc_object = self.object
-- minetest.log("ACtivated")
-- minetest.log(dump(self._npc_object))
-- minetest.log(dump(mobkit.recall(self, "_npc_id")))
if staticdata ~= nil then
staticdata = minetest.deserialize(staticdata)
if mobkit.recall(self, "_npc_id") == nil then -- saves _npc_id to memory so I can get it every time the entity is activated
mobkit.remember(self, "_npc_id", staticdata._npc_id)
-- minetest.log(dump(mobkit.recall(self, "_npc_id")))
-- else
-- self.memory = folks.util.deepcopy(staticdata.memory)
end
local npc_id = mobkit.recall(self, "_npc_id")
local npc_data = folks.backend.get_npc(npc_id)
if npc_data then -- Here I set all the customizable properties
self.object:set_properties({
nametag = npc_data._npc_name,
nametag_color = npc_data._npc_name_color,
textures = npc_data._npc_textures,
})
self._npc_id = npc_id
folks.backend.set_npc_obj(npc_id, self.object)
end
end
end,
get_staticdata = mobkit.statfunc,
view_range = 24,
max_speed = 10,
jump_height = 10,
logic = function(self)
mobkit.vitals(self)
local prty = mobkit.get_queue_priority(self)
local pos = self.object:get_pos()
if prty < 10 then
local plyr = mobkit.get_nearby_player(self)
if plyr and vector.distance(pos,plyr:get_pos()) < 8 then
mobkit.lq_turn2pos(self, plyr:get_pos())
return
end
end
end,
on_rightclick = function(self, player)
local msg_index = folks.backend.get_next_message_index(self._npc_id, player:get_player_name())
local msg = folks.get_npc_message(self._npc_id, msg_index)
if msg then
local npc_name = folks.backend.get_npc(self._npc_id)._npc_name
minetest.chat_send_player(player:get_player_name(), minetest.colorize("#00ff00", npc_name .. ": " .. S(msg)))
end
end,
on_punch = function(self, puncher, t_from_last, tool_cap, dir, dmg)
-- minetest.log("action", minetest.serialize(tool_cap))
end,
-- custom properties
_isfolk = true,
_isremoved = false,
_npc_object = nil,
_bound_player = nil, -- nil or player name
_npc_messages = {"Hey, I'm a Folk!"} -- contains all npc's messages
}
minetest.register_entity("folks:npc", folks.default_npc)

View File

@ -0,0 +1,3 @@
minetest.register_privilege("folks_admin", {
descrption = "Allows you to spawn, edit and remove folks npcs"
})

View File

@ -0,0 +1,5 @@
-- backend type, can be "storage" or "sqlite"
folks.backend_type = "storage"
-- set default texture for your npcs
folks.default_npc_texture = "folks_default.png"

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

View File

@ -0,0 +1,31 @@
local util = {}
local charset = {} do -- [0-9a-zA-Z]
for c = 48, 57 do table.insert(charset, string.char(c)) end
for c = 65, 90 do table.insert(charset, string.char(c)) end
for c = 97, 122 do table.insert(charset, string.char(c)) end
end
function util.randomString(length)
if not length or length <= 0 then return '' end
math.randomseed(os.clock()^5)
return util.randomString(length - 1) .. charset[math.random(1, #charset)]
end
function util.deepcopy(obj, seen)
if type(obj) ~= 'table' then
return obj
end
if seen and seen[obj] then
return seen[obj]
end
local s = seen or {}
local copy = setmetatable({}, getmetatable(obj))
s[obj] = copy
for k, v in pairs(obj) do
copy[util.deepcopy(k, s)] = util.deepcopy(v, s)
end
return copy
end
return util