write something there
4
mods/researcher/.gitattributes
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
*.xcf export-ignore
|
||||
*.blend export-ignore
|
||||
*.bbmodel export-ignore
|
||||
*.json export-ignore
|
13
mods/researcher/CREDITS.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Sounds
|
||||
|
||||
- `sounds/researcher_level_up.ogg` derived from ["Game Pickup" by INEBA](https://freesound.org/people/IENBA/sounds/698768/) at FreeSound.org, licensed [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
- `sounds/researcher_research.ogg` derived from ["Supershort Ping, Or Short Notification.wav" by MATRIXXX_](https://freesound.org/people/MATRIXXX_/sounds/495650/) at FreeSound.org, licensed [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
- `sounds/researcher_duplicate.ogg` derived from ["Rattle Egg" by qubodup](https://freesound.org/people/qubodup/sounds/171935/) at FreeSound.org, licensed [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
|
||||
# Textures
|
||||
|
||||
- `textures/researcher_gui_hb_bg.png` is a copy of [gui_hb_bg.png in Minetest Game](https://github.com/minetest/minetest_game/blob/master/mods/default/textures/gui_hb_bg.png) by paramat et. al., licensed [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/), or [CC0](https://creativecommons.org/publicdomain/zero/1.0/); see [Minetest Game license.txt](https://github.com/minetest/minetest_game/blob/master/mods/default/license.txt) for more info
|
||||
- `textures/researcher_research_table_surface.png` made by Mirtilo as part of the [Baunilha texture pack](https://content.minetest.net/packages/Mirtilo/baunilha/), licensed [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
- `textures/researcher_research_table_frame.png` made by JoeEnderman, dedicated to the public domain via [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
- `textures/researcher_icon*` files derived from "Erlenmeyer icon" by Lorc and downloaded from <https://game-icons.net/1x1/lorc/erlenmeyer.html>, licensed [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)
|
||||
- The light bulb icon used in `screenshot.png` and `screenshot.xcf` is derivied from ["Light Bulb by Anamika Singh"](https://thenounproject.com/icon/light-bulb-6757220/), licensed [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)
|
13
mods/researcher/LICENSE
Normal file
|
@ -0,0 +1,13 @@
|
|||
MIT License
|
||||
|
||||
Copyright © 2024 EmptyStar <https://github.com/EmptyStar>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
Additional license information and appropriate attributions can be found in `CREDITS.md`.
|
76
mods/researcher/README.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
Researcher
|
||||
==========
|
||||
|
||||
Unlock the limitless creative potential of your world with Researcher! This mod adds a thematic "earned creative" mode to Minetest through which you can earn infinite duplication of the items in your world. When applied to a survival world, research becomes an enticing goal that rewards your gameplay efforts with endless command of the items you study the most.
|
||||
|
||||
Research
|
||||
--------
|
||||
|
||||
Research is the mechanism by which items are permanently destroyed in order to gain the ability to duplicate them. You gain *research points* when you destroy an item, a score which is tracked per player and per item such that every player has their own research point total for each item in the game.
|
||||
|
||||
Reaching certain research point milestones known as *research levels* will grant you permanent bonuses towards future research of similar items. Reaching maximum research levels for an item will grant you the permanent ability to duplicate the item.
|
||||
|
||||
### How to Research
|
||||
|
||||
To research an item, open the Researcher menu in your inventory or via `/research`, place an item into the research box, then click the "Research" button. Researching an item will immediately destroy the researched item and grant you research points in return. The amount of research points gained per research is a constant amount plus any research bonuses you've gained.
|
||||
|
||||
### Duplication
|
||||
|
||||
Once you've reached the maximum research level for an item (level 10 by default), you are granted the permanent ability to duplicate the item as many times as you'd like at no cost. When duplication is unlocked for an item, the "Research" button in the research menu will change to "Duplicate" for the item, and clicking the button will add a full stack of the item to your main inventory.
|
||||
|
||||
Bonuses
|
||||
-------
|
||||
|
||||
You can earn bonuses towards research by researching under certain conditions. These bonuses encourage players to specialize in a particular field of research.
|
||||
|
||||
### Group Research
|
||||
|
||||
Gaining research levels for a particular item will earn permanent bonuses on future items that match any of the item's groups. For example, cobblestone will have the `stone` group, and five research levels in cobblestone will add +5 research points for all `stone` items, including cobblestone itself.
|
||||
|
||||
### Focused Research
|
||||
|
||||
Focusing your research on a particular item will grant you cumulative bonuses to continuous research of the item and its groups. Whenever you research an item, the item becomes your current research focus.
|
||||
|
||||
You will gain additional research points for each successive research that matches the focus item or its groups, up to a certain maximum. More points are given for an exact match and fewer points are given for a group match. All focus bonuses are reset to zero if you research a non-matching item.
|
||||
|
||||
### Research Table
|
||||
|
||||
A research table is a special node that allows you to gain significant bonuses to your research. A research table has its own focus item that must be set and matched in order to use it for research.
|
||||
|
||||
To use a research table, craft it, place it, then right-click it in order to access its menu. Place an item into its "focus" slot to set its focus item. The research table can then be used to research items that match its focus item or the item's groups.
|
||||
|
||||
Placing items near the research table that match the groups of the research table's focus item will increase the research bonus it grants, up to a certain maximum (10 items by default). This can be done by placing matching nodes in the world near the research table or by placing a node with an inventory that contains matching items (e.g., a nearby chest full of swords to gain research bonuses to swords).
|
||||
|
||||
The crafing recipe for a research table requires wood (`[W]`) and stone (`[S]`) as illustrated below:
|
||||
|
||||
```
|
||||
[S] [S] [S]
|
||||
[W] [W] [W]
|
||||
[W] [ ] [W]
|
||||
```
|
||||
|
||||
Slash Commands
|
||||
--------------
|
||||
|
||||
Researcher can be controlled via the `/research` slash command.
|
||||
|
||||
- `/research` - Opens the research interface, or prompts you to use your inventory for research if a supported inventory mod is being used (see next section for complete list)
|
||||
- `/research reset` - Reset your entire research progress for all items
|
||||
- `/research reset <item ID>` - Reset your research progress for the item specified by its item ID, e.g., `default:cobble`
|
||||
|
||||
Supported Games/Mods
|
||||
--------------------
|
||||
|
||||
Researcher supports a number of games and mods. Popular games include:
|
||||
|
||||
- [Minetest Game](https://content.minetest.net/packages/Minetest/minetest_game/) and its derivatives, e.g., [MeseCraft](https://content.minetest.net/packages/MeseCraft/mesecraft/), [Asuna](https://content.minetest.net/packages/EmptyStar/asuna/), etc.
|
||||
- [Mineclonia](https://content.minetest.net/packages/ryvnf/mineclonia/)
|
||||
- [VoxeLibre](https://content.minetest.net/packages/Wuzzy/mineclone2/)
|
||||
- Any Minetest game in theory, but those not listed above are untested
|
||||
|
||||
Supported mods include:
|
||||
|
||||
- [Awards](https://content.minetest.net/packages/rubenwardy/awards/) - Adds awards for certain research milestones
|
||||
- [sfinv](https://content.minetest.net/packages/rubenwardy/sfinv/) (Minetest Game default) - Adds an integrated research tab where research can be performed
|
||||
- [i3](https://content.minetest.net/packages/mt-mods/i3/) - Adds an integrated research tab
|
||||
- [Unified Inventory](https://content.minetest.net/packages/RealBadAngel/unified_inventory/) - Adds an integrated research tab
|
120
mods/researcher/init.lua
Normal file
|
@ -0,0 +1,120 @@
|
|||
-- Globals
|
||||
researcher = {
|
||||
settings = {
|
||||
-- Research points and levels
|
||||
points_per_level = tonumber(minetest.settings:get("researcher.points_per_level",600) or 600),
|
||||
points_per_research = tonumber(minetest.settings:get("researcher.points_per_research",100) or 100),
|
||||
level_max = tonumber(minetest.settings:get("researcher.level_max",10) or 10),
|
||||
level_scale = tonumber(minetest.settings:get("researcher.level_scale",1.25) or 1.25),
|
||||
|
||||
-- Bonuses for group research
|
||||
group_research_bonus = tonumber(minetest.settings:get("researcher.group_research_bonus",1) or 1),
|
||||
group_research_bonus_max = tonumber(minetest.settings:get("researcher.group_research_bonus_max",150) or 150),
|
||||
|
||||
-- Bonuses for focused research
|
||||
focused_research_bonus_exact = tonumber(minetest.settings:get("researcher.focused_research_bonus_exact",5) or 5),
|
||||
focused_research_bonus_group = tonumber(minetest.settings:get("researcher.focused_research_bonus_group",1) or 1),
|
||||
focused_research_bonus_max = tonumber(minetest.settings:get("researcher.focused_research_bonus_max",150) or 150),
|
||||
|
||||
-- Bonuses for using a research table
|
||||
research_table_bonus_exact = tonumber(minetest.settings:get("researcher.research_table_bonus_exact",25) or 25),
|
||||
research_table_bonus_group = tonumber(minetest.settings:get("researcher.research_table_bonus_group",5) or 5),
|
||||
research_table_adjacency_bonus = tonumber(minetest.settings:get("researcher.research_table_adjacency_bonus",10) or 10),
|
||||
research_table_adjacency_max = tonumber(minetest.settings:get("researcher.research_table_adjacency_max",10) or 10),
|
||||
research_table_adjacency_radius = tonumber(minetest.settings:get("researcher.research_table_adjacency_radius",3) or 3),
|
||||
research_table_player_radius = tonumber(minetest.settings:get("researcher.research_table_player_radius",2) or 2),
|
||||
research_table_bonus_max = tonumber(minetest.settings:get("researcher.research_table_bonus_max",150) or 150),
|
||||
|
||||
-- Built-in item discount amounts
|
||||
discount_stack_max = tonumber(minetest.settings:get("researcher.discount_stack_max",-250) or -250),
|
||||
discount_mapgen = tonumber(minetest.settings:get("researcher.discount_mapgen",-400) or -400),
|
||||
discount_not_craftable = tonumber(minetest.settings:get("researcher.discount_not_craftable",-250) or -250),
|
||||
|
||||
-- Use research awards
|
||||
awards = minetest.settings:get_bool("researcher.awards",true) and minetest.get_modpath("awards") and true or false,
|
||||
|
||||
-- Groups that are excluded from group matching
|
||||
excluded_groups = minetest.settings:get("researcher.excluded_groups") or table.concat({
|
||||
"not_in_creative_inventory",
|
||||
"attached_node",
|
||||
"connect_to_raillike",
|
||||
"dig_immediate",
|
||||
"disable_jump",
|
||||
"disable_descend",
|
||||
"fall_damage_add_percent",
|
||||
"falling_node",
|
||||
"float",
|
||||
"level",
|
||||
"oddly_breakable_by_hand",
|
||||
"immortal",
|
||||
"disable_repair",
|
||||
"creative_breakable",
|
||||
"opaque",
|
||||
"solid",
|
||||
}," "),
|
||||
},
|
||||
|
||||
-- Cached mod data
|
||||
data = {
|
||||
save = {},
|
||||
},
|
||||
|
||||
-- Mod storage
|
||||
storage = minetest.get_mod_storage(),
|
||||
|
||||
-- Registered data
|
||||
registered_items = {},
|
||||
registered_adjustments = {},
|
||||
registered_bonuses = {},
|
||||
registered_on_research = {},
|
||||
|
||||
-- Item groups indexed by group name
|
||||
groups = {},
|
||||
|
||||
-- Dependency info
|
||||
dependencies = (function(deps)
|
||||
for _,mod in ipairs({
|
||||
"default",
|
||||
"mcl_sounds",
|
||||
"mcl_inventory",
|
||||
"sfinv",
|
||||
"awards",
|
||||
"unified_inventory",
|
||||
"i3",
|
||||
}) do
|
||||
deps[mod] = minetest.get_modpath(mod)
|
||||
end
|
||||
return deps
|
||||
end)({}),
|
||||
}
|
||||
|
||||
-- Get excluded groups from settings
|
||||
researcher.excluded_groups = (function()
|
||||
local groups = researcher.settings.excluded_groups:split("[ \n\r\t]+",false,-1,true)
|
||||
local exclude = {}
|
||||
for _,group in ipairs(groups) do
|
||||
exclude[group] = true
|
||||
end
|
||||
return exclude
|
||||
end)()
|
||||
|
||||
-- Load secondary files if the Research content pack is enabled in Asuna settings
|
||||
if asuna.content.research.enabled then
|
||||
local mpath = minetest.get_modpath("researcher")
|
||||
local function runfile(file)
|
||||
dofile(mpath .. "/src/" .. file .. ".lua")
|
||||
end
|
||||
|
||||
for _,file in ipairs({
|
||||
"api",
|
||||
"inventory",
|
||||
"bonuses",
|
||||
"research_table",
|
||||
"scan",
|
||||
"commands",
|
||||
"gui",
|
||||
"awards",
|
||||
}) do
|
||||
runfile(file)
|
||||
end
|
||||
end
|
5
mods/researcher/mod.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
name = researcher
|
||||
title = Researcher
|
||||
description = Research items to unlock duplication
|
||||
author = EmptyStar
|
||||
optional_depends = sfinv, awards, default, mcl_sounds, mcl_inventory, unified_inventory, i3
|
901
mods/researcher/models/research_table.obj
Normal file
|
@ -0,0 +1,901 @@
|
|||
v -0.25 0 -0.25
|
||||
v -0.25 0 -0.40625
|
||||
v -0.25 -0.25 -0.25
|
||||
v -0.25 -0.25 -0.40625
|
||||
v -0.40625 0 -0.40625
|
||||
v -0.40625 0 -0.25
|
||||
v -0.40625 -0.25 -0.40625
|
||||
v -0.40625 -0.25 -0.25
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 4/4/1 7/3/1 5/2/1 2/1/1
|
||||
f 3/8/2 4/7/2 2/6/2 1/5/2
|
||||
f 8/12/3 3/11/3 1/10/3 6/9/3
|
||||
f 7/16/4 8/15/4 6/14/4 5/13/4
|
||||
f 6/20/5 1/19/5 2/18/5 5/17/5
|
||||
f 7/24/6 4/23/6 3/22/6 8/21/6
|
||||
v -0.23125 0.0625 -0.23125
|
||||
v -0.23125 0.0625 -0.425
|
||||
v -0.23125 0 -0.23125
|
||||
v -0.23125 0 -0.425
|
||||
v -0.425 0.0625 -0.425
|
||||
v -0.425 0.0625 -0.23125
|
||||
v -0.425 0 -0.425
|
||||
v -0.425 0 -0.23125
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 12/28/7 15/27/7 13/26/7 10/25/7
|
||||
f 11/32/8 12/31/8 10/30/8 9/29/8
|
||||
f 16/36/9 11/35/9 9/34/9 14/33/9
|
||||
f 15/40/10 16/39/10 14/38/10 13/37/10
|
||||
f 14/44/11 9/43/11 10/42/11 13/41/11
|
||||
f 15/48/12 12/47/12 11/46/12 16/45/12
|
||||
v -0.21875 -0.25 -0.21875
|
||||
v -0.21875 -0.25 -0.4375
|
||||
v -0.21875 -0.375 -0.21875
|
||||
v -0.21875 -0.375 -0.4375
|
||||
v -0.4375 -0.25 -0.4375
|
||||
v -0.4375 -0.25 -0.21875
|
||||
v -0.4375 -0.375 -0.4375
|
||||
v -0.4375 -0.375 -0.21875
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 20/52/13 23/51/13 21/50/13 18/49/13
|
||||
f 19/56/14 20/55/14 18/54/14 17/53/14
|
||||
f 24/60/15 19/59/15 17/58/15 22/57/15
|
||||
f 23/64/16 24/63/16 22/62/16 21/61/16
|
||||
f 22/68/17 17/67/17 18/66/17 21/65/17
|
||||
f 23/72/18 20/71/18 19/70/18 24/69/18
|
||||
v -0.1875 -0.375 -0.1875
|
||||
v -0.1875 -0.375 -0.46875
|
||||
v -0.1875 -0.5 -0.1875
|
||||
v -0.1875 -0.5 -0.46875
|
||||
v -0.46875 -0.375 -0.46875
|
||||
v -0.46875 -0.375 -0.1875
|
||||
v -0.46875 -0.5 -0.46875
|
||||
v -0.46875 -0.5 -0.1875
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.625 1
|
||||
vt 0.625 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.1875 0.9375
|
||||
vt 0.75 0.9375
|
||||
vt 0.75 0.375
|
||||
vt 0.1875 0.375
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 28/76/19 31/75/19 29/74/19 26/73/19
|
||||
f 27/80/20 28/79/20 26/78/20 25/77/20
|
||||
f 32/84/21 27/83/21 25/82/21 30/81/21
|
||||
f 31/88/22 32/87/22 30/86/22 29/85/22
|
||||
f 30/92/23 25/91/23 26/90/23 29/89/23
|
||||
f 31/96/24 28/95/24 27/94/24 32/93/24
|
||||
v 0.40625 0 -0.25
|
||||
v 0.40625 0 -0.40625
|
||||
v 0.40625 -0.25 -0.25
|
||||
v 0.40625 -0.25 -0.40625
|
||||
v 0.25 0 -0.40625
|
||||
v 0.25 0 -0.25
|
||||
v 0.25 -0.25 -0.40625
|
||||
v 0.25 -0.25 -0.25
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 36/100/25 39/99/25 37/98/25 34/97/25
|
||||
f 35/104/26 36/103/26 34/102/26 33/101/26
|
||||
f 40/108/27 35/107/27 33/106/27 38/105/27
|
||||
f 39/112/28 40/111/28 38/110/28 37/109/28
|
||||
f 38/116/29 33/115/29 34/114/29 37/113/29
|
||||
f 39/120/30 36/119/30 35/118/30 40/117/30
|
||||
v 0.42500000000000004 0.0625 -0.23125
|
||||
v 0.42500000000000004 0.0625 -0.425
|
||||
v 0.42500000000000004 0 -0.23125
|
||||
v 0.42500000000000004 0 -0.425
|
||||
v 0.23124999999999996 0.0625 -0.425
|
||||
v 0.23124999999999996 0.0625 -0.23125
|
||||
v 0.23124999999999996 0 -0.425
|
||||
v 0.23124999999999996 0 -0.23125
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 44/124/31 47/123/31 45/122/31 42/121/31
|
||||
f 43/128/32 44/127/32 42/126/32 41/125/32
|
||||
f 48/132/33 43/131/33 41/130/33 46/129/33
|
||||
f 47/136/34 48/135/34 46/134/34 45/133/34
|
||||
f 46/140/35 41/139/35 42/138/35 45/137/35
|
||||
f 47/144/36 44/143/36 43/142/36 48/141/36
|
||||
v 0.4375 -0.25 -0.21875
|
||||
v 0.4375 -0.25 -0.4375
|
||||
v 0.4375 -0.375 -0.21875
|
||||
v 0.4375 -0.375 -0.4375
|
||||
v 0.21875 -0.25 -0.4375
|
||||
v 0.21875 -0.25 -0.21875
|
||||
v 0.21875 -0.375 -0.4375
|
||||
v 0.21875 -0.375 -0.21875
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 52/148/37 55/147/37 53/146/37 50/145/37
|
||||
f 51/152/38 52/151/38 50/150/38 49/149/38
|
||||
f 56/156/39 51/155/39 49/154/39 54/153/39
|
||||
f 55/160/40 56/159/40 54/158/40 53/157/40
|
||||
f 54/164/41 49/163/41 50/162/41 53/161/41
|
||||
f 55/168/42 52/167/42 51/166/42 56/165/42
|
||||
v 0.46875 -0.375 -0.1875
|
||||
v 0.46875 -0.375 -0.46875
|
||||
v 0.46875 -0.5 -0.1875
|
||||
v 0.46875 -0.5 -0.46875
|
||||
v 0.1875 -0.375 -0.46875
|
||||
v 0.1875 -0.375 -0.1875
|
||||
v 0.1875 -0.5 -0.46875
|
||||
v 0.1875 -0.5 -0.1875
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.625 1
|
||||
vt 0.625 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.1875 0.9375
|
||||
vt 0.75 0.9375
|
||||
vt 0.75 0.375
|
||||
vt 0.1875 0.375
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 60/172/43 63/171/43 61/170/43 58/169/43
|
||||
f 59/176/44 60/175/44 58/174/44 57/173/44
|
||||
f 64/180/45 59/179/45 57/178/45 62/177/45
|
||||
f 63/184/46 64/183/46 62/182/46 61/181/46
|
||||
f 62/188/47 57/187/47 58/186/47 61/185/47
|
||||
f 63/192/48 60/191/48 59/190/48 64/189/48
|
||||
v 0.40625 0 0.40625
|
||||
v 0.40625 0 0.25
|
||||
v 0.40625 -0.25 0.40625
|
||||
v 0.40625 -0.25 0.25
|
||||
v 0.25 0 0.25
|
||||
v 0.25 0 0.40625
|
||||
v 0.25 -0.25 0.25
|
||||
v 0.25 -0.25 0.40625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 68/196/49 71/195/49 69/194/49 66/193/49
|
||||
f 67/200/50 68/199/50 66/198/50 65/197/50
|
||||
f 72/204/51 67/203/51 65/202/51 70/201/51
|
||||
f 71/208/52 72/207/52 70/206/52 69/205/52
|
||||
f 70/212/53 65/211/53 66/210/53 69/209/53
|
||||
f 71/216/54 68/215/54 67/214/54 72/213/54
|
||||
v 0.42500000000000004 0.0625 0.42500000000000004
|
||||
v 0.42500000000000004 0.0625 0.23124999999999996
|
||||
v 0.42500000000000004 0 0.42500000000000004
|
||||
v 0.42500000000000004 0 0.23124999999999996
|
||||
v 0.23124999999999996 0.0625 0.23124999999999996
|
||||
v 0.23124999999999996 0.0625 0.42500000000000004
|
||||
v 0.23124999999999996 0 0.23124999999999996
|
||||
v 0.23124999999999996 0 0.42500000000000004
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 76/220/55 79/219/55 77/218/55 74/217/55
|
||||
f 75/224/56 76/223/56 74/222/56 73/221/56
|
||||
f 80/228/57 75/227/57 73/226/57 78/225/57
|
||||
f 79/232/58 80/231/58 78/230/58 77/229/58
|
||||
f 78/236/59 73/235/59 74/234/59 77/233/59
|
||||
f 79/240/60 76/239/60 75/238/60 80/237/60
|
||||
v 0.4375 -0.25 0.4375
|
||||
v 0.4375 -0.25 0.21875
|
||||
v 0.4375 -0.375 0.4375
|
||||
v 0.4375 -0.375 0.21875
|
||||
v 0.21875 -0.25 0.21875
|
||||
v 0.21875 -0.25 0.4375
|
||||
v 0.21875 -0.375 0.21875
|
||||
v 0.21875 -0.375 0.4375
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 84/244/61 87/243/61 85/242/61 82/241/61
|
||||
f 83/248/62 84/247/62 82/246/62 81/245/62
|
||||
f 88/252/63 83/251/63 81/250/63 86/249/63
|
||||
f 87/256/64 88/255/64 86/254/64 85/253/64
|
||||
f 86/260/65 81/259/65 82/258/65 85/257/65
|
||||
f 87/264/66 84/263/66 83/262/66 88/261/66
|
||||
v 0.46875 -0.375 0.46875
|
||||
v 0.46875 -0.375 0.1875
|
||||
v 0.46875 -0.5 0.46875
|
||||
v 0.46875 -0.5 0.1875
|
||||
v 0.1875 -0.375 0.1875
|
||||
v 0.1875 -0.375 0.46875
|
||||
v 0.1875 -0.5 0.1875
|
||||
v 0.1875 -0.5 0.46875
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.625 1
|
||||
vt 0.625 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.1875 0.9375
|
||||
vt 0.75 0.9375
|
||||
vt 0.75 0.375
|
||||
vt 0.1875 0.375
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 92/268/67 95/267/67 93/266/67 90/265/67
|
||||
f 91/272/68 92/271/68 90/270/68 89/269/68
|
||||
f 96/276/69 91/275/69 89/274/69 94/273/69
|
||||
f 95/280/70 96/279/70 94/278/70 93/277/70
|
||||
f 94/284/71 89/283/71 90/282/71 93/281/71
|
||||
f 95/288/72 92/287/72 91/286/72 96/285/72
|
||||
v -0.25 0 0.40625
|
||||
v -0.25 0 0.25
|
||||
v -0.25 -0.25 0.40625
|
||||
v -0.25 -0.25 0.25
|
||||
v -0.40625 0 0.25
|
||||
v -0.40625 0 0.40625
|
||||
v -0.40625 -0.25 0.25
|
||||
v -0.40625 -0.25 0.40625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0 1
|
||||
vt 0.375 1
|
||||
vt 0.375 0.625
|
||||
vt 0 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vt 0.5625 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.625
|
||||
vt 0.5625 0.625
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 100/292/73 103/291/73 101/290/73 98/289/73
|
||||
f 99/296/74 100/295/74 98/294/74 97/293/74
|
||||
f 104/300/75 99/299/75 97/298/75 102/297/75
|
||||
f 103/304/76 104/303/76 102/302/76 101/301/76
|
||||
f 102/308/77 97/307/77 98/306/77 101/305/77
|
||||
f 103/312/78 100/311/78 99/310/78 104/309/78
|
||||
v -0.23125 0.0625 0.42500000000000004
|
||||
v -0.23125 0.0625 0.23124999999999996
|
||||
v -0.23125 0 0.42500000000000004
|
||||
v -0.23125 0 0.23124999999999996
|
||||
v -0.425 0.0625 0.23124999999999996
|
||||
v -0.425 0.0625 0.42500000000000004
|
||||
v -0.425 0 0.23124999999999996
|
||||
v -0.425 0 0.42500000000000004
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.4375 0.375
|
||||
vt 0.9375 0.375
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vt 0.75 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.3125
|
||||
vt 0.75 0.3125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 108/316/79 111/315/79 109/314/79 106/313/79
|
||||
f 107/320/80 108/319/80 106/318/80 105/317/80
|
||||
f 112/324/81 107/323/81 105/322/81 110/321/81
|
||||
f 111/328/82 112/327/82 110/326/82 109/325/82
|
||||
f 110/332/83 105/331/83 106/330/83 109/329/83
|
||||
f 111/336/84 108/335/84 107/334/84 112/333/84
|
||||
v -0.21875 -0.25 0.4375
|
||||
v -0.21875 -0.25 0.21875
|
||||
v -0.21875 -0.375 0.4375
|
||||
v -0.21875 -0.375 0.21875
|
||||
v -0.4375 -0.25 0.21875
|
||||
v -0.4375 -0.25 0.4375
|
||||
v -0.4375 -0.375 0.21875
|
||||
v -0.4375 -0.375 0.4375
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 0.5
|
||||
vt 0.9375 0.5
|
||||
vt 0.9375 0.25
|
||||
vt 0.4375 0.25
|
||||
vt 0 1
|
||||
vt 0.5 1
|
||||
vt 0.5 0.75
|
||||
vt 0 0.75
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 116/340/85 119/339/85 117/338/85 114/337/85
|
||||
f 115/344/86 116/343/86 114/342/86 113/341/86
|
||||
f 120/348/87 115/347/87 113/346/87 118/345/87
|
||||
f 119/352/88 120/351/88 118/350/88 117/349/88
|
||||
f 118/356/89 113/355/89 114/354/89 117/353/89
|
||||
f 119/360/90 116/359/90 115/358/90 120/357/90
|
||||
|
||||
v -0.1875 -0.375 0.46875
|
||||
v -0.1875 -0.375 0.1875
|
||||
v -0.1875 -0.5 0.46875
|
||||
v -0.1875 -0.5 0.1875
|
||||
v -0.46875 -0.375 0.1875
|
||||
v -0.46875 -0.375 0.46875
|
||||
v -0.46875 -0.5 0.1875
|
||||
v -0.46875 -0.5 0.46875
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.75
|
||||
vt 0.4375 0.75
|
||||
vt 0 0.5
|
||||
vt 0.5 0.5
|
||||
vt 0.5 0.25
|
||||
vt 0 0.25
|
||||
vt 0.4375 1
|
||||
vt 0.625 1
|
||||
vt 0.625 0.8125
|
||||
vt 0.4375 0.8125
|
||||
vt 0.1875 0.9375
|
||||
vt 0.75 0.9375
|
||||
vt 0.75 0.375
|
||||
vt 0.1875 0.375
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 124/364/91 127/363/91 125/362/91 122/361/91
|
||||
f 123/368/92 124/367/92 122/366/92 121/365/92
|
||||
f 128/372/93 123/371/93 121/370/93 126/369/93
|
||||
f 127/376/94 128/375/94 126/374/94 125/373/94
|
||||
f 126/380/95 121/379/95 122/378/95 125/377/95
|
||||
f 127/384/96 124/383/96 123/382/96 128/381/96
|
||||
v 0.34375 -0.28125 0.34375
|
||||
v 0.34375 -0.28125 -0.34375
|
||||
v 0.34375 -0.34375 0.34375
|
||||
v 0.34375 -0.34375 -0.34375
|
||||
v -0.34375 -0.28125 -0.34375
|
||||
v -0.34375 -0.28125 0.34375
|
||||
v -0.34375 -0.34375 -0.34375
|
||||
v -0.34375 -0.34375 0.34375
|
||||
vt 0.1875 0.4375
|
||||
vt 1 0.4375
|
||||
vt 1 0.375
|
||||
vt 0.1875 0.375
|
||||
vt 0.1875 0.4375
|
||||
vt 1 0.4375
|
||||
vt 1 0.375
|
||||
vt 0.1875 0.375
|
||||
vt 0.1875 0.4375
|
||||
vt 1 0.4375
|
||||
vt 1 0.375
|
||||
vt 0.1875 0.375
|
||||
vt 0.1875 0.4375
|
||||
vt 1 0.4375
|
||||
vt 1 0.375
|
||||
vt 0.1875 0.375
|
||||
vt 0.0625 1
|
||||
vt 0.875 1
|
||||
vt 0.875 0.1875
|
||||
vt 0.0625 0.1875
|
||||
vt 0.0625 1
|
||||
vt 0.875 1
|
||||
vt 0.875 0.1875
|
||||
vt 0.0625 0.1875
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 1
|
||||
f 132/388/97 135/387/97 133/386/97 130/385/97
|
||||
f 131/392/98 132/391/98 130/390/98 129/389/98
|
||||
f 136/396/99 131/395/99 129/394/99 134/393/99
|
||||
f 135/400/100 136/399/100 134/398/100 133/397/100
|
||||
f 134/404/101 129/403/101 130/402/101 133/401/101
|
||||
f 135/408/102 132/407/102 131/406/102 136/405/102
|
||||
v 0.4375 0.25 0.4375
|
||||
v 0.4375 0.25 -0.4375
|
||||
v 0.4375 0.0625 0.4375
|
||||
v 0.4375 0.0625 -0.4375
|
||||
v -0.4375 0.25 -0.4375
|
||||
v -0.4375 0.25 0.4375
|
||||
v -0.4375 0.0625 -0.4375
|
||||
v -0.4375 0.0625 0.4375
|
||||
vt 1 1
|
||||
vt 0 1
|
||||
vt 0 0.5
|
||||
vt 1 0.5
|
||||
vt 0 0.5
|
||||
vt 1 0.5
|
||||
vt 1 0
|
||||
vt 0 0
|
||||
vt 0 1
|
||||
vt 1 1
|
||||
vt 1 0.5
|
||||
vt 0 0.5
|
||||
vt 0 0.5
|
||||
vt 1 0.5
|
||||
vt 1 0
|
||||
vt 0 0
|
||||
vt 0 1
|
||||
vt 1 1
|
||||
vt 1 0
|
||||
vt 0 0
|
||||
vt 0 1
|
||||
vt 1 1
|
||||
vt 1 0
|
||||
vt 0 0
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 2
|
||||
f 140/412/103 143/411/103 141/410/103 138/409/103
|
||||
f 139/416/104 140/415/104 138/414/104 137/413/104
|
||||
f 144/420/105 139/419/105 137/418/105 142/417/105
|
||||
f 143/424/106 144/423/106 142/422/106 141/421/106
|
||||
f 142/428/107 137/427/107 138/426/107 141/425/107
|
||||
f 143/432/108 140/431/108 139/430/108 144/429/108
|
||||
v 0.5 0.5 0.5
|
||||
v 0.5 0.5 -0.5
|
||||
v 0.5 0.3125 0.5
|
||||
v 0.5 0.3125 -0.5
|
||||
v -0.5 0.5 -0.5
|
||||
v -0.5 0.5 0.5
|
||||
v -0.5 0.3125 -0.5
|
||||
v -0.5 0.3125 0.5
|
||||
vt 0 0.3125
|
||||
vt 1 0.3125
|
||||
vt 1 0.125
|
||||
vt 0 0.125
|
||||
vt 0 0.3125
|
||||
vt 1 0.3125
|
||||
vt 1 0.125
|
||||
vt 0 0.125
|
||||
vt 0 0.3125
|
||||
vt 1 0.3125
|
||||
vt 1 0.125
|
||||
vt 0 0.125
|
||||
vt 0 0.3125
|
||||
vt 1 0.3125
|
||||
vt 1 0.125
|
||||
vt 0 0.125
|
||||
vt 0 1
|
||||
vt 1 1
|
||||
vt 1 0
|
||||
vt 0 0
|
||||
vt 0 1
|
||||
vt 1 1
|
||||
vt 1 0
|
||||
vt 0 0
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 1
|
||||
f 148/436/109 151/435/109 149/434/109 146/433/109
|
||||
f 147/440/110 148/439/110 146/438/110 145/437/110
|
||||
f 152/444/111 147/443/111 145/442/111 150/441/111
|
||||
f 151/448/112 152/447/112 150/446/112 149/445/112
|
||||
f 150/452/113 145/451/113 146/450/113 149/449/113
|
||||
f 151/456/114 148/455/114 147/454/114 152/453/114
|
||||
v 0.46875 0.3125 0.46875
|
||||
v 0.46875 0.3125 -0.46875
|
||||
v 0.46875 0.25 0.46875
|
||||
v 0.46875 0.25 -0.46875
|
||||
v -0.46875 0.3125 -0.46875
|
||||
v -0.46875 0.3125 0.46875
|
||||
v -0.46875 0.25 -0.46875
|
||||
v -0.46875 0.25 0.46875
|
||||
vt 0 0.8125
|
||||
vt 1 0.8125
|
||||
vt 1 0.75
|
||||
vt 0 0.75
|
||||
vt 0 0.8125
|
||||
vt 1 0.8125
|
||||
vt 1 0.75
|
||||
vt 0 0.75
|
||||
vt 0 0.8125
|
||||
vt 1 0.8125
|
||||
vt 1 0.75
|
||||
vt 0 0.75
|
||||
vt 0 0.8125
|
||||
vt 1 0.8125
|
||||
vt 1 0.75
|
||||
vt 0 0.75
|
||||
vt 0 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.0625
|
||||
vt 0 0.0625
|
||||
vt 0 1
|
||||
vt 0.9375 1
|
||||
vt 0.9375 0.0625
|
||||
vt 0 0.0625
|
||||
vn 0 0 -1
|
||||
vn 1 0 0
|
||||
vn 0 0 1
|
||||
vn -1 0 0
|
||||
vn 0 1 0
|
||||
vn 0 -1 0
|
||||
g 1
|
||||
f 156/460/115 159/459/115 157/458/115 154/457/115
|
||||
f 155/464/116 156/463/116 154/462/116 153/461/116
|
||||
f 160/468/117 155/467/117 153/466/117 158/465/117
|
||||
f 159/472/118 160/471/118 158/470/118 157/469/118
|
||||
f 158/476/119 153/475/119 154/474/119 157/473/119
|
||||
f 159/480/120 156/479/120 155/478/120 160/477/120
|
76
mods/researcher/settingtypes.txt
Normal file
|
@ -0,0 +1,76 @@
|
|||
[Research Points and Levels]
|
||||
|
||||
# How many points a player needs per item to advance to the next research level for a given item. More points required will require more research and vice-versa.
|
||||
researcher.points_per_level (Points per research level) int 600 100 10000
|
||||
|
||||
# How many research points a successful research gives per item.
|
||||
researcher.points_per_research (Points per successful research) int 100 1 10000
|
||||
|
||||
# The maximum research level that can be reached for an item. Duplication is unlocked for any item at max research level.
|
||||
researcher.level_max (Max research level) int 10 1 10000
|
||||
|
||||
# The scale at which the points to the next research level increase. This is an exponential multiplier for each research level.
|
||||
researcher.level_scale (Research level scale) float 1.25 1.0 2.0
|
||||
|
||||
[Group Research]
|
||||
|
||||
# The number of bonus research points granted for each research level of items that match the groups of a researched item.
|
||||
researcher.group_research_bonus (Research group bonus) int 1 0 10000
|
||||
|
||||
# The maximum level group bonus that can be given.
|
||||
researcher.group_research_bonus_max (Research group bonus max) int 100 0 10000
|
||||
|
||||
[Focused Research]
|
||||
|
||||
# How many bonus research points granted per combo that matches player focus item exactly. This value is cumulative for each successive research. This rewards players who continuously research the same item.
|
||||
researcher.focused_research_bonus_exact (Focused research bonus, exact) int 5 0 10000
|
||||
|
||||
# How many bonus research points granted per combo that matches player focus item group. This value is cumulative for each successive research. This rewards players who continuously research items similar to their focus.
|
||||
researcher.focused_research_bonus_group (Focused research bonus, grouped) int 1 0 10000
|
||||
|
||||
# The maximum bonus that focused research can contribute.
|
||||
researcher.focused_research_bonus_max (Focused research max bonus) int 150 0 10000
|
||||
|
||||
[Research Table]
|
||||
|
||||
# How many bonus research points granted for research at a research table that matches its item exactly?
|
||||
researcher.research_table_bonus_exact (Research table bonus, exact) int 25 0 10000
|
||||
|
||||
# How many bonus research points granted for research at a research table that matches one of its item groups?
|
||||
researcher.research_table_bonus_group (Research table bonus, group) int 5 0 10000
|
||||
|
||||
# How many bonus research points are given for each matching item near a research table.
|
||||
researcher.research_table_adjacency_bonus (Research table adjacency bonus, per item) int 10 0 100000
|
||||
|
||||
# The maximum number of items that can contribute to research table adjacency bonus.
|
||||
researcher.research_table_adjacency_max (Max research table adjacency) int 10 0 50
|
||||
|
||||
# The node radius in which research tables will search for matching adjacency.
|
||||
researcher.research_table_adjacency_radius (Research table adjacency radius) int 3 1 10
|
||||
|
||||
# The node radius in which players can gain benefits from a nearby research table.
|
||||
researcher.research_table_player_radius (Research table player radius) int 2 1 3
|
||||
|
||||
# The maximum bonus that research tables can contribute.
|
||||
researcher.research_table_bonus_max (Max research table bonus) int 150 0 10000
|
||||
|
||||
[Item Research Cost Discounts]
|
||||
|
||||
# The research point discount for items with a lower max stack value.
|
||||
researcher.discount_stack_max (Discount for items with low max stacks) int -250 -10000 0
|
||||
|
||||
# The research point discount for items not found commonly in the world.
|
||||
researcher.discount_mapgen (Discount for items not in mapgen) int -400 -10000 0
|
||||
|
||||
# The research point discount for items that cannot be crafted
|
||||
researcher.discount_not_craftable (Discount for non-craftable items) int -250 -10000 0
|
||||
|
||||
[Awards]
|
||||
|
||||
# If this setting is enabled and the Awards mod is enabled, then Researcher awards will be registered.
|
||||
researcher.awards (Enable awards?) bool true
|
||||
|
||||
[Advanced]
|
||||
|
||||
# Which groups to exclude from Researcher. This list should include system groups and other "non-useful" groups that don't make sense to players.
|
||||
researcher.excluded_groups (Item groups to exclude) string not_in_creative_inventory attached_node connect_to_raillike dig_immediate disable_jump disable_descend fall_damage_add_percent falling_node float level oddly_breakable_by_hand immortal disable_repair creative_breakable opaque solid
|
BIN
mods/researcher/sounds/researcher_duplicate.ogg
Normal file
BIN
mods/researcher/sounds/researcher_level_up.ogg
Normal file
BIN
mods/researcher/sounds/researcher_research.ogg
Normal file
456
mods/researcher/src/api.lua
Normal file
|
@ -0,0 +1,456 @@
|
|||
-- ----------------- --
|
||||
-- GROUP FUNCTIONS --
|
||||
-- ----------------- --
|
||||
|
||||
-- Add an item to a group
|
||||
function researcher.add_item_to_group(item,group)
|
||||
researcher.groups[group] = researcher.groups[group] or {}
|
||||
researcher.groups[group][item] = true
|
||||
return true
|
||||
end
|
||||
|
||||
-- Iterate over items in a group
|
||||
function researcher.for_item_in_group(group,fn)
|
||||
for item,_ in pairs(researcher.groups[group]) do
|
||||
if fn(item) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Iterate over groups of an item
|
||||
function researcher.for_group_in_item(item,fn)
|
||||
item = researcher.registered_items[item]
|
||||
if not item then
|
||||
return false
|
||||
end
|
||||
|
||||
for group,_ in pairs(item.groups) do
|
||||
if fn(group) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Determine if item is in a specific group
|
||||
function researcher.is_item_in_group(item,group)
|
||||
return researcher.groups[group] and researcher.groups[group][item] and true or false
|
||||
end
|
||||
|
||||
-- Determine if two items have any groups in common
|
||||
function researcher.do_items_share_groups(item1,item2)
|
||||
item1 = researcher.registered_items[item1]
|
||||
item2 = researcher.registered_items[item2]
|
||||
if not item1 or not item2 then
|
||||
return false
|
||||
end
|
||||
|
||||
for group,_ in pairs(item1.groups) do
|
||||
if item2.groups[group] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- ---------------- --
|
||||
-- ITEM FUNCTIONS --
|
||||
-- ---------------- --
|
||||
|
||||
-- Register an item with Researcher
|
||||
function researcher.register_item(def)
|
||||
-- Do not register duplicate items
|
||||
if researcher.registered_items[def.name] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Create internal item definition
|
||||
local item = {
|
||||
name = def.name,
|
||||
groups = {},
|
||||
}
|
||||
|
||||
-- Determine base research points per level for this item
|
||||
item.points_per_level = def.points_per_level
|
||||
|
||||
-- Determine research point adjustments that apply to this item
|
||||
item.adjustments = {}
|
||||
for _,adjustment in ipairs(researcher.registered_adjustments) do
|
||||
local amount = adjustment.calculate(item.name)
|
||||
if amount ~= 0 then
|
||||
item.points_per_level = math.max(researcher.settings.points_per_research,item.points_per_level + amount)
|
||||
table.insert(item.adjustments,{
|
||||
name = adjustment.name,
|
||||
amount = amount,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Determine groups for this item
|
||||
local groups = def.groups or {}
|
||||
for _,group in ipairs(groups) do
|
||||
item.groups[group] = true
|
||||
researcher.add_item_to_group(def.name,group)
|
||||
end
|
||||
|
||||
-- Register the item
|
||||
researcher.registered_items[def.name] = item
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- ---------------------- --
|
||||
-- ADJUSTMENT FUNCTIONS --
|
||||
-- ---------------------- --
|
||||
|
||||
-- Register a adjustment function that will return a adjustment amount
|
||||
function researcher.register_adjustment(def)
|
||||
-- Do not register duplicate adjustments
|
||||
if researcher.registered_adjustments[def.name] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Register the new adjustment
|
||||
local adjustment = {
|
||||
name = def.name,
|
||||
reason = def.reason,
|
||||
calculate = def.calculate,
|
||||
}
|
||||
|
||||
researcher.registered_adjustments[def.name] = adjustment
|
||||
table.insert(researcher.registered_adjustments,adjustment)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function researcher.get_adjustments_for_item(item)
|
||||
item = researcher.registered_items[item]
|
||||
if not item then
|
||||
return {}
|
||||
end
|
||||
|
||||
return item.adjustments
|
||||
end
|
||||
|
||||
-- ----------------- --
|
||||
-- BONUS FUNCTIONS --
|
||||
-- ----------------- --
|
||||
|
||||
-- Register a bonus
|
||||
function researcher.register_bonus(def)
|
||||
-- Do not register duplicate bonuses
|
||||
if researcher.registered_bonuses[def.name] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Register the new bonus
|
||||
local bonus = {
|
||||
name = def.name,
|
||||
reason = def.reason,
|
||||
calculate = def.calculate,
|
||||
initialize_player_data = def.initialize_player_data or function() end,
|
||||
}
|
||||
|
||||
researcher.registered_bonuses[def.name] = bonus
|
||||
table.insert(researcher.registered_bonuses,bonus)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- ---------------- --
|
||||
-- DATA FUNCTIONS --
|
||||
-- ---------------- --
|
||||
|
||||
-- Initialize player data
|
||||
function researcher.initialize_player_data(player_name)
|
||||
-- Create new player data
|
||||
local player_data = {
|
||||
-- The player's name
|
||||
name = player_name,
|
||||
|
||||
-- All of the player's research progress indexed by item name
|
||||
research = {},
|
||||
|
||||
-- The current subject of the player's research
|
||||
subject = {
|
||||
image = nil,
|
||||
description = "",
|
||||
groups = "(Research an item on the left to see info)",
|
||||
research = nil,
|
||||
},
|
||||
}
|
||||
|
||||
-- Initialize bonus-specific data
|
||||
for _,bonus in ipairs(researcher.registered_bonuses) do
|
||||
bonus.initialize_player_data(player_data)
|
||||
end
|
||||
|
||||
-- Save initialized player data to mod storage
|
||||
researcher.save_player_data(player_name)
|
||||
|
||||
-- Return initialized data
|
||||
return player_data
|
||||
end
|
||||
|
||||
-- Get player data
|
||||
function researcher.get_player_data(player_name)
|
||||
-- Attempt to load player data from cache
|
||||
local pstring = "player_" .. player_name
|
||||
local player_data = researcher.data[pstring]
|
||||
if not player_data then
|
||||
-- Attempt to load player data from mod storage
|
||||
player_data = researcher.storage:get(pstring)
|
||||
|
||||
-- Initialize new player data if not found or parse string if found
|
||||
if not player_data then
|
||||
player_data = researcher.initialize_player_data(player_name)
|
||||
else
|
||||
player_data = minetest.deserialize(player_data)
|
||||
end
|
||||
|
||||
-- Link research subject to actual research
|
||||
if player_data.subject.research then
|
||||
player_data.subject.research = player_data.research[player_data.subject.item.name]
|
||||
end
|
||||
|
||||
-- Cache player data
|
||||
researcher.data[pstring] = player_data
|
||||
end
|
||||
|
||||
-- Return player data
|
||||
return player_data
|
||||
end
|
||||
|
||||
-- Determine the number of points to the next level
|
||||
-- FIXME should be (item,level)?
|
||||
function researcher.get_points_to_next_level(player,item)
|
||||
item = researcher.registered_items[item]
|
||||
if item then
|
||||
local research = (type(player) == "string" and researcher.get_player_data(player) or player).research[item.name]
|
||||
if research then
|
||||
local level = research.level
|
||||
if research.level <= researcher.settings.level_max then
|
||||
return math.round(item.points_per_level * math.pow(research.level,researcher.settings.level_scale) / 100) * 100
|
||||
end
|
||||
end
|
||||
else
|
||||
return 0
|
||||
end
|
||||
return item.points_per_level
|
||||
end
|
||||
|
||||
-- Save player data; can be called many times during the current tick but will
|
||||
-- only execute once on the next tick
|
||||
function researcher.save_player_data(player_name)
|
||||
if not researcher.data.save[player_name] then
|
||||
researcher.data.save[player_name] = true
|
||||
minetest.after(0,function()
|
||||
researcher.storage:set_string("player_" .. player_name,minetest.serialize(researcher.get_player_data(player_name)))
|
||||
researcher.data.save[player_name] = nil
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- -------------------- --
|
||||
-- RESEARCH FUNCTIONS --
|
||||
-- -------------------- --
|
||||
|
||||
-- Research an item
|
||||
function researcher.research_item(player,item)
|
||||
-- Initialize result
|
||||
local result = {
|
||||
item = item,
|
||||
base = researcher.settings.points_per_research,
|
||||
bonuses = {},
|
||||
total = researcher.settings.points_per_research,
|
||||
success = true,
|
||||
}
|
||||
|
||||
-- Get player data
|
||||
local player_data = type(player) == "string" and researcher.get_player_data(player) or player
|
||||
|
||||
-- Cannot research beyond max research level
|
||||
if player_data.research[item] and player_data.research[item].level > researcher.settings.level_max then
|
||||
result = {
|
||||
item = item,
|
||||
base = 0,
|
||||
bonuses = {},
|
||||
total = 0,
|
||||
success = false,
|
||||
}
|
||||
return result
|
||||
end
|
||||
|
||||
-- Calculate bonuses
|
||||
for _,bonus in ipairs(researcher.registered_bonuses) do
|
||||
local amount = bonus.calculate(item,player_data)
|
||||
if amount ~= 0 then
|
||||
result.total = result.total + amount
|
||||
table.insert(result.bonuses,{
|
||||
name = bonus.name,
|
||||
reason = bonus.reason,
|
||||
points = amount,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Get research entry
|
||||
local research = player_data.research[item] or {
|
||||
level = 1,
|
||||
points = 0,
|
||||
}
|
||||
player_data.research[item] = research
|
||||
player_data.subject.research = research
|
||||
|
||||
-- Look up item
|
||||
item = researcher.registered_items[item]
|
||||
|
||||
if not item then
|
||||
return {
|
||||
item = "???",
|
||||
base = 0,
|
||||
bonuses = {},
|
||||
total = 0,
|
||||
success = false,
|
||||
}
|
||||
end
|
||||
|
||||
-- Level up research
|
||||
local points_tally = research.points + result.total
|
||||
local points_level = researcher.get_points_to_next_level(player_data,item.name)
|
||||
while points_tally >= points_level do
|
||||
research.level = math.min(researcher.settings.level_max + 1,research.level + 1)
|
||||
points_tally = points_tally - points_level
|
||||
points_level = researcher.get_points_to_next_level(player_data,item.name)
|
||||
end
|
||||
research.points = points_tally
|
||||
|
||||
-- Unlock award for basic research
|
||||
if researcher.settings.awards then
|
||||
awards.unlock(player.name,"researcher:apprentice")
|
||||
end
|
||||
|
||||
-- Set points to 0 at max level and unlock Prodigious award
|
||||
if research.level > researcher.settings.level_max then
|
||||
research.points = 0
|
||||
if researcher.settings.awards then
|
||||
awards.unlock(player.name,"researcher:prodigious")
|
||||
end
|
||||
end
|
||||
|
||||
-- Save data
|
||||
researcher.save_player_data(player_data.name)
|
||||
|
||||
-- Return final research result
|
||||
return result
|
||||
end
|
||||
|
||||
-- Research an ItemStack
|
||||
function researcher.research_itemstack(player,itemstack)
|
||||
if itemstack:is_empty() then
|
||||
return {
|
||||
item = itemstack:get_name(),
|
||||
success = false,
|
||||
remainder = ItemStack(itemstack:get_name() .. " 0"),
|
||||
}
|
||||
end
|
||||
|
||||
-- Get player data for entire stack
|
||||
local player_data = type(player) == "string" and researcher.get_player_data(player) or player
|
||||
|
||||
-- Research each item in the stack individually
|
||||
local name = itemstack:get_name()
|
||||
local results = {
|
||||
name = name,
|
||||
success = false,
|
||||
remainder = ItemStack(name .. " 0"),
|
||||
}
|
||||
|
||||
for i = 1, itemstack:get_count() do
|
||||
local result = researcher.research_item(player_data,name)
|
||||
results.success = results.success or result.success
|
||||
if result.success then
|
||||
table.insert(results,result)
|
||||
elseif player_data.research[name].level > researcher.settings.level_max then
|
||||
itemstack:set_count(itemstack:get_count() - i + 1)
|
||||
results.remainder = itemstack
|
||||
return results
|
||||
else
|
||||
return results
|
||||
end
|
||||
end
|
||||
|
||||
-- Return all results
|
||||
return results
|
||||
end
|
||||
|
||||
-- Research an entire inventory list
|
||||
function researcher.research_inventory(player,inventory,list)
|
||||
-- Get player data
|
||||
local player_data = type(player) == "string" and researcher.get_player_data(player) or player
|
||||
|
||||
-- Research each ItemStack in the inventory
|
||||
local results = {
|
||||
success = false,
|
||||
}
|
||||
|
||||
for i = 1, inventory:get_size(list) do
|
||||
local itemstack = inventory:get_stack(list,i)
|
||||
local result = researcher.research_itemstack(player_data,itemstack)
|
||||
results.success = results.success or result.success
|
||||
if result.success then
|
||||
inventory:set_stack(list,i,result.remainder)
|
||||
end
|
||||
table.insert(results,result)
|
||||
end
|
||||
|
||||
-- Return full inventory results
|
||||
return results
|
||||
end
|
||||
|
||||
-- ----------------------- --
|
||||
-- DUPLICATION FUNCTIONS --
|
||||
-- ----------------------- --
|
||||
|
||||
-- Duplicate the item in the player's research inventory
|
||||
function researcher.duplicate_research(player)
|
||||
local inventory = player:get_inventory()
|
||||
local itemstack = inventory:get_stack("research",1)
|
||||
if itemstack and not itemstack:is_empty() then
|
||||
local item = itemstack:get_name()
|
||||
local idef = minetest.registered_items[item]
|
||||
if idef then
|
||||
inventory:add_item("main",ItemStack(itemstack:get_name() .. " " .. itemstack:get_stack_max()))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- --------------- --
|
||||
-- GUI FUNCTIONS --
|
||||
-- --------------- --
|
||||
|
||||
-- Return a formspec that implements research
|
||||
function researcher.get_formspec_data(player_name)
|
||||
local player_data = researcher.get_player_data(player_name)
|
||||
local subject = player_data.subject
|
||||
local data = {
|
||||
subject = subject,
|
||||
current_points = subject.research and subject.research.points or 0,
|
||||
points_to_next_level = subject.research and researcher.get_points_to_next_level(player_name,subject.item.name) or 0,
|
||||
is_max_level = subject.research and (subject.research.level > researcher.settings.level_max) and true or false,
|
||||
is_inventory_empty = minetest.get_inventory({ type = "player", name = player_name, }):get_stack("research",1):is_empty(),
|
||||
last_result = player_data.last_result,
|
||||
}
|
||||
player_data.last_result = nil -- last result is only available once immediately after research
|
||||
return data
|
||||
end
|
||||
|
||||
-- -------------------- --
|
||||
-- CALLBACK FUNCTIONS --
|
||||
-- -------------------- --
|
||||
|
||||
function researcher.register_on_research(fn)
|
||||
table.insert(researcher.registered_on_research,fn)
|
||||
end
|
43
mods/researcher/src/awards.lua
Normal file
|
@ -0,0 +1,43 @@
|
|||
if researcher.settings.awards then
|
||||
-- Use the available registration function, if any
|
||||
local register_award = (function()
|
||||
if awards.register_award then
|
||||
return function(...)
|
||||
awards.register_award(...)
|
||||
end
|
||||
elseif awards.register_achievement then
|
||||
return function(...)
|
||||
awards.register_achievement(...)
|
||||
end
|
||||
else
|
||||
minetest.log("warn","Researcher does not support the loaded awards mod.")
|
||||
return function()
|
||||
-- unsupported awards mod, noop
|
||||
end
|
||||
end
|
||||
end)()
|
||||
|
||||
-- Apprentice: awarded for a player's first successful research
|
||||
register_award("researcher:apprentice",{
|
||||
title = "Apprentice",
|
||||
description = "Research any item",
|
||||
difficulty = 5,
|
||||
icon = "researcher_icon_bronze.png",
|
||||
})
|
||||
|
||||
-- Eureka!: awarded for a player's first gained research level
|
||||
register_award("researcher:eureka",{
|
||||
title = "Eureka!",
|
||||
description = "Earn a research level for any item",
|
||||
difficulty = 50,
|
||||
icon = "researcher_icon_silver.png",
|
||||
})
|
||||
|
||||
-- Prodigious: awarded for a player's first max research level
|
||||
register_award("researcher:prodigious",{
|
||||
title = "Prodigious",
|
||||
description = "Earn max research level for any item",
|
||||
difficulty = 150,
|
||||
icon = "researcher_icon_gold.png",
|
||||
})
|
||||
end
|
183
mods/researcher/src/bonuses.lua
Normal file
|
@ -0,0 +1,183 @@
|
|||
-- Research level group bonus
|
||||
if researcher.settings.group_research_bonus > 0 and researcher.settings.group_research_bonus_max > 0 then
|
||||
researcher.register_bonus({
|
||||
name = "researcher:research_group_research_bonus",
|
||||
reason = "Group Research",
|
||||
calculate = function(item,player_data)
|
||||
local bonus = 0
|
||||
for subject,research in pairs(player_data.research) do
|
||||
if researcher.do_items_share_groups(item,subject) then
|
||||
bonus = bonus + (research.level - 1) * researcher.settings.group_research_bonus
|
||||
end
|
||||
end
|
||||
return bonus
|
||||
end,
|
||||
initialize_player_data = function()
|
||||
-- all bonus calculation comes from research; nothing to initialize
|
||||
end,
|
||||
})
|
||||
else
|
||||
researcher.register_bonus({
|
||||
name = "researcher:research_group_research_bonus",
|
||||
reason = "Group Research",
|
||||
calculate = function() return 0 end,
|
||||
initialize_player_data = function()
|
||||
-- bonus is calculated from research; nothing to initialize
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Focused research bonus
|
||||
if researcher.settings.focused_research_bonus_max > 0 and (researcher.settings.focused_research_bonus_exact > 0 or researcher.settings.focused_research_bonus_group > 0) then
|
||||
researcher.register_bonus({
|
||||
name = "researcher:focused_research_bonus",
|
||||
reason = "Focused Research",
|
||||
calculate = function(item,player_data)
|
||||
-- Calculate bonus value
|
||||
if item == player_data.focused_research.item then
|
||||
player_data.focused_research.bonus = player_data.focused_research.bonus + researcher.settings.focused_research_bonus_exact
|
||||
elseif researcher.do_items_share_groups(item,player_data.focused_research.item) then
|
||||
player_data.focused_research.bonus = player_data.focused_research.bonus + researcher.settings.focused_research_bonus_group
|
||||
else
|
||||
player_data.focused_research.bonus = 0
|
||||
end
|
||||
|
||||
-- Set focused item
|
||||
player_data.focused_research.item = item
|
||||
|
||||
-- Return capped bonus value
|
||||
player_data.focused_research.bonus = math.min(player_data.focused_research.bonus,researcher.settings.focused_research_bonus_max)
|
||||
return player_data.focused_research.bonus
|
||||
end,
|
||||
initialize_player_data = function(player_data)
|
||||
player_data.focused_research = {
|
||||
item = "",
|
||||
bonus = 0,
|
||||
}
|
||||
end,
|
||||
})
|
||||
else
|
||||
researcher.register_bonus({
|
||||
name = "researcher:focused_research_bonus",
|
||||
reason = "Focused Research",
|
||||
calculate = function() return 0 end,
|
||||
initialize_player_data = function(player_data)
|
||||
player_data.focused_research = {
|
||||
item = "",
|
||||
bonus = 0,
|
||||
}
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Research table bonus
|
||||
if researcher.settings.research_table_bonus_exact > 0 or researcher.settings.research_table_bonus_group > 0 or (researcher.settings.research_table_adjacency_bonus > 0 and researcher.settings.research_table_adjacency_max > 0) then
|
||||
researcher.register_bonus({
|
||||
name = "researcher:research_table_bonus",
|
||||
reason = "Research Table",
|
||||
calculate = function(item,player_data)
|
||||
-- Initialize bonus and max flag
|
||||
local bonus = 0
|
||||
local bonusmax = false
|
||||
|
||||
-- Track limits when tallying bonuses
|
||||
local nadj = 0
|
||||
local function rtbonus(bonus,increment,adjacency)
|
||||
local result = bonus
|
||||
if adjacency then
|
||||
local adj_bounded = math.min(adjacency,researcher.settings.research_table_adjacency_max - nadj)
|
||||
nadj = nadj + adj_bounded
|
||||
result = result + researcher.settings.research_table_adjacency_bonus * adj_bounded
|
||||
else
|
||||
result = bonus + increment
|
||||
end
|
||||
return math.min(result,researcher.settings.research_table_bonus_max), (result >= researcher.settings.research_table_bonus_max or nadj >= researcher.settings.research_table_adjacency_max)
|
||||
end
|
||||
|
||||
-- Scan radius around player for research tables
|
||||
local player = minetest.get_player_by_name(player_data.name)
|
||||
local research_table = nil
|
||||
if player then
|
||||
local pos = player:get_pos()
|
||||
local radius = researcher.settings.research_table_player_radius
|
||||
for _,rt in ipairs(minetest.find_nodes_in_area(pos:add(-radius),pos:add(radius),"researcher:research_table")) do
|
||||
-- Get research table's focus
|
||||
local meta = minetest.get_meta(rt)
|
||||
local inventory = meta:get_inventory()
|
||||
local itemstack = inventory:get_stack("focus",1)
|
||||
local name = itemstack:get_name()
|
||||
|
||||
-- If the focus item matches the item in question, then add to the
|
||||
-- calculated bonus accordingly
|
||||
if item == name then
|
||||
bonus, bonusmax = rtbonus(bonus,(research_table and (researcher.settings.research_table_bonus_exact - researcher.settings.research_table_bonus_group) or researcher.settings.research_table_bonus_exact))
|
||||
if bonusmax then
|
||||
return bonus
|
||||
end
|
||||
research_table = rt
|
||||
break -- cannot do better than an exact match
|
||||
elseif not research_table and researcher.do_items_share_groups(item,name) then
|
||||
bonus, bonusmax = rtbonus(bonus,researcher.settings.research_table_bonus_group)
|
||||
if bonusmax then
|
||||
return bonus
|
||||
end
|
||||
research_table = rt
|
||||
-- keep scanning for better matches
|
||||
end
|
||||
end
|
||||
|
||||
-- Calculate adjacency bonus for research table
|
||||
if research_table then
|
||||
local radius = researcher.settings.research_table_adjacency_radius
|
||||
local pos1 = research_table:add(-radius)
|
||||
local pos2 = research_table:add(radius)
|
||||
|
||||
-- Check nearby node groups
|
||||
bonus, bonusmax = rtbonus(bonus,researcher.settings.research_table_adjacency_bonus,#minetest.find_nodes_in_area(pos1,pos2,(function()
|
||||
local groups = {}
|
||||
for group,_ in pairs(researcher.registered_items[item].groups) do
|
||||
table.insert(groups,"group:" .. group)
|
||||
end
|
||||
return groups
|
||||
end)()))
|
||||
if bonusmax then
|
||||
return bonus
|
||||
end
|
||||
|
||||
-- Check nearby node inventories
|
||||
for _,node in ipairs(minetest.find_nodes_with_meta(pos1,pos2)) do
|
||||
if not node:equals(research_table) then
|
||||
local nodemeta = minetest.get_meta(node)
|
||||
local nodeinventory = nodemeta:get_inventory()
|
||||
if nodeinventory then
|
||||
for list,stacks in pairs(nodeinventory:get_lists() or {}) do
|
||||
for _,itemstack in ipairs(stacks or {}) do
|
||||
if not itemstack:is_empty() and researcher.do_items_share_groups(item,itemstack:get_name()) then
|
||||
bonus, bonusmax = rtbonus(bonus,researcher.settings.research_table_adjacency_bonus,itemstack:get_count())
|
||||
if bonusmax then
|
||||
return bonus
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
return 0 -- no bonus if no research table was found
|
||||
end
|
||||
else
|
||||
return 0 -- no bonus if player is mysteriously not found
|
||||
end
|
||||
|
||||
-- Return partial bonus total
|
||||
return bonus
|
||||
end,
|
||||
})
|
||||
else
|
||||
researcher.register_bonus({
|
||||
name = "researcher:research_table_bonus",
|
||||
reason = "Research Table",
|
||||
calculate = function() return 0 end,
|
||||
})
|
||||
end
|
51
mods/researcher/src/commands.lua
Normal file
|
@ -0,0 +1,51 @@
|
|||
local show_formspec = (function()
|
||||
if researcher.dependencies.sfinv and sfinv.enabled then
|
||||
return function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
sfinv.set_page(player,"researcher:player_research")
|
||||
return "Use the inventory (default key 'i') to perform research."
|
||||
end
|
||||
elseif researcher.dependencies.mcl_inventory then
|
||||
return function(name)
|
||||
return "Use the inventory (default key 'i') to perform research."
|
||||
end
|
||||
elseif researcher.dependencies.unified_inventory then
|
||||
return function(name)
|
||||
return "Use the inventory (default key 'i') to perform research."
|
||||
end
|
||||
elseif researcher.dependencies.i3 then
|
||||
return function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
i3.set_tab(player,"research")
|
||||
return "Use the inventory (default key 'i') to perform research."
|
||||
end
|
||||
else
|
||||
return function(name)
|
||||
minetest.show_formspec(name,"researcher:player_research",researcher.get_formspec(name))
|
||||
end
|
||||
end
|
||||
end)()
|
||||
|
||||
minetest.register_chatcommand("research",{
|
||||
params = "<action> <value>",
|
||||
description = "interact with researcher",
|
||||
func = function(name,params)
|
||||
if params == "reset" then
|
||||
researcher.data["player_" .. name] = researcher.initialize_player_data(name)
|
||||
return true, "Research has been fully reset."
|
||||
end
|
||||
|
||||
if params:find("^reset .+$") then
|
||||
local item = params:split(" ")[2]
|
||||
researcher.get_player_data(name).research[item] = nil
|
||||
researcher.save_player_data(name)
|
||||
return true, "Research has been reset for " .. item
|
||||
end
|
||||
|
||||
if not params or params == "" or params == "show" then
|
||||
return true, show_formspec(name)
|
||||
end
|
||||
|
||||
return false, "Unknown or incorrect researcher command"
|
||||
end
|
||||
})
|
484
mods/researcher/src/gui.lua
Normal file
|
@ -0,0 +1,484 @@
|
|||
-- ----------------- --
|
||||
-- LOCAL FUNCTIONS --
|
||||
-- ----------------- --
|
||||
|
||||
local function format_formspec(formspec,...)
|
||||
return string.format(string.gsub(formspec,"%-%-[^\n]*",""),...)
|
||||
end
|
||||
|
||||
local get_ui = (function()
|
||||
if researcher.dependencies.sfinv and sfinv.enabled and not researcher.dependencies.i3 then
|
||||
-- Get sfinv formspec
|
||||
return function(player_name)
|
||||
local data = researcher.get_formspec_data(player_name)
|
||||
|
||||
local status_string = "hypertext[0.3,2.3;4,4;;<global halign=center>(place item above to analyze)]"
|
||||
if data.last_result then
|
||||
status_string = "hypertext[0.3,2.3;4,4;;<global halign=center><i>Research successful!</i>\n\n" .. data.last_result .. "]"
|
||||
end
|
||||
|
||||
local progress_bar = data.subject.image and string.format([[
|
||||
box[4.475,3.675;%f,0.625;#00ff00]
|
||||
image[4.45,3.65;3.65,0.8;researcher_research_points_border.png;7]
|
||||
hypertext[4.3,3.65;4,0.9;;<global valign=middle halign=center><b>Level %s</b>%s]
|
||||
]],
|
||||
data.is_max_level and 2.85 or (2.85 * (data.current_points / data.points_to_next_level)),
|
||||
data.is_max_level and "MAX" or (data.subject.research and data.subject.research.level or 1),
|
||||
data.is_max_level and "" or ("\n" .. (data.subject.research and data.subject.research.points or 0) .. " / " .. researcher.get_points_to_next_level(player_name,data.subject.item.name)))
|
||||
|
||||
local decor = data.is_inventory_empty and "inactive" or "active"
|
||||
|
||||
return format_formspec([[
|
||||
-- Background boxes; research/duplication on the left, info/progress on the right
|
||||
box[0,0;3.8,4.9;#00000040]
|
||||
box[4,0;3.8,4.9;#00000040]
|
||||
|
||||
-- Player's 1x1 research inventory with optional research/duplication button
|
||||
-- at the bottom
|
||||
image[1.5,0.8;1,1;researcher_gui_hb_bg.png]
|
||||
image[1.1,0.375;2,2;researcher_research_inventory_decor_%s.png]
|
||||
list[current_player;research;1.5,0.8;1,1;0]
|
||||
listring[current_player;main]
|
||||
listring[current_player;research]
|
||||
%s
|
||||
|
||||
-- Current research item image, name, and groups
|
||||
item_image[5,0.2;2.2,2.2;%s]
|
||||
hypertext[4.3,2.3;4,0.5;;<global halign=center size=18><b>%s</b>]
|
||||
box[4.3,2.6;3.2,0.001;#00000099]
|
||||
hypertext[4.3,2.7;4,1.5;;<global halign=center>%s]
|
||||
|
||||
-- Research level/points progress bar
|
||||
%s
|
||||
]],
|
||||
decor,
|
||||
data.is_inventory_empty and status_string or string.format("button[0.4,3.3;3.15,1.6;%s;%s]",data.is_max_level and "duplicate" or "research",data.is_max_level and "Duplicate" or "Research"),
|
||||
data.subject.image or "",
|
||||
data.subject.description,
|
||||
data.subject.research and data.subject.groups or "(research to learn item groups)",
|
||||
progress_bar or "")
|
||||
end
|
||||
|
||||
-- Get Mineclonia/VoxelLibre formspec
|
||||
elseif researcher.dependencies.mcl_inventory then
|
||||
return function(player_name)
|
||||
local data = researcher.get_formspec_data(player_name)
|
||||
|
||||
local status_string = "hypertext[1,2.75;4.5,4;;<global halign=center>(place item above to analyze)]"
|
||||
if data.last_result then
|
||||
status_string = "hypertext[1,2.75;4.5,4;;<global halign=center><i>Research successful!</i>\n\n" .. data.last_result .. "]"
|
||||
end
|
||||
|
||||
local progress_bar = data.subject.image and string.format([[
|
||||
box[6.55,4.41;%f,0.73;#00ff00]
|
||||
image[6.5,4.4;3.725,0.75;researcher_research_points_border.png;7]
|
||||
hypertext[6.1,4.325;4.5,0.9;;<global valign=middle halign=center><b>Level %s</b>%s]
|
||||
]],
|
||||
data.is_max_level and 3.66 or (3.66 * (data.current_points / data.points_to_next_level)),
|
||||
data.is_max_level and "MAX" or (data.subject.research and data.subject.research.level or 1),
|
||||
data.is_max_level and "" or ("\n" .. (data.subject.research and data.subject.research.points or 0) .. " / " .. researcher.get_points_to_next_level(player_name,data.subject.item.name)))
|
||||
|
||||
local decor = data.is_inventory_empty and "inactive" or "active"
|
||||
|
||||
return format_formspec([[
|
||||
-- Background boxes; research/duplication on the left, info/progress on the right
|
||||
box[1,0.2;4.5,5.1;#00000040]
|
||||
box[6.1,0.2;4.5,5.1;#00000040]
|
||||
|
||||
-- Player's 1x1 research inventory with optional research/duplication button
|
||||
-- at the bottom
|
||||
image[2.75,1;1,1;researcher_gui_hb_bg.png]
|
||||
image[2.25,0.5;2,2;researcher_research_inventory_decor_%s.png]
|
||||
list[current_player;research;2.75,1;1,1;0]
|
||||
listring[current_player;main]
|
||||
listring[current_player;research]
|
||||
%s
|
||||
|
||||
-- Current research item image, name, and groups
|
||||
item_image[7.325,0.4;2,2;%s]
|
||||
hypertext[6.1,2.5;4.5,0.5;;<global halign=center size=18><b>%s</b>]
|
||||
box[6.5,2.8;3.75,0.001;#00000099]
|
||||
hypertext[6.1,2.9;4.5,1.5;;<global halign=center>%s]
|
||||
|
||||
-- Research level/points progress bar
|
||||
%s
|
||||
]],
|
||||
decor,
|
||||
data.is_inventory_empty and status_string or string.format("button[1.25,4.4;4,0.75;%s;%s]",data.is_max_level and "duplicate" or "research",data.is_max_level and "Duplicate" or "Research"),
|
||||
data.subject.image or "",
|
||||
data.subject.description,
|
||||
data.subject.research and data.subject.groups or "(research to learn item groups)",
|
||||
progress_bar or "")
|
||||
end
|
||||
|
||||
-- Get Unified Inventory formspec
|
||||
elseif researcher.dependencies.unified_inventory then
|
||||
return function(player_name)
|
||||
local data = researcher.get_formspec_data(player_name)
|
||||
|
||||
local status_string = "hypertext[0.5,2.75;4.5,4;;<global halign=center>(place item above to analyze)]"
|
||||
if data.last_result then
|
||||
status_string = "hypertext[0.5,2.75;4.5,4;;<global halign=center><i>Research successful!</i>\n\n" .. data.last_result .. "]"
|
||||
end
|
||||
|
||||
local progress_bar = data.subject.image and string.format([[
|
||||
box[6.05,4.61;%f,0.73;#00ff00]
|
||||
image[6,4.6;3.725,0.75;researcher_research_points_border.png;7]
|
||||
hypertext[5.6,4.525;4.5,0.9;;<global valign=middle halign=center><b>Level %s</b>%s]
|
||||
]],
|
||||
data.is_max_level and 3.66 or (3.66 * (data.current_points / data.points_to_next_level)),
|
||||
data.is_max_level and "MAX" or (data.subject.research and data.subject.research.level or 1),
|
||||
data.is_max_level and "" or ("\n" .. (data.subject.research and data.subject.research.points or 0) .. " / " .. researcher.get_points_to_next_level(player_name,data.subject.item.name)))
|
||||
|
||||
local decor = data.is_inventory_empty and "inactive" or "active"
|
||||
|
||||
return format_formspec([[
|
||||
-- Background boxes; research/duplication on the left, info/progress on the right
|
||||
box[0.5,0.2;4.5,5.4;#00000040]
|
||||
box[5.6,0.2;4.5,5.4;#00000040]
|
||||
|
||||
-- Player's 1x1 research inventory with optional research/duplication button
|
||||
-- at the bottom
|
||||
image[2.25,1;1,1;researcher_gui_hb_bg.png]
|
||||
image[1.75,0.5;2,2;researcher_research_inventory_decor_%s.png]
|
||||
list[current_player;research;2.25,1;1,1;0]
|
||||
listring[current_player;main]
|
||||
listring[current_player;research]
|
||||
%s
|
||||
|
||||
-- Current research item image, name, and groups
|
||||
item_image[6.825,0.4;2,2;%s]
|
||||
hypertext[5.6,2.5;4.5,0.5;;<global halign=center size=18><b>%s</b>]
|
||||
box[6,2.8;3.75,0.001;#00000099]
|
||||
hypertext[5.6,2.9;4.5,1.5;;<global halign=center>%s]
|
||||
|
||||
-- Research level/points progress bar
|
||||
%s
|
||||
]],
|
||||
decor,
|
||||
data.is_inventory_empty and status_string or string.format("button[0.75,4.6;4,0.75;%s;%s]",data.is_max_level and "duplicate" or "research",data.is_max_level and "Duplicate" or "Research"),
|
||||
data.subject.image or "",
|
||||
data.subject.description,
|
||||
data.subject.research and data.subject.groups or "(research to learn item groups)",
|
||||
progress_bar or "")
|
||||
end
|
||||
|
||||
-- Get i3 formspec
|
||||
elseif researcher.dependencies.i3 then
|
||||
return function(player_name)
|
||||
local data = researcher.get_formspec_data(player_name)
|
||||
|
||||
local status_string = "hypertext[0.5,3.05;4.5,4;;<global halign=center>(place item above to analyze)]"
|
||||
if data.last_result then
|
||||
status_string = "hypertext[0.5,3.05;4.5,4;;<global halign=center><i>Research successful!</i>\n\n" .. data.last_result .. "]"
|
||||
end
|
||||
|
||||
local progress_bar = data.subject.image and string.format([[
|
||||
box[5.75,4.925;%f,0.715;#00ff00]
|
||||
image[5.7,4.9;3.725,0.75;researcher_research_points_border.png;7]
|
||||
hypertext[5.3,4.825;4.5,0.9;;<global valign=middle halign=center><b>Level %s</b>%s]
|
||||
]],
|
||||
data.is_max_level and 3.64 or (3.64 * (data.current_points / data.points_to_next_level)),
|
||||
data.is_max_level and "MAX" or (data.subject.research and data.subject.research.level or 1),
|
||||
data.is_max_level and "" or ("\n" .. (data.subject.research and data.subject.research.points or 0) .. " / " .. researcher.get_points_to_next_level(player_name,data.subject.item.name)))
|
||||
|
||||
local decor = data.is_inventory_empty and "inactive" or "active"
|
||||
|
||||
return format_formspec([[
|
||||
-- Background boxes; research/duplication on the left, info/progress on the right
|
||||
box[0.5,0.5;4.5,5.4;#00000040]
|
||||
box[5.3,0.5;4.5,5.4;#00000040]
|
||||
|
||||
-- Player's 1x1 research inventory with optional research/duplication button
|
||||
-- at the bottom
|
||||
image[2.25,1.3;1,1;researcher_gui_hb_bg.png]
|
||||
image[1.75,0.8;2,2;researcher_research_inventory_decor_%s.png]
|
||||
list[current_player;research;2.25,1.3;1,1;0]
|
||||
listring[current_player;main]
|
||||
listring[current_player;research]
|
||||
%s
|
||||
|
||||
-- Current research item image, name, and groups
|
||||
item_image[6.525,0.7;2,2;%s]
|
||||
hypertext[5.3,2.8;4.5,0.5;;<global halign=center size=18><b>%s</b>]
|
||||
box[5.7,3.1;3.75,0.001;#00000099]
|
||||
hypertext[5.3,3.2;4.5,1.5;;<global halign=center>%s]
|
||||
|
||||
-- Research level/points progress bar
|
||||
%s
|
||||
]],
|
||||
decor,
|
||||
data.is_inventory_empty and status_string or string.format("button[0.75,4.9;4,0.75;%s;%s]",data.is_max_level and "duplicate" or "research",data.is_max_level and "Duplicate" or "Research"),
|
||||
data.subject.image or "",
|
||||
data.subject.description,
|
||||
data.subject.research and data.subject.groups or "(research to learn item groups)",
|
||||
progress_bar or "")
|
||||
end
|
||||
|
||||
-- Get universal formspec
|
||||
else
|
||||
return function(player_name)
|
||||
local data = researcher.get_formspec_data(player_name)
|
||||
|
||||
local status_string = "hypertext[0.3,2.3;4,4;;<global halign=center>(place item above to analyze)]"
|
||||
if data.last_result then
|
||||
status_string = "hypertext[0.3,2.3;4,4;;<global halign=center><i>Research successful!</i>\n\n" .. data.last_result .. "]"
|
||||
end
|
||||
|
||||
local progress_bar = data.subject.image and string.format([[
|
||||
box[4.475,3.675;%f,0.625;#00ff00]
|
||||
image[4.45,3.65;3.65,0.8;researcher_research_points_border.png;7]
|
||||
hypertext[4.3,3.65;4,0.9;;<global valign=middle halign=center><b>Level %s</b>%s]
|
||||
]],
|
||||
data.is_max_level and 2.85 or (2.85 * (data.current_points / data.points_to_next_level)),
|
||||
data.is_max_level and "MAX" or (data.subject.research and data.subject.research.level or 1),
|
||||
data.is_max_level and "" or ("\n" .. (data.subject.research and data.subject.research.points or 0) .. " / " .. researcher.get_points_to_next_level(player_name,data.subject.item.name)))
|
||||
|
||||
local decor = data.is_inventory_empty and "inactive" or "active"
|
||||
|
||||
return format_formspec([[
|
||||
-- sfinv size + padding
|
||||
size[8,9.1]
|
||||
|
||||
-- Background boxes; research/duplication on the left, info/progress on the right
|
||||
box[0,0;3.8,4.9;#00000040]
|
||||
box[4,0;3.8,4.9;#00000040]
|
||||
|
||||
-- Player's 1x1 research inventory with optional research/duplication button
|
||||
-- at the bottom
|
||||
image[1.5,0.8;1,1;researcher_gui_hb_bg.png]
|
||||
image[1.1,0.375;2,2;researcher_research_inventory_decor_%s.png]
|
||||
list[current_player;research;1.5,0.8;1,1;0]
|
||||
listring[current_player;main]
|
||||
listring[current_player;research]
|
||||
%s
|
||||
|
||||
-- Current research item image, name, and groups
|
||||
item_image[5,0.2;2.2,2.2;%s]
|
||||
hypertext[4.3,2.3;4,0.5;;<global halign=center size=18><b>%s</b>]
|
||||
box[4.3,2.6;3.2,0.001;#00000099]
|
||||
hypertext[4.3,2.7;4,1.5;;<global halign=center>%s]
|
||||
|
||||
-- Research level/points progress bar
|
||||
%s
|
||||
|
||||
-- Player's main inventory a la sfinv
|
||||
image[0,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[1,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[2,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[3,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[4,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[5,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[6,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[7,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
list[current_player;main;0,5.2;8,1;]
|
||||
list[current_player;main;0,6.35;8,3;8]
|
||||
]],
|
||||
decor,
|
||||
data.is_inventory_empty and status_string or string.format("button[0.4,3.3;3.15,1.6;%s;%s]",data.is_max_level and "duplicate" or "research",data.is_max_level and "Duplicate" or "Research"),
|
||||
data.subject.image or "",
|
||||
data.subject.description,
|
||||
data.subject.research and data.subject.groups or "(research to learn item groups)",
|
||||
progress_bar or "")
|
||||
end
|
||||
end
|
||||
end)()
|
||||
|
||||
local refresh_ui = (function()
|
||||
-- Refresh sfinv formspec
|
||||
if researcher.dependencies.sfinv and sfinv.enabled and not researcher.dependencies.i3 then
|
||||
return function(player)
|
||||
sfinv.set_player_inventory_formspec(player,sfinv.get_or_create_context(player))
|
||||
end
|
||||
|
||||
-- Refresh Mineclone formspec
|
||||
elseif researcher.dependencies.mcl_inventory then
|
||||
return function(player)
|
||||
mcl_inventory.update_inventory_formspec(player)
|
||||
end
|
||||
|
||||
-- Refresh Unified Inventory formspec
|
||||
elseif researcher.dependencies.unified_inventory then
|
||||
return function(player)
|
||||
unified_inventory.set_inventory_formspec(player,"Research")
|
||||
end
|
||||
|
||||
-- Refresh i3 formspec
|
||||
elseif researcher.dependencies.i3 then
|
||||
return function(player)
|
||||
i3.set_fs(player)
|
||||
end
|
||||
|
||||
-- Refresh universal formspec
|
||||
else
|
||||
return function(player)
|
||||
local name = player:get_player_name()
|
||||
minetest.show_formspec(name,"researcher:player_research",get_ui(name))
|
||||
end
|
||||
end
|
||||
end)()
|
||||
|
||||
local do_research = function(player,fields,refresh)
|
||||
if fields.research then
|
||||
local player_name = player:get_player_name()
|
||||
local inventory = player:get_inventory()
|
||||
local item = inventory:get_stack("research",1)
|
||||
if not item:is_empty() then
|
||||
-- Get research level to compare with after research
|
||||
local player_data = researcher.get_player_data(player_name)
|
||||
local research = player_data.research[item:get_name()]
|
||||
local level_before = 1
|
||||
if research then
|
||||
level_before = research.level
|
||||
end
|
||||
|
||||
-- Perform research
|
||||
local results = researcher.research_inventory(player_name,inventory,"research")
|
||||
|
||||
-- Aggregate results
|
||||
local totals = {
|
||||
items = 0,
|
||||
base = 0,
|
||||
bonuses = {},
|
||||
}
|
||||
for _,itemstack in ipairs(results) do
|
||||
for _,i in ipairs(itemstack) do
|
||||
totals.items = totals.items + 1
|
||||
totals.base = totals.base + i.base
|
||||
for _,bonus in ipairs(i.bonuses) do
|
||||
totals.bonuses[bonus.reason] = totals.bonuses[bonus.reason] or 0
|
||||
totals.bonuses[bonus.reason] = totals.bonuses[bonus.reason] + bonus.points
|
||||
end
|
||||
end
|
||||
end
|
||||
player_data.last_result = "Items researched: <style color=#0099cc>" .. totals.items .. "</style>\nBase points: <style color=#0099cc>" .. totals.base .. "</style>\n"
|
||||
for reason,points in pairs(totals.bonuses) do
|
||||
player_data.last_result = player_data.last_result .. reason .. ": " .. (points > 0 and "<style color=#00cc00>+" or "<style color=#cc0000>-") .. points .. "</style>\n"
|
||||
end
|
||||
|
||||
-- Refresh UI
|
||||
if refresh ~= false then
|
||||
refresh_ui(player)
|
||||
end
|
||||
|
||||
-- Determine level up progress
|
||||
research = player_data.research[item:get_name()]
|
||||
if research and research.level > level_before then
|
||||
-- Unlock eureka award
|
||||
if researcher.settings.awards then
|
||||
awards.unlock(player_name,"researcher:eureka")
|
||||
end
|
||||
|
||||
-- Play research level up sound
|
||||
minetest.sound_play({
|
||||
name = "researcher_level_up",
|
||||
gain = 0.5,
|
||||
},{ to_player = player_name },true)
|
||||
else
|
||||
-- Play normal research sound
|
||||
minetest.sound_play({
|
||||
name = "researcher_research",
|
||||
gain = 0.1,
|
||||
pitch = 1 + (math.random(1,4) / 10),
|
||||
},{ to_player = player_name },true)
|
||||
end
|
||||
end
|
||||
elseif fields.duplicate then
|
||||
researcher.duplicate_research(player)
|
||||
minetest.sound_play({
|
||||
name = "researcher_duplicate",
|
||||
gain = 0.5,
|
||||
pitch = 1.5,
|
||||
},{ to_player = player_name },true)
|
||||
end
|
||||
end
|
||||
|
||||
-- ------------------ --
|
||||
-- GUI INTEGRATIONS --
|
||||
-- ------------------ --
|
||||
|
||||
-- Configure sfinv UI
|
||||
if researcher.dependencies.sfinv and sfinv.enabled and not researcher.dependencies.i3 then
|
||||
sfinv.register_page("researcher:player_research",{
|
||||
title = "Research",
|
||||
get = function(self,player,context)
|
||||
return sfinv.make_formspec(player,context,get_ui(player:get_player_name()),true)
|
||||
end,
|
||||
is_in_nav = function()
|
||||
return true
|
||||
end,
|
||||
on_player_receive_fields = function(self,player,context,fields)
|
||||
do_research(player,fields)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Configure Mineclonia/VoxelLibre UI
|
||||
elseif researcher.dependencies.mcl_inventory then
|
||||
mcl_inventory.register_survival_inventory_tab({
|
||||
id = "research",
|
||||
description = "Research",
|
||||
item_icon = "researcher:research_table",
|
||||
show_inventory = true,
|
||||
build = function(player)
|
||||
return get_ui(player:get_player_name())
|
||||
end,
|
||||
handle = function(player, fields)
|
||||
do_research(player,fields)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Configure Unified Inventory UI
|
||||
elseif researcher.dependencies.unified_inventory then
|
||||
unified_inventory.register_page("Research",{
|
||||
get_formspec = function(player,formspec)
|
||||
return { formspec = formspec.standard_inv_bg .. get_ui(player:get_player_name()) }
|
||||
end,
|
||||
})
|
||||
|
||||
unified_inventory.register_button("Research",{
|
||||
type = "image",
|
||||
image = "researcher_icon_black.png",
|
||||
tooltip = "Research",
|
||||
hide_lite = false,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player,formname,fields)
|
||||
if formname == "" and (fields.research or fields.duplicate) then
|
||||
do_research(player,fields)
|
||||
return
|
||||
end
|
||||
end)
|
||||
|
||||
-- Configure i3 UI
|
||||
elseif researcher.dependencies.i3 then
|
||||
i3.new_tab("research",{
|
||||
description = "Research",
|
||||
slots = true,
|
||||
formspec = function(player, data, fs)
|
||||
fs(get_ui(player:get_player_name()))
|
||||
end,
|
||||
fields = function(player, data, fields)
|
||||
if fields.research or fields.duplicate then
|
||||
do_research(player,fields,false)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Configure universal/standalone UI
|
||||
else
|
||||
minetest.register_on_player_receive_fields(function(player,formname,fields)
|
||||
if formname == "researcher:player_research" then
|
||||
do_research(player,fields)
|
||||
return
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Refresh UI when research inventory changes
|
||||
minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info)
|
||||
if inventory_info.listname == "research" or inventory_info.to_list == "research" or inventory_info.from_list == "research" then
|
||||
refresh_ui(player)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
62
mods/researcher/src/inventory.lua
Normal file
|
@ -0,0 +1,62 @@
|
|||
-- Initialize player research inventory
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
player:get_inventory():set_size("research",1)
|
||||
end)
|
||||
|
||||
-- Set the subject of the player's research based on their research inventory
|
||||
minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info)
|
||||
if inventory_info.listname == "research" or inventory_info.to_list == "research" or inventory_info.from_list == "research" then
|
||||
-- Get item info from research inventory
|
||||
local item = inventory:get_stack("research",1)
|
||||
|
||||
-- Get player name and data
|
||||
local player_name = player:get_player_name()
|
||||
local player_data = researcher.get_player_data(player_name)
|
||||
|
||||
-- Cache inventory empty status
|
||||
player_data.subject.is_empty = item:is_empty()
|
||||
|
||||
-- Set the player's research subject if research inventory has an item in it
|
||||
local name = item:get_name()
|
||||
local subject = player_data.subject
|
||||
local description, groups
|
||||
if not item:is_empty() then
|
||||
subject.name = name
|
||||
subject.item = researcher.registered_items[name]
|
||||
subject.image = name
|
||||
description = minetest.registered_items[name]
|
||||
if description then
|
||||
subject.description = description.description:split("\n",1)[1]
|
||||
else
|
||||
subject.description = "???"
|
||||
end
|
||||
|
||||
if subject.item then
|
||||
subject.groups = (function()
|
||||
local str = ""
|
||||
local grouplist = {}
|
||||
for group,_ in pairs(subject.item.groups) do
|
||||
table.insert(grouplist,group)
|
||||
end
|
||||
table.sort(grouplist)
|
||||
return table.concat(grouplist,", ")
|
||||
end)()
|
||||
subject.research = player_data.research[name]
|
||||
else
|
||||
subject.groups = "(groups unknown)"
|
||||
subject.research = nil
|
||||
subject.item = {
|
||||
name = "???",
|
||||
groups = {},
|
||||
adjustments = {},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Save player data
|
||||
researcher.save_player_data(player_name)
|
||||
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
178
mods/researcher/src/research_table.lua
Normal file
|
@ -0,0 +1,178 @@
|
|||
-- Function to get formspec string
|
||||
local function get_formspec(item)
|
||||
-- Set item info string
|
||||
local iteminfo = [[
|
||||
box[1,2.75;6,0.001;#00000099]
|
||||
hypertext[2.3,3.15;4,1.5;;<global halign=center>(place item above to set focus)]
|
||||
]]
|
||||
if item then
|
||||
local description = minetest.registered_items[item] and minetest.registered_items[item].description:split("\n")[1]
|
||||
local groups = researcher.registered_items[item] and (function()
|
||||
local str = ""
|
||||
local grouplist = {}
|
||||
for group,_ in pairs(researcher.registered_items[item].groups) do
|
||||
table.insert(grouplist,group)
|
||||
end
|
||||
table.sort(grouplist)
|
||||
return table.concat(grouplist,", ")
|
||||
end)()
|
||||
iteminfo = string.format([[
|
||||
box[1,2.75;6,0.001;#00000099]
|
||||
hypertext[2.3,3;4,0.5;;<global halign=center size=24><b>%s</b>]
|
||||
hypertext[2.3,3.35;4,1.5;;<global halign=center size=18>%s]
|
||||
]],
|
||||
description or "???",
|
||||
groups or "(groups unknown)")
|
||||
end
|
||||
|
||||
return string.format([[
|
||||
size[8,9.1]
|
||||
box[0,0;7.8,5;#00000040]
|
||||
|
||||
image[3.1,0.575;2,2;researcher_research_inventory_decor_%s.png]
|
||||
list[context;focus;3.5,1;1,1;0]
|
||||
listring[current_player;main]
|
||||
listring[context;focus]
|
||||
|
||||
%s
|
||||
|
||||
image[0,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[1,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[2,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[3,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[4,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[5,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[6,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
image[7,5.2;1,1;researcher_gui_hb_bg.png]
|
||||
list[current_player;main;0,5.2;8,1;]
|
||||
list[current_player;main;0,6.35;8,3;8]
|
||||
]],
|
||||
item and "active" or "inactive",
|
||||
iteminfo)
|
||||
end
|
||||
|
||||
-- Function for refreshing infotext when node meta inventory changes
|
||||
local function update_metadata(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local item = meta:get_inventory():get_stack("focus",1)
|
||||
if item and not item:is_empty() then
|
||||
local iname = item:get_name()
|
||||
item = minetest.registered_items[iname]
|
||||
description = ""
|
||||
if item and item.description then
|
||||
meta:set_string("infotext","Research Table: " .. item.description:split("\n")[1])
|
||||
end
|
||||
meta:set_string("formspec",get_formspec(iname))
|
||||
else
|
||||
meta:set_string("formspec",get_formspec(nil))
|
||||
meta:set_string("infotext","Research Table")
|
||||
end
|
||||
end
|
||||
|
||||
-- Register research table node
|
||||
minetest.register_node("researcher:research_table",{
|
||||
-- Node definition fields
|
||||
description = "Research Table",
|
||||
short_description = "Research Table",
|
||||
drawtype = "mesh",
|
||||
mesh = "research_table.obj",
|
||||
tiles = {
|
||||
{ name = "researcher_research_table_frame.png" },
|
||||
{ name = "researcher_research_table_surface.png" },
|
||||
},
|
||||
paramtype2 = "4dir",
|
||||
stack_max = 1,
|
||||
sounds = (function()
|
||||
if researcher.dependencies.default then
|
||||
return default.node_sound_wood_defaults()
|
||||
elseif researcher.dependencies.mcl_sounds then
|
||||
return mcl_sounds.node_sound_wood_defaults()
|
||||
else
|
||||
return nil -- no specific sounds
|
||||
end
|
||||
end)(),
|
||||
|
||||
-- Set research table groups
|
||||
groups = {
|
||||
oddly_breakable_by_hand = 1,
|
||||
},
|
||||
|
||||
-- Initialize research table data
|
||||
on_construct = function(pos)
|
||||
-- Set inventory size
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inventory = meta:get_inventory()
|
||||
inventory:set_size("focus", 1)
|
||||
|
||||
-- Set infotext
|
||||
meta:set_string("infotext","Research Table")
|
||||
|
||||
-- Set meta formspec
|
||||
meta:set_string("formspec",get_formspec(nil))
|
||||
end,
|
||||
|
||||
-- Drop inventory contents when destroyed
|
||||
on_destruct = function(pos)
|
||||
local item = minetest.get_meta(pos):get_inventory():get_stack("focus",1)
|
||||
if item and not item:is_empty() then
|
||||
minetest.add_item(pos,item)
|
||||
end
|
||||
end,
|
||||
|
||||
-- Update infotext when inventory changes
|
||||
on_metadata_inventory_move = update_metadata,
|
||||
on_metadata_inventory_take = update_metadata,
|
||||
on_metadata_inventory_put = update_metadata,
|
||||
})
|
||||
|
||||
-- Register research table crafting recipe
|
||||
minetest.register_craft({
|
||||
output = "researcher:research_table",
|
||||
recipe = {
|
||||
{"group:stone", "group:stone", "group:stone"},
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"group:wood", "", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
-- Register ABM for activation particles
|
||||
minetest.register_abm({
|
||||
label = "Researcher: Research Table Activation Particles",
|
||||
nodenames = {"researcher:research_table"},
|
||||
interval = 4,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos)
|
||||
-- Do nothing if research table is empty
|
||||
if minetest.get_meta(pos):get_inventory():get_stack("focus",1):is_empty() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Show item particles to nearby players
|
||||
local radius = researcher.settings.research_table_player_radius
|
||||
for object in minetest.objects_in_area(pos:add(-2),pos:add(2)) do
|
||||
if object:is_player() then
|
||||
minetest.add_particlespawner({
|
||||
playername = object:get_player_name(),
|
||||
amount = 16,
|
||||
time = 4,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.6,0,-0.6)),
|
||||
max = pos:add(vector.new(0.6,0,0.6)),
|
||||
},
|
||||
minsize = 1,
|
||||
maxsize = 1.5,
|
||||
minvel = { x = 0, y = 0.05, z = 0 },
|
||||
maxvel = { x = 0, y = 0.1, z = 0 },
|
||||
minacc = { x = 0, y = 0.1, z = 0 },
|
||||
maxacc = { x = 0, y = 0.2, z = 0 },
|
||||
minexptime = 4.5,
|
||||
maxexptime = 3,
|
||||
texture = "plus.png^[colorize:#ffff77^[opacity:180",
|
||||
glow = 14,
|
||||
collisiondetection = false,
|
||||
})
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
195
mods/researcher/src/scan.lua
Normal file
|
@ -0,0 +1,195 @@
|
|||
-- Scan all items for groups
|
||||
minetest.register_on_mods_loaded(function()
|
||||
-- Reduce research difficulty for items not present in mapgen
|
||||
local mapgen_nodes = {}
|
||||
if researcher.settings.discount_mapgen < 0 then
|
||||
mapgen_nodes = {
|
||||
map_drop = function(self,node)
|
||||
local def = minetest.registered_nodes[node]
|
||||
if not def then
|
||||
return "<ndef>"
|
||||
end
|
||||
|
||||
local drop = def.drop
|
||||
if not drop or drop == "" then
|
||||
self[node] = true
|
||||
return node
|
||||
elseif type(drop) == "string" then
|
||||
self[drop] = true
|
||||
return drop
|
||||
elseif type(drop) == "table" then
|
||||
for _,item in ipairs(drop.items or {}) do
|
||||
if type(item) == "table" then
|
||||
for _,i in ipairs(item.items) do
|
||||
self[i] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
for _,def in pairs(minetest.registered_biomes) do
|
||||
for _,node_type in ipairs({
|
||||
"node_top",
|
||||
"node_filler",
|
||||
"node_stone",
|
||||
"node_water_top",
|
||||
"node_water",
|
||||
"node_river_water",
|
||||
"node_riverbed",
|
||||
"node_cave_liquid",
|
||||
"node_dungeon",
|
||||
"node_dungeon_alt",
|
||||
"node_dungeon_stair",
|
||||
}) do
|
||||
if def[node_type] then
|
||||
local node = def[node_type]
|
||||
if type(node) == "string" then
|
||||
mapgen_nodes[node] = true
|
||||
mapgen_nodes:map_drop(node)
|
||||
elseif type(node) == "table" then
|
||||
for _,n in ipairs(node) do
|
||||
if type(n) == "string" then
|
||||
mapgen_nodes[n] = true
|
||||
mapgen_nodes:map_drop(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local read_schematics = {}
|
||||
for _,def in pairs(minetest.registered_decorations) do
|
||||
if (not def.deco_type or def.deco_type == "simple") and def.decoration then
|
||||
if type(def.decoration) == "string" then
|
||||
mapgen_nodes[def.decoration] = true
|
||||
mapgen_nodes:map_drop(def.decoration)
|
||||
elseif type(def.decoration) == "table" then
|
||||
for _,node in ipairs(def.decoration) do
|
||||
if type(node) == "string" then
|
||||
mapgen_nodes[node] = true
|
||||
local to = mapgen_nodes:map_drop(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif def.deco_type == "schematic" and def.schematic then
|
||||
local schematic
|
||||
if type(def.schematic) == "string" and not read_schematics[def.schematic] then
|
||||
read_schematics[def.schematic] = true
|
||||
schematic = minetest.read_schematic(def.schematic,{ write_yslice_prob = "none" })
|
||||
elseif type(def.schematic) == "table" then
|
||||
schematic = def.schematic
|
||||
end
|
||||
|
||||
if schematic then
|
||||
for _,node in ipairs(schematic.data) do
|
||||
if type(node.name) == "string" and node.name ~= "air" then
|
||||
mapgen_nodes[node.name] = true
|
||||
mapgen_nodes:map_drop(node.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _,def in pairs(minetest.registered_ores) do
|
||||
if type(def.ore) == "string" then
|
||||
mapgen_nodes[def.ore] = true
|
||||
mapgen_nodes:map_drop(def.ore)
|
||||
end
|
||||
end
|
||||
|
||||
researcher.register_adjustment({
|
||||
name = "researcher:discount_mapgen",
|
||||
reason = "Item not abundant in the world",
|
||||
calculate = function(item)
|
||||
return (not mapgen_nodes[item]) and researcher.settings.discount_mapgen or 0
|
||||
end,
|
||||
})
|
||||
else
|
||||
researcher.register_adjustment({
|
||||
name = "researcher:discount_mapgen",
|
||||
reason = "Item not abundant in world",
|
||||
calculate = function() return 0 end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Register low stack discount
|
||||
local stack_max = researcher.dependencies.mcl_inventory and 64 or tonumber(minetest.settings:get("default_stack_max",99) or 99)
|
||||
local low_stack = {}
|
||||
if researcher.settings.discount_stack_max < 0 then
|
||||
researcher.register_adjustment({
|
||||
name = "researcher:discount_stack_max",
|
||||
reason = "Item has a stack max less than the default max",
|
||||
calculate = function(item)
|
||||
return low_stack[item] and researcher.settings.discount_stack_max or 0
|
||||
end,
|
||||
})
|
||||
else
|
||||
researcher.register_adjustment({
|
||||
name = "researcher:discount_stack_max",
|
||||
reason = "Item has a stack max less than the default max",
|
||||
calculate = function() return 0 end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Reduce research difficulty for items that are not craftable
|
||||
local not_craftable = {}
|
||||
if researcher.settings.discount_not_craftable < 0 then
|
||||
researcher.register_adjustment({
|
||||
name = "researcher:discount_not_craftable",
|
||||
reason = "Item is not craftable",
|
||||
calculate = function(item)
|
||||
return (not mapgen_nodes[item] and not_craftable[item]) and researcher.settings.discount_not_craftable or 0
|
||||
end,
|
||||
})
|
||||
else
|
||||
researcher.register_adjustment({
|
||||
name = "researcher:discount_not_craftable",
|
||||
reason = "Item is not craftable",
|
||||
calculate = function() return 0 end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Register items with Researcher
|
||||
for name,def in pairs(minetest.registered_items) do
|
||||
-- Get low stack data
|
||||
if def.tool_capabilities or ((def.stack_max or stack_max) < stack_max) then
|
||||
low_stack[name] = def.stack_max or (def.tool_capabilities and 1) or stack_max
|
||||
end
|
||||
|
||||
-- Filter out unwanted groups
|
||||
local groups = def.groups
|
||||
local keep_groups = {}
|
||||
|
||||
if groups then
|
||||
for group,value in pairs(groups) do
|
||||
if value > 0 and not researcher.excluded_groups[group] then
|
||||
table.insert(keep_groups,group)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for valid recipes
|
||||
local recipes = minetest.get_all_craft_recipes(name) or {}
|
||||
for _,recipe in ipairs(recipes) do
|
||||
if recipe.method == "normal" or recipe.method == "cooking" then
|
||||
recipes = 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if recipes ~= 1 then
|
||||
not_craftable[name] = true
|
||||
end
|
||||
|
||||
-- Register item
|
||||
researcher.register_item({
|
||||
name = name,
|
||||
points_per_level = researcher.settings.points_per_level,
|
||||
groups = keep_groups,
|
||||
})
|
||||
end
|
||||
end)
|
BIN
mods/researcher/textures/researcher_gui_hb_bg.png
Normal file
After Width: | Height: | Size: 98 B |
BIN
mods/researcher/textures/researcher_icon_black.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mods/researcher/textures/researcher_icon_bronze.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
mods/researcher/textures/researcher_icon_gold.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
mods/researcher/textures/researcher_icon_silver.png
Normal file
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 333 B |
After Width: | Height: | Size: 323 B |
BIN
mods/researcher/textures/researcher_research_points_border.png
Normal file
After Width: | Height: | Size: 183 B |
BIN
mods/researcher/textures/researcher_research_table_frame.png
Normal file
After Width: | Height: | Size: 430 B |
BIN
mods/researcher/textures/researcher_research_table_surface.png
Normal file
After Width: | Height: | Size: 416 B |