From 04dd3d514d9c4eb55addf79234ba6b6ad5264b1d Mon Sep 17 00:00:00 2001 From: AliasAlreadyTaken Date: Fri, 20 Nov 2020 11:45:50 +0100 Subject: [PATCH] first commit --- config.lua | 21 + dev/example.json | 107 + dev/example2.json | 80 + dev/n_1.json | 1 + dev/n_12.json | 77 + dev/n_20.json | 1 + dev/whatdowedo.txt | 16 + functions.lua | 2140 +++++++++++++++++ init.lua | 28 + mobs.lua | 333 +++ mod.conf | 6 + models/skinsdb_3d_armor_character_5.b3d | Bin 0 -> 178730 bytes privs.lua | 7 + readme.md | 0 textures/3d_armor_trans.png | Bin 0 -> 274 bytes textures/yl_npc_cape_default.png | Bin 0 -> 2274 bytes textures/yl_speak_up_bg_dialog.png | Bin 0 -> 146 bytes textures/yl_speak_up_bg_dialog_bl.png | Bin 0 -> 1052 bytes textures/yl_speak_up_bg_dialog_br.png | Bin 0 -> 1099 bytes textures/yl_speak_up_bg_dialog_hl.png | Bin 0 -> 1057 bytes textures/yl_speak_up_bg_dialog_hr.png | Bin 0 -> 1065 bytes textures/yl_speak_up_bg_dialog_tl.png | Bin 0 -> 1087 bytes textures/yl_speak_up_bg_dialog_tr.png | Bin 0 -> 1039 bytes textures/yl_speak_up_bg_dialog_vb.png | Bin 0 -> 934 bytes textures/yl_speak_up_bg_dialog_vt.png | Bin 0 -> 935 bytes textures/yl_speak_up_blank.png | Bin 0 -> 165 bytes textures/yl_speak_up_cape_test.png | Bin 0 -> 290 bytes textures/yl_speak_up_main_default.png | Bin 0 -> 17686 bytes textures/yl_speak_up_mask_cape.png | Bin 0 -> 214 bytes textures/yl_speak_up_mask_chest.png | Bin 0 -> 114 bytes textures/yl_speak_up_mask_head.png | Bin 0 -> 139 bytes textures/yl_speak_up_mask_rarm.png | Bin 0 -> 118 bytes textures/yl_speak_up_mask_rleg.png | Bin 0 -> 118 bytes textures/yl_speak_up_mask_shield.png | Bin 0 -> 214 bytes textures/yl_speak_up_shield_test.png | Bin 0 -> 207 bytes textures/yl_speak_up_staff_of_dawai_dawai.png | Bin 0 -> 264 bytes textures/yl_speak_up_staff_of_fashion.png | Bin 0 -> 1240 bytes textures/yl_speak_up_staff_of_game_over.png | Bin 0 -> 1216 bytes textures/yl_speak_up_staff_of_i_said_so.png | Bin 0 -> 263 bytes textures/yl_speak_up_staff_of_shut_up.png | Bin 0 -> 1214 bytes textures/yl_speak_up_transform.png | Bin 0 -> 191 bytes tools.lua | 147 ++ 42 files changed, 2964 insertions(+) create mode 100644 config.lua create mode 100644 dev/example.json create mode 100644 dev/example2.json create mode 100644 dev/n_1.json create mode 100644 dev/n_12.json create mode 100644 dev/n_20.json create mode 100644 dev/whatdowedo.txt create mode 100644 functions.lua create mode 100644 init.lua create mode 100644 mobs.lua create mode 100644 mod.conf create mode 100644 models/skinsdb_3d_armor_character_5.b3d create mode 100644 privs.lua create mode 100644 readme.md create mode 100644 textures/3d_armor_trans.png create mode 100644 textures/yl_npc_cape_default.png create mode 100644 textures/yl_speak_up_bg_dialog.png create mode 100644 textures/yl_speak_up_bg_dialog_bl.png create mode 100644 textures/yl_speak_up_bg_dialog_br.png create mode 100644 textures/yl_speak_up_bg_dialog_hl.png create mode 100644 textures/yl_speak_up_bg_dialog_hr.png create mode 100644 textures/yl_speak_up_bg_dialog_tl.png create mode 100644 textures/yl_speak_up_bg_dialog_tr.png create mode 100644 textures/yl_speak_up_bg_dialog_vb.png create mode 100644 textures/yl_speak_up_bg_dialog_vt.png create mode 100644 textures/yl_speak_up_blank.png create mode 100644 textures/yl_speak_up_cape_test.png create mode 100644 textures/yl_speak_up_main_default.png create mode 100644 textures/yl_speak_up_mask_cape.png create mode 100644 textures/yl_speak_up_mask_chest.png create mode 100644 textures/yl_speak_up_mask_head.png create mode 100644 textures/yl_speak_up_mask_rarm.png create mode 100644 textures/yl_speak_up_mask_rleg.png create mode 100644 textures/yl_speak_up_mask_shield.png create mode 100644 textures/yl_speak_up_shield_test.png create mode 100644 textures/yl_speak_up_staff_of_dawai_dawai.png create mode 100644 textures/yl_speak_up_staff_of_fashion.png create mode 100644 textures/yl_speak_up_staff_of_game_over.png create mode 100644 textures/yl_speak_up_staff_of_i_said_so.png create mode 100644 textures/yl_speak_up_staff_of_shut_up.png create mode 100644 textures/yl_speak_up_transform.png create mode 100644 tools.lua diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..1ed4999 --- /dev/null +++ b/config.lua @@ -0,0 +1,21 @@ +-- Do the NPCs talk right after they spawned? + +yl_speak_up.talk_after_spawn = true + +-- What shall we call the folder all the dialogs will reside in? + +yl_speak_up.path = "yl_speak_up_dialogs" + +-- Texts + +yl_speak_up.message_button_option_exit = "Farewell!" +yl_speak_up.message_button_option_prerequisites_not_met_default = "Locked answer" +yl_speak_up.message_tool_taken_because_of_lacking_priv = "We took the tool from you and logged this event. You used an admin item while lacking the neccessary priv npc_master" +yl_speak_up.text_new_dialog_id = "New dialog" +yl_speak_up.text_new_option_id = "New option" +yl_speak_up.text_new_prerequisite_id = "New prerequisite" +yl_speak_up.text_new_result_id = "New result" +yl_speak_up.text_version_warning = "You are using an outdated Minetest version!\nI will have a hard time talking to you properly, but I will try my best.\nYou can help me by upgrading to at least 5.3.0!\nGet it at https://minetest.net/downloads" +yl_speak_up.infotext = "Rightclick to talk" + +yl_speak_up.max_number_of_buttons = 7 \ No newline at end of file diff --git a/dev/example.json b/dev/example.json new file mode 100644 index 0000000..b144674 --- /dev/null +++ b/dev/example.json @@ -0,0 +1,107 @@ +{ + "n_id": "n_1", + "n_npc": "Mayor", + "n_description": "The Mayor of Haven", + "n_dialogs": { + "d_1": { + "d_id": "d_1", + "d_type": "text", + "d_text": "Hello Adventurer, I am the Mayor of Haven.", + "d_options": { + "o_1": { + "o_id": "o_1", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "", + "o_text_when_prerequisites_met": "", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + } + } + }, + "d_2": { + "d_id": "d_2", + "d_text": "What can I do for you?", + "d_options": { + "o_1": { + "o_id": "o_1", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "This option is not available.", + "o_text_when_prerequisites_met": "Let's talk about the plots in the city!", + "o_prerequisites": {}, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "3" + } + } + }, + "o_2": { + "o_id": "o_2", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "This option is not available.", + "o_text_when_prerequisites_met": "Let's talk about Airports!", + "o_prerequisites": {}, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "4" + } + } + } + } + }, + "d_3": { + "d_id": "d_3", + "d_text": "Text3", + "d_options": { + "o_1": { + "o_id": "o_1", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "This option is not available.", + "o_text_when_prerequisites_met": "Let's talk about the plots in the city!", + "o_prerequisites": {}, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "3" + } + } + }, + "o_2": { + "o_id": "o_2", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "This option is not available.", + "o_text_when_prerequisites_met": "Let's talk about Airports!", + "o_prerequisites": {}, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "4" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dev/example2.json b/dev/example2.json new file mode 100644 index 0000000..b4f8f23 --- /dev/null +++ b/dev/example2.json @@ -0,0 +1,80 @@ +{ + "n_id": "n_12", + "n_npc": "Mayor", + "n_description": "The Mayor of Haven", + "n_dialogs": { + "d_1": { + "d_id": "d_1", + "d_type": "text", + "d_text": "Hello Adventurer, I am the Mayor of Haven.", + "d_options": { + "o_1": { + "o_id": "o_1", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "o_1 notmet", + "o_text_when_prerequisites_met": "o_1 met", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + }, + "o_2": { + "o_id": "o_2", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "o_2 notmet", + "o_text_when_prerequisites_met": "o_2 met", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + }, + "o_3": { + "o_id": "o_3", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "o3 notmet", + "o_text_when_prerequisites_met": "o3 met", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + } + } + + } + + + } +} \ No newline at end of file diff --git a/dev/n_1.json b/dev/n_1.json new file mode 100644 index 0000000..699b7d6 --- /dev/null +++ b/dev/n_1.json @@ -0,0 +1 @@ +{ "n_id": "n_1", "n_npc": "Mayor", "n_description": "The Mayor of Haven", "n_dialogs": { "d_1": { "d_id": "d_1", "d_type": "text", "d_text": "Hello Adventurer, I am the Mayor of Haven.", "d_options": { "o_1": { "o_id": "o_1", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "", "o_text_when_prerequisites_met": "", "o_prerequisites": { "p_1": { "p_id": "p_1", "p_type": "auto", "p_value": "5" } }, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "2" } } } } }, "d_2": { "d_id": "d_2", "d_text": "What can I do for you?", "d_options": { "o_1": { "o_id": "o_1", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "This option is not available.", "o_text_when_prerequisites_met": "Let's talk about the plots in the city!", "o_prerequisites": {}, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "3" } } }, "o_2": { "o_id": "o_2", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "This option is not available.", "o_text_when_prerequisites_met": "Let's talk about Airports!", "o_prerequisites": {}, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "4" } } } } }, "d_3": { "d_id": "d_3", "d_text": "Text3", "d_options": { "o_1": { "o_id": "o_1", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "This option is not available.", "o_text_when_prerequisites_met": "Let's talk about the plots in the city!", "o_prerequisites": {}, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "3" } } }, "o_2": { "o_id": "o_2", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "This option is not available.", "o_text_when_prerequisites_met": "Let's talk about Airports!", "o_prerequisites": {}, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "4" } } } } } } } diff --git a/dev/n_12.json b/dev/n_12.json new file mode 100644 index 0000000..1ffb5f9 --- /dev/null +++ b/dev/n_12.json @@ -0,0 +1,77 @@ +{ + "n_id": "n_12", + "n_npc": "Mayor", + "n_description": "The Mayor of Haven", + "n_dialogs": { + "d_1": { + "d_id": "d_1", + "d_type": "text", + "d_text": "Hello Adventurer, I am the Mayor of Haven.", + "d_options": { + "o_1": { + "o_id": "o_1", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "o_1 notmet", + "o_text_when_prerequisites_met": "o_1 met", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + }, + "o_2": { + "o_id": "o_2", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "o_2 notmet", + "o_text_when_prerequisites_met": "o_2 met", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + }, + "o_3": { + "o_id": "o_3", + "o_hide_when_prerequisites_not_met": "false", + "o_grey_when_prerequisites_not_met": "true", + "o_text_when_prerequisites_not_met": "o3 notmet", + "o_text_when_prerequisites_met": "o3 met", + "o_prerequisites": { + "p_1": { + "p_id": "p_1", + "p_type": "auto", + "p_value": "5" + } + }, + "o_results": { + "r_1": { + "r_id": "r_1", + "r_type": "dialog", + "r_value": "2" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/dev/n_20.json b/dev/n_20.json new file mode 100644 index 0000000..2c9458c --- /dev/null +++ b/dev/n_20.json @@ -0,0 +1 @@ +{ "n_id": "n_12", "n_npc": "Mayor", "n_description": "The Mayor of Haven", "n_dialogs": { "d_1": { "d_id": "d_1", "d_type": "text", "d_text": "Hello Adventurer, I am the Mayor of Haven.", "d_options": { "o_1": { "o_id": "o_1", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "o_1 notmet", "o_text_when_prerequisites_met": "o_1 met", "o_prerequisites": { "p_1": { "p_id": "p_1", "p_type": "auto", "p_value": "5" } }, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "2" } } }, "o_2": { "o_id": "o_2", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "o_2 notmet", "o_text_when_prerequisites_met": "o_2 met", "o_prerequisites": { "p_1": { "p_id": "p_1", "p_type": "auto", "p_value": "5" } }, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "2" } } }, "o_3": { "o_id": "o_3", "o_hide_when_prerequisites_not_met": "false", "o_grey_when_prerequisites_not_met": "true", "o_text_when_prerequisites_not_met": "o3 notmet", "o_text_when_prerequisites_met": "o3 met", "o_prerequisites": { "p_1": { "p_id": "p_1", "p_type": "auto", "p_value": "5" } }, "o_results": { "r_1": { "r_id": "r_1", "r_type": "dialog", "r_value": "2" } } } } } } } diff --git a/dev/whatdowedo.txt b/dev/whatdowedo.txt new file mode 100644 index 0000000..8817976 --- /dev/null +++ b/dev/whatdowedo.txt @@ -0,0 +1,16 @@ +Next: + +Bugfixes and executing functions when talked to + +### + +https://codeberg.org/Hamlet/mobs_humans/src/branch/master/init.lua +https://github.com/minetest-mods/areas/blob/master/internal.lua +https://forum.minetest.net/viewtopic.php?t=20379 +https://forum.minetest.net/viewtopic.php?t=19108 + +### + +Make flat: + +echo $(cat example.json) > n_12.json diff --git a/functions.lua b/functions.lua new file mode 100644 index 0000000..caa3250 --- /dev/null +++ b/functions.lua @@ -0,0 +1,2140 @@ +--### +-- Init +--### + +function yl_speak_up.init_mob_table() + return false +end + +minetest.register_on_leaveplayer( + function(player) + yl_speak_up.speak_to[player:get_player_name()] = nil + end +) + +minetest.register_on_joinplayer( + function(player) + yl_speak_up.speak_to[player:get_player_name()] = nil + end +) + +--### +-- Debug +--### + +yl_speak_up.debug = true + +local function say(text) + if yl_speak_up.debug then + --minetest.chat_send_all(text) + minetest.log("action", "[MOD] yl_speak_up: " .. text) + end +end + +--### +-- Helpers +--### + +local function get_prerequisite_types() + --local t_gpt = {"item", "quest", "function", "auto", "delete"} + local t_gpt = {"item", "function", "delete"} + local s_gpt = "" + for _, v in pairs(t_gpt) do + s_gpt = s_gpt .. v .. "," + end + + s_gpt = s_gpt:sub(1, -2) -- cut last comma + + return s_gpt, t_gpt +end + +local function get_result_types() + --local t_grt = {"dialog", "give_item", "quest", "function", "auto", "delete"} + local t_grt = {"dialog","give_item","take_item","move","function","delete"} + local s_grt = "" + for _, v in pairs(t_grt) do + s_grt = s_grt .. v .. "," + end + + s_grt = s_grt:sub(1, -2) -- cut last comma + + return s_grt, t_grt +end + +local function get_number_from_id(any_id) + return string.split(any_id, "_")[2] +end + +local function save_path(n_id) + return yl_speak_up.worldpath .. yl_speak_up.path .. DIR_DELIM .. n_id .. ".json" +end + +local function get_error_message() + local formspec = { + "formspec_version[3]", + "size[13.4,8.5]", + "bgcolor[#FF0000]", + "label[0.2,0.35;Please save a NPC file first]", + "button_exit[0.2,7.7;3,0.75;button_back;Back]" + } + + return table.concat(formspec, "") +end + +local function find_next_id(t) + local start_id = 1 + + if t == nil then + return start_id + end + + local keynum = 1 + for k, _ in pairs(t) do + local keynum = tonumber(get_number_from_id(k)) + if keynum >= start_id then + start_id = keynum + 1 + end + end + return start_id +end + +local function sanitize_sort(options, value) + local retval = value + + if value == "" or value == nil or tonumber(value) == nil then + local temp = 0 + for k, v in pairs(options) do + if v.o_sort ~= nil then + if tonumber(v.o_sort) > temp then + temp = tonumber(v.o_sort) + end + end + end + retval = tostring(temp + 1) + end + return retval +end + +--### +--Load and Save +--### + +local function save_dialog(n_id, dialog) + if type(n_id) ~= "string" or type(dialog) ~= "table" then + return false + end + local p = save_path(n_id) + local content = minetest.write_json(dialog) + return minetest.safe_file_write(p, content) +end + + +local function load_dialog(n_id) -- returns the saved dialog + local p = save_path(n_id) + + local file, err = io.open(p, "r") + if err then + return {} + end + io.input(file) + local content = io.read() + local dialog = minetest.parse_json(content) + io.close(file) + + if type(dialog) ~= "table" then + dialog = {} + end + + return dialog +end + +local function fields_to_dialog(pname, fields) + local n_id = yl_speak_up.speak_to[pname].n_id + local dialog = load_dialog(n_id) + local save_d_id = "" + + if next(dialog) == nil then -- No file found. Let's create the basic values + dialog = {} + dialog.n_dialogs = {} + end + + if dialog.n_dialogs == nil or next(dialog.n_dialogs) == nil then --No dialogs found. Let's make a table + dialog.n_dialogs = {} + end + + if fields.d_text ~= "" then -- If there is dialog text, then save new or old dialog + if fields.d_id == yl_speak_up.text_new_dialog_id then --New dialog -- + -- Find highest d_id and increase by 1 + save_d_id = "d_" .. find_next_id(dialog.n_dialogs) + + -- Initialize empty dialog + dialog.n_dialogs[save_d_id] = {} + else -- Already existing dialog + save_d_id = fields.d_id + end + -- Change dialog + dialog.n_dialogs[save_d_id].d_id = save_d_id + dialog.n_dialogs[save_d_id].d_type = "text" + dialog.n_dialogs[save_d_id].d_text = fields.d_text + dialog.n_dialogs[save_d_id].d_sort = fields.d_sort + end + + --Context + yl_speak_up.speak_to[pname].d_id = save_d_id + + -- Just in case the NPC vlaues where changed or set + dialog.n_id = n_id + dialog.n_description = fields.n_description + dialog.n_npc = fields.n_npc + + return dialog +end + +local function options_to_dialog(pname) + local dialog = yl_speak_up.speak_to[pname].dialog + + local n_id = yl_speak_up.speak_to[pname].n_id + local d_id = yl_speak_up.speak_to[pname].d_id + local o_id = yl_speak_up.speak_to[pname].o_id + local p_id = yl_speak_up.speak_to[pname].p_id + local r_id = yl_speak_up.speak_to[pname].r_id + + if yl_speak_up.speak_to[pname].d_text then + dialog.n_dialogs[d_id].d_text = yl_speak_up.speak_to[pname].d_text + end + + --Find the o_id to save to + local future_o_id = "" + if yl_speak_up.speak_to[pname].o_id ~= nil and yl_speak_up.speak_to[pname].o_id ~= yl_speak_up.text_new_option_id then + future_o_id = yl_speak_up.speak_to[pname].o_id + else + future_o_id = "o_" .. find_next_id(dialog.n_dialogs[d_id].d_options) + + if dialog.n_dialogs[d_id].d_options == nil then + dialog.n_dialogs[d_id].d_options = {} + end + + dialog.n_dialogs[d_id].d_options[future_o_id] = {} + end + + --Find the p_id to save to + local future_p_id = "" + if + yl_speak_up.speak_to[pname].p_id ~= nil and + yl_speak_up.speak_to[pname].p_id ~= yl_speak_up.text_new_prerequisite_id + then + future_p_id = yl_speak_up.speak_to[pname].p_id + else + future_p_id = "p_" .. find_next_id(dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites) + if future_p_id == "p_1" then + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites = {} + end + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id] = {} + end + + --Find the r_id to save to + local future_r_id = "" + if yl_speak_up.speak_to[pname].r_id ~= nil and yl_speak_up.speak_to[pname].r_id ~= yl_speak_up.text_new_result_id then + future_r_id = yl_speak_up.speak_to[pname].r_id + else + future_r_id = "r_" .. find_next_id(dialog.n_dialogs[d_id].d_options[future_o_id].o_results) + if future_r_id == "r_1" then + dialog.n_dialogs[d_id].d_options[future_o_id].o_results = {} + end + dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id] = {} + end + + --Edit options + dialog.n_dialogs[d_id].d_options[future_o_id].o_id = future_o_id + dialog.n_dialogs[d_id].d_options[future_o_id].o_hide_when_prerequisites_not_met = + yl_speak_up.speak_to[pname].o_hide or "false" + dialog.n_dialogs[d_id].d_options[future_o_id].o_grey_when_prerequisites_not_met = + yl_speak_up.speak_to[pname].o_grey or "false" + local s = sanitize_sort(dialog.n_dialogs[d_id].d_options, yl_speak_up.speak_to[pname].o_sort) + dialog.n_dialogs[d_id].d_options[future_o_id].o_sort = s + dialog.n_dialogs[d_id].d_options[future_o_id].o_text_when_prerequisites_not_met = + yl_speak_up.speak_to[pname].o_not_met or "" + dialog.n_dialogs[d_id].d_options[future_o_id].o_text_when_prerequisites_met = + yl_speak_up.speak_to[pname].o_met or "" + + --Edit prerequisites + + --Do we delete the prerequisite? + if + yl_speak_up.speak_to[pname].p_type == "delete" and yl_speak_up.speak_to[pname].p_id ~= nil and + yl_speak_up.speak_to[pname].p_id ~= yl_speak_up.text_new_prerequisite_id + then + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id] = nil + else + if yl_speak_up.speak_to[pname].p_value ~= nil and yl_speak_up.speak_to[pname].p_value ~= "" then + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id].p_id = future_p_id + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id].p_type = + yl_speak_up.speak_to[pname].p_type + dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id].p_value = + yl_speak_up.speak_to[pname].p_value + end + end + + --Do we delete the result? + if + yl_speak_up.speak_to[pname].r_type == "delete" and yl_speak_up.speak_to[pname].r_id ~= nil and + yl_speak_up.speak_to[pname].r_id ~= yl_speak_up.text_new_result_id + then + dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id] = nil + else + if yl_speak_up.speak_to[pname].r_value ~= nil and yl_speak_up.speak_to[pname].r_value ~= "" then + dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id].r_id = future_r_id + dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id].r_type = + yl_speak_up.speak_to[pname].r_type + dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id].r_value = + yl_speak_up.speak_to[pname].r_value + end + end + + return dialog +end + +local function delete_dialog(n_id, d_id) + if d_id == yl_speak_up.text_new_dialog_id then + return false + end -- We don't delete "New dialog" + + local dialog = load_dialog(n_id) + + dialog.n_dialogs[d_id] = nil + + save_dialog(n_id, dialog) +end + +local function delete_option(n_id, d_id, o_id) + if d_id == yl_speak_up.text_new_dialog_id then + return false + end -- We don't delete "New dialog" Also, something might have gone wrong here. + if o_id == yl_speak_up.text_new_option_id then + return false + end -- We don't delete "New option" + + local dialog = load_dialog(n_id) + + dialog.n_dialogs[d_id].d_options[o_id] = nil + + save_dialog(n_id, dialog) +end + +local function delete_prerequisite(n_id, d_id, o_id, p_id) + if d_id == yl_speak_up.text_new_dialog_id then + return false + end -- We don't delete "New dialog" Also, something might have gone wrong here. + if o_id == yl_speak_up.text_new_option_id then + return false + end -- We don't delete "New option" Also, something might have gone wrong here. + if p_id == yl_speak_up.text_new_prerequisite_id then + return false + end + + local dialog = load_dialog(n_id) + + dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id] = nil + + save_dialog(n_id, dialog) +end + +local function delete_result(n_id, d_id, o_id, r_id) + if d_id == yl_speak_up.text_new_dialog_id then + return false + end -- We don't delete "New dialog" Also, something might have gone wrong here. + if o_id == yl_speak_up.text_new_option_id then + return false + end -- We don't delete "New option" Also, something might have gone wrong here. + if r_id == yl_speak_up.text_new_result_id then + return false + end + + local dialog = load_dialog(n_id) + + dialog.n_dialogs[d_id].d_options[o_id].o_resultss[r_id] = nil + + save_dialog(n_id, dialog) +end + +local function calculate_displayable_options(pname, d_options) + -- Let's go through all the options and see if we need to display them to the user + + local retval = {} + + local player = minetest.get_player_by_name(pname) + + if d_options == nil then + return {} + end + + for o_k, o_v in pairs(d_options) do + --minetest.chat_send_all("#####"..o_k.."#####") + local o_p_met = true + if o_v.o_prerequisites == nil then + --minetest.chat_send_all("display this because no prereq:"..dump(o_v)) + else + --minetest.chat_send_all("prereqs exists") + if next(o_v.o_prerequisites) == nil then + --minetest.chat_send_all("prereqs exist, but empty") + else + --minetest.chat_send_all("prereqs exists and not empty") + --minetest.chat_send_all("if all prereqs are met, then we can display the option") + local p_met = {} + for p_k, p_v in pairs(o_v.o_prerequisites) do + --minetest.chat_send_all("this is in a single prereq: "..dump(p_v)) + p_met[p_k] = false + local p_id = p_v.p_id + if p_v.p_type == "item" then + --minetest.chat_send_all("item! Does the player have this thing?") + if player:get_inventory():contains_item("main", p_v.p_value) then + --minetest.chat_send_all("found item:"..p_v.p_value) + p_met[p_k] = true + end + end + if p_v.p_type == "quest" then + --minetest.chat_send_all("quest! let's call the quest api?") + p_met[p_k] = false + end + if p_v.p_type == "function" then + + local code = p_v.p_value + if code:byte(1) == 27 then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " because of illegal bytecode for player "..pname) + else + minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown because of illegal bytecode") + end + end + + local f, msg = loadstring("return function(playername) " .. code .. " end") + + if not f then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " for player "..pname) + else + minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown") + end + else + + + local func = f() + + local ok, ret = pcall(func,pname) + + if not ok then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..p_id.." :"..dump(code) .. " for player "..pname) + else + minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..p_id.." :"..dump(code) .. " for player unknown") + end + end + + if type(ret) == "boolean" then + p_met[p_k] = ret + end + + end + + + + end + if p_v.p_type == "auto" then + --minetest.chat_send_all("auto! what shall we do now?") + p_met[p_k] = true + end + end + -- is there a "false" in the p_met ? + for m_k, m_v in pairs(p_met) do + --minetest.chat_send_all(o_k..",m_v="..dump(m_v)) + if m_v == false then + o_p_met = false + end + end + end + end + -- Can we display this option? + retval[o_k] = o_p_met + --[[ + if o_p_met == true then + minetest.chat_send_all("Prereqs say YES") + retval[o_k] = o_p_met + else + minetest.chat_send_all("Prereqs say NOO") + end + ]]-- + end + return retval +end + +local function calculate_portrait(pname, n_id) + local tex = yl_speak_up.speak_to[pname].textures + + local head = "" + + head = head .. "[combine:8x8:-8,-8=" .. tex[2] .. ":-40,-8=" .. tex[2] + + return head +end + +--### +--Formspecs +--### + +-- get formspecs + +-- dialog + +local function get_fs_setdialog(clicker, n_id, d_id) + local dialog = load_dialog(n_id) + + local items = yl_speak_up.text_new_dialog_id + local count = 1 + local d_sort = "" + local text = "" + + if next(dialog) == nil then -- file does not exist + dialog.n_id = n_id + dialog.n_npc = "" + dialog.n_description = "" + elseif dialog.n_dialogs == nil then -- file does exist, but no dialogs are set + dialog.n_id = n_id + text = "" + else -- file exists and there's already content + local n = 1 + for k, v in pairs(dialog.n_dialogs) do + n = n + 1 + if k == d_id then + count = n + d_sort = v.d_sort or "" + text = v.d_text or "" + end + items = items .. "," .. v.d_id + end + + text = minetest.formspec_escape(text) + end + + local formspec = { + "formspec_version[3]", + "size[13.4,8.5]", + "label[0.2,0.35;", + dialog.n_id, + "]", + "field[1,0.1;4,0.5;n_npc;;", + dialog.n_npc, + "]", + "field[5.2,0.1;8,0.5;n_description;;", + dialog.n_description, + "]", + "dropdown[5.2,0.75;5,0.75;d_id;", + items, + ";", + count, + "]", + "label[0.2,1;Sort]", + "field[1.0,0.75;4,0.5;d_sort;;", + d_sort, + "]", + "textarea[0.2,1.6;13,6;d_text;;", + text, + "]", + "button_exit[0.2,7.7;3,0.75;button_cancel;Cancel]", + "button[4,7.7;3,0.75;button_delete;Delete]", + "button[7.1,7.7;3,0.75;button_option;Options]", + "button[10.2,7.7;3,0.75;button_save;Save]", + -- tooltips + + "tooltip[n_npc;n_npc: The name of the NPC;#FFFFFF;#000000]", + "tooltip[n_description;n_description: A description for the NPC;#FFFFFF;#000000]", + "tooltip[d_sort;d_sort: Make this 0 on your dialog if you want it to be shown the first time a player talks to the NPC\nNegative values are ignored\n;#FFFFFF;#000000]", + "tooltip[5.2,0.75;5,0.75;d_id: Dialog Id;#FFFFFF;#000000]", + "tooltip[d_text;d_text: Dialog text. What the NPC says to you;#FFFFFF;#000000]" + } + + return table.concat(formspec, "") +end + +-- options + +local function get_fs_optiondialog(player, n_id, d_id, o_id, p_id, r_id) + local dialog = load_dialog(n_id) + local pname = player:get_player_name() + + if next(dialog) == nil or d_id == yl_speak_up.text_new_dialog_id or dialog.n_dialogs == nil then -- file does not exist or the user sent the New dialog + return get_error_message(n_id) + end + + -- default values + local out = {} + + -- npc + out.n_id = n_id + + -- dialog + out.d_id = yl_speak_up.text_new_dialog_id + out.d_text = "Dialog Text" + + -- option + out.o_id_items = yl_speak_up.text_new_option_id + out.o_id_count = 1 + out.o_hide = "false" + out.o_grey = "false" + out.o_sort = "" + out.o_met = "Text when conditions are met" + out.o_not_met = "Text when conditions are not met" + + -- prerequisite + out.p_id_items = yl_speak_up.text_new_prerequisite_id + out.p_id_count = 1 + out.p_type_items, out.p_type_items_table = get_prerequisite_types() + out.p_type_count = 1 + out.p_value = "" + + -- result + out.r_id_items = yl_speak_up.text_new_result_id + out.r_id_count = 1 + out.r_type_items = get_result_types() + out.r_type_count = 1 + out.r_value = "" + + --dialogs + for k, v in pairs(dialog.n_dialogs) do + if k == d_id then + out.d_id = v.d_id + out.d_text = minetest.formspec_escape(v.d_text):trim() + end + end + + --options + if dialog.n_dialogs[d_id].d_options ~= nil then + local o_n = 1 + for o_k, o_v in pairs(dialog.n_dialogs[d_id].d_options) do + o_n = o_n + 1 + if o_k == o_id then + out.o_id_count = o_n + end + out.o_id_items = out.o_id_items .. "," .. o_v.o_id + end + if dialog.n_dialogs[d_id].d_options[o_id] then + if dialog.n_dialogs[d_id].d_options[o_id].o_hide_when_prerequisites_not_met then + out.o_hide = dialog.n_dialogs[d_id].d_options[o_id].o_hide_when_prerequisites_not_met + end + if dialog.n_dialogs[d_id].d_options[o_id].o_grey_when_prerequisites_not_met then + out.o_grey = dialog.n_dialogs[d_id].d_options[o_id].o_grey_when_prerequisites_not_met + end + if dialog.n_dialogs[d_id].d_options[o_id].o_sort then + out.o_sort = dialog.n_dialogs[d_id].d_options[o_id].o_sort + end + if dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met then + out.o_met = + minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met):trim( + + ) + end + if dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_not_met then + out.o_not_met = + minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_not_met):trim( + + ) + end + + -- prerequisite + + --if dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites ~= nil and p_id == yl_speak_up.text_new_prerequisite_id then -- new prerequisite + --end + if dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites ~= nil then + local p_n = 1 + for p_k, p_v in pairs(dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites) do + p_n = p_n + 1 + if p_k == p_id then + out.p_id_count = p_n + end + out.p_id_items = out.p_id_items .. "," .. p_v.p_id + end + + out.p_type_items, out.p_type_items_table = get_prerequisite_types() + + if + dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id] ~= nil and + dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id].p_type ~= nil + then + local p_type_n = 1 + for _, p_type_v in pairs(out.p_type_items_table) do + if p_type_v == dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id].p_type then + out.p_type_count = p_type_n + end + p_type_n = p_type_n + 1 + end + + out.p_value = + minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id].p_value):trim( + + ) + end + end + + -- result + if dialog.n_dialogs[d_id].d_options[o_id].o_results ~= nil then + local r_n = 1 + for r_k, r_v in pairs(dialog.n_dialogs[d_id].d_options[o_id].o_results) do + r_n = r_n + 1 + if r_k == r_id then + out.r_id_count = r_n + end + out.r_id_items = out.r_id_items .. "," .. r_v.r_id + end + + out.r_type_items, out.r_type_items_table = get_result_types() + + if + dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id] ~= nil and + dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id].r_type ~= nil + then + local r_type_n = 1 + for _, r_type_v in pairs(out.r_type_items_table) do + if r_type_v == dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id].r_type then + out.r_type_count = r_type_n + end + r_type_n = r_type_n + 1 + end + + out.r_value = + minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id].r_value):trim() + end + end + end + end + + local formspec = { + "formspec_version[3]", + "size[13.4,8.5]", + -- npc + "field[-10,-10;4,0.5;n_id;;", + out.n_id, + "]", + -- dialog + "field[-10,-10;4,0.5;d_id;;", + out.d_id, + "]", + "label[0.2,0.35;", + out.d_id, + "]", + "field[1,0.1;12.2,0.5;d_text;;", + out.d_text, + "]", + -- option + "dropdown[0.2,0.75;2,0.75;o_id;", + out.o_id_items, + ";", + out.o_id_count, + "]", + "checkbox[0.2,1.85;o_hide;Hide;", + out.o_hide, + "]", + "checkbox[0.2,2.35;o_grey;Grey;", + out.o_grey, + "]", + "field[1.6,2.05;0.75,0.5;o_sort;Sort;", + out.o_sort, + "]", + "textarea[2.4,0.75;10.8,0.95;o_met;;", + out.o_met, + "]", + "textarea[2.4,1.75;10.8,0.95;o_not_met;;", + out.o_not_met, + "]", + -- prerequisite + "dropdown[0.2,2.8;3,0.75;p_id;", + out.p_id_items, + ";", + out.p_id_count, + "]", + "dropdown[0.2,3.8;3,0.75;p_type;", + out.p_type_items, + ";", + out.p_type_count, + "]", + "textarea[3.4,2.8;9.8,2.1;p_value;;", + out.p_value, + "]", + -- result + "dropdown[0.2,5;3,0.75;r_id;", + out.r_id_items, + ";", + out.r_id_count, + "]", + "dropdown[0.2,6;3,0.75;r_type;", + out.r_type_items, + ";", + out.r_type_count, + "]", + "textarea[3.4,5;9.8,2.1;r_value;;", + out.r_value, + "]", + -- buttons + "button[0.2,7.7;3,0.75;button_back;Back]", + "button[7.1,7.7;3,0.75;button_delete;Delete OPTION]", + "button[10.2,7.7;3,0.75;button_save;Save]", + -- tooltips + + "tooltip[d_text;d_text: Dialog text. What the NPC says to you\n\n", + out.d_text, + ";#FFFFFF;#000000]", + "tooltip[0.2,0.75;2,0.75;o_id: Option Id;#FFFFFF;#000000]", + "tooltip[o_met;o_met: Option text when prerequisites are met\n(What you say to the NPC)\n\n", + out.o_met, + ";#FFFFFF;#000000]", + "tooltip[o_not_met;o_not_met: Option text when prerequisites are not met\n(What you say to the NPC instead)\n\n", + out.o_not_met, + ";#FFFFFF;#000000]", + "tooltip[o_hide;o_hide: If checked, this option is hidden when prerequistes are not met;#FFFFFF;#000000]", + "tooltip[o_grey;o_grey: If checked, this option is shown when prerequistes are not met, but grey and not selectable;#FFFFFF;#000000]", + "tooltip[o_sort;o_sort: The lower the number, the higher up in the list this option goes\nNegative values are ignored;#FFFFFF;#000000]", + "tooltip[0.2,2.8;3,0.75;p_id: Prerequisite Id;#FFFFFF;#000000]", + "tooltip[0.2,3.8;3,0.75;p_type: Defines what the p_value stands for.\n\n", + "item: requires the user to have this item in the inventory\n", + "quest: requires the user to have completed a quest with this id\n", + "function: executes the given LUA function which must return true or false. true = prerequisite met\n", + "auto: ???\n", + "delete: Deletes the chosen prerequiste when the dialog is saved", + ";#FFFFFF;#000000]", + "tooltip[p_value;p_value: This is evaluated to decide whether the prerequisites are met or not;#FFFFFF;#000000]", + "tooltip[0.2,5;3,0.75;r_id: Result Id;#FFFFFF;#000000]", + "tooltip[0.2,6;3,0.75;r_type: Defines what the r_value stands for.\n\n", + "dialog: forwards the user to the given dialog d_id\n", + "item: places this item in the inventory of the user\n", + "quest: starts a quest with this id for the user\n", + "function: executes the given LUA function\n", + "auto: automatically forwards the user to the given dialog d_id after a given time\n", + "delete: Deletes the chosen prerequiste when the dialog is saved", + ";#FFFFFF;#000000]", + "tooltip[r_value;r_value: This is what happens when the user chooses this option;#FFFFFF;#000000]" + } + + return table.concat(formspec, "") +end + +-- talk + +local function get_fs_talkdialog(player, n_id, d_id) + local pname = player:get_player_name() + local dialog = yl_speak_up.speak_to[pname].dialog + local context_d_id = yl_speak_up.speak_to[pname].d_id + local active_dialog + + if not player and not player:is_player() then + minetest.log( + "action", + "[MOD] yl_speak_up: User " .. + pname .. + " talked to unconfigured NPC with ID n_" .. + n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0) + ) + return get_error_message() + end + + --[[ If we have an explicit call for a certain d_id, we grab it from parameters. + If not, we grab in from context. + When neither are present, we grab it from d_sort + ]]-- + + local c_d_id + + if d_id ~= nil then + active_dialog = dialog.n_dialogs[d_id] + c_d_id = d_id + elseif yl_speak_up.speak_to[pname].d_id ~= nil then + c_d_id = yl_speak_up.speak_to[pname].d_id + active_dialog = dialog.n_dialogs[c_d_id] + elseif dialog.n_dialogs ~= nil then + -- Find the dialog with d_sort = 0 + local lowest_sort = nil + for k, v in pairs(dialog.n_dialogs) do + if tonumber(v.d_sort) ~= nil then + if not lowest_sort or tonumber(v.d_sort) < tonumber(lowest_sort) or v.d_sort == "0" then + if tonumber(v.d_sort) >= 0 then + lowest_sort = v.d_sort + active_dialog = v + c_d_id = k + end + end + end + end + else + minetest.log( + "action", + "[MOD] yl_speak_up: User " .. + pname .. + " talked to unconfigured NPC with ID n_" .. + n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0) + ) + return get_error_message() + end + + if c_d_id == nil then return get_error_message() end + + yl_speak_up.speak_to[pname].d_id = c_d_id + + -- Now we have a dialog to display to the user + + local allowed = calculate_displayable_options(pname, active_dialog.d_options) + yl_speak_up.speak_to[pname].allowed = allowed + + local portrait = calculate_portrait(pname, n_id) + + local formspec_v = minetest.get_player_information(pname).formspec_version + local protocol_v = minetest.get_player_information(pname).protocol_version + local formspec = {} + local h + + if formspec_v >= 4 then + formspec = { + "formspec_version[3]", + "size[57,33]", + "position[0,0.45]", + "anchor[0,0.45]", + "no_prepend[]", + "bgcolor[#00000000;false]", + -- Container + + "container[2,0.75]", + -- Background + + "background[0,0;20,23;yl_speak_up_bg_dialog.png;false]", + "background[0,24;54.5,7.5;yl_speak_up_bg_dialog.png;false]", + -- Frame Dialog + + "image[-0.25,-0.25;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.25,22.25;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[19.25,-0.25;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[19.25,22.25;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.25,0.75;1,21.5;yl_speak_up_bg_dialog_hl.png]", + "image[19.25,0.75;1,21.5;yl_speak_up_bg_dialog_hr.png]", + "image[0.75,-0.25;18.5,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.75,22.25;18.5,1;yl_speak_up_bg_dialog_vb.png]", + -- Frame Options + + "image[-0.25,23.75;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.25,30.75;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[53.75,23.75;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[53.75,30.75;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.25,24.75;1,6;yl_speak_up_bg_dialog_hl.png]", + "image[53.75,24.75;1,6;yl_speak_up_bg_dialog_hr.png]", + "image[0.75,23.75;53,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.75,30.75;53,1;yl_speak_up_bg_dialog_vb.png]", + "style_type[button;bgcolor=#a37e45]", + "style_type[button_exit;bgcolor=#a37e45]", -- Dialog + + --[[ + "background[-1,-1;22,25;yl_speak_up_bg_dialog2.png;false]", + "background[-1,23;58,10;yl_speak_up_bg_dialog2.png;false]", + "style_type[button;bgcolor=#a37e45]", + ]]-- + "label[0.3,0.6;", + minetest.formspec_escape(dialog.n_npc), + "]", + "label[0.3,1.8;", + minetest.formspec_escape(dialog.n_description), + "]", + "image[15.5,0.5;4,4;", + portrait, + "]", + "hypertext[0.2,5;19.6,17.8;d_text;", + minetest.formspec_escape(active_dialog.d_text) .. "\n", + "]", + "tooltip[d_text;", + minetest.formspec_escape(active_dialog.d_text):trim(), + ";#000000;#FFFFFF]", + "scrollbaroptions[min=0;max=14;smallstep=1;largestep=2;arrows=show]", + "scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]", + "scroll_container[0,24;56,7;scr0;vertical;1]" + } + h = -0.8 + + -- Let#s sort the options by o_sort + if active_dialog ~= nil and active_dialog.d_options ~= nil then + local sorted_buttons = {} + for _, ad_v in pairs(active_dialog.d_options) do + sorted_buttons[tonumber(ad_v.o_sort)] = ad_v + end + + for _, sb_v in pairs(sorted_buttons) do + if allowed[sb_v.o_id] == true then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. sb_v.o_id .. ";]") + table.insert( + formspec, + "tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_met .. "]" + ) + local l = h + 0.45 + table.insert(formspec, "label[0.7," .. l .. ";" .. sb_v.o_text_when_prerequisites_met .. "]") + else + if sb_v.o_hide_when_prerequisites_not_met == "true" then + else + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met == "" + then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. sb_v.o_id .. ";]") + table.insert( + formspec, + "tooltip[button_" .. + sb_v.o_id .. + ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" + ) + local l = h + 0.45 + table.insert( + formspec, + "label[0.7," .. + l .. ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" + ) + table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]") + end + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. sb_v.o_id .. ";]") + table.insert( + formspec, + "tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + local l = h + 0.45 + table.insert( + formspec, + "label[0.7," .. l .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]") + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met == "" + then + -- no hide, no grey, no text + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + -- no grey, but text + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. sb_v.o_id .. ";]") + table.insert( + formspec, + "tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + local l = h + 0.45 + table.insert( + formspec, + "label[0.7," .. l .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + end + end + end + end + end + h = h + 1 + table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_exit;]") + table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]") + local l = h + 0.45 + table.insert(formspec, "label[0.7," .. l .. ";" .. yl_speak_up.message_button_option_exit .. "]") + + table.insert(formspec, "scroll_container_end[]") + + table.insert(formspec, "container_end[]") + else + minetest.log( + "info", + "[MOD] yl_speak_up: User " .. pname .. " talked to NPC ID n_" .. n_id .. " with an old formspec version!" + ) + + local upgrade_warning = "" + local max_number_of_buttons = yl_speak_up.max_number_of_buttons + local start_index = yl_speak_up.speak_to[pname].option_index + + if formspec_v < 3 or protocol_v < 39 then + local warn = { + "box[0.3,3;15,2;red]", + "label[0.7,3.2;", + yl_speak_up.text_version_warning, + "]" + } + upgrade_warning = table.concat(warn, "") + end + + formspec = { + "formspec_version[1]", + "size[48,28]", + "position[0,0.45]", + "anchor[0,0.45]", + "no_prepend[]", + "bgcolor[#00000000;false]", + -- Container + + "container[2,0.75]", + -- Background + + "background[0,0;20,17;yl_speak_up_bg_dialog.png]", + "background[0,18;45,8;yl_speak_up_bg_dialog.png]", + -- Frame Dialog + + "image[-0.35,-0.35;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.35,16.25;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[19.35,-0.35;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[19.35,16.25;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.35,0.35;1,19.5;yl_speak_up_bg_dialog_hl.png]", + "image[19.35,0.35;1,19.5;yl_speak_up_bg_dialog_hr.png]", + "image[0.35,-0.35;24.5,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.35,16.25;24.5,1;yl_speak_up_bg_dialog_vb.png]", + -- Frame Options + + "image[-0.35,17.65;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.35,25.35;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[44.35,17.65;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[44.35,25.35;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.35,18.35;1,8.5;yl_speak_up_bg_dialog_hl.png]", + "image[44.35,18.35;1,8.5;yl_speak_up_bg_dialog_hr.png]", + "image[0.35,17.65;56.5,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.35,25.35;56.5,1;yl_speak_up_bg_dialog_vb.png]", + -- Upgrade Warning + + upgrade_warning, + -- Dialog + + "label[0.3,0.6;", + minetest.formspec_escape(dialog.n_npc), + "]", + "label[0.3,1.8;", + minetest.formspec_escape(dialog.n_description), + "]", + "image[15.5,0.5;4,4;", + portrait, + "]", + "textarea[0.5,5;19.6,13.5;;;", + minetest.formspec_escape(active_dialog.d_text) .. "\n", + "]", + "container[0,18]" + } + h = -1 + + if active_dialog ~= nil and active_dialog.d_options ~= nil then + -- Let's sort the options by o_sort. + local sorted_buttons = {} + for _, ad_v in pairs(active_dialog.d_options) do + sorted_buttons[tonumber(ad_v.o_sort)] = ad_v + end + + if #sorted_buttons > max_number_of_buttons then + -- Generate arrows + table.insert(formspec, "button[0.1,0;1,0.9;button_down;^]") + table.insert(formspec, "button[0.1,7.0;1,0.9;button_up;v]") + end + + local counter = 1 + + for _, sb_v in pairs(sorted_buttons) do + local end_index = start_index + max_number_of_buttons + if counter >= start_index and counter < end_index then + if allowed[sb_v.o_id] == true then + h = h + 1 + table.insert( + formspec, + "button[1," .. + h .. ";44,0.9;button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_met .. "]" + ) + table.insert( + formspec, + "tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_met .. "]" + ) + else + if sb_v.o_hide_when_prerequisites_not_met == "true" then + -- hide! Nothing to do + else + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met == "" + then + h = h + 1 + local l = h - 0.2 + table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]") + table.insert( + formspec, + "label[3," .. + h .. + ";" .. + yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" + ) + end + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + h = h + 1 + local l = h - 0.2 + table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]") + table.insert( + formspec, + "label[3," .. + h .. + ";" .. + yl_speak_up.message_button_option_prerequisites_not_met_default .. + " : " .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met == "" + then + -- no hide, no grey, no text + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + h = h + 1 + table.insert( + formspec, + "button[1," .. + h .. + ";44,0.9;button_" .. + sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + table.insert( + formspec, + "tooltip[button_" .. + sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]" + ) + end + end + end + end + + counter = counter + 1 + end + end + h = h + 1 + table.insert( + formspec, + "button_exit[1," .. h .. ";44,0.9;button_exit;" .. yl_speak_up.message_button_option_exit .. "]" + ) + table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]") + + table.insert(formspec, "container_end[]") + table.insert(formspec, "container_end[]") + end + + return table.concat(formspec, "") +end + +-- receive fields + +-- options + +minetest.register_on_player_receive_fields( + function(player, formname, fields) + if formname ~= "yl_speak_up:optiondialog" then + return + end + + local pname = player:get_player_name() + local n_id = yl_speak_up.speak_to[pname].n_id + local d_id = yl_speak_up.speak_to[pname].d_id + local dialog = yl_speak_up.speak_to[pname].dialog + + if fields then + -- Button Cancel: Exit the form + if fields.button_back or fields.quit then + local temp_dialog = yl_speak_up.speak_to[pname].dialog + + yl_speak_up.speak_to[pname] = {} + + yl_speak_up.speak_to[pname].dialog = temp_dialog + yl_speak_up.speak_to[pname].n_id = n_id + yl_speak_up.speak_to[pname].d_id = d_id + + minetest.show_formspec(pname, "yl_speak_up:setdialog", get_fs_setdialog(player, n_id, d_id)) + return + end + + -- Button Delete Option: Delete option dialog, but do not exit + if fields.button_delete and fields.d_id and fields.o_id ~= yl_speak_up.text_new_option_id then + local temp_dialog = yl_speak_up.speak_to[pname].dialog + + yl_speak_up.speak_to[pname] = {} + + yl_speak_up.speak_to[pname].dialog = temp_dialog + yl_speak_up.speak_to[pname].n_id = n_id + yl_speak_up.speak_to[pname].d_id = d_id + + yl_speak_up.speak_to[pname].o_id = yl_speak_up.text_new_option_id + yl_speak_up.speak_to[pname].p_id = yl_speak_up.text_new_prerequisite_id + yl_speak_up.speak_to[pname].r_id = yl_speak_up.text_new_result_id + + local o_id = yl_speak_up.text_new_option_id + local p_id = yl_speak_up.text_new_prerequisite_id + local r_id = yl_speak_up.text_new_result_id + + delete_option(fields.n_id, fields.d_id, fields.o_id) + minetest.show_formspec( + pname, + "yl_speak_up:optiondialog", + get_fs_optiondialog(player, n_id, d_id, o_id, p_id, r_id) + ) + return + end + + if fields.d_text then + yl_speak_up.speak_to[pname].d_text = fields.d_text + end + if fields.d_type then + yl_speak_up.speak_to[pname].d_type = fields.d_type + end + + -- When a new o_id is chosen, then all the other stuff is invalidated and we need to take values from the dialog or even default values + if fields.o_id ~= nil and fields.o_id ~= yl_speak_up.speak_to[pname].o_id then + local o_id = fields.o_id + yl_speak_up.speak_to[pname].o_id = o_id + yl_speak_up.speak_to[pname].p_id = yl_speak_up.text_new_prerequisite_id + yl_speak_up.speak_to[pname].r_id = yl_speak_up.text_new_result_id + + -- Depends on the dialog + if o_id == yl_speak_up.text_new_option_id then --New dialog + yl_speak_up.speak_to[pname].o_hide = "false" + yl_speak_up.speak_to[pname].o_grey = "false" + yl_speak_up.speak_to[pname].o_sort = "" + yl_speak_up.speak_to[pname].o_met = "Text when conditions are not met" + yl_speak_up.speak_to[pname].o_not_met = "Text when conditions are met" + else -- existing dialog + yl_speak_up.speak_to[pname].o_hide = + dialog.n_dialogs[d_id].d_options[o_id].o_hide_when_prerequisites_not_met or "false" + yl_speak_up.speak_to[pname].o_grey = + dialog.n_dialogs[d_id].d_options[o_id].o_grey_when_prerequisites_not_met or "false" + yl_speak_up.speak_to[pname].o_sort = dialog.n_dialogs[d_id].d_options[o_id].o_sort or "" + yl_speak_up.speak_to[pname].o_met = + dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_not_met or + "Text when conditions are not met" + yl_speak_up.speak_to[pname].o_not_met = + dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met or + "Text when conditions are met" + end + else + if fields.o_hide then + yl_speak_up.speak_to[pname].o_hide = fields.o_hide + end + if fields.o_grey then + yl_speak_up.speak_to[pname].o_grey = fields.o_grey + end + if fields.o_sort then + yl_speak_up.speak_to[pname].o_sort = fields.o_sort + end + if fields.o_met then + yl_speak_up.speak_to[pname].o_met = fields.o_met + end + if fields.o_not_met then + yl_speak_up.speak_to[pname].o_not_met = fields.o_not_met + end + + if fields.p_id ~= nil and fields.p_id ~= yl_speak_up.speak_to[pname].p_id then + yl_speak_up.speak_to[pname].p_id = fields.p_id + yl_speak_up.speak_to[pname].p_value = fields.p_value + yl_speak_up.speak_to[pname].p_type = fields.p_type + end + if fields.p_value then + yl_speak_up.speak_to[pname].p_value = fields.p_value + end + if fields.p_type then + yl_speak_up.speak_to[pname].p_type = fields.p_type + end + if fields.r_id ~= nil and fields.r_id ~= yl_speak_up.speak_to[pname].r_id then + yl_speak_up.speak_to[pname].r_id = fields.r_id + yl_speak_up.speak_to[pname].r_value = fields.r_value + yl_speak_up.speak_to[pname].r_type = fields.r_type + end + if fields.r_value then + yl_speak_up.speak_to[pname].r_value = fields.r_value + end + if fields.r_type then + yl_speak_up.speak_to[pname].r_type = fields.r_type + end + end + end + + local o_id = fields.o_id or yl_speak_up.speak_to[pname].o_id + local p_id = fields.p_id or yl_speak_up.speak_to[pname].p_id + local r_id = fields.r_id or yl_speak_up.speak_to[pname].r_id + + -- Button Save + if fields and fields.button_save and fields.n_id and fields.d_id then + local temp_dialog = options_to_dialog(pname) + + save_dialog(n_id, temp_dialog) + + minetest.show_formspec( + pname, + "yl_speak_up:optiondialog", + get_fs_optiondialog(player, fields.n_id, fields.d_id, fields.o_id, fields.p_id, fields.r_id) + ) + end + + minetest.show_formspec( + pname, + "yl_speak_up:optiondialog", + get_fs_optiondialog(player, n_id, d_id, o_id, p_id, r_id) + ) + end +) + +-- dialog + +minetest.register_on_player_receive_fields( + function(player, formname, fields) + if formname ~= "yl_speak_up:setdialog" then + return + end + + local pname = player:get_player_name() + local n_id = yl_speak_up.speak_to[pname].n_id + local d_id = yl_speak_up.speak_to[pname].d_id or yl_speak_up.text_new_dialog_id + + -- Button Cancel: Exit the form + if fields.button_cancel or fields.quit then + yl_speak_up.speak_to[pname] = nil + minetest.close_formspec(pname, "yl_speak_up:setdialog") + return + end + + -- Button Options with valid d_id: Show options formspec + if fields.button_option and fields.d_id and fields.d_id ~= yl_speak_up.text_new_dialog_id then + local o_id = yl_speak_up.text_new_option_id + local p_id = yl_speak_up.text_new_prerequisite_id + local r_id = yl_speak_up.text_new_result_id + + -- Context + yl_speak_up.speak_to[pname].d_id = fields.d_id + yl_speak_up.speak_to[pname].o_id = o_id + yl_speak_up.speak_to[pname].p_id = p_id + yl_speak_up.speak_to[pname].r_id = r_id + + minetest.show_formspec( + pname, + "yl_speak_up:optiondialog", + get_fs_optiondialog(player, n_id, fields.d_id, o_id, p_id, r_id) + ) + return + end + + -- Button Options with invalid d_id: Show options formspec + if fields.button_option and fields.d_id and fields.d_id == yl_speak_up.text_new_dialog_id then + yl_speak_up.speak_to[pname].d_id = yl_speak_up.text_new_dialog_id + minetest.show_formspec(pname, "yl_speak_up:optiondialog", get_error_message()) + return + end + + -- Button Save: Save the settings, but do not exit + if fields.button_save and fields.d_id then + local dialog = fields_to_dialog(pname, fields) + save_dialog(n_id, dialog) + yl_speak_up.speak_to[pname].dialog = dialog + + if yl_speak_up.speak_to[pname].obj then + local obj = yl_speak_up.speak_to[pname].obj + local ent = obj:get_luaentity() + if ent ~= nil then + ent.yl_speak_up.npc_name = dialog.n_npc + ent.yl_speak_up.npc_description = dialog.n_description + + local i_text = dialog.n_npc .. "\n" .. dialog.n_description .. "\n" .. yl_speak_up.infotext + obj:set_properties({infotext = i_text}) + obj:set_nametag_attributes({color="#00ff00", text=dialog.n_npc}) + end + end + + minetest.show_formspec(pname, "yl_speak_up:setdialog", get_fs_setdialog(player, n_id, d_id)) + return + end + + -- Button Delete: Delete dialog, but do not exit + if fields.button_delete and fields.d_id then + delete_dialog(n_id, fields.d_id) + yl_speak_up.speak_to[pname].d_id = yl_speak_up.text_new_dialog_id + minetest.show_formspec( + pname, + "yl_speak_up:setdialog", + get_fs_setdialog(player, n_id, yl_speak_up.text_new_dialog_id) + ) + end + + -- Change in Dropdown List: Show dialog formspec again with different dialog selected + if fields.d_id then + yl_speak_up.speak_to[pname].d_id = fields.d_id + minetest.show_formspec(pname, "yl_speak_up:setdialog", get_fs_setdialog(player, n_id, fields.d_id)) + end + end +) + +-- talk + +minetest.register_on_player_receive_fields( + function(player, formname, fields) + if formname ~= "yl_speak_up:talk" then + return + end + + local pname = player:get_player_name() + local o = "" + + if fields.quit or fields.button_exit then + -- Formspec was quit + yl_speak_up.speak_to[pname] = nil + return + end + + if fields.button_up then + yl_speak_up.speak_to[pname].option_index = + yl_speak_up.speak_to[pname].option_index + yl_speak_up.max_number_of_buttons + minetest.show_formspec( + pname, + "yl_speak_up:talk", + get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, yl_speak_up.speak_to[pname].d_id) + ) + return + elseif fields.button_down then --and yl_speak_up.speak_to[pname].option_index > yl_speak_up.max_number_of_buttons then + yl_speak_up.speak_to[pname].option_index = + yl_speak_up.speak_to[pname].option_index - yl_speak_up.max_number_of_buttons + if yl_speak_up.speak_to[pname].option_index < 0 then + yl_speak_up.speak_to[pname].option_index = 1 + end + minetest.show_formspec( + pname, + "yl_speak_up:talk", + get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, yl_speak_up.speak_to[pname].d_id) + ) + return + else + yl_speak_up.speak_to[pname].option_index = 1 + end + + for k, v in pairs(fields) do + local s = string.split(k, "_") + + if + s[1] == "button" and s[2] ~= nil and s[2] ~= "" and s[2] ~= "exit" and s[2] ~= "back" and s[3] ~= nil and + s[2] ~= "up" and + s[2] ~= "down" + then + o = s[2] .. "_" .. s[3] + end + end + + if o == "" then + return + end + + -- Let's check if the button was among the "allowed buttons". Only those may be executed + if yl_speak_up.speak_to[pname].allowed and yl_speak_up.speak_to[pname].allowed[o] == false then + return + end + + -- button was clicked, now let's execute the results + + local d_id = yl_speak_up.speak_to[pname].d_id + + local dialog = yl_speak_up.speak_to[pname].dialog + + local n_dialog = dialog.n_dialogs[d_id] + + local d_option = n_dialog.d_options[o] + + -- Let's do something if results exist + if d_option.o_results ~= nil then + for k, v in pairs(d_option.o_results) do + if v.r_type == "give_item" then + local item = ItemStack(v.r_value) + + if minetest.registered_items[item:get_name()] ~= nil then + local r = player:get_inventory():add_item("main", item) + else + say("Item not found!") + end + end + if v.r_type == "take_item" then + local item = ItemStack(v.r_value) + + if minetest.registered_items[item:get_name()] ~= nil then + local r = player:get_inventory():remove_item("main", item) + else + say("Item not found!") + end + end + if v.r_type == "move" then + local target_pos = nil + local target_pos_valid = false + + --pos like (100,20,400) + if minetest.string_to_pos(v.r_value) then + target_pos = minetest.string_to_pos(v.r_value) + target_pos_valid = true + end + + --pos like 100,20,400 + local maybe = string.split(v.r_value, ",") + if not target_pos_valid and maybe and tonumber(maybe[1]) and tonumber(maybe[2]) and tonumber(maybe[3]) and maybe[4] == nil and + tonumber(maybe[1]) <= 32000 and tonumber(maybe[1]) >= -32000 and + tonumber(maybe[2]) <= 32000 and tonumber(maybe[2]) >= -32000 and + tonumber(maybe[3]) <= 32000 and tonumber(maybe[3]) >= -32000 then + target_pos = {x=maybe[1],y=maybe[2],z=maybe[3]} + target_pos_valid = true + end + + --pos like {x=100,y=20,z=400} + if not target_pos_valid and string.sub(v.r_value,1,1) == "{" and string.sub(v.r_value,-1,-1) == "}" then + local might_be_pos = minetest.deserialize("return " .. v.r_value) + if tonumber(might_be_pos.x) and tonumber(might_be_pos.x) <= 32000 and tonumber(might_be_pos.x) >= -32000 and + tonumber(might_be_pos.y) and tonumber(might_be_pos.y) <= 32000 and tonumber(might_be_pos.y) >= -32000 and + tonumber(might_be_pos.z) and tonumber(might_be_pos.z) <= 32000 and tonumber(might_be_pos.z) >= -32000 then + target_pos = might_be_pos + target_pos_valid = true + end + + end + + if target_pos_valid == true then + player:set_pos(target_pos) + if vector.distance(player:get_pos(),target_pos) >= 2 then + say("Something went wrong! Player wasn't moved properly.") + end + end + + -- Debug + if target_pos_valid == false then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not move player "..pname.." because the content of "..v.r_id.." is wrong:"..dump(v.r_value)) + else + minetest.log("error","[MOD] yl_speak_up: NPC with unknown ID or without proper object could not move player "..dump(pname).." because the content of "..v.r_id.." is wrong:"..dump(v.r_value)) + end + end + + end + if v.r_type == "function" then + local code = v.r_value + if code:byte(1) == 27 then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " because of illegal bytecode for player "..pname) + else + minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " for player unknown because of illegal bytecode") + end + end + + local f, msg = loadstring("return function(player) " .. code .. " end") + + if not f then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " for player "..pname) + else + minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " for player unknown") + end + else + + + local func = f() + + local ok, ret = pcall(func,pname) + + if not ok then + local obj = yl_speak_up.speak_to[pname].obj + local n_id = yl_speak_up.speak_to[pname].n_id + local npc = get_number_from_id(n_id) + if obj:get_luaentity() and tonumber(npc) then + minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..v.r_id.." :"..dump(v.r_value) .. " for player "..pname) + else + minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..v.r_id.." :"..dump(v.r_value) .. " for player unknown") + end + end + end + + end + if v.r_type == "dialog" then + if dialog.n_dialogs[v.r_value] ~= nil then + yl_speak_up.speak_to[pname].d_id = v.r_value + local n_id = yl_speak_up.speak_to[pname].n_id + minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(player, n_id, v.r_value)) + else + say("This dialog does not exist") + end + end + if v.r_type == "auto" then + say("auto forward") + end + end + end + end +) + +-- Make the NPC talk + +function yl_speak_up.config(clicker, npc) + if not clicker and not clicker:is_player() then + return + end + if not npc then + return + end + + local npc_id = npc:get_luaentity().yl_speak_up.id + local n_id = "n_" .. npc_id + local pname = clicker:get_player_name() + + yl_speak_up.speak_to[pname] = {} + yl_speak_up.speak_to[pname].n_id = n_id -- Memorize which player talks to which NPC + yl_speak_up.speak_to[pname].d_id = yl_speak_up.text_new_dialog_id -- The only d_id we can rely on existing + yl_speak_up.speak_to[pname].dialog = load_dialog(n_id) -- Load the dialog and see what we can do with it + yl_speak_up.speak_to[pname].obj = npc + + minetest.show_formspec( + pname, + "yl_speak_up:setdialog", + get_fs_setdialog(clicker, n_id, yl_speak_up.text_new_dialog_id) + ) +end + +--### +-- Mob functions +--### + +function yl_speak_up.on_rightclick(self, clicker) + --local item = clicker:get_wielded_item() + local name = clicker:get_player_name() + + -- Take the mob only with net or lasso + if self.owner and self.owner == name then + if mobs:capture_mob(self, clicker, nil, 100, 100, true, nil) then + return + end + end + + -- protect npc with mobs:protector + if mobs:protect(self, clicker) then + return + end + + -- bring up the dialog options + if clicker then + yl_speak_up.talk(self, clicker) + return + end +end + +function yl_speak_up.on_spawn(self) + --Let's assign an ID + + local m_talk = yl_speak_up.talk_after_spawn or true + local m_id = yl_speak_up.number_of_npcs + 1 + + yl_speak_up.number_of_npcs = m_id + yl_speak_up.modstorage:set_int("amount", m_id) + + yl_speak_up.mob_table[m_id] = "yl_speak_up:test_npc" + + self.yl_speak_up = { + talk = m_talk, + id = m_id, + textures = self.textures + } + + --Let's protect it + self.protected = true + self.tamed = true + self.object:set_armor_groups({immortal = 100}) + + minetest.log( + "action", + "[MOD] yl_speak_up: NPC with ID n_" .. + self.yl_speak_up.id .. " spawned at " .. minetest.pos_to_string(self.object:get_pos(), 0) + ) + + --Let's do it only once + return true +end + +function yl_speak_up.after_activate(self, staticdata, def, dtime) + minetest.log( + "action", + "[MOD] yl_speak_up: NPC with ID n_" .. + self.yl_speak_up.id .. " activated at " .. minetest.pos_to_string(self.object:get_pos(), 0) + ) + + if yl_speak_up.status == 2 then + self.object:remove() + return true + end + + if self.yl_speak_up and self.yl_speak_up.skin then + local tex = self.yl_speak_up.skin + self.object:set_properties({textures = {tex[1], tex[2], tex[3], tex[4]}}) + end + + if yl_speak_up.infotext then + local i_text = "" + if self.yl_speak_up.npc_name then + i_text = i_text .. self.yl_speak_up.npc_name .. "\n" + end + if self.yl_speak_up.npc_description then + i_text = i_text .. self.yl_speak_up.npc_description .. "\n" + end + i_text = i_text .. yl_speak_up.infotext + self.object:set_properties({infotext = i_text}) + end + + if self.yl_speak_up.npc_name then + self.object:set_nametag_attributes({color="#00ff00", text=self.yl_speak_up.npc_name}) + end + +end + +function yl_speak_up.talk(self, clicker) + + if not clicker and not clicker:is_player() then + return + end + if not self then + return + end + if not self.yl_speak_up or not self.yl_speak_up.id then + return + end + + if not self.yl_speak_up or not self.yl_speak_up.talk or self.yl_speak_up.talk~=true then + return + end + + local npc_id = self.yl_speak_up.id + local n_id = "n_" .. npc_id + local pname = clicker:get_player_name() + + yl_speak_up.speak_to[pname] = {} + yl_speak_up.speak_to[pname].n_id = n_id -- Memorize which player talks to which NPC + yl_speak_up.speak_to[pname].dialog = load_dialog(n_id) -- Load the dialog and see what we can do with it + yl_speak_up.speak_to[pname].textures = self.yl_speak_up.textures + yl_speak_up.speak_to[pname].option_index = 1 + yl_speak_up.speak_to[pname].obj = self.object + + minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(clicker, n_id)) +end + +-- ### +-- Fashion +-- ### + +local function cape2texture(t) + return "yl_speak_up_mask_cape.png^[combine:32x64:56,20=" .. t +end + +local function shield2texture(t) + return "yl_speak_up_mask_shield.png^[combine:32x64:0,0=(" .. t .. ")" +end + +local function textures2skin(textures) + local temp = {} + -- Cape + + local cape = cape2texture(textures[1]) + + -- Main + + local main = textures[2] + + -- left (Shield) + + local left = shield2texture(textures[3]) + + -- right (Sword) + + local right = textures[4] + + temp = {cape, main, left, right} + + return temp +end + +local function set_textures(obj, textures) -- this function takes the base name of the textures, converts them to usable textures and stores those on the NPC + local skins = textures2skin(textures) + + local ent = obj:get_luaentity() + ent.yl_speak_up.skin = skins + + obj:set_properties({textures = skins}) +end + +local function get_npc_skins(skin) + local retstring = "yl_speak_up_main_default.png" + local rettable = {"yl_speak_up_main_default.png"} + local temp = {} + + -- get the files out of modpath + local mp_list = minetest.get_dir_list(yl_speak_up.modpath .. DIR_DELIM .. "textures", false) + + -- get the files out of worlddir + local wp_list = + minetest.get_dir_list( + yl_speak_up.worldpath .. DIR_DELIM .. "worldmods" .. DIR_DELIM .. "yl_npc" .. DIR_DELIM .. "textures", + false + ) + + -- Let's join both lists. + table.insert_all(temp, mp_list) + table.insert_all(temp, wp_list) + + --[[ Let's see if the files are the ones we want. Format is + yl_npc_main_name.png <-- Those are the ones we want + yl_npc_cape_name.png + yl_npc_item_name.png + ]]-- + local index = 1 + local retindex = 1 + + for _, v in pairs(temp) do + local s = string.split(v, "_") + if s[1] == "yl" and s[2] == "npc" and s[3] == "main" then + index = index + 1 + retstring = retstring .. "," .. v + table.insert(rettable, v) + if v == skin then + retindex = index + end + end + end + + return rettable, retstring, retindex +end + +local function get_npc_capes(cape) + local retstring = "yl_npc_cape_default.png" + local rettable = {"yl_npc_cape_default.png"} + local temp = {} + + -- get the files out of modpath + local mp_list = minetest.get_dir_list(yl_speak_up.modpath .. DIR_DELIM .. "textures", false) + + -- get the files out of worlddir + local wp_list = + minetest.get_dir_list( + yl_speak_up.worldpath .. DIR_DELIM .. "worldmods" .. DIR_DELIM .. "yl_npc" .. DIR_DELIM .. "textures", + false + ) + + -- Let's join both lists. + table.insert_all(temp, mp_list) + table.insert_all(temp, wp_list) + + --[[ Let's see if the files are the ones we want. Format is + yl_npc_main_name.png + yl_npc_cape_name.png <-- Those are the ones we want + yl_npc_item_name.png + ]]-- + local index = 1 + local retindex = 1 + + for _, v in pairs(temp) do + local s = string.split(v, "_") + + if s[1] == "yl" and s[2] == "npc" and s[3] == "cape" then + index = index + 1 + retstring = retstring .. "," .. v + table.insert(rettable, v) + if cape ~= "" and v == cape then + retindex = index + end + end + end + + return rettable, retstring, retindex +end + +local function create_preview(main_skin) + if main_skin == nil or main_skin == "" then + main_skin = "default_greyscale.png" + end + + local player_skin = "(" .. main_skin .. ")" + local skin = "" + + -- Consistent on both sizes: + --Chest + skin = skin .. "([combine:16x32:-16,-12=" .. player_skin .. "^[mask:yl_speak_up_mask_chest.png)^" + + --Head + skin = skin .. "([combine:16x32:-4,-8=" .. player_skin .. "^[mask:yl_speak_up_mask_head.png)^" + + --Hat + skin = skin .. "([combine:16x32:-36,-8=" .. player_skin .. "^[mask:yl_speak_up_mask_head.png)^" + --Right Arm + skin = skin .. "([combine:16x32:-44,-12=" .. player_skin .. "^[mask:yl_speak_up_mask_rarm.png)^" + --Right Leg + skin = skin .. "([combine:16x32:0,0=" .. player_skin .. "^[mask:yl_speak_up_mask_rleg.png)^" + + -- Left Arm + skin = skin .. "([combine:16x32:-24,-44=" .. player_skin .. "^[mask:(yl_speak_up_mask_rarm.png^[transformFX))^" + --Left Leg + skin = skin .. "([combine:16x32:-12,-32=" .. player_skin .. "^[mask:(yl_speak_up_mask_rleg.png^[transformFX))^" + + -- Add overlays for 64x skins. these wont appear if skin is 32x because it will be cropped out + --Chest Overlay + skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:yl_speak_up_mask_chest.png)^" + --Right Arm Overlay + skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:yl_speak_up_mask_rarm.png)^" + --Right Leg Overlay + skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:yl_speak_up_mask_rleg.png)^" + --Left Arm Overlay + skin = skin .. "([combine:16x32:-40,-44=" .. player_skin .. "^[mask:(yl_speak_up_mask_rarm.png^[transformFX))^" + --Left Leg Overlay + skin = skin .. "([combine:16x32:4,-32=" .. player_skin .. "^[mask:(yl_speak_up_mask_rleg.png^[transformFX))" + + -- Full Preview + skin = "(((" .. skin .. ")^[resize:64x128)^[mask:yl_speak_up_transform.png)" + + return skin +end + +local function get_fs_fashion(pname) + local textures = yl_speak_up.speak_to[pname].textures + + local maintable, mainlist, mainindex = get_npc_skins(textures[2]) + local capetable, capelist, capeindex = get_npc_capes(textures[1]) + local preview = create_preview(textures[2]) + + local formspec = { + "formspec_version[3]", + "size[13.4,9.5]", + "dropdown[0.3,0.2;4,0.75;set_skin;", + mainlist, + ";", + mainindex, + "]", + "label[4.6,0.45;", + yl_speak_up.speak_to[pname].n_id, + "]", + "label[6,0.45;", + yl_speak_up.speak_to[pname].n_npc, + "]", + "dropdown[9.1,0.2;4,0.75;set_cape;", + capelist, + ";", + capeindex, + "]", + "field[0.3,3.2;4,0.75;set_sword;;", + textures[4], + "]", + "field[9.1,3.2;4,0.75;set_shield;;", + textures[3], + "]", + "field_close_on_enter[set_sword;false]", + "field_close_on_enter[set_shield;false]", + "image[0.3,1;4,2;", + textures[2], + "]", -- Main + "image[9.1,1;4,2;", + textures[1], + "]", -- Cape + "image[0.3,4.2;4,4;", + textures[4], + "]", -- Sword + "image[9.1,4.2;4,4;", + textures[3], + "]", --textures[3],"]", -- Shield + "image[4.7,1;4,8;", + preview, + "]", + "button_exit[0.3,8.4;3,0.75;button_cancel;Cancel]", + "button[10.1,8.4;3,0.75;button_save;Save]" + } + return table.concat(formspec, "") +end + +minetest.register_on_player_receive_fields( --fashion + function(player, formname, fields) + if formname ~= "yl_speak_up:fashion" then + return + end + + local pname = player:get_player_name() + local textures = yl_speak_up.speak_to[pname].textures + + if fields then + if fields.quit or fields.button_cancel then + yl_speak_up.speak_to[pname] = nil + return + end + + if fields.set_cape then + textures[1] = fields.set_cape + end + if fields.set_skin then + textures[2] = fields.set_skin + end + if fields.set_shield then + textures[3] = fields.set_shield + end + if fields.set_sword then + textures[4] = fields.set_sword + end + + if fields.button_save then + local obj = yl_speak_up.speak_to[pname].obj + if obj ~= nil and obj:get_luaentity() ~= nil then + -- save textures + yl_speak_up.speak_to[pname].skins = textures2skin(textures) + set_textures(obj, textures) + end + end + + --yl_speak_up.speak_to[pname].textures = textures + end + + minetest.show_formspec(pname, "yl_speak_up:fashion", get_fs_fashion(pname)) + end +) + +function yl_speak_up.fashion(player, obj) + local luaentity = obj:get_luaentity() + local pname = player:get_player_name() + local npc_id = luaentity.yl_speak_up.id + local skins = luaentity.yl_speak_up.skins + local n_id = "n_" .. npc_id + local t = luaentity.textures + + yl_speak_up.speak_to[pname] = {} + yl_speak_up.speak_to[pname].n_id = n_id + yl_speak_up.speak_to[pname].obj = obj + yl_speak_up.speak_to[pname].textures = t + yl_speak_up.speak_to[pname].skins = textures2skin(t) + + local dialog = load_dialog(n_id) + if next(dialog) then + yl_speak_up.speak_to[pname].n_npc = dialog.n_npc + else + yl_speak_up.speak_to[pname].n_npc = "Unknown" + end + + minetest.show_formspec(pname, "yl_speak_up:fashion", get_fs_fashion(pname)) +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..01de574 --- /dev/null +++ b/init.lua @@ -0,0 +1,28 @@ +yl_speak_up = {} + +local modpath = minetest.get_modpath("yl_speak_up")..DIR_DELIM +yl_speak_up.worldpath = minetest.get_worldpath()..DIR_DELIM +yl_speak_up.modpath = modpath + +yl_speak_up.modstorage = minetest.get_mod_storage() + +-- status +-- 0: NPCs may speak +-- 1: NPCs may not speak +-- 2: NPCs must selfdestruct on load. Their dialogs remain safed + +yl_speak_up.status = yl_speak_up.modstorage:get_int("status") or 0 +yl_speak_up.number_of_npcs = yl_speak_up.modstorage:get_int("amount") or 0 +yl_speak_up.speak_to = {} + +dofile(modpath .. "config.lua") +dofile(modpath .. "privs.lua") +dofile(modpath .. "functions.lua") +dofile(modpath .. "tools.lua") +dofile(modpath .. "mobs.lua") +--dofile(modpath .. "debug.lua") + +minetest.mkdir(yl_speak_up.worldpath..yl_speak_up.path) +yl_speak_up.mob_table = yl_speak_up.init_mob_table() or {} + +minetest.log("action","[MOD] yl_speak_up loaded") \ No newline at end of file diff --git a/mobs.lua b/mobs.lua new file mode 100644 index 0000000..0eb0011 --- /dev/null +++ b/mobs.lua @@ -0,0 +1,333 @@ +mobs:register_mob("yl_speak_up:human", { + type = "npc", + passive = true, + damage = 9, + attack_type = "dogfight", + attacks_monsters = true, + attack_npcs = false, + owner_loyal = false, + pathfinding = false, + hp_min = 60, + hp_max = 100, + armor = 0, + collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}, + visual = "mesh", + visual_size = {x = 1, y = 1}, + mesh = "skinsdb_3d_armor_character_5.b3d", + drawtype = "front", + textures = {{ + "blank.png", -- cape? + "yl_speak_up_main_default.png", -- 64x64 skin + "3d_armor_trans.png", -- shield?! + "3d_armor_trans.png", -- item right hand + }}, + makes_footstep_sound = true, + sounds = {}, + walk_velocity = 2, + run_velocity = 3, + jump = false, + water_damage = 0, + lava_damage = 0, + light_damage = 0, + view_range = 4, + owner = "Haven", + order = "stand", + fear_height = 3, + animation = { + speed_normal = 30, + speed_run = 30, + stand_start = 0, + stand_end = 79, + walk_start = 168, + walk_end = 187, + run_start = 168, + run_end = 187, + punch_start = 200, + punch_end = 219, + }, + + on_rightclick = yl_speak_up.on_rightclick, + on_spawn = yl_speak_up.on_spawn, + after_activate = yl_speak_up.after_activate +}) + +mobs:register_egg("yl_speak_up:human", "Human", "wool_blue.png", 1) + +mobs:register_mob("yl_speak_up:elf", { + type = "npc", + passive = true, + damage = 9, + attack_type = "dogfight", + attacks_monsters = true, + attack_npcs = false, + owner_loyal = false, + pathfinding = false, + hp_min = 60, + hp_max = 100, + armor = 0, + collisionbox = {-0.25, 0.0, -0.25, 0.25, 1.9, 0.25}, + visual = "mesh", + visual_size = {x = 0.9, y = 1.1}, + mesh = "skinsdb_3d_armor_character_5.b3d", + drawtype = "front", + textures = {{ + "blank.png", -- cape? + "yl_speak_up_main_default.png", -- 64x64 skin + "3d_armor_trans.png", -- shield?! + "3d_armor_trans.png", -- item right hand + }}, + makes_footstep_sound = true, + sounds = {}, + walk_velocity = 2, + run_velocity = 3, + jump = false, + water_damage = 0, + lava_damage = 0, + light_damage = 0, + view_range = 4, + owner = "Haven", + order = "stand", + fear_height = 3, + animation = { + speed_normal = 30, + speed_run = 30, + stand_start = 0, + stand_end = 79, + walk_start = 168, + walk_end = 187, + run_start = 168, + run_end = 187, + punch_start = 200, + punch_end = 219, + }, + + on_rightclick = yl_speak_up.on_rightclick, + on_spawn = yl_speak_up.on_spawn, + after_activate = yl_speak_up.after_activate +}) + +mobs:register_egg("yl_speak_up:elf", "Elf", "wool_cyan.png", 1) + +mobs:register_mob("yl_speak_up:dwarf", { + type = "npc", + passive = true, + damage = 9, + attack_type = "dogfight", + attacks_monsters = true, + attack_npcs = false, + owner_loyal = false, + pathfinding = false, + hp_min = 60, + hp_max = 100, + armor = 0, + collisionbox = {-0.35, 0.0, -0.35, 0.35, 1.4, 0.35}, + visual = "mesh", + visual_size = {x = 1.1, y = 0.8}, + mesh = "skinsdb_3d_armor_character_5.b3d", + drawtype = "front", + textures = {{ + "blank.png", -- cape? + "yl_speak_up_main_default.png", -- 64x64 skin + "3d_armor_trans.png", -- shield?! + "3d_armor_trans.png", -- item right hand + }}, + makes_footstep_sound = true, + sounds = {}, + walk_velocity = 2, + run_velocity = 3, + jump = false, + water_damage = 0, + lava_damage = 0, + light_damage = 0, + view_range = 4, + owner = "Haven", + order = "stand", + fear_height = 3, + animation = { + speed_normal = 30, + speed_run = 30, + stand_start = 0, + stand_end = 79, + walk_start = 168, + walk_end = 187, + run_start = 168, + run_end = 187, + punch_start = 200, + punch_end = 219, + }, + + on_rightclick = yl_speak_up.on_rightclick, + on_spawn = yl_speak_up.on_spawn, + after_activate = yl_speak_up.after_activate +}) + +mobs:register_egg("yl_speak_up:dwarf", "Dwarf", "wool_red.png", 1) + +mobs:register_mob("yl_speak_up:goblin", { + type = "npc", + passive = true, + damage = 9, + attack_type = "dogfight", + attacks_monsters = true, + attack_npcs = false, + owner_loyal = false, + pathfinding = false, + hp_min = 60, + hp_max = 100, + armor = 0, + collisionbox = {-0.25, 0.0, -0.25, 0.25, 1.4, 0.25}, + visual = "mesh", + visual_size = {x = 0.7, y = 0.8}, + mesh = "skinsdb_3d_armor_character_5.b3d", + drawtype = "front", + textures = {{ + "blank.png", -- cape? + "yl_speak_up_main_default.png", -- 64x64 skin + "3d_armor_trans.png", -- shield?! + "3d_armor_trans.png", -- item right hand + }}, + makes_footstep_sound = true, + sounds = {}, + walk_velocity = 2, + run_velocity = 3, + jump = false, + water_damage = 0, + lava_damage = 0, + light_damage = 0, + view_range = 4, + owner = "Haven", + order = "stand", + fear_height = 3, + animation = { + speed_normal = 30, + speed_run = 30, + stand_start = 0, + stand_end = 79, + walk_start = 168, + walk_end = 187, + run_start = 168, + run_end = 187, + punch_start = 200, + punch_end = 219, + }, + + on_rightclick = yl_speak_up.on_rightclick, + on_spawn = yl_speak_up.on_spawn, + after_activate = yl_speak_up.after_activate +}) + +mobs:register_egg("yl_speak_up:goblin", "Goblin", "wool_yellow.png", 1) + +mobs:register_mob("yl_speak_up:orc", { + type = "npc", + passive = true, + damage = 9, + attack_type = "dogfight", + attacks_monsters = true, + attack_npcs = false, + owner_loyal = false, + pathfinding = false, + hp_min = 60, + hp_max = 100, + armor = 0, + collisionbox = {-0.35, 0.0, -0.35, 0.35, 1.9, 0.35}, + visual = "mesh", + visual_size = {x = 1.1, y = 1.1}, + mesh = "skinsdb_3d_armor_character_5.b3d", + drawtype = "front", + textures = {{ + "blank.png", -- cape? + "yl_speak_up_main_default.png", -- 64x64 skin + "3d_armor_trans.png", -- shield?! + "3d_armor_trans.png", -- item right hand + }}, + makes_footstep_sound = true, + sounds = {}, + walk_velocity = 2, + run_velocity = 3, + jump = false, + water_damage = 0, + lava_damage = 0, + light_damage = 0, + view_range = 4, + owner = "Haven", + order = "stand", + fear_height = 3, + animation = { + speed_normal = 30, + speed_run = 30, + stand_start = 0, + stand_end = 79, + walk_start = 168, + walk_end = 187, + run_start = 168, + run_end = 187, + punch_start = 200, + punch_end = 219, + }, + + on_rightclick = yl_speak_up.on_rightclick, + on_spawn = yl_speak_up.on_spawn, + after_activate = yl_speak_up.after_activate +}) + +mobs:register_egg("yl_speak_up:orc", "Orc", "wool_dark_green.png", 1) + +-- ### +-- Test +-- ### + +mobs:register_mob("yl_speak_up:npc", { + type = "npc", + passive = true, + damage = 9, + attack_type = "dogfight", + attacks_monsters = true, + attack_npcs = false, + owner_loyal = false, + pathfinding = false, + hp_min = 60, + hp_max = 100, + armor = 0, + collisionbox = {-0.35, 0.0, -0.35, 0.35, 1.9, 0.35}, + visual = "mesh", + visual_size = {x = 1.1, y = 1.1}, + mesh = "skinsdb_3d_armor_character_5.b3d", + drawtype = "front", + textures = {{ + "blank.png", -- cape? wenn 64x32, dann 56,20 - 63,31, wenn 128x64, dann 111,39 - 127,63, rest invis + "yl_speak_up_main_default.png", -- 64x64 skin oder auch 128x128? + "blank.png", -- shield?! 64x32 + "3d_armor_trans.png", -- item right hand (16x16) + }}, + makes_footstep_sound = true, + sounds = {}, + walk_velocity = 2, + run_velocity = 3, + jump = false, + water_damage = 0, + lava_damage = 0, + light_damage = 0, + view_range = 4, + owner = "Haven", + order = "stand", + fear_height = 3, + animation = { + speed_normal = 30, + speed_run = 30, + stand_start = 0, + stand_end = 79, + walk_start = 168, + walk_end = 187, + run_start = 168, + run_end = 187, + punch_start = 200, + punch_end = 219, + }, + + on_rightclick = yl_speak_up.on_rightclick, + on_spawn = yl_speak_up.on_spawn, + after_activate = yl_speak_up.after_activate +}) + +mobs:register_egg("yl_speak_up:npc", "NPC", "wool_black.png", 1) diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..26b2cda --- /dev/null +++ b/mod.conf @@ -0,0 +1,6 @@ +author = Alias +description = NPCs deliver speeches +release = 202009231753 +title = Yourland Speak up +name = yl_speak_up +depends = mobs \ No newline at end of file diff --git a/models/skinsdb_3d_armor_character_5.b3d b/models/skinsdb_3d_armor_character_5.b3d new file mode 100644 index 0000000000000000000000000000000000000000..1287690d19704f5f7e547871c53cc85700897c93 GIT binary patch literal 178730 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 Me4BZ{WSX1*1+ONS)Bpeg literal 0 HcmV?d00001 diff --git a/privs.lua b/privs.lua new file mode 100644 index 0000000..e27f5dd --- /dev/null +++ b/privs.lua @@ -0,0 +1,7 @@ +local npc_master_priv_definition = { + description="Can use the staffs to command NPCs", + give_to_singleplayer = false, + give_to_admin = true, +} + +minetest.register_privilege("npc_master", npc_master_priv_definition) \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e69de29 diff --git a/textures/3d_armor_trans.png b/textures/3d_armor_trans.png new file mode 100644 index 0000000000000000000000000000000000000000..4a31242b54fceda3cea196c6083d758a5a8b9c74 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx?BpA#)4xIr~Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8W<`RdP`(kYX@0Ff`XSG|)9P4l%T_GBLL@HrF;Vv@$T*WTH3$MMG|WN@iLm WZViVsXX*eoFnGH9xvX zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1e?dLtWd4Oe%?dC;gXy5Xu*H&@$T2gK;7XSCjTA6zGN3dktF zgH9KsT~0lN@(~09&=C#+z5a^Dt!2wvt~)avRPY4U( z@|I~pVaz#4hr=*1;`DTCS`NNk_aEamfWfrfEHJPh&1L}oExC{23XAA~feGzSV8`Q} z04@T81TaXL=#K0{a@ig1J+X6eq=29b=srZ>K#NPs4}kPBgbZ?!1y~#JuvX>wQ}Idi8C_`7p^2hq9n<} zB2uIlUjh_2735=)QcA5@v|{Fpw<`wJ+I$Nww$yUtCatvEJ@wFIPd#_;(o3(yk1&wO zBM%-j%Ba&#gwl*N%{+O^EVC*?O@l^Fnw3?wkk%_}?o8YJ>l>`OlQqAXvVnYM4ZX~l zDV)hkGS0x5j}C;hG2jAuZ=4O`ZB8{?J6AAH8;H1rkO8gLB1~hu`X6c4PkH* zvyx~bMcFhkQrhMQDRgXZ5ZmC!>{iHg&$G2UM^&tsN|WK|oS_sr1xY(+7CC%nWO8=M zk%KD`)HX_O6UdQmHmy%koVZ+^VM0aLx5AD~g7g)N4n~)$11ZpiXX)$)(lU8?$4g@@ z&}!0JSYOdXZ$~piYe#brmuS8XX8KJu-v%@NCYo=Trr$*KZ7|dO(R`SM_y&vf17V_l zqqWbiV31}-dAnXhAy*Yaf`|sP68%WPFu)%MM~rnJ@j-eCj};tv)51g8OmrSfs^AHx zBd&*5%y6)l9%rn3UDtJ3t@cBOd|&{5(}E`v4*EZA0HN<$@em~az>0@P&>xfSfkS z0qO)!I#5?=Sx-_^)C0XhC`{yhNdLlzuU zBZHHnMRPXPA>|;3?yESR5rWqt0I1gBKcx8-h=-)i)2*$b;=qYvy`L&^Ze1r5(gg$$ zHk1YA)-L!r<0xXL=$y$gI}Lrfq&VWf_R*Hu3zSCJtn?@gk8tln`dm zD6{3wCZxB|5AwcBSh%20CPs)I0p(;!WuVvuVLAc?ykP@!Z9rm+WBZ=~L@8>z0k6z# z3SLrzQg&{(k4C~inLJ?Q7?{zMdTQ86gK66vhm{iR|b`1@9#a-GaCaf?KPOuae}o zkG_#4gd*lalGL$x!-2>K^;dCv-qKeC-fe$1QRu7eV2^kv@Bx>dVXwR5(36T8Q50HH zi9-xBgeRP40nvJxCX77WNMeZqg+!GDA+xGR`LKxTsDoCyQcwh&MC*wBNjO>w-Rfh= zO|4d&S|x?D#-zi_QLAe-)9VS$Gx-2u0(JE5Pz=|q0wzM#gw zaf^FL)o0i@pl1uK8bP=K1`f_wQf6S3UsAZ5z5~&{y&CZg|jF@$qhW&{y&CZg|j(_&5t|fxd;Od22Cbw}kVZ zXbL2~MFjo8iibwf zPgCOF1o~-8yqzNN1k)`M^wX5MCxX6V1*CBl`XA`Cn51RiFna(100v@9M??Vs0RI60 zpuMM)00009a7bBm001r{001r{0eGc9b^rhX2XskIMF->w3jqxUUl#VU0000PbVXQn zLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbNEJ;K`R2b7^yuD7D0Uu!ekIefI wFdh=ghO6PNBgsi)ES_VWQl7;iF1B#Zfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEytv lkEe@c2*>s00^^G;42 zaB^>EX>4U6ba`-PAZ2)IW&i+q+P#)xlG`8*h5zdmJpvL4!ErE{nckqs?*o(UCYyF9 zTc%-bFowrNAK=jW>u0AQ_=#hopq65e(cu?z$Wl;9?)qIhyD;zT!QBcE^>lv$S?7`a z>oK0IeL=l`bg;1MkH>nlx^&VKx+yO~KSmZ5R=c!>yj=CXo++<>C48FBx_XuQT9511 z^K&o+qbc;fker^q!##~r{B;#vdENsW!4=Bt)2xZFV@LPmQ}FJkr`Wl&>iuIV$i>wl zKE-I&v%ZeqjL;Lc+sF?uYP=8eHFmwF-p^}xB}7Cj(wfw5Dzk)!2xsuK+YmHdin7g= zkrJj@L9Bu1fmDez3!>dQaO1+A&zV=Quo&bNLoQOq&${GIveXG6jKp0GU>bpi_zG$s z)pGa3bKE?~tDG=$W3&{87^BCRM6W8plkP&jkEP4((mhu2C1Xv)#vnKDXaykLS4?%r zcP9F@ZrmrZ9t3rUIbnju9hbzR{IRzD6`6hN?AItEuK%nl#_A{ab8CEw$Xr$tRZ58BRUpX=gg~ zSvuBQ_dRs&v0G0)_j0GUs6JBTCvtzM#*5k@7G^v9poVI*n*@*JLy{D4^000SaNLh0L04^f{04^f| zc%?sf00007bV*G`2jmF@7di%u2k;UA002x$L_t(I%VT_adKm*0{AXZbU__!B7#J8B z zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3yia>FPLh5zdmdjup9g5zK?Gkb$Q{ys2GlQd1! z#%64=5aL-Mke2rU{_XS+E^#SRP)jk#=x~J`vJ^CuJFcT<7v^)lxR1t-oh}^$lSl23 z*SIhL0=s?=u(0ggt(~l{IB5m?h`a*&F|(kt{L%{K<*@U5r@Y3MaP>Ls?p0>DZb#Ss zdoU!UDdKq{**$xQXBxBk<1V=Kd0GTn_Or zN6XIoICnEccho*+zUgq?nY+HObJttyeLudggosQF+7D4j4|R%p-0BQluMxA$I@kX>5?nxlCh>CV~`t9cmW9al~bMh zorSK}jk^NtK~QI!6F#uG?Fg|cf37V*IeYl23*?Ti!PZs)qzH2>#IOee7uhREb|zxX z$YTPZ0_B2bzkvX&N@MC^<9!62Bx7!jF`mAna+qVmi)NNATS-2Jq$ws#DdkkMWRD|EbIe9L<(x~wqy?c1 zMi+RLvdQMRu;~^z+tQY|Qn7q$s9IySnrg14N%IZczr|+MQp>IEykjZtVb?wGwx>Ps zrDLsi-$U0PyYMb?CBlnydFKUBWn11$24Q8{C5Il|(9n3&X0)w~>0$9*8 zn7PCu$RIbExh&WeDxk(X;4~h@Krrkq+WErnliX8oq~TZGN73qIAyowR!8T}||Z z!^_&}OYa{w(aQsV&_+*sf2E23kl2UX=sDEawb5J9uV$xL7u-+U=>5%cXrte6hR53I zZ7p8{_4l;V3HkXp(4Vx?P2z67+;qX8dau;y{D4^000SaNLh0L04^f{04^f|c%?sf00007bV*G` z2jmF@7dsM&#uY9A002`-L_t(I%VS_50X#gt>_0Niz`(%3#6Te!HDE9p!1(a=GE&t4 zXJBApgwgV*QS|k~e{3`hk^oU&MmLZJO^_HDpa4=`fMFP^F8Ghl1?2c24gi4e6~wFC R#YF%B002ovPDHLkV1gnX3!?x4 literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_bg_dialog_hl.png b/textures/yl_speak_up_bg_dialog_hl.png new file mode 100644 index 0000000000000000000000000000000000000000..86662c107b75ef37244518a1004be610c83139e1 GIT binary patch literal 1057 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 JPDHLkV1nZ^;Vb|E literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_bg_dialog_vb.png b/textures/yl_speak_up_bg_dialog_vb.png new file mode 100644 index 0000000000000000000000000000000000000000..c6693171f0e1064767412f5b26f8097b79858107 GIT binary patch literal 934 zcmV;X16lluP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=H2lH@21h4-9dj({Wt;y9R}h`GTWe;$~0Rc5;@ zdb(>di!@<_W$}RbVQ0Gj&)>uSg@bZn7c^PSF?t*!hs*^H^XGBa)H3O!-RT}V8xHb;^3EI9MJ1|-c`nOV59p%Z|&l70$<< z5MMmnangJ5mP0Sp9wR?JuuJO3yU}cney*;B2&baki+U_&wU7uqgRE{5CE--G(<&Lo zu*3>t4Kxpw>L?S#$3cM_6&lo6U8!Pk@gpyu!SA?rBV8lABj#0SJxlsV;w4 zai8{${{&V*P?yby1r~pt5{J^q-jc;xkf%O4f4&-&+7^IC7+WzJ5)kl_L@AO_%!vp- z8u%25Fk}<GU&2Coiag`n6!DH!%pQPJR3 zHEL4RtR+eeQDclL&Tmvnl0woHQ%aU{Dkc_8Em|_OY$bCRe8M!xoLsh?b14|KK)RrJ zK}IPDA9934k9efRj(n7g?NdY58f&Unb1jXVG`#*Tw&a?%+)Ag8t<*!;9((H6b1wsX zZO8~iM;vL`$fI1;Hq~cp^g`}yYP6|MVqxW_4{8Xf^pK$WInl`s#5fR$r%3=CnkO@F z6ar6jlbO$qmqHoTNGF^|lNbnwPOO72b|2)vt7L)F1(sY3u5(ty6#&H`da7{!c;!e=D;47v@-+zudhB zwEzGB24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf00007bV*G`2jmF^ z016#inu0+9000_EL_t(I%VS^|1*2eC0LF)>m;E2%E}$3y0J1O!rNIG|761SM07*qo IM6N<$f{9_HaR2}S literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_bg_dialog_vt.png b/textures/yl_speak_up_bg_dialog_vt.png new file mode 100644 index 0000000000000000000000000000000000000000..7b1d91d47f797133794f576939a73da84c5b61d0 GIT binary patch literal 935 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 literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_mask_chest.png b/textures/yl_speak_up_mask_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..b0456da44cf232578425bb3744c28132bdb45748 GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!2%>VHW;`ADb50q$YKTtZeb8+WSBKa0w}2B z>Eak-aXR_W`389d!yXP+odU6{1_M4h_dWsEp2e9(7c>|c>?bnYfBE-&6;M5cr>mdK II;Vst0N`pHc>n+a literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_mask_head.png b/textures/yl_speak_up_mask_head.png new file mode 100644 index 0000000000000000000000000000000000000000..50586731233520d2b5ab3b3523ff4dea19b7edc0 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!2%>VHW;`ADb50q$YKTtZeb8+WSBKa0w`$f z>Eak-aXR_Wc?Q-3HW33OBLfgH6ks^)%&6SVHW;`ADb50q$YKTtZeb8+WSBKa0w}2A z>Eak-aXL9cf|Umddj9`E9C1U`K+q^LgmJ5g!N28KS^`?mC98mxO literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_mask_rleg.png b/textures/yl_speak_up_mask_rleg.png new file mode 100644 index 0000000000000000000000000000000000000000..d06c3d8a396f2dc41af89d89ef66e66ebd330610 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!2%>VHW;`ADb50q$YKTtZeb8+WSBKa0w}2A z>Eak-aXL9cf|Umddj4~2Bq*gETEUd1nb5d7Vd5c+A2OCsA`A@Ar!pB{<(tk9)WhKE L>gTe~DWM4f9>^OU literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_mask_shield.png b/textures/yl_speak_up_mask_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..bd5d5308f7af0d11ca520230181e5538ac611cb1 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQfx`y?k)`fL2$v|<&%LToCO|{ z#S9F3${@^GvDCf{D9B#o>Fdh=gp-@Wgn6g&7EYj$WQl7;iF1B#Zfaf$gL6@8Vo7R> zLV0FMhJw4NZ$Nk>pEyvFou`XqNXEUl7Z@2C7&w>~y!xLmt>lw1*=O_YA|@6N0R@K! f21Xo=1qYdx7ctwmdGhWDn#ADg>gTe~DWM4fr*Jg= literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_shield_test.png b/textures/yl_speak_up_shield_test.png new file mode 100644 index 0000000000000000000000000000000000000000..68203c5dc4c5361b3d6bf9d7eefa0322ee85e86a GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!VDw>HYaZfQn~>?A+G=b|7XZ<3)}a<|m&0%1||np?CsAPRHu2r$G7{OM?7@862M7NCO$> z>Eak-aXL9cfklGVamfM(y-G1QMS-O??Aw|Cayz`TWzJ(gbj;z^d%HVaERu~^>e~|6 zJ=lM_ZVy+(M8)s#?(Xj1v9wW;)#xGfVGRk^D+~-gdqpRgTe~DWM4f DurfTT$X?><>&pIwO-jUEAiatUq)xKLHKN2hKQ}iuuY|$5C^fMpHASI3 zvm`^o-P1Q9ypc~FsHnix#WBR<^wvoiIS(iZI8T0exo^c0QR{$TfdVYu%P%#^XuOwn zxpmBsFDK2*;nI=RNpaZ^HlGXXdd|A%HfO_Pca==f6PC_Fcl|X#<|gxtGk6z$*Ws%7 zoVFmKnd6Ut^NX{w2X*dnggwxTXwB`ff26^m{B~9D9^F!Zpxq3fu6{1-oD!M<&iq;V literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_staff_of_fashion.png b/textures/yl_speak_up_staff_of_fashion.png new file mode 100644 index 0000000000000000000000000000000000000000..23332e0c625c2f9b28ee0cefe53910c93f4aee3a GIT binary patch literal 1240 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 CkzQZ` literal 0 HcmV?d00001 diff --git a/textures/yl_speak_up_staff_of_shut_up.png b/textures/yl_speak_up_staff_of_shut_up.png new file mode 100644 index 0000000000000000000000000000000000000000..790c477fc4d3fa1d38966b772b201b6e6139ca7f GIT binary patch literal 1214 zcmV;v1VQ_WP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=HGcH}4whTpY{Spt%{E(dctr+3iHpC61}msBd% zN%fhRF)?5vOF|!Vh1&05o&Lck%$}?Z(I@W>E}LwUj7c>6m1ob!&VD_ZV{#*>b3q_d zjJiKYzvK;afxawxS#~ny?#fVVYhk-x$jgw#<>2tR94YS(%rZ=Q8B$rE?f$&&+cG}I zaJ_}H^!r+$X~0dRvl_%Z&%vU%VnXl<^C-p!_e_VXASP+bYS70K zq3(Ca?a_-S=nPp_)rj6H?~#I*kO`QlBd^b|XOYtp`a*t#^qlc0@+;Cb6M8J@9U*^? z59RDGa=J!3xm|ho{`7n!ukmzolA{A(?=|+`_T6Rcv1PwDb!=tMptp@|&9^*S!0k*N z$sR4CMU+IrIjT5h8(QJS0*sw4SI*qI&9QJnh*p{ej3Pyp3qcqGBS`^TRy!JZ37WJ5 zvS`6^P)OyBg{Rvz-4%{-Tsaa>55pMal^->FMXC2uP|PNtxk6krDh*K#a?E5HY9PXW zCDqLYAMO4cTonY}OmoBrv)P6P;NKiy4wp{Q3Pp!;FJKOJ89<5Wv#>c2{z&k zWv9+M=e!Fpy5zD~KPt@o;Kvs=2Msp3;6n&8q>w3Uw9!Q$LyR%SoFpl}xGtd{Q%ou4 z45t~EGv3Zn$a#}ZH@msbZ()mD+Hw&pT5NH}mr!C!C0D6hQODKSP-9IsH*Ppe%{JG3 z3oW+Pa>r5Y+HH5;_t0Zc)bmMgVg1nlfEq7qGD!`^a8iRbgu?`n&xsq%KujzHco+m& zNn|iHk*$?MZZI=RP!&!Pq#JN54`Lu_8;k3Fvb&ITgwN<(xDi0eJIK*M_X4?(+#XS@ zdH&GpBs9u!B-JRY4@y}g{_uJsxyuB7s~#SV^p^VgOZZ=`k56*&4*VYsJx{_Hdn0*MlKxXeihqO> z{Rt9RMaX5Ik$-0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht z7XSbNXh}ptR5;7El0gc? z(0lhMBQSbIl9q(l8YGSVS}Oqnw!;O$k~}@j!f%o-DAgbX|I;9Q^Hi`3KLk7Q5bVOX cEaktaqI1^jhqYy94rTFzTLl>Bf!0S!tbz&hy4t;&f3E3TcrQ~;g3R~Vq_3>`S<^? nyvQO*>?8JyNIV4d!1o5`v?px8@(#TI3ljBo^>bP0l+XkKL0v9J literal 0 HcmV?d00001 diff --git a/tools.lua b/tools.lua new file mode 100644 index 0000000..63fc178 --- /dev/null +++ b/tools.lua @@ -0,0 +1,147 @@ +minetest.register_tool("yl_speak_up:staff_of_i_said_so", { + description = "Staff of I-said-so", + inventory_image = "yl_speak_up_staff_of_i_said_so.png", + on_use = function(itemstack, user, pointed_thing) + + local has_priv = minetest.check_player_privs(user, {npc_master=true}) + local p_name = user:get_player_name() + + if not has_priv then + minetest.chat_send_player(p_name,yl_speak_up.message_tool_taken_because_of_lacking_priv) + minetest.log("action","[MOD] yl_speak_up: User "..p_name.." used an admin item: "..itemstack:get_name().." at pos "..minetest.pos_to_string(user:get_pos(),0)) + itemstack:take_item(1) + return itemstack + end + + if (pointed_thing.type == "object") then + local obj = pointed_thing.ref + local luaentity = obj:get_luaentity() + + if luaentity ~= nil and luaentity.yl_speak_up == nil then return itemstack end -- Is the thing we clicked really one of our NPCs? + + yl_speak_up.config(user, obj) + + return itemstack + end + end +}) + +minetest.register_tool("yl_speak_up:staff_of_shut_up", { + description = "Staff of shut-up", + inventory_image = "yl_speak_up_staff_of_shut_up.png", + on_use = function(itemstack, user, pointed_thing) + + local has_priv = minetest.check_player_privs(user, {npc_master=true}) + local p_name = user:get_player_name() + + if not has_priv then + minetest.chat_send_player(p_name,yl_speak_up.message_tool_taken_because_of_lacking_priv) + minetest.log("action","[MOD] yl_speak_up: User "..p_name.." used an admin item: "..itemstack:get_name().." at pos "..minetest.pos_to_string(user:get_pos(),0)) + itemstack:take_item(1) + return itemstack + end + + if (pointed_thing.type == "object") then + local obj = pointed_thing.ref + local luaentity = obj:get_luaentity() + + if luaentity ~= nil and luaentity.yl_speak_up == nil then return itemstack end -- Is the thing we clicked really one of our NPCs? + + luaentity.yl_speak_up.talk = false + local npc = luaentity.yl_speak_up.id + minetest.chat_send_player(p_name,"NPC with ID "..npc.." will shut up at pos "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + minetest.log("action","[MOD] yl_speak_up: NPC with ID n_"..npc.." will shut up at pos "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + return itemstack + end + end +}) + +minetest.register_tool("yl_speak_up:staff_of_dawai_dawai", { + description = "Staff of dawai-dawai", + inventory_image = "yl_speak_up_staff_of_dawai_dawai.png", + on_use = function(itemstack, user, pointed_thing) + + local has_priv = minetest.check_player_privs(user, {npc_master=true}) + local p_name = user:get_player_name() + + if not has_priv then + minetest.chat_send_player(p_name,yl_speak_up.message_tool_taken_because_of_lacking_priv) + minetest.log("action","[MOD] yl_speak_up: User "..p_name.." used an admin item: "..itemstack:get_name().." at pos "..minetest.pos_to_string(user:get_pos(),0)) + itemstack:take_item(1) + return itemstack + end + + if (pointed_thing.type == "object") then + local obj = pointed_thing.ref + local luaentity = obj:get_luaentity() + + if luaentity ~= nil and luaentity.yl_speak_up == nil then return itemstack end -- Is the thing we clicked really one of our NPCs? + + luaentity.yl_speak_up.talk = true + local npc = luaentity.yl_speak_up.id + minetest.chat_send_player(p_name,"NPC with ID "..npc.." will resume speech at pos "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + minetest.log("action","[MOD] yl_speak_up: NPC with ID n_"..npc.." will resume speech at pos "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + return itemstack + end + end +}) + +minetest.register_tool("yl_speak_up:staff_of_game_over", { + description = "Staff of game-over", + inventory_image = "yl_speak_up_staff_of_game_over.png", + on_use = function(itemstack, user, pointed_thing) + + local has_priv = minetest.check_player_privs(user, {npc_master=true}) + local p_name = user:get_player_name() + + if not has_priv then + minetest.chat_send_player(p_name,yl_speak_up.message_tool_taken_because_of_lacking_priv) + minetest.log("action","[MOD] yl_speak_up: User "..p_name.." used an admin item: "..itemstack:get_name().." at pos "..minetest.pos_to_string(user:get_pos(),0)) + itemstack:take_item(1) + return itemstack + end + + if (pointed_thing.type == "object") then + local obj = pointed_thing.ref + local luaentity = obj:get_luaentity() + + if luaentity ~= nil and luaentity.yl_speak_up == nil then return itemstack end -- Is the thing we clicked really one of our NPCs? + + local npc = luaentity.yl_speak_up.id + minetest.chat_send_player(p_name,"NPC with ID "..npc.." removed from position "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + minetest.log("action","[MOD] yl_speak_up: NPC with ID "..npc.." removed from position "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + obj:remove() + return itemstack + end + end +}) + +minetest.register_tool("yl_speak_up:staff_of_fashion", { + description = "Staff of fashion", + inventory_image = "yl_speak_up_staff_of_fashion.png", + on_use = function(itemstack, user, pointed_thing) + + local has_priv = minetest.check_player_privs(user, {npc_master=true}) + local p_name = user:get_player_name() + + if not has_priv then + minetest.chat_send_player(p_name,yl_speak_up.message_tool_taken_because_of_lacking_priv) + minetest.log("action","[MOD] yl_speak_up: User "..p_name.." used an admin item: "..itemstack:get_name().." at pos "..minetest.pos_to_string(user:get_pos(),0)) + itemstack:take_item(1) + return itemstack + end + + if (pointed_thing.type == "object") then + local obj = pointed_thing.ref + local luaentity = obj:get_luaentity() + + if luaentity ~= nil and luaentity.yl_speak_up == nil then return itemstack end -- Is the thing we clicked really one of our NPCs? + + yl_speak_up.fashion(user, obj) + local npc = luaentity.yl_speak_up.id + minetest.chat_send_player(p_name,"NPC with ID n_"..npc.." will redress at pos "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + minetest.log("action","[MOD] yl_speak_up: NPC with ID n_"..npc.." will redress at pos "..minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name) + return itemstack + end + end +}) \ No newline at end of file