Compare commits

...

108 Commits

Author SHA1 Message Date
Phaethon H 874d5986f6 Replace multiple add_basic_rule() calls with per-item-proxy-class add_*_rules(). 2021-11-12 11:20:32 -08:00
Phaethon H 7ac3ffd0fe Adding docs generated by LDoc. 2021-11-10 21:14:22 -08:00
Phaethon H c08c98e74a Updates to general description (actual API calls descriptions desyncing with sources; hopeful to fill in with LDoc). 2021-11-10 21:06:36 -08:00
Phaethon H c9d5bbbde3 linting. 2021-11-10 21:03:04 -08:00
Phaethon H a8d5d4fd55 Added notes on installing ldoc. 2021-11-10 19:09:53 -08:00
Phaethon H 34c6aa1442 Create ItemProxy base prototype for RuleBasedItemProxy. 2021-11-10 19:08:34 -08:00
Phaethon H 1c9012c3ef Add LDoc configuration for running ldoc(1). 2021-11-10 19:08:14 -08:00
Phaethon H b435bcbd5a Elide mcl_cauldrons.Cauldron class and move its function to under mcl_cauldrons. 2021-11-10 18:45:23 -08:00
Phaethon H 92b03acd35 Play sound for dipping banner in cauldron. 2021-11-10 18:21:45 -08:00
Phaethon H 4c9c770405 migrate from doxygen to LDoc (LuaDoc derivative). 2021-11-10 18:21:34 -08:00
Phaethon H 3a68e96ffe Starting doxygen documentation. 2021-11-10 15:49:03 -08:00
Phaethon H 07d9ae98d9 Optional dependency on potions (bottles).
Rename extension cauldrons to pattern cauldron_*.lua
2021-11-10 13:02:16 -08:00
Phaethon H 02e905eb51 Roll back some changes to mcl_banner; keep wash_banner() and _mcl_on_wash callback. 2021-11-10 12:52:04 -08:00
Phaethon H daa9354d32 Revert to master branch (roll back changes to mcl_potions). 2021-11-10 12:48:51 -08:00
Phaethon H a72359af4d Revert to master branch's version (rolling back changes to mcl_buckets). 2021-11-10 12:46:31 -08:00
Phaethon H cc35ebede0 Merge branch 'master' into PhaethonH/api-cauldrons-wip+unittest 2021-11-10 12:44:15 -08:00
Phaethon H 4e6f06619b Fix river water bucket interaction.
Added explicits tests for river water bucket.
Minor updates to code comments.
2021-11-10 12:36:10 -08:00
Phaethon H c43223ad59 Moved dosubfile() to init.lua (mode-wide availablility)
Finer-grained describe()/it() in test spec.
2021-11-10 12:02:37 -08:00
Phaethon H c0dea55862 proxy items operate on itemstack to accomodate banner interacting with cauldron (washing banner).
_mcl_on_wash callback in mcl_banners for interaction with cauldron.
2021-11-10 04:09:33 -08:00
Phaethon H 2f9fea5a5c Remove cauldron-specific code from mcl_buckets, now handled by cauldron's item proxy for bucket. 2021-11-10 03:07:49 -08:00
Phaethon H 9d69f7e9e4 Full lava cauldron is light source. 2021-11-10 03:07:35 -08:00
Phaethon H 9b0178f9c0 Removed cauldron-specific code in mcl_potions, now handled by cauldron item proxy for bottles. 2021-11-10 02:56:27 -08:00
Phaethon H 11ce6c70e9 Temporarily disable banner interaction until the banner items are sorted out. 2021-11-10 02:54:43 -08:00
Phaethon H 88d95cd87d Proxy item rules use sound_specifier function.
Added sound for lava-based interaction with cauldron.
2021-11-10 02:29:13 -08:00
Phaethon H f7c0848163 added playing sound on cauldron interaction with bucket, and bottle. 2021-11-10 01:45:37 -08:00
Phaethon H f4b9bc379b Move out river-water cauldron as an extension. 2021-11-10 00:56:51 -08:00
Phaethon H 8271397f48 Mock mclx_core for unit test.
Lava cauldron extension, with lava buckets interaction.
2021-11-10 00:51:41 -08:00
Phaethon H aa4fbcffa2 Migrate banners interaction to rule-based proxy items.
Removed older cauldron-interaction prototypes.
2021-11-09 23:22:18 -08:00
Phaethon H ceee755bab Migrate bottles-proxy interaction to be rule-based proxy item. 2021-11-09 23:14:20 -08:00
Phaethon H 34eff4a6a7 Generalized rule-based proxy item as a prototype. 2021-11-09 23:03:54 -08:00
Phaethon H 5a0cad372f Handle bucket interaction with cauldron as FSM. 2021-11-09 22:41:37 -08:00
iliekprogrammar f61143758e
Fix small typo in API.md 2021-11-10 12:54:28 +08:00
Phaethon H 09d6174cf6 Change ItemProxy to ItemVariantProxy to be more stateful. 2021-11-09 14:55:20 -08:00
Phaethon H 0df6e4c57b Remove deprecated functions.
Fix spelling mistakes.
2021-11-09 14:19:45 -08:00
Phaethon H 47ca9e76b7 Leverage recursive-merge to reduce nodebox overrides.
Replace member() with table_find().
2021-11-09 14:15:11 -08:00
Phaethon H b3797ce5de Item proxy objects to represent cauldron interactions with items. 2021-11-09 14:04:33 -08:00
Phaethon H ea16d7cdf3 Trimming down bucket-proxy handling. 2021-11-09 12:00:13 -08:00
iliekprogrammar fa22ec4dd0
Add helper functions to update/merge tables. 2021-11-10 02:50:49 +08:00
Lizzy Fleckenstein a7bc460fae
Fix boat and enchanting book texture glitches / warnings 2021-11-09 17:39:39 +01:00
Phaethon H dddf4912bd Move handling item proxies to a pattern of registry and add_...(). 2021-11-08 17:08:36 -08:00
Phaethon H 5177a955f5 Symbolic constants for item names.
"verify_...()" -> "assert_...()"
2021-11-08 17:08:00 -08:00
iliekprogrammar 0491b814dd Merge pull request 'Simple bone meal API (Fix #1862)' (#1892) from NO11/MineClone2:bone_meal into master
Reviewed-on: MineClone2/MineClone2#1892
2021-11-08 16:53:01 +00:00
NO11 51dffc6c53 Merge branch 'master' into bone_meal 2021-11-08 13:16:39 +00:00
NO11 30ce6f8a77 Fix typo min -> max 2021-11-08 13:16:20 +00:00
Lizzy Fleckenstein a34ae040c8
Add ContentDB links 2021-11-08 14:02:22 +01:00
Lizzy Fleckenstein a410a7fabe Merge pull request 'Add OpenCollective link and credits' (#1893) from EliasFleckenstein03/MineClone2:opencollective into master
Reviewed-on: MineClone2/MineClone2#1893
2021-11-08 11:48:10 +00:00
Phaethon H 4f521903c0 Banner interaction. 2021-11-07 21:21:57 -08:00
Phaethon H 507e907c77 Clean up the way the unit test looks (hopefully easier to read). 2021-11-07 16:51:34 -08:00
Phaethon H 6874591951 rather byzantine, but principle of unit test in place. 2021-11-07 12:30:59 -08:00
Lizzy Fleckenstein ea46c8741b
Add OpenCollective link and credits 2021-11-07 20:29:11 +01:00
Phaethon H 7ac997d33d Reduce node-definition callbacks to just on_rightclick()
Added "bucket-like" namespace of functions for cauldron interacting with bucket.
Added unit tests for bucket/cauldron interactions.
2021-11-06 21:31:00 -07:00
Phaethon H 4a1d037590 Merge branch 'PhaethonH/api-cauldrons-wip' into PhaethonH/api-cauldrons-wip+unittest 2021-11-06 17:31:46 -07:00
Phaethon H 05a9968081 Adjust callbacks to operate from on_rightclick (delegated from potions and buckets). 2021-11-06 17:25:39 -07:00
Phaethon H 91fe85fd1b rename api functions to flowlib style node_FOO(node) and FOO(pos). 2021-11-06 13:20:35 -07:00
NO11 5b52deaa8a Merge branch 'master' into bone_meal 2021-11-06 13:38:20 +00:00
NO11 7bbc1e9951 Add API.md for bone meal API 2021-11-06 13:34:22 +00:00
Lizzy Fleckenstein 0dd780ee2a Merge pull request 'Implement grass palette conversion, Add sugar cane colorisation' (#1889) from EliasFleckenstein03/MineClone2:tint into master
Reviewed-on: MineClone2/MineClone2#1889
2021-11-06 13:29:39 +00:00
NO11 1b259f928b Add simple bone meal API
- callback api
- particle api
2021-11-06 13:12:03 +00:00
Phaethon H 48eb27655a API changes to facilitiate lava/new-substances cauldron. 2021-11-06 04:21:58 -07:00
Phaethon H a3de5b7cb0 Move cauldron nodebox preset table outside of function, to be built once instead of with each function call.
Skip intermediate variable for returns.
Added mcl_cauldrons.get_substance() to get substance from cauldron.
Added explicit list of cauldron substances.
2021-11-06 00:11:35 -07:00
Phaethon H 5b03c604b3 Update banner dipping into cauldron with new cauldron names/behavior.
Update weather/rain filling cauldrons with new cauldron names/behavior.
set_level() determining current substance.
2021-11-05 19:22:51 -07:00
Phaethon H 407903b73a Renaming some cauldron-associated symbols.
Item group comparision against 0 instead of greater than.
Cauldron callbacks assigned function references instead of functions defined in-place.
Short-hand invocation of callback with default alternate value.
local helper function merge_tables made recursive to allow selective subkey override/substituion.
Cauldron fill heights determined by table lookup instead of cascading "if"s.
Trimming some dead code.
2021-11-05 17:18:30 -07:00
Lizzy Fleckenstein 970988cb39
Add sugar cane colorisation 2021-11-03 19:58:50 +01:00
Lizzy Fleckenstein 34f329a9d5
TextureConverter: Implement grass palette conversion
Source: https://minecraft.fandom.com/wiki/Tint
Since the MineClone2 biomes do not entirely match with the Minecraft ones I picked the Minecraft biomes that seem to match them best.
This also changes the palette index of the nether to match the desert instead of the mesa biome
and changes the color of grass blocks in item form to the default minecraft one.
2021-11-03 19:57:21 +01:00
Phaethon H 6131f941b7 Unit-testing with mineunit. 2021-10-26 11:45:49 -07:00
Phaethon H ad46840fa9 Allow filling up glass bottles from cauldrons with river water.
Play sounds for filling bottles from water sources.
Play water_source sound for taking bucket of river water from cauldron.
Remove redundant check to skip building water source due to filling cauldron.
2021-10-25 14:31:24 -07:00
Phaethon H 3b9c553b6c Styling edits. 2021-10-24 20:54:11 -07:00
Phaethon H 6ed5865aff Fix spelling mistake in variable name. 2021-10-24 16:10:52 -07:00
Phaethon H fd8182c74c Wire in documentation aliases for the filled cauldrons.
Delete commented out code.
Change names of variables to be more clear of purpose.

Slightly cleaner logic getting bucket from cauldron.
Play sounds when bucket takes from cauldron.
Guard against entirety of bucket placing water source if cauldron filled successfully (avoid empty if branch).

Adjust cauldron collision box to make it possible to jump/fall inside.
Minimally functioning state: water cauldron extinguish entity on fire.

Fix interaction of bottles with water sources and cauldrons:
 * take and give 1/3 level from/to cauldrons (creative mode and survival mode inventory changse).
 * take from mcl_core:water_source

Removed utils.lua (contents were moved into register.lua).
2021-10-24 15:37:43 -07:00
Phaethon H 2775f89979 Cauldron API, callback-based; changes to mcl_buckets and mcl_potions for interaction with water cauldron. 2021-10-22 10:30:33 -07:00
AFCMS 1b0b5fc733 Merge branch 'master' into api-cauldrons 2021-05-27 09:10:19 +02:00
AFCMS 246ece7440 Merge branch 'master' into api-cauldrons 2021-05-19 00:20:08 +02:00
AFCMS 5c078b3d7b Merge branch 'master' into api-cauldrons 2021-05-04 10:29:34 +02:00
AFCMS 3fddffb942 Merge branch 'master' into api-cauldrons 2021-05-03 23:27:09 +02:00
AFCMS 225650904c Merge branch 'master' into api-cauldrons 2021-03-31 20:33:18 +02:00
AFCMS 1b144d507d Merge branch 'master' into api-cauldrons 2021-03-31 16:15:53 +02:00
AFCMS 738519f0a4 Merge branch 'master' into api-cauldrons 2021-03-30 22:28:33 +02:00
AFCMS d1ca012ea0 small API fix 2021-03-27 12:27:46 +01:00
AFCMS a18a2127c5 add helper function to give item in creative 2021-03-27 12:20:14 +01:00
AFCMS 7156afcd03 add helper functio nto give item 2021-03-27 12:16:08 +01:00
AFCMS e3b3e2343b fix crash 2021-03-27 12:08:00 +01:00
AFCMS 6f23351126 move every function in the right place 2021-03-27 12:06:52 +01:00
AFCMS a99619774d move more functions 2021-03-27 12:03:14 +01:00
AFCMS 989384f362 move is_cauldron to the right place 2021-03-27 11:57:07 +01:00
AFCMS 69da355f96 fix API design 2021-03-27 11:55:22 +01:00
AFCMS 297d4e707c WIP separate functions into different files 2021-03-27 11:52:54 +01:00
AFCMS b4033ddfc9 Merge branch 'master' into api-cauldrons 2021-03-27 11:35:24 +01:00
AFCMS c35c8a1713 Merge branch 'master' into api-cauldrons 2021-03-19 15:12:54 +01:00
AFCMS 64b49ba719 try to fix give function 2021-03-19 14:58:25 +01:00
AFCMS d00490c86f fix stupid crash 2021-03-19 14:27:57 +01:00
AFCMS 8c91d23eb8 fix give function 2021-03-19 14:26:43 +01:00
AFCMS 294cb3eb7d cleanup 2021-03-19 14:17:59 +01:00
AFCMS cf4d57e584 fix crash 2021-03-19 14:13:34 +01:00
AFCMS 890b95bb49 Merge branch 'master' into api-cauldrons 2021-03-19 13:39:17 +01:00
AFCMS 8611d27f1d init function 2021-03-19 10:23:22 +01:00
AFCMS 59892abf6d fix 2021-03-19 10:19:08 +01:00
AFCMS e69365f44d init give function 2021-03-19 10:18:15 +01:00
AFCMS aa0c7bd2cf Merge branch 'master' into api-cauldrons 2021-03-19 10:16:27 +01:00
AFCMS 2b73c16c69 fix doc 2 2021-03-18 20:58:06 +01:00
AFCMS 36f479b2ec cleanup 2021-03-18 20:52:24 +01:00
AFCMS 717a18ef16 fix doc 2021-03-18 20:50:37 +01:00
AFCMS 9b0c87dabc fixes 2021-03-18 19:43:25 +01:00
AFCMS 3c83898095 quick fix 2021-03-18 19:30:53 +01:00
AFCMS 6ebae965f6 fixes 2021-03-18 19:27:43 +01:00
AFCMS 5e5b3ebf75 WIP API for mcl_cauldrons 2021-03-18 19:18:35 +01:00
AFCMS e68736a040 WIP 2021-03-18 18:02:08 +01:00
AFCMS 72149a2c56 part 2 2021-03-18 09:43:31 +01:00
AFCMS 182e825303 init mcl_cauldrons API (WIP) 2021-03-17 21:47:27 +01:00
40 changed files with 5108 additions and 210 deletions

View File

@ -32,6 +32,8 @@ playerbase on low spec computers, optimizations are hard to investigate.
* [Matrix](https://app.element.io/#/room/#mc2:matrix.org)
* [Reddit](https://www.reddit.com/r/MineClone2/)
* [Minetest forums](https://forum.minetest.net/viewtopic.php?f=50&t=16407)
* [ContentDB](https://content.minetest.net/packages/wuzzy/mineclone2/)
* [OpenCollective](https://opencollective.com/mineclone2)
## Using git
MineClone2 is developed using the version control system
@ -194,9 +196,16 @@ MeseHub, let us know what you think about a topic and help us make
decisions. Also, note that a lot of discussion takes place on the
Discord server, so it's definitely worth checking it out.
### Funding
You can help pay for our infrastructure (Mesehub) by donating to our
OpenCollective link (See Links section).
### Crediting
If you opened or have contributed to an issue, you receive the
`Community` role on our Discord (after asking for it).
OpenCollective Funders are credited in their own section in
`CREDITS.md` and receive a special role "Funder" on our discord (unless
they have made their donation Incognito).
## How you can help as a programmer
(Almost) all the MineClone2 development is done using pull requests.

View File

@ -130,6 +130,9 @@
* todoporlalibertad
* Marcin Serwin
## Funders
* 40W
## Special thanks
* celeron55 for creating Minetest
* Jordach for the jukebox music compilation from Big Freaking Dig

View File

@ -85,6 +85,8 @@ The MineClone2 repository is hosted at Mesehub. To contribute or report issues,
* Matrix: <https://app.element.io/#/room/#mc2:matrix.org>
* Reddit: <https://www.reddit.com/r/MineClone2/>
* Minetest forums: <https://forum.minetest.net/viewtopic.php?f=50&t=16407>
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
* OpenCollective: <https://opencollective.com/mineclone2>
## Target
- Crucially, create a stable, moddable, free/libre clone of Minecraft

View File

@ -1,5 +1,27 @@
mcl_util = {}
-- Updates all values in t using values from to*.
function table.update(t, ...)
for _, to in ipairs{...} do
for k,v in pairs(to) do
t[k] = v
end
end
return t
end
-- Updates nil values in t using values from to*.
function table.update_nil(t, ...)
for _, to in ipairs{...} do
for k,v in pairs(to) do
if t[k] == nil then
t[k] = v
end
end
end
return t
end
-- Based on minetest.rotate_and_place
--[[

View File

@ -12,7 +12,7 @@ Params:
* pos: position
## mcl_worlds.y_to_layer(y)
## mcl_worlds.y_to_layer(y)
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
Mainly used for ore generation.
Takes an Y coordinate as input and returns:

View File

@ -115,7 +115,7 @@ local boat = {
collisionbox = {-0.5, -0.35, -0.5, 0.5, 0.3, 0.5},
visual = "mesh",
mesh = "mcl_boats_boat.b3d",
textures = {"mcl_boats_texture_oak_boat.png"},
textures = {"mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png", "mcl_boats_texture_oak_boat.png"},
visual_size = boat_visual_size,
hp_max = boat_max_hp,
damage_texture_modifier = "^[colorize:white:0",
@ -148,6 +148,11 @@ function boat.on_activate(self, staticdata, dtime_s)
self._v = data.v
self._last_v = self._v
self._itemstring = data.itemstring
while #data.textures < 5 do
table.insert(data.textures, data.textures[1])
end
self.object:set_properties({textures = data.textures})
end
end
@ -434,8 +439,9 @@ for b=1, #boat_ids do
pos = vector.add(pos, vector.multiply(dir, boat_y_offset_ground))
end
local boat = minetest.add_entity(pos, "mcl_boats:boat")
local texture = "mcl_boats_texture_"..images[b].."_boat.png"
boat:get_luaentity()._itemstring = itemstring
boat:set_properties({textures = { "mcl_boats_texture_"..images[b].."_boat.png" }})
boat:set_properties({textures = { texture, texture, texture, texture, texture }})
boat:set_yaw(placer:get_look_horizontal())
if not minetest.is_creative_enabled(placer:get_player_name()) then
itemstack:take_item()

View File

@ -228,22 +228,20 @@ if mcl_weather.allow_abm then
-- Slowly fill up cauldrons
minetest.register_abm({
label = "Rain fills cauldrons with water",
nodenames = {"mcl_cauldrons:cauldron", "mcl_cauldrons:cauldron_1", "mcl_cauldrons:cauldron_2"},
nodenames = {"group:cauldron"},
interval = 56.0,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
-- Rain is equivalent to a water bottle
if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
if node.name == "mcl_cauldrons:cauldron" then
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_1"})
elseif node.name == "mcl_cauldrons:cauldron_1" then
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_2"})
elseif node.name == "mcl_cauldrons:cauldron_2" then
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3"})
elseif node.name == "mcl_cauldrons:cauldron_1r" then
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_2r"})
elseif node.name == "mcl_cauldrons:cauldron_2r" then
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3r"})
local newnode
if mcl_cauldrons.is_cauldron(node) then
if mcl_cauldrons.is_empty(node) or mcl_cauldrons.has_substance(node, "cauldron_water") then
newnode = mcl_cauldrons.fill_levels(node, 1, "cauldron_water")
end
end
if newnode then
minetest.set_node(pos, newnode)
end
end
end

View File

@ -132,6 +132,9 @@ return {
"todoporlalibertad",
"Marcin Serwin",
}},
{S("Funders"), 0xF7FF00, {
"40W",
}},
{S("Special thanks"), 0x00E9FF, {
"celeron55 for creating Minetest",
"Jordach for the jukebox music compilation from Big Freaking Dig",

View File

@ -348,6 +348,23 @@ minetest.register_node("mcl_banners:hanging_banner", {
end,
})
local function wash_banner(itemstack)
local imeta = itemstack:get_meta()
local layers_raw = imeta:get_string("layers")
local layers = minetest.deserialize(layers_raw)
if type(layers) == "table" and #layers > 0 then
table.remove(layers)
imeta:set_string("layers", minetest.serialize(layers))
local newdesc = mcl_banners.make_advanced_banner_description(itemstack:get_definition().description, layers)
local mname = imeta:get_string("name")
-- Don't change description if item has a name
if mname == "" then
imeta:set_string("description", newdesc)
end
return itemstack
end
end
-- for pattern_name, pattern in pairs(patterns) do
for colorid, colortab in pairs(mcl_banners.colors) do
for i, pattern_name in ipairs(pattern_names) do
@ -425,6 +442,10 @@ for colorid, colortab in pairs(mcl_banners.colors) do
groups = groups,
stack_max = 16,
_mcl_on_wash = function(itemstack)
return wash_banner(itemstack)
end,
on_place = function(itemstack, placer, pointed_thing)
local above = pointed_thing.above
local under = pointed_thing.under

View File

@ -0,0 +1,88 @@
# `mcl_cauldrons`
This mod add an API to add cauldrons to mcl.
Node definitions
----------------
Extensions to the base node definitions (as passed to `minetest.register_node()`) for cauldrons:
* `.groups.cauldron`: 1 - is a cauldron
* `.groups.cauldron_filled`: number 0..3 - content level (0=empty, 3=full)
* `.groups.cauldron_maximum`: number 1.. - maximum contents level
* `._mcl_cauldron_substance`: string - content of cauldron (nil for empty)
### `mcl_cauldrons.registered_cauldrons`
Table containing cauldron definitions, indexed by name.
### `mcl_cauldrons.register_cauldron_node(nodename, cauldron_def)`
Cauldron interactions
---------------------
### `mcl_cauldrons.find_cauldron(substance, fill_level)`
Returns table, { node\_name, node\_definition }, returns nil if no match.
Find registered cauldron best matching substance and fill level.
* `substance`: string, one of the groups keys qualifying the content (e.g. `"cauldron_water"`); empty string for empty cauldron.
* `fill_level`: desired fill level of cauldron; `nil` for first/any match; 0 resolves to empty cauldron.
### `mcl_cauldrons.is_cauldron(node)`
Returns boolean.
Test is nodename describes a cauldron.
* `node`: Minetest node { name, param1, param2 }
### `mcl_cauldrons.is_empty(node)`
Returns boolean.
Test cauldron is empty.
* `node`: Minetest node { name, param1, param2 }
### `mcl_cauldrons.is_full(node)`
Returns boolean.
Test cauldron is full.
* `node`: Minetest node { name, param1, param2 }
### `mcl_cauldrons.has_substance(node, substance)`
Returns boolean.
Test the content is a certain substance.
* `node`: Minetest node { name, param1, param2 }
* `substance`: string, one of the groups key qualifying the content (e.g. `"cauldron_water"`).
### `mcl_cauldrons.get_maximum(node, substance)`
Returns number.
Get the maximum number of levels for requested substance.
* `node`: Minetest node { name, param1, param2 }
* `substance`: string, group-key qualifying the content (e.g. `"cauldron_water"`); `nil` for any/do-not-care.
### `mcl_cauldrons.get_level(node, substance)`
Returns number.
Get the fill level of the requested substance.
* `node`: Minetest node { name, param1, param2 }
* `substance`: string, group-key qualifying the content (e.g. `"cauldron_water"`); `nil` for any/do-not-care.
### `mcl_cauldrons.set_level(node, fill_level, substance)`
Returns node { name, param1, param2 }.
Set the fill level of cauldron, with optional explicit substance.
* `node`: Minetest node { name, param1, param2 }
* `substance`: string, group-key of cauldron content; `nil` to preserve substance.
### `mcl_cauldrons.fill_levels(node, change_levels, substance)`
Returns node { name, param1, param2 }.
Increase levels in cauldron, constrained by game rules.
* `node`: Minetest node { name, param1, param2 }
* `substance`: string, group-key of cauldron content; `nil` to preserve substance.
### `mcl_cauldrons.drain_levels(nodename, change_levels, substance)`
Returns node { name, param1, param2 }.
Reduce levels in cauldron, constrained by game rules.
* `node`: Minetest node { name, param1, param2 }
* `substance`: string, group-key of cauldron content; `nil` to preserve substance.
### `mcl_cauldrons.register_cauldron_node(nodename, cauldron_definition, doc_alias)`
Register cauldron's node defintion. Affects `mcl_cauldrons.registered_cauldrons` while ultimately ending up at `minetest.register_node`.
* `nodename`: string - name to assign to registered node.
* `cauldron_defintion`: table - node definition (expecting mcl\_cauldrons extensions).
* `doc_alias`: string - documentation (in-game encyclopedia) entry associated with this definition.

View File

@ -0,0 +1,354 @@
--- API for Cauldrons in MineClone2.
-- Documentation Generator is LDoc (backwards-compatible with LuaDoc)
-- $ ldoc -f markdown ...
--- Table of registered cauldron nodes, indexed by name.
-- Filled by register_cauldron_node().
mcl_cauldrons.registered_cauldrons = {}
--- List of recognized substances in cauldron.
-- Filled by register_cauldron_node()
-- @see mcl_cauldrons.list_substances
mcl_cauldrons.substances = {}
--- Provided for documentation purposes, frequently-used table format.
--
-- Position in world { x, y, z }
-- @name Position
-- @class table
local Position = { x=nil, y=nil, z=nil }
--- Provided for documentation purposes, frequently-used table format.
--
-- Node description { name, param1, param2 }, as used by `minetest.get_node()` and `minetest.set_node()`
-- (not to be confused with node definition)
-- @name Node
-- @class table
local Node = { name=nil, param1=nil, param2=nil }
-- symbolic constants.
--- Levels of cauldron change per bucket.
mcl_cauldrons.BUCKET_FILL_LEVELS = 3
--- Levels of cauldron change per bottle (potion).
mcl_cauldrons.BOTTLE_FILL_LEVELS = 1
--- Determine registered cauldron satisfying specifications.
--
-- Cauldrons are described by two state variables: { substance, fill\_level }.
--
-- Determine the cauldron node definition from these states.
--
-- e.g. `make_cauldron("water", 2)` -> `"mcl_cauldrons:cauldron_water_2"`
--
-- @param substance string : Desired cauldron substance, nil for any substance.
-- @param fill_level number : Desired cauldron fill level, nil to match any.
-- @return 2-value: cauldron node name (string), cauldron node definition (table)
function mcl_cauldrons.make_cauldron(substance, fill_level)
local empty_nodename = "mcl_cauldrons:cauldron"
local empty_nodedef = mcl_cauldrons.registered_cauldrons[empty_nodename]
if (substance == "") then
return empty_nodename, empty_nodedef
end
for nodename, nodedef in pairs(mcl_cauldrons.registered_cauldrons) do
-- matched on substance: 'any', or exact match from .groups.
local match_substance = (substance == nil) or (nodedef._mcl_cauldron_substance == substance)
-- matched on fill_level: 'any', or exact match from .groups.
local match_level = (fill_level == nil) or (nodedef.groups.cauldron_filled == fill_level)
if match_substance and match_level then
return nodename, nodedef
end
end
-- no match, use empty.
return empty_nodename, empty_nodedef
end
--- Test: node describes a cauldron
--
-- @param node Node : Cauldron node description (as from minetest.get_node())
-- @return boolean: node is a cauldron
function mcl_cauldrons.node_is_cauldron(node)
if node == nil then return false end
return (minetest.get_item_group(node.name, "cauldron") ~= 0)
end
--- Test: position holds a cauldron
--
-- @param pos Position : World position (as passed to minetest.get_node())
-- @return boolean: a cauldron is at position
function mcl_cauldrons.is_cauldron(pos)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_is_cauldron(node)
end
--- Test: cauldron node contains specified substance
--
-- @param node Node : Cauldron node description
-- @param substance string : substance that should be present in cauldron.
-- @return boolean
function mcl_cauldrons.node_has_substance(node, substance)
--return (minetest.get_item_group(node.name, substance) ~= 0)
local nodedef = minetest.registered_nodes[node.name]
return (nodedef._mcl_cauldron_substance == substance)
end
--- Test: cauldron at position contains specified substance
--
-- @param pos Position : World position
-- @param substance string : substance that should be present in cauldron
-- @return boolean
function mcl_cauldrons.has_substance(pos, substance)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_has_substance(node)
end
--- List of supported substances
--
-- @return table: List of substances
function mcl_cauldrons.list_substances()
return mcl_cauldrons.substances
end
--- Get substance inside cauldron node
--
-- @param node Node : Cauldron node description
-- @return string : Substance inside cauldron
-- @return `nil`: Cauldron is empty
function mcl_cauldrons.node_get_substance(node)
local nodedef = mcl_cauldrons.registered_cauldrons[node.name]
return nodedef._mcl_cauldron_substance
end
--- Get substance inside cauldron (by position)
--
-- @param pos Position : World position
-- @return string: Substance inside cauldron
-- @return `nil`: Cauldron is empty
function mcl_cauldrons.get_substance(pos)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_get_substance(node)
end
--- Get maximum number of levels available for cauldron node -> number
function mcl_cauldrons.node_get_maximum(node)
local nodename = node.name
return minetest.get_item_group(nodename, "cauldron_maximum")
end
function mcl_cauldrons.get_maximum(pos)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_get_maximum(node)
end
--- Get current level from cauldron node
--
-- @param node Node : Cauldron node description
-- @return number: Typically one of 0,1,2,3
function mcl_cauldrons.node_get_level(node)
return minetest.get_item_group(node.name, "cauldron_filled")
end
--- Get current level of cauldron (by position)
--
-- @param pos Position : World position
-- @return number: Typically one of 0,1,2,3
-- Get current level of cauldron -> number
function mcl_cauldrons.get_level(pos)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_get_level(node)
end
--- Test: cauldron node is empty
--
-- @param node Node : Cauldron node description
-- @return boolean
function mcl_cauldrons.node_is_empty(node)
if not mcl_cauldrons.node_is_cauldron(node) then
error("not a cauldron", node.name)
end
if minetest.get_item_group(node.name, "cauldron_filled") == 0 then
return true
end
return false
end
--- Test: cauldron is empty (by position)
--
-- @param pos Position : World position
-- @return boolean
function mcl_cauldrons.is_empty(pos)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_is_empty(node)
end
--- Test: cauldron node is full
--
-- @param node Node : Cauldron node description
-- @return boolean
function mcl_cauldrons.node_is_full(node)
local maximum = mcl_cauldrons.get_maximum(node)
local current = mcl_cauldrons.get_level(node)
return (current >= maximum)
end
--- Test: cauldron is full (by position)
--
-- @param pos Position : World position
-- @return boolean
function mcl_cauldrons.is_full(pos)
local node = minetest.get_node(pos)
return mcl_cauldrons.node_is_full(node)
end
--- Convert cauldron node to satisfy content specifications.
--
-- Cauldron specification is subject to normalization based on substance
-- and fill level (e.g. any substance at level 0 becomes empty cauldron).
--
-- @param node Node : node to have its contents changed
-- @param substance string : intended cauldron substance
-- @param fill_level number : intended fill level
-- @return table: new cauldron node
function mcl_cauldrons.node_set_content(node, substance, fill_level)
local newname = mcl_cauldrons.make_cauldron(substance, fill_level)
local newnode = table.copy(node)
newnode.name = newname
return newnode
end
--- Place cauldron of specified substance and fill level at world position.
--
-- Cauldron specification is subject to normalization based on substance
-- and fill level (e.g. any substance at level 0 becomes empty cauldron).
--
-- @param pos Position : World position.
-- @param substance : string cauldron substance (item def _mcl_cauldron_substance)
-- @param fill_level number : cauldron fill level (typically 0,1,2,3)
-- @return table: new cauldron node (as passed to minetest.set_pos())
function mcl_cauldrons.set_content(pos, substance, fill_level)
local node = minetest.get_node(pos)
if not mcl_cauldrons.is_cauldron(node) then return end
local newnode = mcl_cauldrons.node_set_content(node, substance, fill_level)
minetest.set_node(pos, newnode)
return newnode
end
--- Wrapper to register cauldron-specific nodes.
-- additional bookkeeping specific to mcl_cauldrons.
-- @param nodename string : name of node
-- @param nodedef table : node description
-- @param doc_alias string : for documentation purposes, alias for this specified node name.
function mcl_cauldrons.register_cauldron_node(nodename, nodedef, doc_alias)
mcl_cauldrons.registered_cauldrons[nodename] = nodedef
minetest.register_node(nodename, nodedef)
if doc_alias and has_doc then
doc.add_entry_alias("nodes", doc_alias, "nodes", nodename)
end
-- build list of supported substances.
if table.indexof(mcl_cauldrons.substances, nodedef._mcl_cauldron_substance) == -1 then
table.insert(mcl_cauldrons.substances, nodedef._mcl_cauldron_substance)
end
end
--- List of item proxy classes.
--
-- For item interactions (right-click), handlers for items that may interact
-- with cauldron.
mcl_cauldrons.item_proxies = {}
--- Add item proxy class.
--
-- ItemProxy classes expose `do_apply()` that carries out the duties of
-- `on_rightclick()` (applying itemstack to cauldron node). This method
-- returns 2 values, a boolean and an ItemStack.
-- The first value is true if no further handling is required (typically
-- interaction is complete), false if it could not handle the interaction.
-- Priority of proxy classes are determined by lateness in `add_item_proxy` --
-- later additions take priority over earlier ones, allowing for stacked mods
-- to override basal behavior.
--
-- @param proxy ItemProxy : ItemProxy prototype (to have do_apply() invoked on it)
function mcl_cauldrons.add_item_proxy(proxy)
if table.indexof(mcl_cauldrons.item_proxies, proxy) == -1 then
-- insert at front so newer proxies override older ones.
table.insert(mcl_cauldrons.item_proxies, 1, proxy)
end
end
--- Invoke applying item to cauldron node.
--
-- carry out all the duties of on_rightclick().
-- @return ItemStack: item to be wielded in hand (may be same `itemstack`).
function mcl_cauldrons.apply_item(pos, node, user, itemstack)
for _, item_handler in ipairs(mcl_cauldrons.item_proxies) do
local handled, retval = item_handler:do_apply(pos, node, user, itemstack)
if handled then return retval end
end
return itemstack
end
function mcl_cauldrons.node_fill_levels(node, change_levels, substance)
-- base rules for filling cauldrons.
if change_levels <= 0 then
-- no change.
return nil
end
local old_level = mcl_cauldrons.node_get_level(node)
local whole, fraction = math.modf(change_levels)
local new_level
if fraction > 0 then
if math.random() < fraction then
whole = whole + 1
end
end
if (substance and mcl_cauldrons.node_has_substance(node, substance)) or (substance == nil) then
-- same substance, add more.
new_level = old_level + whole
else
-- different substance, delete old content.
new_level = whole
end
-- clamp 0 through 3 inclusive.
new_level = math.max(0, math.min(new_level, 3))
if substance == nil then
substance = mcl_cauldrons.node_get_substance(node)
end
return mcl_cauldrons.node_set_content(node, substance, new_level)
end
function mcl_cauldrons.node_drain_levels(node, change_levels, substance)
-- base rules for draining cauldrons.
if change_levels <= 0 then
-- no change.
return nil
end
if (substance and not mcl_cauldrons.node_has_substance(node, substance)) then
-- different substance, cannot drain.
return nil
end
local old_level = mcl_cauldrons.node_get_level(node)
local whole, fraction = math.modf(change_levels)
local new_level
if fraction > 0 then
if math.random() < fraction then
whole = whole + 1
end
end
if (whole > old_level) then
-- insufficient amount, no change.
return nil
end
-- same substance, drain.
new_level = old_level - whole
-- clamp 0 through 3 inclusive.
new_level = math.max(0, math.min(new_level, 3))
if substance == nil then
substance = mcl_cauldrons.node_get_substance(node)
end
return mcl_cauldrons.node_set_content(node, substance, new_level)
end

View File

@ -0,0 +1,72 @@
-- lava cauldron as extension of empty cauldron.
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_lava_1",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron"], {
description = S("Cauldron (1/3 Lava)"),
groups = {
pickaxey = 1,
cauldron = 1,
cauldron_filled = 1,
cauldron_maximum = 3,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[1],
collision_box = mcl_cauldrons.cauldron_nodeboxes[0],
tiles = {
[1]="(default_lava_source_animated.png^[verticalframe:16:0)^mcl_cauldrons_cauldron_top.png",
},
drop = "mcl_cauldrons:cauldron",
_mcl_cauldron_substance = "lava",
}), "mcl_cauldrons:cauldron")
-- lava cauldron 2/3 as extension of 1/3.
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_lava_2",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron_lava_1"], {
description = S("Cauldron (2/3 Lava)"),
groups = {
cauldron_filled = 2,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[2],
}), "mcl_cauldrons:cauldron")
-- lava cauldron 3/3 as extension of 1/3.
local LIGHT_LAVA = minetest.registered_nodes["mcl_core:lava_source"].light_source
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_lava_3",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron_lava_1"], {
description = S("Cauldron (3/3 Lava)"),
groups = {
cauldron_filled = 3,
},
paramtype = "light",
light_source = LIGHT_LAVA,
node_box = mcl_cauldrons.cauldron_nodeboxes[3],
}), "mcl_cauldrons:cauldron")
-- Bucket interaction, lava.
local BucketProxy = mcl_cauldrons.BucketProxy
function BucketProxy.is_lava(itemstack)
if itemstack:get_name() == "mcl_buckets:bucket_lava" then return true end
-- other tests.
end
function BucketProxy:sound_lava(new_itemstack, new_cauldron_node)
local nodedef = minetest.registered_nodes["mcl_core:lava_source"]
if self.is_empty(new_itemstack) then
-- lava placed.
return nodedef.sounds.place, { pitch = 1.0 + math.random(-10, 10) * .005 }
else
-- lava taken.
return nodedef.sounds.dug, { pitch = 1.0 + math.random(-10, 10) * .005 }
end
end
function BucketProxy.make_lava(old_itemstack, cauldron_node)
return ItemStack("mcl_buckets:bucket_lava")
end
BucketProxy:add_bucket_rules("lava", BucketProxy.is_lava, BucketProxy.make_lava, BucketProxy.sound_lava)

View File

@ -0,0 +1,75 @@
-- river water cauldron as extension of empty cauldron.
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_river_water_1",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron"], {
description = S("Cauldron (1/3 River Water)"),
groups = {
pickaxey = 1,
cauldron = 1,
cauldron_filled = 1,
cauldron_maximum = 3,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[1],
collision_box = mcl_cauldrons.cauldron_nodeboxes[0],
tiles = {
[1]="(default_river_water_source_animated.png^[verticalframe:16:0)^mcl_cauldrons_cauldron_top.png",
},
drop = "mcl_cauldrons:cauldron",
_mcl_cauldron_substance = "river_water",
}), "mcl_cauldrons:cauldron")
-- water cauldron 2/3 as extension of 1/3.
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_river_water_2",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron_river_water_1"], {
description = S("Cauldron (2/3 River Water)"),
groups = {
cauldron_filled = 2,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[2],
}), "mcl_cauldrons:cauldron")
-- water cauldron 3/3 as extension of 1/3.
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_river_water_3",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron_river_water_1"], {
description = S("Cauldron (3/3 River Water)"),
groups = {
cauldron_filled = 3,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[3],
}), "mcl_cauldrons:cauldron")
-- bucket interactions, river water.
local BucketProxy = mcl_cauldrons.BucketProxy
function BucketProxy.is_river_water(itemstack)
if (itemstack:get_name() == "mcl_buckets:bucket_river_water") then return true end
-- other tests.
end
function BucketProxy.make_river_water(itemstack, cauldron_node)
for scan_name, scan_def in pairs(minetest.registered_items) do
-- TODO: expand mcl_buckets node definitions.
end
end
BucketProxy:add_bucket_rules("river_water", BucketProxy.is_river_water, "mcl_buckets:bucket_river_water", BucketProxy.sound_water)
-- bottle interactions, river water.
local BottleProxy = mcl_cauldrons.BottleProxy
function BottleProxy.is_river_water(itemstack)
if (itemstack:get_name() == "mcl_potions:river_water") then return true end
-- other tests.
end
function BottleProxy.make_river_water(old_itemstack, cauldron_node)
return ItemStack("mcl_potions:river_water")
end
BottleProxy:add_bottle_rules("river_water", BottleProxy.is_river_water, "mcl_potions:bottle_river_water", BottleProxy.sound_water)

View File

@ -0,0 +1,8 @@
-- LDoc (LuaDoc derivative) configuration; not a linker script.
-- $ luarocks install --local ldoc markdown
project = 'mcl_cauldrons'
file = { 'api.lua', 'cauldron_lava.lua', 'cauldron_river_water.lua', 'init.lua', 'register.lua' }
all = true
format = 'markdown'

View File

@ -0,0 +1,69 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>Reference</title>
<link rel="stylesheet" href="ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>mcl_cauldrons</h1>
<h2>Modules</h2>
<ul class="nowrap">
<li><a href="modules/api.html">api</a></li>
<li><a href="modules/init.html">init</a></li>
<li><a href="modules/register.html">register</a></li>
</ul>
</div>
<div id="content">
<h2>Modules</h2>
<table class="module_list">
<tr>
<td class="name" nowrap><a href="modules/api.html">api</a></td>
<td class="summary">API for Cauldrons in MineClone2.</td>
</tr>
<tr>
<td class="name" nowrap><a href="modules/init.html">init</a></td>
<td class="summary">Initialization (primary entry point) for mcl_cauldrons.</td>
</tr>
<tr>
<td class="name" nowrap><a href="modules/register.html">register</a></td>
<td class="summary">Base cauldron types.</td>
</tr>
</table>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2021-11-12 11:20:21 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,303 @@
/* BEGIN RESET
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.2r1
*/
html {
color: #000;
background: #FFF;
}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset,img {
border: 0;
}
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
font-style: inherit;
font-weight: inherit;
}
del,ins {
text-decoration: none;
}
li {
margin-left: 20px;
}
caption,th {
text-align: left;
}
h1,h2,h3,h4,h5,h6 {
font-size: 100%;
font-weight: bold;
}
q:before,q:after {
content: '';
}
abbr,acronym {
border: 0;
font-variant: normal;
}
sup {
vertical-align: baseline;
}
sub {
vertical-align: baseline;
}
legend {
color: #000;
}
input,button,textarea,select,optgroup,option {
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-weight: inherit;
}
input,button,textarea,select {*font-size:100%;
}
/* END RESET */
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; font-size: 1.1em; }
span.parameter { font-family:monospace; }
span.parameter:after { content:":"; }
span.types:before { content:"("; }
span.types:after { content:")"; }
.type { font-weight: bold; font-style:italic }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 0px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 20px 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid #C0C0C0; /* silver */
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
pre.example {
font-size: .85em;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #f0f0f0;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
background-color: #f0f0f0;
border-left: 2px solid #cccccc;
}
#navigation {
float: left;
width: 14em;
vertical-align: top;
background-color: #f0f0f0;
overflow: visible;
}
#navigation h2 {
background-color:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
padding: 1em;
width: 700px;
border-left: 2px solid #cccccc;
border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about {
clear: both;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; min-width: 200px; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f0f0f0; min-width: 200px; }
table.function_list td.summary { width: 100%; }
ul.nowrap {
overflow:auto;
white-space:nowrap;
}
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
/* styles for prettification of source */
pre .comment { color: #558817; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #aa5050; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #8080ff; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #800080; }
pre .user-keyword { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }

View File

@ -0,0 +1,905 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>Reference</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>mcl_cauldrons</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#Functions">Functions</a></li>
<li><a href="#Tables">Tables</a></li>
<li><a href="#Fields">Fields</a></li>
</ul>
<h2>Modules</h2>
<ul class="nowrap">
<li><strong>api</strong></li>
<li><a href="../modules/init.html">init</a></li>
<li><a href="../modules/register.html">register</a></li>
</ul>
</div>
<div id="content">
<h1>Module <code>api</code></h1>
<p>API for Cauldrons in MineClone2.</p>
<p>
</p>
<h2><a href="#Functions">Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.make_cauldron">mcl_cauldrons.make_cauldron (substance, fill_level)</a></td>
<td class="summary">Determine registered cauldron satisfying specifications.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_is_cauldron">mcl_cauldrons.node_is_cauldron (node)</a></td>
<td class="summary">Test: node describes a cauldron</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.is_cauldron">mcl_cauldrons.is_cauldron (pos)</a></td>
<td class="summary">Test: position holds a cauldron</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_has_substance">mcl_cauldrons.node_has_substance (node, substance)</a></td>
<td class="summary">Test: cauldron node contains specified substance</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.has_substance">mcl_cauldrons.has_substance (pos, substance)</a></td>
<td class="summary">Test: cauldron at position contains specified substance</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.list_substances">mcl_cauldrons.list_substances ()</a></td>
<td class="summary">List of supported substances</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_get_substance">mcl_cauldrons.node_get_substance (node)</a></td>
<td class="summary">Get substance inside cauldron node</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.get_substance">mcl_cauldrons.get_substance (pos)</a></td>
<td class="summary">Get substance inside cauldron (by position)</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_get_maximum">mcl_cauldrons.node_get_maximum (node)</a></td>
<td class="summary">Get maximum number of levels available for cauldron node -> number</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_get_level">mcl_cauldrons.node_get_level (node)</a></td>
<td class="summary">Get current level from cauldron node</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.get_level">mcl_cauldrons.get_level (pos)</a></td>
<td class="summary">Get current level of cauldron (by position)</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_is_empty">mcl_cauldrons.node_is_empty (node)</a></td>
<td class="summary">Test: cauldron node is empty</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.is_empty">mcl_cauldrons.is_empty (pos)</a></td>
<td class="summary">Test: cauldron is empty (by position)</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_is_full">mcl_cauldrons.node_is_full (node)</a></td>
<td class="summary">Test: cauldron node is full</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.is_full">mcl_cauldrons.is_full (pos)</a></td>
<td class="summary">Test: cauldron is full (by position)</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.node_set_content">mcl_cauldrons.node_set_content (node, substance, fill_level)</a></td>
<td class="summary">Convert cauldron node to satisfy content specifications.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.set_content">mcl_cauldrons.set_content (pos, substance, fill_level)</a></td>
<td class="summary">Place cauldron of specified substance and fill level at world position.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.register_cauldron_node">mcl_cauldrons.register_cauldron_node (nodename, nodedef, doc_alias)</a></td>
<td class="summary">Wrapper to register cauldron-specific nodes.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.add_item_proxy">mcl_cauldrons.add_item_proxy (proxy)</a></td>
<td class="summary">Add item proxy class.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.apply_item">mcl_cauldrons.apply_item (pos, node, user, itemstack)</a></td>
<td class="summary">Invoke applying item to cauldron node.</td>
</tr>
</table>
<h2><a href="#Tables">Tables</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.registered_cauldrons">mcl_cauldrons.registered_cauldrons</a></td>
<td class="summary">Table of registered cauldron nodes, indexed by name.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.substances">mcl_cauldrons.substances</a></td>
<td class="summary">List of recognized substances in cauldron.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#Position">Position</a></td>
<td class="summary">Provided for documentation purposes, frequently-used table format.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#Node">Node</a></td>
<td class="summary">Provided for documentation purposes, frequently-used table format.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.item_proxies">mcl_cauldrons.item_proxies</a></td>
<td class="summary">List of item proxy classes.</td>
</tr>
</table>
<h2><a href="#Fields">Fields</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.BUCKET_FILL_LEVELS">mcl_cauldrons.BUCKET_FILL_LEVELS</a></td>
<td class="summary">Levels of cauldron change per bucket.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons.BOTTLE_FILL_LEVELS">mcl_cauldrons.BOTTLE_FILL_LEVELS</a></td>
<td class="summary">Levels of cauldron change per bottle (potion).</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
<dl class="function">
<dt>
<a name = "mcl_cauldrons.make_cauldron"></a>
<strong>mcl_cauldrons.make_cauldron (substance, fill_level)</strong>
</dt>
<dd>
Determine registered cauldron satisfying specifications. </p>
<p> Cauldrons are described by two state variables: { substance, fill_level }.</p>
<p> Determine the cauldron node definition from these states.</p>
<p> e.g. <code>make_cauldron(&quot;water&quot;, 2)</code> -> <code>&quot;mcl_cauldrons:cauldron_water_2&quot;</code>
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">substance</span>
string : Desired cauldron substance, nil for any substance.
</li>
<li><span class="parameter">fill_level</span>
number : Desired cauldron fill level, nil to match any.
</li>
</ul>
<h3>Returns:</h3>
<ol>
2-value: cauldron node name (string), cauldron node definition (table)
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_is_cauldron"></a>
<strong>mcl_cauldrons.node_is_cauldron (node)</strong>
</dt>
<dd>
Test: node describes a cauldron
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : Cauldron node description (as from minetest.get_node())
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean: node is a cauldron
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.is_cauldron"></a>
<strong>mcl_cauldrons.is_cauldron (pos)</strong>
</dt>
<dd>
Test: position holds a cauldron
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position (as passed to minetest.get_node())
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean: a cauldron is at position
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_has_substance"></a>
<strong>mcl_cauldrons.node_has_substance (node, substance)</strong>
</dt>
<dd>
Test: cauldron node contains specified substance
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : Cauldron node description
</li>
<li><span class="parameter">substance</span>
string : substance that should be present in cauldron.
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.has_substance"></a>
<strong>mcl_cauldrons.has_substance (pos, substance)</strong>
</dt>
<dd>
Test: cauldron at position contains specified substance
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position
</li>
<li><span class="parameter">substance</span>
string : substance that should be present in cauldron
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.list_substances"></a>
<strong>mcl_cauldrons.list_substances ()</strong>
</dt>
<dd>
List of supported substances
<h3>Returns:</h3>
<ol>
table: List of substances
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_get_substance"></a>
<strong>mcl_cauldrons.node_get_substance (node)</strong>
</dt>
<dd>
Get substance inside cauldron node
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : Cauldron node description
</li>
</ul>
<h3>Returns:</h3>
<ol>
<li>
string : Substance inside cauldron</li>
<li>
<code>nil</code>: Cauldron is empty</li>
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.get_substance"></a>
<strong>mcl_cauldrons.get_substance (pos)</strong>
</dt>
<dd>
Get substance inside cauldron (by position)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position
</li>
</ul>
<h3>Returns:</h3>
<ol>
<li>
string: Substance inside cauldron</li>
<li>
<code>nil</code>: Cauldron is empty</li>
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_get_maximum"></a>
<strong>mcl_cauldrons.node_get_maximum (node)</strong>
</dt>
<dd>
Get maximum number of levels available for cauldron node -> number
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
</li>
</ul>
</dd>
<dt>
<a name = "mcl_cauldrons.node_get_level"></a>
<strong>mcl_cauldrons.node_get_level (node)</strong>
</dt>
<dd>
Get current level from cauldron node
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : Cauldron node description
</li>
</ul>
<h3>Returns:</h3>
<ol>
number: Typically one of 0,1,2,3
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.get_level"></a>
<strong>mcl_cauldrons.get_level (pos)</strong>
</dt>
<dd>
Get current level of cauldron (by position)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position
</li>
</ul>
<h3>Returns:</h3>
<ol>
number: Typically one of 0,1,2,3
Get current level of cauldron -> number
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_is_empty"></a>
<strong>mcl_cauldrons.node_is_empty (node)</strong>
</dt>
<dd>
Test: cauldron node is empty
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : Cauldron node description
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.is_empty"></a>
<strong>mcl_cauldrons.is_empty (pos)</strong>
</dt>
<dd>
Test: cauldron is empty (by position)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_is_full"></a>
<strong>mcl_cauldrons.node_is_full (node)</strong>
</dt>
<dd>
Test: cauldron node is full
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : Cauldron node description
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.is_full"></a>
<strong>mcl_cauldrons.is_full (pos)</strong>
</dt>
<dd>
Test: cauldron is full (by position)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position
</li>
</ul>
<h3>Returns:</h3>
<ol>
boolean
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.node_set_content"></a>
<strong>mcl_cauldrons.node_set_content (node, substance, fill_level)</strong>
</dt>
<dd>
Convert cauldron node to satisfy content specifications. </p>
<p> Cauldron specification is subject to normalization based on substance
and fill level (e.g. any substance at level 0 becomes empty cauldron).
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">node</span>
Node : node to have its contents changed
</li>
<li><span class="parameter">substance</span>
string : intended cauldron substance
</li>
<li><span class="parameter">fill_level</span>
number : intended fill level
</li>
</ul>
<h3>Returns:</h3>
<ol>
table: new cauldron node
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.set_content"></a>
<strong>mcl_cauldrons.set_content (pos, substance, fill_level)</strong>
</dt>
<dd>
Place cauldron of specified substance and fill level at world position. </p>
<p> Cauldron specification is subject to normalization based on substance
and fill level (e.g. any substance at level 0 becomes empty cauldron).
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
Position : World position.
</li>
<li><span class="parameter">substance</span>
: string cauldron substance (item def <em>mcl</em>cauldron_substance)
</li>
<li><span class="parameter">fill_level</span>
number : cauldron fill level (typically 0,1,2,3)
</li>
</ul>
<h3>Returns:</h3>
<ol>
table: new cauldron node (as passed to minetest.set_pos())
</ol>
</dd>
<dt>
<a name = "mcl_cauldrons.register_cauldron_node"></a>
<strong>mcl_cauldrons.register_cauldron_node (nodename, nodedef, doc_alias)</strong>
</dt>
<dd>
Wrapper to register cauldron-specific nodes.
additional bookkeeping specific to mcl_cauldrons.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">nodename</span>
string : name of node
</li>
<li><span class="parameter">nodedef</span>
table : node description
</li>
<li><span class="parameter">doc_alias</span>
string : for documentation purposes, alias for this specified node name.
</li>
</ul>
</dd>
<dt>
<a name = "mcl_cauldrons.add_item_proxy"></a>
<strong>mcl_cauldrons.add_item_proxy (proxy)</strong>
</dt>
<dd>
Add item proxy class. </p>
<p> ItemProxy classes expose <code>do_apply()</code> that carries out the duties of
<code>on_rightclick()</code> (applying itemstack to cauldron node). This method
returns 2 values, a boolean and an ItemStack.
The first value is true if no further handling is required (typically
interaction is complete), false if it could not handle the interaction.
Priority of proxy classes are determined by lateness in <a href="../modules/api.html#mcl_cauldrons.add_item_proxy">add_item_proxy</a> --
later additions take priority over earlier ones, allowing for stacked mods
to override basal behavior.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">proxy</span>
ItemProxy : ItemProxy prototype (to have do_apply() invoked on it)
</li>
</ul>
</dd>
<dt>
<a name = "mcl_cauldrons.apply_item"></a>
<strong>mcl_cauldrons.apply_item (pos, node, user, itemstack)</strong>
</dt>
<dd>
Invoke applying item to cauldron node. </p>
<p> carry out all the duties of on_rightclick().
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">pos</span>
</li>
<li><span class="parameter">node</span>
</li>
<li><span class="parameter">user</span>
</li>
<li><span class="parameter">itemstack</span>
</li>
</ul>
<h3>Returns:</h3>
<ol>
ItemStack: item to be wielded in hand (may be same <code>itemstack</code>).
</ol>
</dd>
</dl>
<h2 class="section-header "><a name="Tables"></a>Tables</h2>
<dl class="function">
<dt>
<a name = "mcl_cauldrons.registered_cauldrons"></a>
<strong>mcl_cauldrons.registered_cauldrons</strong>
</dt>
<dd>
Table of registered cauldron nodes, indexed by name.
Filled by register<em>cauldron</em>node().
</dd>
<dt>
<a name = "mcl_cauldrons.substances"></a>
<strong>mcl_cauldrons.substances</strong>
</dt>
<dd>
List of recognized substances in cauldron.
Filled by register<em>cauldron</em>node()
<h3>See also:</h3>
<ul>
<a href="../modules/api.html#mcl_cauldrons.list_substances">mcl_cauldrons.list_substances</a>
</ul>
</dd>
<dt>
<a name = "Position"></a>
<strong>Position</strong>
</dt>
<dd>
Provided for documentation purposes, frequently-used table format. </p>
<p> Position in world { x, y, z }
<h3>Fields:</h3>
<ul>
<li><span class="parameter">x</span>
</li>
<li><span class="parameter">y</span>
</li>
<li><span class="parameter">z</span>
</li>
</ul>
</dd>
<dt>
<a name = "Node"></a>
<strong>Node</strong>
</dt>
<dd>
Provided for documentation purposes, frequently-used table format. </p>
<p> Node description { name, param1, param2 }, as used by <code>minetest.get_node()</code> and <code>minetest.set_node()</code>
(not to be confused with node definition)
<h3>Fields:</h3>
<ul>
<li><span class="parameter">name</span>
</li>
<li><span class="parameter">param1</span>
</li>
<li><span class="parameter">param2</span>
</li>
</ul>
</dd>
<dt>
<a name = "mcl_cauldrons.item_proxies"></a>
<strong>mcl_cauldrons.item_proxies</strong>
</dt>
<dd>
List of item proxy classes. </p>
<p> For item interactions (right-click), handlers for items that may interact
with cauldron.
</dd>
</dl>
<h2 class="section-header "><a name="Fields"></a>Fields</h2>
<dl class="function">
<dt>
<a name = "mcl_cauldrons.BUCKET_FILL_LEVELS"></a>
<strong>mcl_cauldrons.BUCKET_FILL_LEVELS</strong>
</dt>
<dd>
Levels of cauldron change per bucket.
</dd>
<dt>
<a name = "mcl_cauldrons.BOTTLE_FILL_LEVELS"></a>
<strong>mcl_cauldrons.BOTTLE_FILL_LEVELS</strong>
</dt>
<dd>
Levels of cauldron change per bottle (potion).
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2021-11-12 11:20:21 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

View File

@ -0,0 +1,288 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>Reference</title>
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>mcl_cauldrons</h1>
<ul>
<li><a href="../index.html">Index</a></li>
</ul>
<h2>Contents</h2>
<ul>
<li><a href="#Tables">Tables</a></li>
<li><a href="#Fields">Fields</a></li>
<li><a href="#Local_Functions">Local Functions</a></li>
</ul>
<h2>Modules</h2>
<ul class="nowrap">
<li><a href="../modules/api.html">api</a></li>
<li><strong>init</strong></li>
<li><a href="../modules/register.html">register</a></li>
</ul>
</div>
<div id="content">
<h1>Module <code>init</code></h1>
<p>Initialization (primary entry point) for mcl_cauldrons.</p>
<p> Adds cauldrons to MineClone2.</p>
<h2><a href="#Tables">Tables</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#mcl_cauldrons">mcl_cauldrons</a></td>
<td class="summary">Mod namespace.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#MODENV">MODENV</a></td>
<td class="summary">mod's environment (preserve references to mod's locals for nested dofile)</td>
</tr>
</table>
<h2><a href="#Fields">Fields</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#S">S</a></td>
<td class="summary">Common Minetest idiom for text/string translation function.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#modpath">modpath</a></td>
<td class="summary">Path of the current Minetest mod.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#modname">modname</a></td>
<td class="summary">Name of current mod.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#modpath">modpath</a></td>
<td class="summary">Path of current mod.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#mclx_core">mclx_core</a></td>
<td class="summary">Path to <a href="../modules/init.html#mclx_core">mclx_core</a> (test <a href="../modules/init.html#mclx_core">mclx_core</a> is loaded)</td>
</tr>
<tr>
<td class="name" nowrap><a href="#has_doc">has_doc</a></td>
<td class="summary">Path to <code>doc</code> (test <code>doc</code> is loaded)</td>
</tr>
</table>
<h2><a href="#Local_Functions">Local Functions</a></h2>
<table class="function_list">
<tr>
<td class="name" nowrap><a href="#dosubfile">dosubfile (relpath)</a></td>
<td class="summary">Execute dofile using mod's environment.</td>
</tr>
</table>
<br/>
<br/>
<h2 class="section-header "><a name="Tables"></a>Tables</h2>
<dl class="function">
<dt>
<a name = "mcl_cauldrons"></a>
<strong>mcl_cauldrons</strong>
</dt>
<dd>
Mod namespace.
</dd>
<dt>
<a name = "MODENV"></a>
<strong>MODENV</strong>
</dt>
<dd>
mod's environment (preserve references to mod's locals for nested dofile)
<h3>Fields:</h3>
<ul>
<li><span class="parameter">S</span>
Idiomatic function for text translation
</li>
<li><span class="parameter">modpath</span>
path of current mod
</li>
<li><span class="parameter">modname</span>
name of current mod
</li>
<li><span class="parameter">mclx_core</span>
path to mclx_core (<a href="../modules/init.html#mclx_core">mclx_core</a> is loaded)
</li>
<li><span class="parameter">has_doc</span>
path to doc (<code>doc</code> is loaded)
</li>
</ul>
</dd>
</dl>
<h2 class="section-header "><a name="Fields"></a>Fields</h2>
<dl class="function">
<dt>
<a name = "S"></a>
<strong>S</strong>
</dt>
<dd>
Common Minetest idiom for text/string translation function.
</dd>
<dt>
<a name = "modpath"></a>
<strong>modpath</strong>
</dt>
<dd>
Path of the current Minetest mod.
</dd>
<dt>
<a name = "modname"></a>
<strong>modname</strong>
</dt>
<dd>
Name of current mod.
</dd>
<dt>
<a name = "modpath"></a>
<strong>modpath</strong>
</dt>
<dd>
Path of current mod.
</dd>
<dt>
<a name = "mclx_core"></a>
<strong>mclx_core</strong>
</dt>
<dd>
Path to <a href="../modules/init.html#mclx_core">mclx_core</a> (test <a href="../modules/init.html#mclx_core">mclx_core</a> is loaded)
</dd>
<dt>
<a name = "has_doc"></a>
<strong>has_doc</strong>
</dt>
<dd>
Path to <code>doc</code> (test <code>doc</code> is loaded)
</dd>
</dl>
<h2 class="section-header "><a name="Local_Functions"></a>Local Functions</h2>
<dl class="function">
<dt>
<a name = "dosubfile"></a>
<strong>dosubfile (relpath)</strong>
</dt>
<dd>
Execute dofile using mod's environment. </p>
<p> The environment provided to the the dofile() is that of the current mod's.
Present init.lua locals at the same level for other files in this mod.
(roughly emulates #include behavior)
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">relpath</span>
string: path to a file under current mod path
</li>
</ul>
<h3>Returns:</h3>
<ol>
whatever dofile() returns.
</ol>
</dd>
</dl>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2021-11-12 11:20:21 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,143 +1,66 @@
--- Initialization (primary entry point) for mcl_cauldrons.
-- Adds cauldrons to MineClone2.
--- Common Minetest idiom for text/string translation function.
local S = minetest.get_translator(minetest.get_current_modname())
--- Path of the current Minetest mod.
local modpath = minetest.get_modpath(minetest.get_current_modname())
-- Cauldron mod, adds cauldrons.
--- Mod namespace.
mcl_cauldrons = {}
-- TODO: Extinguish fire of burning entities
-- Convenience function because the cauldron nodeboxes are very similar
local create_cauldron_nodebox = function(water_level)
local floor_y
if water_level == 0 then -- empty
floor_y = -0.1875
elseif water_level == 1 then -- 1/3 filled
floor_y = 1/16
elseif water_level == 2 then -- 2/3 filled
floor_y = 4/16
elseif water_level == 3 then -- full
floor_y = 7/16
end
return {
type = "fixed",
fixed = {
{-0.5, -0.1875, -0.5, -0.375, 0.5, 0.5}, -- Left wall
{0.375, -0.1875, -0.5, 0.5, 0.5, 0.5}, -- Right wall
{-0.375, -0.1875, 0.375, 0.375, 0.5, 0.5}, -- Back wall
{-0.375, -0.1875, -0.5, 0.375, 0.5, -0.375}, -- Front wall
{-0.5, -0.3125, -0.5, 0.5, floor_y, 0.5}, -- Floor
{-0.5, -0.5, -0.5, -0.375, -0.3125, -0.25}, -- Left front foot, part 1
{-0.375, -0.5, -0.5, -0.25, -0.3125, -0.375}, -- Left front foot, part 2
{-0.5, -0.5, 0.25, -0.375, -0.3125, 0.5}, -- Left back foot, part 1
{-0.375, -0.5, 0.375, -0.25, -0.3125, 0.5}, -- Left back foot, part 2
{0.375, -0.5, 0.25, 0.5, -0.3125, 0.5}, -- Right back foot, part 1
{0.25, -0.5, 0.375, 0.375, -0.3125, 0.5}, -- Right back foot, part 2
{0.375, -0.5, -0.5, 0.5, -0.3125, -0.25}, -- Right front foot, part 1
{0.25, -0.5, -0.5, 0.375, -0.3125, -0.375}, -- Right front foot, part 2
}
--- Name of current mod.
local modname = minetest.get_current_modname()
--- Path of current mod.
local modpath = minetest.get_modpath(modname)
--- Path to `mclx_core` (test `mclx_core` is loaded)
local mclx_core = minetest.get_modpath("mclx_core")
--- Path to `doc` (test `doc` is loaded)
local has_doc = minetest.get_modpath("doc")
--- mod's environment (preserve references to mod's locals for nested dofile)
-- @field S Idiomatic function for text translation
-- @field modpath path of current mod
-- @field modname name of current mod
-- @field mclx_core path to mclx_core (`mclx_core` is loaded)
-- @field has_doc path to doc (`doc` is loaded)
local MODENV = {
S = S,
modpath = modpath,
modname = modname,
mclx_core = mclx_core,
has_doc = has_doc,
}
MODENV.MODENV = MODENV
setmetatable(MODENV, {__index=_G}) -- chain to global.
--- Execute dofile using mod's environment.
--
-- The environment provided to the the dofile() is that of the current mod's.
-- Present init.lua locals at the same level for other files in this mod.
-- (roughly emulates #include behavior)
--
-- @param relpath string: path to a file under current mod path
-- @return whatever dofile() returns.
local function dosubfile(relpath)
-- present mod-local environment to nested subfile.
if setfenv then
-- Lua 5.1
local submodule, err = loadfile(modpath .. "/" .. relpath)
if err then
error(err)
end
local cauldron_nodeboxes = {}
for w=0,3 do
cauldron_nodeboxes[w] = create_cauldron_nodebox(w)
end
-- Empty cauldron
minetest.register_node("mcl_cauldrons:cauldron", {
description = S("Cauldron"),
_tt_help = S("Stores water"),
_doc_items_longdesc = S("Cauldrons are used to store water and slowly fill up under rain."),
_doc_items_usagehelp = S("Place a water bucket into the cauldron to fill it with water. Place an empty bucket on a full cauldron to retrieve the water. Place a water bottle into the cauldron to fill the cauldron to one third with water. Place a glass bottle in a cauldron with water to retrieve one third of the water."),
wield_image = "mcl_cauldrons_cauldron.png",
inventory_image = "mcl_cauldrons_cauldron.png",
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
drawtype = "nodebox",
paramtype = "light",
is_ground_content = false,
groups = {pickaxey=1, deco_block=1, cauldron=1},
node_box = cauldron_nodeboxes[0],
selection_box = { type = "regular" },
tiles = {
"mcl_cauldrons_cauldron_inner.png^mcl_cauldrons_cauldron_top.png",
"mcl_cauldrons_cauldron_inner.png^mcl_cauldrons_cauldron_bottom.png",
"mcl_cauldrons_cauldron_side.png"
},
sounds = mcl_sounds.node_sound_metal_defaults(),
_mcl_hardness = 2,
_mcl_blast_resistance = 2,
})
-- Template function for cauldrons with water
local register_filled_cauldron = function(water_level, description, river_water)
local id = "mcl_cauldrons:cauldron_"..water_level
local water_tex
if river_water then
id = id .. "r"
water_tex = "default_river_water_source_animated.png^[verticalframe:16:0"
setfenv(submodule, MODENV)
return submodule()
else
water_tex = "default_water_source_animated.png^[verticalframe:16:0"
end
minetest.register_node(id, {
description = description,
_doc_items_create_entry = false,
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
drawtype = "nodebox",
paramtype = "light",
is_ground_content = false,
groups = {pickaxey=1, not_in_creative_inventory=1, cauldron=(1+water_level), cauldron_filled=water_level, comparator_signal=water_level},
node_box = cauldron_nodeboxes[water_level],
collision_box = cauldron_nodeboxes[0],
selection_box = { type = "regular" },
tiles = {
"("..water_tex..")^mcl_cauldrons_cauldron_top.png",
"mcl_cauldrons_cauldron_inner.png^mcl_cauldrons_cauldron_bottom.png",
"mcl_cauldrons_cauldron_side.png"
},
sounds = mcl_sounds.node_sound_metal_defaults(),
drop = "mcl_cauldrons:cauldron",
_mcl_hardness = 2,
_mcl_blast_resistance = 2,
})
-- Add entry aliases for the Help
if minetest.get_modpath("doc") then
doc.add_entry_alias("nodes", "mcl_cauldrons:cauldron", "nodes", id)
-- Lua 5.2+
return dofile(modpath .. "/" .. relpath, "bt", MODENV)
end
end
-- Filled cauldrons (3 levels)
register_filled_cauldron(1, S("Cauldron (1/3 Water)"))
register_filled_cauldron(2, S("Cauldron (2/3 Water)"))
register_filled_cauldron(3, S("Cauldron (3/3 Water)"))
MODENV.dosubfile = dosubfile
if minetest.get_modpath("mclx_core") then
register_filled_cauldron(1, S("Cauldron (1/3 River Water)"), true)
register_filled_cauldron(2, S("Cauldron (2/3 River Water)"), true)
register_filled_cauldron(3, S("Cauldron (3/3 River Water)"), true)
end
minetest.register_craft({
output = "mcl_cauldrons:cauldron",
recipe = {
{ "mcl_core:iron_ingot", "", "mcl_core:iron_ingot" },
{ "mcl_core:iron_ingot", "", "mcl_core:iron_ingot" },
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" },
}
})
dofile(modpath.."/api.lua")
dosubfile("register.lua")
minetest.register_abm({
label = "cauldrons",
nodenames = {"group:cauldron_filled"},
interval = 0.5,
chance = 1,
action = function(pos, node)
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0.4)) do
if mcl_burning.is_burning(obj) then
mcl_burning.extinguish(obj)
local new_group = minetest.get_item_group(node.name, "cauldron_filled") - 1
minetest.swap_node(pos, {name = "mcl_cauldrons:cauldron" .. (new_group == 0 and "" or "_" .. new_group)})
break
end
end
end
})

View File

@ -1,3 +1,4 @@
name = mcl_cauldrons
depends = mcl_core, mcl_sounds
optional_depends = mclx_core, doc
description = Add cauldrons to mcl
depends = mcl_core, mcl_sounds, mcl_buckets
optional_depends = mclx_core, doc, mcl_potions

View File

@ -0,0 +1,699 @@
--- Base cauldron types.
--local S = minetest.get_translator(minetest.get_current_modname())
-- expected to be provided by setfenv/environment.
-- Convenience function because the cauldron nodeboxes are very similar
local floors_table = {
[0]=-0.1875, -- empty
[1]=1/16, -- 1/3 filled
[2]=4/16, -- 2/3 filled
[3]=7/16, -- full
}
local create_cauldron_nodebox = function(water_level)
local floor_y = floors_table[water_level]
return {
type = "fixed",
fixed = {
{-0.5, -0.1875, -0.5, -0.375, 0.5, 0.5}, -- Left wall
{0.375, -0.1875, -0.5, 0.5, 0.5, 0.5}, -- Right wall
{-0.375, -0.1875, 0.375, 0.375, 0.5, 0.5}, -- Back wall
{-0.375, -0.1875, -0.5, 0.375, 0.5, -0.375}, -- Front wall
{-0.5, -0.3125, -0.5, 0.5, floor_y, 0.5}, -- Floor
{-0.5, -0.5, -0.5, -0.375, -0.3125, -0.25}, -- Left front foot, part 1
{-0.375, -0.5, -0.5, -0.25, -0.3125, -0.375}, -- Left front foot, part 2
{-0.5, -0.5, 0.25, -0.375, -0.3125, 0.5}, -- Left back foot, part 1
{-0.375, -0.5, 0.375, -0.25, -0.3125, 0.5}, -- Left back foot, part 2
{0.375, -0.5, 0.25, 0.5, -0.3125, 0.5}, -- Right back foot, part 1
{0.25, -0.5, 0.375, 0.375, -0.3125, 0.5}, -- Right back foot, part 2
{0.375, -0.5, -0.5, 0.5, -0.3125, -0.25}, -- Right front foot, part 1
{0.25, -0.5, -0.5, 0.375, -0.3125, -0.375}, -- Right front foot, part 2
}
}
end
-- Generate nodeboxes for cauldrons, (0=empty, 1=1/3 filled, 2=2/3 filled, 3=full)
mcl_cauldrons.cauldron_nodeboxes = {}
for w=0,3 do
mcl_cauldrons.cauldron_nodeboxes[w] = create_cauldron_nodebox(w)
end
--- Merge tables of declarative objects.
--
-- The normal Lua means of inheritance uses setmetatable.
-- For node definitions, minetest.register_node() breaks/overwrite the metatable for its own use.
-- This function copies in the "old" keys/values, to let them be overwritten by another (newer) table.
-- Nested tables are recursively merged/copied, so nested keys may be selectively overridden.
-- Modifying the returned table does not risk mangling the source/parent table.
-- This workaround happens to work as declarative objects do not change after instantiation.
--
-- @return recursively-merged table of first and second arguments
local function merge_tables(a,b)
local result = {}
for k,v in pairs(a) do result[k]=v end
for k,v in pairs(b) do
if type(a[k]) == "table" then
-- recursive merge table (with copy).
result[k] = merge_tables(a[k], v)
else
result[k] = v
end
end
return result
end
MODENV.merge_tables = merge_tables
--------
--------
local ItemProxy = {}
--- Item Proxy (base prototype).
--
-- Interaction with items are done through a proxy that presume certain
-- behaviors of actual/registered items.
-- The idea is to avoid invoking mod-specific functions by name
-- (therefore relying on callbacks from node definition),
-- as mods may be extend, replace, or disable items.
--
-- Initial item proxies include:
--
-- * BucketProxy - interacting with bucket-like items (change fill level by 3)
-- * BottleProxy - interacting with bottle-like items (change fill level by 1)
-- * BannerProxy - banner-like items (wash banner, deplete 1 level)
mcl_cauldrons.ItemProxy = ItemProxy
function ItemProxy.__call(self, ...)
local child = {
__index = self,
__call = self.__call,
rules = {},
}
setmetatable(child, child)
return child
end
setmetatable(ItemProxy, ItemProxy)
--- Handle applying (right-clicking) item to cauldron node.
--
-- delegated by do_rightclick()
-- @param pos Position : position of (cauldron) node
-- @param node Node : cauldron node description
-- @param user UserRef : user using the item on cauldron node
-- @param itemstack ItemStack : item wielded by user
-- @return 2-value: apply succeded (boolean), itemstack to go in hand (ItemStack, may be same `itemstack`)
function ItemProxy:do_apply(pos, node, user, itemstack)
local newnode, newitemstack, sound_specifier
newnode, newitemstack, sound_specifier = self:apply_basic_rules(itemstack, node)
if not newnode and not newitemstack then
return false
end
-- TODO: fix the redundancy
local held_itemstack = self:common_exchange(pos, newnode, user, itemstack, newitemstack)
if sound_specifier then
local soundspec
local soundparm = {}
if type(sound_specifier) == "function" then
local spec_parm
soundspec, spec_parm = sound_specifier(self, newitemstack, node)
if not soundspec then return true, held_itemstack end -- shortcut
if spec_parm then
soundparm = table.copy(spec_parm)
end
else
soundspec = sound_specifier
end
if soundspec then
-- update position.
soundparm.pos = pos
minetest.sound_play(soundspec, soundparm, true)
end
end
return true, held_itemstack
end
--------
--------
local RuleBasedItemProxy = ItemProxy()
--- Item Proxy interaction with cauldron defined by rulesets.
mcl_cauldrons.RuleBasedItemProxy = RuleBasedItemProxy
--- prototype derivation to keep separate .rules
function RuleBasedItemProxy.__call(self, ...)
local child = {
__index = self,
__call = self.__call,
rules = {},
}
setmetatable(child, child)
return child
end
function RuleBasedItemProxy.add_basic_rule(self, match_substance, match_level, input_constraint, next_substance, next_level, output_specifier, sound_specifier)
end
--- common one-for-one item exchange
--
-- put new_node at pos, and in exchange take 1 from itemstack, and give 1 of new_item to user.
-- @return ItemStack: new itemstack to replace `itemstack` (may be itself with changes)
function RuleBasedItemProxy.common_exchange(self, pos, new_node, user, itemstack, new_itemstack)
if new_node ~= nil then
minetest.set_node(pos, new_node)
end
if type(new_itemstack) == "string" then
new_itemstack = ItemStack(new_itemstack)
end
if new_itemstack ~= nil then
if minetest.is_creative_enabled(user:get_player_name()) then
-- creative inventory: add new_itemstack if missing.
local inv = user:get_inventory()
if not inv:contains_item("main", new_itemstack) then
inv:add_item("main", new_itemstack)
end
else
-- survival.
if itemstack:get_count() == 1 then
-- swap with new item.
return new_itemstack
else
-- subtract one, deposit new_itemstack
itemstack:take_item(1)
local inv = user:get_inventory()
if inv:room_for_item("main", new_itemstack) then
-- add to inventory
inv:add_item("main", new_itemstack)
else
-- drop on ground.
minetest.add_item(user:get_pos(), new_itemstack)
end
end
end
end
return itemstack
end
--- Add item/cauldron exchange rule.
--
-- Rule following the basic cauldron fill/empty and bucket fill/empty pattern:
--
-- * cauldron consistes of a particular substance and specific level.
-- * cauldron turns into one of a particular substance and level.
-- * the one bucket used turns into one bucket of something else.
--
-- @param match_substance string : cauldron substance matching this rule (nil means empty)
-- @param match_level number : cauldron fill level matching this rule (nil means any, true maps to full)
-- @param input_constraint
-- * string => exact match with item name to trigger rule.
-- * function(ItemStack) => predicate given item stack, to trigger rule.
-- @param next_substance string : new cauldron content (nil means empty)
-- @param next_level number : new cauldron fill level (true means full, 0 means empty)
-- @param output_specifier
-- * string => specific item name to generate after successful rule match
-- * function(ItemStack, table) => given old item stack and new cauldron node, generate new item stack on rule match
-- @param sound_specifier
-- * string => specific item name to play 'place' sound on match rule.
-- * function(ItemStack, table) => given ItemStack and new cauldron node, return (soundspec, soundparam)
function RuleBasedItemProxy:add_basic_rule(match_substance, match_level, input_constraint, next_substance, next_level, output_specifier, sound_specifier)
local rule = {
match_substance = match_substance,
match_level = match_level,
input_constraint = input_constraint,
next_substance = next_substance,
next_level = next_level,
output_specifier = output_specifier,
sound_specifier = sound_specifier,
}
table.insert(self.rules, rule)
end
-- helper function for determining if a rule matches.
function RuleBasedItemProxy:_rule_match(rule, itemstack, cauldron_node)
if rule.match_substance ~= mcl_cauldrons.node_get_substance(cauldron_node) then
return
end
if rule.match_level ~= nil then
local current_level = mcl_cauldrons.node_get_level(cauldron_node)
-- `true` as alias for maximum.
if rule.match_level == true then
if current_level ~= mcl_cauldrons.BUCKET_FILL_LEVELS then
return
end
elseif current_level ~= rule.match_level then
return
end
end
-- else match any level.
if type(rule.input_constraint) == "string" then
-- require exact string match.
if itemstack:get_name() ~= rule.input_constraint then return end
elseif type(rule.input_constraint) == "function" then
-- require item predicate.
if not rule.input_constraint(itemstack) then return end
end
return true
end
--- Apply basic rules with given ItemStack and cauldron node.
--
-- apply basic (patterned) rules to the provided item stack and cauldron node.
-- @return 3-value: new cauldron node table, new item stack, sound_specifier
function RuleBasedItemProxy:apply_basic_rules(itemstack, cauldron_node)
local match_rule
for i, rule in ipairs(self.rules) do
if self:_rule_match(rule, itemstack, cauldron_node) then
match_rule = rule
break
end
end
if not match_rule then return end
-- cauldron transition.
local next_level = match_rule.next_level
if next_level == true then
-- `true` as alias for maximum.
next_level = mcl_cauldrons.BUCKET_FILL_LEVELS
end
local newnode = mcl_cauldrons.node_set_content(cauldron_node, match_rule.next_substance, next_level)
local newitemstack
if type(match_rule.output_specifier) == "string" then
-- one particular item
newitemstack = ItemStack(match_rule.output_specifier)
elseif type(match_rule.output_specifier) == "function" then
-- function.
newitemstack = match_rule.output_specifier(itemstack, newnode)
end
return newnode, newitemstack, match_rule.sound_specifier
end
-------
-------
-- Bucket(-like) item proxy.
local BucketProxy = mcl_cauldrons.RuleBasedItemProxy()
--- Bucket-like interaction with cauldron.
--
-- * empty bucket: requires full cauldron, produce something bucket, cauldron becomes empty.
-- * something bucket: requires empty cauldron, produce empty bucket, cauldron becomes full.
-- * something bucket: requires compatible-liquid cauldron, produce empty bucket, cauldron becomes full.
mcl_cauldrons.BucketProxy = BucketProxy
-- TODO: expand mcl_buckets node definitions to avoid relying on name matching.
--- Rule's `input_constraint` callback for empty bucket.
-- @param itemstack ItemStack : item stack to test.
function BucketProxy.is_empty(itemstack)
if (itemstack:get_name() == "mcl_buckets:bucket_empty") then return true end
-- other tests.
end
--- Rule's `input_constraint` callback for water bucket.
-- @param itemstack ItemStack : item stack to test.
function BucketProxy.is_water(itemstack)
if (itemstack:get_name() == "mcl_buckets:bucket_water") then return true end
-- other tests.
end
--- Rule's `sound_specifier` method for water bucket use.
-- @param new_itemstack ItemStack : item stack produced due to rule match
-- @param new_cauldron_node Node : cauldron node produced due to rule match
-- @return 2-value: sound specifier, sound parameter
function BucketProxy:sound_water(new_itemstack, new_cauldron_node)
-- water bucket.
local nodedef = minetest.registered_nodes["mcl_core:water_source"]
if self.is_empty(new_itemstack) then
-- water placed.
return nodedef.sounds.place, { pitch = 1 + math.random(-10, 10) * .005 }
else
-- water taken.
return nodedef.sounds.dug, { pitch = 1 + math.random(-10, 10) * .005 }
end
end
--- Rule's `output_specifier` callback for obtaining an empty bucket.
-- @param old_itemstack ItemStack : item stack provided as input
-- @param cauldron_node Node : cauldron node produced due to rule match.
-- @return ItemStack: item stack to be wielded by user
function BucketProxy.make_empty(old_itemstack, cauldron_node)
-- TODO: scan registered items for first matching "empty bucket" criteria
for scan_name, scan_def in pairs(minetest.registered_items) do
-- TODO: expand mcl_buckets node definitions.
end
end
--- Rule's `output_specifier` callback for obtaining a water bucket.
-- @param old_itemstack ItemStack : item stack provided as input
-- @param cauldron_node Node : cauldron node produced due to rule match.
-- @return ItemStack: item stack to be wielded by user
function BucketProxy.make_water(old_itemstack, cauldron_node)
-- TODO: scan registered items for first matching "empty bucket" criteria
for scan_name, scan_def in pairs(minetest.registered_items) do
-- TODO: expand mcl_buckets node definitions.
end
end
--- Patterned bucket rules.
--
-- Buckets fill to full, empty from full.
-- @param substance string : substance shared between bucket and cauldron
-- @param filled_input string/function : filled bucket given to cauldron
-- @param filled_output string/function : filled bucket taken from cauldron
-- @param sound_specifier string/function : rule's sound specifier
function BucketProxy:add_bucket_rules(substance, filled_input, filled_output, sound_specifier)
local empty = "mcl_buckets:bucket_empty"
local empty_output = "mcl_buckets:bucket_empty"
-- full stuff cauldron + empty bucket -> empty cauldron, full bucket
self:add_basic_rule(substance, true, empty, nil, 0, filled_output, sound_specifier)
-- any level stuff cauldron + stuff bucket -> full stuff cauldron, empty bucket (may waste bucket)
self:add_basic_rule(substance, nil, filled_input, substance, true, empty_output, sound_specifier)
-- empty cauldron + stuff bucket -> full stuff cauldron, empty bucket
self:add_basic_rule(nil, 0, filled_input, substance, true, empty_output, sound_specifier)
-- other combinations -> do nothing (no interaction)
end
BucketProxy:add_bucket_rules("water", BucketProxy.is_water, "mcl_buckets:bucket_water", BucketProxy.sound_water)
-------
-------
-- Bottle(-like) item proxy.
local BottleProxy = mcl_cauldrons.RuleBasedItemProxy()
--- Bottle-like interaction with Cauldron.
--
-- * empty bottle: requires non-empty cauldron, generate something bottle, reduce cauldron level by 1.
-- * something bottle: empty cauldron or same-liquid cauldron, generate empty bottle, increase cauldron level by 1.
mcl_cauldrons.BottleProxy = BottleProxy
-- TODO: expand mcl_potions to avoid relying on name comparisons.
--- Rule's `input_constraint` callback for empty bottle.
-- @param itemstack ItemStack : item stack to test.
-- @return boolean
function BottleProxy.is_empty(itemstack)
if (itemstack:get_name() == "mcl_potions:glass_bottle") then return true end
-- other tests.
end
--- Rule's `input_constraint` callback for water bottle.
-- @param itemstack ItemStack : item stack to test.
-- @return boolean
function BottleProxy.is_water(itemstack)
if (itemstack:get_name() == "mcl_potions:water") then return true end
-- other tests.
end
--- Rule's `sound_specifier` method for water bottle use.
-- @param new_itemstack ItemStack : item stack produced from rule matching
-- @param new_cauldron_node Node : cauldron node produced from rule matching
-- @return 2-value: sound specifier, sound parameter
function BottleProxy:sound_water(new_itemstack, new_cauldron_node)
if self.is_empty(new_itemstack) then
return "mcl_potions_bottle_pour"
else
return "mcl_potions_bottle_fill"
end
end
--- Rule's `output_specifier` function for obtaining empty bottle.
-- @param old_itemstack ItemStack : item stack as input to rule matching
-- @param cauldron_node Node : cauldron node produced from rule matching
-- @return ItemStack: item stack to be wielded by user
function BottleProxy.make_empty(old_itemstack, cauldron_node)
return ItemStack("mcl_potions:glass_bottle")
end
--- Rule's `output_specifier` function for obtaining water bottle.
-- @param old_itemstack ItemStack : item stack as input to rule matching
-- @param cauldron_node Node : cauldron node produced from rule matching
-- @return ItemStack: item stack to be wielded by user
function BottleProxy.make_water(old_itemstack, cauldron_node)
return ItemStack("mcl_potions:water")
end
--- Patterned bottle rules.
--
-- Bottles fill by 1 level, drain by 1 level, not wasted on full cauldron.
-- @param substance string : substance shared between bottle and cauldron.
-- @param filled_input string/function : filled bottle given to cauldron
-- @param filled_output string/function : filled bottle taken from cauldron
-- @param sound_specifier string/function : rule's sound specifier
function BottleProxy:add_bottle_rules(substance, filled_input, filled_output, sound_specifier)
local empty_input = self.is_empty
local empty_output = self.make_empty
-- stuff cauldron + empty bottle -> stuff cauldron less 1, water bottle
self:add_basic_rule(substance, 3, empty_input, substance, 2, filled_output, sound_specifier)
self:add_basic_rule(substance, 2, empty_input, substance, 1, filled_output, sound_specifier)
self:add_basic_rule(substance, 1, empty_input, nil, 0, filled_output, sound_specifier)
-- empty cauldron + stuff bottle -> stuff cauldron 1, empty bottle
self:add_basic_rule(nil, 0, filled_input, substance, 1, empty_output, sound_specifier)
-- stuff cauldron + stuff bottle -> stuff cauldron plus 1, empty bottle
self:add_basic_rule(substance, 1, filled_input, substance, 2, empty_output, sound_specifier)
self:add_basic_rule(substance, 2, filled_input, substance, 3, empty_output, sound_specifier)
-- full cauldron + stuff bottle -> do nothing (do not waste bottle)
end
BottleProxy:add_bottle_rules("water", BottleProxy.is_water, BottleProxy.make_water, BottleProxy.sound_water)
-------
-------
-- Banner item proxy
local BannerProxy = mcl_cauldrons.RuleBasedItemProxy()
--- Banner-like interaction with Cauldron.
--
-- * Apply to water cauldron to wash topmost layer.
-- * Applying causes water cauldron to drop 1 level.
mcl_cauldrons.BannerProxy = BannerProxy
-- TODO: better predicate tests
--- Rule's `input_constraint` function for banner.
-- @param itemstack ItemStack : item stack to test.
-- @return boolean
function BannerProxy.is_banner(itemstack)
if string.sub(itemstack:get_name(), 1, 12) == "mcl_banners:" then
return true
end
-- other tests.
end
--- Rule's `output_specifier` function for obtaining washed banner.
--
-- In the case of banners, the resulting itemstack is the old itemstack with metadata modification (handled by on_wash()).
-- @param old_itemstack ItemStack : item stack that was input to rule matching.
-- @param cauldron_node Node : cauldron node produced from rule matching.
-- @return ItemStack: to be the item wielded by user
function BannerProxy.wash_banner(old_itemstack, cauldron_node)
local nodedef = old_itemstack:get_definition()
local washed_itemstack
if nodedef._mcl_on_wash then
washed_itemstack = nodedef._mcl_on_wash(old_itemstack)
else
washed_itemstack = old_itemstack
end
return washed_itemstack
end
--- Patterned banner rules.
--
-- Banner washed in cauldron, reduces by 1 level, may waste washing (blank banner washable)
-- @param substance string : substance consumed from cauldron
-- @param sound_specifier string/function : rule's sound specifier
function BannerProxy:add_banner_rules(substance, sound_specifier)
self:add_basic_rule(substance, 3, self.is_banner, substance, 2, self.wash_banner, sound_specifier)
self:add_basic_rule(substance, 2, self.is_banner, substance, 1, self.wash_banner, sound_specifier)
self:add_basic_rule(substance, 1, self.is_banner, nil, 0, self.wash_banner, sound_specifier)
end
BannerProxy:add_banner_rules("water", "mcl_potions_bottle_pour")
--------
--------
-- initial item proxies.
-- FIXME: I don't like where this is buried, considering how crucial it is to enabling item interaction. -PH
mcl_cauldrons.add_item_proxy(mcl_cauldrons.BannerProxy)
if minetest.get_modpath("mcl_potions") then
mcl_cauldrons.add_item_proxy(mcl_cauldrons.BottleProxy)
end
mcl_cauldrons.add_item_proxy(mcl_cauldrons.BucketProxy)
--------
--------
--- Empty cauldron node definition
-- @name mcl_cauldrons:cauldron
-- @class table
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron", {
description = S("Cauldron"),
_tt_help = S("Stores water"),
_doc_items_longdesc = S("Cauldrons are used to store water and slowly fill up under rain."),
_doc_items_usagehelp = S("Place a water pucket into the cauldron to fill it with water. Place an empty bucket on a full cauldron to retrieve the water. Place a water bottle into the cauldron to fill the cauldron to one third with water. Place a glass bottle in a cauldron with water to retrieve one third of the water."),
wield_image = "mcl_cauldrons_cauldron.png",
inventory_image = "mcl_cauldrons_cauldron.png",
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
drawtype = "nodebox",
paramtype = "light",
is_ground_content = false,
groups = {pickaxey=1, deco_block=1, cauldron=1},
node_box = mcl_cauldrons.cauldron_nodeboxes[0],
selection_box = { type = "regular" },
tiles = {
"mcl_cauldrons_cauldron_inner.png^mcl_cauldrons_cauldron_top.png",
"mcl_cauldrons_cauldron_inner.png^mcl_cauldrons_cauldron_bottom.png",
"mcl_cauldrons_cauldron_side.png"
},
sounds = mcl_sounds.node_sound_metal_defaults(),
_mcl_hardness = 2,
_mcl_blast_resistance = 2,
-- delegated to by mcl_buckets and mcl_potions.
on_rightclick = function(place_pos, node, user, itemstack)
return mcl_cauldrons.apply_item(place_pos, node, user, itemstack)
end,
})
--- Water cauldron (1/3) node definition.
--
-- water cauldron as extension of empty cauldron.
-- @name mcl_cauldrons:cauldron_water_1
-- @class table
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_water_1",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron"], {
description = S("Cauldron (1/3 Water)"),
groups = {
pickaxey = 1,
cauldron = 1,
cauldron_filled = 1,
cauldron_maximum = 3,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[1],
collision_box = mcl_cauldrons.cauldron_nodeboxes[0],
tiles = {
[1]="(default_water_source_animated.png^[verticalframe:16:0)^mcl_cauldrons_cauldron_top.png",
},
drop = "mcl_cauldrons:cauldron",
_mcl_cauldron_substance = "water",
}), "mcl_cauldrons:cauldron")
--- Water cauldron (2/3) node definition.
--
-- water cauldron 2/3 as extension of 1/3.
-- @name mcl_cauldrons:cauldron_water_2
-- @class table
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_water_2",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron_water_1"], {
description = S("Cauldron (2/3 Water)"),
groups = {
cauldron_filled = 2,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[2],
}), "mcl_cauldrons:cauldron")
--- Water cauldron (2/3) node definition.
--
-- water cauldron 3/3 as extension of 1/3.
-- @name mcl_cauldrons:cauldron_water_3
-- @class table
mcl_cauldrons.register_cauldron_node("mcl_cauldrons:cauldron_water_3",
merge_tables(mcl_cauldrons.registered_cauldrons["mcl_cauldrons:cauldron_water_1"], {
description = S("Cauldron (3/3 Water)"),
groups = {
cauldron_filled = 3,
},
node_box = mcl_cauldrons.cauldron_nodeboxes[3],
}), "mcl_cauldrons:cauldron")
-- River Water Cauldron
if minetest.get_modpath("mclx_core") then
dosubfile("cauldron_river_water.lua")
end
-- Lava Cauldron
if minetest.get_modpath("mclx_core") then
dosubfile("cauldron_lava.lua")
end
-- Crafting recipe: Cauldron
minetest.register_craft({
output = "mcl_cauldrons:cauldron",
recipe = {
{ "mcl_core:iron_ingot", "", "mcl_core:iron_ingot" },
{ "mcl_core:iron_ingot", "", "mcl_core:iron_ingot" },
{ "mcl_core:iron_ingot", "mcl_core:iron_ingot", "mcl_core:iron_ingot" },
}
})
minetest.register_abm({
label = "cauldrons",
nodenames = {"group:cauldron_filled"},
interval = 0.5,
chance = 1,
action = function(pos, node)
local EXTINGUISHING_RADIUS = 0.4
for _, obj in pairs(minetest.get_objects_inside_radius(pos, EXTINGUISHING_RADIUS)) do
if mcl_burning.is_burning(obj) then
local newnode = mcl_cauldrons.node_drain_levels(node, 1, "water")
if node ~= newnode then
-- extinguishing requires 1 level of water from cauldron.
minetest.swap_node(pos, newnode)
mcl_burning.extinguish(obj)
break
end
end
end
end
})
-- backwards compatibility
local aliases = {
-- key=old/legacy name; value=current name
["mcl_cauldrons:cauldron_1"] = "mcl_cauldrons:cauldron_water_1",
["mcl_cauldrons:cauldron_2"] = "mcl_cauldrons:cauldron_water_2",
["mcl_cauldrons:cauldron_3"] = "mcl_cauldrons:cauldron_water_3",
["mcl_cauldrons:cauldron_1r"] = "mcl_cauldrons:cauldron_river_water_1",
["mcl_cauldrons:cauldron_2r"] = "mcl_cauldrons:cauldron_river_water_2",
["mcl_cauldrons:cauldron_3r"] = "mcl_cauldrons:cauldron_river_water_3",
}
for legacy, current in pairs(aliases) do
minetest.register_alias(legacy, current)
end

View File

@ -0,0 +1,6 @@
Unit testing of mcl\_cauldrons using mineunit.
$ luarocks install --server=https://luarocks.org/dev --local mineunit
$ luarocks install busted

View File

@ -0,0 +1,19 @@
-- Add some simple nodes
local function register_default(name)
minetest.register_node(":default:"..name, {
description = name.." description",
tiles = name.."_texture.png",
buildable_to = false,
walkable = true,
})
end
register_default("furnace")
register_default("stone")
register_default("stonebrick")
register_default("sand")
register_default("sandstone")
register_default("sandstonebrick")
register_default("steelblock")

View File

@ -0,0 +1,32 @@
-- Simple digilines mod fixture that logs sent messages, works with some simple digiline mods
mineunit:set_modpath("digilines", "spec/fixtures")
_G.digilines = {
_msg_log = {},
receptor_send = function(pos, rules, channel, msg)
table.insert(_G.digilines._msg_log, {
pos = pos,
rules = rules,
channel = channel,
msg = msg,
})
end,
rules = {
default = {
{x=0, y=0, z=-1},
{x=1, y=0, z=0},
{x=-1, y=0, z=0},
{x=0, y=0, z=1},
{x=1, y=1, z=0},
{x=1, y=-1, z=0},
{x=-1, y=1, z=0},
{x=-1, y=-1, z=0},
{x=0, y=1, z=1},
{x=0, y=-1, z=1},
{x=0, y=1, z=-1},
{x=0, y=-1, z=-1}
}
}
}

View File

@ -0,0 +1,29 @@
-- Simple mesecons fixture that allows many node registrations requiring mesecons
mineunit:set_modpath("mesecons", "spec/fixtures")
mineunit:set_modpath("mesecons_mvps", "spec/fixtures")
mesecon = {
state = {},
rules = {
default = {
{x = 0, y = 0, z = -1},
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = 1, y = 1, z = 0},
{x = 1, y = -1, z = 0},
{x = -1, y = 1, z = 0},
{x = -1, y = -1, z = 0},
{x = 0, y = 1, z = 1},
{x = 0, y = -1, z = 1},
{x = 0, y = 1, z = -1},
{x = 0, y = -1, z = -1},
}
}
}
mesecon = setmetatable(mesecon, {
__call = function(self,...) return self end,
__index = function(...) return function(...)end end,
})

View File

@ -0,0 +1 @@
active_block_range = 1

View File

@ -0,0 +1,21 @@
-- Simple pipeworks fixture with few no-op methods, enough for technic, metatool and jumpdrive
mineunit:set_modpath("pipeworks", "spec/fixtures")
local function noop(t)
return setmetatable(t, {
__call = function(self,...) return self end,
__index = function(...) return function(...)end end,
})
end
local pipeworks = {
button_label = "",
fs_helpers = {
cycling_button = function(...) return "" end
},
tptube = {},
}
pipeworks.tptube = noop(pipeworks.tptube)
_G.pipeworks = noop(pipeworks)

View File

@ -0,0 +1,812 @@
#!/usr/bin/env mineunit
-- unit test for mcl_cauldrons, using mineunit (https://github.com/S-S-X/mineunit)
-- quick primer: this runs *outside* of Minetest, with the mineunit executable,
-- and calls to actual minetest engine calls must be mocked.
require("mineunit")
mineunit("player")
mineunit("protection")
mineunit("common/after")
mineunit("server")
mineunit("voxelmanip")
fixture("default")
fixture("mesecons")
fixture("digilines")
fixture("pipeworks")
-- mock mclx_core
_get_modpath = minetest.get_modpath
minetest.get_modpath = function(modname)
if modname == "mclx_core" then return modname end
if modname == "doc" then return modname end
if modname == "mcl_buckets" then return modname end
if modname == "mcl_potions" then return modname end
if modname == "mcl_banners" then return modname end
return _get_modpath(modname)
end
describe("MineClone2 cauldrons test", function()
-- mock liquid sources (mock sound)
local s_water_source = "mcl_core:water_source"
local s_lava_source = "mcl_core:lava_source"
local water_source = {
sounds = {
place = "place",
dig = "dig",
}
}
local lava_source = {
paramtype = "light",
light_source = minetest.LIGHT_MAX,
sounds = {
place = "place",
dig = "dig",
}
}
minetest.registered_nodes[s_water_source] = water_source
minetest.registered_nodes[s_lava_source] = lava_source
-- Mocked functions and objects.
if not minetest.features then
minetest.features = {}
minetest.features.use_texture_alpha_string_modes = true
end
if not minetest.get_objects_inside_radius then
minetest.get_objects_inside_radius = function(pos, radius) return {} end
end
_G.mcl_sounds = {}
_G.mcl_sounds.node_sound_metal_defaults = function(...) end
_G.doc = {}
_G.doc.add_entry_alias = function(...) end
-- Create world with 100 x 1 x 3 stone layer and 3 nodes high air layer.
world.layout({
{{{x=0,y=0,z=-1},{x=99,y=0,z=1}}, "default:stone"},
{{{x=0,y=1,z=-1},{x=99,y=3,z=1}}, "air"},
-- empty cauldrons at start.
{ {x=0, y=1, z=-3}, "mcl_cauldrons:cauldron" },
})
-- Load current mod executing init.lua
sourcefile("init")
-- Execute on mods loaded callbacks to finish loading.
mineunit:mods_loaded()
-- Tell mods that 1 minute passed already to execute possible weird minetest.after hacks.
mineunit:execute_globalstep(60)
local Sam = Player("Sam")
-- Players join before tests begin
setup(function()
mineunit:execute_on_joinplayer(Sam)
end)
-- Players leave after tests finished
teardown(function()
mineunit:execute_on_leaveplayer(Sam)
end)
-- Generate placement test for all registered nodes without not_in_creative_inventory group
local function placement_test(name, xpos)
return function()
Sam:get_inventory():set_stack("main", 1, name)
Sam:do_place({x=xpos, y=1, z=0})
end
end
local xpos = 0
for nodename, def in pairs(minetest.registered_nodes) do
if not (def.groups and def.groups.not_in_creative_inventory) then
it("wont crash placing "..nodename, placement_test(nodename, xpos))
xpos = xpos + 1
end
end
--[[
it("placed all nodes", function()
pending("This test might be too simple for potentially complex operation and fails if node is changed")
local index = 0
for nodename, def in pairs(minetest.registered_nodes) do
if not (def.groups and def.groups.not_in_creative_inventory) then
local node = minetest.get_node({x=index, y=1, z=0})
assert.equals(node.name, nodename)
index = index + 1
end
end
end)
]]
-- abbreviated symbols
local s_cauldron0 = "mcl_cauldrons:cauldron"
local s_cauldron1 = "mcl_cauldrons:cauldron_water_1"
local s_cauldron2 = "mcl_cauldrons:cauldron_water_2"
local s_cauldron3 = "mcl_cauldrons:cauldron_water_3"
local s_cauldron1r = "mcl_cauldrons:cauldron_river_water_1"
local s_cauldron2r = "mcl_cauldrons:cauldron_river_water_2"
local s_cauldron3r = "mcl_cauldrons:cauldron_river_water_3"
local s_cauldron1l = "mcl_cauldrons:cauldron_lava_1"
local s_cauldron2l = "mcl_cauldrons:cauldron_lava_2"
local s_cauldron3l = "mcl_cauldrons:cauldron_lava_3"
local s_bucket0 = "mcl_buckets:bucket_empty"
local s_bucket1 = "mcl_buckets:bucket_water"
local s_bucket1r = "mcl_buckets:bucket_river_water"
local s_bucket1l = "mcl_buckets:bucket_lava"
local s_bottle0 = "mcl_potions:glass_bottle"
local s_bottle1 = "mcl_potions:water"
local s_bottle1r = "mcl_potions:river_water"
local s_banner0 = "mcl_banners:placeholder_banner"
it("test state normalizer", function()
-- test reducing (substance,fill_level) tuples to a valid cauldron.
local result
-- synonyms for empty cauldron: (empty,any), (any,0)
result = mcl_cauldrons.make_cauldron("empty", 0)
assert.equals(s_cauldron0, result)
result = mcl_cauldrons.make_cauldron("empty", 1)
assert.equals(s_cauldron0, result)
result = mcl_cauldrons.make_cauldron("empty", 2)
assert.equals(s_cauldron0, result)
result = mcl_cauldrons.make_cauldron("empty", 3)
assert.equals(s_cauldron0, result)
result = mcl_cauldrons.make_cauldron("water", 0)
assert.equals(s_cauldron0, result)
result = mcl_cauldrons.make_cauldron("water", 1)
assert.equals(s_cauldron1, result)
result = mcl_cauldrons.make_cauldron("water", 2)
assert.equals(s_cauldron2, result)
result = mcl_cauldrons.make_cauldron("water", 3)
assert.equals(s_cauldron3, result)
end)
it("test state transition rules", function()
-- test the rules about filling and emptying cauldrons.
local node
local newnode
local cauldron0 = mcl_cauldrons.registered_cauldrons[s_cauldron0]
newnode = mcl_cauldrons.node_fill_levels(cauldron0, 1, "water")
assert.equals(s_cauldron1, newnode.name)
newnode = mcl_cauldrons.node_fill_levels(cauldron0, 2, "water")
assert.equals(s_cauldron2, newnode.name)
newnode = mcl_cauldrons.node_fill_levels(cauldron0, 3, "water")
assert.equals(s_cauldron3, newnode.name)
local cauldron1 = mcl_cauldrons.registered_cauldrons[s_cauldron1]
assert.is.truthy(mcl_cauldrons.node_has_substance(cauldron1, "water"))
assert.equals(1, mcl_cauldrons.node_get_level(cauldron1))
newnode = mcl_cauldrons.node_fill_levels(cauldron1, 1, "water")
assert.equals(s_cauldron2, newnode.name)
newnode = mcl_cauldrons.node_fill_levels(cauldron1, 2, "water")
assert.equals(s_cauldron3, newnode.name)
local cauldron2 = mcl_cauldrons.registered_cauldrons[s_cauldron2]
newnode = mcl_cauldrons.node_fill_levels(cauldron2, 1, "water")
assert.equals(s_cauldron3, newnode.name)
newnode = mcl_cauldrons.node_fill_levels(cauldron2, 2, "water")
assert.equals(s_cauldron3, newnode.name)
newnode = mcl_cauldrons.node_drain_levels(cauldron2, 1, "water")
assert.equals(s_cauldron1, newnode.name)
newnode = mcl_cauldrons.node_drain_levels(cauldron2, 2, "water")
assert.equals(s_cauldron0, newnode.name)
local cauldron3 = mcl_cauldrons.registered_cauldrons[s_cauldron3]
newnode = mcl_cauldrons.node_drain_levels(cauldron3, 1, "water")
assert.equals(s_cauldron2, newnode.name)
newnode = mcl_cauldrons.node_drain_levels(cauldron3, 2, "water")
assert.equals(s_cauldron1, newnode.name)
newnode = mcl_cauldrons.node_drain_levels(cauldron3, 3, "water")
assert.equals(s_cauldron0, newnode.name)
newnode = mcl_cauldrons.node_drain_levels(cauldron3, 4, "water")
assert.equals(nil, newnode)
end)
----
-- helper functions
-- all cauldron activity at this position.
local CAULDRON_POS = { x=0, y=1, z=-3 }
-- Make sure node (at CAULDRON_POS) is a particular cauldron.
local function force_cauldron(nodename)
local node = { name=nodename }
minetest.set_node(CAULDRON_POS, node)
return minetest.get_node(CAULDRON_POS)
end
local function assert_cauldron(nodename)
local node = minetest.get_node(CAULDRON_POS)
assert.is_not_nil(node)
assert.equals(nodename, node.name)
return node
end
-- Make sure Sam has, and is wielding, inventory.
local function force_inv(itemname)
Sam:get_inventory():set_stack("main", 1, itemname)
local itemstack = Sam:get_inventory():get_stack("main", 1)
return itemstack
end
local function assert_inv(itemname)
itemstack = Sam:get_inventory():get_stack("main", 1)
assert.equals(itemname, itemstack:get_name())
return itemstack
end
-- mimick right-clicking (placing) item onto cauldron at CAULDRON_POS.
local function rclick_cauldron()
local node = minetest.get_node(CAULDRON_POS)
local nodedef = minetest.registered_nodes[node.name]
local itemstack = Sam:get_inventory():get_stack("main", 1)
-- do_place() not viable as buckets/potions/etc. (and their on_place()) are not loaded in this unit test.
local newstack = nodedef.on_rightclick(CAULDRON_POS, node, Sam, itemstack)
Sam:get_inventory():set_stack("main", 1, newstack)
return newstack
end
-- mock buckets
local bucket0 = {
description="Bucket",
groups = { bucket=1 },
}
local bucket1 = {
description="Water Bucket",
groups = { bucket=1 },
}
local bucket1r = {
description="River Water Bucket",
groups = { bucket=1 },
}
local bucket1l = {
description="Lava Bucket",
groups = { bucket=1 },
}
minetest.registered_items[s_bucket0] = bucket0
minetest.registered_items[s_bucket1] = bucket1
minetest.registered_items[s_bucket1r] = bucket1r
describe("apply empty bucket to cauldron", function()
----
-- test the implementation details (in the spirit of TDD)
it(", implementation details", function()
-- empty + full water cauldron = empty cauldron, water bucket
local itemstack0 = ItemStack(s_bucket0)
local pos = CAULDRON_POS
local node = { name=s_cauldron3 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.is_not_nil(newitemstack)
assert.equals(s_cauldron0, newnode.name)
assert.equals(s_bucket1, newitemstack:get_name())
-- empty bucket + 2/3 water cauldron = nil, nil (no change)
local node = { name=s_cauldron2 }
newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_nil(newnode)
assert.is_nil(newitemstack)
-- empty bucket + 1/3 water cauldron = nil, nil (no change)
local node = { name=s_cauldron1 }
newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_nil(newnode)
assert.is_nil(newitemstack)
-- empty bucket + empty cauldron = nil, nil (no change)
local node = { name=s_cauldron0 }
newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_nil(newnode)
assert.is_nil(newitemstack)
-- empty bucket + lava cauldron = empty cauldron, lava bucket
local node = { name=s_cauldron3l }
newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.is_not_nil(newitemstack)
assert.equals(s_cauldron0, newnode.name)
assert.equals(s_bucket1l, newitemstack:get_name())
end)
----
-- mimick world activity (validate for gameplay)
it(", mimick world activity", function()
force_cauldron(s_cauldron3)
local node = assert_cauldron(s_cauldron3)
assert.equals(3, mcl_cauldrons.node_get_level(node))
-- start with empty bucket.
force_inv(s_bucket0)
local itemstack = assert_inv(s_bucket0)
assert.equals("mcl_buckets:bucket_empty", itemstack:get_name())
-- apply itemstack (rightclick empty bucket) to cauldron (full water) -> empty cauldron, water bucket.
local newstack = rclick_cauldron()
assert.not_equals(nil, newstack)
assert.equals("mcl_buckets:bucket_water", newstack:get_name())
assert_inv(s_bucket1)
-- check cauldron node updated properly.
assert_cauldron(s_cauldron0)
-- apply empty bucket to 2/3 cauldron -> (no change)
force_cauldron(s_cauldron2)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron2)
assert_inv(s_bucket0)
-- apply empty bucket to 1/3 cauldron -> (no change)
force_cauldron(s_cauldron1)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron1)
assert_inv(s_bucket0)
-- apply empty bucket to empty cauldron -> (no change)
force_cauldron(s_cauldron0)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron0)
assert_inv(s_bucket0)
-- apply empty bucket to full river water cauldron -> empty cauldron, river water bucket
force_cauldron(s_cauldron3r)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron0)
assert_inv(s_bucket1r)
-- apply empty bucket to 2/3 river water cauldron -> empty cauldron, river water bucket
force_cauldron(s_cauldron2r)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron2r)
assert_inv(s_bucket0)
-- apply empty bucket to 1/3 river water cauldron -> empty cauldron, river water bucket
force_cauldron(s_cauldron1r)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron1r)
assert_inv(s_bucket0)
-- apply empty bucket to full lava cauldron -> empty cauldron, lava bucket
force_cauldron(s_cauldron3l)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron0)
assert_inv(s_bucket1l)
-- apply empty bucket to 2/3 lava cauldron -> empty cauldron, lava bucket
force_cauldron(s_cauldron2l)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron2l)
assert_inv(s_bucket0)
-- apply empty bucket to 1/3 lava cauldron -> empty cauldron, lava bucket
force_cauldron(s_cauldron1l)
force_inv(s_bucket0)
rclick_cauldron()
assert_cauldron(s_cauldron1l)
assert_inv(s_bucket0)
end)
end)
describe("apply water bucket to cauldron", function()
----
-- test the implementation details (gets messy).
it(", implementation details", function()
local itemstack0 = ItemStack(s_bucket1)
-- water bucket + empty cauldrons = full water cauldron, empty bucket
local pos = CAULDRON_POS
local node = { name=s_cauldron0 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.is_not_nil(newitemstack)
assert.equals(s_cauldron3, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- water bucket + 1/3 cauldron = full water cauldron, empty bucket
node = { name=s_cauldron1 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- water bucket + 2/3 cauldron = full water cauldron, empty bucket
node = { name=s_cauldron2 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- water bucket + full cauldron = full water cauldron, empty bucket (wastes the water bucket)
node = { name=s_cauldron3 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
end)
----
-- mimick world activity (validate for gameplay)
it(", mimick world activity", function()
-- apply water bucket to full cauldron -> cauldron_water_3, empty bucket (bucket wasted)
force_cauldron(s_cauldron3)
force_inv(s_bucket1)
rclick_cauldron()
assert_cauldron(s_cauldron3)
assert_inv(s_bucket0)
-- apply water bucket to 2/3 cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron2)
force_inv(s_bucket1)
rclick_cauldron()
assert_cauldron(s_cauldron3)
assert_inv(s_bucket0)
-- apply water bucket to 1/3 cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron1)
force_inv(s_bucket1)
rclick_cauldron()
assert_cauldron(s_cauldron3)
assert_inv(s_bucket0)
-- apply water bucket to empty cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron0)
force_inv(s_bucket1)
rclick_cauldron()
assert_cauldron(s_cauldron3)
assert_inv(s_bucket0)
end)
end)
describe("apply river water bucket to cauldron #riverwaterbucket", function()
it(", implementation details", function()
assert.is_not_nil(minetest.registered_nodes["mcl_cauldrons:cauldron_river_water_1"])
assert.is_not_nil(minetest.registered_nodes["mcl_cauldrons:cauldron_river_water_2"])
assert.is_not_nil(minetest.registered_nodes["mcl_cauldrons:cauldron_river_water_3"])
----
-- test the implementation details (gets messy).
local itemstack0 = ItemStack(s_bucket1r)
-- river water bucket + empty cauldrons = full river water cauldron, empty bucket
local pos = CAULDRON_POS
local node = { name=s_cauldron0 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.is_not_nil(newitemstack)
assert.equals(s_cauldron3r, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- river water bucket + 1/3 cauldron = full river water cauldron, empty bucket
node = { name=s_cauldron1r }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3r, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- river water bucket + 2/3 cauldron = full river water cauldron, empty bucket
node = { name=s_cauldron2r }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3r, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- river water bucket + full cauldron = full river water cauldron, empty bucket (wastes the river water bucket)
node = { name=s_cauldron3r }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3r, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
end)
----
-- mimick world activity (validate for gameplay)
it(", mimick world activity", function()
-- apply river water bucket to full cauldron -> cauldron_water_3, empty bucket (bucket wasted)
force_cauldron(s_cauldron3r)
force_inv(s_bucket1r)
rclick_cauldron()
assert_cauldron(s_cauldron3r)
assert_inv(s_bucket0)
-- apply river water bucket to 2/3 cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron2r)
force_inv(s_bucket1r)
rclick_cauldron()
assert_cauldron(s_cauldron3r)
assert_inv(s_bucket0)
-- apply river water bucket to 1/3 cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron1r)
force_inv(s_bucket1r)
rclick_cauldron()
assert_cauldron(s_cauldron3r)
assert_inv(s_bucket0)
-- apply river water bucket to empty cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron0)
force_inv(s_bucket1r)
rclick_cauldron()
assert_cauldron(s_cauldron3r)
assert_inv(s_bucket0)
end)
end)
describe("apply lava bucket to cauldron #lavabucket", function()
it(", implementation details", function()
assert.is_not_nil(minetest.registered_nodes["mcl_cauldrons:cauldron_lava_1"])
assert.is_not_nil(minetest.registered_nodes["mcl_cauldrons:cauldron_lava_2"])
assert.is_not_nil(minetest.registered_nodes["mcl_cauldrons:cauldron_lava_3"])
----
-- test the implementation details (gets messy).
local itemstack0 = ItemStack(s_bucket1l)
-- lava bucket + empty cauldrons = full lava cauldron, empty bucket
local pos = CAULDRON_POS
local node = { name=s_cauldron0 }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.is_not_nil(newitemstack)
assert.equals(s_cauldron3l, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- lava bucket + 1/3 cauldron = full lava cauldron, empty bucket
node = { name=s_cauldron1l }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3l, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- lava bucket + 2/3 cauldron = full lava cauldron, empty bucket
node = { name=s_cauldron2l }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3l, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
-- lava bucket + full cauldron = full lava cauldron, empty bucket (wastes the lava bucket)
node = { name=s_cauldron3l }
local newnode, newitemstack = mcl_cauldrons.BucketProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3l, newnode.name)
assert.equals(s_bucket0, newitemstack:get_name())
end)
----
-- mimick world activity (validate for gameplay)
it(", mimick world activity", function()
-- apply lava bucket to full cauldron -> cauldron_water_3, empty bucket (bucket wasted)
force_cauldron(s_cauldron3l)
force_inv(s_bucket1l)
rclick_cauldron()
assert_cauldron(s_cauldron3l)
assert_inv(s_bucket0)
-- apply lava bucket to 2/3 cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron2l)
force_inv(s_bucket1l)
rclick_cauldron()
assert_cauldron(s_cauldron3l)
assert_inv(s_bucket0)
-- apply lava bucket to 1/3 cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron1l)
force_inv(s_bucket1l)
rclick_cauldron()
assert_cauldron(s_cauldron3l)
assert_inv(s_bucket0)
-- apply lava bucket to empty cauldron -> cauldron_water_3, empty bucket
force_cauldron(s_cauldron0)
force_inv(s_bucket1l)
rclick_cauldron()
assert_cauldron(s_cauldron3l)
assert_inv(s_bucket0)
end)
end)
-- mock potions
local bottle0 = {
description="Glass Bottle",
groups = { potion=1 },
}
local bottle1 = {
description="Water Bottle",
groups = { potion=1 },
}
local bottle1r = {
description="River Water Bottle",
groups = { potion=1 },
}
minetest.registered_items[s_bottle0] = bottle0
minetest.registered_items[s_bottle1] = bottle1
minetest.registered_items[s_bottle1r] = bottle1r
describe("apply glass/empty bottle to cauldron", function()
----
-- implementation details
it(", implementation details", function()
-- empty bottle + full cauldron = cauldron_water_2, water bottle
local itemstack0 = ItemStack(s_bottle0)
local pos = CAULDRON_POS
local node = { name=s_cauldron3 }
-- force to full cauldron
minetest.set_node(pos, node)
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.equals(s_cauldron2, newnode.name)
assert.equals(s_bottle1, newitemstack:get_name())
-- empty bottle + 2/3 water cauldron = cauldron_water_1, water_bottle
node = { name=s_cauldron2 }
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.equals("mcl_cauldrons:cauldron_water_1", newnode.name)
assert.equals("mcl_potions:water", newitemstack:get_name())
-- empty bottle + 1/3 water cauldron = empty cauldron, water_bottle
node = { name=s_cauldron1 }
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.equals("mcl_cauldrons:cauldron", newnode.name)
assert.equals("mcl_potions:water", newitemstack:get_name())
-- empty bottle + empty cauldron = nil, nil (no changes)
node = { name=s_cauldron0 }
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.equals(nil, newnode)
assert.equals(nil, newitemstack)
end)
----
-- mimick world activity (validate for gameplay)
it(", mimick world activity", function()
-- apply empty bottle to empty cauldron -> (no change)
force_cauldron(s_cauldron0)
force_inv(s_bottle0)
rclick_cauldron()
assert_cauldron(s_cauldron0)
assert_inv(s_bottle0)
-- apply empty bottle to 1/3 cauldron -> empty cauldron, water bottle.
force_cauldron(s_cauldron1)
force_inv(s_bottle0)
rclick_cauldron()
assert_cauldron(s_cauldron0)
assert_inv(s_bottle1)
-- apply empty bottle to 2/3 cauldron -> cauldron_water_1, water_bottle.
force_cauldron(s_cauldron2)
force_inv(s_bottle0)
rclick_cauldron()
assert_cauldron(s_cauldron1)
assert_inv(s_bottle1)
-- apply empty bottle to full cauldron -> cauldron_water_2, water_bottle.
force_cauldron(s_cauldron3)
force_inv(s_bottle0)
rclick_cauldron()
assert_cauldron(s_cauldron2)
assert_inv(s_bottle1)
end)
end)
describe("apply water bottle to cauldron", function()
it("water battle vs cauldron implementation details", function()
----
-- implementation details
local itemstack0 = ItemStack(s_bottle1)
local pos = CAULDRON_POS
local node = { name=s_cauldron0 }
-- force to empty cauldron
minetest.set_node(pos, node)
-- water bottle + empty cauldron = cauldron_water_1, empty bottle
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.equals(s_cauldron1, newnode.name)
assert.equals(s_bottle0, newitemstack:get_name())
-- water bottle + 1/3 water cauldron = cauldron_water_2, empty bottle
node = { name=s_cauldron1 }
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron2, newnode.name)
assert.equals(s_bottle0, newitemstack:get_name())
-- water bottle + 2/3 water cauldron = cauldron_water_3, empty bottle
node = { name=s_cauldron2 }
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.equals(s_cauldron3, newnode.name)
assert.equals(s_bottle0, newitemstack:get_name())
-- water bottle + full water cauldron = nil, nil (no change)
node = { name=s_cauldron3 }
local newnode, newitemstack = mcl_cauldrons.BottleProxy:apply_basic_rules(itemstack0, node)
assert.equals(nil, newnode)
assert.equals(nil, newitemstack)
end)
----
-- mimick world activity (validate for gameplay)
it("water bottle vs cauldron, mimick world activity", function()
-- apply water bottle to empty cauldron -> cauldron1, empty bottle
force_cauldron(s_cauldron0)
force_inv(s_bottle1)
rclick_cauldron()
assert_cauldron(s_cauldron1)
assert_inv(s_bottle0)
-- apply water bottle to 1/3 cauldron -> cauldron2, empty bottle
force_cauldron(s_cauldron1)
force_inv(s_bottle1)
rclick_cauldron()
assert_cauldron(s_cauldron2)
assert_inv(s_bottle0)
-- apply water bottle to 2/3 cauldron -> cauldron3, empty bottle
force_cauldron(s_cauldron2)
force_inv(s_bottle1)
rclick_cauldron()
assert_cauldron(s_cauldron3)
assert_inv(s_bottle0)
-- apply water bottle to full cauldron -> (no change)
force_cauldron(s_cauldron3)
force_inv(s_bottle1)
rclick_cauldron()
assert_cauldron(s_cauldron3)
assert_inv(s_bottle1)
end)
end)
-- mock banner
local banner0 = {
description="Placeholder Banner",
groups = { banner=1 },
}
minetest.registered_items[s_banner0] = banner0
describe("apply banner to cauldron", function()
it(", implementation details", function()
local itemstack0 = ItemStack(s_banner0)
local node = { name=s_cauldron3 }
local newnode, newitem = mcl_cauldrons.BannerProxy:apply_basic_rules(itemstack0, node)
assert.is_not_nil(newnode)
assert.is_not_nil(newitem)
end)
it(", mimick world activity", function()
force_cauldron(s_cauldron3)
assert_cauldron(s_cauldron3)
force_inv(s_banner0)
assert_inv(s_banner0)
rclick_cauldron()
assert_cauldron(s_cauldron2)
assert_inv(s_banner0)
end)
end)
-- Execute globalstep few times just to see if something happens
it("gloabalstep works", function()
for _=1,60 do
mineunit:execute_globalstep(0.42)
end
end)
end)

View File

@ -0,0 +1,5 @@
time_step = 100
verbose = 3
exclude = {
"integration_test",
}

View File

@ -773,8 +773,7 @@ end
local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
-- Return appropriate grass block node for pos
function mcl_core.get_grass_block_type(pos)
function mcl_core.get_grass_palette_index(pos)
local biome_data = minetest.get_biome_data(pos)
local index = 0
if biome_data then
@ -785,7 +784,12 @@ function mcl_core.get_grass_block_type(pos)
index = reg_biome._mcl_palette_index
end
end
return {name="mcl_core:dirt_with_grass", param2=index}
return index
end
-- Return appropriate grass block node for pos
function mcl_core.get_grass_block_type(pos)
return {name = "mcl_core:dirt_with_grass", param2 = mcl_core.get_grass_palette_index(pos)}
end
------------------------------

View File

@ -365,7 +365,7 @@ minetest.register_node("mcl_core:dirt_with_grass", {
overlay_tiles = {"mcl_core_grass_block_top.png", "", {name="mcl_core_grass_block_side_overlay.png", tileable_vertical=false}},
palette = "mcl_core_palette_grass.png",
palette_index = 0,
color = "#55aa60",
color = "#8EB971",
is_ground_content = true,
stack_max = 64,
groups = {handy=1,shovely=1,dirt=2,grass_block=1, grass_block_no_snow=1, soil=1, soil_sapling=2, soil_sugarcane=1, cultivatable=2, spreading_dirt_type=1, enderman_takable=1, building_block=1},

View File

@ -53,7 +53,10 @@ minetest.register_node("mcl_core:reeds", {
_doc_items_longdesc = S("Sugar canes are a plant which has some uses in crafting. Sugar canes will slowly grow up to 3 blocks when they are next to water and are placed on a grass block, dirt, sand, red sand, podzol or coarse dirt. When a sugar cane is broken, all sugar canes connected above will break as well."),
_doc_items_usagehelp = S("Sugar canes can only be placed top of other sugar canes and on top of blocks on which they would grow."),
drawtype = "plantlike",
paramtype2 = "color",
tiles = {"default_papyrus.png"},
palette = "mcl_core_palette_grass.png",
palette_index = 0,
inventory_image = "mcl_core_reeds.png",
wield_image = "mcl_core_reeds.png",
paramtype = "light",
@ -79,6 +82,7 @@ minetest.register_node("mcl_core:reeds", {
groups = {dig_immediate=3, craftitem=1, deco_block=1, plant=1, non_mycelium_plant=1, dig_by_piston=1},
sounds = mcl_sounds.node_sound_leaves_defaults(),
node_placement_prediction = "",
drop = "mcl_core:reeds", -- to prevent color inheritation
on_place = mcl_util.generate_on_place_plant_function(function(place_pos, place_node)
local soil_pos = {x=place_pos.x, y=place_pos.y-1, z=place_pos.z}
local soil_node = minetest.get_node_or_nil(soil_pos)
@ -114,6 +118,15 @@ minetest.register_node("mcl_core:reeds", {
return false
end),
on_construct = function(pos)
local node = minetest.get_node(pos)
if node.param2 == 0 then
node.param2 = mcl_core.get_grass_palette_index(pos)
if node.param2 ~= 0 then
minetest.set_node(pos, node)
end
end
end,
_mcl_blast_resistance = 0,
_mcl_hardness = 0,
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 1.9 KiB

14
mods/ITEMS/mcl_dye/API.md Normal file
View File

@ -0,0 +1,14 @@
# mcl_dye
# Bone meal API
Callback and particle functions.
## mcl_dye.add_bone_meal_particle(pos, def)
Spawns standard or custom bone meal particles.
* `pos`: position, is ignored if you define def.minpos and def.maxpos
* `def`: (optional) particle definition
## mcl_dye.register_on_bone_meal_apply(function(pointed_thing, user))
Called when the bone meal is applied anywhere.
* `pointed_thing`: exact pointing location (see Minetest API), where the bone meal is applied
* `user`: ObjectRef of the player who aplied the bone meal, can be nil!

View File

@ -128,26 +128,35 @@ for _, row in ipairs(dyelocal.dyes) do
end
-- Bone Meal
local function bone_meal_particle(pos)
function mcl_dye.add_bone_meal_particle(pos, def)
if not def then
def = {}
end
minetest.add_particlespawner({
amount = 10,
time = 0.1,
minpos = { x = pos.x - 0.5, y = pos.y - 0.5, z = pos.z - 0.5 },
maxpos = { x = pos.x + 0.5, y = pos.y + 0.5, z = pos.z + 0.5 },
minvel = { x = 0, y = 0, z = 0},
maxvel = { x = 0, y = 0, z = 0},
minacc = { x = 0, y = 0, z = 0},
maxacc = { x = 0, y = 0, z = 0},
minexptime = 1,
maxexptime = 4,
minsize = 0.7,
maxsize = 2.4,
amount = def.amount or 10,
time = def.time or 0.1,
minpos = def.minpos or vector.subtract(pos, 0.5),
maxpos = def.maxpos or vector.add(pos, 0.5),
minvel = def.minvel or vector.new(0, 0, 0),
maxvel = def.maxvel or vector.new(0, 0, 0),
minacc = def.minacc or vector.new(0, 0, 0),
maxacc = def.maxacc or vector.new(0, 0, 0),
minexptime = def.minexptime or 1,
maxexptime = def.maxexptime or 4,
minsize = def.minsize or 0.7,
maxsize = def.maxsize or 2.4,
texture = "mcl_particles_bonemeal.png^[colorize:#00EE00:125", -- TODO: real MC color
glow = 5,
glow = def.glow or 5,
})
end
function mcl_dye.apply_bone_meal(pointed_thing)
mcl_dye.bone_meal_callbacks = {}
function mcl_dye.register_on_bone_meal_apply(func)
table.insert(mcl_dye.bone_meal_callbacks, func)
end
local function apply_bone_meal(pointed_thing)
-- Bone meal currently spawns all flowers found in the plains.
local flowers_table_plains = {
"mcl_flowers:dandelion",
@ -183,14 +192,21 @@ function mcl_dye.apply_bone_meal(pointed_thing)
local pos = pointed_thing.under
local n = minetest.get_node(pos)
if n.name == "" then return false end
for _, func in pairs(mcl_dye.bone_meal_callbacks) do
if func(pointed_thing, user) then
return true
end
end
if minetest.get_item_group(n.name, "sapling") >= 1 then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
-- Saplings: 45% chance to advance growth stage
if math.random(1,100) <= 45 then
return mcl_core.grow_sapling(pos, n)
end
elseif minetest.get_item_group(n.name, "mushroom") == 1 then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
-- Try to grow huge mushroom
-- Must be on a dirt-type block
@ -240,39 +256,37 @@ function mcl_dye.apply_bone_meal(pointed_thing)
return false
-- Wheat, Potato, Carrot, Pumpkin Stem, Melon Stem: Advance by 2-5 stages
elseif string.find(n.name, "mcl_farming:wheat_") then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_wheat", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:potato_") then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_potato", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:carrot_") then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_carrot", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:pumpkin_") then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_pumpkin_stem", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:melontige_") then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
local stages = math.random(2, 5)
return mcl_farming:grow_plant("plant_melon_stem", pos, n, stages, true)
elseif string.find(n.name, "mcl_farming:beetroot_") then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
-- Beetroot: 75% chance to advance to next stage
if math.random(1, 100) <= 75 then
return mcl_farming:grow_plant("plant_beetroot", pos, n, 1, true)
end
elseif n.name == "mcl_cocoas:cocoa_1" or n.name == "mcl_cocoas:cocoa_2" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
-- Cocoa: Advance by 1 stage
mcl_cocoas.grow(pos)
return true
elseif minetest.get_item_group(n.name, "grass_block") == 1 then
local grass_block_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
bone_meal_particle(grass_block_pos)
-- Grass Block: Generate tall grass and random flowers all over the place
for i = -2, 2 do
for j = -2, 2 do
@ -285,6 +299,7 @@ function mcl_dye.apply_bone_meal(pointed_thing)
-- Randomly generate flowers, tall grass or nothing
if math.random(1,100) <= 90 then
-- 90% tall grass, 10% flower
mcl_dye.add_bone_meal_particle(pos, {amount = 4})
if math.random(1,100) <= 90 then
local col = n2.param2
minetest.add_node(pos, {name="mcl_flowers:tallgrass", param2=col})
@ -314,24 +329,24 @@ function mcl_dye.apply_bone_meal(pointed_thing)
-- Double flowers: Drop corresponding item
elseif n.name == "mcl_flowers:rose_bush" or n.name == "mcl_flowers:rose_bush_top" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:rose_bush")
return true
elseif n.name == "mcl_flowers:peony" or n.name == "mcl_flowers:peony_top" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:peony")
return true
elseif n.name == "mcl_flowers:lilac" or n.name == "mcl_flowers:lilac_top" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:lilac")
return true
elseif n.name == "mcl_flowers:sunflower" or n.name == "mcl_flowers:sunflower_top" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
minetest.add_item(pos, "mcl_flowers:sunflower")
return true
elseif n.name == "mcl_flowers:tallgrass" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
-- Tall Grass: Grow into double tallgrass
local toppos = { x=pos.x, y=pos.y+1, z=pos.z }
local topnode = minetest.get_node(toppos)
@ -342,7 +357,7 @@ function mcl_dye.apply_bone_meal(pointed_thing)
end
elseif n.name == "mcl_flowers:fern" then
bone_meal_particle(pos)
mcl_dye.add_bone_meal_particle(pos)
-- Fern: Grow into large fern
local toppos = { x=pos.x, y=pos.y+1, z=pos.z }
local topnode = minetest.get_node(toppos)
@ -374,7 +389,7 @@ minetest.register_craftitem("mcl_dye:white", {
end
-- Use the bone meal on the ground
if(mcl_dye.apply_bone_meal(pointed_thing) and (not minetest.is_creative_enabled(user:get_player_name()))) then
if (apply_bone_meal(pointed_thing, user) and (not minetest.is_creative_enabled(user:get_player_name()))) then
itemstack:take_item()
end
return itemstack
@ -387,7 +402,7 @@ minetest.register_craftitem("mcl_dye:white", {
else
pointed_thing = { above = pos, under = droppos }
end
local success = mcl_dye.apply_bone_meal(pointed_thing)
local success = apply_bone_meal(pointed_thing, nil)
if success then
stack:take_item()
end

View File

@ -183,7 +183,7 @@ minetest.register_entity("mcl_enchanting:book", {
collisionbox = {0, 0, 0},
pointable = false,
physical = false,
textures = {"mcl_enchanting_book_entity.png"},
textures = {"mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png", "mcl_enchanting_book_entity.png"},
static_save = false,
},
_player_near = false,

View File

@ -1496,7 +1496,7 @@ local function register_dimension_biomes()
heat_point = 100,
humidity_point = 0,
_mcl_biome_type = "hot",
_mcl_palette_index = 19,
_mcl_palette_index = 17,
})
--[[ THE END ]]

View File

@ -118,17 +118,6 @@ def colorize_alpha(colormap, source, colormap_pixel, texture_size, destination):
colorize(colormap, source, colormap_pixel, texture_size, tempfile2.name)
os.system("composite -compose Dst_In "+source+" "+tempfile2.name+" -alpha Set "+destination)
# This function is unused atm.
# TODO: Implemnt colormap extraction
def extract_colormap(colormap, colormap_pixel, positions):
os.system("convert -size 16x16 canvas:black "+tempfile1.name)
x=0
y=0
for p in positions:
os.system("convert "+colormap+" -crop 1x1+"+colormap_pixel+" -depth 8 "+tempfile2.name)
os.system("composite -geometry 16x16+"+x+"+"+y+" "+tempfile2.name)
x = x+1
def target_dir(directory):
if make_texture_pack:
return output_dir + "/" + output_dir_name
@ -397,20 +386,60 @@ def convert_textures():
colorize_alpha(FOLIAG, tex_dir+"/blocks/vine.png", "16+39", str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/mcl_core_vine.png")
# Tall grass, fern (inventory images)
pcol = "49+172" # Plains grass color
pcol = "50+173" # Plains grass color
colorize_alpha(GRASS, tex_dir+"/blocks/tallgrass.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_tallgrass_inv.png")
colorize_alpha(GRASS, tex_dir+"/blocks/fern.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_fern_inv.png")
colorize_alpha(GRASS, tex_dir+"/blocks/double_plant_fern_top.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_double_plant_fern_inv.png")
colorize_alpha(GRASS, tex_dir+"/blocks/double_plant_grass_top.png", pcol, str(PXSIZE), target_dir("/mods/ITEMS/mcl_flowers/textures")+"/mcl_flowers_double_plant_grass_inv.png")
# TODO: Convert grass palette
offset = [
[ pcol, "", "grass" ], # Default grass: Plains
# Convert grass palette: https://minecraft.fandom.com/wiki/Tint
grass_colors = [
# [Coords or #Color, AdditionalTint], # Index - Minecraft biome name (MineClone2 biome names)
["50+173"], # 0 - Plains (flat, Plains, Plains_beach, Plains_ocean, End)
["0+255"], # 1 - Savanna (Savanna, Savanna_beach, Savanna_ocean)
["255+255"], # 2 - Ice Spikes (IcePlainsSpikes, IcePlainsSpikes_ocean)
["255+255"], # 3 - Snowy Taiga (ColdTaiga, ColdTaiga_beach, ColdTaiga_beach_water, ColdTaiga_ocean)
["178+193"], # 4 - Giant Tree Taiga (MegaTaiga, MegaTaiga_ocean)
["178+193"], # 5 - Giant Tree Taiga (MegaSpruceTaiga, MegaSpruceTaiga_ocean)
["203+239"], # 6 - Montains (ExtremeHills, ExtremeHills_beach, ExtremeHills_ocean)
["203+239"], # 7 - Montains (ExtremeHillsM, ExtremeHillsM_ocean)
["203+239"], # 8 - Montains (ExtremeHills+, ExtremeHills+_snowtop, ExtremeHills+_ocean)
["50+173"], # 9 - Beach (StoneBeach, StoneBeach_ocean)
["255+255"], # 10 - Snowy Tundra (IcePlains, IcePlains_ocean)
["50+173"], # 11 - Sunflower Plains (SunflowerPlains, SunflowerPlains_ocean)
["191+203"], # 12 - Taiga (Taiga, Taiga_beach, Taiga_ocean)
["76+112"], # 13 - Forest (Forest, Forest_beach, Forest_ocean)
["76+112"], # 14 - Flower Forest (FlowerForest, FlowerForest_beach, FlowerForest_ocean)
["101+163"], # 15 - Birch Forest (BirchForest, BirchForest_ocean)
["101+163"], # 16 - Birch Forest Hills (BirchForestM, BirchForestM_ocean)
["0+255"], # 17 - Desert and Nether (Desert, Desert_ocean, Nether)
["76+112", "#28340A"], # 18 - Dark Forest (RoofedForest, RoofedForest_ocean)
["#90814d"], # 19 - Mesa (Mesa, Mesa_sandlevel, Mesa_ocean, )
["#90814d"], # 20 - Mesa (MesaBryce, MesaBryce_sandlevel, MesaBryce_ocean)
["#90814d"], # 21 - Mesa (MesaPlateauF, MesaPlateauF_grasstop, MesaPlateauF_sandlevel, MesaPlateauF_ocean)
["#90814d"], # 22 - Mesa (MesaPlateauFM, MesaPlateauFM_grasstop, MesaPlateauFM_sandlevel, MesaPlateauFM_ocean)
["0+255"], # 23 - Shattered Savanna (or Savanna Plateau ?) (SavannaM, SavannaM_ocean)
["12+36"], # 24 - Jungle (Jungle, Jungle_shore, Jungle_ocean)
["12+36"], # 25 - Modified Jungle (JungleM, JungleM_shore, JungleM_ocean)
["12+61"], # 26 - Jungle Edge (JungleEdge, JungleEdge_ocean)
["12+61"], # 27 - Modified Jungle Edge (JungleEdgeM, JungleEdgeM_ocean)
["#6A7039"], # 28 - Swamp (Swampland, Swampland_shore, Swampland_ocean)
["25+25"], # 29 - Mushroom Fields and Mushroom Field Shore (MushroomIsland, MushroomIslandShore, MushroomIsland_ocean)
]
for o in offset:
colorize(GRASS, tex_dir+"/blocks/grass_top.png", o[0], str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_"+o[2]+".png")
colorize_alpha(GRASS, tex_dir+"/blocks/grass_side_overlay.png", o[0], str(PXSIZE), target_dir("/mods/ITEMS/mcl_core/textures")+"/default_"+o[2]+"_side.png")
grass_palette_file = target_dir("/mods/ITEMS/mcl_core/textures") + "/mcl_core_palette_grass.png"
os.system("convert -size 16x16 canvas:transparent " + grass_palette_file)
for i, color in enumerate(grass_colors):
if color[0][0] == "#":
os.system("convert -size 1x1 xc:\"" + color[0] + "\" " + tempfile1.name + ".png")
else:
os.system("convert " + GRASS + " -crop 1x1+" + color[0] + " " + tempfile1.name + ".png")
if len(color) > 1:
os.system("convert " + tempfile1.name + ".png \\( -size 1x1 xc:\"" + color[1] + "\" \\) -compose blend -define compose:args=50,50 -composite " + tempfile1.name + ".png")
os.system("convert " + grass_palette_file + " \\( " + tempfile1.name + ".png -geometry +" + str(i % 16) + "+" + str(int(i / 16)) + " \\) -composite " + grass_palette_file)
# Metadata
if make_texture_pack:

View File

@ -13,6 +13,7 @@ local colors = {
["3D Models"] = "0x0019FF",
["Textures"] = "0xFF9705",
["Translations"] = "0x00FF60",
["Funders"] = "0xF7FF00",
["Special thanks"] = "0x00E9FF",
}