From 206aab22733a2e6de51ebb66b86845f48e2ce3b6 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Thu, 10 Sep 2020 16:31:41 +0200 Subject: [PATCH] Use version 1.0.0 of tt/tt_base mods --- mods/tt/API.md | 10 +- mods/tt/README.md | 11 +- mods/tt/init.lua | 163 ++++----------- mods/tt/mod.conf | 2 +- mods/tt/snippets.lua | 11 + mods/tt_base/README.md | 25 +++ mods/tt_base/init.lua | 190 ++++++++++++++++++ mods/{tt => tt_base}/locale/template.txt | 7 + .../tt.de.tr => tt_base/locale/tt_base.de.tr} | 9 +- mods/tt_base/mod.conf | 3 + mods/tt_base/screenshot.png | Bin 0 -> 9878 bytes 11 files changed, 298 insertions(+), 133 deletions(-) create mode 100644 mods/tt/snippets.lua create mode 100644 mods/tt_base/README.md create mode 100644 mods/tt_base/init.lua rename mods/{tt => tt_base}/locale/template.txt (71%) rename mods/{tt/locale/tt.de.tr => tt_base/locale/tt_base.de.tr} (67%) create mode 100644 mods/tt_base/mod.conf create mode 100644 mods/tt_base/screenshot.png diff --git a/mods/tt/API.md b/mods/tt/API.md index 4616fdc..a510553 100644 --- a/mods/tt/API.md +++ b/mods/tt/API.md @@ -7,9 +7,6 @@ Add these to the item definition. * `_tt_ignore`: If `true`, the `description` of this item won't be altered at all * `_tt_help`: Custom help text -* `_tt_food`: If `true`, item is a food item that can be consumed by the player -* `_tt_food_hp`: Health increase (in HP) for player when consuming food item -* `_tt_food_satiation`: Satiation increase for player when consuming food item (note: the meaning of satiation is depending on the game being used; some games might not have a satiation mechanic at all, in which case you can skip this field) Once this mod had overwritten the `description` field of an item was overwritten, it will save the original (unaltered) `description` in the `_tt_original_description` field. @@ -17,8 +14,11 @@ Once this mod had overwritten the `description` field of an item was overwritten Register a custom snippet function. `func` is a function of the form `func(itemstring)`. -It will be called for (nearly) every itemstring and it must return a string you want to append to this item or `nil` if nothing shall be appended. -You can optionally return the text color in `"#RRGGBB"` format as the second return value. +It will be called for (nearly) every itemstring. + +Returns: Two values, the first one is required. +1st return value: A string you want to append to this item or `nil` if nothing shall be appended. +2nd return value: If nil, `tt` will take of the text color. If a ColorString in `"#RRGGBB"` format, entire text is colorized in this color. Return `false` to force `tt` to not apply text any colorization (useful if you want to call `minetest.colorize` yourself. Example: diff --git a/mods/tt/README.md b/mods/tt/README.md index ca76902..92cd652 100644 --- a/mods/tt/README.md +++ b/mods/tt/README.md @@ -1,12 +1,11 @@ # Extended Tooltip (`tt`) This mod extends the tooltip of items to add more informative texts. -It displays the following useful information: -* Weapon damage and speed -* Tool properties -* Noteworthy block properties -* Food satiation -* Custom help text (added by mods) +The mod itself does nothing and is meant to be integrated into +games to use the API to define custom tooltips (see `API.md`). + +## Version +1.0.0 ## License MIT License. diff --git a/mods/tt/init.lua b/mods/tt/init.lua index fe663c5..a79bf27 100644 --- a/mods/tt/init.lua +++ b/mods/tt/init.lua @@ -1,127 +1,50 @@ -local S = minetest.get_translator("tt") -local COLOR_DEFAULT = "#d0ffd0" -local COLOR_DANGER = "#ffff00" -local COLOR_GOOD = "#00ff00" - tt = {} +tt.COLOR_DEFAULT = "#d0ffd0" +tt.COLOR_DANGER = "#ffff00" +tt.COLOR_GOOD = "#00ff00" + +-- API tt.registered_snippets = {} -local function append_descs() - for itemstring, def in pairs(minetest.registered_items) do - if itemstring ~= "" and itemstring ~= "air" and itemstring ~= "ignore" and itemstring ~= "unknown" and def ~= nil and def.description ~= nil and def.description ~= "" and def._tt_ignore ~= true then - local desc = def.description - local orig_desc = desc - -- Custom text - if def._tt_help then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, def._tt_help) - end - -- Tool info - if def.tool_capabilities then - -- Digging stats - if def.tool_capabilities.groupcaps then - -- TODO: Add more detail (such as digging speed) - --local groups = {} - --for group, caps in pairs(def.tool_capabilities.groupcaps) do - -- table.insert(groups, group) - --end - --desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Digs: @1", table.concat(groups, ", "))) - end - -- Weapon stats - if def.tool_capabilities.damage_groups then - for group, damage in pairs(def.tool_capabilities.damage_groups) do - if group == "fleshy" then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Damage: @1", damage)) - else - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Damage (@1): @2", group, damage)) - end - end - local full_punch_interval = def.tool_capabilities.full_punch_interval - if not full_punch_interval then - full_punch_interval = 1 - end - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Full punch interval: @1s", full_punch_interval)) - end - end - -- Food - if def._tt_food then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Food item")) - if def._tt_food_hp then - local msg = S("+@1 food points", def._tt_food_hp) - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, msg) - end - -- NOTE: This is unused atm - --[[if def._tt_food_satiation then - if def._tt_food_satiation >= 0 then - msg = S("+@1 satiation", def._tt_food_satiation) - else - msg = S("@1 satiation", def._tt_food_satiation) - end - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, msg) - end]] - end - -- Node info - -- Damage-related - do - if def.damage_per_second and def.damage_per_second > 0 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DANGER, S("Contact damage: @1 per second", def.damage_per_second)) - end - if def.drowning and def.drowning > 0 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DANGER, S("Drowning damage: @1", def.drowning)) - end - local tmp = minetest.get_item_group(itemstring, "fall_damage_add_percent") - if tmp > 0 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DANGER, S("Fall damage: +@1%", tmp)) - elseif tmp == -100 then - desc = desc .. "\n" .. minetest.colorize(COLOR_GOOD, S("No fall damage")) - elseif tmp < 0 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Fall damage: @1%", tmp)) - end - end - -- Movement-related node facts - if minetest.get_item_group(itemstring, "disable_jump") == 1 and not def.climbable then - if def.liquidtype == "none" then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("No jumping")) - else - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("No swimming upwards")) - end - end - if def.climbable then - if minetest.get_item_group(itemstring, "disable_jump") == 1 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Climbable (only downwards)")) - else - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Climbable")) - end - end - if minetest.get_item_group(itemstring, "slippery") >= 1 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Slippery")) - end - local tmp = minetest.get_item_group(itemstring, "bouncy") - if tmp >= 1 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Bouncy (@1%)", tmp)) - end - -- Node appearance - tmp = def.light_source - if tmp and tmp >= 1 then - desc = desc .. "\n" .. minetest.colorize(COLOR_DEFAULT, S("Luminance: @1", tmp)) - end - -- Custom functions - for s=1, #tt.registered_snippets do - local str, snippet_color = tt.registered_snippets[s](itemstring) - if not snippet_color then - snippet_color = COLOR_DEFAULT - end - if str then - desc = desc .. "\n" .. minetest.colorize(snippet_color, str) - end - end - - minetest.override_item(itemstring, { description = desc, _tt_original_description = orig_desc }) - end - end -end - tt.register_snippet = function(func) table.insert(tt.registered_snippets, func) end -minetest.register_on_mods_loaded(append_descs) +dofile(minetest.get_modpath(minetest.get_current_modname()).."/snippets.lua") + +-- Apply item description updates + +local function append_snippets() + for itemstring, def in pairs(minetest.registered_items) do + if itemstring ~= "" and itemstring ~= "air" and itemstring ~= "ignore" and itemstring ~= "unknown" and def ~= nil and def.description ~= nil and def.description ~= "" and def._tt_ignore ~= true then + local desc = def.description + local orig_desc = desc + local first = true + -- Apply snippets + for s=1, #tt.registered_snippets do + local str, snippet_color = tt.registered_snippets[s](itemstring) + if snippet_color == nil then + snippet_color = tt.COLOR_DEFAULT + elseif snippet_color == false then + snippet_color = false + end + if str then + if first then + first = false + end + desc = desc .. "\n" + if snippet_color then + desc = desc .. minetest.colorize(snippet_color, str) + else + desc = desc .. str + end + end + end + if desc ~= def.description then + minetest.override_item(itemstring, { description = desc, _tt_original_description = orig_desc }) + end + end + end +end + +minetest.register_on_mods_loaded(append_snippets) diff --git a/mods/tt/mod.conf b/mods/tt/mod.conf index aee1572..d5c8c2e 100644 --- a/mods/tt/mod.conf +++ b/mods/tt/mod.conf @@ -1,2 +1,2 @@ name = tt -description = Appends a helpful tooltip to the item description +description = Support for custom tooltip extensions for items diff --git a/mods/tt/snippets.lua b/mods/tt/snippets.lua new file mode 100644 index 0000000..694b225 --- /dev/null +++ b/mods/tt/snippets.lua @@ -0,0 +1,11 @@ +-- CUSTOM SNIPPETS -- + +-- Custom text (_tt_help) +tt.register_snippet(function(itemstring) + local def = minetest.registered_items[itemstring] + if def._tt_help then + return def._tt_help + end +end) + + diff --git a/mods/tt_base/README.md b/mods/tt_base/README.md new file mode 100644 index 0000000..5622e36 --- /dev/null +++ b/mods/tt_base/README.md @@ -0,0 +1,25 @@ +This mod is for the Extended Tooltips [tt] mod to extend item tooltips with the following +basic info: + +* Tool digging times +* Weapon stats +* Food stats +* Node damage +* Node light level +* Node info: climbable, slippery, bouncy, jumping restriction + +This mod assumes that the default gameplay behavior of Minetest is used. + +This mod introduces support for new item definition fields: + +* `_tt_food`: If `true`, item is a food item that can be consumed by the player +* `_tt_food_hp`: Health increase (in HP) for player when consuming food item + +Because there is no standard way in Minetest (yet) to mark an item as food, these fields +are required for food items to be recognized as such. + +## Version +1.0.0 + +## License +MIT License. diff --git a/mods/tt_base/init.lua b/mods/tt_base/init.lua new file mode 100644 index 0000000..4ea8a00 --- /dev/null +++ b/mods/tt_base/init.lua @@ -0,0 +1,190 @@ +local S = minetest.get_translator("tt_base") + +local function get_min_digtime(caps) + local mintime + local unique = true + local maxlevel = caps.maxlevel + if not maxlevel then + maxlevel = 1 + end + if maxlevel > 1 then + unique = false + end + if caps.times then + for r=1,3 do + local time = caps.times[r] + if time and maxlevel > 1 then + time = time / maxlevel + end + if time and ((not mintime) or (time < mintime)) then + if mintime and (time < mintime) then + unique = false + end + mintime = time + end + end + end + return mintime, unique +end + +local function newline(str) + if str ~= "" then + str = str .. "\n" + end + return str +end + +-- Tool information (digging times, weapon stats) +tt.register_snippet(function(itemstring) + local def = minetest.registered_items[itemstring] + local desc = "" + if def.tool_capabilities then + -- Digging times + local digs = "" + local d + if def.tool_capabilities.groupcaps then + for group, caps in pairs(def.tool_capabilities.groupcaps) do + local mintime, unique_mintime + if caps.times then + mintime, unique_mintime = get_min_digtime(caps) + if mintime and (mintime > 0 or (not unique_mintime)) then + d = S("Digs @1 blocks", group) .. "\n" + d = d .. S("Minimum dig time: @1s", string.format("%.2f", mintime)) + digs = newline(digs) + digs = digs .. d + elseif mintime and mintime == 0 then + d = S("Digs @1 blocks instantly", group) + digs = newline(digs) + digs = digs .. d + end + end + end + if digs ~= "" then + desc = desc .. digs + end + end + -- Weapon stats + if def.tool_capabilities.damage_groups then + for group, damage in pairs(def.tool_capabilities.damage_groups) do + local msg + if group == "fleshy" then + if damage >= 0 then + msg = S("Damage: @1", damage) + else + msg = S("Healing: @1", math.abs(damage)) + end + else + if damage >= 0 then + msg = S("Damage (@1): @2", group, damage) + else + msg = S("Healing (@1): @2", group, math.abs(damage)) + end + end + desc = newline(desc) + desc = desc .. msg + end + local full_punch_interval = def.tool_capabilities.full_punch_interval + if not full_punch_interval then + full_punch_interval = 1 + end + desc = newline(desc) + desc = desc .. S("Full punch interval: @1s", string.format("%.2f", full_punch_interval)) + end + end + if desc == "" then + desc = nil + end + return desc +end) + +-- Food +tt.register_snippet(function(itemstring) + local def = minetest.registered_items[itemstring] + local desc + if def._tt_food then + desc = S("Food item") + if def._tt_food_hp then + local msg = S("+@1 food points", def._tt_food_hp) + desc = desc .. "\n" .. msg + end + end + return desc +end) + +-- Node info +tt.register_snippet(function(itemstring) + local def = minetest.registered_items[itemstring] + local desc = "" + + -- Health-related node facts + if def.damage_per_second then + if def.damage_per_second > 0 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DANGER, S("Contact damage: @1 per second", def.damage_per_second)) + elseif def.damage_per_second < 0 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_GOOD, S("Contact healing: @1 per second", math.abs(def.damage_per_second))) + end + end + if def.drowning and def.drowning ~= 0 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DANGER, S("Drowning damage: @1", def.drowning)) + end + local tmp = minetest.get_item_group(itemstring, "fall_damage_add_percent") + if tmp > 0 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DANGER, S("Fall damage: +@1%", tmp)) + elseif tmp == -100 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_GOOD, S("No fall damage")) + elseif tmp < 0 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("Fall damage: @1%", tmp)) + end + + -- Movement-related node facts + if minetest.get_item_group(itemstring, "disable_jump") == 1 and not def.climbable then + if def.liquidtype == "none" then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("No jumping")) + elseif minetest.get_item_group(itemstring, "fake_liquid") == 0 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("No swimming upwards")) + else + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("No rising")) + end + end + if def.climbable then + if minetest.get_item_group(itemstring, "disable_jump") == 1 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("Climbable (only downwards)")) + else + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("Climbable")) + end + end + if minetest.get_item_group(itemstring, "slippery") >= 1 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("Slippery")) + end + local tmp = minetest.get_item_group(itemstring, "bouncy") + if tmp >= 1 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("Bouncy (@1%)", tmp)) + end + + -- Node appearance + tmp = def.light_source + if tmp and tmp >= 1 then + desc = newline(desc) + desc = desc .. minetest.colorize(tt.COLOR_DEFAULT, S("Luminance: @1", tmp)) + end + + + if desc == "" then + desc = nil + end + return desc, false +end) + diff --git a/mods/tt/locale/template.txt b/mods/tt_base/locale/template.txt similarity index 71% rename from mods/tt/locale/template.txt rename to mods/tt_base/locale/template.txt index 62b248b..488a0e2 100644 --- a/mods/tt/locale/template.txt +++ b/mods/tt_base/locale/template.txt @@ -1,12 +1,15 @@ # textdomain:tt Damage: @1= Damage (@1): @2= +Healing: @1= +Healing (@1): @2= Full punch interval: @1s= Food item= +@1 satiation= @1 satiation= +@1 food points= Contact damage: @1 per second= +Contact healing: @1 per second= Drowning damage: @1= Bouncy (@1%)= Luminance: @1= @@ -15,6 +18,10 @@ Climbable= Climbable (only downwards)= No jumping= No swimming upwards= +No rising= Fall damage: @1%= Fall damage: +@1%= No fall damage= +Digs @1 blocks= +Digs @1 blocks instantly= +Minimum dig time: @1s= diff --git a/mods/tt/locale/tt.de.tr b/mods/tt_base/locale/tt_base.de.tr similarity index 67% rename from mods/tt/locale/tt.de.tr rename to mods/tt_base/locale/tt_base.de.tr index adf0432..6e45bf6 100644 --- a/mods/tt/locale/tt.de.tr +++ b/mods/tt_base/locale/tt_base.de.tr @@ -1,12 +1,15 @@ -# textdomain:tt +# textdomain:tt_base Damage: @1=Schaden: @1 Damage (@1): @2=Schaden (@1): @2 +Healing: @1=Heilung: @1 +Healing (@1): @2=Heilung (@1): @2 Full punch interval: @1s=Zeit zum Ausholen: @1s Food item=Lebensmittel +@1 satiation=+@1 Sättigung @1 satiation=@1 Sättigung +@1 food points=+@1 Nahrungspunkte Contact damage: @1 per second=Kontaktschaden: @1 pro Sekunde +Contact healing: @1 per second=Kontaktheilung: @1 pro Sekunde Drowning damage: @1=Ertrinkensschaden: @1 Bouncy (@1%)=Sprunghaft (@1%) Luminance: @1=Lichtstärke: @1 @@ -15,6 +18,10 @@ Climbable=Erkletterbar Climbable (only downwards)=Erkletterbar (nur nach unten) No jumping=Kein Springen No swimming upwards=Kein nach oben schwimmen +No rising=Kein Aufsteigen Fall damage: @1%=Fallschaden: @1% Fall damage: +@1%=Fallschaden: +@1% No fall damage=Kein Fallschaden +Digs @1 blocks=Gräbt „@1“-Blöcke +Digs @1 blocks instantly=Gräbt „@1“-Blöcke sofort +Minimum dig time: @1s=Minimale Grabezeit: @1s diff --git a/mods/tt_base/mod.conf b/mods/tt_base/mod.conf new file mode 100644 index 0000000..f333041 --- /dev/null +++ b/mods/tt_base/mod.conf @@ -0,0 +1,3 @@ +name = tt_base +description = Adds generic tooltip extensions to items +depends = tt diff --git a/mods/tt_base/screenshot.png b/mods/tt_base/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..ef2dfa57c83404a13e6459fc3f1d8ba61df5333c GIT binary patch literal 9878 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 J1;jY${{fgk=-U7Q literal 0 HcmV?d00001