Compare commits
1 Commits
master
...
fix-portal
Author | SHA1 | Date |
---|---|---|
|
97e9dae7c6 |
|
@ -27,41 +27,19 @@ Any Pull Request that isn't a bug fix can be closed within a week unless it rece
|
||||||
|
|
||||||
Start coding!
|
Start coding!
|
||||||
|
|
||||||
Refer to [Minetest Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt), [Developer Wiki](https://dev.minetest.net/), [MineClone 5 Wiki](https://git.minetest.land/MineClone5/MineClone5/wiki/) and other documentation.
|
Refer to Minetest Lua API, Developer Wiki and other documentation.
|
||||||
|
|
||||||
Follow [Lua code style guidelines](https://dev.minetest.net/Lua_code_style_guidelines). Use tabs, not spaces for indentation (tab size = 8). Never use `minetest.env`.
|
Follow Lua code style guidelines. Use tabs, not spaces for indentation (tab size = 8). Never use `minetest.env`.
|
||||||
|
|
||||||
If you do a translation, try detecting translational issues with `check_translate_files.py` - just run it from tools folder:
|
|
||||||
```bash
|
|
||||||
# python3 check_translate_files.py fr | less
|
|
||||||
```
|
|
||||||
(`fr` is a language code)
|
|
||||||
|
|
||||||
Check your code works as expected.
|
Check your code works as expected.
|
||||||
|
|
||||||
Commit & push your changes to a new branch (not master, one change per a branch).
|
Commit & push your changes to a new branch (not master, one change per branch)
|
||||||
|
|
||||||
Commit messages should use the present tense and be descriptive.
|
Commit messages should use the present tense and be descriptive.
|
||||||
|
|
||||||
Once you are happy with your changes, submit a pull request.
|
Once you are happy with your changes, submit a pull request.
|
||||||
|
|
||||||
A pull-request is considered merge-able when it looks good to one person from the community.
|
A pull-request is considered merge-able when:
|
||||||
|
|
||||||
Please invite other developers to review your contribution when you know they are online. If there is no any reaction during 24 hours after posting the invitation and pinging developers - you are welcome to do a self-review and merge the request.
|
|
||||||
|
|
||||||
If someone else's contribution looks good to you - you are free to merge it ASAP.
|
|
||||||
|
|
||||||
Different git branches are welcomed! Releases by different people are welcomed! Releases from different branches are welcomed! Frequent releases are welcomed!
|
|
||||||
|
|
||||||
It is nice not to block other developers by your work and don't dictate them what to do, unsless they really want that. Git branches and forks are recommended to avoid conflicts at development stage.
|
|
||||||
|
|
||||||
It is nice to try splitting big features into small steps.
|
|
||||||
|
|
||||||
It is nice to create an issue for any work and mention the issue in the commit text, like `#123 Fix blast resistance of cactus`, where `#123` is the issue number.
|
|
||||||
|
|
||||||
Actually, it looks like we all love what we do, so any stupid situations should be carefully discussed before merging into upstreams. But nothing prevents us from releasing controversial stuff through dedicated branches. Release your contribution when you need more feedback.
|
|
||||||
|
|
||||||
Feel free to break the rules if you're sure you have to.
|
|
||||||
|
|
||||||
#### Contributors
|
#### Contributors
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
|
||||||
* `coral_block=X`: Coral block (1 = alive, 2 = dead)
|
* `coral_block=X`: Coral block (1 = alive, 2 = dead)
|
||||||
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
* `coral_species=X`: Specifies the species of a coral; equal X means equal species
|
||||||
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
* `set_on_fire=X`: Sets any (not fire-resistant) mob or player on fire for X seconds when touching
|
||||||
* `compostability`: Amount from 1 to 100 that defines the percentage of likelyhood that the composter will advance a level.
|
|
||||||
|
|
||||||
#### Footnotes
|
#### Footnotes
|
||||||
|
|
||||||
|
@ -201,8 +200,6 @@ These groups are used mostly for informational purposes
|
||||||
* `building_block=1`: Block is a building block
|
* `building_block=1`: Block is a building block
|
||||||
* `deco_block=1`: Block is a decorational block
|
* `deco_block=1`: Block is a decorational block
|
||||||
|
|
||||||
* `blast_furnace_smeltable=1` : Item or node is smeltable by a blast furnace
|
|
||||||
* `smoker_cookable=1` : Food is cookable by a smoker.
|
|
||||||
|
|
||||||
## Fake item groups
|
## Fake item groups
|
||||||
These groups put similar items together which should all be treated by the gameplay or the GUI as a single item.
|
These groups put similar items together which should all be treated by the gameplay or the GUI as a single item.
|
||||||
|
|
10
README.md
|
@ -70,11 +70,7 @@ an explanation.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This game requires latest stable [Minetest](http://minetest.net) to run, please install
|
This game requires latest stable [Minetest](http://minetest.net) to run, please install
|
||||||
it first. Only latest stable version of Minetest is officially supported.
|
it first. Only stable versions of Minetest are officially supported.
|
||||||
There are lots of questions about Ubuntu, which has minetest-5.1.1 still.
|
|
||||||
Please, first of all, visit this page, it should fix the problem: https://launchpad.net/~minetestdevs/+archive/ubuntu/stable
|
|
||||||
Also, here is endless issue #123: https://git.minetest.land/MineClone5/MineClone5/issues/123 - really less preferable way.
|
|
||||||
|
|
||||||
There is no support for running MineClone 5 in development versions of Minetest.
|
There is no support for running MineClone 5 in development versions of Minetest.
|
||||||
|
|
||||||
To install MineClone 5 (if you haven't already), move this directory into the
|
To install MineClone 5 (if you haven't already), move this directory into the
|
||||||
|
@ -192,7 +188,3 @@ Technical differences from Minecraft:
|
||||||
* `API.md`: For Minetest modders who want to mod this game
|
* `API.md`: For Minetest modders who want to mod this game
|
||||||
* `LEGAL.md`: Legal information
|
* `LEGAL.md`: Legal information
|
||||||
* `CREDITS.md`: List of everyone who contributed
|
* `CREDITS.md`: List of everyone who contributed
|
||||||
|
|
||||||
## Menu music
|
|
||||||
|
|
||||||
* horizonchris96 — 02_what_we_ll_build_next
|
|
||||||
|
|
BIN
menu/theme.ogg
|
@ -36,10 +36,6 @@ mgvalleys_spflags = noaltitude_chill,noaltitude_dry,nohumid_rivers,vary_river_de
|
||||||
# Probably values >10 won't work because of numerous overridings. Type: int.
|
# Probably values >10 won't work because of numerous overridings. Type: int.
|
||||||
max_block_generate_distance = 13
|
max_block_generate_distance = 13
|
||||||
|
|
||||||
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
|
|
||||||
# type: int
|
|
||||||
chunksize = 4
|
|
||||||
|
|
||||||
# MCL2-specific stuff
|
# MCL2-specific stuff
|
||||||
keepInventory = false
|
keepInventory = false
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# mcl_bubble_column by j45
|
||||||
|
|
||||||
|
https://github.com/Minetest-j45/mcl_bubble_column/
|
||||||
|
|
||||||
|
Adds whirlpools and upwards bubble columns to Mineclone2/5
|
||||||
|
|
||||||
|
A bubble column is a block generated by placing magma blocks or soul sand in water (source).
|
||||||
|
|
||||||
|
Bubble columns push or pull entities and items in certain directions.
|
|
@ -0,0 +1,195 @@
|
||||||
|
mcl_bubble_column = {}
|
||||||
|
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "bubbleColumnUpStop",
|
||||||
|
nodenames = {"group:water"},
|
||||||
|
interval = 0.05,
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
if meta:get_int("bubbly") == 1 then--bubble column
|
||||||
|
--check down if current needs to be deleted
|
||||||
|
local downpos = vector.add(pos, {x = 0, y = -1, z = 0})
|
||||||
|
local downposnode = minetest.get_node(downpos)
|
||||||
|
local downmeta = minetest.get_meta(downpos)
|
||||||
|
if (downmeta:get_int("bubbly") ~= 1 and downposnode.name ~= "mcl_nether:soul_sand") then
|
||||||
|
meta:set_int("bubbly", 0)
|
||||||
|
end
|
||||||
|
--check up to see if needs to go up
|
||||||
|
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||||
|
local upposnode = minetest.get_node(uppos)
|
||||||
|
local upmeta = minetest.get_meta(uppos)
|
||||||
|
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("bubbly") ~= 1) then
|
||||||
|
upmeta:set_int("bubbly", 1)
|
||||||
|
end
|
||||||
|
elseif meta:get_int("whirly") == 1 then--whirlpool
|
||||||
|
--check down if current needs to be deleted
|
||||||
|
local downpos = vector.add(pos, {x = 0, y = -1, z = 0})
|
||||||
|
local downposnode = minetest.get_node(downpos)
|
||||||
|
local downmeta = minetest.get_meta(downpos)
|
||||||
|
if (downmeta:get_int("whirly") ~= 1 and downposnode.name ~= "mcl_nether:magma") then
|
||||||
|
meta:set_int("whirly", 0)
|
||||||
|
end
|
||||||
|
--check up to see if needs to go up
|
||||||
|
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||||
|
local upposnode = minetest.get_node(uppos)
|
||||||
|
local upmeta = minetest.get_meta(uppos)
|
||||||
|
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("whirly") ~= 1) then
|
||||||
|
upmeta:set_int("whirly", 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "startBubbleColumn",
|
||||||
|
nodenames = {"mcl_nether:soul_sand"},
|
||||||
|
interval = 0.05,
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||||
|
local upposnode = minetest.get_node(uppos)
|
||||||
|
local upmeta = minetest.get_meta(uppos)
|
||||||
|
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("bubbly") ~= 1) then
|
||||||
|
upmeta:set_int("bubbly", 1)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "startWhirlpool",
|
||||||
|
nodenames = {"mcl_nether:magma"},
|
||||||
|
interval = 0.05,
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
local uppos = vector.add(pos, {x = 0, y = 1, z = 0})
|
||||||
|
local upposnode = minetest.get_node(uppos)
|
||||||
|
local upmeta = minetest.get_meta(uppos)
|
||||||
|
if (minetest.get_item_group(upposnode.name, "water") == 3 and upmeta:get_int("whirly") ~= 1) then
|
||||||
|
upmeta:set_int("whirly", 1)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mcl_bubble_column.on_enter_bubble_column = function(self)
|
||||||
|
local velocity = self:get_velocity()
|
||||||
|
--[[if down.name == "mcl_nether:soul_sand" then
|
||||||
|
self:add_velocity({x = 0, y = math.min(10, math.abs(velocity.y)+9.4), z = 0})
|
||||||
|
else]]
|
||||||
|
self:add_velocity({x = 0, y = math.min(3.6, math.abs(velocity.y)+3), z = 0})
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_bubble_column.on_enter_whirlpool = function(self)
|
||||||
|
local velocity = self:get_velocity()
|
||||||
|
--self:add_velocity({x = 0, y = math.max(-3, (-math.abs(velocity.y))-2), z = 0})
|
||||||
|
self:add_velocity({x = 0, y = math.max(-0.3, (-math.abs(velocity.y))-0.03), z = 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_bubble_column.on_enter_bubble_column_with_air_above = function(self)
|
||||||
|
local velocity = self:get_velocity()
|
||||||
|
--[[if down.name == "mcl_nether:soul_sand" then
|
||||||
|
self:add_velocity({x = 0, y = math.min(4.3, math.abs(velocity.y)+2.8), z = 0})
|
||||||
|
else]]
|
||||||
|
self:add_velocity({x = 0, y = math.min(2.6, math.abs(velocity.y)+2), z = 0})
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_bubble_column.on_enter_whirlpool_with_air_above = function(self)
|
||||||
|
local velocity = self:get_velocity()
|
||||||
|
--self:add_velocity({x = 0, y = math.max(-3.5, (-math.abs(velocity.y))-2), z = 0})
|
||||||
|
self:add_velocity({x = 0, y = math.max(-0.9, (-math.abs(velocity.y))-0.03), z = 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "entGo",
|
||||||
|
nodenames = {"group:water"},
|
||||||
|
interval = 0.05,
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
--if not bubble column block return
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
if meta:get_int("bubbly") == 1 then
|
||||||
|
local up = minetest.get_node(vector.add(pos, {x = 0, y = 1, z = 0}))
|
||||||
|
for _,entity in pairs(minetest.get_objects_inside_radius(pos, 0.75)) do
|
||||||
|
if up.name == "air" then
|
||||||
|
mcl_bubble_column.on_enter_bubble_column_with_air_above(entity)
|
||||||
|
else
|
||||||
|
mcl_bubble_column.on_enter_bubble_column(entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif meta:get_int("whirly") == 1 then
|
||||||
|
local up = minetest.get_node(vector.add(pos, {x = 0, y = 1, z = 0}))
|
||||||
|
for _,entity in pairs(minetest.get_objects_inside_radius(pos, 0.75)) do
|
||||||
|
if up.name == "air" then
|
||||||
|
mcl_bubble_column.on_enter_whirlpool_with_air_above(entity)
|
||||||
|
else
|
||||||
|
mcl_bubble_column.on_enter_whirlpool(entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
minetest.register_globalstep(function()
|
||||||
|
for _,player in ipairs(minetest.get_connected_players()) do
|
||||||
|
local ppos = player:get_pos()
|
||||||
|
local eyepos = {x = ppos.x, y = ppos.y + player:get_properties().eye_height, z = ppos.z}
|
||||||
|
local node = minetest.get_node(ppos)
|
||||||
|
local eyenode = minetest.get_node(eyepos)
|
||||||
|
local meta = minetest.get_meta(ppos)
|
||||||
|
local eyemeta = minetest.get_meta(eyepos)
|
||||||
|
|
||||||
|
local eyemeta = minetest.get_meta(ppos)
|
||||||
|
--if minetest.get_item_group(node.name, "water") == 3 and minetest.get_item_group(eyenode.name, "water") == 3 then return end
|
||||||
|
if meta:get_int("bubbly") == 1 or eyemeta:get_int("bubbly") == 1 then
|
||||||
|
local up = minetest.get_node(vector.add(eyepos, {x = 0, y = 1, z = 0}))
|
||||||
|
if up.name == "air" then
|
||||||
|
mcl_bubble_column.on_enter_bubble_column_with_air_above(player)
|
||||||
|
else
|
||||||
|
mcl_bubble_column.on_enter_bubble_column(player)
|
||||||
|
end
|
||||||
|
elseif meta:get_int("whirly") == 1 or eyemeta:get_int("whirly") == 1 then
|
||||||
|
local up = minetest.get_node(vector.add(ppos, {x = 0, y = 1, z = 0}))
|
||||||
|
if up.name == "air" then
|
||||||
|
mcl_bubble_column.on_enter_whirlpool_with_air_above(player)
|
||||||
|
else
|
||||||
|
mcl_bubble_column.on_enter_whirlpool(player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--abms to remove and replace old bubble columns/whirlpools
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "removeOldFlowingColumns",
|
||||||
|
nodenames = {"mcl_bubble_column:water_flowing_up", "mcl_bubble_column:water_flowing_down"},
|
||||||
|
interval = 1,--reduce lag
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
minetest.set_node(pos, {name = "air"})
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "replaceBubbleColumns",
|
||||||
|
nodenames = {"mcl_bubble_column:water_source_up"},
|
||||||
|
interval = 1,--reduce lag
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
minetest.set_node(pos, {name = "mcl_core:water_source"})
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
meta:set_int("bubbly", 1)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
minetest.register_abm{
|
||||||
|
label = "replaceWhirlpools",
|
||||||
|
nodenames = {"mcl_bubble_column:water_source_down"},
|
||||||
|
interval = 1,--reduce lag
|
||||||
|
chance = 1,
|
||||||
|
action = function(pos)
|
||||||
|
minetest.set_node(pos, {name = "mcl_core:water_source"})
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
meta:set_int("whirly", 1)
|
||||||
|
end,
|
||||||
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 est mort(e) dans une explosion
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 es mòrt(a) dins una petarada.
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 попал под взрыв.
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=@1 困于爆炸.
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_explosions
|
|
||||||
@1 was caught in an explosion.=
|
|
|
@ -148,48 +148,16 @@ local chunk_scan_range = {
|
||||||
[ CS_NODES] = {LAST_BLOCK+1, LAST_BLOCK+1},
|
[ CS_NODES] = {LAST_BLOCK+1, LAST_BLOCK+1},
|
||||||
}
|
}
|
||||||
|
|
||||||
local EDGE_MIN = mcl_mapgen.EDGE_MIN
|
|
||||||
local EDGE_MAX = mcl_mapgen.EDGE_MAX
|
|
||||||
local function is_chunk_finished(minp)
|
local function is_chunk_finished(minp)
|
||||||
local center_x = minp.x + HALF_CS_NODES
|
local center = vector.add(minp, HALF_CS_NODES)
|
||||||
local center_y = minp.y + HALF_CS_NODES
|
for check_x = center.x - CS_NODES, center.x + CS_NODES, CS_NODES do
|
||||||
local center_z = minp.z + HALF_CS_NODES
|
for check_y = center.y - CS_NODES, center.y + CS_NODES, CS_NODES do
|
||||||
local from_x = center_x - CS_NODES
|
for check_z = center.z - CS_NODES, center.z + CS_NODES, CS_NODES do
|
||||||
local from_y = center_y - CS_NODES
|
local pos = vector.new(check_x, check_y, check_z)
|
||||||
local from_z = center_z - CS_NODES
|
if pos ~= center then
|
||||||
local to_x = center_x + CS_NODES
|
minetest_get_voxel_manip():read_from_map(pos, pos)
|
||||||
local to_y = center_y + CS_NODES
|
local node = minetest_get_node(pos)
|
||||||
local to_z = center_z + CS_NODES
|
|
||||||
if from_x < EDGE_MIN then from_x = center_x end
|
|
||||||
if from_y < EDGE_MIN then from_y = center_y end
|
|
||||||
if from_z < EDGE_MIN then from_z = center_z end
|
|
||||||
if to_x > EDGE_MAX then to_x = center_x end
|
|
||||||
if to_y > EDGE_MAX then to_y = center_y end
|
|
||||||
if to_z > EDGE_MAX then to_z = center_z end
|
|
||||||
for check_x = from_x, to_x, CS_NODES do
|
|
||||||
local are_we_in_central_chunk = check_x == center_x
|
|
||||||
for check_y = from_y, to_y, CS_NODES do
|
|
||||||
are_we_in_central_chunk = are_we_in_central_chunk and (check_y == center_y)
|
|
||||||
for check_z = from_z, to_z, CS_NODES do
|
|
||||||
are_we_in_central_chunk = are_we_in_central_chunk and (check_z == center_z)
|
|
||||||
if not are_we_in_central_chunk then
|
|
||||||
local check_pos = {x = check_x, y = check_y, z = check_z}
|
|
||||||
minetest_get_voxel_manip():read_from_map(check_pos, check_pos)
|
|
||||||
local node = minetest_get_node(check_pos)
|
|
||||||
if node.name == "ignore" then
|
if node.name == "ignore" then
|
||||||
-- return nil, means false, means, there is something to generate still,
|
|
||||||
-- (because one of adjacent chunks is unfinished - "ignore" means that),
|
|
||||||
-- means this chunk will be changed, at least one of its sides or corners
|
|
||||||
-- means it's unsafe to place anything there right now, it might disappar,
|
|
||||||
-- better to wait, see the diagram of conflict/ok areas per a single axis:
|
|
||||||
|
|
||||||
-- conflict| ok |conflict|conflict| ok |conflict|conflict| ok |conflict
|
|
||||||
-- (_________Chunk1_________)|(_________Chunk2_________)|(_________Chunk3_________)
|
|
||||||
-- [Block1]|[MidBlk]|[BlockN]|[Block1]|[MidBlk]|[BlockN]|[Block1]|[MidBlk]|[BlockN]
|
|
||||||
-- \_____________Chunk2-with-shell____________/
|
|
||||||
-- ...______Chunk1-with-shell________/ \________Chunk3-with-shell______...
|
|
||||||
-- Generation of chunk 1 AFFECTS 2 ^^^ ^^^ Generation of chunk 3 affects 2
|
|
||||||
-- ^^^^^^^^Chunk 2 gen. affects 1 and 3^^^^^^^^
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -357,7 +325,7 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
|
||||||
-- mcl_mapgen.register_mapgen_lvm(function(vm_context), order_number) --
|
-- mcl_mapgen.register_mapgen_lvm(function(vm_context), order_number) --
|
||||||
-- --
|
-- --
|
||||||
for _, v in pairs(queue_chunks_lvm) do
|
for _, v in pairs(queue_chunks_lvm) do
|
||||||
v.f(vm_context)
|
vm_context = v.f(vm_context)
|
||||||
end
|
end
|
||||||
-- --
|
-- --
|
||||||
-- mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed, vm_context), order_number) --
|
-- mcl_mapgen.register_mapgen(function(minp, maxp, chunkseed, vm_context), order_number) --
|
||||||
|
@ -394,17 +362,7 @@ minetest.register_on_generated(function(minp, maxp, chunkseed)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local register_on_generated_old = minetest.register_on_generated
|
minetest.register_on_generated = mcl_mapgen.register_chunk_generator
|
||||||
minetest.register_on_generated = function(...)
|
|
||||||
minetest.log("warning", "minetest.register_on_generated() is being called by the mod '"
|
|
||||||
.. minetest.get_current_modname() .. "'. MineClone5's map generation system avoids using "
|
|
||||||
.. "this callback to work around engine map generation issues. If possible please read "
|
|
||||||
.. "mods/CORE/mcl_mapgen/API.md and update " .. minetest.get_current_modname()
|
|
||||||
.. " to use the method described from there. MineClone5 makes no promises that "
|
|
||||||
.. "mapgen mods will be fully compatible with it, please test your server and use at "
|
|
||||||
.. "your own risk.")
|
|
||||||
return register_on_generated_old(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mapgen.get_far_node(p)
|
function mcl_mapgen.get_far_node(p)
|
||||||
local p = p
|
local p = p
|
||||||
|
@ -450,21 +408,6 @@ function mcl_mapgen.get_chunk_number(pos) -- unsigned int
|
||||||
c.x + k_positive
|
c.x + k_positive
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Components of this game should register functions here to update their internal
|
|
||||||
-- state when external mods modify mapgen settings that they care about.
|
|
||||||
local settings_changed_callbacks = {}
|
|
||||||
function mcl_mapgen.register_on_settings_changed(callback)
|
|
||||||
table.insert(settings_changed_callbacks, callback)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- this is to be called by external mods after modifying these settings
|
|
||||||
-- to notify Mineclone that it needs to update local copies and whatever's based on them.
|
|
||||||
function mcl_mapgen.on_settings_changed()
|
|
||||||
for _, callback in pairs(settings_changed_callbacks) do
|
|
||||||
callback()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mapgen.minecraft_height_limit = 256
|
mcl_mapgen.minecraft_height_limit = 256
|
||||||
|
|
||||||
mcl_mapgen.bedrock_is_rough = normal
|
mcl_mapgen.bedrock_is_rough = normal
|
||||||
|
@ -473,7 +416,7 @@ mcl_mapgen.bedrock_is_rough = normal
|
||||||
overworld.min = -62
|
overworld.min = -62
|
||||||
if superflat then
|
if superflat then
|
||||||
mcl_mapgen.ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
|
mcl_mapgen.ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
|
||||||
overworld.min = mcl_mapgen.ground - 3
|
overworld.min = ground - 3
|
||||||
end
|
end
|
||||||
-- if singlenode then mcl_mapgen.overworld.min = -66 end -- DONT KNOW WHY
|
-- if singlenode then mcl_mapgen.overworld.min = -66 end -- DONT KNOW WHY
|
||||||
overworld.max = mcl_mapgen.EDGE_MAX
|
overworld.max = mcl_mapgen.EDGE_MAX
|
||||||
|
@ -537,6 +480,7 @@ function mcl_mapgen.get_voxel_manip(vm_context)
|
||||||
return vm_context.vm
|
return vm_context.vm
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local CS_NODES = mcl_mapgen.CS_NODES
|
||||||
function mcl_mapgen.clamp_to_chunk(x, size)
|
function mcl_mapgen.clamp_to_chunk(x, size)
|
||||||
if not size then
|
if not size then
|
||||||
minetest.log("warning", "[mcl_mapgen] Couldn't clamp " .. tostring(x) .. " - missing size")
|
minetest.log("warning", "[mcl_mapgen] Couldn't clamp " .. tostring(x) .. " - missing size")
|
||||||
|
@ -560,33 +504,6 @@ function mcl_mapgen.clamp_to_chunk(x, size)
|
||||||
end
|
end
|
||||||
return x - overflow
|
return x - overflow
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_mapgen.get_chunk_beginning(x)
|
function mcl_mapgen.get_chunk_beginning(x)
|
||||||
if tonumber(x) then
|
|
||||||
return x - ((x + central_chunk_min_pos) % CS_NODES)
|
return x - ((x + central_chunk_min_pos) % CS_NODES)
|
||||||
end
|
end
|
||||||
if x.x then
|
|
||||||
return {
|
|
||||||
x = mcl_mapgen.get_chunk_beginning(x.x),
|
|
||||||
y = mcl_mapgen.get_chunk_beginning(x.y),
|
|
||||||
z = mcl_mapgen.get_chunk_beginning(x.z)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_mapgen.get_chunk_ending(x)
|
|
||||||
if tonumber(x) then
|
|
||||||
return mcl_mapgen.get_chunk_beginning(x) + LAST_NODE_IN_CHUNK
|
|
||||||
end
|
|
||||||
if x.x then
|
|
||||||
return {
|
|
||||||
x = mcl_mapgen.get_chunk_beginning(x.x) + LAST_NODE_IN_CHUNK,
|
|
||||||
y = mcl_mapgen.get_chunk_beginning(x.y) + LAST_NODE_IN_CHUNK,
|
|
||||||
z = mcl_mapgen.get_chunk_beginning(x.z) + LAST_NODE_IN_CHUNK
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_mapgen.get_block_seed = get_block_seed
|
|
||||||
mcl_mapgen.get_block_seed2 = get_block_seed2
|
|
||||||
mcl_mapgen.get_block_seed3 = get_block_seed3
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
# mcl_time v2.2
|
# mcl_time
|
||||||
## by kay27 for MineClone 5
|
## by kay27 for MineClone 5
|
||||||
---------------------------
|
---------------------------
|
||||||
This mod counts time when all players sleep or some area is inactive.
|
This mod counts time when all players sleep or some area is inactive.
|
||||||
|
|
||||||
It depends very much on `time_speed` configuration variable, which could be changed 'on the fly' by a chat command:
|
It depends very much on `time_speed` configuration variable, which could be changed 'on the fly' by a chat command.
|
||||||
* `/set time_speed 72`
|
|
||||||
|
|
||||||
If `time_speed` set to 0, this mod logs warnings and returns zeroes.
|
If `time_speed` set to 0, this mod logs warnings and returns zeroes.
|
||||||
|
|
||||||
|
@ -16,15 +15,13 @@ Usually this value grow smoothly. But when you skip the night being in the bed,
|
||||||
|
|
||||||
### mcl_time.get_number_of_times(last_time, interval, chance)
|
### mcl_time.get_number_of_times(last_time, interval, chance)
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
Returns the number of how many times something would probably happen if the area was active and we didn't skip the nights.
|
Handy to process AMBs.
|
||||||
|
|
||||||
Arguments:
|
You pass `last_time` - last known value of `seconds_irl`, also ABM `interval` and ABM `chance`.
|
||||||
* `last_time` - you pass last known for you value of `seconds_irl`
|
|
||||||
* `interval` and `chance` - interval and chance like from ABM setup
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
* Integer number of how many times something would probably happen if the area was active all the time and we didn't skip the nights.
|
* Integer number of how many times ABM function should be called if the area was active all the time and you didn't skip the night.
|
||||||
* Integer value of in-real-life (not in-game) seconds since world creation.
|
* Integer value of realtime (not in-game) seconds since world creation.
|
||||||
|
|
||||||
### mcl_time.touch(pos)
|
### mcl_time.touch(pos)
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -32,76 +29,24 @@ This function 'toches' node at position `pos` by writing `_t` meta variable of `
|
||||||
|
|
||||||
### mcl_time.get_number_of_times_at_pos(pos, interval, chance)
|
### mcl_time.get_number_of_times_at_pos(pos, interval, chance)
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
|
Much more handy to call from LBM on area load, than `mcl_time.get_number_of_times()`!
|
||||||
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
|
|
||||||
|
|
||||||
Argunments:
|
It reads meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, which then pass as first argument into `mcl_time.get_number_of_times()`.
|
||||||
* `pos` - node position
|
After calling this, it also 'touches' the node at `pos` by writing `seconds_irl` into meta variable `_t`.
|
||||||
* `interval` and `chance` - interval and chance like from ABM setup
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
|
* Integer number of how many times ABM function should be called if the area was active all the time and you didn't skip the night.
|
||||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `0`.
|
* Integer value of realtime (not in-game) seconds since world creation.
|
||||||
|
|
||||||
|
*Warning!* This function can return 0. So it's better not to use it for regular ABMs - use `mcl_time.get_number_of_times_at_pos_or_1()` instead.
|
||||||
|
|
||||||
### mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
|
### mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
|
Much more handy to process ABMs than `mcl_time.get_number_of_times()` and `mcl_time.get_number_of_times_at_pos()`!
|
||||||
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
|
|
||||||
|
|
||||||
Argunments:
|
It just calls `mcl_time.get_number_of_times_at_pos()` but doesn't return 0, the minimum number it can return is 1,
|
||||||
* `pos` - node position
|
which is the most suitable for regular ABM processing function.
|
||||||
* `interval` and `chance` - interval and chance like from ABM setup
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
|
* Integer number of how many times ABM function should be called if the area was active all the time and you didn't skip the night.
|
||||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `1`.
|
* Integer value of realtime (not in-game) seconds since world creation.
|
||||||
|
|
||||||
### mcl_time.get_number_of_times_at_pos_or_nil(pos, interval, chance)
|
|
||||||
---------------------------------------------------------------------
|
|
||||||
Returns the number of how many times something would probably happen for node at pos `pos` if the area was active and we didn't skip the nights.
|
|
||||||
It reads and updates meta variable `_t` from position `pos` and uses it as previous `seconds_irl`, so we don't need to remember it.
|
|
||||||
|
|
||||||
Argunments:
|
|
||||||
* `pos` - node position
|
|
||||||
* `interval` and `chance` - interval and chance like from ABM setup
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
* Integer number of how many times something would happen to the node at position `pos` if the area was active all the time and we didn't skip the nights.
|
|
||||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `nil`.
|
|
||||||
|
|
||||||
### mcl_time.get_irl_seconds_passed_at_pos(pos)
|
|
||||||
-----------------------------------------------
|
|
||||||
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
|
|
||||||
It uses node meta variable `_t` to calculate this value.
|
|
||||||
|
|
||||||
Argunments:
|
|
||||||
* `pos` - node position
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
|
|
||||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `0`.
|
|
||||||
|
|
||||||
### mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
|
|
||||||
----------------------------------------------------
|
|
||||||
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
|
|
||||||
It uses node meta variable `_t` to calculate this value.
|
|
||||||
|
|
||||||
Argunments:
|
|
||||||
* `pos` - node position
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
|
|
||||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `1`.
|
|
||||||
|
|
||||||
### mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
|
|
||||||
----------------------------------------------------
|
|
||||||
Returns the number of how many in-real-life seconds would be passed for the node at position `pos`, if the area was active all the time and we didn't skip the nights.
|
|
||||||
It uses node meta variable `_t` to calculate this value.
|
|
||||||
|
|
||||||
Argunments:
|
|
||||||
* `pos` - node position
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
* Integer number of how many in-real-life seconds would be passed for the node at position `pos, if the area was active all the time and we didn't skip the nights.
|
|
||||||
* For unclear conditions, like missing meta or zero `time_speed`, this function will return `nil`.
|
|
||||||
|
|
||||||
|
|
|
@ -14,21 +14,11 @@ local last_save_seconds_irl = seconds_irl_public
|
||||||
local next_save_seconds_irl = last_save_seconds_irl + save_to_storage_interval
|
local next_save_seconds_irl = last_save_seconds_irl + save_to_storage_interval
|
||||||
|
|
||||||
local previous_seconds_irl = -2
|
local previous_seconds_irl = -2
|
||||||
local time_speed_is_ok = true
|
|
||||||
|
|
||||||
local function get_seconds_irl()
|
local function get_seconds_irl()
|
||||||
local time_speed = tonumber(minetest.settings:get("time_speed") or default_time_speed)
|
local time_speed = tonumber(minetest.settings:get("time_speed") or default_time_speed)
|
||||||
if time_speed < 1 then
|
if time_speed < 1 then
|
||||||
if time_speed_is_ok then
|
|
||||||
minetest.log("warning", "[mcl_time] time_speed < 1 - please increase to make mcl_time api work (default: " .. default_time_speed .. ")")
|
minetest.log("warning", "[mcl_time] time_speed < 1 - please increase to make mcl_time api work (default: " .. default_time_speed .. ")")
|
||||||
time_speed_is_ok = false
|
|
||||||
end
|
|
||||||
return 0
|
return 0
|
||||||
else
|
|
||||||
if not time_speed_is_ok then
|
|
||||||
minetest.log("warning", "[mcl_time] time_speed is now " .. time_speed)
|
|
||||||
time_speed_is_ok = true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
local irl_multiplier = 86400 / time_speed
|
local irl_multiplier = 86400 / time_speed
|
||||||
local day_count = minetest.get_day_count()
|
local day_count = minetest.get_day_count()
|
||||||
|
@ -61,10 +51,10 @@ local function get_seconds_irl()
|
||||||
next_save_seconds_irl = seconds_irl + save_to_storage_interval
|
next_save_seconds_irl = seconds_irl + save_to_storage_interval
|
||||||
end
|
end
|
||||||
|
|
||||||
return math.floor(seconds_irl)
|
return seconds_irl
|
||||||
end
|
end
|
||||||
|
|
||||||
seconds_irl_public = get_seconds_irl()
|
local seconds_irl_public = get_seconds_irl()
|
||||||
|
|
||||||
function mcl_time.get_seconds_irl()
|
function mcl_time.get_seconds_irl()
|
||||||
return seconds_irl_public
|
return seconds_irl_public
|
||||||
|
@ -76,14 +66,14 @@ local function time_runner()
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_time.get_number_of_times(last_time, interval, chance)
|
function mcl_time.get_number_of_times(last_time, interval, chance)
|
||||||
if not last_time then return 0, seconds_irl_publicend end
|
if not last_time then return 0 end
|
||||||
if seconds_irl_public < 2 then return 0, seconds_irl_public end
|
if seconds_irl_public < 2 then return 0 end
|
||||||
if not interval then return 0, seconds_irl_public end
|
if not interval then return 0 end
|
||||||
if not chance then return 0, seconds_irl_public end
|
if not chance then return 0 end
|
||||||
if interval < 1 then return 0, seconds_irl_public end
|
if interval < 1 then return 0 end
|
||||||
if chance < 1 then return 0, seconds_irl_public end
|
if chance < 1 then return 0 end
|
||||||
local number_of_intervals = (seconds_irl_public - last_time) / interval
|
local number_of_intervals = (seconds_irl_public - last_time) / interval
|
||||||
if number_of_intervals < 1 then return 0, seconds_irl_public end
|
if number_of_intervals < 1 then return 0 end
|
||||||
local average_chance = (1 + chance) / 2
|
local average_chance = (1 + chance) / 2
|
||||||
local number_of_times = math.floor(number_of_intervals / average_chance)
|
local number_of_times = math.floor(number_of_intervals / average_chance)
|
||||||
return number_of_times, seconds_irl_public
|
return number_of_times, seconds_irl_public
|
||||||
|
@ -96,56 +86,44 @@ function mcl_time.touch(pos)
|
||||||
meta:set_int(meta_name, seconds_irl_public)
|
meta:set_int(meta_name, seconds_irl_public)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local touch = mcl_time.touch
|
||||||
|
|
||||||
function mcl_time.get_number_of_times_at_pos(pos, interval, chance)
|
function mcl_time.get_number_of_times_at_pos(pos, interval, chance)
|
||||||
if not pos then return 0 end
|
if not pos then return 0 end
|
||||||
if not time_speed_is_ok then return 0 end
|
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local last_time = meta:get_int(meta_name)
|
local last_time = meta:get_int(meta_name)
|
||||||
meta:set_int(meta_name, seconds_irl_public)
|
local number_of_times = (last_time == 0) and 0 or get_number_of_times(last_time, interval, chance)
|
||||||
local number_of_times = (last_time <= 0) and 0 or get_number_of_times(last_time, interval, chance)
|
touch(pos)
|
||||||
return number_of_times
|
return number_of_times, seconds_irl_public
|
||||||
end
|
end
|
||||||
|
|
||||||
local get_number_of_times_at_pos = mcl_time.get_number_of_times_at_pos
|
local get_number_of_times_at_pos = mcl_time.get_number_of_times_at_pos
|
||||||
|
|
||||||
function mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
|
function mcl_time.get_number_of_times_at_pos_or_1(pos, interval, chance)
|
||||||
return math.max(get_number_of_times_at_pos(pos, interval, chance), 1)
|
return math.max(get_number_of_times_at_pos(pos, interval, chance), 1), seconds_irl_public
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_time.get_number_of_times_at_pos_or_nil(pos, interval, chance)
|
|
||||||
local number_of_times_at_pos = get_number_of_times_at_pos(pos, interval, chance)
|
|
||||||
if number_of_times_at_pos > 0 then
|
|
||||||
return number_of_times_at_pos
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_time.get_irl_seconds_passed_at_pos(pos)
|
function mcl_time.get_irl_seconds_passed_at_pos(pos)
|
||||||
if not pos then return 0 end
|
if not pos then return 0 end
|
||||||
if not time_speed_is_ok then return 0 end
|
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local last_time = meta:get_int(meta_name)
|
local last_time = meta:get_int(meta_name)
|
||||||
meta:set_int(meta_name, seconds_irl_public)
|
local irl_seconds_passed = (last_time == 0) and 0 or (seconds_irl_public - last_time)
|
||||||
local irl_seconds_passed = (last_time <= 0) and 0 or (seconds_irl_public - last_time)
|
|
||||||
return irl_seconds_passed
|
return irl_seconds_passed
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
|
function mcl_time.get_irl_seconds_passed_at_pos_or_1(pos)
|
||||||
if not pos then return 1 end
|
if not pos then return 1 end
|
||||||
if not time_speed_is_ok then return 1 end
|
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local last_time = meta:get_int(meta_name)
|
local last_time = meta:get_int(meta_name)
|
||||||
meta:set_int(meta_name, seconds_irl_public)
|
local irl_seconds_passed = (last_time == 0) and 1 or (seconds_irl_public - last_time)
|
||||||
local irl_seconds_passed = (last_time <= 0) and 1 or (seconds_irl_public - last_time)
|
|
||||||
return irl_seconds_passed
|
return irl_seconds_passed
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
|
function mcl_time.get_irl_seconds_passed_at_pos_or_nil(pos)
|
||||||
if not pos then return end
|
if not pos then return end
|
||||||
if not time_speed_is_ok then return end
|
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(pos)
|
||||||
local last_time = meta:get_int(meta_name)
|
local last_time = meta:get_int(meta_name)
|
||||||
meta:set_int(meta_name, seconds_irl_public)
|
if last_time == 0 then return end
|
||||||
if last_time <= 0 then return end
|
|
||||||
local delta_time = seconds_irl_public - last_time
|
local delta_time = seconds_irl_public - last_time
|
||||||
if delta_time <= 0 then return end
|
if delta_time <= 0 then return end
|
||||||
return delta_time
|
return delta_time
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
mcl_util = {}
|
mcl_util = {}
|
||||||
|
|
||||||
local minetest_get_item_group = minetest.get_item_group
|
|
||||||
local minetest_get_meta = minetest.get_meta
|
|
||||||
local minetest_get_node = minetest.get_node
|
|
||||||
local minetest_get_node_timer = minetest.get_node_timer
|
|
||||||
local table_copy = table.copy
|
|
||||||
|
|
||||||
-- Updates all values in t using values from to*.
|
-- Updates all values in t using values from to*.
|
||||||
function table.update(t, ...)
|
function table.update(t, ...)
|
||||||
for _, to in ipairs{...} do
|
for _, to in ipairs{...} do
|
||||||
|
@ -39,6 +33,36 @@ function mcl_util.rotate_axis(itemstack, placer, pointed_thing)
|
||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Returns position of the neighbor of a double chest node
|
||||||
|
-- or nil if node is invalid.
|
||||||
|
-- This function assumes that the large chest is actually intact
|
||||||
|
-- * pos: Position of the node to investigate
|
||||||
|
-- * param2: param2 of that node
|
||||||
|
-- * side: Which "half" the investigated node is. "left" or "right"
|
||||||
|
function mcl_util.get_double_container_neighbor_pos(pos, param2, side)
|
||||||
|
if side == "right" then
|
||||||
|
if param2 == 0 then
|
||||||
|
return {x=pos.x-1, y=pos.y, z=pos.z}
|
||||||
|
elseif param2 == 1 then
|
||||||
|
return {x=pos.x, y=pos.y, z=pos.z+1}
|
||||||
|
elseif param2 == 2 then
|
||||||
|
return {x=pos.x+1, y=pos.y, z=pos.z}
|
||||||
|
elseif param2 == 3 then
|
||||||
|
return {x=pos.x, y=pos.y, z=pos.z-1}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if param2 == 0 then
|
||||||
|
return {x=pos.x+1, y=pos.y, z=pos.z}
|
||||||
|
elseif param2 == 1 then
|
||||||
|
return {x=pos.x, y=pos.y, z=pos.z-1}
|
||||||
|
elseif param2 == 2 then
|
||||||
|
return {x=pos.x-1, y=pos.y, z=pos.z}
|
||||||
|
elseif param2 == 3 then
|
||||||
|
return {x=pos.x, y=pos.y, z=pos.z+1}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Iterates through all items in the given inventory and
|
-- Iterates through all items in the given inventory and
|
||||||
-- returns the slot of the first item which matches a condition.
|
-- returns the slot of the first item which matches a condition.
|
||||||
-- Returns nil if no item was found.
|
-- Returns nil if no item was found.
|
||||||
|
@ -63,7 +87,7 @@ end
|
||||||
|
|
||||||
-- Returns true if itemstack is a shulker box
|
-- Returns true if itemstack is a shulker box
|
||||||
local function is_not_shulker_box(itemstack)
|
local function is_not_shulker_box(itemstack)
|
||||||
local g = minetest_get_item_group(itemstack:get_name(), "shulker_box")
|
local g = minetest.get_item_group(itemstack:get_name(), "shulker_box")
|
||||||
return g == 0 or g == nil
|
return g == 0 or g == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -109,117 +133,137 @@ end
|
||||||
--- source_stack_id (optional): The inventory position ID of the source inventory to take the item from (-1 for slot of the first valid item; -1 is default)
|
--- source_stack_id (optional): The inventory position ID of the source inventory to take the item from (-1 for slot of the first valid item; -1 is default)
|
||||||
--- destination_list (optional): List name of the destination inventory. Default is normally "main"; "src" for furnace
|
--- destination_list (optional): List name of the destination inventory. Default is normally "main"; "src" for furnace
|
||||||
-- Returns true on success and false on failure.
|
-- Returns true on success and false on failure.
|
||||||
local SHULKER_BOX = 3
|
|
||||||
local FURNACE = 4
|
|
||||||
local DOUBLE_CHEST_LEFT = 5
|
|
||||||
local DOUBLE_CHEST_RIGHT = 6
|
|
||||||
local CONTAINER_GROUP_TO_LIST = {
|
|
||||||
[1] = "main",
|
|
||||||
[2] = "main",
|
|
||||||
[SHULKER_BOX] = "main",
|
|
||||||
[FURNACE] = "dst",
|
|
||||||
[DOUBLE_CHEST_LEFT] = "main",
|
|
||||||
[DOUBLE_CHEST_RIGHT] = "main",
|
|
||||||
}
|
|
||||||
function mcl_util.move_item_container(source_pos, destination_pos, source_list, source_stack_id, destination_list)
|
function mcl_util.move_item_container(source_pos, destination_pos, source_list, source_stack_id, destination_list)
|
||||||
local spos = table_copy(source_pos)
|
local dpos = table.copy(destination_pos)
|
||||||
local snode = minetest_get_node(spos)
|
local spos = table.copy(source_pos)
|
||||||
local sctype = minetest_get_item_group(snode.name, "container")
|
local snode = minetest.get_node(spos)
|
||||||
local default_source_list = CONTAINER_GROUP_TO_LIST[sctype]
|
local dnode = minetest.get_node(dpos)
|
||||||
if not default_source_list then return end
|
|
||||||
if sctype == DOUBLE_CHEST_RIGHT then
|
|
||||||
local sparam2 = snode.param2
|
|
||||||
if sparam2 == 0 then spos.x = spos.x - 1
|
|
||||||
elseif sparam2 == 1 then spos.z = spos.z + 1
|
|
||||||
elseif sparam2 == 2 then spos.x = spos.x + 1
|
|
||||||
elseif sparam2 == 3 then spos.z = spos.z - 1
|
|
||||||
end
|
|
||||||
snode = minetest_get_node(spos)
|
|
||||||
sctype = minetest_get_item_group(snode.name, "container")
|
|
||||||
if sctype ~= DOUBLE_CHEST_LEFT then return end
|
|
||||||
end
|
|
||||||
local smeta = minetest_get_meta(spos)
|
|
||||||
local sinv = smeta:get_inventory()
|
|
||||||
local source_list = source_list or default_source_list
|
|
||||||
|
|
||||||
local dpos = table_copy(destination_pos)
|
local dctype = minetest.get_item_group(dnode.name, "container")
|
||||||
local dnode = minetest_get_node(dpos)
|
local sctype = minetest.get_item_group(snode.name, "container")
|
||||||
local dctype = minetest_get_item_group(dnode.name, "container")
|
|
||||||
local default_destination_list = CONTAINER_GROUP_TO_LIST[sctype]
|
-- Container type 7 does not allow any movement
|
||||||
if not default_destination_list then return end
|
if sctype == 7 then
|
||||||
if dctype == DOUBLE_CHEST_RIGHT then
|
return false
|
||||||
local dparam2 = dnode.param2
|
|
||||||
if dparam2 == 0 then dpos.x = dpos.x - 1
|
|
||||||
elseif dparam2 == 1 then dpos.z = dpos.z + 1
|
|
||||||
elseif dparam2 == 2 then dpos.x = dpos.x + 1
|
|
||||||
elseif dparam2 == 3 then dpos.z = dpos.z - 1
|
|
||||||
end
|
end
|
||||||
dnode = minetest_get_node(dpos)
|
|
||||||
dctype = minetest_get_item_group(dnode.name, "container")
|
-- Normalize double container by forcing to always use the left segment first
|
||||||
if dctype ~= DOUBLE_CHEST_LEFT then return end
|
local function normalize_double_container(pos, node, ctype)
|
||||||
|
if ctype == 6 then
|
||||||
|
pos = mcl_util.get_double_container_neighbor_pos(pos, node.param2, "right")
|
||||||
|
if not pos then
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
local dmeta = minetest_get_meta(dpos)
|
node = minetest.get_node(pos)
|
||||||
|
ctype = minetest.get_item_group(node.name, "container")
|
||||||
|
-- The left segment seems incorrect? We better bail out!
|
||||||
|
if ctype ~= 5 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return pos, node, ctype
|
||||||
|
end
|
||||||
|
|
||||||
|
spos, snode, sctype = normalize_double_container(spos, snode, sctype)
|
||||||
|
dpos, dnode, dctype = normalize_double_container(dpos, dnode, dctype)
|
||||||
|
if not spos or not dpos then return false end
|
||||||
|
|
||||||
|
local smeta = minetest.get_meta(spos)
|
||||||
|
local dmeta = minetest.get_meta(dpos)
|
||||||
|
|
||||||
|
local sinv = smeta:get_inventory()
|
||||||
local dinv = dmeta:get_inventory()
|
local dinv = dmeta:get_inventory()
|
||||||
|
|
||||||
|
-- Default source lists
|
||||||
|
if not source_list then
|
||||||
|
-- Main inventory for most container types
|
||||||
|
if sctype == 2 or sctype == 3 or sctype == 5 or sctype == 6 or sctype == 7 then
|
||||||
|
source_list = "main"
|
||||||
|
-- Furnace: output
|
||||||
|
elseif sctype == 4 then
|
||||||
|
source_list = "dst"
|
||||||
|
-- Unknown source container type. Bail out
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Automatically select stack slot ID if set to automatic
|
-- Automatically select stack slot ID if set to automatic
|
||||||
local source_stack_id = source_stack_id or -1
|
if not source_stack_id then
|
||||||
|
source_stack_id = -1
|
||||||
|
end
|
||||||
if source_stack_id == -1 then
|
if source_stack_id == -1 then
|
||||||
local cond = nil
|
local cond = nil
|
||||||
-- Prevent shulker box inception
|
-- Prevent shulker box inception
|
||||||
if dctype == SHULKER_BOX then cond = is_not_shulker_box end
|
if dctype == 3 then
|
||||||
|
cond = is_not_shulker_box
|
||||||
|
end
|
||||||
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
||||||
if not source_stack_id then
|
if not source_stack_id then
|
||||||
if sctype == DOUBLE_CHEST_LEFT then
|
-- Try again if source is a double container
|
||||||
local sparam2 = snode.param2
|
if sctype == 5 then
|
||||||
if sparam2 == 0 then spos.x = spos.x + 1
|
spos = mcl_util.get_double_container_neighbor_pos(spos, snode.param2, "left")
|
||||||
elseif sparam2 == 1 then spos.z = spos.z - 1
|
smeta = minetest.get_meta(spos)
|
||||||
elseif sparam2 == 2 then spos.x = spos.x - 1
|
|
||||||
elseif sparam2 == 3 then spos.z = spos.z + 1
|
|
||||||
end
|
|
||||||
snode = minetest_get_node(spos)
|
|
||||||
sctype = minetest_get_item_group(snode.name, "container")
|
|
||||||
if sctype ~= DOUBLE_CHEST_RIGHT then return end
|
|
||||||
smeta = minetest_get_meta(spos)
|
|
||||||
sinv = smeta:get_inventory()
|
sinv = smeta:get_inventory()
|
||||||
|
|
||||||
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
source_stack_id = mcl_util.get_eligible_transfer_item_slot(sinv, source_list, dinv, dpos, cond)
|
||||||
|
if not source_stack_id then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not source_stack_id then return end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Abort transfer if shulker box wants to go into shulker box
|
-- Abort transfer if shulker box wants to go into shulker box
|
||||||
if dctype == SHULKER_BOX then
|
if dctype == 3 then
|
||||||
local stack = sinv:get_stack(source_list, source_stack_id)
|
local stack = sinv:get_stack(source_list, source_stack_id)
|
||||||
if stack and minetest_get_item_group(stack:get_name(), "shulker_box") == 1 then return end
|
if stack and minetest.get_item_group(stack:get_name(), "shulker_box") == 1 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Container type 7 does not allow any placement
|
||||||
|
if dctype == 7 then
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local destination_list = destination_list or default_destination_list
|
-- If it's a container, put it into the container
|
||||||
|
if dctype ~= 0 then
|
||||||
|
-- Automatically select a destination list if omitted
|
||||||
|
if not destination_list then
|
||||||
|
-- Main inventory for most container types
|
||||||
|
if dctype == 2 or dctype == 3 or dctype == 5 or dctype == 6 or dctype == 7 then
|
||||||
|
destination_list = "main"
|
||||||
|
-- Furnace source slot
|
||||||
|
elseif dctype == 4 then
|
||||||
|
destination_list = "src"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if destination_list then
|
||||||
-- Move item
|
-- Move item
|
||||||
local ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
local ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
||||||
|
|
||||||
-- Try transfer to neighbor node if transfer failed and double container
|
-- Try transfer to neighbor node if transfer failed and double container
|
||||||
if not ok then
|
if not ok and dctype == 5 then
|
||||||
if dctype == DOUBLE_CHEST_LEFT then
|
dpos = mcl_util.get_double_container_neighbor_pos(dpos, dnode.param2, "left")
|
||||||
local dparam2 = dnode.param2
|
dmeta = minetest.get_meta(dpos)
|
||||||
if dparam2 == 0 then dpos.x = dpos.x + 1
|
|
||||||
elseif dparam2 == 1 then dpos.z = dpos.z - 1
|
|
||||||
elseif dparam2 == 2 then dpos.x = dpos.x - 1
|
|
||||||
elseif dparam2 == 3 then dpos.z = dpos.z + 1
|
|
||||||
end
|
|
||||||
dnode = minetest_get_node(dpos)
|
|
||||||
dctype = minetest_get_item_group(dnode.name, "container")
|
|
||||||
if dctype ~= DOUBLE_CHEST_RIGHT then return end
|
|
||||||
dmeta = minetest_get_meta(dpos)
|
|
||||||
dinv = dmeta:get_inventory()
|
dinv = dmeta:get_inventory()
|
||||||
|
|
||||||
ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
ok = mcl_util.move_item(sinv, source_list, source_stack_id, dinv, destination_list)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
-- Update furnace
|
-- Update furnace
|
||||||
if ok and dctype == FURNACE then
|
if ok and dctype == 4 then
|
||||||
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
|
-- Start furnace's timer function, it will sort out whether furnace can burn or not.
|
||||||
minetest_get_node_timer(dpos):start(1.0)
|
minetest.get_node_timer(dpos):start(1.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- Returns the ID of the first non-empty slot in the given inventory list
|
-- Returns the ID of the first non-empty slot in the given inventory list
|
||||||
-- or nil, if inventory is empty.
|
-- or nil, if inventory is empty.
|
||||||
|
@ -248,7 +292,7 @@ function mcl_util.generate_on_place_plant_function(condition)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Call on_rightclick if the pointed node defines it
|
-- Call on_rightclick if the pointed node defines it
|
||||||
local node = minetest_get_node(pointed_thing.under)
|
local node = minetest.get_node(pointed_thing.under)
|
||||||
if placer and not placer:get_player_control().sneak then
|
if placer and not placer:get_player_control().sneak then
|
||||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||||
|
@ -256,8 +300,8 @@ function mcl_util.generate_on_place_plant_function(condition)
|
||||||
end
|
end
|
||||||
|
|
||||||
local place_pos
|
local place_pos
|
||||||
local def_under = minetest.registered_nodes[minetest_get_node(pointed_thing.under).name]
|
local def_under = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
|
||||||
local def_above = minetest.registered_nodes[minetest_get_node(pointed_thing.above).name]
|
local def_above = minetest.registered_nodes[minetest.get_node(pointed_thing.above).name]
|
||||||
if not def_under or not def_above then
|
if not def_under or not def_above then
|
||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
|
@ -315,7 +359,7 @@ function mcl_util.call_on_rightclick(itemstack, player, pointed_thing)
|
||||||
-- Call on_rightclick if the pointed node defines it
|
-- Call on_rightclick if the pointed node defines it
|
||||||
if pointed_thing and pointed_thing.type == "node" then
|
if pointed_thing and pointed_thing.type == "node" then
|
||||||
local pos = pointed_thing.under
|
local pos = pointed_thing.under
|
||||||
local node = minetest_get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
if player and not player:get_player_control().sneak then
|
if player and not player:get_player_control().sneak then
|
||||||
local nodedef = minetest.registered_nodes[node.name]
|
local nodedef = minetest.registered_nodes[node.name]
|
||||||
local on_rightclick = nodedef and nodedef.on_rightclick
|
local on_rightclick = nodedef and nodedef.on_rightclick
|
||||||
|
@ -328,7 +372,7 @@ end
|
||||||
|
|
||||||
function mcl_util.calculate_durability(itemstack)
|
function mcl_util.calculate_durability(itemstack)
|
||||||
local unbreaking_level = mcl_enchanting.get_enchantment(itemstack, "unbreaking")
|
local unbreaking_level = mcl_enchanting.get_enchantment(itemstack, "unbreaking")
|
||||||
local armor_uses = minetest_get_item_group(itemstack:get_name(), "mcl_armor_uses")
|
local armor_uses = minetest.get_item_group(itemstack:get_name(), "mcl_armor_uses")
|
||||||
|
|
||||||
local uses
|
local uses
|
||||||
|
|
||||||
|
@ -373,7 +417,6 @@ function mcl_util.deal_damage(target, damage, mcl_reason)
|
||||||
-- target:punch(puncher, 1.0, {full_punch_interval = 1.0, damage_groups = {fleshy = damage}}, vector.direction(puncher:get_pos(), target:get_pos()), damage)
|
-- target:punch(puncher, 1.0, {full_punch_interval = 1.0, damage_groups = {fleshy = damage}}, vector.direction(puncher:get_pos(), target:get_pos()), damage)
|
||||||
if luaentity.health > 0 then
|
if luaentity.health > 0 then
|
||||||
luaentity.health = luaentity.health - damage
|
luaentity.health = luaentity.health - damage
|
||||||
luaentity.pause_timer = 0.4
|
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -455,24 +498,3 @@ function mcl_util.get_pointed_thing(player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local possible_hackers = {}
|
|
||||||
|
|
||||||
function mcl_util.is_player(obj)
|
|
||||||
if not obj then return end
|
|
||||||
if not obj.is_player then return end
|
|
||||||
if not obj:is_player() then return end
|
|
||||||
local name = obj:get_player_name()
|
|
||||||
if not name then return end
|
|
||||||
if possible_hackers[name] then return end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_authplayer(function(name, ip, is_success)
|
|
||||||
if not is_success then return end
|
|
||||||
possible_hackers[name] = true
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
possible_hackers[player:get_player_name()] = nil
|
|
||||||
end)
|
|
||||||
|
|
|
@ -2,18 +2,11 @@ mcl_worlds = {}
|
||||||
|
|
||||||
local get_connected_players = minetest.get_connected_players
|
local get_connected_players = minetest.get_connected_players
|
||||||
|
|
||||||
local min1, min2, min3
|
|
||||||
local max1, max2, max3
|
|
||||||
local get_local_settings = function()
|
|
||||||
min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
|
|
||||||
max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
|
|
||||||
end
|
|
||||||
get_local_settings()
|
|
||||||
mcl_mapgen.register_on_settings_changed(get_local_settings)
|
|
||||||
|
|
||||||
-- For a given position, returns a 2-tuple:
|
-- For a given position, returns a 2-tuple:
|
||||||
-- 1st return value: true if pos is in void
|
-- 1st return value: true if pos is in void
|
||||||
-- 2nd return value: true if it is in the deadly part of the void
|
-- 2nd return value: true if it is in the deadly part of the void
|
||||||
|
local min1, min2, min3 = mcl_mapgen.overworld.min, mcl_mapgen.end_.min, mcl_mapgen.nether.min
|
||||||
|
local max1, max2, max3 = mcl_mapgen.overworld.max, mcl_mapgen.end_.max, mcl_mapgen.nether.max+128
|
||||||
function mcl_worlds.is_in_void(pos)
|
function mcl_worlds.is_in_void(pos)
|
||||||
local y = pos.y
|
local y = pos.y
|
||||||
local void = not ((y < max1 and y > min1) or (y < max2 and y > min2) or (y < max3 and y > min3))
|
local void = not ((y < max1 and y > min1) or (y < max2 and y > min2) or (y < max3 and y > min3))
|
||||||
|
@ -159,23 +152,3 @@ minetest.register_globalstep(function(dtime)
|
||||||
dimtimer = 0
|
dimtimer = 0
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function mcl_worlds.get_cloud_parameters()
|
|
||||||
if mcl_mapgen.name == "valleys" then
|
|
||||||
return {
|
|
||||||
height = 384,
|
|
||||||
speed = {x=-2, z=0},
|
|
||||||
thickness=5,
|
|
||||||
color="#FFF0FEF",
|
|
||||||
ambient = "#201060",
|
|
||||||
}
|
|
||||||
else
|
|
||||||
-- MC-style clouds: Layer 127, thickness 4, fly to the “West”
|
|
||||||
return {
|
|
||||||
height = mcl_worlds.layer_to_y(127),
|
|
||||||
speed = {x=-2, z=0},
|
|
||||||
thickness = 4,
|
|
||||||
color = "#FFF0FEF",
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,87 +1,4 @@
|
||||||
# tga_encoder
|
# tga_encoder
|
||||||
A TGA Encoder written in Lua without the use of external Libraries.
|
A TGA Encoder written in Lua without the use of external Libraries.
|
||||||
|
|
||||||
Created by fleckenstein for MineClone2, then improved by erlehmann.
|
|
||||||
|
|
||||||
May be used as a Minetest mod.
|
May be used as a Minetest mod.
|
||||||
|
|
||||||
See `examples.lua` for example code and usage hints.
|
|
||||||
|
|
||||||
## Use Cases for `tga_encoder`
|
|
||||||
|
|
||||||
### Encoding Textures for Editing
|
|
||||||
|
|
||||||
TGA images of types 1/2/3 consist of header data followed by a pixel array.
|
|
||||||
|
|
||||||
This makes it trivial to parse TGA files – and even edit pixels in-place.
|
|
||||||
|
|
||||||
No checksums need to be updated on any kind of in-place texture editing.
|
|
||||||
|
|
||||||
**Tip**: When storing an editable image in item meta, use zlib compression.
|
|
||||||
|
|
||||||
### Legacy Minetest Texture Encoding
|
|
||||||
|
|
||||||
Minetest 5.4 did not include `minetest.encode_png()` (or any equvivalent).
|
|
||||||
|
|
||||||
Since `tga_encoder` is written in pure Lua, it does not need engine support.
|
|
||||||
|
|
||||||
**Tip:** Look at `examples.lua` and the Minetest mod `mcl_maps` for guidance.
|
|
||||||
|
|
||||||
### Advanced Texture Format Control
|
|
||||||
|
|
||||||
The function `minetest.encode_png()` always encodes images as 32bpp RGBA.
|
|
||||||
|
|
||||||
`tga_encoder` allows saving images as grayscale, 16bpp RGBA and 24bpp RGB.
|
|
||||||
|
|
||||||
For generating maps from terrain, color-mapped formats can be more useful.
|
|
||||||
|
|
||||||
### Encoding Very Small Textures
|
|
||||||
|
|
||||||
Images of size 8×8 or below are often smaller than an equivalent PNG file.
|
|
||||||
|
|
||||||
Note that on many filesystems files use at least 4096 bytes (i.e. 64×64).
|
|
||||||
|
|
||||||
Therefore, saving bytes on files up to a few 100 bytes is often useless.
|
|
||||||
|
|
||||||
### Encoding Reference Textures
|
|
||||||
|
|
||||||
TGA is a simple format, which makes it easy to create reference textures.
|
|
||||||
|
|
||||||
Using a hex editor, one can trivially see how all the pixels are stored.
|
|
||||||
|
|
||||||
## Supported Image Types
|
|
||||||
|
|
||||||
For all types, images are encoded in a fast single pass (i.e. append-only).
|
|
||||||
|
|
||||||
### Color-Mapped Images (Type 1)
|
|
||||||
|
|
||||||
These images contain a palette, followed by pixel data.
|
|
||||||
|
|
||||||
* `A1R5G5B5` (8bpp RGB)
|
|
||||||
* `B8G8R8` (8bpp RGB)
|
|
||||||
* `B8G8R8A8` (8bpp RGBA)
|
|
||||||
|
|
||||||
### True-Color Images (Type 2)
|
|
||||||
|
|
||||||
These images contain uncompressed RGB(A) pixel data.
|
|
||||||
|
|
||||||
* `A1R5G5B5` (16bpp RGBA)
|
|
||||||
* `B8G8R8` (24bpp RGB)
|
|
||||||
* `B8G8R8A8` (32bpp RGBA)
|
|
||||||
|
|
||||||
### Grayscale Images (Type 3)
|
|
||||||
|
|
||||||
* `Y8` (8bpp grayscale)
|
|
||||||
|
|
||||||
### Run-Length Encoded (RLE), True-Color Images (Type 10)
|
|
||||||
|
|
||||||
These images contain compressed RGB(A) pixel data.
|
|
||||||
|
|
||||||
* `A1R5G5B5` (16bpp RGBA)
|
|
||||||
* `B8G8R8` (24bpp RGB)
|
|
||||||
* `B8G8R8A8` (32bpp RGBA)
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
* Actually support `R8G8B8A8` input for `A1R5G5B5` output
|
|
||||||
* Add both zoomable and explorable maps to `mcl_maps`.
|
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
dofile("init.lua")
|
|
||||||
|
|
||||||
-- encode a bitmap
|
|
||||||
local _ = { 0, 0, 0 }
|
|
||||||
local R = { 255, 127, 127 }
|
|
||||||
local pixels = {
|
|
||||||
{ _, _, _, _, _, _, _ },
|
|
||||||
{ _, _, _, R, _, _, _ },
|
|
||||||
{ _, _, R, R, R, _, _ },
|
|
||||||
{ _, R, R, R, R, R, _ },
|
|
||||||
{ _, R, R, R, R, R, _ },
|
|
||||||
{ _, _, R, _, R, _, _ },
|
|
||||||
{ _, _, _, _, _, _, _ },
|
|
||||||
}
|
|
||||||
tga_encoder.image(pixels):save("bitmap_small.tga")
|
|
||||||
|
|
||||||
-- change a single pixel, then rescale the bitmap
|
|
||||||
local pixels_orig = pixels
|
|
||||||
pixels_orig[4][4] = { 255, 255, 255 }
|
|
||||||
local pixels = {}
|
|
||||||
for x = 1,56,1 do
|
|
||||||
local x_orig = math.ceil(x/8)
|
|
||||||
for z = 1,56,1 do
|
|
||||||
local z_orig = math.ceil(z/8)
|
|
||||||
local color = pixels_orig[z_orig][x_orig]
|
|
||||||
pixels[z] = pixels[z] or {}
|
|
||||||
pixels[z][x] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tga_encoder.image(pixels):save("bitmap_large.tga")
|
|
||||||
|
|
||||||
-- note that the uncompressed grayscale TGA file written in this
|
|
||||||
-- example is 80 bytes – but an optimized PNG file is 81 bytes …
|
|
||||||
local pixels = {}
|
|
||||||
for x = 1,6,1 do -- left to right
|
|
||||||
for z = 1,6,1 do -- bottom to top
|
|
||||||
local color = { math.min(x * z * 4 - 1, 255) }
|
|
||||||
pixels[z] = pixels[z] or {}
|
|
||||||
pixels[z][x] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tga_encoder.image(pixels):save("gradient_8bpp_raw.tga", {color_format="Y8", compression="RAW"})
|
|
||||||
|
|
||||||
local pixels = {}
|
|
||||||
for x = 1,16,1 do -- left to right
|
|
||||||
for z = 1,16,1 do -- bottom to top
|
|
||||||
local r = math.min(x * 32 - 1, 255)
|
|
||||||
local g = math.min(z * 32 - 1, 255)
|
|
||||||
local b = 0
|
|
||||||
-- blue rectangle in top right corner
|
|
||||||
if x > 8 and z > 8 then
|
|
||||||
r = 0
|
|
||||||
g = 0
|
|
||||||
b = math.min(z * 16 - 1, 255)
|
|
||||||
end
|
|
||||||
local color = { r, g, b }
|
|
||||||
pixels[z] = pixels[z] or {}
|
|
||||||
pixels[z][x] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local gradients = tga_encoder.image(pixels)
|
|
||||||
gradients:save("gradients_8bpp_raw.tga", {color_format="Y8", compression="RAW"})
|
|
||||||
gradients:save("gradients_16bpp_raw.tga", {color_format="A1R5G5B5", compression="RAW"})
|
|
||||||
gradients:save("gradients_16bpp_rle.tga", {color_format="A1R5G5B5", compression="RLE"})
|
|
||||||
gradients:save("gradients_24bpp_raw.tga", {color_format="B8G8R8", compression="RAW"})
|
|
||||||
gradients:save("gradients_24bpp_rle.tga", {color_format="B8G8R8", compression="RLE"})
|
|
||||||
|
|
||||||
for x = 1,16,1 do -- left to right
|
|
||||||
for z = 1,16,1 do -- bottom to top
|
|
||||||
local color = pixels[z][x]
|
|
||||||
color[#color+1] = ((x * x) + (z * z)) % 256
|
|
||||||
pixels[z][x] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
gradients:save("gradients_32bpp_raw.tga", {color_format="B8G8R8A8", compression="RAW"})
|
|
||||||
-- the RLE-compressed file is larger than just dumping pixels because
|
|
||||||
-- the gradients in this picture can not be compressed well using RLE
|
|
||||||
gradients:save("gradients_32bpp_rle.tga", {color_format="B8G8R8A8", compression="RLE"})
|
|
||||||
|
|
||||||
local pixels = {}
|
|
||||||
for x = 1,512,1 do -- left to right
|
|
||||||
for z = 1,512,1 do -- bottom to top
|
|
||||||
local oz = (z - 256) / 256 + 0.75
|
|
||||||
local ox = (x - 256) / 256
|
|
||||||
local px, pz, i = 0, 0, 0
|
|
||||||
while (px * px) + (pz * pz) <= 4 and i < 128 do
|
|
||||||
px = (px * px) - (pz * pz) + oz
|
|
||||||
pz = (2 * px * pz) + ox
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
local color = {
|
|
||||||
math.max(0, math.min(255, math.floor(px * 64))),
|
|
||||||
math.max(0, math.min(255, math.floor(pz * 64))),
|
|
||||||
math.max(0, math.min(255, math.floor(i))),
|
|
||||||
}
|
|
||||||
pixels[z] = pixels[z] or {}
|
|
||||||
pixels[z][x] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tga_encoder.image(pixels):save("fractal_8bpp.tga", {color_format="Y8"})
|
|
||||||
tga_encoder.image(pixels):save("fractal_16bpp.tga", {color_format="A1R5G5B5"})
|
|
||||||
tga_encoder.image(pixels):save("fractal_24bpp.tga", {color_format="B8G8R8"})
|
|
||||||
|
|
||||||
-- encode a colormapped bitmap
|
|
||||||
local K = { 0 }
|
|
||||||
local B = { 1 }
|
|
||||||
local R = { 2 }
|
|
||||||
local G = { 3 }
|
|
||||||
local W = { 4 }
|
|
||||||
local colormap = {
|
|
||||||
{ 1, 2, 3 }, -- K
|
|
||||||
{ 0, 0, 255 }, -- B
|
|
||||||
{ 255, 0, 0 }, -- R
|
|
||||||
{ 0, 255, 0 }, -- G
|
|
||||||
{ 253, 254, 255 }, -- W
|
|
||||||
}
|
|
||||||
local pixels = {
|
|
||||||
{ W, K, W, K, W, K, W },
|
|
||||||
{ R, G, B, R, G, B, K },
|
|
||||||
{ K, W, K, W, K, W, K },
|
|
||||||
{ G, B, R, G, B, R, W },
|
|
||||||
{ W, W, W, K, K, K, W },
|
|
||||||
{ B, R, G, B, R, G, K },
|
|
||||||
{ B, R, G, B, R, G, W },
|
|
||||||
}
|
|
||||||
-- note that the uncompressed colormapped TGA file written in this
|
|
||||||
-- example is 108 bytes – but an optimized PNG file is 121 bytes …
|
|
||||||
tga_encoder.image(pixels):save("colormapped_B8G8R8.tga", {colormap=colormap})
|
|
||||||
-- encoding as A1R5G5B5 saves 1 byte per palette entry → 103 bytes
|
|
||||||
tga_encoder.image(pixels):save("colormapped_A1R5G5B5.tga", {colormap=colormap, color_format="A1R5G5B5"})
|
|
||||||
|
|
||||||
-- encode a colormapped bitmap with transparency
|
|
||||||
local _ = { 0 }
|
|
||||||
local K = { 1 }
|
|
||||||
local W = { 2 }
|
|
||||||
local colormap = {
|
|
||||||
{ 0, 0, 0, 0 },
|
|
||||||
{ 0, 0, 0, 255 },
|
|
||||||
{ 255, 255, 255, 255 },
|
|
||||||
}
|
|
||||||
local pixels = {
|
|
||||||
{ _, K, K, K, K, K, _ },
|
|
||||||
{ _, K, W, W, W, K, _ },
|
|
||||||
{ K, K, W, W, W, K, K },
|
|
||||||
{ K, W, W, W, W, W, K },
|
|
||||||
{ _, K, W, W, W, K, _ },
|
|
||||||
{ _, _, K, W, K, _, _ },
|
|
||||||
{ _, _, _, K, _, _, _ },
|
|
||||||
}
|
|
||||||
tga_encoder.image(pixels):save("colormapped_B8G8R8A8.tga", {colormap=colormap})
|
|
|
@ -9,533 +9,60 @@ local image = setmetatable({}, {
|
||||||
})
|
})
|
||||||
|
|
||||||
function image:constructor(pixels)
|
function image:constructor(pixels)
|
||||||
|
self.data = ""
|
||||||
self.pixels = pixels
|
self.pixels = pixels
|
||||||
self.width = #pixels[1]
|
self.width = #pixels[1]
|
||||||
self.height = #pixels
|
self.height = #pixels
|
||||||
|
|
||||||
|
self:encode()
|
||||||
end
|
end
|
||||||
|
|
||||||
local pixel_depth_by_color_format = {
|
function image:encode_colormap_spec()
|
||||||
["Y8"] = 8,
|
self.data = self.data
|
||||||
["A1R5G5B5"] = 16,
|
.. string.char(0, 0) -- first entry index
|
||||||
["B8G8R8"] = 24,
|
.. string.char(0, 0) -- number of entries
|
||||||
["B8G8R8A8"] = 32,
|
.. string.char(0) -- bits per pixel
|
||||||
}
|
|
||||||
|
|
||||||
function image:encode_colormap_spec(properties)
|
|
||||||
local colormap = properties.colormap
|
|
||||||
local colormap_pixel_depth = 0
|
|
||||||
if 0 ~= #colormap then
|
|
||||||
colormap_pixel_depth = pixel_depth_by_color_format[
|
|
||||||
properties.color_format
|
|
||||||
]
|
|
||||||
end
|
|
||||||
local colormap_spec =
|
|
||||||
string.char(0, 0) .. -- first entry index
|
|
||||||
string.char(#colormap % 256, math.floor(#colormap / 256)) .. -- number of entries
|
|
||||||
string.char(colormap_pixel_depth) -- bits per pixel
|
|
||||||
self.data = self.data .. colormap_spec
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:encode_image_spec(properties)
|
function image:encode_image_spec()
|
||||||
local color_format = properties.color_format
|
|
||||||
assert(
|
|
||||||
"Y8" == color_format or -- (8 bit grayscale = 1 byte = 8 bits)
|
|
||||||
"A1R5G5B5" == color_format or -- (A1R5G5B5 = 2 bytes = 16 bits)
|
|
||||||
"B8G8R8" == color_format or -- (B8G8R8 = 3 bytes = 24 bits)
|
|
||||||
"B8G8R8A8" == color_format -- (B8G8R8A8 = 4 bytes = 32 bits)
|
|
||||||
)
|
|
||||||
local pixel_depth
|
|
||||||
if 0 ~= #properties.colormap then
|
|
||||||
pixel_depth = self.pixel_depth
|
|
||||||
else
|
|
||||||
pixel_depth = pixel_depth_by_color_format[color_format]
|
|
||||||
end
|
|
||||||
assert( nil ~= pixel_depth)
|
|
||||||
self.data = self.data
|
self.data = self.data
|
||||||
.. string.char(0, 0) -- X-origin
|
.. string.char(0, 0) -- X-origin
|
||||||
.. string.char(0, 0) -- Y-origin
|
.. string.char(0, 0) -- Y-origin
|
||||||
.. string.char(self.width % 256, math.floor(self.width / 256)) -- width
|
.. string.char(self.width % 256, math.floor(self.width / 256)) -- width
|
||||||
.. string.char(self.height % 256, math.floor(self.height / 256)) -- height
|
.. string.char(self.height % 256, math.floor(self.height / 256)) -- height
|
||||||
.. string.char(pixel_depth)
|
.. string.char(24) -- pixel depth (RGB = 3 bytes = 24 bits)
|
||||||
.. string.char(0) -- image descriptor
|
.. string.char(0) -- image descriptor
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:encode_colormap(properties)
|
function image:encode_header()
|
||||||
local colormap = properties.colormap
|
|
||||||
if 0 == #colormap then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local color_format = properties.color_format
|
|
||||||
assert (
|
|
||||||
"A1R5G5B5" == color_format or
|
|
||||||
"B8G8R8" == color_format or
|
|
||||||
"B8G8R8A8" == color_format
|
|
||||||
)
|
|
||||||
local colors = {}
|
|
||||||
if "A1R5G5B5" == color_format then
|
|
||||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
|
||||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
|
||||||
local max_sample_in = math.pow(2, 8) - 1
|
|
||||||
local max_sample_out = math.pow(2, 5) - 1
|
|
||||||
for i = 1,#colormap,1 do
|
|
||||||
local color = colormap[i]
|
|
||||||
local colorword = 32768 +
|
|
||||||
((math.floor((color[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
|
||||||
((math.floor((color[2] * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
|
||||||
((math.floor((color[3] * max_sample_out / max_sample_in) + 0.5)) * 1)
|
|
||||||
local color_bytes = string.char(
|
|
||||||
colorword % 256,
|
|
||||||
math.floor(colorword / 256)
|
|
||||||
)
|
|
||||||
colors[#colors + 1] = color_bytes
|
|
||||||
end
|
|
||||||
elseif "B8G8R8" == color_format then
|
|
||||||
for i = 1,#colormap,1 do
|
|
||||||
local color = colormap[i]
|
|
||||||
local color_bytes = string.char(
|
|
||||||
color[3], -- B
|
|
||||||
color[2], -- G
|
|
||||||
color[1] -- R
|
|
||||||
)
|
|
||||||
colors[#colors + 1] = color_bytes
|
|
||||||
end
|
|
||||||
elseif "B8G8R8A8" == color_format then
|
|
||||||
for i = 1,#colormap,1 do
|
|
||||||
local color = colormap[i]
|
|
||||||
local color_bytes = string.char(
|
|
||||||
color[3], -- B
|
|
||||||
color[2], -- G
|
|
||||||
color[1], -- R
|
|
||||||
color[4] -- A
|
|
||||||
)
|
|
||||||
colors[#colors + 1] = color_bytes
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert( 0 ~= #colors )
|
|
||||||
self.data = self.data .. table.concat(colors)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_header(properties)
|
|
||||||
local color_format = properties.color_format
|
|
||||||
local colormap = properties.colormap
|
|
||||||
local compression = properties.compression
|
|
||||||
local colormap_type
|
|
||||||
local image_type
|
|
||||||
if "Y8" == color_format and "RAW" == compression then
|
|
||||||
colormap_type = 0
|
|
||||||
image_type = 3 -- grayscale
|
|
||||||
elseif (
|
|
||||||
"A1R5G5B5" == color_format or
|
|
||||||
"B8G8R8" == color_format or
|
|
||||||
"B8G8R8A8" == color_format
|
|
||||||
) then
|
|
||||||
if "RAW" == compression then
|
|
||||||
if 0 ~= #colormap then
|
|
||||||
colormap_type = 1
|
|
||||||
image_type = 1 -- colormapped RGB(A)
|
|
||||||
else
|
|
||||||
colormap_type = 0
|
|
||||||
image_type = 2 -- RAW RGB(A)
|
|
||||||
end
|
|
||||||
elseif "RLE" == compression then
|
|
||||||
colormap_type = 0
|
|
||||||
image_type = 10 -- RLE RGB
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert( nil ~= colormap_type )
|
|
||||||
assert( nil ~= image_type )
|
|
||||||
self.data = self.data
|
self.data = self.data
|
||||||
.. string.char(0) -- image id
|
.. string.char(0) -- image id
|
||||||
.. string.char(colormap_type)
|
.. string.char(0) -- color map type
|
||||||
.. string.char(image_type)
|
.. string.char(10) -- image type (RLE RGB = 10)
|
||||||
self:encode_colormap_spec(properties) -- color map specification
|
self:encode_colormap_spec() -- color map specification
|
||||||
self:encode_image_spec(properties) -- image specification
|
self:encode_image_spec() -- image specification
|
||||||
self:encode_colormap(properties)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:encode_data(properties)
|
function image:encode_data()
|
||||||
local color_format = properties.color_format
|
local current_pixel = ''
|
||||||
local colormap = properties.colormap
|
local previous_pixel = ''
|
||||||
local compression = properties.compression
|
|
||||||
|
|
||||||
local data_length_before = #self.data
|
|
||||||
if "Y8" == color_format and "RAW" == compression then
|
|
||||||
if 8 == self.pixel_depth then
|
|
||||||
self:encode_data_Y8_as_Y8_raw()
|
|
||||||
elseif 24 == self.pixel_depth then
|
|
||||||
self:encode_data_R8G8B8_as_Y8_raw()
|
|
||||||
end
|
|
||||||
elseif "A1R5G5B5" == color_format then
|
|
||||||
if 0 ~= #colormap then
|
|
||||||
if "RAW" == compression then
|
|
||||||
if 8 == self.pixel_depth then
|
|
||||||
self:encode_data_Y8_as_Y8_raw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if "RAW" == compression then
|
|
||||||
self:encode_data_R8G8B8_as_A1R5G5B5_raw()
|
|
||||||
elseif "RLE" == compression then
|
|
||||||
self:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif "B8G8R8" == color_format then
|
|
||||||
if 0 ~= #colormap then
|
|
||||||
if "RAW" == compression then
|
|
||||||
if 8 == self.pixel_depth then
|
|
||||||
self:encode_data_Y8_as_Y8_raw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if "RAW" == compression then
|
|
||||||
self:encode_data_R8G8B8_as_B8G8R8_raw()
|
|
||||||
elseif "RLE" == compression then
|
|
||||||
self:encode_data_R8G8B8_as_B8G8R8_rle()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif "B8G8R8A8" == color_format then
|
|
||||||
if 0 ~= #colormap then
|
|
||||||
if "RAW" == compression then
|
|
||||||
if 8 == self.pixel_depth then
|
|
||||||
self:encode_data_Y8_as_Y8_raw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if "RAW" == compression then
|
|
||||||
self:encode_data_R8G8B8A8_as_B8G8R8A8_raw()
|
|
||||||
elseif "RLE" == compression then
|
|
||||||
self:encode_data_R8G8B8A8_as_B8G8R8A8_rle()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local data_length_after = #self.data
|
|
||||||
assert(
|
|
||||||
data_length_after ~= data_length_before,
|
|
||||||
"No data encoded for color format: " .. color_format
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_Y8_as_Y8_raw()
|
|
||||||
assert(8 == self.pixel_depth)
|
|
||||||
local raw_pixels = {}
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
local raw_pixel = string.char(pixel[1])
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(raw_pixels)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8_as_Y8_raw()
|
|
||||||
assert(24 == self.pixel_depth)
|
|
||||||
local raw_pixels = {}
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
-- the HSP RGB to brightness formula is
|
|
||||||
-- sqrt( 0.299 r² + .587 g² + .114 b² )
|
|
||||||
-- see <https://alienryderflex.com/hsp.html>
|
|
||||||
local gray = math.floor(
|
|
||||||
math.sqrt(
|
|
||||||
0.299 * pixel[1]^2 +
|
|
||||||
0.587 * pixel[2]^2 +
|
|
||||||
0.114 * pixel[3]^2
|
|
||||||
) + 0.5
|
|
||||||
)
|
|
||||||
local raw_pixel = string.char(gray)
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(raw_pixels)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8_as_A1R5G5B5_raw()
|
|
||||||
assert(24 == self.pixel_depth)
|
|
||||||
local raw_pixels = {}
|
|
||||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
|
||||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
|
||||||
local max_sample_in = math.pow(2, 8) - 1
|
|
||||||
local max_sample_out = math.pow(2, 5) - 1
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
local colorword = 32768 +
|
|
||||||
((math.floor((pixel[1] * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
|
||||||
((math.floor((pixel[2] * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
|
||||||
((math.floor((pixel[3] * max_sample_out / max_sample_in) + 0.5)) * 1)
|
|
||||||
local raw_pixel = string.char(colorword % 256, math.floor(colorword / 256))
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(raw_pixels)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8_as_A1R5G5B5_rle()
|
|
||||||
assert(24 == self.pixel_depth)
|
|
||||||
local colorword = nil
|
|
||||||
local previous_r = nil
|
|
||||||
local previous_g = nil
|
|
||||||
local previous_b = nil
|
|
||||||
local raw_pixel = ''
|
|
||||||
local raw_pixels = {}
|
|
||||||
local count = 1
|
local count = 1
|
||||||
local packets = {}
|
local packets = {}
|
||||||
local raw_packet = ''
|
|
||||||
local rle_packet = ''
|
|
||||||
-- Sample depth rescaling is done according to the algorithm presented in:
|
|
||||||
-- <https://www.w3.org/TR/2003/REC-PNG-20031110/#13Sample-depth-rescaling>
|
|
||||||
local max_sample_in = math.pow(2, 8) - 1
|
|
||||||
local max_sample_out = math.pow(2, 5) - 1
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or count == 128 then
|
|
||||||
if nil ~= previous_r then
|
|
||||||
colorword = 32768 +
|
|
||||||
((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
|
||||||
((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
|
||||||
((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1)
|
|
||||||
if 1 == count then
|
|
||||||
-- remember pixel verbatim for raw encoding
|
|
||||||
raw_pixel = string.char(colorword % 256, math.floor(colorword / 256))
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
if 128 == #raw_pixels then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- encode raw pixels, if any
|
|
||||||
if #raw_pixels > 0 then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
-- RLE encoding
|
|
||||||
rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256))
|
|
||||||
packets[#packets +1] = rle_packet
|
|
||||||
end
|
|
||||||
end
|
|
||||||
count = 1
|
|
||||||
previous_r = pixel[1]
|
|
||||||
previous_g = pixel[2]
|
|
||||||
previous_b = pixel[3]
|
|
||||||
else
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
colorword = 32768 +
|
|
||||||
((math.floor((previous_r * max_sample_out / max_sample_in) + 0.5)) * 1024) +
|
|
||||||
((math.floor((previous_g * max_sample_out / max_sample_in) + 0.5)) * 32) +
|
|
||||||
((math.floor((previous_b * max_sample_out / max_sample_in) + 0.5)) * 1)
|
|
||||||
if 1 == count then
|
|
||||||
raw_pixel = string.char(colorword % 256, math.floor(colorword / 256))
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
else
|
|
||||||
-- encode raw pixels, if any
|
|
||||||
if #raw_pixels > 0 then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
-- RLE encoding
|
|
||||||
rle_packet = string.char(128 + count - 1, colorword % 256, math.floor(colorword / 256))
|
|
||||||
packets[#packets +1] = rle_packet
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(packets)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8_as_B8G8R8_raw()
|
|
||||||
assert(24 == self.pixel_depth)
|
|
||||||
local raw_pixels = {}
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
local raw_pixel = string.char(pixel[3], pixel[2], pixel[1])
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(raw_pixels)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8_as_B8G8R8_rle()
|
|
||||||
assert(24 == self.pixel_depth)
|
|
||||||
local previous_r = nil
|
|
||||||
local previous_g = nil
|
|
||||||
local previous_b = nil
|
|
||||||
local raw_pixel = ''
|
|
||||||
local raw_pixels = {}
|
|
||||||
local count = 1
|
|
||||||
local packets = {}
|
|
||||||
local raw_packet = ''
|
|
||||||
local rle_packet = ''
|
local rle_packet = ''
|
||||||
for _, row in ipairs(self.pixels) do
|
for _, row in ipairs(self.pixels) do
|
||||||
for _, pixel in ipairs(row) do
|
for _, pixel in ipairs(row) do
|
||||||
if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or count == 128 then
|
current_pixel = string.char(pixel[3], pixel[2], pixel[1])
|
||||||
if nil ~= previous_r then
|
if current_pixel ~= previous_pixel or count == 128 then
|
||||||
if 1 == count then
|
|
||||||
-- remember pixel verbatim for raw encoding
|
|
||||||
raw_pixel = string.char(previous_b, previous_g, previous_r)
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
if 128 == #raw_pixels then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- encode raw pixels, if any
|
|
||||||
if #raw_pixels > 0 then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
-- RLE encoding
|
|
||||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r)
|
|
||||||
packets[#packets +1] = rle_packet
|
packets[#packets +1] = rle_packet
|
||||||
end
|
|
||||||
end
|
|
||||||
count = 1
|
count = 1
|
||||||
previous_r = pixel[1]
|
previous_pixel = current_pixel
|
||||||
previous_g = pixel[2]
|
|
||||||
previous_b = pixel[3]
|
|
||||||
else
|
else
|
||||||
count = count + 1
|
count = count + 1
|
||||||
end
|
end
|
||||||
|
rle_packet = string.char(128 + count - 1) .. current_pixel
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if 1 == count then
|
|
||||||
raw_pixel = string.char(previous_b, previous_g, previous_r)
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
else
|
|
||||||
-- encode raw pixels, if any
|
|
||||||
if #raw_pixels > 0 then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
-- RLE encoding
|
|
||||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r)
|
|
||||||
packets[#packets +1] = rle_packet
|
packets[#packets +1] = rle_packet
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(packets)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8A8_as_B8G8R8A8_raw()
|
|
||||||
assert(32 == self.pixel_depth)
|
|
||||||
local raw_pixels = {}
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
local raw_pixel = string.char(pixel[3], pixel[2], pixel[1], pixel[4])
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(raw_pixels)
|
|
||||||
end
|
|
||||||
|
|
||||||
function image:encode_data_R8G8B8A8_as_B8G8R8A8_rle()
|
|
||||||
assert(32 == self.pixel_depth)
|
|
||||||
local previous_r = nil
|
|
||||||
local previous_g = nil
|
|
||||||
local previous_b = nil
|
|
||||||
local previous_a = nil
|
|
||||||
local raw_pixel = ''
|
|
||||||
local raw_pixels = {}
|
|
||||||
local count = 1
|
|
||||||
local packets = {}
|
|
||||||
local raw_packet = ''
|
|
||||||
local rle_packet = ''
|
|
||||||
for _, row in ipairs(self.pixels) do
|
|
||||||
for _, pixel in ipairs(row) do
|
|
||||||
if pixel[1] ~= previous_r or pixel[2] ~= previous_g or pixel[3] ~= previous_b or pixel[4] ~= previous_a or count == 128 then
|
|
||||||
if nil ~= previous_r then
|
|
||||||
if 1 == count then
|
|
||||||
-- remember pixel verbatim for raw encoding
|
|
||||||
raw_pixel = string.char(previous_b, previous_g, previous_r, previous_a)
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
if 128 == #raw_pixels then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- encode raw pixels, if any
|
|
||||||
if #raw_pixels > 0 then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
-- RLE encoding
|
|
||||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r, previous_a)
|
|
||||||
packets[#packets +1] = rle_packet
|
|
||||||
end
|
|
||||||
end
|
|
||||||
count = 1
|
|
||||||
previous_r = pixel[1]
|
|
||||||
previous_g = pixel[2]
|
|
||||||
previous_b = pixel[3]
|
|
||||||
previous_a = pixel[4]
|
|
||||||
else
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if 1 == count then
|
|
||||||
raw_pixel = string.char(previous_b, previous_g, previous_r, previous_a)
|
|
||||||
raw_pixels[#raw_pixels + 1] = raw_pixel
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
else
|
|
||||||
-- encode raw pixels, if any
|
|
||||||
if #raw_pixels > 0 then
|
|
||||||
raw_packet = string.char(#raw_pixels - 1)
|
|
||||||
packets[#packets + 1] = raw_packet
|
|
||||||
for i=1, #raw_pixels do
|
|
||||||
packets[#packets +1] = raw_pixels[i]
|
|
||||||
end
|
|
||||||
raw_pixels = {}
|
|
||||||
end
|
|
||||||
-- RLE encoding
|
|
||||||
rle_packet = string.char(128 + count - 1, previous_b, previous_g, previous_r, previous_a)
|
|
||||||
packets[#packets +1] = rle_packet
|
|
||||||
end
|
|
||||||
self.data = self.data .. table.concat(packets)
|
self.data = self.data .. table.concat(packets)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -548,44 +75,15 @@ function image:encode_footer()
|
||||||
.. string.char(0)
|
.. string.char(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:encode(properties)
|
function image:encode()
|
||||||
self.data = ""
|
self:encode_header() -- header
|
||||||
self:encode_header(properties) -- header
|
|
||||||
-- no color map and image id data
|
-- no color map and image id data
|
||||||
self:encode_data(properties) -- encode data
|
self:encode_data() -- encode data
|
||||||
-- no extension or developer area
|
-- no extension or developer area
|
||||||
self:encode_footer() -- footer
|
self:encode_footer() -- footer
|
||||||
end
|
end
|
||||||
|
|
||||||
function image:save(filename, properties)
|
function image:save(filename)
|
||||||
local properties = properties or {}
|
|
||||||
properties.colormap = properties.colormap or {}
|
|
||||||
properties.compression = properties.compression or "RAW"
|
|
||||||
|
|
||||||
self.pixel_depth = #self.pixels[1][1] * 8
|
|
||||||
|
|
||||||
local color_format_defaults_by_pixel_depth = {
|
|
||||||
[8] = "Y8",
|
|
||||||
[24] = "B8G8R8",
|
|
||||||
[32] = "B8G8R8A8",
|
|
||||||
}
|
|
||||||
if nil == properties.color_format then
|
|
||||||
if 0 ~= #properties.colormap then
|
|
||||||
properties.color_format =
|
|
||||||
color_format_defaults_by_pixel_depth[
|
|
||||||
#properties.colormap[1] * 8
|
|
||||||
]
|
|
||||||
else
|
|
||||||
properties.color_format =
|
|
||||||
color_format_defaults_by_pixel_depth[
|
|
||||||
self.pixel_depth
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert( nil ~= properties.color_format )
|
|
||||||
|
|
||||||
self:encode(properties)
|
|
||||||
|
|
||||||
local f = assert(io.open(filename, "wb"))
|
local f = assert(io.open(filename, "wb"))
|
||||||
f:write(self.data)
|
f:write(self.data)
|
||||||
f:close()
|
f:close()
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
name = tga_encoder
|
name = tga_encoder
|
||||||
|
author = Fleckenstein
|
||||||
description = A TGA Encoder written in Lua without the use of external Libraries.
|
description = A TGA Encoder written in Lua without the use of external Libraries.
|
||||||
|
|
|
@ -95,13 +95,6 @@ local cod = {
|
||||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
|
||||||
on_rightclick = function(self, clicker)
|
|
||||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
|
||||||
self.object:remove()
|
|
||||||
clicker:set_wielded_item("mcl_fishing:bucket_cod")
|
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,4 +246,4 @@ water-16,
|
||||||
water)
|
water)
|
||||||
|
|
||||||
--spawn egg
|
--spawn egg
|
||||||
mobs:register_egg("extra_mobs:dolphin", S("Dolphin"), "extra_mobs_spawn_icon_dolphin.png", 0)
|
mobs:register_egg("extra_mobs:dolphin", S("dolphin"), "extra_mobs_spawn_icon_dolphin.png", 0)
|
||||||
|
|
|
@ -18,14 +18,17 @@ local S = minetest.get_translator("extra_mobs")
|
||||||
--################### fox
|
--################### fox
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
local followitem = "mcl_farming:sweet_berry"
|
local followitem = ""
|
||||||
|
if minetest.get_modpath("mc_sweet_berry") then
|
||||||
|
followitem = "mc_sweet_berry:sweet_berry"
|
||||||
|
else
|
||||||
|
followitem = nil
|
||||||
|
end
|
||||||
|
|
||||||
local fox = {
|
local fox = {
|
||||||
type = "animal",
|
type = "monster",
|
||||||
passive = false,
|
passive = false,
|
||||||
spawn_class = "hostile",
|
spawn_class = "hostile",
|
||||||
skittish = true,
|
|
||||||
runaway = true,
|
|
||||||
hp_min = 10,
|
hp_min = 10,
|
||||||
hp_max = 10,
|
hp_max = 10,
|
||||||
xp_min = 1,
|
xp_min = 1,
|
||||||
|
@ -34,20 +37,9 @@ local fox = {
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
damage = 2,
|
damage = 2,
|
||||||
reach = 1.5,
|
reach = 1.5,
|
||||||
jump = true,
|
|
||||||
makes_footstep_sound = true,
|
|
||||||
walk_velocity = 3,
|
|
||||||
run_velocity = 6,
|
|
||||||
follow_velocity = 2,
|
|
||||||
follow = followitem,
|
|
||||||
pathfinding = 1,
|
|
||||||
fear_height = 4,
|
|
||||||
view_range = 16,
|
|
||||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.84, 0.3},
|
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.84, 0.3},
|
||||||
specific_attack = { "mobs_mc:chicken", "extra_mobs:cod", "extra_mobs:salmon" },
|
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "extra_mobs_fox.b3d",
|
mesh = "extra_mobs_fox.b3d",
|
||||||
rotate = 270,
|
|
||||||
textures = { {
|
textures = { {
|
||||||
"extra_mobs_fox.png",
|
"extra_mobs_fox.png",
|
||||||
"extra_mobs_trans.png",
|
"extra_mobs_trans.png",
|
||||||
|
@ -55,6 +47,10 @@ local fox = {
|
||||||
visual_size = {x=3, y=3},
|
visual_size = {x=3, y=3},
|
||||||
sounds = {
|
sounds = {
|
||||||
},
|
},
|
||||||
|
jump = true,
|
||||||
|
makes_footstep_sound = true,
|
||||||
|
walk_velocity = 3,
|
||||||
|
run_velocity = 6,
|
||||||
drops = {
|
drops = {
|
||||||
},
|
},
|
||||||
animation = {
|
animation = {
|
||||||
|
@ -72,9 +68,9 @@ local fox = {
|
||||||
lay_start = 34,
|
lay_start = 34,
|
||||||
lay_end = 34,
|
lay_end = 34,
|
||||||
},
|
},
|
||||||
|
runaway = true,
|
||||||
on_spawn = function(self)
|
on_spawn = function(self)
|
||||||
if minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:snow") ~= nil
|
if minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:snow") ~= nil or minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:dirt_with_grass_snow") ~= nil then
|
||||||
or minetest.find_node_near(self.object:get_pos(), 4, "mcl_core:dirt_with_grass_snow") ~= nil then
|
|
||||||
self.object:set_properties({textures={"extra_mobs_artic_fox.png", "extra_mobs_trans.png"}})
|
self.object:set_properties({textures={"extra_mobs_artic_fox.png", "extra_mobs_trans.png"}})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -92,11 +88,7 @@ local fox = {
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 8)) do
|
for _,object in pairs(minetest.get_objects_inside_radius(self.object:get_pos(), 8)) do
|
||||||
if object
|
if object and not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "extra_mobs:fox" and self.state ~= "attack" and math.random(1, 500) == 1 then
|
||||||
and not object:is_player()
|
|
||||||
and object:get_luaentity()
|
|
||||||
and object:get_luaentity().name == "extra_mobs:fox"
|
|
||||||
and self.state ~= "attack" and math.random(1, 500) == 1 then
|
|
||||||
self.horny = true
|
self.horny = true
|
||||||
end
|
end
|
||||||
local lp = object:get_pos()
|
local lp = object:get_pos()
|
||||||
|
@ -106,45 +98,36 @@ local fox = {
|
||||||
y = lp.y - s.y,
|
y = lp.y - s.y,
|
||||||
z = lp.z - s.z
|
z = lp.z - s.z
|
||||||
}
|
}
|
||||||
-- scare logic
|
if object and object:is_player() and not object:get_player_control().sneak or not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mobs_mc:wolf" then
|
||||||
if (object
|
|
||||||
and object:is_player()
|
|
||||||
and not object:get_player_control().sneak)
|
|
||||||
or (not object:is_player()
|
|
||||||
and object:get_luaentity()
|
|
||||||
and object:get_luaentity().name == "mobs_mc:wolf") then
|
|
||||||
-- don't keep setting it once it's set
|
|
||||||
if not self.state == "runaway" then
|
|
||||||
self.state = "runaway"
|
self.state = "runaway"
|
||||||
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
self.object:set_rotation({x=0,y=(atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate,z=0})
|
||||||
end
|
if self.reach > vector.distance(self.object:get_pos(), object:get_pos()) and self.timer > .9 then
|
||||||
-- if it is within a distance of the player or wolf
|
|
||||||
if 6 > vector.distance(self.object:get_pos(), object:get_pos()) then
|
|
||||||
self.timer = self.timer + 1
|
|
||||||
-- have some time before getting scared
|
|
||||||
if self.timer > 6 then
|
|
||||||
self.timer = 0
|
self.timer = 0
|
||||||
-- punch the fox for the player, but don't do any damage
|
object:punch(self.object, 1.0, {
|
||||||
self.object:punch(object, 0, {
|
full_punch_interval = 1.0,
|
||||||
full_punch_interval = 0,
|
damage_groups = {fleshy = self.damage}
|
||||||
damage_groups = {fleshy = 0}
|
|
||||||
}, nil)
|
}, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
do_punch = function(self)
|
do_punch = function(self)
|
||||||
self.state = "runaway"
|
self.state = "runaway"
|
||||||
end,
|
end,
|
||||||
|
follow = followitem,
|
||||||
|
fear_height = 4,
|
||||||
|
view_range = 16,
|
||||||
|
specific_attack = { "mobs_mc:chicken", "extra_mobs:cod", "extra_mobs:salmon" },
|
||||||
}
|
}
|
||||||
|
|
||||||
mobs:register_mob("extra_mobs:fox", fox)
|
mobs:register_mob("extra_mobs:fox", fox)
|
||||||
|
|
||||||
-- spawning
|
-- spawning
|
||||||
mobs:spawn_setup({
|
mobs:spawn_specific(
|
||||||
name = "extra_mobs:fox",
|
"extra_mobs:fox",
|
||||||
biomes = {
|
"overworld",
|
||||||
|
"ground",
|
||||||
|
{
|
||||||
"FlowerForest",
|
"FlowerForest",
|
||||||
"Swampland",
|
"Swampland",
|
||||||
"Taiga",
|
"Taiga",
|
||||||
|
@ -162,10 +145,13 @@ mobs:spawn_setup({
|
||||||
"ExtremeHillsM",
|
"ExtremeHillsM",
|
||||||
"BirchForestM",
|
"BirchForestM",
|
||||||
},
|
},
|
||||||
interval = 30,
|
0,
|
||||||
chance = 6000,
|
minetest.LIGHT_MAX+1,
|
||||||
min_height = mobs_mc.spawn_height.water,
|
30,
|
||||||
})
|
6000,
|
||||||
|
3,
|
||||||
|
mobs_mc.spawn_height.water,
|
||||||
|
mobs_mc.spawn_height.overworld_max)
|
||||||
|
|
||||||
--mobs:spawn_specific("extra_mobs:fox", "overworld", "ground", 0, minetest.LIGHT_MAX+1, 30, 6000, 3, 0, 500)
|
--mobs:spawn_specific("extra_mobs:fox", "overworld", "ground", 0, minetest.LIGHT_MAX+1, 30, 6000, 3, 0, 500)
|
||||||
--[[
|
--[[
|
||||||
|
|
|
@ -231,13 +231,3 @@ water)
|
||||||
|
|
||||||
-- spawn egg
|
-- spawn egg
|
||||||
mobs:register_egg("extra_mobs:glow_squid", S("Glow Squid"), "extra_mobs_spawn_icon_glow_squid.png", 0)
|
mobs:register_egg("extra_mobs:glow_squid", S("Glow Squid"), "extra_mobs_spawn_icon_glow_squid.png", 0)
|
||||||
|
|
||||||
-- dropped item (used to craft glowing itemframe)
|
|
||||||
|
|
||||||
minetest.register_craftitem("extra_mobs:glow_ink_sac", {
|
|
||||||
description = S("Glow Ink Sac"),
|
|
||||||
_doc_items_longdesc = S("Use it to craft the Glow Item Frame."),
|
|
||||||
_doc_items_usagehelp = S("Use the Glow Ink Sac and the normal Item Frame to craft the Glow Item Frame."),
|
|
||||||
inventory_image = "extra_mobs_glow_ink_sac.png",
|
|
||||||
groups = { craftitem = 1 },
|
|
||||||
})
|
|
||||||
|
|
|
@ -0,0 +1,329 @@
|
||||||
|
local S = minetest.get_translator("extra_mobs")
|
||||||
|
|
||||||
|
minetest.register_craftitem("extra_mobs:glow_ink_sac", {
|
||||||
|
description = S("Glow Ink Sac"),
|
||||||
|
_doc_items_longdesc = S("Use it to craft the Glow Item Frame."),
|
||||||
|
_doc_items_usagehelp = S("Use the Glow Ink Sac and the normal Item Frame to craft the Glow Item Frame."),
|
||||||
|
inventory_image = "extra_mobs_glow_ink_sac.png",
|
||||||
|
groups = { craftitem = 1 },
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
--[[This mod is originally by Zeg9, but heavily modified for MineClone 2.
|
||||||
|
|
||||||
|
Model created by 22i, licensed under the
|
||||||
|
GNU GPLv3 <https://www.gnu.org/licenses/gpl-3.0.html>.
|
||||||
|
|
||||||
|
Source: <https://github.com/22i/amc>
|
||||||
|
]]
|
||||||
|
|
||||||
|
|
||||||
|
local VISUAL_SIZE = 0.3
|
||||||
|
|
||||||
|
minetest.register_entity("extra_mobs:glow_item_frame_item",{
|
||||||
|
hp_max = 1,
|
||||||
|
visual = "wielditem",
|
||||||
|
visual_size = {x=VISUAL_SIZE, y=VISUAL_SIZE},
|
||||||
|
physical = false,
|
||||||
|
pointable = false,
|
||||||
|
textures = { "blank.png" },
|
||||||
|
_texture = "blank.png",
|
||||||
|
_scale = 1,
|
||||||
|
glow = minetest.LIGHT_MAX,
|
||||||
|
|
||||||
|
on_activate = function(self, staticdata)
|
||||||
|
if staticdata ~= nil and staticdata ~= "" then
|
||||||
|
local data = staticdata:split(';')
|
||||||
|
if data and data[1] and data[2] then
|
||||||
|
self._nodename = data[1]
|
||||||
|
self._texture = data[2]
|
||||||
|
if data[3] then
|
||||||
|
self._scale = data[3]
|
||||||
|
else
|
||||||
|
self._scale = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self._texture ~= nil then
|
||||||
|
self.object:set_properties({
|
||||||
|
textures={self._texture},
|
||||||
|
visual_size={x=VISUAL_SIZE/self._scale, y=VISUAL_SIZE/self._scale},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
get_staticdata = function(self)
|
||||||
|
if self._nodename ~= nil and self._texture ~= nil then
|
||||||
|
local ret = self._nodename .. ';' .. self._texture
|
||||||
|
if self._scale ~= nil then
|
||||||
|
ret = ret .. ';' .. self._scale
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
return ""
|
||||||
|
end,
|
||||||
|
|
||||||
|
_update_texture = function(self)
|
||||||
|
if self._texture ~= nil then
|
||||||
|
self.object:set_properties({
|
||||||
|
textures={self._texture},
|
||||||
|
visual_size={x=VISUAL_SIZE/self._scale, y=VISUAL_SIZE/self._scale},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
local facedir = {}
|
||||||
|
facedir[0] = {x=0,y=0,z=1}
|
||||||
|
facedir[1] = {x=1,y=0,z=0}
|
||||||
|
facedir[2] = {x=0,y=0,z=-1}
|
||||||
|
facedir[3] = {x=-1,y=0,z=0}
|
||||||
|
|
||||||
|
local remove_item_entity = function(pos, node)
|
||||||
|
local objs = nil
|
||||||
|
if node.name == "extra_mobs:glow_item_frame" then
|
||||||
|
objs = minetest.get_objects_inside_radius(pos, .5)
|
||||||
|
end
|
||||||
|
if objs then
|
||||||
|
for _, obj in ipairs(objs) do
|
||||||
|
if obj and obj:get_luaentity() and obj:get_luaentity().name == "extra_mobs:glow_item_frame_item" then
|
||||||
|
obj:remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local update_item_entity = function(pos, node, param2)
|
||||||
|
remove_item_entity(pos, node)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local inv = meta:get_inventory()
|
||||||
|
local item = inv:get_stack("main", 1)
|
||||||
|
if not item:is_empty() then
|
||||||
|
if not param2 then
|
||||||
|
param2 = node.param2
|
||||||
|
end
|
||||||
|
if node.name == "extra_mobs:glow_item_frame" then
|
||||||
|
local posad = facedir[param2]
|
||||||
|
pos.x = pos.x + posad.x*6.5/16
|
||||||
|
pos.y = pos.y + posad.y*6.5/16
|
||||||
|
pos.z = pos.z + posad.z*6.5/16
|
||||||
|
end
|
||||||
|
local e = minetest.add_entity(pos, "extra_mobs:glow_item_frame_item")
|
||||||
|
local lua = e:get_luaentity()
|
||||||
|
lua._nodename = node.name
|
||||||
|
local itemname = item:get_name()
|
||||||
|
if itemname == "" or itemname == nil then
|
||||||
|
lua._texture = "blank.png"
|
||||||
|
lua._scale = 1
|
||||||
|
else
|
||||||
|
lua._texture = itemname
|
||||||
|
local def = minetest.registered_items[itemname]
|
||||||
|
if def and def.wield_scale then
|
||||||
|
lua._scale = def.wield_scale.x
|
||||||
|
else
|
||||||
|
lua._scale = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
lua:_update_texture()
|
||||||
|
if node.name == "extra_mobs:glow_item_frame" then
|
||||||
|
local yaw = math.pi*2 - param2 * math.pi/2
|
||||||
|
e:set_yaw(yaw)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local drop_item = function(pos, node, meta, clicker)
|
||||||
|
local cname = ""
|
||||||
|
if clicker and clicker:is_player() then
|
||||||
|
cname = clicker:get_player_name()
|
||||||
|
end
|
||||||
|
if node.name == "extra_mobs:glow_item_frame" and not minetest.is_creative_enabled(cname) then
|
||||||
|
local inv = meta:get_inventory()
|
||||||
|
local item = inv:get_stack("main", 1)
|
||||||
|
if not item:is_empty() then
|
||||||
|
minetest.add_item(pos, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
meta:set_string("infotext", "")
|
||||||
|
remove_item_entity(pos, node)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_node("extra_mobs:glow_item_frame",{
|
||||||
|
description = S("Glow Item Frame"),
|
||||||
|
_tt_help = S("Can hold an item and glows"),
|
||||||
|
_doc_items_longdesc = S("Glow Item frames are decorative blocks in which items can be placed."),
|
||||||
|
_doc_items_usagehelp = S("Just place any item on the item frame. Use the item frame again to retrieve the item."),
|
||||||
|
drawtype = "mesh",
|
||||||
|
is_ground_content = false,
|
||||||
|
mesh = "extra_mobs_glow_item_frame.obj",
|
||||||
|
selection_box = { type = "fixed", fixed = {-6/16, -6/16, 7/16, 6/16, 6/16, 0.5} },
|
||||||
|
collision_box = { type = "fixed", fixed = {-6/16, -6/16, 7/16, 6/16, 6/16, 0.5} },
|
||||||
|
tiles = {"extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png", "extra_mobs_glow_item_frame_border.png"},
|
||||||
|
inventory_image = "extra_mobs_glow_item_frame_item.png",
|
||||||
|
wield_image = "extra_mobs_glow_item_frame.png",
|
||||||
|
paramtype = "light",
|
||||||
|
paramtype2 = "facedir",
|
||||||
|
|
||||||
|
--FIXME: should only be glowing, no light source. How is that possible with a node?
|
||||||
|
light_source = 1,
|
||||||
|
|
||||||
|
sunlight_propagates = true,
|
||||||
|
groups = { dig_immediate=3,deco_block=1,dig_by_piston=1,container=7,attached_node_facedir=1 },
|
||||||
|
sounds = mcl_sounds.node_sound_defaults(),
|
||||||
|
node_placement_prediction = "",
|
||||||
|
on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
if pointed_thing.type ~= "node" then
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Use pointed node's on_rightclick function first, if present
|
||||||
|
local node = minetest.get_node(pointed_thing.under)
|
||||||
|
if placer and not placer:get_player_control().sneak then
|
||||||
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||||
|
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return minetest.item_place(itemstack, placer, pointed_thing, minetest.dir_to_facedir(vector.direction(pointed_thing.above, pointed_thing.under)))
|
||||||
|
end,
|
||||||
|
on_construct = function(pos)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local inv = meta:get_inventory()
|
||||||
|
inv:set_size("main", 1)
|
||||||
|
end,
|
||||||
|
on_rightclick = function(pos, node, clicker, itemstack)
|
||||||
|
if not itemstack then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local pname = clicker:get_player_name()
|
||||||
|
if minetest.is_protected(pos, pname) then
|
||||||
|
minetest.record_protection_violation(pos, pname)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
drop_item(pos, node, meta, clicker)
|
||||||
|
local inv = meta:get_inventory()
|
||||||
|
if itemstack:is_empty() then
|
||||||
|
remove_item_entity(pos, node)
|
||||||
|
meta:set_string("infotext", "")
|
||||||
|
inv:set_stack("main", 1, "")
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
local put_itemstack = ItemStack(itemstack)
|
||||||
|
put_itemstack:set_count(1)
|
||||||
|
inv:set_stack("main", 1, put_itemstack)
|
||||||
|
update_item_entity(pos, node)
|
||||||
|
-- Add node infotext when item has been named
|
||||||
|
local imeta = itemstack:get_meta()
|
||||||
|
local iname = imeta:get_string("name")
|
||||||
|
if iname then
|
||||||
|
meta:set_string("infotext", iname)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
if minetest.is_protected(pos, name) then
|
||||||
|
minetest.record_protection_violation(pos, name)
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
if minetest.is_protected(pos, name) then
|
||||||
|
minetest.record_protection_violation(pos, name)
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return stack:get_count()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
if minetest.is_protected(pos, name) then
|
||||||
|
minetest.record_protection_violation(pos, name)
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return stack:get_count()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_destruct = function(pos)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
drop_item(pos, node, meta)
|
||||||
|
end,
|
||||||
|
on_rotate = function(pos, node, user, mode, param2)
|
||||||
|
if mode == screwdriver.ROTATE_FACE then
|
||||||
|
-- Rotate face
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
|
||||||
|
local objs = nil
|
||||||
|
if node.name == "extra_mobs:glow_item_frame" then
|
||||||
|
objs = minetest.get_objects_inside_radius(pos, .5)
|
||||||
|
end
|
||||||
|
if objs then
|
||||||
|
for _, obj in ipairs(objs) do
|
||||||
|
if obj and obj:get_luaentity() and obj:get_luaentity().name == "extra_mobs:glow_item_frame_item" then
|
||||||
|
update_item_entity(pos, node, (node.param2+1) % 4)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
elseif mode == screwdriver.ROTATE_AXIS then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
type = "shapeless",
|
||||||
|
output = 'extra_mobs:glow_item_frame',
|
||||||
|
recipe = {'mcl_itemframes:item_frame', 'extra_mobs:glow_ink_sac'},
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_lbm({
|
||||||
|
label = "Update legacy item frames",
|
||||||
|
name = "extra_mobs:update_legacy_glow_item_frames",
|
||||||
|
nodenames = {"extra_mobs:glow_frame"},
|
||||||
|
action = function(pos, node)
|
||||||
|
-- Swap legacy node, then respawn entity
|
||||||
|
node.name = "extra_mobs:glow_item_frame"
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local item = meta:get_string("item")
|
||||||
|
minetest.swap_node(pos, node)
|
||||||
|
if item ~= "" then
|
||||||
|
local itemstack = ItemStack(minetest.deserialize(meta:get_string("itemdata")))
|
||||||
|
local inv = meta:get_inventory()
|
||||||
|
inv:set_size("main", 1)
|
||||||
|
if not itemstack:is_empty() then
|
||||||
|
inv:set_stack("main", 1, itemstack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
update_item_entity(pos, node)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- FIXME: Item entities can get destroyed by /clearobjects
|
||||||
|
minetest.register_lbm({
|
||||||
|
label = "Respawn item frame item entities",
|
||||||
|
name = "extra_mobs:respawn_entities",
|
||||||
|
nodenames = {"extra_mobs:glow_item_frame"},
|
||||||
|
run_at_every_load = true,
|
||||||
|
action = function(pos, node)
|
||||||
|
update_item_entity(pos, node)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_alias("extra_mobs:glow_frame", "extra_mobs:glow_item_frame")
|
||||||
|
|
||||||
|
--------------------
|
|
@ -21,3 +21,8 @@ dofile(path .. "/cod.lua")
|
||||||
dofile(path .. "/salmon.lua")
|
dofile(path .. "/salmon.lua")
|
||||||
dofile(path .. "/dolphin.lua")
|
dofile(path .. "/dolphin.lua")
|
||||||
dofile(path .. "/glow_squid.lua")
|
dofile(path .. "/glow_squid.lua")
|
||||||
|
|
||||||
|
--Items
|
||||||
|
dofile(path .. "/glow_squid_items.lua")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# textdomain:extra_mobs
|
|
||||||
Hoglin=Hoglin
|
|
||||||
Piglin=Piglin
|
|
||||||
Piglin Brute=Piglin Barbare
|
|
||||||
Strider=Arpenteur
|
|
||||||
Fox=Renard
|
|
||||||
Cod=Poisson
|
|
||||||
Salmon=Saumon
|
|
||||||
Dolphin=Dauphin
|
|
||||||
Glow Squid=Pieuvre Lumineuse
|
|
||||||
Glow Ink Sac=Sac d'Encre Lumineuse
|
|
|
@ -1,11 +0,0 @@
|
||||||
# textdomain:extra_mobs
|
|
||||||
Hoglin=Хоглин
|
|
||||||
piglin=Пиглин
|
|
||||||
piglin Brute=Жестокий пиглин
|
|
||||||
Strider=Страйдер
|
|
||||||
Fox=Лиса
|
|
||||||
Cod=Треска
|
|
||||||
Salmon=Лосось
|
|
||||||
dolphin=Дельфин
|
|
||||||
Glow Squid=Светящийся спрут
|
|
||||||
Glow Ink Sac=Светящийся чернильный мешок
|
|
|
@ -1,11 +0,0 @@
|
||||||
# textdomain:extra_mobs
|
|
||||||
Hoglin=
|
|
||||||
Piglin=
|
|
||||||
Piglin Brute=
|
|
||||||
Strider=
|
|
||||||
Fox=
|
|
||||||
Cod=
|
|
||||||
Salmon=
|
|
||||||
Dolphin=
|
|
||||||
Glow Squid=
|
|
||||||
Glow Ink Sac=
|
|
|
@ -296,5 +296,5 @@ minetest.LIGHT_MAX+1,
|
||||||
mobs_mc.spawn_height.nether_min,
|
mobs_mc.spawn_height.nether_min,
|
||||||
mobs_mc.spawn_height.nether_max)
|
mobs_mc.spawn_height.nether_max)
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("extra_mobs:piglin", S("Piglin"), "extra_mobs_spawn_icon_piglin.png", 0)
|
mobs:register_egg("extra_mobs:piglin", S("piglin"), "extra_mobs_spawn_icon_piglin.png", 0)
|
||||||
mobs:register_egg("extra_mobs:piglin_brute", S("Piglin Brute"), "extra_mobs_spawn_icon_piglin.png", 0)
|
mobs:register_egg("extra_mobs:piglin_brute", S("piglin Brute"), "extra_mobs_spawn_icon_piglin.png", 0)
|
||||||
|
|
|
@ -55,13 +55,6 @@ local salmon = {
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
runaway = true,
|
runaway = true,
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
on_rightclick = function(self, clicker)
|
|
||||||
if clicker:get_wielded_item():get_name() == "mcl_buckets:bucket_water" then
|
|
||||||
self.object:remove()
|
|
||||||
clicker:set_wielded_item("mcl_fishing:bucket_salmon")
|
|
||||||
awards.unlock(clicker:get_player_name(), "mcl:tacticalFishing")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mobs:register_mob("extra_mobs:salmon", salmon)
|
mobs:register_mob("extra_mobs:salmon", salmon)
|
||||||
|
|
|
@ -213,36 +213,20 @@ baby_strider.child = 1
|
||||||
mobs:register_mob("extra_mobs:baby_strider", baby_strider)
|
mobs:register_mob("extra_mobs:baby_strider", baby_strider)
|
||||||
|
|
||||||
-- Regular spawning in the Nether
|
-- Regular spawning in the Nether
|
||||||
|
mobs:spawn_specific(
|
||||||
mobs:spawn_setup({
|
"extra_mobs:strider",
|
||||||
name = "extra_mobs:strider",
|
"nether",
|
||||||
type_of_spawning = "lava",
|
"lava",
|
||||||
dimension = "nether",
|
{
|
||||||
biomes = {
|
|
||||||
"Nether"
|
"Nether"
|
||||||
},
|
},
|
||||||
min_height = mcl_mapgen.nether.min,
|
0,
|
||||||
max_height = mcl_mapgen.nether.max,
|
minetest.LIGHT_MAX+1,
|
||||||
chance = 2000,
|
30,
|
||||||
check_position = function(pos)
|
6000,
|
||||||
return minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name:find("lava")
|
3,
|
||||||
end
|
mobs_mc.spawn_height.nether_min,
|
||||||
})
|
mobs_mc.spawn_height.nether_max)
|
||||||
|
|
||||||
mobs:spawn_setup({
|
|
||||||
name = "extra_mobs:baby_strider",
|
|
||||||
type_of_spawning = "lava",
|
|
||||||
dimension = "nether",
|
|
||||||
biomes = {
|
|
||||||
"Nether"
|
|
||||||
},
|
|
||||||
min_height = mcl_mapgen.nether.min,
|
|
||||||
max_height = mcl_mapgen.nether.max,
|
|
||||||
chance = 100,
|
|
||||||
check_position = function(pos)
|
|
||||||
return minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z}).name:find("lava")
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
-- spawn eggs
|
-- spawn eggs
|
||||||
mobs:register_egg("extra_mobs:strider", S("Strider"), "extra_mobs_spawn_icon_strider.png", 0)
|
mobs:register_egg("extra_mobs:strider", S("Strider"), "extra_mobs_spawn_icon_strider.png", 0)
|
||||||
|
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 296 B |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
@ -166,7 +166,6 @@ function boat.on_activate(self, staticdata, dtime_s)
|
||||||
self._last_v = self._v
|
self._last_v = self._v
|
||||||
self._itemstring = data.itemstring
|
self._itemstring = data.itemstring
|
||||||
|
|
||||||
if data.textures then
|
|
||||||
while #data.textures < 5 do
|
while #data.textures < 5 do
|
||||||
table.insert(data.textures, data.textures[1])
|
table.insert(data.textures, data.textures[1])
|
||||||
end
|
end
|
||||||
|
@ -174,16 +173,12 @@ function boat.on_activate(self, staticdata, dtime_s)
|
||||||
self.object:set_properties({textures = data.textures})
|
self.object:set_properties({textures = data.textures})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
function boat.get_staticdata(self)
|
function boat.get_staticdata(self)
|
||||||
if not self then return end
|
|
||||||
local object = self.object
|
|
||||||
local object_properties = object and object.get_properties and object:get_properties()
|
|
||||||
return minetest.serialize({
|
return minetest.serialize({
|
||||||
v = self._v,
|
v = self._v,
|
||||||
itemstring = self._itemstring,
|
itemstring = self._itemstring,
|
||||||
textures = object_properties and object_properties.textures
|
textures = self.object:get_properties().textures
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ Boats are used to travel on the surface of water.=Les bateaux sont utilisés pou
|
||||||
Dark Oak Boat=Bateau en Chêne Noir
|
Dark Oak Boat=Bateau en Chêne Noir
|
||||||
Jungle Boat=Bateau en Acajou
|
Jungle Boat=Bateau en Acajou
|
||||||
Oak Boat=Bateau en Chêne
|
Oak Boat=Bateau en Chêne
|
||||||
Obsidian Boat=Bateau en Obsidienne
|
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
||||||
Spruce Boat=Bateau en Sapin
|
Spruce Boat=Bateau en Sapin
|
||||||
Water vehicle=Véhicule aquatique
|
Water vehicle=Véhicule aquatique
|
||||||
Sneak to dismount=S'accroupir (sneak) pour descendre
|
Sneak to dismount=
|
|
@ -1,13 +0,0 @@
|
||||||
# textdomain: mcl_boats
|
|
||||||
Acacia Boat=Barca de Cacièr
|
|
||||||
Birch Boat=Barca de Bèç
|
|
||||||
Boat=Barca
|
|
||||||
Boats are used to travel on the surface of water.= Las Barcas permetàn de vogar per l'aiga.
|
|
||||||
Dark Oak Boat=Barca de Jàrric
|
|
||||||
Jungle Boat=Barca de Jungla
|
|
||||||
Oak Boat=Barca de Ròure
|
|
||||||
Obsidian Boat=Barca d'Obsidiana
|
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Fasetz un clic dreit sobre una sorça d'aiga per botar la barca. Fasetz un clic dreit sobre la barca per ne'n dintrar. Utilisatz [Esquèrra] e [Dreita] per menar, [Davant] per accelerar, e [Darrèir] per alentir o recular. Utilizatz [S'acatar] per o quitar, picatz la barca per o faire tombar en objèct.
|
|
||||||
Spruce Boat=Barca de Sap
|
|
||||||
Water vehicle=Veïcule d'Aiga
|
|
||||||
Sneak to dismount=S'acatar per descendre
|
|
|
@ -4,9 +4,8 @@ Birch Boat=Берёзовая лодка
|
||||||
Boat=Лодка
|
Boat=Лодка
|
||||||
Boats are used to travel on the surface of water.=С помощью лодки можно путешествовать по водной поверхности.
|
Boats are used to travel on the surface of water.=С помощью лодки можно путешествовать по водной поверхности.
|
||||||
Dark Oak Boat=Лодка из тёмного дуба
|
Dark Oak Boat=Лодка из тёмного дуба
|
||||||
Jungle Boat=Лодка из тропического дерева
|
Jungle Boat=Лодка из дерева джунглей
|
||||||
Oak Boat=Дубовая лодка
|
Oak Boat=Дубовая лодка
|
||||||
Obsidian Boat=Обсидиановая лодка
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Правый клик по воде спустит лодку на воду. Правый клик по лодке разместит вас в ней. [Влево] и [Вправо] - рулить, [Вперед] - разгоняться, [Назад] - тормозить или плыть назад. Правый клик по лодке, когда вы в ней, позволит выйти из неё. Удар по лодке превратит её обратно в предмет.
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Правый клик по воде спустит лодку на воду. Правый клик по лодке, чтобы сесть в неё. [Влево] и [Вправо] - рулить, [Вперед] для ускорения, [Назад] - тормозить или плыть назад. Правый клик по лодке, когда вы в ней, позволит выйти из неё. Удар по лодке превратит её обратно в предмет.
|
|
||||||
Spruce Boat=Еловая лодка
|
Spruce Boat=Еловая лодка
|
||||||
Water vehicle=Водный транспорт
|
Water vehicle=Водный транспорт
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
# textdomain: mcl_boats
|
|
||||||
Acacia Boat=金合欢木船
|
|
||||||
Birch Boat=白桦木船
|
|
||||||
Boat=船
|
|
||||||
Boats are used to travel on the surface of water.=船是用来在水面上航行的.
|
|
||||||
Dark Oak Boat=黑色橡木船
|
|
||||||
Jungle Boat=从林木船
|
|
||||||
Oak Boat=橡木船
|
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=右键单击水源以放置船。右键单击船进入。使用[左]和[右]转向,[前]加速,[后]减速或向后移动。使用[潜行]离开船,击打船使其作为物品掉落。
|
|
||||||
Spruce Boat=云杉船
|
|
||||||
Water vehicle=水上交通工具
|
|
||||||
Sneak to dismount=潜行以下船
|
|
|
@ -6,7 +6,6 @@ Boats are used to travel on the surface of water.=
|
||||||
Dark Oak Boat=
|
Dark Oak Boat=
|
||||||
Jungle Boat=
|
Jungle Boat=
|
||||||
Oak Boat=
|
Oak Boat=
|
||||||
Obsidian Boat=
|
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=
|
||||||
Spruce Boat=
|
Spruce Boat=
|
||||||
Water vehicle=
|
Water vehicle=
|
||||||
|
|
|
@ -1,24 +1,16 @@
|
||||||
-- Dripping Water Mod
|
-- Dripping Water Mod
|
||||||
-- by kddekadenz
|
-- by kddekadenz
|
||||||
|
|
||||||
|
local math = math
|
||||||
|
|
||||||
-- License of code, textures & sounds: CC0
|
-- License of code, textures & sounds: CC0
|
||||||
|
|
||||||
local math_random = math.random
|
local function register_drop(liquid, glow, sound, nodes)
|
||||||
|
minetest.register_entity("mcl_dripping:drop_" .. liquid, {
|
||||||
local all_dirs = {
|
|
||||||
{x = 0, y = 0, z = 1},
|
|
||||||
{x = 0, y = 1, z = 0},
|
|
||||||
{x = 1, y = 0, z = 0},
|
|
||||||
{x = 0, y = 0, z =-1},
|
|
||||||
{x = 0, y =-1, z = 0},
|
|
||||||
{x =-1, y = 0, z = 0},
|
|
||||||
}
|
|
||||||
|
|
||||||
local function register_drop_entity(substance, glow, sound, texture_file_name)
|
|
||||||
minetest.register_entity("mcl_dripping:drop_" .. substance, {
|
|
||||||
hp_max = 1,
|
hp_max = 1,
|
||||||
physical = true,
|
physical = true,
|
||||||
collide_with_objects = false,
|
collide_with_objects = false,
|
||||||
collisionbox = {-0.01, -0.01, -0.01, 0.01, 0.01, 0.01},
|
collisionbox = {-0.01, 0.01, -0.01, 0.01, 0.01, 0.01},
|
||||||
glow = glow,
|
glow = glow,
|
||||||
pointable = false,
|
pointable = false,
|
||||||
visual = "sprite",
|
visual = "sprite",
|
||||||
|
@ -30,18 +22,11 @@ local function register_drop_entity(substance, glow, sound, texture_file_name)
|
||||||
_dropped = false,
|
_dropped = false,
|
||||||
on_activate = function(self)
|
on_activate = function(self)
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
textures = {
|
textures = {"[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"}
|
||||||
"[combine:2x2:"
|
|
||||||
.. -math_random(1, 16)
|
|
||||||
.. ","
|
|
||||||
.. -math_random(1, 16)
|
|
||||||
.. "="
|
|
||||||
.. (texture_file_name or ("default_" .. substance .. "_source_animated.png"))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime)
|
||||||
local k = math_random(1, 222)
|
local k = math.random(1, 222)
|
||||||
local ownpos = self.object:get_pos()
|
local ownpos = self.object:get_pos()
|
||||||
if k == 1 then
|
if k == 1 then
|
||||||
self.object:set_acceleration(vector.new(0, -5, 0))
|
self.object:set_acceleration(vector.new(0, -5, 0))
|
||||||
|
@ -53,20 +38,14 @@ local function register_drop_entity(substance, glow, sound, texture_file_name)
|
||||||
local ent = self.object:get_luaentity()
|
local ent = self.object:get_luaentity()
|
||||||
if not ent._dropped then
|
if not ent._dropped then
|
||||||
ent._dropped = true
|
ent._dropped = true
|
||||||
if sound then
|
|
||||||
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = ownpos, gain = 0.5, max_hear_distance = 8}, true)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
if k < 3 then
|
if k < 3 then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
|
||||||
|
|
||||||
local function register_liquid_drop(liquid, glow, sound, nodes)
|
|
||||||
register_drop_entity(liquid, glow, sound)
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Create drops",
|
label = "Create drops",
|
||||||
nodenames = nodes,
|
nodenames = nodes,
|
||||||
|
@ -76,31 +55,12 @@ local function register_liquid_drop(liquid, glow, sound, nodes)
|
||||||
action = function(pos)
|
action = function(pos)
|
||||||
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
|
if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1, 0)).name, liquid) ~= 0
|
||||||
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
|
and minetest.get_node(vector.offset(pos, 0, -1, 0)).name == "air" then
|
||||||
local x, z = math_random(-45, 45) / 100, math_random(-45, 45) / 100
|
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||||
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
|
minetest.add_entity(vector.offset(pos, x, -0.520, z), "mcl_dripping:drop_" .. liquid)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
register_liquid_drop("water", 1, "", {"group:opaque", "group:leaves"})
|
register_drop("water", 1, "", {"group:opaque", "group:leaves"})
|
||||||
register_liquid_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})
|
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"})
|
||||||
|
|
||||||
register_drop_entity("crying_obsidian", 10, nil, "mcl_core_crying_obsidian.png")
|
|
||||||
minetest.register_abm({
|
|
||||||
label = "Create crying obsidian drops",
|
|
||||||
nodenames = {"mcl_core:crying_obsidian"},
|
|
||||||
neighbors = {"air"},
|
|
||||||
interval = 2,
|
|
||||||
chance = 22,
|
|
||||||
action = function(pos)
|
|
||||||
local i0 = math_random(1, 6)
|
|
||||||
for i = i0, i0 + 5 do
|
|
||||||
local dir = all_dirs[(i % 6) + 1]
|
|
||||||
if minetest.get_node(vector.add(pos, dir)).name == "air" then
|
|
||||||
minetest.add_entity(vector.offset(pos, dir.x * 0.52, dir.y * 0.52, dir.z * 0.52), "mcl_dripping:drop_crying_obsidian")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
|
@ -2,7 +2,6 @@ Dripping Mod
|
||||||
by kddekadenz
|
by kddekadenz
|
||||||
|
|
||||||
modified for MineClone 2 by Wuzzy and NO11
|
modified for MineClone 2 by Wuzzy and NO11
|
||||||
modified for MineClone 5 by kay27
|
|
||||||
|
|
||||||
|
|
||||||
Installing instructions:
|
Installing instructions:
|
||||||
|
|
|
@ -89,7 +89,6 @@ minetest.register_entity(":__builtin:falling_node", {
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
if not self then return end
|
|
||||||
local meta = self.meta
|
local meta = self.meta
|
||||||
-- Workaround: Save inventory seperately from metadata.
|
-- Workaround: Save inventory seperately from metadata.
|
||||||
-- Because Minetest crashes when a node with inventory gets deactivated
|
-- Because Minetest crashes when a node with inventory gets deactivated
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 a été écrasé par une enclume.
|
|
||||||
@1 was smashed by a falling block.=@1 a été écrasé par un bloc.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 fuguèt espotit per una enclutge.
|
|
||||||
@1 was smashed by a falling block.=@1 fuguèt espotit per un blòc.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 был(а) раздавлен упавшей наковальней.
|
|
||||||
@1 was smashed by a falling block.=@1 был(а) раздавлен упавшим блоком.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=@1 被落下的铁砧砸碎了.
|
|
||||||
@1 was smashed by a falling block.=@1 被落下的方块砸碎了.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# textdomain: mcl_falling_nodes
|
|
||||||
@1 was smashed by a falling anvil.=
|
|
||||||
@1 was smashed by a falling block.=
|
|
|
@ -6,8 +6,9 @@ local pool = {}
|
||||||
|
|
||||||
local tick = false
|
local tick = false
|
||||||
|
|
||||||
minetest.register_on_authplayer(function(name, ip, is_success)
|
minetest.register_on_joinplayer(function(player)
|
||||||
if not is_success then return end
|
local name
|
||||||
|
name = player:get_player_name()
|
||||||
pool[name] = 0
|
pool[name] = 0
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -42,8 +43,6 @@ item_drop_settings.drop_single_item = false --if true, the drop control dro
|
||||||
|
|
||||||
item_drop_settings.magnet_time = 0.75 -- how many seconds an item follows the player before giving up
|
item_drop_settings.magnet_time = 0.75 -- how many seconds an item follows the player before giving up
|
||||||
|
|
||||||
local is_player = mcl_util.is_player
|
|
||||||
|
|
||||||
local function get_gravity()
|
local function get_gravity()
|
||||||
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
||||||
end
|
end
|
||||||
|
@ -65,8 +64,6 @@ mcl_item_entity.register_pickup_achievement("tree", "mcl:mineWood")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_core:crying_obsidian", "mcl:whosCuttingOnions")
|
|
||||||
mcl_item_entity.register_pickup_achievement("mcl_nether:ancient_debris", "mcl:hiddenInTheDepths")
|
|
||||||
|
|
||||||
local function check_pickup_achievements(object, player)
|
local function check_pickup_achievements(object, player)
|
||||||
if has_awards then
|
if has_awards then
|
||||||
|
@ -136,7 +133,7 @@ minetest.register_globalstep(function(dtime)
|
||||||
|
|
||||||
--magnet and collection
|
--magnet and collection
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
|
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
|
||||||
if not is_player(object) and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
|
if not object:is_player() and vector.distance(checkpos, object:get_pos()) < item_drop_settings.radius_magnet and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" and object:get_luaentity()._magnet_timer and (object:get_luaentity()._insta_collect or (object:get_luaentity().age > item_drop_settings.age)) then
|
||||||
|
|
||||||
if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
if object:get_luaentity()._magnet_timer >= 0 and object:get_luaentity()._magnet_timer < item_drop_settings.magnet_time and inv and inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
||||||
|
|
||||||
|
@ -156,10 +153,6 @@ minetest.register_globalstep(function(dtime)
|
||||||
object:set_velocity({x=0,y=0,z=0})
|
object:set_velocity({x=0,y=0,z=0})
|
||||||
object:set_acceleration({x=0,y=0,z=0})
|
object:set_acceleration({x=0,y=0,z=0})
|
||||||
|
|
||||||
if object._flowing then
|
|
||||||
object._flowing = false
|
|
||||||
end
|
|
||||||
|
|
||||||
object:move_to(checkpos)
|
object:move_to(checkpos)
|
||||||
|
|
||||||
pool[name] = pool[name] + 1
|
pool[name] = pool[name] + 1
|
||||||
|
@ -174,7 +167,7 @@ minetest.register_globalstep(function(dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif not is_player(object) and object:get_luaentity() and object:get_luaentity().name == "mcl_experience:orb" then
|
elseif not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "mcl_experience:orb" then
|
||||||
local entity = object:get_luaentity()
|
local entity = object:get_luaentity()
|
||||||
entity.collector = player:get_player_name()
|
entity.collector = player:get_player_name()
|
||||||
entity.collected = true
|
entity.collected = true
|
||||||
|
@ -237,7 +230,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
-- This means there is no digger. This is a special case which allows this function to be called
|
-- This means there is no digger. This is a special case which allows this function to be called
|
||||||
-- by hand. Creative Mode is intentionally ignored in this case.
|
-- by hand. Creative Mode is intentionally ignored in this case.
|
||||||
|
|
||||||
if (digger and is_player(digger) and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
|
if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -347,7 +340,7 @@ end
|
||||||
|
|
||||||
-- Drop single items by default
|
-- Drop single items by default
|
||||||
function minetest.item_drop(itemstack, dropper, pos)
|
function minetest.item_drop(itemstack, dropper, pos)
|
||||||
if dropper and is_player(dropper) then
|
if dropper and dropper:is_player() then
|
||||||
local v = dropper:get_look_dir()
|
local v = dropper:get_look_dir()
|
||||||
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
|
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
|
||||||
local cs = itemstack:get_count()
|
local cs = itemstack:get_count()
|
||||||
|
@ -492,7 +485,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
if not self then return end
|
|
||||||
local data = minetest.serialize({
|
local data = minetest.serialize({
|
||||||
itemstring = self.itemstring,
|
itemstring = self.itemstring,
|
||||||
always_collect = self.always_collect,
|
always_collect = self.always_collect,
|
||||||
|
@ -799,9 +791,6 @@ minetest.register_entity(":__builtin:item", {
|
||||||
|
|
||||||
local oldvel = self.object:get_velocity() -- v is vector, vel is velocity
|
local oldvel = self.object:get_velocity() -- v is vector, vel is velocity
|
||||||
|
|
||||||
-- apply gravity *before* drag computations
|
|
||||||
oldvel.y = oldvel.y - get_gravity() * dtime
|
|
||||||
|
|
||||||
-- drag
|
-- drag
|
||||||
local fluid_drag = item_drop_settings.fluid_drag
|
local fluid_drag = item_drop_settings.fluid_drag
|
||||||
|
|
||||||
|
@ -816,6 +805,12 @@ minetest.register_entity(":__builtin:item", {
|
||||||
newv.y = newv.y - (oldvel.y - newv.y) * fluid_drag * dtime
|
newv.y = newv.y - (oldvel.y - newv.y) * fluid_drag * dtime
|
||||||
newv.z = newv.z - (oldvel.z - newv.z) * fluid_drag * dtime
|
newv.z = newv.z - (oldvel.z - newv.z) * fluid_drag * dtime
|
||||||
|
|
||||||
|
newv.y = newv.y + -0.22 -- (keep slight downward thrust from previous version of code)
|
||||||
|
-- NOTE: is there any particular reason we have this, anyway?
|
||||||
|
-- since fluid drag is now on, we could as well just
|
||||||
|
-- apply gravity here; drag will slow down the fall
|
||||||
|
-- realistically
|
||||||
|
|
||||||
self.object:set_velocity({x = oldvel.x + newv.x * dtime, y = oldvel.y + newv.y * dtime, z = oldvel.z + newv.z * dtime})
|
self.object:set_velocity({x = oldvel.x + newv.x * dtime, y = oldvel.y + newv.y * dtime, z = oldvel.z + newv.z * dtime})
|
||||||
|
|
||||||
self.physical_state = true
|
self.physical_state = true
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name = mcl_item_entity
|
name = mcl_item_entity
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Dropped items will be attracted to the player like a magnet.
|
description = Dropped items will be attracted to the player like a magnet.
|
||||||
depends = flowlib, mcl_enchanting, mcl_util
|
depends = flowlib, mcl_enchanting
|
||||||
|
|
|
@ -503,7 +503,6 @@ local function register_entity(entity_id, mesh, textures, drop, on_rightclick, o
|
||||||
end
|
end
|
||||||
|
|
||||||
function cart:get_staticdata()
|
function cart:get_staticdata()
|
||||||
if not self then return end
|
|
||||||
return minetest.serialize({_railtype = self._railtype})
|
return minetest.serialize({_railtype = self._railtype})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,32 +5,32 @@ Minecarts only ride on rails and always follow the tracks. At a T-junction with
|
||||||
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=Vous pouvez placer le wagonnet sur des rails. Faites un clic droit dessus pour entrer dedans. Frappez-le pour le faire bouger.
|
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=Vous pouvez placer le wagonnet sur des rails. Faites un clic droit dessus pour entrer dedans. Frappez-le pour le faire bouger.
|
||||||
To obtain the minecart, punch it while holding down the sneak key.=Pour obtenir la wagonnet, frappez-le tout en maintenant la touche furtive enfoncée.
|
To obtain the minecart, punch it while holding down the sneak key.=Pour obtenir la wagonnet, frappez-le tout en maintenant la touche furtive enfoncée.
|
||||||
A minecart with TNT is an explosive vehicle that travels on rail.=Un wagonnet avec de la TNT est un véhicule explosif qui se déplace sur rail.
|
A minecart with TNT is an explosive vehicle that travels on rail.=Un wagonnet avec de la TNT est un véhicule explosif qui se déplace sur rail.
|
||||||
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=Placez-le sur des rails. Frappez-le pour le déplacer. La TNT est allumée avec un briquet ou lorsque le minecart est sur un rail d'activation alimenté.
|
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=Placez-le sur des rails. Frappez-le pour le déplacer. Le TNT est allumé avec un briquet ou lorsque le minecart est sur un rail d'activation alimenté.
|
||||||
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=Pour obtenir le wagonnet avec la TNT, frappez-les tout en maintenant la touche furtive [Sneak] enfoncée. Vous ne pouvez pas faire cela si la TNT a été allumée.
|
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=Pour obtenir la wagonnet et la TNT, frappez-les tout en maintenant la touche furtive enfoncée. Vous ne pouvez pas faire cela si le TNT a été allumé.
|
||||||
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=Un wagonnet avec un four est un véhicule qui se déplace sur rails. Il peut se propulser avec du carburant.
|
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=Une wagonnet avec un four est un véhicule qui se déplace sur rails. Il peut se propulser avec du carburant.
|
||||||
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=Placez-le sur des rails. Si vous lui donnez du charbon, le four commencera à brûler pour longtemps et le wagonnet pourra se déplacer. Frappez-le pour le faire bouger.
|
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=Placez-le sur des rails. Si vous lui donnez du charbon, le four commencera à brûler pendant longtemps et le wagonnet pourra se déplacer. Frappez-le pour le faire bouger.
|
||||||
To obtain the minecart and furnace, punch them while holding down the sneak key.=Pour obtenir le wagonnet avec le four, frappez-les tout en maintenant la touche furtive [Sneak] enfoncée.
|
To obtain the minecart and furnace, punch them while holding down the sneak key.=Pour obtenir le wagonnet et le four, frappez-les tout en maintenant la touche furtive enfoncée.
|
||||||
Minecart with Chest=Wagonnet avec Coffre
|
Minecart with Chest=Wagonnet avec Coffre
|
||||||
Minecart with Furnace=Wagonnet avec Four
|
Minecart with Furnace=Wagonnet avec Four
|
||||||
Minecart with Command Block=Wagonnet avec Bloc de Commande
|
Minecart with Command Block=Wagonnet avec Bloc de Commande
|
||||||
Minecart with Hopper=Wagonnet avec Entonnoir
|
Minecart with Hopper=Wagonnet avec Entonoir
|
||||||
Minecart with TNT=Wagonnet avec TNT
|
Minecart with TNT=Wagonnet avec TNT
|
||||||
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=Placez-les sur le sol pour construire votre chemin de fer, les rails se connecteront automatiquement les uns aux autres et se transformeront en courbes, en jonctions en T, en traversées et en pentes au besoin.
|
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=Placez-les sur le sol pour construire votre chemin de fer, les rails se connecteront automatiquement les uns aux autres et se transformeront en courbes, en jonctions en T, en traversées et en pentes au besoin.
|
||||||
Rail=Rail
|
Rail=Rail
|
||||||
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Les rails ralentissent légèrement les wagonnets en raison de la friction.
|
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Les rails ralentissent légèrement les wagonnets en raison de la friction.
|
||||||
Powered Rail=Rail alimenté
|
Powered Rail=Rail allimenté
|
||||||
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Les rails motorisés sont capables d'accélérer et de freiner les wagonnets.
|
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Les rails motorisés sont capables d'accélérer et de freiner les wagonnets.
|
||||||
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=Sans énergie de redstone, le rail freinera les wagonnets. Pour que ce rail accélère les minecarts, alimentez-le avec une source d'énergie redstone.
|
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=Sans énergie de redstone, le rail freinera les wagonnets. Pour que ce rail accélère les minecarts, alimentez-le avec une source d'énergie redstone.
|
||||||
Activator Rail=Rail d'activation
|
Activator Rail=Rail d'activation
|
||||||
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Les rails activateurs sont utilisés pour activer des wagonnets spéciaux.
|
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Des rails activateurs sont utilisés pour activer des wagonnets spéciaux.
|
||||||
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=Pour activer ce rail, activez le wagonnet, alimentez-le avec de l'énergie redstone et envoyez un wagonnet sur ce morceau de rail.
|
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=Pour activer ce rail, activez les wagonnets, alimentez-le avec de l'énergie redstone et envoyez un wagonnet sur ce morceau de rail.
|
||||||
Detector Rail=Rail de détection
|
Detector Rail=Rail de détection
|
||||||
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Un rail de détection est capable de détecter un wagonnet au-dessus et alimente les mécanismes de redstone.
|
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=Les rails peuvent être utilisés pour construire des voies de transport pour les wagonnets. Un rail de détection est capable de détecter un wagonnet au-dessus et alimente les mécanismes de redstone.
|
||||||
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=Pour détecter un wagonnet et fournir une alimentation redstone, connectez-le aux câbles redstone ou aux mécanismes redstone et envoyez n'importe quel wagonnet sur le rail.
|
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=Pour détecter un wagonnet et fournir une alimentation redstone, connectez-le aux câble redstone ou aux mécanismes redstone et envoyez n'importe quel wagonnet sur le rail.
|
||||||
Track for minecarts=Piste pour wagonnets
|
Track for minecarts=Piste pour wagonnets
|
||||||
Speed up when powered, slow down when not powered=Accélérez lorsqu'il est alimenté, ralentissez lorsqu'il n'est pas alimenté
|
Speed up when powered, slow down when not powered=Accélérez lorsqu'il est alimenté, ralentissez lorsqu'il n'est pas alimenté
|
||||||
Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
||||||
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
||||||
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
||||||
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
||||||
Sneak to dismount=S'accroupir [Sneak] pour descendre
|
Sneak to dismount=
|
|
@ -1,36 +1,36 @@
|
||||||
# textdomain: mcl_minecarts
|
# textdomain: mcl_minecarts
|
||||||
Minecart=Вагонетка
|
Minecart=Вагонетка
|
||||||
Minecarts can be used for a quick transportion on rails.=Вагонетка может быть использована для быстрого перемещения по рельсам.
|
Minecarts can be used for a quick transportion on rails.=Вагонетки нужны, чтобы быстро перемещаться по рельсам.
|
||||||
Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type.=Вагонетки едут только по проложенным рельсам. На Т-образной развилке они поворачивают налево. Скорость зависит от типа рельсов.
|
Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type.=Вагонетки едут строго по проложенному железнодорожному пути. На Т-образной развилке они поворачивают налево. Скорость зависит от типа рельсов.
|
||||||
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=Вы можете поставить вагонетку на рельсы. Правым кликом сядьте в неё. Бейте по вагонетке, чтобы она ехала.
|
You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving.=Вы ставите вагонетку на рельсы. Правым кликом садитесь в неё. Стукаете, чтобы начать движение.
|
||||||
To obtain the minecart, punch it while holding down the sneak key.=Чтобы забрать вагонетку, ударьте по ней, удерживая клавишу [Красться].
|
To obtain the minecart, punch it while holding down the sneak key.=Чтобы взять вагонетку, стукните её, удерживая клавишу [Красться].
|
||||||
A minecart with TNT is an explosive vehicle that travels on rail.=Вагонетка с ТНТ это взрывающийся железнодорожный транспорт.
|
A minecart with TNT is an explosive vehicle that travels on rail.=Вагон тротила это подрывной железнодорожный транспорт.
|
||||||
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=Поместите вагонетку на рельсы. Ударьте по ней, чтобы она поехала. ТНТ активируется, если его поджечь огнивом или когда вагонетка проедет через подключенные активирующие рельсы.
|
Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail.=Поместите его на рельсы. Стукните, чтобы он поехал. Тротил воспламеняется, если его поджечь огнивом, либо при попадании на подключенный рельсовый активатор.
|
||||||
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=Чтобы забрать вагонетку с ТНТ, ударьте по ней, удерживая клавишу [Красться]. Если ТНТ подожжён, сделать это нельзя.
|
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=Чтобы взять вагон тротила, стукните его, удерживая клавишу [Красться]. Если тротил воспламенён, сделать это нельзя.
|
||||||
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=Вагонетка с печью - это железнодорожный транспорт. Она может ехать сама за счёт топлива.
|
A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel.=Вагон с печью - это железнодорожный транспорт. Он может двигаться за счёт топлива.
|
||||||
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=Поставьте вагонетку на рельсы. Если добавить в неё угля, то печь зажжётся на продолжительное время и вагонетка сможет поехать сама. Ударьте по ней для начала движения.
|
Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving.=Поставьте его на рельсы. Если добавить немного угля, то печь зажжётся на продолжительное время и вагон сможет ехать. Стукните вагон для начала движения.
|
||||||
To obtain the minecart and furnace, punch them while holding down the sneak key.=Чтобы забрать вагонетку с печью, ударьте по ней, удерживая клавишу [Красться].
|
To obtain the minecart and furnace, punch them while holding down the sneak key.=Чтобы взять вагон с печью, стукните его, удерживая клавишу [Красться].
|
||||||
Minecart with Chest=Вагонетка с сундуком
|
Minecart with Chest=Вагон с сундуком
|
||||||
Minecart with Furnace=Вагонетка с печью
|
Minecart with Furnace=Вагон с печью
|
||||||
Minecart with Command Block=Вагонетка с командным блоком
|
Minecart with Command Block=Вагон с командным блоком
|
||||||
Minecart with Hopper=Вагонетка с воронкой
|
Minecart with Hopper=Вагон с бункером
|
||||||
Minecart with TNT=Вагонетка с ТНТ
|
Minecart with TNT=Вагон тротила
|
||||||
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=Поместите рельсы на землю, чтобы сделать железную дорогу, рельсы автоматически соединятся между собой и будут образовывать повороты, T-образные развилки, перекрёстки и склоны там, где это потребуется.
|
Place them on the ground to build your railway, the rails will automatically connect to each other and will turn into curves, T-junctions, crossings and slopes as needed.=Поместите на землю, чтобы сделать железную дорогу, рельсы автоматически соединятся между собой и будут превращаться в плавный повороты, T-образные развилки, перекрёстки и уклоны там, где это потребуется.
|
||||||
Rail=Рельсы
|
Rail=Рельсы
|
||||||
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=Рельсы используются для строительства железной дороги. Обычные рельсы немного замедляют движение вагонеток из-за трения.
|
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=Рельсы используются для строительства железной дороги. Обычные рельсы немного замедляют движение вагонеток из-за трения.
|
||||||
Powered Rail=Энергорельсы
|
Powered Rail=Подключаемые рельсы
|
||||||
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=Энергорельсы используются для строительства железной дороги. Энергорельсы могут ускорять и тормозить вагонетки.
|
Rails can be used to build transport tracks for minecarts. Powered rails are able to accelerate and brake minecarts.=Рельсы используются для строительства железной дороги. Подключённые рельсы могут разгонять и тормозить вагонетки.
|
||||||
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=Неподключенные энергорельсы замедляют вагонетки. Чтобы энергорельсы ускоряли вагонетки, проведите к ним сигнал редстоуна.
|
Without redstone power, the rail will brake minecarts. To make this rail accelerate minecarts, power it with redstone power.=Без энергии редстоуна рельсы будут тормозить вагонетки.
|
||||||
Activator Rail=Активирующие рельсы
|
Activator Rail=Рельсовый активатор
|
||||||
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=Активирующие рельсы используются для строительства железной дороги. Активирующие рельсы активируют некоторые особые вагонетки.
|
Rails can be used to build transport tracks for minecarts. Activator rails are used to activate special minecarts.=Рельсы используются для строительства железной дороги. Рельсовый активатор активирует особые вагонетки.
|
||||||
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=Чтобы эти рельсы активировали вагонетки, подключите активирующие рельсы к сигналу редстоуна и направьте вагонетку через них.
|
To make this rail activate minecarts, power it with redstone power and send a minecart over this piece of rail.=Чтобы этот блок рельсов активировал вагонетку, подключите его к энергии редстоуна и направьте вагонетку через него.
|
||||||
Detector Rail=Нажимные рельсы
|
Detector Rail=Рельсовый детектор
|
||||||
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=Нажимные рельсы используются для строительства железной дороги. Нажимные рельсы реагируют на проезжающие по ним вагонетки и выдают сигнал для механизмов из редстоуна.
|
Rails can be used to build transport tracks for minecarts. A detector rail is able to detect a minecart above it and powers redstone mechanisms.=Рельсы используются для строительства железной дороги. Рельсовый детектор может обнаруживать вагонетку у себя наверху и подключать механизмы редстоуна.
|
||||||
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=Подсоедините к нажимным рельсам провод редстоуна или редстоуновые механизмы, чтобы активировать их когда по рельсам проезжает вагонетка.
|
To detect a minecart and provide redstone power, connect it to redstone trails or redstone mechanisms and send any minecart over the rail.=Чтобы обнаруживать вагонетку и подавать энергию редстоуна, подключите его к дорожке редстоуна или механизму редстоуна, после чего направьте любую вагонетку через него.
|
||||||
Track for minecarts=Железная дорога
|
Track for minecarts=Железная дорога
|
||||||
Speed up when powered, slow down when not powered=Разгоняет, если подключёны, тормозит, если не подключёны
|
Speed up when powered, slow down when not powered=Разгоняет, если подключён, тормозит, если не подключён
|
||||||
Activates minecarts when powered=Активирует особые вагонетки, если подключёны
|
Activates minecarts when powered=Активирует особые вагонетки, если подключён
|
||||||
Emits redstone power when a minecart is detected=Подает сигнал редстоуна при обнаружении вагонетки
|
Emits redstone power when a minecart is detected=Испускает энергию редстоуна при обнаружении вагонетки
|
||||||
Vehicle for fast travel on rails=Быстрый железнодорожный транспорт
|
Vehicle for fast travel on rails=Быстрый железнодорожный транспорт
|
||||||
Can be ignited by tools or powered activator rail=Можно поджечь с помощью инструмента или активирующими рельсами
|
Can be ignited by tools or powered activator rail=Можно воспламенить с помощью инструмента или подключенного рельсового активатора
|
||||||
Sneak to dismount=Нажмите [Красться] для высадки
|
Sneak to dismount=Нажмите [Красться] для высадки
|
||||||
|
|
|
@ -227,11 +227,6 @@ functions needed for the mob to work properly which contains the following:
|
||||||
older mobs.
|
older mobs.
|
||||||
'pushable' Allows players, & other mobs to push the mob.
|
'pushable' Allows players, & other mobs to push the mob.
|
||||||
|
|
||||||
'spawn_with_armor' If set to true, the mob has a small chance of spawning with
|
|
||||||
random matched armor. If set to a string, the string represents
|
|
||||||
the material type of the armor. Any materials used by
|
|
||||||
mcl_armor will work. Example: "gold"
|
|
||||||
It is assumed that the first texture is for armor.
|
|
||||||
|
|
||||||
|
|
||||||
MineClone 2 extensions:
|
MineClone 2 extensions:
|
||||||
|
|
|
@ -375,7 +375,6 @@ function mobs:register_mob(name, def)
|
||||||
--moves the wrong way
|
--moves the wrong way
|
||||||
swap_y_with_x = def.swap_y_with_x or false,
|
swap_y_with_x = def.swap_y_with_x or false,
|
||||||
reverse_head_yaw = def.reverse_head_yaw or false,
|
reverse_head_yaw = def.reverse_head_yaw or false,
|
||||||
_spawn_with_armor = def.spawn_with_armor,
|
|
||||||
|
|
||||||
--END HEAD CODE VARIABLES
|
--END HEAD CODE VARIABLES
|
||||||
|
|
||||||
|
@ -402,7 +401,6 @@ function mobs:register_mob(name, def)
|
||||||
ignited_by_sunlight = def.ignited_by_sunlight or false,
|
ignited_by_sunlight = def.ignited_by_sunlight or false,
|
||||||
eye_height = def.eye_height or 1.5,
|
eye_height = def.eye_height or 1.5,
|
||||||
defuse_reach = def.defuse_reach or 4,
|
defuse_reach = def.defuse_reach or 4,
|
||||||
spawn = def.spawn,
|
|
||||||
-- End of MCL2 extensions
|
-- End of MCL2 extensions
|
||||||
|
|
||||||
on_spawn = def.on_spawn,
|
on_spawn = def.on_spawn,
|
||||||
|
@ -417,7 +415,7 @@ function mobs:register_mob(name, def)
|
||||||
|
|
||||||
--on_breed = def.on_breed,
|
--on_breed = def.on_breed,
|
||||||
|
|
||||||
on_grown = def.on_grown,
|
--on_grown = def.on_grown,
|
||||||
|
|
||||||
--on_detach_child = mob_detach_child,
|
--on_detach_child = mob_detach_child,
|
||||||
|
|
||||||
|
@ -427,9 +425,7 @@ function mobs:register_mob(name, def)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
if self and mobs then
|
|
||||||
return mobs.mob_staticdata(self)
|
return mobs.mob_staticdata(self)
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--harmed_by_heal = def.harmed_by_heal,
|
--harmed_by_heal = def.harmed_by_heal,
|
||||||
|
|
|
@ -88,7 +88,7 @@ local function land_state_switch(self, dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
--ignore everything else if following
|
--ignore everything else if following
|
||||||
if mobs.check_following(self, dtime) and
|
if mobs.check_following(self) and
|
||||||
(not self.breed_lookout_timer or (self.breed_lookout_timer and self.breed_lookout_timer == 0)) and
|
(not self.breed_lookout_timer or (self.breed_lookout_timer and self.breed_lookout_timer == 0)) and
|
||||||
(not self.breed_timer or (self.breed_timer and self.breed_timer == 0)) then
|
(not self.breed_timer or (self.breed_timer and self.breed_timer == 0)) then
|
||||||
self.state = "follow"
|
self.state = "follow"
|
||||||
|
@ -801,6 +801,20 @@ function mobs.mob_step(self, dtime)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--DEBUG TIME!
|
||||||
|
--REMEMBER TO MOVE THIS AFTER DEATH CHECK
|
||||||
|
|
||||||
|
--if self.has_head then
|
||||||
|
-- mobs.do_head_logic(self,dtime)
|
||||||
|
--end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--if true then--DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
|
||||||
|
-- return
|
||||||
|
--end
|
||||||
|
|
||||||
--despawn mechanism
|
--despawn mechanism
|
||||||
--don't despawned tamed or bred mobs
|
--don't despawned tamed or bred mobs
|
||||||
if not self.tamed and not self.bred then
|
if not self.tamed and not self.bred then
|
||||||
|
@ -879,40 +893,14 @@ function mobs.mob_step(self, dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--water damage
|
||||||
|
if self.water_damage and self.water_damage ~= 0 then
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local node = minetest_get_node(pos).name
|
local node = minetest_get_node(pos).name
|
||||||
|
if minetest_get_item_group(node, "water") ~= 0 then
|
||||||
--water damage
|
|
||||||
if self.water_damage and self.water_damage ~= 0 and minetest_get_item_group(node, "water") ~= 0 then
|
|
||||||
self.water_counter = (self.water_counter or 0) + dtime
|
|
||||||
if self.water_counter >= 1 then
|
|
||||||
mobs.smoke_effect(self)
|
mobs.smoke_effect(self)
|
||||||
self.health = self.health - self.water_damage
|
self.health = self.health - self.water_damage
|
||||||
self:teleport()
|
self:teleport()
|
||||||
self.water_counter = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--lava damage
|
|
||||||
local lava_damage = self.lava_damage
|
|
||||||
if lava_damage and lava_damage ~= 0 and minetest_get_item_group(node, "lava") ~= 0 then
|
|
||||||
self.lava_counter = (self.lava_counter or 0) + dtime
|
|
||||||
if self.lava_counter >= 1 then
|
|
||||||
minetest.sound_play("default_punch", {
|
|
||||||
object = self.object,
|
|
||||||
max_hear_distance = 5
|
|
||||||
}, true)
|
|
||||||
--[[ if not mcl_burning.is_burning(self.object) then
|
|
||||||
mcl_burning.set_on_fire(self.object, 1.1)
|
|
||||||
else
|
|
||||||
]] self.object:punch(self.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
|
||||||
damage_groups = {fleshy = self.lava_damage}
|
|
||||||
}, nil)
|
|
||||||
-- end
|
|
||||||
self.lava_counter = 0
|
|
||||||
self.health = self.health - lava_damage
|
|
||||||
self:teleport()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -984,7 +972,7 @@ function mobs.mob_step(self, dtime)
|
||||||
|
|
||||||
--go get the closest player
|
--go get the closest player
|
||||||
if attacking then
|
if attacking then
|
||||||
mobs.do_head_logic(self, dtime, attacking)
|
|
||||||
self.memory = 6 --6 seconds of memory
|
self.memory = 6 --6 seconds of memory
|
||||||
|
|
||||||
--set initial punch timer
|
--set initial punch timer
|
||||||
|
@ -1040,7 +1028,6 @@ function mobs.mob_step(self, dtime)
|
||||||
--don't break eye contact
|
--don't break eye contact
|
||||||
if self.hostile and self.attacking then
|
if self.hostile and self.attacking then
|
||||||
mobs.set_yaw_while_attacking(self)
|
mobs.set_yaw_while_attacking(self)
|
||||||
mobs.do_head_logic(self, dtime, self.attacking)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--perfectly reset pause_timer
|
--perfectly reset pause_timer
|
||||||
|
|
|
@ -26,8 +26,6 @@ local math_random = math.random
|
||||||
|_|
|
|_|
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
local minetest_line_of_sight = minetest.line_of_sight
|
|
||||||
|
|
||||||
mobs.explode_attack_walk = function(self,dtime)
|
mobs.explode_attack_walk = function(self,dtime)
|
||||||
|
|
||||||
--this needs an exception
|
--this needs an exception
|
||||||
|
@ -38,27 +36,17 @@ mobs.explode_attack_walk = function(self,dtime)
|
||||||
|
|
||||||
mobs.set_yaw_while_attacking(self)
|
mobs.set_yaw_while_attacking(self)
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
local distance_from_attacking = vector_distance(self.object:get_pos(), self.attacking:get_pos())
|
||||||
local attack_pos = self.attacking:get_pos()
|
|
||||||
local distance_from_attacking = vector_distance(pos, attack_pos)
|
|
||||||
|
|
||||||
--make mob walk up to player within 2 nodes distance then start exploding
|
--make mob walk up to player within 2 nodes distance then start exploding
|
||||||
if (
|
if distance_from_attacking >= self.reach and
|
||||||
distance_from_attacking >= self.reach and
|
|
||||||
|
|
||||||
--don't allow explosion to cancel unless out of the reach boundary
|
--don't allow explosion to cancel unless out of the reach boundary
|
||||||
not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) or
|
not (self.explosion_animation and self.explosion_animation > 0 and distance_from_attacking <= self.defuse_reach) then
|
||||||
--don't allow creeper to finish exploding animation if can't see target
|
|
||||||
not minetest_line_of_sight(
|
|
||||||
{x = pos.x, y = pos.y + self.eye_height, z = pos.z},
|
|
||||||
{x = attack_pos.x, y = attack_pos.y + (self.attacking.eye_height or 0), z = attack_pos.z}
|
|
||||||
)
|
|
||||||
) then
|
|
||||||
mobs.set_velocity(self, self.run_velocity)
|
mobs.set_velocity(self, self.run_velocity)
|
||||||
mobs.set_mob_animation(self,"run")
|
mobs.set_mob_animation(self,"run")
|
||||||
|
|
||||||
mobs.reverse_explosion_animation(self,dtime)
|
mobs.reverse_explosion_animation(self,dtime)
|
||||||
|
|
||||||
else
|
else
|
||||||
mobs.set_velocity(self,0)
|
mobs.set_velocity(self,0)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
local vector = vector
|
local vector = vector
|
||||||
|
|
||||||
--check to see if someone nearby has some tasty food
|
--check to see if someone nearby has some tasty food
|
||||||
mobs.check_following = function(self, dtime) -- returns true or false
|
mobs.check_following = function(self) -- returns true or false
|
||||||
--ignore
|
--ignore
|
||||||
if not self.follow then
|
if not self.follow then
|
||||||
self.following_person = nil
|
self.following_person = nil
|
||||||
|
@ -15,7 +15,6 @@ mobs.check_following = function(self, dtime) -- returns true or false
|
||||||
|
|
||||||
--check if the follower is a player incase they log out
|
--check if the follower is a player incase they log out
|
||||||
if follower and follower:is_player() then
|
if follower and follower:is_player() then
|
||||||
mobs.do_head_logic(self, dtime, follower)
|
|
||||||
local stack = follower:get_wielded_item()
|
local stack = follower:get_wielded_item()
|
||||||
--safety check
|
--safety check
|
||||||
if not stack then
|
if not stack then
|
||||||
|
@ -151,7 +150,6 @@ mobs.baby_grow_up = function(self)
|
||||||
self.collisionbox = self.backup_collisionbox
|
self.collisionbox = self.backup_collisionbox
|
||||||
self.selectionbox = self.backup_selectionbox
|
self.selectionbox = self.backup_selectionbox
|
||||||
self.object:set_properties(self)
|
self.object:set_properties(self)
|
||||||
if self.on_grown then self.on_grown(self) end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--makes the baby grow up faster with diminishing returns
|
--makes the baby grow up faster with diminishing returns
|
||||||
|
|
|
@ -211,6 +211,26 @@ mobs.teleport = function(self, target)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--a function used for despawning mobs
|
||||||
|
mobs.check_for_player_within_area = function(self, radius)
|
||||||
|
local pos1 = self.object:get_pos()
|
||||||
|
if not pos1 then return end
|
||||||
|
--get players in radius
|
||||||
|
for _,player in pairs(minetest_get_connected_players()) do
|
||||||
|
if player and player:get_hp() > 0 then
|
||||||
|
local pos2 = player:get_pos()
|
||||||
|
local distance = vector_distance(pos1,pos2)
|
||||||
|
if distance < radius then
|
||||||
|
--found a player
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--did not find a player
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--a simple helper function for mobs following
|
--a simple helper function for mobs following
|
||||||
mobs.get_2d_distance = function(pos1,pos2)
|
mobs.get_2d_distance = function(pos1,pos2)
|
||||||
pos1.y = 0
|
pos1.y = 0
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
local math = math
|
local math = math
|
||||||
local vector = vector
|
local vector = vector
|
||||||
local debug_head = minetest.settings:get_bool("mcl_debug_head_code", false)
|
|
||||||
|
|
||||||
--converts yaw to degrees
|
--converts yaw to degrees
|
||||||
local degrees = function(yaw)
|
local degrees = function(yaw)
|
||||||
return yaw*180.0/math.pi
|
return yaw*180.0/math.pi
|
||||||
end
|
end
|
||||||
|
|
||||||
mobs.do_head_logic = function(self, dtime, player)
|
mobs.do_head_logic = function(self,dtime)
|
||||||
if not self.has_head == true then return end
|
|
||||||
local player = player or minetest.get_player_by_name("singleplayer")
|
local player = minetest.get_player_by_name("singleplayer")
|
||||||
|
|
||||||
local look_at = player:get_pos()
|
local look_at = player:get_pos()
|
||||||
look_at.y = look_at.y + player:get_properties().eye_height
|
look_at.y = look_at.y + player:get_properties().eye_height
|
||||||
|
@ -26,7 +25,6 @@ mobs.do_head_logic = function(self, dtime, player)
|
||||||
|
|
||||||
pos = vector.add(pos, head_offset)
|
pos = vector.add(pos, head_offset)
|
||||||
|
|
||||||
if debug_head then
|
|
||||||
minetest.add_particle({
|
minetest.add_particle({
|
||||||
pos = pos,
|
pos = pos,
|
||||||
velocity = {x=0, y=0, z=0},
|
velocity = {x=0, y=0, z=0},
|
||||||
|
@ -35,7 +33,7 @@ mobs.do_head_logic = function(self, dtime, player)
|
||||||
size = 1,
|
size = 1,
|
||||||
texture = "default_dirt.png",
|
texture = "default_dirt.png",
|
||||||
})
|
})
|
||||||
end
|
|
||||||
local bone_pos = vector.new(0,0,0)
|
local bone_pos = vector.new(0,0,0)
|
||||||
|
|
||||||
--(horizontal)
|
--(horizontal)
|
||||||
|
@ -91,21 +89,10 @@ mobs.do_head_logic = function(self, dtime, player)
|
||||||
head_pitch = head_pitch + self.head_pitch_modifier
|
head_pitch = head_pitch + self.head_pitch_modifier
|
||||||
end
|
end
|
||||||
|
|
||||||
local head_bone = self.head_bone
|
|
||||||
if (type(head_bone) == "table") then
|
|
||||||
for _, v in pairs(head_bone) do
|
|
||||||
if self.swap_y_with_x then
|
if self.swap_y_with_x then
|
||||||
self.object:set_bone_position(v, bone_pos, vector.new(degrees(head_pitch),degrees(head_yaw),0))
|
self.object:set_bone_position(self.head_bone, bone_pos, vector.new(degrees(head_pitch),degrees(head_yaw),0))
|
||||||
else
|
else
|
||||||
self.object:set_bone_position(v, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
self.object:set_bone_position(self.head_bone, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if self.swap_y_with_x then
|
|
||||||
self.object:set_bone_position(head_bone, bone_pos, vector.new(degrees(head_pitch),degrees(head_yaw),0))
|
|
||||||
else
|
|
||||||
self.object:set_bone_position(head_bone, bone_pos, vector.new(degrees(head_pitch),0,degrees(head_yaw)))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
--set_bone_position([bone, position, rotation])
|
--set_bone_position([bone, position, rotation])
|
||||||
end
|
end
|
|
@ -28,14 +28,12 @@ mobs.shoot_projectile_handling = function(arrow_item, pos, dir, yaw, shooter, po
|
||||||
obj:set_acceleration({x=0, y=gravity, z=0})
|
obj:set_acceleration({x=0, y=gravity, z=0})
|
||||||
obj:set_yaw(yaw-math.pi/2)
|
obj:set_yaw(yaw-math.pi/2)
|
||||||
local le = obj:get_luaentity()
|
local le = obj:get_luaentity()
|
||||||
if le then
|
|
||||||
le._shooter = shooter
|
le._shooter = shooter
|
||||||
le._damage = damage
|
le._damage = damage
|
||||||
le._is_critical = is_critical
|
le._is_critical = is_critical
|
||||||
le._startpos = pos
|
le._startpos = pos
|
||||||
le._knockback = knockback
|
le._knockback = knockback
|
||||||
le._collectable = collectable
|
le._collectable = collectable
|
||||||
end
|
|
||||||
|
|
||||||
--play custom shoot sound
|
--play custom shoot sound
|
||||||
if shooter and shooter.shoot_sound then
|
if shooter and shooter.shoot_sound then
|
||||||
|
|
|
@ -5,32 +5,9 @@ local minetest_settings = minetest.settings
|
||||||
-- CMI support check
|
-- CMI support check
|
||||||
local use_cmi = minetest.global_exists("cmi")
|
local use_cmi = minetest.global_exists("cmi")
|
||||||
|
|
||||||
local vector_distance = vector.distance
|
|
||||||
local minetest_get_connected_players = minetest.get_connected_players
|
|
||||||
local math_random = math.random
|
|
||||||
|
|
||||||
mobs.can_despawn = function(self)
|
mobs.can_despawn = function(self)
|
||||||
if self.tamed or self.bred or self.nametag then return false end
|
return (not self.tamed and not self.bred and not self.nametag and
|
||||||
local mob_pos = self.object:get_pos()
|
not mobs.check_for_player_within_area(self, 64));
|
||||||
if not mob_pos then return true end
|
|
||||||
local players = minetest_get_connected_players()
|
|
||||||
if #players == 0 then return false end
|
|
||||||
-- If no players, probably this is being called from get_staticdata() at server shutdown time
|
|
||||||
-- Minetest is to buggy (as of 5.5) to delete entities at server shutdown time anyway
|
|
||||||
|
|
||||||
local distance = 999
|
|
||||||
for _, player in pairs(players) do
|
|
||||||
if player and player:get_hp() > 0 then
|
|
||||||
local player_pos = player:get_pos()
|
|
||||||
local new_distance = vector_distance(player_pos, mob_pos)
|
|
||||||
if new_distance < distance then
|
|
||||||
distance = new_distance
|
|
||||||
if distance < 33 then return false end
|
|
||||||
if distance < 128 and math_random(1, 42) ~= 11 then return false end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get entity staticdata
|
-- get entity staticdata
|
||||||
|
@ -47,7 +24,7 @@ mobs.mob_staticdata = function(self)
|
||||||
self.following = nil
|
self.following = nil
|
||||||
|
|
||||||
if use_cmi then
|
if use_cmi then
|
||||||
self.serialized_cmi_components = cmi and cmi.serialize_components(self._cmi_components)
|
self.serialized_cmi_components = cmi.serialize_components(self._cmi_components)
|
||||||
end
|
end
|
||||||
|
|
||||||
local tmp = {}
|
local tmp = {}
|
||||||
|
@ -67,102 +44,6 @@ mobs.mob_staticdata = function(self)
|
||||||
return minetest.serialize(tmp)
|
return minetest.serialize(tmp)
|
||||||
end
|
end
|
||||||
|
|
||||||
mobs.armor_setup = function(self)
|
|
||||||
if not self._armor_items then
|
|
||||||
local armor = {}
|
|
||||||
-- Source: https://minecraft.fandom.com/wiki/Zombie
|
|
||||||
local materials = {
|
|
||||||
{name = "leather", chance = 0.3706},
|
|
||||||
{name = "gold", chance = 0.4873},
|
|
||||||
{name = "chain", chance = 0.129},
|
|
||||||
{name = "iron", chance = 0.0127},
|
|
||||||
{name = "diamond", chance = 0.0004}
|
|
||||||
}
|
|
||||||
local types = {
|
|
||||||
{name = "helmet", chance = 0.15},
|
|
||||||
{name = "chestplate", chance = 0.75},
|
|
||||||
{name = "leggings", chance = 0.75},
|
|
||||||
{name = "boots", chance = 0.75}
|
|
||||||
}
|
|
||||||
|
|
||||||
local material
|
|
||||||
if type(self._spawn_with_armor) == "string" then
|
|
||||||
material = self._spawn_with_armor
|
|
||||||
else
|
|
||||||
local chance = 0
|
|
||||||
for i, m in pairs(materials) do
|
|
||||||
chance = chance + m.chance
|
|
||||||
if math.random() <= chance then
|
|
||||||
material = m.name
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i, t in pairs(types) do
|
|
||||||
if math.random() <= t.chance then
|
|
||||||
armor[t.name] = material
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save armor items in lua entity
|
|
||||||
self._armor_items = {}
|
|
||||||
for atype, material in pairs(armor) do
|
|
||||||
local item = "mcl_armor:" .. atype .. "_" .. material
|
|
||||||
self._armor_items[atype] = item
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Setup armor drops
|
|
||||||
for atype, material in pairs(armor) do
|
|
||||||
local wear = math.random(1, 65535)
|
|
||||||
local item = "mcl_armor:" .. atype .. "_" .. material .. " 1 " .. wear
|
|
||||||
self.drops = table.copy(self.drops)
|
|
||||||
table.insert(self.drops, {
|
|
||||||
name = item,
|
|
||||||
chance = 1/0.085, -- 8.5%
|
|
||||||
min = 1,
|
|
||||||
max = 1,
|
|
||||||
looting = "rare",
|
|
||||||
looting_factor = 0.01 / 3,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Configure textures
|
|
||||||
local t = ""
|
|
||||||
local first_image = true
|
|
||||||
for atype, material in pairs(armor) do
|
|
||||||
if not first_image then
|
|
||||||
t = t .. "^"
|
|
||||||
end
|
|
||||||
t = t .. "mcl_armor_" .. atype .. "_" .. material .. ".png"
|
|
||||||
first_image = false
|
|
||||||
end
|
|
||||||
if t ~= "" then
|
|
||||||
self.base_texture = table.copy(self.base_texture)
|
|
||||||
self.base_texture[1] = t
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Configure damage groups based on armor
|
|
||||||
-- Source: https://minecraft.fandom.com/wiki/Armor#Armor_points
|
|
||||||
local points = 2
|
|
||||||
for atype, material in pairs(armor) do
|
|
||||||
local item_name = "mcl_armor:" .. atype .. "_" .. material
|
|
||||||
points = points + minetest.get_item_group(item_name, "mcl_armor_points")
|
|
||||||
end
|
|
||||||
local armor_strength = 100 - 4 * points
|
|
||||||
local armor_groups = self.object:get_armor_groups()
|
|
||||||
armor_groups.fleshy = armor_strength
|
|
||||||
self.armor = armor_groups
|
|
||||||
|
|
||||||
-- Helmet protects mob from sun damage
|
|
||||||
if armor.helmet then
|
|
||||||
self.ignited_by_sunlight = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- activate mob and reload settings
|
-- activate mob and reload settings
|
||||||
mobs.mob_activate = function(self, staticdata, def, dtime)
|
mobs.mob_activate = function(self, staticdata, def, dtime)
|
||||||
|
@ -206,11 +87,6 @@ mobs.mob_activate = function(self, staticdata, def, dtime)
|
||||||
self.base_selbox = self.selectionbox
|
self.base_selbox = self.selectionbox
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Setup armor on mobs
|
|
||||||
if self._spawn_with_armor then
|
|
||||||
mobs.armor_setup(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- for current mobs that dont have this set
|
-- for current mobs that dont have this set
|
||||||
if not self.base_selbox then
|
if not self.base_selbox then
|
||||||
self.base_selbox = self.selectionbox or self.base_colbox
|
self.base_selbox = self.selectionbox or self.base_colbox
|
||||||
|
|
|
@ -6,14 +6,11 @@ local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
||||||
local get_biome_name = minetest.get_biome_name
|
local get_biome_name = minetest.get_biome_name
|
||||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
local get_connected_players = minetest.get_connected_players
|
local get_connected_players = minetest.get_connected_players
|
||||||
local minetest_get_perlin = minetest.get_perlin
|
|
||||||
|
|
||||||
local math_random = math.random
|
local math_random = math.random
|
||||||
local math_floor = math.floor
|
local math_floor = math.floor
|
||||||
local math_ceil = math.ceil
|
--local max = math.max
|
||||||
local math_cos = math.cos
|
|
||||||
local math_sin = math.sin
|
|
||||||
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
|
|
||||||
|
|
||||||
--local vector_distance = vector.distance
|
--local vector_distance = vector.distance
|
||||||
local vector_new = vector.new
|
local vector_new = vector.new
|
||||||
|
@ -25,35 +22,18 @@ local table_remove = table.remove
|
||||||
local pairs = pairs
|
local pairs = pairs
|
||||||
|
|
||||||
-- range for mob count
|
-- range for mob count
|
||||||
local aoc_range = 32
|
local aoc_range = 48
|
||||||
|
|
||||||
--do mobs spawn?
|
--do mobs spawn?
|
||||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
local noise_params = {
|
THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
||||||
offset = 0,
|
|
||||||
scale = 3,
|
|
||||||
spread = {
|
|
||||||
x = 301,
|
|
||||||
y = 50,
|
|
||||||
z = 304,
|
|
||||||
},
|
|
||||||
seed = 100,
|
|
||||||
octaves = 3,
|
|
||||||
persistence = 0.5,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
|
||||||
-- Also used for missing parameter
|
|
||||||
-- Please update the list when adding new biomes!
|
|
||||||
|
|
||||||
local list_of_all_biomes = {
|
|
||||||
|
|
||||||
-- underground:
|
|
||||||
|
|
||||||
|
underground:
|
||||||
"FlowerForest_underground",
|
"FlowerForest_underground",
|
||||||
"JungleEdge_underground",
|
"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)]
|
||||||
"ColdTaiga_underground",
|
"ColdTaiga_underground",
|
||||||
"IcePlains_underground",
|
"IcePlains_underground",
|
||||||
"IcePlainsSpikes_underground",
|
"IcePlainsSpikes_underground",
|
||||||
|
@ -64,8 +44,7 @@ local list_of_all_biomes = {
|
||||||
"ExtremeHillsM_underground",
|
"ExtremeHillsM_underground",
|
||||||
"JungleEdgeM_underground",
|
"JungleEdgeM_underground",
|
||||||
|
|
||||||
-- ocean:
|
ocean:
|
||||||
|
|
||||||
"RoofedForest_ocean",
|
"RoofedForest_ocean",
|
||||||
"JungleEdgeM_ocean",
|
"JungleEdgeM_ocean",
|
||||||
"BirchForestM_ocean",
|
"BirchForestM_ocean",
|
||||||
|
@ -127,15 +106,13 @@ local list_of_all_biomes = {
|
||||||
"Taiga_deep_ocean",
|
"Taiga_deep_ocean",
|
||||||
"JungleM_ocean",
|
"JungleM_ocean",
|
||||||
|
|
||||||
-- water or beach?
|
water or beach?
|
||||||
|
|
||||||
"MesaPlateauFM_sandlevel",
|
"MesaPlateauFM_sandlevel",
|
||||||
"MesaPlateauF_sandlevel",
|
"MesaPlateauF_sandlevel",
|
||||||
"MesaBryce_sandlevel",
|
"MesaBryce_sandlevel",
|
||||||
"Mesa_sandlevel",
|
"Mesa_sandlevel",
|
||||||
|
|
||||||
-- beach:
|
beach:
|
||||||
|
|
||||||
"FlowerForest_beach",
|
"FlowerForest_beach",
|
||||||
"Forest_beach",
|
"Forest_beach",
|
||||||
"StoneBeach",
|
"StoneBeach",
|
||||||
|
@ -150,13 +127,11 @@ local list_of_all_biomes = {
|
||||||
"JungleM_shore",
|
"JungleM_shore",
|
||||||
"Jungle_shore",
|
"Jungle_shore",
|
||||||
|
|
||||||
-- dimension biome:
|
dimension biome:
|
||||||
|
|
||||||
"Nether",
|
"Nether",
|
||||||
"End",
|
"End",
|
||||||
|
|
||||||
-- Overworld regular:
|
Overworld regular:
|
||||||
|
|
||||||
"Mesa",
|
"Mesa",
|
||||||
"FlowerForest",
|
"FlowerForest",
|
||||||
"Swampland",
|
"Swampland",
|
||||||
|
@ -189,7 +164,9 @@ local list_of_all_biomes = {
|
||||||
"MesaBryce",
|
"MesaBryce",
|
||||||
"JungleEdge",
|
"JungleEdge",
|
||||||
"SavannaM",
|
"SavannaM",
|
||||||
}
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- count how many mobs are in an area
|
-- count how many mobs are in an area
|
||||||
local function count_mobs(pos)
|
local function count_mobs(pos)
|
||||||
|
@ -239,88 +216,11 @@ WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua??
|
||||||
|
|
||||||
--this is where all of the spawning information is kept
|
--this is where all of the spawning information is kept
|
||||||
local spawn_dictionary = {}
|
local spawn_dictionary = {}
|
||||||
local summary_chance = 0
|
|
||||||
|
|
||||||
function mobs:spawn_setup(def)
|
|
||||||
if not mobs_spawn then return end
|
|
||||||
|
|
||||||
if not def then
|
|
||||||
minetest.log("warning", "Empty mob spawn setup definition")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = def.name
|
|
||||||
if not name then
|
|
||||||
minetest.log("warning", "Missing mob name")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local dimension = def.dimension or "overworld"
|
|
||||||
local type_of_spawning = def.type_of_spawning or "ground"
|
|
||||||
local biomes = def.biomes or list_of_all_biomes
|
|
||||||
local min_light = def.min_light or 0
|
|
||||||
local max_light = def.max_light or (minetest.LIGHT_MAX + 1)
|
|
||||||
local chance = def.chance or 1000
|
|
||||||
local aoc = def.aoc or aoc_range
|
|
||||||
local min_height = def.min_height or mcl_mapgen.overworld.min
|
|
||||||
local max_height = def.max_height or mcl_mapgen.overworld.max
|
|
||||||
local day_toggle = def.day_toggle
|
|
||||||
local on_spawn = def.on_spawn
|
|
||||||
local check_position = def.check_position
|
|
||||||
local group_size_min = def.group_size_min or 1
|
|
||||||
local group_size_max = def.group_size_max or 1
|
|
||||||
|
|
||||||
-- chance/spawn number override in minetest.conf for registered mob
|
|
||||||
local numbers = minetest.settings:get(name)
|
|
||||||
if numbers then
|
|
||||||
numbers = numbers:split(",")
|
|
||||||
chance = tonumber(numbers[1]) or chance
|
|
||||||
aoc = tonumber(numbers[2]) or aoc
|
|
||||||
if chance == 0 then
|
|
||||||
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
|
||||||
end
|
|
||||||
|
|
||||||
if chance < 1 then
|
|
||||||
chance = 1
|
|
||||||
minetest.log("warning", "Chance shouldn't be less than 1 (mob name: " .. name ..")")
|
|
||||||
end
|
|
||||||
|
|
||||||
spawn_dictionary[#spawn_dictionary + 1] = {
|
|
||||||
name = name,
|
|
||||||
dimension = dimension,
|
|
||||||
type_of_spawning = type_of_spawning,
|
|
||||||
biomes = biomes,
|
|
||||||
min_light = min_light,
|
|
||||||
max_light = max_light,
|
|
||||||
chance = chance,
|
|
||||||
aoc = aoc,
|
|
||||||
min_height = min_height,
|
|
||||||
max_height = max_height,
|
|
||||||
day_toggle = day_toggle,
|
|
||||||
check_position = check_position,
|
|
||||||
on_spawn = on_spawn,
|
|
||||||
group_size_min = group_size_min,
|
|
||||||
group_size_max = group_size_max,
|
|
||||||
}
|
|
||||||
summary_chance = summary_chance + chance
|
|
||||||
end
|
|
||||||
|
|
||||||
function mobs.spawn_mob(name, pos)
|
|
||||||
local def = minetest.registered_entities[name]
|
|
||||||
if not def then return end
|
|
||||||
if def.spawn then
|
|
||||||
return def.spawn(pos)
|
|
||||||
end
|
|
||||||
return minetest.add_entity(pos, name)
|
|
||||||
end
|
|
||||||
|
|
||||||
local spawn_mob = mobs.spawn_mob
|
|
||||||
|
|
||||||
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||||
|
|
||||||
|
--print(dump(biomes))
|
||||||
|
|
||||||
-- Do mobs spawn at all?
|
-- Do mobs spawn at all?
|
||||||
if not mobs_spawn then
|
if not mobs_spawn then
|
||||||
return
|
return
|
||||||
|
@ -339,7 +239,179 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.log("action", string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
minetest.log("action",
|
||||||
|
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
local function spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||||
|
|
||||||
|
local orig_pos = table.copy(pos)
|
||||||
|
-- is mob actually registered?
|
||||||
|
if not mobs.spawning_mobs[name]
|
||||||
|
or not minetest.registered_entities[name] then
|
||||||
|
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- additional custom checks for spawning mob
|
||||||
|
if mobs:spawn_abm_check(pos, node, name) == true then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- count nearby mobs in same spawn class
|
||||||
|
local entdef = minetest.registered_entities[name]
|
||||||
|
local spawn_class = entdef and entdef.spawn_class
|
||||||
|
if not spawn_class then
|
||||||
|
if entdef.type == "monster" then
|
||||||
|
spawn_class = "hostile"
|
||||||
|
else
|
||||||
|
spawn_class = "passive"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
||||||
|
-- do not spawn if too many of same mob in area
|
||||||
|
if active_object_count_wider >= max_per_block -- large-range mob cap
|
||||||
|
or (not in_class_cap) -- spawn class mob cap
|
||||||
|
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
||||||
|
-- too many entities
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if toggle set to nil then ignore day/night check
|
||||||
|
if day_toggle then
|
||||||
|
|
||||||
|
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||||
|
|
||||||
|
if tod > 4500 and tod < 19500 then
|
||||||
|
-- daylight, but mob wants night
|
||||||
|
if day_toggle == false then
|
||||||
|
-- mob needs night
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- night time but mob wants day
|
||||||
|
if day_toggle == true then
|
||||||
|
-- mob needs day
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- spawn above node
|
||||||
|
pos.y = pos.y + 1
|
||||||
|
|
||||||
|
-- only spawn away from player
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, 24)
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
if objs[n]:is_player() then
|
||||||
|
-- player too close
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- mobs cannot spawn in protected areas when enabled
|
||||||
|
if not spawn_protected
|
||||||
|
and minetest.is_protected(pos, "") then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- are we spawning within height limits?
|
||||||
|
if pos.y > max_height
|
||||||
|
or pos.y < min_height then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- are light levels ok?
|
||||||
|
local light = minetest.get_node_light(pos)
|
||||||
|
if not light
|
||||||
|
or light > max_light
|
||||||
|
or light < min_light then
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- do we have enough space to spawn mob?
|
||||||
|
local ent = minetest.registered_entities[name]
|
||||||
|
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
||||||
|
local min_x, max_x
|
||||||
|
if width_x % 2 == 0 then
|
||||||
|
max_x = math.floor(width_x/2)
|
||||||
|
min_x = -(max_x-1)
|
||||||
|
else
|
||||||
|
max_x = math.floor(width_x/2)
|
||||||
|
min_x = -max_x
|
||||||
|
end
|
||||||
|
|
||||||
|
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
||||||
|
local min_z, max_z
|
||||||
|
if width_z % 2 == 0 then
|
||||||
|
max_z = math.floor(width_z/2)
|
||||||
|
min_z = -(max_z-1)
|
||||||
|
else
|
||||||
|
max_z = math.floor(width_z/2)
|
||||||
|
min_z = -max_z
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
||||||
|
|
||||||
|
for y = 0, max_y do
|
||||||
|
for x = min_x, max_x do
|
||||||
|
for z = min_z, max_z do
|
||||||
|
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
||||||
|
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
||||||
|
-- inside block
|
||||||
|
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
||||||
|
if ent.spawn_small_alternative and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
||||||
|
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
||||||
|
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- tweak X/Y/Z spawn pos
|
||||||
|
if width_x % 2 == 0 then
|
||||||
|
pos.x = pos.x + 0.5
|
||||||
|
end
|
||||||
|
if width_z % 2 == 0 then
|
||||||
|
pos.z = pos.z + 0.5
|
||||||
|
end
|
||||||
|
pos.y = pos.y - 0.5
|
||||||
|
|
||||||
|
local mob = minetest.add_entity(pos, name)
|
||||||
|
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
||||||
|
|
||||||
|
if on_spawn then
|
||||||
|
|
||||||
|
local ent = mob:get_luaentity()
|
||||||
|
|
||||||
|
on_spawn(ent, pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
||||||
|
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||||
|
end
|
||||||
|
]]--
|
||||||
|
|
||||||
|
local entdef = minetest.registered_entities[name]
|
||||||
|
local spawn_class
|
||||||
|
if entdef.type == "monster" then
|
||||||
|
spawn_class = "hostile"
|
||||||
|
else
|
||||||
|
spawn_class = "passive"
|
||||||
end
|
end
|
||||||
|
|
||||||
--load information into the spawn dictionary
|
--load information into the spawn dictionary
|
||||||
|
@ -351,36 +423,107 @@ function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_ligh
|
||||||
spawn_dictionary[key]["biomes"] = biomes
|
spawn_dictionary[key]["biomes"] = biomes
|
||||||
spawn_dictionary[key]["min_light"] = min_light
|
spawn_dictionary[key]["min_light"] = min_light
|
||||||
spawn_dictionary[key]["max_light"] = max_light
|
spawn_dictionary[key]["max_light"] = max_light
|
||||||
|
spawn_dictionary[key]["interval"] = interval
|
||||||
spawn_dictionary[key]["chance"] = chance
|
spawn_dictionary[key]["chance"] = chance
|
||||||
spawn_dictionary[key]["aoc"] = aoc
|
spawn_dictionary[key]["aoc"] = aoc
|
||||||
spawn_dictionary[key]["min_height"] = min_height
|
spawn_dictionary[key]["min_height"] = min_height
|
||||||
spawn_dictionary[key]["max_height"] = max_height
|
spawn_dictionary[key]["max_height"] = max_height
|
||||||
spawn_dictionary[key]["day_toggle"] = day_toggle
|
spawn_dictionary[key]["day_toggle"] = day_toggle
|
||||||
spawn_dictionary[key]["group_size_min"] = 1
|
--spawn_dictionary[key]["on_spawn"] = spawn_abm_action
|
||||||
spawn_dictionary[key]["group_size_max"] = 3
|
spawn_dictionary[key]["spawn_class"] = spawn_class
|
||||||
|
|
||||||
summary_chance = summary_chance + chance
|
--[[
|
||||||
|
minetest.register_abm({
|
||||||
|
label = name .. " spawning",
|
||||||
|
nodenames = nodes,
|
||||||
|
neighbors = neighbors,
|
||||||
|
interval = interval,
|
||||||
|
chance = floor(max(1, chance * mobs_spawn_chance)),
|
||||||
|
catch_up = false,
|
||||||
|
action = spawn_abm_action,
|
||||||
|
})
|
||||||
|
]]--
|
||||||
end
|
end
|
||||||
|
|
||||||
local two_pi = 2 * math.pi
|
-- compatibility with older mob registration
|
||||||
local function get_next_mob_spawn_pos(pos)
|
-- we're going to forget about this for now -j4i
|
||||||
local distance = math_random(25, 32)
|
--[[
|
||||||
local angle = math_random() * two_pi
|
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
||||||
return {
|
|
||||||
x = math_round(pos.x + distance * math_cos(angle)),
|
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
||||||
y = pos.y,
|
chance, active_object_count, -31000, max_height, day_toggle)
|
||||||
z = math_round(pos.z + distance * math_sin(angle))
|
end
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
--Don't disable this yet-j4i
|
||||||
|
-- MarkBu's spawn function
|
||||||
|
|
||||||
|
function mobs:spawn(def)
|
||||||
|
--does nothing for now
|
||||||
|
--[[
|
||||||
|
local name = def.name
|
||||||
|
local nodes = def.nodes or {"group:soil", "group:stone"}
|
||||||
|
local neighbors = def.neighbors or {"air"}
|
||||||
|
local min_light = def.min_light or 0
|
||||||
|
local max_light = def.max_light or 15
|
||||||
|
local interval = def.interval or 30
|
||||||
|
local chance = def.chance or 5000
|
||||||
|
local active_object_count = def.active_object_count or 1
|
||||||
|
local min_height = def.min_height or -31000
|
||||||
|
local max_height = def.max_height or 31000
|
||||||
|
local day_toggle = def.day_toggle
|
||||||
|
local on_spawn = def.on_spawn
|
||||||
|
|
||||||
|
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
||||||
|
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
||||||
|
]]--
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local axis
|
||||||
|
--inner and outer part of square donut radius
|
||||||
|
local inner = 15
|
||||||
|
local outer = 64
|
||||||
|
local int = {-1,1}
|
||||||
|
|
||||||
|
local function position_calculation(pos)
|
||||||
|
|
||||||
|
pos = vector_floor(pos)
|
||||||
|
|
||||||
|
--this is used to determine the axis buffer from the player
|
||||||
|
axis = math_random(0,1)
|
||||||
|
|
||||||
|
--cast towards the direction
|
||||||
|
if axis == 0 then --x
|
||||||
|
pos.x = pos.x + math_random(inner,outer)*int[math_random(1,2)]
|
||||||
|
pos.z = pos.z + math_random(-outer,outer)
|
||||||
|
else --z
|
||||||
|
pos.z = pos.z + math_random(inner,outer)*int[math_random(1,2)]
|
||||||
|
pos.x = pos.x + math_random(-outer,outer)
|
||||||
|
end
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
local decypher_limits_dictionary = {
|
||||||
|
["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max},
|
||||||
|
["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max},
|
||||||
|
["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max}
|
||||||
}
|
}
|
||||||
end
|
]]--
|
||||||
|
|
||||||
local function decypher_limits(posy)
|
local function decypher_limits(posy)
|
||||||
|
--local min_max_table = decypher_limits_dictionary[dimension]
|
||||||
|
--return min_max_table[1],min_max_table[2]
|
||||||
posy = math_floor(posy)
|
posy = math_floor(posy)
|
||||||
return posy - 32, posy + 32
|
return posy - 32, posy + 32
|
||||||
end
|
end
|
||||||
|
|
||||||
--a simple helper function for mob_spawn
|
--a simple helper function for mob_spawn
|
||||||
local function biome_check(biome_list, biome_goal)
|
local function biome_check(biome_list, biome_goal)
|
||||||
for _, data in pairs(biome_list) do
|
for _,data in ipairs(biome_list) do
|
||||||
if data == biome_goal then
|
if data == biome_goal then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -390,27 +533,69 @@ local function biome_check(biome_list, biome_goal)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--todo mob limiting
|
||||||
|
--MAIN LOOP
|
||||||
|
|
||||||
if mobs_spawn then
|
if mobs_spawn then
|
||||||
|
local timer = 0
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
timer = timer + dtime
|
||||||
|
if timer >= 10 then
|
||||||
|
timer = 0
|
||||||
|
for _,player in pairs(get_connected_players()) do
|
||||||
|
-- after this line each "break" means "continue"
|
||||||
|
local do_mob_spawning = true
|
||||||
|
repeat
|
||||||
|
--don't need to get these variables more than once
|
||||||
|
--they happen in a single server step
|
||||||
|
|
||||||
local perlin_noise
|
local player_pos = player:get_pos()
|
||||||
|
local dimension = mcl_worlds.pos_to_dimension(player_pos)
|
||||||
|
|
||||||
|
if dimension == "void" or dimension == "default" then
|
||||||
|
break -- ignore void and unloaded area
|
||||||
|
end
|
||||||
|
|
||||||
|
local min, max = decypher_limits(player_pos.y)
|
||||||
|
|
||||||
|
for i = 1, math_random(1,4) do
|
||||||
|
-- after this line each "break" means "continue"
|
||||||
|
local do_mob_algorithm = true
|
||||||
|
repeat
|
||||||
|
|
||||||
|
local goal_pos = position_calculation(player_pos)
|
||||||
|
|
||||||
|
local spawning_position_list = find_nodes_in_area_under_air(vector_new(goal_pos.x,min,goal_pos.z), vector_new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"})
|
||||||
|
|
||||||
|
--couldn't find node
|
||||||
|
if #spawning_position_list <= 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
|
||||||
local dimension = dimension or mcl_worlds.pos_to_dimension(pos)
|
|
||||||
local goal_pos = get_next_mob_spawn_pos(pos)
|
|
||||||
local spawning_position_list = find_nodes_in_area_under_air(
|
|
||||||
{x = goal_pos.x, y = y_min, z = goal_pos.z},
|
|
||||||
{x = goal_pos.x, y = y_max, z = goal_pos.z},
|
|
||||||
{"group:solid", "group:water", "group:lava"}
|
|
||||||
)
|
|
||||||
if #spawning_position_list <= 0 then return end
|
|
||||||
local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)]
|
local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)]
|
||||||
|
|
||||||
|
--Prevent strange behavior --- this is commented out: /too close to player --fixed with inner circle
|
||||||
|
if not spawning_position then -- or vector_distance(player_pos, spawning_position) < 15
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
--hard code mob limit in area to 5 for now
|
--hard code mob limit in area to 5 for now
|
||||||
if count_mobs(spawning_position) >= 5 then return end
|
if count_mobs(spawning_position) >= 5 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
local gotten_node = get_node(spawning_position).name
|
local gotten_node = get_node(spawning_position).name
|
||||||
|
|
||||||
|
if not gotten_node or gotten_node == "air" then --skip air nodes
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
local gotten_biome = minetest.get_biome_data(spawning_position)
|
local gotten_biome = minetest.get_biome_data(spawning_position)
|
||||||
if not gotten_node or not gotten_biome then return end
|
|
||||||
|
if not gotten_biome then
|
||||||
|
break --skip if in unloaded area
|
||||||
|
end
|
||||||
|
|
||||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
||||||
|
|
||||||
--add this so mobs don't spawn inside nodes
|
--add this so mobs don't spawn inside nodes
|
||||||
|
@ -421,12 +606,8 @@ if mobs_spawn then
|
||||||
|
|
||||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||||
local is_ground = not (is_water or is_lava)
|
|
||||||
if not is_ground then
|
|
||||||
spawning_position.y = spawning_position.y - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local mob_def
|
local mob_def = nil
|
||||||
|
|
||||||
--create a disconnected clone of the spawn dictionary
|
--create a disconnected clone of the spawn dictionary
|
||||||
--prevents memory leak
|
--prevents memory leak
|
||||||
|
@ -434,58 +615,94 @@ if mobs_spawn then
|
||||||
|
|
||||||
--grab mob that fits into the spawning location
|
--grab mob that fits into the spawning location
|
||||||
--randomly grab a mob, don't exclude any possibilities
|
--randomly grab a mob, don't exclude any possibilities
|
||||||
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
|
local repeat_mob_search = true
|
||||||
local noise = perlin_noise:get_3d(spawning_position)
|
repeat
|
||||||
local current_summary_chance = summary_chance
|
|
||||||
while #mob_library_worker_table > 0 do
|
--do not infinite loop
|
||||||
local mob_chance_offset = (math_round(noise * current_summary_chance + 12345) % current_summary_chance) + 1
|
if #mob_library_worker_table <= 0 then
|
||||||
local mob_index = 1
|
--print("breaking infinite loop")
|
||||||
local mob_chance = mob_library_worker_table[mob_index].chance
|
break
|
||||||
local step_chance = mob_chance
|
|
||||||
while step_chance < mob_chance_offset do
|
|
||||||
mob_index = mob_index + 1
|
|
||||||
mob_chance = mob_library_worker_table[mob_index].chance
|
|
||||||
step_chance = step_chance + mob_chance
|
|
||||||
end
|
end
|
||||||
local mob_def = mob_library_worker_table[mob_index]
|
|
||||||
if mob_def
|
local skip = false
|
||||||
and spawning_position.y >= mob_def.min_height
|
|
||||||
and spawning_position.y <= mob_def.max_height
|
--use this for removing table elements of mobs that do not match
|
||||||
and mob_def.dimension == dimension
|
local temp_index = math_random(1,#mob_library_worker_table)
|
||||||
and biome_check(mob_def.biomes, gotten_biome)
|
|
||||||
and gotten_light >= mob_def.min_light
|
local temp_def = mob_library_worker_table[temp_index]
|
||||||
and gotten_light <= mob_def.max_light
|
|
||||||
and (is_ground or mob_def.type_of_spawning ~= "ground")
|
--skip if something ridiculous happens (nil mob def)
|
||||||
and (mob_def.check_position and mob_def.check_position(spawning_position) or true)
|
--something truly horrible has happened if skip gets
|
||||||
then
|
--activated at this point
|
||||||
|
if not temp_def then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not skip and (spawning_position.y < temp_def.min_height or spawning_position.y > temp_def.max_height) then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--skip if not correct dimension
|
||||||
|
if not skip and (temp_def.dimension ~= dimension) then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--skip if not in correct biome
|
||||||
|
if not skip and (not biome_check(temp_def.biomes, gotten_biome)) then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--don't spawn if not in light limits
|
||||||
|
if not skip and (gotten_light < temp_def.min_light or gotten_light > temp_def.max_light) then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--skip if not in correct spawning type
|
||||||
|
if not skip and (temp_def.type_of_spawning == "ground" and is_water) then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not skip and (temp_def.type_of_spawning == "ground" and is_lava) then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--found a mob, exit out of loop
|
||||||
|
if not skip then
|
||||||
|
--minetest.log("warning", "found mob:"..temp_def.name)
|
||||||
|
--print("found mob:"..temp_def.name)
|
||||||
|
mob_def = table_copy(temp_def)
|
||||||
|
break
|
||||||
|
else
|
||||||
|
--minetest.log("warning", "deleting temp index "..temp_index)
|
||||||
|
--print("deleting temp index")
|
||||||
|
table_remove(mob_library_worker_table, temp_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
until repeat_mob_search == false --this is needed to sort through mobs randomly
|
||||||
|
|
||||||
|
|
||||||
|
--catch if went through all mobs and something went horribly wrong
|
||||||
|
--could not find a valid mob to spawn that fits the environment
|
||||||
|
if not mob_def then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
--adjust the position for water and lava mobs
|
||||||
|
if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then
|
||||||
|
spawning_position.y = spawning_position.y - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
--print("spawning: " .. mob_def.name)
|
||||||
|
|
||||||
--everything is correct, spawn mob
|
--everything is correct, spawn mob
|
||||||
local object = spawn_mob(mob_def.name, spawning_position)
|
minetest.add_entity(spawning_position, mob_def.name)
|
||||||
if object then
|
|
||||||
return mob_def.on_spawn and mob_def.on_spawn(object, spawning_position)
|
break
|
||||||
end
|
until do_mob_algorithm == false --this is a safety catch
|
||||||
end
|
|
||||||
current_summary_chance = current_summary_chance - mob_chance
|
|
||||||
table_remove(mob_library_worker_table, mob_index)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
break
|
||||||
--MAIN LOOP
|
until do_mob_spawning == false --this is a performance catch
|
||||||
|
|
||||||
local timer = 0
|
|
||||||
minetest.register_globalstep(function(dtime)
|
|
||||||
timer = timer + dtime
|
|
||||||
if timer < 10 then return end
|
|
||||||
timer = 0
|
|
||||||
for _, player in pairs(get_connected_players()) do
|
|
||||||
local pos = player:get_pos()
|
|
||||||
local dimension = mcl_worlds.pos_to_dimension(pos)
|
|
||||||
-- ignore void and unloaded area
|
|
||||||
if dimension ~= "void" and dimension ~= "default" then
|
|
||||||
local y_min, y_max = decypher_limits(pos.y)
|
|
||||||
for i = 1, math_random(1, 4) do
|
|
||||||
spawn_a_mob(pos, dimension, y_min, y_max)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -6,6 +6,6 @@ You need the “maphack” privilege to change the mob spawner.=Vous avez besoin
|
||||||
Name Tag=Étiquette de nom
|
Name Tag=Étiquette de nom
|
||||||
A name tag is an item to name a mob.=Une étiquette de nom est un élément pour nommer un mob.
|
A name tag is an item to name a mob.=Une étiquette de nom est un élément pour nommer un mob.
|
||||||
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=Avant d'utiliser l'étiquette de nom, vous devez définir un nom sur une enclume. Ensuite, vous pouvez utiliser l'étiquette de nom pour nommer un mob. Cela utilise l'étiquette de nom.
|
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=Avant d'utiliser l'étiquette de nom, vous devez définir un nom sur une enclume. Ensuite, vous pouvez utiliser l'étiquette de nom pour nommer un mob. Cela utilise l'étiquette de nom.
|
||||||
Only peaceful mobs allowed!=Seuls les mobs pacifiques sont autorisés !
|
Only peaceful mobs allowed!=Seuls les mobs pacifiques sont autorisées!
|
||||||
Give names to mobs=Donne des noms aux mobs
|
Give names to mobs=Donne des noms aux mobs
|
||||||
Set name at anvil=Définir le nom sur l'enclume
|
Set name at anvil=Définir le nom sur l'enclume
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# textdomain: mcl_mobs
|
# textdomain: mcl_mobs
|
||||||
Peaceful mode active! No monsters will spawn.=Мирный режим включён! Монстры не будут появляться.
|
Peaceful mode active! No monsters will spawn.=Мирный режим включён! Монстры не будут появляться.
|
||||||
This allows you to place a single mob.=Позволяет вам разместить одного моба.
|
This allows you to place a single mob.=Позволяет вам разместить одного моба.
|
||||||
Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns.=Используйте предмет там, где хотите, чтобы появился моб. Животные будут появляться уже прирученные, если это не нужно, удерживайте клавишу [Красться] при использовании. Если поместить это на спаунер, появляющийся из него моб будет изменён.
|
Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns.=Просто поместите это туда, где хотите, чтобы появился моб. Животные будут появляться уже прирученные, если это не нужно, удерживайте клавишу [Красться] при размещении. Если поместить это на спаунер, появляющийся из него моб будет изменён.
|
||||||
You need the “maphack” privilege to change the mob spawner.=Вам нужно обладать привилегией “maphack”, чтобы изменить спаунер моба.
|
You need the “maphack” privilege to change the mob spawner.=Вам нужно обладать привилегией “maphack”, чтобы изменить спаунер моба.
|
||||||
Name Tag=Бирка
|
Name Tag=Именная бирка
|
||||||
A name tag is an item to name a mob.=Бирка это предмет, которым можно дать мобу имя.
|
A name tag is an item to name a mob.=Именная бирка это предмет, чтобы дать мобу имя.
|
||||||
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=Прежде чем использовать бирку, переименуйте её на наковальне. Тогда вы сможете использовать бирку на мобе, чтобы дать ему имя.
|
Before you use the name tag, you need to set a name at an anvil. Then you can use the name tag to name a mob. This uses up the name tag.=Прежде чем использовать именную бирку, нужно задать имя на наковальне. Тогда вы сможете использовать бирку, чтобы дать имя мобу.
|
||||||
Only peaceful mobs allowed!=Разрешены только мирные мобы!
|
Only peaceful mobs allowed!=Разрешены только мирные мобы!
|
||||||
Give names to mobs=Даёт имена мобам
|
Give names to mobs=Даёт имена мобам
|
||||||
Set name at anvil=Переименуйте на наковальне
|
Set name at anvil=Задайте имя при помощи наковальни
|
||||||
|
|
|
@ -160,7 +160,6 @@ minetest.register_entity("mcl_paintings:painting", {
|
||||||
set_entity(self.object)
|
set_entity(self.object)
|
||||||
end,
|
end,
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
if not self then return end
|
|
||||||
local data = {
|
local data = {
|
||||||
_facing = self._facing,
|
_facing = self._facing,
|
||||||
_pos = self._pos,
|
_pos = self._pos,
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
# textdomain:mcl_paintings
|
|
||||||
Painting=Pintura
|
|
|
@ -1,2 +1,2 @@
|
||||||
# textdomain:mcl_paintings
|
# textdomain:mcl_paintings
|
||||||
Painting=Картина
|
Painting=Рисование
|
||||||
|
|
|
@ -68,7 +68,7 @@ mobs:register_mob("mobs_mc:blaze", {
|
||||||
light_damage = 0,
|
light_damage = 0,
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
attack_type = "projectile",
|
attack_type = "projectile",
|
||||||
arrow = "mobs_mc:blaze_fireball_entity",
|
arrow = "mobs_mc:blaze_fireball",
|
||||||
shoot_interval = 3.5,
|
shoot_interval = 3.5,
|
||||||
shoot_offset = 1.0,
|
shoot_offset = 1.0,
|
||||||
passive = false,
|
passive = false,
|
||||||
|
|
|
@ -61,7 +61,6 @@ dofile(path .. "/villager.lua") -- KrupnoPavel Mesh and animation by toby109tt
|
||||||
--dofile(path .. "/agent.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
--dofile(path .. "/agent.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
|
|
||||||
-- Illagers and witch
|
-- Illagers and witch
|
||||||
dofile(path .. "/pillager.lua") -- Mesh by KrupnoPavel and MrRar, animation by MrRar
|
|
||||||
dofile(path .. "/villager_evoker.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager_evoker.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
dofile(path .. "/villager_vindicator.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager_vindicator.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
dofile(path .. "/villager_zombie.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
dofile(path .. "/villager_zombie.lua") -- Mesh and animation by toby109tt / https://github.com/22i
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# textdomain: mobs_mc
|
# textdomain: mobs_mc
|
||||||
Totem of Undying=Totem d'immortalité
|
Totem of Undying=Totem d'immortalité
|
||||||
A totem of undying is a rare artifact which may safe you from certain death.=Un totem d'immortalité est un artefact rare qui peut vous protéger d'une mort certaine.
|
A totem of undying is a rare artifact which may safe you from certain death.=Un totem d'immortalité est un artefact rare qui peut vous protéger d'une mort certaine.
|
||||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Le totem ne fonctionne que lorsque vous le tenez dans votre main. Si vous recevez des dégâts mortels, vous êtes sauvé(e) de la mort et vous obtenez une seconde chance avec 1 HP. Cependant, le totem est détruit.
|
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Le totem ne fonctionne que lorsque vous le tenez dans votre main. Si vous recevez des dégâts mortels, vous êtes sauvé de la mort et vous obtenez une seconde chance avec 1 HP. Cependant, le totem est détruit.
|
||||||
Agent=Agent
|
Agent=Agent
|
||||||
Bat=Chauve-souris
|
Bat=Chauve-souris
|
||||||
Blaze=Blaze
|
Blaze=Blaze
|
||||||
|
@ -58,7 +58,7 @@ Iron horse armor can be worn by horses to increase their protection from harm a
|
||||||
Golden Horse Armor=Armure de cheval en or
|
Golden Horse Armor=Armure de cheval en or
|
||||||
Golden horse armor can be worn by horses to increase their protection from harm.=Une armure de cheval en or peut être portée par les chevaux pour augmenter leur protection contre les dommages.
|
Golden horse armor can be worn by horses to increase their protection from harm.=Une armure de cheval en or peut être portée par les chevaux pour augmenter leur protection contre les dommages.
|
||||||
Diamond Horse Armor=Armure de cheval en diamant
|
Diamond Horse Armor=Armure de cheval en diamant
|
||||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Une armure de cheval en diamant peut être portée par les chevaux pour augmenter fortement leur protection contre les dommages.
|
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Une armure de cheval en diament peut être portée par les chevaux pour augmenter fortement leur protection contre les dommages.
|
||||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Placez-la sur un cheval pour mettre l'armure de cheval. Les ânes et les mules ne peuvent pas porter d'armure de cheval.
|
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Placez-la sur un cheval pour mettre l'armure de cheval. Les ânes et les mules ne peuvent pas porter d'armure de cheval.
|
||||||
Farmer=Fermier
|
Farmer=Fermier
|
||||||
Fisherman=Pêcheur
|
Fisherman=Pêcheur
|
||||||
|
@ -73,5 +73,4 @@ Weapon Smith=Fabriquant d'arme
|
||||||
Tool Smith=Fabriquant d'outil
|
Tool Smith=Fabriquant d'outil
|
||||||
Cleric=Clerc
|
Cleric=Clerc
|
||||||
Nitwit=Crétin
|
Nitwit=Crétin
|
||||||
Protects you from death while wielding it=Vous protège de la mort en le maniant
|
Protects you from death while wielding it=Vous protège de la mort en la maniant
|
||||||
Pillager=Pillard
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
# textdomain: mobs_mc
|
|
||||||
Totem of Undying=Totèm d'Imortalitat
|
|
||||||
A totem of undying is a rare artifact which may safe you from certain death.=Un totèm d'imortalitat es un artefacte rara que pòt vos sauvar d'una mòrt surada.
|
|
||||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=
|
|
||||||
Agent=Agent
|
|
||||||
Bat=Ratapenada
|
|
||||||
Blaze=Blaze
|
|
||||||
Chicken=Pola
|
|
||||||
Cow=Vacha
|
|
||||||
Mooshroom=Champavacha
|
|
||||||
Creeper=Creeper
|
|
||||||
Ender Dragon=Drac de l'End
|
|
||||||
Enderman=Òme de l'End
|
|
||||||
Endermite=Endarna
|
|
||||||
Ghast=Trèva
|
|
||||||
Elder Guardian=Gardian Ainat
|
|
||||||
Guardian=Gardian
|
|
||||||
Horse=Ega
|
|
||||||
Skeleton Horse=Ega Esquelèta
|
|
||||||
Zombie Horse=Ega Mòrtaviva
|
|
||||||
Donkey=Asne
|
|
||||||
Mule=Miula
|
|
||||||
Iron Golem=Golèm de Fèrre
|
|
||||||
Llama=Lama
|
|
||||||
Ocelot=Ocelòt
|
|
||||||
Parrot=Papagai
|
|
||||||
Pig=Pòrc
|
|
||||||
Polar Bear=Ors Polar
|
|
||||||
Rabbit=Lapin
|
|
||||||
Killer Bunny=Lapin Tuaire
|
|
||||||
The Killer Bunny=Lo Lapin Tuaire
|
|
||||||
Sheep=Moton
|
|
||||||
Shulker=
|
|
||||||
Silverfish=
|
|
||||||
Skeleton=
|
|
||||||
Stray=
|
|
||||||
Wither Skeleton=
|
|
||||||
Magma Cube=
|
|
||||||
Slime=
|
|
||||||
Snow Golem=
|
|
||||||
Spider=
|
|
||||||
Cave Spider=
|
|
||||||
Squid=
|
|
||||||
Vex=
|
|
||||||
Evoker=
|
|
||||||
Illusioner=
|
|
||||||
Villager=
|
|
||||||
Vindicator=
|
|
||||||
Zombie Villager=
|
|
||||||
Witch=
|
|
||||||
Wither=
|
|
||||||
Wolf=
|
|
||||||
Husk=
|
|
||||||
Zombie=
|
|
||||||
Zombie Pigman=
|
|
||||||
Iron Horse Armor=
|
|
||||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=
|
|
||||||
Golden Horse Armor=
|
|
||||||
Golden horse armor can be worn by horses to increase their protection from harm.=
|
|
||||||
Diamond Horse Armor=
|
|
||||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=
|
|
||||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=
|
|
||||||
Farmer=
|
|
||||||
Fisherman=
|
|
||||||
Fletcher=
|
|
||||||
Shepherd=
|
|
||||||
Librarian=
|
|
||||||
Cartographer=
|
|
||||||
Armorer=
|
|
||||||
Leatherworker=
|
|
||||||
Butcher=
|
|
||||||
Weapon Smith=
|
|
||||||
Tool Smith=
|
|
||||||
Cleric=
|
|
||||||
Nitwit=
|
|
||||||
Protects you from death while wielding it=
|
|
||||||
Pillager=
|
|
|
@ -1,24 +1,24 @@
|
||||||
# textdomain: mobs_mc
|
# textdomain: mobs_mc
|
||||||
Totem of Undying=Тотем бессмертия
|
Totem of Undying=Тотем бессмертия
|
||||||
A totem of undying is a rare artifact which may safe you from certain death.=Тотем бессмертия это редкий артефакт, способный спасти вас от смерти.
|
A totem of undying is a rare artifact which may safe you from certain death.=Тотем бессмертия это редкий артефакт, способный спасти вас от смерти.
|
||||||
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Тотем работает только тогда, когда вы держите его в руке. Если вы получаете смертельный урон, вы спасаетесь от смерти и получаете второй шанс с 1 HP. Однако тотем при этом уничтожается.
|
The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however.=Тотем работает только когда вы держите его в руке. Если вы получаете смертельный урон, вы спасаетесь от смерти и получаете второй шанс с 1 HP. Однако тотем при этом уничтожается.
|
||||||
Agent=Агент
|
Agent=Агент
|
||||||
Bat=Летучая мышь
|
Bat=Летучая мышь
|
||||||
Blaze=Ифрит
|
Blaze=Ифрит
|
||||||
Chicken=Курица
|
Chicken=Курица
|
||||||
Cow=Корова
|
Cow=Корова
|
||||||
Mooshroom=Грибная корова
|
Mooshroom=Гриб
|
||||||
Creeper=Крипер
|
Creeper=Крипер
|
||||||
Ender Dragon=Дракон Края
|
Ender Dragon=Дракон Предела
|
||||||
Enderman=Эндермен
|
Enderman=Эндермен
|
||||||
Endermite=Эндермит
|
Endermite=Эндермит
|
||||||
Ghast=Гаст
|
Ghast=Гаст
|
||||||
Elder Guardian=Древний страж
|
Elder Guardian=Древний страж
|
||||||
Guardian=Страж
|
Guardian=Страж
|
||||||
Horse=Лошадь
|
Horse=Лошадь
|
||||||
Skeleton Horse=Лошадь-скелет
|
Skeleton Horse=Скелет лошади
|
||||||
Zombie Horse=Лошадь-зомби
|
Zombie Horse=Зомби-лошадь
|
||||||
Donkey=Осёл
|
Donkey=Ослик
|
||||||
Mule=Мул
|
Mule=Мул
|
||||||
Iron Golem=Железный голем
|
Iron Golem=Железный голем
|
||||||
Llama=Лама
|
Llama=Лама
|
||||||
|
@ -36,7 +36,7 @@ Skeleton=Скелет
|
||||||
Stray=Странник
|
Stray=Странник
|
||||||
Wither Skeleton=Скелет-иссушитель
|
Wither Skeleton=Скелет-иссушитель
|
||||||
Magma Cube=Лавовый куб
|
Magma Cube=Лавовый куб
|
||||||
Slime=Слизень
|
Slime=Слизняк
|
||||||
Snow Golem=Снежный голем
|
Snow Golem=Снежный голем
|
||||||
Spider=Паук
|
Spider=Паук
|
||||||
Cave Spider=Пещерный паук
|
Cave Spider=Пещерный паук
|
||||||
|
@ -53,13 +53,13 @@ Wolf=Волк
|
||||||
Husk=Кадавр
|
Husk=Кадавр
|
||||||
Zombie=Зомби
|
Zombie=Зомби
|
||||||
Zombie Pigman=Зомби-свиночеловек
|
Zombie Pigman=Зомби-свиночеловек
|
||||||
Iron Horse Armor=Железная конская броня
|
Iron Horse Armor=Железные доспехи лошади
|
||||||
Iron horse armor can be worn by horses to increase their protection from harm a bit.=Железная конская броня может быть надета на лошадь, чтобы повысить её защиту от урона.
|
Iron horse armor can be worn by horses to increase their protection from harm a bit.=Железные доспехи лошади, надетые на лошадь, немного защищают её от вреда.
|
||||||
Golden Horse Armor=Золотая конская броня
|
Golden Horse Armor=Золотые доспехи лошади
|
||||||
Golden horse armor can be worn by horses to increase their protection from harm.=Золотая конская броня может быть надета на лошадь, чтобы повысить её защиту от урона.
|
Golden horse armor can be worn by horses to increase their protection from harm.=Золотые доспехи лошади, надетые на лошадь, защищают её от вреда.
|
||||||
Diamond Horse Armor=Алмазная конская броня
|
Diamond Horse Armor=Алмазные доспехи лошади
|
||||||
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Алмазная конская броня может быть надета на лошадь, чтобы повысить её защиту от урона.
|
Diamond horse armor can be worn by horses to greatly increase their protection from harm.=Алмазные доспехи лошади, надетые на лошадь, отлично защищают её от вреда.
|
||||||
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Поместите это на лошадь, чтобы одеть лошадь в броню. Ослы и мулы не могут носить конскую броню.
|
Place it on a horse to put on the horse armor. Donkeys and mules can't wear horse armor.=Поместите это на лошадь, чтобы одеть лошадь в доспехи. Ослики и мулы не могут носить лошадиные доспехи.
|
||||||
Farmer=Фермер
|
Farmer=Фермер
|
||||||
Fisherman=Рыбак
|
Fisherman=Рыбак
|
||||||
Fletcher=Лучник
|
Fletcher=Лучник
|
||||||
|
@ -71,6 +71,6 @@ Leatherworker=Кожевник
|
||||||
Butcher=Мясник
|
Butcher=Мясник
|
||||||
Weapon Smith=Оружейник
|
Weapon Smith=Оружейник
|
||||||
Tool Smith=Инструментальщик
|
Tool Smith=Инструментальщик
|
||||||
Cleric=Священник
|
Cleric=Церковник
|
||||||
Nitwit=Нищий
|
Nitwit=Нищий
|
||||||
Protects you from death while wielding it=Защищает вас от смерти, пока вы держите его
|
Protects you from death while wielding it=Защищает вас от смерти, пока вы владеете им
|
||||||
|
|
|
@ -74,4 +74,3 @@ Tool Smith=
|
||||||
Cleric=
|
Cleric=
|
||||||
Nitwit=
|
Nitwit=
|
||||||
Protects you from death while wielding it=
|
Protects you from death while wielding it=
|
||||||
Pillager=
|
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
local S = minetest.get_translator("mobs_mc")
|
|
||||||
|
|
||||||
local function reload(self)
|
|
||||||
if not self or not self.object then return end
|
|
||||||
minetest.sound_play("mcl_bows_crossbow_drawback_1", {object = self.object, max_hear_distance=16}, true)
|
|
||||||
local props = self.object:get_properties()
|
|
||||||
if not props then return end
|
|
||||||
props.textures[2] = "mcl_bows_crossbow_3.png^[resize:16x16"
|
|
||||||
self.object:set_properties(props)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reset_animation(self, animation)
|
|
||||||
if not self or not self.object or self.current_animation ~= animation then return end
|
|
||||||
self.current_animation = "stand_reload" -- Mobs Redo won't set the animation unless we do this
|
|
||||||
mobs.set_mob_animation(self, animation)
|
|
||||||
end
|
|
||||||
|
|
||||||
pillager = {
|
|
||||||
description = S("Pillager"),
|
|
||||||
type = "monster",
|
|
||||||
spawn_class = "hostile",
|
|
||||||
hostile = true,
|
|
||||||
rotate = 270,
|
|
||||||
hp_min = 24,
|
|
||||||
hp_max = 24,
|
|
||||||
xp_min = 6,
|
|
||||||
xp_max = 6,
|
|
||||||
breath_max = -1,
|
|
||||||
eye_height = 1.5,
|
|
||||||
projectile_cooldown = 3, -- Useless
|
|
||||||
shoot_interval = 3, -- Useless
|
|
||||||
shoot_offset = 1.5,
|
|
||||||
dogshoot_switch = 1,
|
|
||||||
dogshoot_count_max = 1.8,
|
|
||||||
projectile_cooldown_min = 3,
|
|
||||||
projectile_cooldown_max = 2.5,
|
|
||||||
armor = {fleshy = 100},
|
|
||||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.98, 0.3},
|
|
||||||
pathfinding = 1,
|
|
||||||
group_attack = true,
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "mobs_mc_pillager.b3d",
|
|
||||||
|
|
||||||
--head code
|
|
||||||
has_head = false,
|
|
||||||
head_bone = "head",
|
|
||||||
|
|
||||||
swap_y_with_x = true,
|
|
||||||
reverse_head_yaw = true,
|
|
||||||
|
|
||||||
head_bone_pos_y = 2.4,
|
|
||||||
head_bone_pos_z = 0,
|
|
||||||
|
|
||||||
head_height_offset = 1.1,
|
|
||||||
head_direction_offset = 0,
|
|
||||||
head_pitch_modifier = 0,
|
|
||||||
--end head code
|
|
||||||
|
|
||||||
visual_size = {x=2.75, y=2.75},
|
|
||||||
makes_footstep_sound = true,
|
|
||||||
walk_velocity = 1.2,
|
|
||||||
run_velocity = 4,
|
|
||||||
damage = 2,
|
|
||||||
reach = 8,
|
|
||||||
view_range = 16,
|
|
||||||
fear_height = 4,
|
|
||||||
attack_type = "projectile",
|
|
||||||
arrow = "mcl_bows:arrow_entity",
|
|
||||||
sounds = {
|
|
||||||
random = "mobs_mc_pillager_grunt2",
|
|
||||||
war_cry = "mobs_mc_pillager_grunt1",
|
|
||||||
death = "mobs_mc_pillager_ow2",
|
|
||||||
damage = "mobs_mc_pillager_ow1",
|
|
||||||
distance = 16,
|
|
||||||
},
|
|
||||||
textures = {
|
|
||||||
{
|
|
||||||
"mobs_mc_pillager.png", -- Skin
|
|
||||||
"mcl_bows_crossbow_3.png^[resize:16x16", -- Wielded item
|
|
||||||
}
|
|
||||||
},
|
|
||||||
drops = {
|
|
||||||
{
|
|
||||||
name = "mcl_bows:arrow",
|
|
||||||
chance = 1,
|
|
||||||
min = 0,
|
|
||||||
max = 2,
|
|
||||||
looting = "common",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name = "mcl_bows:crossbow",
|
|
||||||
chance = 100 / 8.5,
|
|
||||||
min = 1,
|
|
||||||
max = 1,
|
|
||||||
looting = "rare",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
animation = {
|
|
||||||
unloaded_walk_start = 1,
|
|
||||||
unloaded_walk_end = 40,
|
|
||||||
unloaded_stand_start = 41,
|
|
||||||
unloaded_stand_end = 60,
|
|
||||||
|
|
||||||
reload_stand_speed = 20,
|
|
||||||
reload_stand_start = 61,
|
|
||||||
reload_stand_end = 100,
|
|
||||||
|
|
||||||
stand_speed = 6,
|
|
||||||
stand_start = 101,
|
|
||||||
stand_end = 109,
|
|
||||||
|
|
||||||
walk_speed = 25,
|
|
||||||
walk_start = 111,
|
|
||||||
walk_end = 150,
|
|
||||||
run_speed = 40,
|
|
||||||
run_start = 111,
|
|
||||||
run_end = 150,
|
|
||||||
|
|
||||||
reload_run_speed = 20,
|
|
||||||
reload_run_start = 151,
|
|
||||||
reload_run_end = 190,
|
|
||||||
|
|
||||||
die_speed = 15,
|
|
||||||
die_start = 191,
|
|
||||||
die_end = 192,
|
|
||||||
die_loop = false,
|
|
||||||
|
|
||||||
stand_unloaded_start = 40,
|
|
||||||
stand_unloaded_end = 59,
|
|
||||||
},
|
|
||||||
shoot_arrow = function(self, pos, dir)
|
|
||||||
minetest.sound_play("mcl_bows_crossbow_shoot", {object = self.object, max_hear_distance=16}, true)
|
|
||||||
local props = self.object:get_properties()
|
|
||||||
props.textures[2] = "mcl_bows_crossbow_0.png^[resize:16x16"
|
|
||||||
self.object:set_properties(props)
|
|
||||||
local old_anim = self.current_animation
|
|
||||||
if old_anim == "run" then
|
|
||||||
mobs.set_mob_animation(self, "reload_run")
|
|
||||||
end
|
|
||||||
if old_anim == "stand" then
|
|
||||||
mobs.set_mob_animation(self, "reload_stand")
|
|
||||||
end
|
|
||||||
self.current_animation = old_anim -- Mobs Redo will imediately reset the animation otherwise
|
|
||||||
minetest.after(1, reload, self)
|
|
||||||
minetest.after(2, reset_animation, self, old_anim)
|
|
||||||
mobs.shoot_projectile_handling(
|
|
||||||
"mcl_bows:arrow", pos, dir, self.object:get_yaw(),
|
|
||||||
self.object, 30, math.random(3,4))
|
|
||||||
|
|
||||||
-- While we are at it, change the sounds since there is no way to do this in Mobs Redo
|
|
||||||
if self.sounds and self.sounds.random then
|
|
||||||
self.sounds = table.copy(self.sounds)
|
|
||||||
self.sounds.random = "mobs_mc_pillager_grunt" .. math.random(2)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
mobs:register_mob("mobs_mc:pillager", pillager)
|
|
||||||
mobs:register_egg("mobs_mc:pillager", S("Pillager"), "mobs_mc_spawn_icon_pillager.png", 0)
|
|
|
@ -2,27 +2,118 @@
|
||||||
|
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
local mob_name = "mobs_mc:rabbit"
|
local rabbit = {
|
||||||
|
description = S("Rabbit"),
|
||||||
|
type = "animal",
|
||||||
|
spawn_class = "passive",
|
||||||
|
passive = true,
|
||||||
|
reach = 1,
|
||||||
|
rotate = 270,
|
||||||
|
hp_min = 3,
|
||||||
|
hp_max = 3,
|
||||||
|
xp_min = 1,
|
||||||
|
xp_max = 3,
|
||||||
|
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
||||||
|
|
||||||
local textures = {
|
visual = "mesh",
|
||||||
|
mesh = "mobs_mc_rabbit.b3d",
|
||||||
|
textures = {
|
||||||
{"mobs_mc_rabbit_brown.png"},
|
{"mobs_mc_rabbit_brown.png"},
|
||||||
{"mobs_mc_rabbit_gold.png"},
|
{"mobs_mc_rabbit_gold.png"},
|
||||||
{"mobs_mc_rabbit_white.png"},
|
{"mobs_mc_rabbit_white.png"},
|
||||||
{"mobs_mc_rabbit_white_splotched.png"},
|
{"mobs_mc_rabbit_white_splotched.png"},
|
||||||
{"mobs_mc_rabbit_salt.png"},
|
{"mobs_mc_rabbit_salt.png"},
|
||||||
{"mobs_mc_rabbit_black.png"},
|
{"mobs_mc_rabbit_black.png"},
|
||||||
}
|
},
|
||||||
|
visual_size = {x=1.5, y=1.5},
|
||||||
local sounds = {
|
sounds = {
|
||||||
random = "mobs_mc_rabbit_random",
|
random = "mobs_mc_rabbit_random",
|
||||||
damage = "mobs_mc_rabbit_hurt",
|
damage = "mobs_mc_rabbit_hurt",
|
||||||
death = "mobs_mc_rabbit_death",
|
death = "mobs_mc_rabbit_death",
|
||||||
attack = "mobs_mc_rabbit_attack",
|
attack = "mobs_mc_rabbit_attack",
|
||||||
eat = "mobs_mc_animal_eat_generic",
|
eat = "mobs_mc_animal_eat_generic",
|
||||||
distance = 16,
|
distance = 16,
|
||||||
|
},
|
||||||
|
makes_footstep_sound = false,
|
||||||
|
walk_velocity = 1,
|
||||||
|
run_velocity = 3.7,
|
||||||
|
follow_velocity = 1.1,
|
||||||
|
floats = 1,
|
||||||
|
runaway = true,
|
||||||
|
jump = true,
|
||||||
|
drops = {
|
||||||
|
{name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, max = 1, looting = "common",},
|
||||||
|
{name = mobs_mc.items.rabbit_hide, chance = 1, min = 0, max = 1, looting = "common",},
|
||||||
|
{name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1, looting = "rare", looting_factor = 0.03,},
|
||||||
|
},
|
||||||
|
fear_height = 4,
|
||||||
|
animation = {
|
||||||
|
speed_normal = 25, speed_run = 50,
|
||||||
|
stand_start = 0, stand_end = 0,
|
||||||
|
walk_start = 0, walk_end = 20,
|
||||||
|
run_start = 0, run_end = 20,
|
||||||
|
},
|
||||||
|
-- Follow (yellow) dangelions, carrots and golden carrots
|
||||||
|
follow = mobs_mc.follow.rabbit,
|
||||||
|
view_range = 8,
|
||||||
|
-- Eat carrots and reduce their growth stage by 1
|
||||||
|
replace_rate = 10,
|
||||||
|
replace_what = mobs_mc.replace.rabbit,
|
||||||
|
on_rightclick = function(self, clicker)
|
||||||
|
-- Feed, tame protect or capture
|
||||||
|
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
||||||
|
end,
|
||||||
|
do_custom = function(self)
|
||||||
|
-- Easter egg: Change texture if rabbit is named “Toast”
|
||||||
|
if self.nametag == "Toast" and not self._has_toast_texture then
|
||||||
|
self._original_rabbit_texture = self.base_texture
|
||||||
|
self.base_texture = { "mobs_mc_rabbit_toast.png" }
|
||||||
|
self.object:set_properties({ textures = self.base_texture })
|
||||||
|
self._has_toast_texture = true
|
||||||
|
elseif self.nametag ~= "Toast" and self._has_toast_texture then
|
||||||
|
self.base_texture = self._original_rabbit_texture
|
||||||
|
self.object:set_properties({ textures = self.base_texture })
|
||||||
|
self._has_toast_texture = false
|
||||||
|
end
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
local biome_list = {
|
mobs:register_mob("mobs_mc:rabbit", rabbit)
|
||||||
|
|
||||||
|
-- The killer bunny (Only with spawn egg)
|
||||||
|
local killer_bunny = table.copy(rabbit)
|
||||||
|
killer_bunny.description = S("Killer Bunny")
|
||||||
|
killer_bunny.type = "monster"
|
||||||
|
killer_bunny.spawn_class = "hostile"
|
||||||
|
killer_bunny.attack_type = "dogfight"
|
||||||
|
killer_bunny.specific_attack = { "player", "mobs_mc:wolf", "mobs_mc:dog" }
|
||||||
|
killer_bunny.damage = 8
|
||||||
|
killer_bunny.passive = false
|
||||||
|
-- 8 armor points
|
||||||
|
killer_bunny.armor = 50
|
||||||
|
killer_bunny.textures = { "mobs_mc_rabbit_caerbannog.png" }
|
||||||
|
killer_bunny.view_range = 16
|
||||||
|
killer_bunny.replace_rate = nil
|
||||||
|
killer_bunny.replace_what = nil
|
||||||
|
killer_bunny.on_rightclick = nil
|
||||||
|
killer_bunny.run_velocity = 6
|
||||||
|
killer_bunny.do_custom = function(self)
|
||||||
|
if not self._killer_bunny_nametag_set then
|
||||||
|
self.nametag = S("The Killer Bunny")
|
||||||
|
self._killer_bunny_nametag_set = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
||||||
|
|
||||||
|
-- Mob spawning rules.
|
||||||
|
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
|
||||||
|
|
||||||
|
mobs:spawn_specific(
|
||||||
|
"mobs_mc:rabbit",
|
||||||
|
"overworld",
|
||||||
|
"ground",
|
||||||
|
{
|
||||||
"FlowerForest_beach",
|
"FlowerForest_beach",
|
||||||
"Forest_beach",
|
"Forest_beach",
|
||||||
"StoneBeach",
|
"StoneBeach",
|
||||||
|
@ -70,20 +161,41 @@ local biome_list = {
|
||||||
"MesaBryce",
|
"MesaBryce",
|
||||||
"JungleEdge",
|
"JungleEdge",
|
||||||
"SavannaM",
|
"SavannaM",
|
||||||
|
},
|
||||||
|
9,
|
||||||
|
minetest.LIGHT_MAX+1,
|
||||||
|
30,
|
||||||
|
15000,
|
||||||
|
8,
|
||||||
|
mobs_mc.spawn_height.overworld_min,
|
||||||
|
mobs_mc.spawn_height.overworld_max)
|
||||||
|
|
||||||
|
--[[
|
||||||
|
local spawn = {
|
||||||
|
name = "mobs_mc:rabbit",
|
||||||
|
neighbors = {"air"},
|
||||||
|
chance = 15000,
|
||||||
|
active_object_count = 10,
|
||||||
|
min_light = 0,
|
||||||
|
max_light = minetest.LIGHT_MAX+1,
|
||||||
|
min_height = mobs_mc.spawn_height.overworld_min,
|
||||||
|
max_height = mobs_mc.spawn_height.overworld_max,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function spawn_rabbit(pos)
|
local spawn_desert = table.copy(spawn)
|
||||||
local biome_data = minetest.get_biome_data(pos)
|
spawn_desert.nodes = mobs_mc.spawn.desert
|
||||||
local biome_name = biome_data and minetest.get_biome_name(biome_data.biome) or ""
|
spawn_desert.on_spawn = function(self, pos)
|
||||||
local mob = minetest.add_entity(pos, mob_name)
|
local texture = "mobs_mc_rabbit_gold.png"
|
||||||
if not mob then return end
|
self.base_texture = { "mobs_mc_rabbit_gold.png" }
|
||||||
local self = mob:get_luaentity()
|
self.object:set_properties({textures = self.base_texture})
|
||||||
|
end
|
||||||
|
mobs:spawn(spawn_desert)
|
||||||
|
|
||||||
|
local spawn_snow = table.copy(spawn)
|
||||||
|
spawn_snow.nodes = mobs_mc.spawn.snow
|
||||||
|
spawn_snow.on_spawn = function(self, pos)
|
||||||
local texture
|
local texture
|
||||||
if biome_name:find("Desert") then
|
|
||||||
texture = "mobs_mc_rabbit_gold.png"
|
|
||||||
else
|
|
||||||
local r = math.random(1, 100)
|
local r = math.random(1, 100)
|
||||||
if biome_name:find("Ice") or biome_name:find("snow") or biome_name:find("Cold") then
|
|
||||||
-- 80% white fur
|
-- 80% white fur
|
||||||
if r <= 80 then
|
if r <= 80 then
|
||||||
texture = "mobs_mc_rabbit_white.png"
|
texture = "mobs_mc_rabbit_white.png"
|
||||||
|
@ -91,7 +203,16 @@ local function spawn_rabbit(pos)
|
||||||
else
|
else
|
||||||
texture = "mobs_mc_rabbit_white_splotched.png"
|
texture = "mobs_mc_rabbit_white_splotched.png"
|
||||||
end
|
end
|
||||||
else
|
self.base_texture = { texture }
|
||||||
|
self.object:set_properties({textures = self.base_texture})
|
||||||
|
end
|
||||||
|
mobs:spawn(spawn_snow)
|
||||||
|
|
||||||
|
local spawn_grass = table.copy(spawn)
|
||||||
|
spawn_grass.nodes = mobs_mc.spawn.grassland
|
||||||
|
spawn_grass.on_spawn = function(self, pos)
|
||||||
|
local texture
|
||||||
|
local r = math.random(1, 100)
|
||||||
-- 50% brown fur
|
-- 50% brown fur
|
||||||
if r <= 50 then
|
if r <= 50 then
|
||||||
texture = "mobs_mc_rabbit_brown.png"
|
texture = "mobs_mc_rabbit_brown.png"
|
||||||
|
@ -102,117 +223,11 @@ local function spawn_rabbit(pos)
|
||||||
else
|
else
|
||||||
texture = "mobs_mc_rabbit_black.png"
|
texture = "mobs_mc_rabbit_black.png"
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
self.base_texture = { texture }
|
self.base_texture = { texture }
|
||||||
self.object:set_properties({textures = {texture}})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function do_custom_rabbit(self)
|
|
||||||
-- Easter egg: Change texture if rabbit is named “Toast”
|
|
||||||
if self.nametag == "Toast" and not self._has_toast_texture then
|
|
||||||
self._original_rabbit_texture = self.base_texture
|
|
||||||
self.base_texture = { "mobs_mc_rabbit_toast.png" }
|
|
||||||
self.object:set_properties({textures = self.base_texture})
|
self.object:set_properties({textures = self.base_texture})
|
||||||
self._has_toast_texture = true
|
|
||||||
elseif self.nametag ~= "Toast" and self._has_toast_texture then
|
|
||||||
self.base_texture = self._original_rabbit_texture
|
|
||||||
self.object:set_properties({ textures = self.base_texture })
|
|
||||||
self._has_toast_texture = false
|
|
||||||
end
|
end
|
||||||
end
|
mobs:spawn(spawn_grass)
|
||||||
|
]]--
|
||||||
local rabbit = {
|
|
||||||
description = S("Rabbit"),
|
|
||||||
type = "animal",
|
|
||||||
spawn_class = "passive",
|
|
||||||
passive = true,
|
|
||||||
reach = 1,
|
|
||||||
rotate = 270,
|
|
||||||
hp_min = 3,
|
|
||||||
hp_max = 3,
|
|
||||||
xp_min = 1,
|
|
||||||
xp_max = 3,
|
|
||||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
|
||||||
visual = "mesh",
|
|
||||||
mesh = "mobs_mc_rabbit.b3d",
|
|
||||||
textures = textures,
|
|
||||||
visual_size = {x=1.5, y=1.5},
|
|
||||||
sounds = sounds,
|
|
||||||
makes_footstep_sound = false,
|
|
||||||
walk_velocity = 1,
|
|
||||||
run_velocity = 3.7,
|
|
||||||
follow_velocity = 1.1,
|
|
||||||
floats = 1,
|
|
||||||
runaway = true,
|
|
||||||
jump = true,
|
|
||||||
drops = {
|
|
||||||
{name = mobs_mc.items.rabbit_raw, chance = 1, min = 0, max = 1, looting = "common",},
|
|
||||||
{name = mobs_mc.items.rabbit_hide, chance = 1, min = 0, max = 1, looting = "common",},
|
|
||||||
{name = mobs_mc.items.rabbit_foot, chance = 10, min = 0, max = 1, looting = "rare", looting_factor = 0.03,},
|
|
||||||
},
|
|
||||||
fear_height = 4,
|
|
||||||
animation = {
|
|
||||||
speed_normal = 25, speed_run = 50,
|
|
||||||
stand_start = 0, stand_end = 0,
|
|
||||||
walk_start = 0, walk_end = 20,
|
|
||||||
run_start = 0, run_end = 20,
|
|
||||||
},
|
|
||||||
-- Follow (yellow) dangelions, carrots and golden carrots
|
|
||||||
follow = mobs_mc.follow.rabbit,
|
|
||||||
view_range = 8,
|
|
||||||
-- Eat carrots and reduce their growth stage by 1
|
|
||||||
replace_rate = 10,
|
|
||||||
replace_what = mobs_mc.replace.rabbit,
|
|
||||||
on_rightclick = function(self, clicker)
|
|
||||||
-- Feed, tame protect or capture
|
|
||||||
if mobs:feed_tame(self, clicker, 1, true, true) then return end
|
|
||||||
end,
|
|
||||||
do_custom = do_custom_rabbit,
|
|
||||||
spawn = spawn_rabbit
|
|
||||||
}
|
|
||||||
|
|
||||||
mobs:register_mob(mob_name, rabbit)
|
|
||||||
|
|
||||||
-- The killer bunny (Only with spawn egg)
|
|
||||||
local killer_bunny = table.copy(rabbit)
|
|
||||||
killer_bunny.description = S("Killer Bunny")
|
|
||||||
killer_bunny.type = "monster"
|
|
||||||
killer_bunny.spawn_class = "hostile"
|
|
||||||
killer_bunny.attack_type = "dogfight"
|
|
||||||
killer_bunny.specific_attack = { "player", "mobs_mc:wolf", "mobs_mc:dog" }
|
|
||||||
killer_bunny.damage = 8
|
|
||||||
killer_bunny.passive = false
|
|
||||||
-- 8 armor points
|
|
||||||
killer_bunny.armor = 50
|
|
||||||
killer_bunny.textures = { "mobs_mc_rabbit_caerbannog.png" }
|
|
||||||
killer_bunny.view_range = 16
|
|
||||||
killer_bunny.replace_rate = nil
|
|
||||||
killer_bunny.replace_what = nil
|
|
||||||
killer_bunny.on_rightclick = nil
|
|
||||||
killer_bunny.run_velocity = 6
|
|
||||||
killer_bunny.do_custom = function(self)
|
|
||||||
if not self._killer_bunny_nametag_set then
|
|
||||||
self.nametag = S("The Killer Bunny")
|
|
||||||
self._killer_bunny_nametag_set = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
|
||||||
|
|
||||||
-- Mob spawning rules.
|
|
||||||
-- Different skins depending on spawn location <- we customized spawn function
|
|
||||||
|
|
||||||
mobs:spawn_setup({
|
|
||||||
name = mob_name,
|
|
||||||
min_light = 9,
|
|
||||||
chance = 1000,
|
|
||||||
aoc = 8,
|
|
||||||
biomes = biome_list,
|
|
||||||
group_size_max = 1,
|
|
||||||
baby_min = 1,
|
|
||||||
baby_max = 2,
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Spawn egg
|
-- Spawn egg
|
||||||
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
||||||
|
|
|
@ -87,11 +87,11 @@ mobs:register_mob("mobs_mc:sheep", {
|
||||||
swap_y_with_x = false,
|
swap_y_with_x = false,
|
||||||
reverse_head_yaw = false,
|
reverse_head_yaw = false,
|
||||||
|
|
||||||
head_bone_pos_y = 0,
|
head_bone_pos_y = 3.6,
|
||||||
head_bone_pos_z = 0,
|
head_bone_pos_z = -0.6,
|
||||||
|
|
||||||
head_height_offset = 1.2,
|
head_height_offset = 1.0525,
|
||||||
head_direction_offset = 0,
|
head_direction_offset = 0.5,
|
||||||
head_pitch_modifier = 0,
|
head_pitch_modifier = 0,
|
||||||
--end head code
|
--end head code
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ mobs:register_mob("mobs_mc:sheep", {
|
||||||
},
|
},
|
||||||
animation = {
|
animation = {
|
||||||
speed_normal = 25, run_speed = 65,
|
speed_normal = 25, run_speed = 65,
|
||||||
stand_start = 0, stand_end = 0,
|
stand_start = 40, stand_end = 80,
|
||||||
walk_start = 0, walk_end = 40,
|
walk_start = 0, walk_end = 40,
|
||||||
run_start = 0, run_end = 40,
|
run_start = 0, run_end = 40,
|
||||||
},
|
},
|
||||||
|
@ -330,24 +330,6 @@ mobs:register_mob("mobs_mc:sheep", {
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
on_spawn = function(self)
|
|
||||||
if self.baby then
|
|
||||||
self.animation = table.copy(self.animation)
|
|
||||||
self.animation.stand_start = 81
|
|
||||||
self.animation.stand_end = 81
|
|
||||||
self.animation.walk_start = 81
|
|
||||||
self.animation.walk_end = 121
|
|
||||||
self.animation.run_start = 81
|
|
||||||
self.animation.run_end = 121
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
on_grown = function(self)
|
|
||||||
self.animation = nil
|
|
||||||
local anim = self.current_animation
|
|
||||||
self.current_animation = nil -- Mobs Redo does nothing otherwise
|
|
||||||
mobs.set_mob_animation(self, anim)
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
mobs:spawn_specific(
|
mobs:spawn_specific(
|
||||||
"mobs_mc:sheep",
|
"mobs_mc:sheep",
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
--################### SILVERFISH
|
--################### SILVERFISH
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
local PLAYER_SCAN_RADIUS = 5
|
|
||||||
|
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
mobs:register_mob("mobs_mc:silverfish", {
|
mobs:register_mob("mobs_mc:silverfish", {
|
||||||
|
@ -48,20 +46,6 @@ mobs:register_mob("mobs_mc:silverfish", {
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
attack_type = "punch",
|
attack_type = "punch",
|
||||||
damage = 1,
|
damage = 1,
|
||||||
do_custom = function(self, dtime)
|
|
||||||
self.do_custom_time = (self.do_custom_time or 0) + dtime
|
|
||||||
if self.do_custom_time < 1.5 then return end
|
|
||||||
self.do_custom_time = 0
|
|
||||||
local selfpos = self.object:get_pos()
|
|
||||||
local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS)
|
|
||||||
for _, obj in pairs(objects) do
|
|
||||||
if obj:is_player() and not minetest.is_creative_enabled(obj:get_player_name()) then
|
|
||||||
self.attacking = obj
|
|
||||||
mobs.group_attack_initialization(self)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
})
|
})
|
||||||
|
|
||||||
mobs:register_egg("mobs_mc:silverfish", S("Silverfish"), "mobs_mc_spawn_icon_silverfish.png", 0)
|
mobs:register_egg("mobs_mc:silverfish", S("Silverfish"), "mobs_mc_spawn_icon_silverfish.png", 0)
|
||||||
|
|
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 477 B |
|
@ -712,11 +712,6 @@ local trade_inventory = {
|
||||||
elseif listname == "output" then
|
elseif listname == "output" then
|
||||||
if not trader_exists(player:get_player_name()) then
|
if not trader_exists(player:get_player_name()) then
|
||||||
return 0
|
return 0
|
||||||
-- Begin Award Code
|
|
||||||
-- May need to be moved if award gets unlocked in the wrong cases.
|
|
||||||
elseif trader_exists(player:get_player_name()) then
|
|
||||||
awards.unlock(player:get_player_name(), "mcl:whatAdeal")
|
|
||||||
-- End Award Code
|
|
||||||
end
|
end
|
||||||
-- Only allow taking full stack
|
-- Only allow taking full stack
|
||||||
local count = stack:get_count()
|
local count = stack:get_count()
|
||||||
|
|
|
@ -9,95 +9,6 @@ local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
--################### ZOMBIE
|
--################### ZOMBIE
|
||||||
--###################
|
--###################
|
||||||
|
|
||||||
local husk_biomes = {
|
|
||||||
"Desert",
|
|
||||||
"SavannaM",
|
|
||||||
"Savanna",
|
|
||||||
"Savanna_beach",
|
|
||||||
}
|
|
||||||
|
|
||||||
local zombie_biomes = {
|
|
||||||
"FlowerForest_underground",
|
|
||||||
"JungleEdge_underground",
|
|
||||||
"StoneBeach_underground",
|
|
||||||
"MesaBryce_underground",
|
|
||||||
"Mesa_underground",
|
|
||||||
"RoofedForest_underground",
|
|
||||||
"Jungle_underground",
|
|
||||||
"Swampland_underground",
|
|
||||||
"MushroomIsland_underground",
|
|
||||||
"BirchForest_underground",
|
|
||||||
"Plains_underground",
|
|
||||||
"MesaPlateauF_underground",
|
|
||||||
"ExtremeHills_underground",
|
|
||||||
"MegaSpruceTaiga_underground",
|
|
||||||
"BirchForestM_underground",
|
|
||||||
"SavannaM_underground",
|
|
||||||
"MesaPlateauFM_underground",
|
|
||||||
"Desert_underground",
|
|
||||||
"Savanna_underground",
|
|
||||||
"Forest_underground",
|
|
||||||
"SunflowerPlains_underground",
|
|
||||||
"ColdTaiga_underground",
|
|
||||||
"IcePlains_underground",
|
|
||||||
"IcePlainsSpikes_underground",
|
|
||||||
"MegaTaiga_underground",
|
|
||||||
"Taiga_underground",
|
|
||||||
"ExtremeHills+_underground",
|
|
||||||
"JungleM_underground",
|
|
||||||
"ExtremeHillsM_underground",
|
|
||||||
"JungleEdgeM_underground",
|
|
||||||
"Mesa",
|
|
||||||
"FlowerForest",
|
|
||||||
"Swampland",
|
|
||||||
"Taiga",
|
|
||||||
"ExtremeHills",
|
|
||||||
"Jungle",
|
|
||||||
"Savanna",
|
|
||||||
"BirchForest",
|
|
||||||
"MegaSpruceTaiga",
|
|
||||||
"MegaTaiga",
|
|
||||||
"ExtremeHills+",
|
|
||||||
"Forest",
|
|
||||||
"Plains",
|
|
||||||
"Desert",
|
|
||||||
"ColdTaiga",
|
|
||||||
"MushroomIsland",
|
|
||||||
"IcePlainsSpikes",
|
|
||||||
"SunflowerPlains",
|
|
||||||
"IcePlains",
|
|
||||||
"RoofedForest",
|
|
||||||
"ExtremeHills+_snowtop",
|
|
||||||
"MesaPlateauFM_grasstop",
|
|
||||||
"JungleEdgeM",
|
|
||||||
"ExtremeHillsM",
|
|
||||||
"JungleM",
|
|
||||||
"BirchForestM",
|
|
||||||
"MesaPlateauF",
|
|
||||||
"MesaPlateauFM",
|
|
||||||
"MesaPlateauF_grasstop",
|
|
||||||
"MesaBryce",
|
|
||||||
"JungleEdge",
|
|
||||||
"SavannaM",
|
|
||||||
"FlowerForest_beach",
|
|
||||||
"Forest_beach",
|
|
||||||
"StoneBeach",
|
|
||||||
"ColdTaiga_beach_water",
|
|
||||||
"Taiga_beach",
|
|
||||||
"Savanna_beach",
|
|
||||||
"Plains_beach",
|
|
||||||
"ExtremeHills_beach",
|
|
||||||
"ColdTaiga_beach",
|
|
||||||
"Swampland_shore",
|
|
||||||
"MushroomIslandShore",
|
|
||||||
"JungleM_shore",
|
|
||||||
"Jungle_shore",
|
|
||||||
"MesaPlateauFM_sandlevel",
|
|
||||||
"MesaPlateauF_sandlevel",
|
|
||||||
"MesaBryce_sandlevel",
|
|
||||||
"Mesa_sandlevel",
|
|
||||||
}
|
|
||||||
|
|
||||||
local drops_common = {
|
local drops_common = {
|
||||||
{name = mobs_mc.items.rotten_flesh,
|
{name = mobs_mc.items.rotten_flesh,
|
||||||
chance = 1,
|
chance = 1,
|
||||||
|
@ -204,7 +115,6 @@ local zombie = {
|
||||||
attack_type = "punch",
|
attack_type = "punch",
|
||||||
punch_timer_cooloff = 0.5,
|
punch_timer_cooloff = 0.5,
|
||||||
harmed_by_heal = true,
|
harmed_by_heal = true,
|
||||||
spawn_with_armor = true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mobs:register_mob("mobs_mc:zombie", zombie)
|
mobs:register_mob("mobs_mc:zombie", zombie)
|
||||||
|
@ -256,36 +166,230 @@ baby_husk.child = 1
|
||||||
|
|
||||||
mobs:register_mob("mobs_mc:baby_husk", baby_husk)
|
mobs:register_mob("mobs_mc:baby_husk", baby_husk)
|
||||||
|
|
||||||
|
|
||||||
-- Spawning
|
-- Spawning
|
||||||
|
|
||||||
mobs:spawn_setup({
|
mobs:spawn_specific(
|
||||||
name = "mobs_mc:zombie",
|
"mobs_mc:zombie",
|
||||||
biomes = zombie_biomes,
|
"overworld",
|
||||||
max_light = 7,
|
"ground",
|
||||||
chance = 2000,
|
{
|
||||||
})
|
"FlowerForest_underground",
|
||||||
|
"JungleEdge_underground",
|
||||||
|
"StoneBeach_underground",
|
||||||
|
"MesaBryce_underground",
|
||||||
|
"Mesa_underground",
|
||||||
|
"RoofedForest_underground",
|
||||||
|
"Jungle_underground",
|
||||||
|
"Swampland_underground",
|
||||||
|
"MushroomIsland_underground",
|
||||||
|
"BirchForest_underground",
|
||||||
|
"Plains_underground",
|
||||||
|
"MesaPlateauF_underground",
|
||||||
|
"ExtremeHills_underground",
|
||||||
|
"MegaSpruceTaiga_underground",
|
||||||
|
"BirchForestM_underground",
|
||||||
|
"SavannaM_underground",
|
||||||
|
"MesaPlateauFM_underground",
|
||||||
|
"Desert_underground",
|
||||||
|
"Savanna_underground",
|
||||||
|
"Forest_underground",
|
||||||
|
"SunflowerPlains_underground",
|
||||||
|
"ColdTaiga_underground",
|
||||||
|
"IcePlains_underground",
|
||||||
|
"IcePlainsSpikes_underground",
|
||||||
|
"MegaTaiga_underground",
|
||||||
|
"Taiga_underground",
|
||||||
|
"ExtremeHills+_underground",
|
||||||
|
"JungleM_underground",
|
||||||
|
"ExtremeHillsM_underground",
|
||||||
|
"JungleEdgeM_underground",
|
||||||
|
"Mesa",
|
||||||
|
"FlowerForest",
|
||||||
|
"Swampland",
|
||||||
|
"Taiga",
|
||||||
|
"ExtremeHills",
|
||||||
|
"Jungle",
|
||||||
|
"Savanna",
|
||||||
|
"BirchForest",
|
||||||
|
"MegaSpruceTaiga",
|
||||||
|
"MegaTaiga",
|
||||||
|
"ExtremeHills+",
|
||||||
|
"Forest",
|
||||||
|
"Plains",
|
||||||
|
"Desert",
|
||||||
|
"ColdTaiga",
|
||||||
|
"MushroomIsland",
|
||||||
|
"IcePlainsSpikes",
|
||||||
|
"SunflowerPlains",
|
||||||
|
"IcePlains",
|
||||||
|
"RoofedForest",
|
||||||
|
"ExtremeHills+_snowtop",
|
||||||
|
"MesaPlateauFM_grasstop",
|
||||||
|
"JungleEdgeM",
|
||||||
|
"ExtremeHillsM",
|
||||||
|
"JungleM",
|
||||||
|
"BirchForestM",
|
||||||
|
"MesaPlateauF",
|
||||||
|
"MesaPlateauFM",
|
||||||
|
"MesaPlateauF_grasstop",
|
||||||
|
"MesaBryce",
|
||||||
|
"JungleEdge",
|
||||||
|
"SavannaM",
|
||||||
|
"FlowerForest_beach",
|
||||||
|
"Forest_beach",
|
||||||
|
"StoneBeach",
|
||||||
|
"ColdTaiga_beach_water",
|
||||||
|
"Taiga_beach",
|
||||||
|
"Savanna_beach",
|
||||||
|
"Plains_beach",
|
||||||
|
"ExtremeHills_beach",
|
||||||
|
"ColdTaiga_beach",
|
||||||
|
"Swampland_shore",
|
||||||
|
"MushroomIslandShore",
|
||||||
|
"JungleM_shore",
|
||||||
|
"Jungle_shore",
|
||||||
|
"MesaPlateauFM_sandlevel",
|
||||||
|
"MesaPlateauF_sandlevel",
|
||||||
|
"MesaBryce_sandlevel",
|
||||||
|
"Mesa_sandlevel",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
7,
|
||||||
|
30,
|
||||||
|
6000,
|
||||||
|
4,
|
||||||
|
mobs_mc.spawn_height.overworld_min,
|
||||||
|
mobs_mc.spawn_height.overworld_max)
|
||||||
-- Baby zombie is 20 times less likely than regular zombies
|
-- Baby zombie is 20 times less likely than regular zombies
|
||||||
mobs:spawn_setup({
|
mobs:spawn_specific(
|
||||||
name = "mobs_mc:baby_zombie",
|
"mobs_mc:baby_zombie",
|
||||||
biomes = zombie_biomes,
|
"overworld",
|
||||||
max_lignt = 7,
|
"ground",
|
||||||
chance = 100,
|
{
|
||||||
})
|
"FlowerForest_underground",
|
||||||
|
"JungleEdge_underground",
|
||||||
|
"StoneBeach_underground",
|
||||||
|
"MesaBryce_underground",
|
||||||
|
"Mesa_underground",
|
||||||
|
"RoofedForest_underground",
|
||||||
|
"Jungle_underground",
|
||||||
|
"Swampland_underground",
|
||||||
|
"MushroomIsland_underground",
|
||||||
|
"BirchForest_underground",
|
||||||
|
"Plains_underground",
|
||||||
|
"MesaPlateauF_underground",
|
||||||
|
"ExtremeHills_underground",
|
||||||
|
"MegaSpruceTaiga_underground",
|
||||||
|
"BirchForestM_underground",
|
||||||
|
"SavannaM_underground",
|
||||||
|
"MesaPlateauFM_underground",
|
||||||
|
"Desert_underground",
|
||||||
|
"Savanna_underground",
|
||||||
|
"Forest_underground",
|
||||||
|
"SunflowerPlains_underground",
|
||||||
|
"ColdTaiga_underground",
|
||||||
|
"IcePlains_underground",
|
||||||
|
"IcePlainsSpikes_underground",
|
||||||
|
"MegaTaiga_underground",
|
||||||
|
"Taiga_underground",
|
||||||
|
"ExtremeHills+_underground",
|
||||||
|
"JungleM_underground",
|
||||||
|
"ExtremeHillsM_underground",
|
||||||
|
"JungleEdgeM_underground",
|
||||||
|
"Mesa",
|
||||||
|
"FlowerForest",
|
||||||
|
"Swampland",
|
||||||
|
"Taiga",
|
||||||
|
"ExtremeHills",
|
||||||
|
"Jungle",
|
||||||
|
"Savanna",
|
||||||
|
"BirchForest",
|
||||||
|
"MegaSpruceTaiga",
|
||||||
|
"MegaTaiga",
|
||||||
|
"ExtremeHills+",
|
||||||
|
"Forest",
|
||||||
|
"Plains",
|
||||||
|
"Desert",
|
||||||
|
"ColdTaiga",
|
||||||
|
"MushroomIsland",
|
||||||
|
"IcePlainsSpikes",
|
||||||
|
"SunflowerPlains",
|
||||||
|
"IcePlains",
|
||||||
|
"RoofedForest",
|
||||||
|
"ExtremeHills+_snowtop",
|
||||||
|
"MesaPlateauFM_grasstop",
|
||||||
|
"JungleEdgeM",
|
||||||
|
"ExtremeHillsM",
|
||||||
|
"JungleM",
|
||||||
|
"BirchForestM",
|
||||||
|
"MesaPlateauF",
|
||||||
|
"MesaPlateauFM",
|
||||||
|
"MesaPlateauF_grasstop",
|
||||||
|
"MesaBryce",
|
||||||
|
"JungleEdge",
|
||||||
|
"SavannaM",
|
||||||
|
"FlowerForest_beach",
|
||||||
|
"Forest_beach",
|
||||||
|
"StoneBeach",
|
||||||
|
"ColdTaiga_beach_water",
|
||||||
|
"Taiga_beach",
|
||||||
|
"Savanna_beach",
|
||||||
|
"Plains_beach",
|
||||||
|
"ExtremeHills_beach",
|
||||||
|
"ColdTaiga_beach",
|
||||||
|
"Swampland_shore",
|
||||||
|
"MushroomIslandShore",
|
||||||
|
"JungleM_shore",
|
||||||
|
"Jungle_shore",
|
||||||
|
"MesaPlateauFM_sandlevel",
|
||||||
|
"MesaPlateauF_sandlevel",
|
||||||
|
"MesaBryce_sandlevel",
|
||||||
|
"Mesa_sandlevel",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
7,
|
||||||
|
30,
|
||||||
|
60000,
|
||||||
|
4,
|
||||||
|
mobs_mc.spawn_height.overworld_min,
|
||||||
|
mobs_mc.spawn_height.overworld_max)
|
||||||
|
|
||||||
mobs:spawn_setup({
|
|
||||||
name = "mobs_mc:husk",
|
|
||||||
biomes = husk_biomes,
|
|
||||||
max_light = 7,
|
|
||||||
chance = 2000,
|
|
||||||
})
|
|
||||||
|
|
||||||
mobs:spawn_setup({
|
mobs:spawn_specific(
|
||||||
name = "mobs_mc:baby_husk",
|
"mobs_mc:husk",
|
||||||
biomes = husk_biomes,
|
"overworld",
|
||||||
max_light = 7,
|
"ground",
|
||||||
chance = 100,
|
{
|
||||||
})
|
"Desert",
|
||||||
|
"SavannaM",
|
||||||
|
"Savanna",
|
||||||
|
"Savanna_beach",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
7,
|
||||||
|
30,
|
||||||
|
6500,
|
||||||
|
4,
|
||||||
|
mobs_mc.spawn_height.overworld_min,
|
||||||
|
mobs_mc.spawn_height.overworld_max)
|
||||||
|
mobs:spawn_specific(
|
||||||
|
"mobs_mc:baby_husk",
|
||||||
|
"overworld",
|
||||||
|
"ground",
|
||||||
|
{
|
||||||
|
"Desert",
|
||||||
|
"SavannaM",
|
||||||
|
"Savanna",
|
||||||
|
"Savanna_beach",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
7,
|
||||||
|
30,
|
||||||
|
65000,
|
||||||
|
4,
|
||||||
|
mobs_mc.spawn_height.overworld_min,
|
||||||
|
mobs_mc.spawn_height.overworld_max)
|
||||||
|
|
||||||
-- Spawn eggs
|
-- Spawn eggs
|
||||||
mobs:register_egg("mobs_mc:husk", S("Husk"), "mobs_mc_spawn_icon_husk.png", 0)
|
mobs:register_egg("mobs_mc:husk", S("Husk"), "mobs_mc_spawn_icon_husk.png", 0)
|
||||||
|
|
|
@ -229,7 +229,6 @@ mobs_mc.override.spawn = {
|
||||||
}
|
}
|
||||||
|
|
||||||
-- This table contains important spawn height references for the mob spawn height.
|
-- This table contains important spawn height references for the mob spawn height.
|
||||||
local get_local_settings = function()
|
|
||||||
mobs_mc.override.spawn_height = {
|
mobs_mc.override.spawn_height = {
|
||||||
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
||||||
|
|
||||||
|
@ -245,7 +244,4 @@ local get_local_settings = function()
|
||||||
end_min = mcl_mapgen.end_.min,
|
end_min = mcl_mapgen.end_.min,
|
||||||
end_max = mcl_mapgen.end_.max,
|
end_max = mcl_mapgen.end_.max,
|
||||||
}
|
}
|
||||||
end
|
|
||||||
get_local_settings()
|
|
||||||
mcl_mapgen.register_on_settings_changed(get_local_settings)
|
|
||||||
|
|
||||||
|
|
|
@ -238,8 +238,8 @@ after(5, function(dtime)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_chatcommand("lightning", {
|
minetest.register_chatcommand("lightning", {
|
||||||
params = "[<X> <Y> <Z> | <player name>]",
|
params = "[<X> <Y> <Z>]",
|
||||||
description = S("Let lightning strike at the specified position or player. No parameter will strike yourself."),
|
description = S("Let lightning strike at the specified position or yourself"),
|
||||||
privs = { maphack = true },
|
privs = { maphack = true },
|
||||||
func = function(name, param)
|
func = function(name, param)
|
||||||
local pos = {}
|
local pos = {}
|
||||||
|
@ -247,21 +247,21 @@ minetest.register_chatcommand("lightning", {
|
||||||
pos.x = tonumber(pos.x)
|
pos.x = tonumber(pos.x)
|
||||||
pos.y = tonumber(pos.y)
|
pos.y = tonumber(pos.y)
|
||||||
pos.z = tonumber(pos.z)
|
pos.z = tonumber(pos.z)
|
||||||
local player_to_strike
|
|
||||||
if not (pos.x and pos.y and pos.z) then
|
if not (pos.x and pos.y and pos.z) then
|
||||||
pos = nil
|
pos = nil
|
||||||
player_to_strike = minetest.get_player_by_name(param)
|
|
||||||
if not player_to_strike and param == "" then
|
|
||||||
player_to_strike = minetest.get_player_by_name(name)
|
|
||||||
end
|
end
|
||||||
end
|
if name == "" and pos == nil then
|
||||||
if not player_to_strike and pos == nil then
|
|
||||||
return false, "No position specified and unknown player"
|
return false, "No position specified and unknown player"
|
||||||
end
|
end
|
||||||
if pos then
|
if pos then
|
||||||
lightning.strike(pos)
|
lightning.strike(pos)
|
||||||
elseif player_to_strike then
|
else
|
||||||
lightning.strike(player_to_strike:get_pos())
|
local player = minetest.get_player_by_name(name)
|
||||||
|
if player then
|
||||||
|
lightning.strike(player:get_pos())
|
||||||
|
else
|
||||||
|
return false, S("No position specified and unknown player")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
# textdomain: lightning
|
|
||||||
@1 was struck by lightning.=@1 fuguèt pica·t·da per lo tròn
|
|
||||||
Let lightning strike at the specified position or yourself=Pica lo tròn vès una posicion mencionada o sobre vosautr·e·a·s-mema
|
|
||||||
No position specified and unknown player=Pas de posicion mencionada e jogair·e·a pas conegu·t·da
|
|
|
@ -1,4 +1,4 @@
|
||||||
# textdomain: lightning
|
# textdomain: lightning
|
||||||
@1 was struck by lightning.=@1 a été frappé(e) par la foudre.
|
@1 was struck by lightning.=@1 a été frappé par la foudre.
|
||||||
Let lightning strike at the specified position or player. No parameter will strike yourself.=Fait frapper la foudre sur la position ou le joueur indiqué. Sans paramètre, la foudre frappera sur vous-même.
|
Let lightning strike at the specified position or yourself=Laissez la foudre frapper à la position spécifiée ou sur vous-même
|
||||||
No position specified and unknown player=Aucune position spécifiée et joueur inconnu
|
No position specified and unknown player=Aucune position spécifiée et joueur inconnu
|
||||||
|
|