Compare commits

..

3 Commits

498 changed files with 2884 additions and 11035 deletions

View File

@ -1,12 +0,0 @@
root = true
[*]
end_of_line = lf
[*.lua]
charset = utf8
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
keep_one_space_between_table_and_bracket = false
spaces_around_operators = true

View File

@ -1,36 +0,0 @@
---
name: "Bug report"
about: "File a bug report"
labels:
- unconfirmed
- bug
---
<!--
Thanks for taking the time to fill out this bug report!
Please follow our contributing guidelines first:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
By submitting this issue, you agree to follow our Code of Conduct:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
-->
<!--
What version of MineClone2 are you using? We do not provide support for outdated versions of MineClone2.
Current latest version is listed here, at the top:
https://git.minetest.land/MineClone2/MineClone2/tags
-->
MineClone2 version:
### What happened?
Report about the bug! Please send large log snippets as an attachement file.
### What should happen:
Tell us what should happen!
### Steps to reproduce
Tell us how we can reproduce the bug!

View File

@ -1,26 +0,0 @@
---
name: "Feature request"
about: "File a feature request not in Minecraft"
labels:
- "non-Minecraft feature"
- "needs discussion"
---
<!--
Got a new non-Minecraft feature request? Explain to us why we should consider your idea.
Please follow our contributing guidelines first:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
By submitting this issue, you agree to follow our Code of Conduct:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
-->
### Feature
Tell us about your requested feature not in Minecraft!
### Why
Tell us why should we implement it!

View File

@ -1,25 +0,0 @@
---
name: "Missing Feature request"
about: "File a missing feature request in Minecraft but not in MineClone2"
labels:
- "missing feature"
---
<!--
Thanks for taking the time to fill out this missing feature request!
Please follow our contributing guidelines first:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#rules-about-both-bugs-and-feature-requests
By submitting this issue, you agree to follow our Code of Conduct:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
-->
### Current feature in Minecraft
Tell us about the feature currently in Minecraft! What is it like on Minecraft?
### Current feature in MineClone2
Tell us about the feature currently in MineClone2! What is different?

View File

@ -1,20 +0,0 @@
---
name: "Pull request"
about: "Submit a pull request"
labels:
---
<!--
Please follow our contributing guidelines first:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CONTRIBUTING.md#how-you-can-help-as-a-programmer
By submitting this pull request, you agree to follow our Code of Conduct:
https://git.minetest.land/MineClone2/MineClone2/src/branch/master/CODE_OF_CONDUCT.md
-->
Tell us about your pull request! Reference related issues, if necessary
### Testing
Tell us how to test your changes!

1
.gitignore vendored
View File

@ -4,4 +4,3 @@
*.blend2
*.blend3
/.idea/
*.xcf

View File

@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
ancientmariner_dev@proton.me.
eliasfleckenstein@web.de.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

View File

@ -8,13 +8,12 @@
## Maintainers
* AncientMariner
* Nicu
* cora
## Previous Maintainers
* Fleckenstein
* jordan4ibanez
* cora
## Developers
* bzoss
@ -35,8 +34,8 @@
* SumianVoice
* MrRar
* talamh
* Faerraven / Michieal
* FossFanatic
* Faerraven
* AncientMariner
## Contributors
* Laurent Rocher
@ -96,9 +95,6 @@
* anarquimico
* TheOnlyJoeEnderman
* Ranko Saotome
* Gregor Parzefall
* Wbjitscool
* b3nderman
## MineClone5
* kay27
@ -150,13 +146,11 @@
* jordan4ibanez
* paramat
* cora
* Faerraven / Michieal
## 3D Models
* 22i
* tobyplowy
* epCode
* Faerraven / Michieal
## Textures
* XSSheep
@ -171,7 +165,6 @@
* RandomLegoBrick
* cora
* Faerraven / Michieal
* Nicu
## Translations
* Wuzzy
@ -185,12 +178,9 @@
* Emojigit
* snowyu
* 3raven
* SakuraRiu
## Funders
* 40W
* bauknecht
* Cora
## Special thanks
* celeron55 for creating Minetest
@ -198,4 +188,3 @@
* wsor for working tirelessly in the shadows for the good of all of us, particularly helping with solving contentDB and copyright issues.
* The workaholics who spent way too much time writing for the Minecraft Wiki. It's an invaluable resource for creating this game
* Notch and Jeb for being the major forces behind Minecraft
* Dark Reaven Music (https://soundcloud.com/dark-reaven-music) for the main menu theme (Calmed Cube), which is licensed under https://creativecommons.org/licenses/by-sa/3.0/

View File

@ -74,8 +74,6 @@ Please read <http://minecraft.gamepedia.com/Breaking> to learn how digging times
* `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
* `compostability=X`: Item can be used on a composter block; X (1-100) is the % chance of adding a level of compost
* `leaves=X`: Node will spotaneously decay if no tree trunk nodes remain within 6 blocks distance.
* `leaves_orphan`: See above, these nodes are in the process of decayed.
#### Footnotes

View File

@ -1,21 +0,0 @@
Survive, farm, build, explore, play with friends, and do much more. Inspired by a well known block game, pushing beyond.
How to play:
#### Download Minetest
- Navigate to https://www.minetest.net/ to download the client.
- Once installed, open and select the "Content" tab
#### Install MineClone2 from ContentDB
- Click "Browse Online Content" and filter by Games (select "Games" from the dropdown box)
- Find "MineClone2" (should be first on the list or on the first page)
- Click the [+] button next to MineClone2 and wait for download to finish
- Click "Back to Main Menu"
#### Create new world and play
- Click "Start Game" tab
- At the bottom click the MineClone2 icon (the 2 dirt with grass blocks)
- Click "New", give your world a name
- You can leave seed blank or put in a word of your choice
- Select your new world
- Click "Play Game" and enjoy!

View File

@ -2,6 +2,8 @@
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
Developed by many people. Not developed or endorsed by Mojang AB.
Version: 0.82 (in development)
### Gameplay
You start in a randomly-generated world made entirely of cubes. You can explore
the world and dig and build almost every block in the world to create new
@ -156,7 +158,7 @@ The following features are incomplete:
* Some monsters and animals
* Redstone-related things
* Some special minecarts (hopper and chest minecarts work)
* Special minecarts
* A couple of non-trivial blocks and items
Bonus features (not found in Minecraft):

View File

@ -1,22 +0,0 @@
#File to document release steps with a view to evolving into a script
#Update CREDITS.md
#Update version in game.conf
lua tools/generate_ingame_credits.lua
git add CREDITS.md
git add mods/HUD/mcl_credits/people.lua
git add game.conf
#git add RELEASE.md
git commit -m "Pre-release update credits and set version 0.82.0"
git tag 0.82.0
git push origin 0.82.0
#Update version in game.conf to -SNAPSHOT
git commit -m "Post-release set version 0.82.0-SNAPSHOT"

View File

@ -1,4 +1,3 @@
title = MineClone 2
description = A survival sandbox game. Survive, gather, hunt, build, explore, and do much more.
disallowed_mapgens = v6
version=0.82.0-SNAPSHOT

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 990 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 76 KiB

BIN
menu/overlay.1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
menu/overlay.2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
menu/overlay.3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
menu/overlay.4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
menu/overlay.5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
menu/overlay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

View File

@ -155,6 +155,7 @@ end, true)
minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
if not damage_enabled then return 0 end
if player:get_hp() > 0 then
mt_reason.approved = true
if hp_change < 0 then
mcl_damage.run_damage_callbacks(player, -hp_change, mcl_damage.from_mt(mt_reason))
end
@ -162,7 +163,9 @@ minetest.register_on_player_hpchange(function(player, hp_change, mt_reason)
end, false)
minetest.register_on_dieplayer(function(player, mt_reason)
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
if mt_reason.approved then
mcl_damage.run_death_callbacks(player, mcl_damage.from_mt(mt_reason))
end
minetest.log("action","Player "..player:get_player_name().." died at "..minetest.pos_to_string(vector.round(player:get_pos())))
end)

View File

@ -102,7 +102,6 @@ function check_events(dtime)
for _,e in pairs(mcl_events.registered_events) do
local pp = e.cond_start()
if pp then
--minetest.log("It's gonna start the raid maybe")
for _,p in pairs(pp) do
local start = true
if e.exclusive_to_area then
@ -111,14 +110,11 @@ function check_events(dtime)
end
end
if start then
--minetest.log("It's gonna start the raid definitely")
start_event(p,e)
elseif DBG then
mcl_log("[mcl_events] Event "..e.readable_name.." already active at "..minetest.pos_to_string(vector.round(p.pos)))
end
end
else
--minetest.log("Do not start this raid")
end
end
for idx,ae in pairs(active_events) do

View File

@ -1,2 +0,0 @@
# textdomain:mcl_explosions
@1 was caught in an explosion.=@1は爆発に巻き込まれた。

View File

@ -3,26 +3,26 @@ mcl_vars = {}
mcl_vars.redstone_tick = 0.1
-- GUI / inventory menu settings
--- GUI / inventory menu settings
mcl_vars.gui_slots = "listcolors[#9990;#FFF7;#FFF0;#000;#FFF]"
-- nonbg is added as formspec prepend in mcl_formspec_prepend
mcl_vars.gui_nonbg = table.concat({
mcl_vars.gui_slots,
"style_type[image_button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]",
"style_type[button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]",
"style_type[field;textcolor=#323232]",
"style_type[label;textcolor=#323232]",
"style_type[textarea;textcolor=#323232]",
"style_type[checkbox;textcolor=#323232]",
})
mcl_vars.gui_nonbg = mcl_vars.gui_slots ..
"style_type[image_button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]"..
"style_type[button;border=false;bgimg=mcl_inventory_button9.png;bgimg_pressed=mcl_inventory_button9_pressed.png;bgimg_middle=2,2]"..
"style_type[field;textcolor=#323232]"..
"style_type[label;textcolor=#323232]"..
"style_type[textarea;textcolor=#323232]"..
"style_type[checkbox;textcolor=#323232]"
-- Background stuff must be manually added by mods (no formspec prepend)
mcl_vars.gui_bg_color = "bgcolor[#00000000]"
mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;true;7]"
-- Legacy
mcl_vars.inventory_header = ""
-- Tool wield size
mcl_vars.tool_wield_scale = vector.new(1.8, 1.8, 1)
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
-- Mapgen variables
local mg_name = minetest.get_mapgen_setting("mg_name")
@ -35,69 +35,55 @@ mcl_vars.chunksize = math.max(1, tonumber(minetest.get_mapgen_setting("chunksize
mcl_vars.MAP_BLOCKSIZE = math.max(1, minetest.MAP_BLOCKSIZE or 16)
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, minetest.MAX_MAP_GENERATION_LIMIT or 31000)
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) /
mcl_vars.MAP_BLOCKSIZE)
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
---@param x integer
---@return integer
local function coordinate_to_block(x)
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
end
---@param x integer
---@return integer
local function coordinate_to_chunk(x)
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
end
---@param pos Vector
---@return Vector
function mcl_vars.pos_to_block(pos)
return vector.new(
coordinate_to_block(pos.x),
coordinate_to_block(pos.y),
coordinate_to_block(pos.z)
)
return {
x = coordinate_to_block(pos.x),
y = coordinate_to_block(pos.y),
z = coordinate_to_block(pos.z)
}
end
---@param pos Vector
---@return Vector
function mcl_vars.pos_to_chunk(pos)
return vector.new(
coordinate_to_chunk(pos.x),
coordinate_to_chunk(pos.y),
coordinate_to_chunk(pos.z)
)
return {
x = coordinate_to_chunk(pos.x),
y = coordinate_to_chunk(pos.y),
z = coordinate_to_chunk(pos.z)
}
end
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
local k_positive_z = k_positive * 2
local k_positive_y = k_positive_z * k_positive_z
---@param pos Vector
---@return integer
function mcl_vars.get_chunk_number(pos) -- unsigned int
local c = mcl_vars.pos_to_chunk(pos)
return (c.y + k_positive) * k_positive_y +
return
(c.y + k_positive) * k_positive_y +
(c.z + k_positive) * k_positive_z +
c.x + k_positive
c.x + k_positive
end
if not superflat and not singlenode then
@ -131,8 +117,11 @@ elseif singlenode then
mcl_vars.mg_bedrock_is_rough = false
else
-- Classic superflat
local ground = tonumber(minetest.get_mapgen_setting("mgflat_ground_level")) or 8
local ground = minetest.get_mapgen_setting("mgflat_ground_level")
ground = tonumber(ground)
if not ground then
ground = 8
end
mcl_vars.mg_overworld_min = ground - 3
mcl_vars.mg_overworld_max_official = mcl_vars.mg_overworld_min + minecraft_height_limit
mcl_vars.mg_bedrock_overworld_min = mcl_vars.mg_overworld_min
@ -192,16 +181,14 @@ minetest.craftitemdef_default.stack_max = 64
math.randomseed(os.time())
local chunks = {} -- intervals of chunks generated
---@param pos Vector
function mcl_vars.add_chunk(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
local prev
for i, d in pairs(chunks) do
if n <= d[2] then -- we've found it
if (n == d[2]) or (n >= d[1]) then return end -- already here
if n == d[1] - 1 then -- right before:
if prev and (prev[2] == n - 1) then
if n == d[1]-1 then -- right before:
if prev and (prev[2] == n-1) then
prev[2] = d[2]
table.remove(chunks, i)
return
@ -209,20 +196,17 @@ function mcl_vars.add_chunk(pos)
d[1] = n
return
end
if prev and (prev[2] == n - 1) then --join to previous
if prev and (prev[2] == n-1) then --join to previous
prev[2] = n
return
end
table.insert(chunks, i, { n, n }) -- insert new interval before i
table.insert(chunks, i, {n, n}) -- insert new interval before i
return
end
prev = d
end
chunks[#chunks + 1] = { n, n }
chunks[#chunks+1] = {n, n}
end
---@param pos Vector
---@return boolean
function mcl_vars.is_generated(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
for i, d in pairs(chunks) do
@ -233,46 +217,47 @@ function mcl_vars.is_generated(pos)
return false
end
---"Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
---@param pos Vector Position, if it's wrong, `{name="error"}` node will return.
---@param force? boolean Optional (default: `false`), Do the maximum to still read the node within us_timeout.
---@param us_timeout? number Optional (default: `244 = 0.000244 s = 1/80/80/80`), set it at least to `3000000` to let mapgen to finish its job
---@return node # Node definition, eg. `{name="air"}`. Unfortunately still can return `{name="ignore"}`.
---@nodiscard
function mcl_vars.get_node(pos, force, us_timeout)
-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
-- p: Position, if it's wrong, {name="error"} node will return.
-- force: optional (default: false) - Do the maximum to still read the node within us_timeout.
-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job.
--
-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}.
function mcl_vars.get_node(p, force, us_timeout)
-- check initial circumstances
if not pos or not pos.x or not pos.y or not pos.z then return { name = "error" } end
if not p or not p.x or not p.y or not p.z then return {name="error"} end
-- try common way
local node = minetest.get_node(pos)
local node = minetest.get_node(p)
if node.name ~= "ignore" then
return node
end
-- copy vector to get sure it won't changed by other threads
local pos_copy = vector.copy(pos)
-- copy table to get sure it won't changed by other threads
local pos = {x=p.x,y=p.y,z=p.z}
-- try LVM
minetest.get_voxel_manip():read_from_map(pos_copy, pos_copy)
node = minetest.get_node(pos_copy)
minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node(pos)
if node.name ~= "ignore" or not force then
return node
end
-- all ways failed - need to emerge (or forceload if generated)
if mcl_vars.is_generated(pos_copy) then
local us_timeout = us_timeout or 244
if mcl_vars.is_generated(pos) then
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
minetest.forceload_block(pos_copy)
minetest.forceload_block(pos)
else
minetest.emerge_area(pos_copy, pos_copy)
minetest.emerge_area(pos, pos)
end
local t = minetest.get_us_time()
node = minetest.get_node(pos_copy)
node = minetest.get_node(pos)
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < (us_timeout or 244)) do
node = minetest.get_node(pos_copy)
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
node = minetest.get_node(pos)
end
return node

View File

@ -11,22 +11,42 @@ Creative Commons Attribution 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by/3.0/
Glass breaking sounds (CC BY 3.0):
1: http://www.freesound.org/people/cmusounddesign/sounds/71947/
2: http://www.freesound.org/people/Tomlija/sounds/97669/
3: http://www.freesound.org/people/lsprice/sounds/88808/
1: http://www.freesound.org/people/cmusounddesign/sounds/71947/
2: http://www.freesound.org/people/Tomlija/sounds/97669/
3: http://www.freesound.org/people/lsprice/sounds/88808/
default_tool_breaks.ogg by EdgardEdition (CC BY 3.0), http://www.freesound.org/people/EdgardEdition
Mito551 (sounds) (CC BY-SA 3.0):
default_dig_crumbly.ogg
default_dig_choppy.ogg
default_dig_cracky.ogg
default_dig_crumbly.1.ogg
default_dig_crumbly.2.ogg
default_dig_oddly_breakable_by_hand.ogg
default_dug_node.*.ogg
default_grass_footstep.*.ogg
default_gravel_footstep.*.ogg
default_place_node.*.ogg
default_place_node_hard.*.ogg
default_wood_footstep.*.ogg
default_dirt_footstep.*.ogg
default_dug_node.1.ogg
default_dug_node.2.ogg
default_grass_footstep.1.ogg
default_grass_footstep.2.ogg
default_grass_footstep.3.ogg
default_gravel_footstep.1.ogg
default_gravel_footstep.2.ogg
default_gravel_footstep.3.ogg
default_gravel_footstep.4.ogg
default_grass_footstep.1.ogg
default_place_node.1.ogg
default_place_node.2.ogg
default_place_node.3.ogg
default_place_node_hard.1.ogg
default_place_node_hard.2.ogg
default_hard_footstep.1.ogg
default_hard_footstep.2.ogg
default_hard_footstep.3.ogg
default_sand_footstep.1.ogg
default_sand_footstep.2.ogg
default_wood_footstep.1.ogg
default_wood_footstep.2.ogg
default_dirt_footstep.1.ogg
default_dirt_footstep.2.ogg
default_glass_footstep.ogg
Metal sounds:
@ -34,64 +54,35 @@ Metal sounds:
- https://www.freesound.org/people/yadronoff/sounds/320397/
default_dug_metal.*.ogg - Iwan Gabovitch - qubodup - CC0
- http://opengameart.org/users/qubodup
default_metal_footstep.*.ogg - (CC0 1.0) - CC0 1.0
- https://freesound.org/people/mypantsfelldown/sounds/398937/
default_metal_footstep.*.ogg - Ottomaani138 - CC0
- https://www.freesound.org/people/Ottomaani138/sounds/232692/
default_place_node_metal.*.ogg - Ogrebane - CC0
- http://opengameart.org/content/wood-and-metal-sound-effects-volume-2
AGFX (CC BY 3.0):
AGFX (CC BY 3.0)
https://www.freesound.org/people/AGFX/packs/1253/
default_water_footstep.*.ogg
default_water_footstep.1.ogg
default_water_footstep.2.ogg
default_water_footstep.3.ogg
(default_water_footstep.4.ogg is silent)
blukotek (CC0 1.0):
blukotek (CC0 1.0)
https://www.freesound.org/people/blukotek/sounds/251660/
default_dig_snappy.ogg
sonictechtonic (CC BY 3.0):
sonictechtonic (CC BY 3.0)
https://www.freesound.org/people/sonictechtonic/sounds/241872/
player_damage.ogg
Sheyvan (CC0 1.0):
https://freesound.org/people/Sheyvan/sounds/476113/
default_dig_choppy.*.ogg
lolamadeus (CC0 1.0):
https://freesound.org/people/lolamadeus/sounds/179341/
default_gravel_dig.*.ogg
default_gravel_dug.*.ogg
Benboncan (CC BY 3.0):
https://freesound.org/people/Benboncan/sounds/71823/
default_dig_cracky.*.ogg
Erdie (CC BY 3.0):
https://freesound.org/people/Erdie/sounds/41579/
default_hard_footstep.*.ogg
worthahep88 (CC0 1.0):
https://freesound.org/people/worthahep88/sounds/319224/
default_sand_footstep.*.ogg
dheming (CC BY 3.0):
https://freesound.org/people/dheming/sounds/268023/
default_ice_dig.*.ogg
InspectorJ (CC BY 3.0):
https://freesound.org/people/InspectorJ/sounds/416967/
default_ice_footstep.*.ogg
Angel_Perez_Grandi (CC BY 3.0):
https://freesound.org/people/Angel_Perez_Grandi/sounds/49190/
default_ice_dug.ogg
Voxelands project <http://www.voxelands.com/> (CC BY-SA 3.0):
Voxelands project <http://www.voxelands.com/> (CC BY-SA 3.0)
mcl_sounds_place_node_water.ogg
mcl_sounds_dug_water.ogg
(Note: Artists from the Voxelands project include: sdzen, darkrose, sapier,
Tom Peter, Telaron, juskiddink)
Michel Baradari <https://opengameart.org/content/lava-splash> (CC BY 3.0):
Michel Baradari <https://opengameart.org/content/lava-splash> (CC BY 3.0)
default_place_node_lava.ogg
Adam_N (CC0 1.0):
@ -99,7 +90,7 @@ Adam_N (CC0 1.0):
Source: <https://www.freesound.org/people/Adam_N/sounds/346692/>
Alecia Shepherd (CC BY-SA 4.0):
mcl_sounds_cloth.*.ogg
mcl_sounds_cloth.ogg
Source: SnowSong sound and music pack <https://opengameart.org/content/snowsong-sound-and-music-pack>
Unknown authors (WTFPL):

View File

@ -11,7 +11,7 @@ function mcl_sounds.node_sound_defaults(table)
table.dug = table.dug or
{name="default_dug_node", gain=0.25}
table.dig = table.dig or
{name="default_dig_oddly_breakable_by_hand", gain=0.5}
{name="default_dig_oddly_breakable_by_hand", gain=1.0}
table.place = table.place or
{name="default_place_node_hard", gain=1.0}
return table
@ -20,11 +20,11 @@ end
function mcl_sounds.node_sound_stone_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_hard_footstep", gain=0.2}
{name="default_hard_footstep", gain=0.5}
table.dug = table.dug or
{name="default_hard_footstep", gain=1.0}
table.dig = table.dig or
{name="default_dig_cracky", gain=0.5}
{name="default_dig_cracky", gain=1.0}
mcl_sounds.node_sound_defaults(table)
return table
end
@ -32,13 +32,13 @@ end
function mcl_sounds.node_sound_metal_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_metal_footstep", gain=0.2}
{name="default_metal_footstep", gain=0.5}
table.dug = table.dug or
{name="default_dug_metal", gain=0.5}
{name="default_dug_metal", gain=1.0}
table.dig = table.dig or
{name="default_dig_metal", gain=0.5}
{name="default_dig_metal", gain=1.0}
table.place = table.place or
{name="default_place_node_metal", gain=0.5}
{name="default_place_node_metal", gain=1.0}
mcl_sounds.node_sound_defaults(table)
return table
end
@ -46,11 +46,11 @@ end
function mcl_sounds.node_sound_dirt_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_dirt_footstep", gain=0.25}
table.dug = table.dug or
{name="default_dirt_footstep", gain=1.0}
table.dug = table.dug or
{name="default_dirt_footstep", gain=1.5}
table.dig = table.dig or
{name="default_dig_crumbly", gain=0.4}
{name="default_dig_crumbly", gain=1.0}
table.place = table.place or
{name="default_place_node", gain=1.0}
mcl_sounds.node_sound_defaults(table)
@ -60,25 +60,11 @@ end
function mcl_sounds.node_sound_sand_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_sand_footstep", gain=0.05}
{name="default_sand_footstep", gain=0.5}
table.dug = table.dug or
{name="default_sand_footstep", gain=0.15}
{name="default_sand_footstep", gain=1.0}
table.dig = table.dig or
{name="default_dig_crumbly", gain=0.4}
table.place = table.place or
{name="default_place_node", gain=1.0}
mcl_sounds.node_sound_defaults(table)
return table
end
function mcl_sounds.node_sound_gravel_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_gravel_footstep", gain=0.25}
table.dug = table.dug or
{name="default_gravel_dug", gain=1.0}
table.dig = table.dig or
{name="default_gravel_dig", gain=0.35}
{name="default_dig_crumbly", gain=1.0}
table.place = table.place or
{name="default_place_node", gain=1.0}
mcl_sounds.node_sound_defaults(table)
@ -92,33 +78,21 @@ function mcl_sounds.node_sound_snow_defaults(table)
table.dug = table.dug or
{name="pedology_snow_soft_footstep", gain=1.0}
table.dig = table.dig or
{name="pedology_snow_soft_footstep", gain=1.0}
{name="default_dig_crumbly", gain=1.0}
table.place = table.place or
{name="default_place_node", gain=1.0}
mcl_sounds.node_sound_defaults(table)
return table
end
function mcl_sounds.node_sound_ice_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_ice_footstep", gain=0.15}
table.dug = table.dug or
{name="default_ice_dug", gain=0.5}
table.dig = table.dig or
{name="default_ice_dig", gain=0.5}
mcl_sounds.node_sound_defaults(table)
return table
end
function mcl_sounds.node_sound_wood_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_wood_footstep", gain=0.15}
{name="default_wood_footstep", gain=0.5}
table.dug = table.dug or
{name="default_wood_footstep", gain=1.0}
table.dig = table.dig or
{name="default_dig_choppy", gain=0.4}
{name="default_dig_choppy", gain=1.0}
mcl_sounds.node_sound_defaults(table)
return table
end
@ -154,11 +128,11 @@ end
function mcl_sounds.node_sound_glass_defaults(table)
table = table or {}
table.footstep = table.footstep or
{name="default_glass_footstep", gain=0.3}
{name="default_glass_footstep", gain=0.5}
table.dug = table.dug or
{name="default_break_glass", gain=1.0}
table.dig = table.dig or
{name="default_dig_cracky", gain=0.5}
{name="default_dig_cracky", gain=1.0}
mcl_sounds.node_sound_defaults(table)
return table
end

Binary file not shown.

Binary file not shown.

View File

@ -2,8 +2,8 @@ mcl_util = {}
-- Updates all values in t using values from to*.
function table.update(t, ...)
for _, to in ipairs {...} do
for k, v in pairs(to) do
for _, to in ipairs{...} do
for k,v in pairs(to) do
t[k] = v
end
end
@ -12,8 +12,8 @@ end
-- Updates nil values in t using values from to*.
function table.update_nil(t, ...)
for _, to in ipairs {...} do
for k, v in pairs(to) do
for _, to in ipairs{...} do
for k,v in pairs(to) do
if t[k] == nil then
t[k] = v
end
@ -22,9 +22,9 @@ function table.update_nil(t, ...)
return t
end
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false)
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default",false)
local LOG_MODULE = "[MCL2]"
function mcl_util.mcl_log(message, module, bypass_default_logger)
function mcl_util.mcl_log (message, module, bypass_default_logger)
local selected_module = LOG_MODULE
if module then
selected_module = module
@ -34,6 +34,7 @@ function mcl_util.mcl_log(message, module, bypass_default_logger)
end
end
function mcl_util.file_exists(name)
if type(name) ~= "string" then return end
local f = io.open(name)
@ -68,7 +69,7 @@ function mcl_util.rotate_axis_and_place(itemstack, placer, pointed_thing, infini
local undef = minetest.registered_nodes[unode.name]
if undef and undef.on_rightclick then
undef.on_rightclick(pointed_thing.under, unode, placer,
itemstack, pointed_thing)
itemstack, pointed_thing)
return
end
local fdir = minetest.dir_to_facedir(placer:get_look_dir())
@ -150,23 +151,23 @@ end
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}
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}
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}
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}
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}
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}
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}
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}
return {x=pos.x, y=pos.y, z=pos.z+1}
end
end
end
@ -184,7 +185,7 @@ end
function mcl_util.get_eligible_transfer_item_slot(src_inventory, src_list, dst_inventory, dst_list, condition)
local size = src_inventory:get_size(src_list)
local stack
for i = 1, size do
for i=1, size do
stack = src_inventory:get_stack(src_list, i)
if not stack:is_empty() and (condition == nil or condition(stack, src_inventory, src_list, dst_inventory, dst_list)) then
return i
@ -287,10 +288,10 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
-- 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
-- Furnace: output
elseif sctype == 4 then
source_list = "dst"
-- Unknown source container type. Bail out
-- Unknown source container type. Bail out
else
return false
end
@ -343,7 +344,7 @@ function mcl_util.move_item_container(source_pos, destination_pos, source_list,
-- 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
-- Furnace source slot
elseif dctype == 4 then
destination_list = "src"
end
@ -408,7 +409,7 @@ end
-- Returns true if item (itemstring or ItemStack) can be used as a furnace fuel.
-- Returns false otherwise
function mcl_util.is_fuel(item)
return minetest.get_craft_result({method = "fuel", width = 1, items = {item}}).time ~= 0
return minetest.get_craft_result({method="fuel", width=1, items={item}}).time ~= 0
end
-- Returns a on_place function for plants
@ -455,7 +456,7 @@ function mcl_util.generate_on_place_plant_function(condition)
if success then
if idef.sounds and idef.sounds.place then
minetest.sound_play(idef.sounds.place, {pos = pointed_thing.above, gain = 1}, true)
minetest.sound_play(idef.sounds.place, {pos=pointed_thing.above, gain=1}, true)
end
end
itemstack = new_itemstack
@ -642,80 +643,78 @@ end
local function roundN(n, d)
if type(n) ~= "number" then return n end
local m = 10 ^ d
return math.floor(n * m + 0.5) / m
local m = 10^d
return math.floor(n * m + 0.5) / m
end
local function close_enough(a, b)
local rt = true
local function close_enough(a,b)
local rt=true
if type(a) == "table" and type(b) == "table" then
for k, v in pairs(a) do
if roundN(v, 2) ~= roundN(b[k], 2) then
rt = false
for k,v in pairs(a) do
if roundN(v,2) ~= roundN(b[k],2) then
rt=false
break
end
end
else
rt = roundN(a, 2) == roundN(b, 2)
rt = roundN(a,2) == roundN(b,2)
end
return rt
end
local function props_changed(props, oldprops)
local changed = false
local p = {}
for k, v in pairs(props) do
if not close_enough(v, oldprops[k]) then
p[k] = v
changed = true
local function props_changed(props,oldprops)
local changed=false
local p={}
for k,v in pairs(props) do
if not close_enough(v,oldprops[k]) then
p[k]=v
changed=true
end
end
return changed, p
return changed,p
end
--tests for roundN
local test_round1 = 15
local test_round2 = 15.00199999999
local test_round3 = 15.00111111
local test_round4 = 15.00999999
local test_round1=15
local test_round2=15.00199999999
local test_round3=15.00111111
local test_round4=15.00999999
assert(roundN(test_round1, 2) == roundN(test_round1, 2))
assert(roundN(test_round1, 2) == roundN(test_round2, 2))
assert(roundN(test_round1, 2) == roundN(test_round3, 2))
assert(roundN(test_round1, 2) ~= roundN(test_round4, 2))
assert(roundN(test_round1,2)==roundN(test_round1,2))
assert(roundN(test_round1,2)==roundN(test_round2,2))
assert(roundN(test_round1,2)==roundN(test_round3,2))
assert(roundN(test_round1,2)~=roundN(test_round4,2))
-- tests for close_enough
local test_cb = {-0.35, 0, -0.35, 0.35, 0.8, 0.35} --collisionboxes
local test_cb_close = {-0.351213, 0, -0.35, 0.35, 0.8, 0.351212}
local test_cb_diff = {-0.35, 0, -1.35, 0.35, 0.8, 0.35}
local test_cb = {-0.35,0,-0.35,0.35,0.8,0.35} --collisionboxes
local test_cb_close = {-0.351213,0,-0.35,0.35,0.8,0.351212}
local test_cb_diff = {-0.35,0,-1.35,0.35,0.8,0.35}
local test_eh = 1.65 --eye height
local test_eh_close = 1.65123123
local test_eh_diff = 1.35
local test_nt = {r = 225, b = 225, a = 225, g = 225} --nametag
local test_nt_diff = {r = 225, b = 225, a = 0, g = 225}
local test_nt = { r = 225, b = 225, a = 225, g = 225 } --nametag
local test_nt_diff = { r = 225, b = 225, a = 0, g = 225 }
assert(close_enough(test_cb, test_cb_close))
assert(not close_enough(test_cb, test_cb_diff))
assert(close_enough(test_eh, test_eh_close))
assert(not close_enough(test_eh, test_eh_diff))
assert(not close_enough(test_nt, test_nt_diff)) --no floats involved here
assert(close_enough(test_cb,test_cb_close))
assert(not close_enough(test_cb,test_cb_diff))
assert(close_enough(test_eh,test_eh_close))
assert(not close_enough(test_eh,test_eh_diff))
assert(not close_enough(test_nt,test_nt_diff)) --no floats involved here
--tests for properties_changed
local test_properties_set1 = {collisionbox = {-0.35, 0, -0.35, 0.35, 0.8, 0.35}, eye_height = 0.65,
nametag_color = {r = 225, b = 225, a = 225, g = 225}}
local test_properties_set2 = {collisionbox = {-0.35, 0, -0.35, 0.35, 0.8, 0.35}, eye_height = 1.35,
nametag_color = {r = 225, b = 225, a = 225, g = 225}}
local test_properties_set1={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 0.65, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
local test_properties_set2={collisionbox = {-0.35,0,-0.35,0.35,0.8,0.35}, eye_height = 1.35, nametag_color = { r = 225, b = 225, a = 225, g = 225 }}
local test_p1, _ = props_changed(test_properties_set1, test_properties_set1)
local test_p2, _ = props_changed(test_properties_set1, test_properties_set2)
local test_p1,_=props_changed(test_properties_set1,test_properties_set1)
local test_p2,_=props_changed(test_properties_set1,test_properties_set2)
assert(not test_p1)
assert(test_p2)
function mcl_util.set_properties(obj, props)
local changed, p = props_changed(props, obj:get_properties())
function mcl_util.set_properties(obj,props)
local changed,p=props_changed(props,obj:get_properties())
if changed then
obj:set_properties(p)
end
@ -729,285 +728,3 @@ function mcl_util.set_bone_position(obj, bone, pos, rot)
obj:set_bone_position(bone, pos or current_pos, rot or current_rot)
end
end
---Return a function to use in `on_place`.
---
---Allow to bypass the `buildable_to` node field in a `on_place` callback.
---
---You have to make sure that the nodes you return true for have `buildable_to = true`.
---@param func fun(node_name: string): boolean Return `true` if node must not replace the buildable_to node which have `node_name`
---@return fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: pointed_thing, param2: integer): ItemStack?
function mcl_util.bypass_buildable_to(func)
--------------------------
-- MINETEST CODE: UTILS --
--------------------------
local function copy_pointed_thing(pointed_thing)
return {
type = pointed_thing.type,
above = pointed_thing.above and vector.copy(pointed_thing.above),
under = pointed_thing.under and vector.copy(pointed_thing.under),
ref = pointed_thing.ref,
}
end
local function user_name(user)
return user and user:get_player_name() or ""
end
-- Returns a logging function. For empty names, does not log.
local function make_log(name)
return name ~= "" and minetest.log or function() end
end
local function check_attached_node(p, n, group_rating)
local def = core.registered_nodes[n.name]
local d = vector.zero()
if group_rating == 3 then
-- always attach to floor
d.y = -1
elseif group_rating == 4 then
-- always attach to ceiling
d.y = 1
elseif group_rating == 2 then
-- attach to facedir or 4dir direction
if (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir") then
-- Attach to whatever facedir is "mounted to".
-- For facedir, this is where tile no. 5 point at.
-- The fallback vector here is in case 'facedir to dir' is nil due
-- to voxelmanip placing a wallmounted node without resetting a
-- pre-existing param2 value that is out-of-range for facedir.
-- The fallback vector corresponds to param2 = 0.
d = core.facedir_to_dir(n.param2) or vector.new(0, 0, 1)
elseif (def.paramtype2 == "4dir" or
def.paramtype2 == "color4dir") then
-- Similar to facedir handling
d = core.fourdir_to_dir(n.param2) or vector.new(0, 0, 1)
end
elseif def.paramtype2 == "wallmounted" or
def.paramtype2 == "colorwallmounted" then
-- Attach to whatever this node is "mounted to".
-- This where tile no. 2 points at.
-- The fallback vector here is used for the same reason as
-- for facedir nodes.
d = core.wallmounted_to_dir(n.param2) or vector.new(0, 1, 0)
else
d.y = -1
end
local p2 = vector.add(p, d)
local nn = core.get_node(p2).name
local def2 = core.registered_nodes[nn]
if def2 and not def2.walkable then
return false
end
return true
end
return function(itemstack, placer, pointed_thing, param2)
-------------------
-- MINETEST CODE --
-------------------
local def = itemstack:get_definition()
if def.type ~= "node" or pointed_thing.type ~= "node" then
return itemstack
end
local under = pointed_thing.under
local oldnode_under = minetest.get_node_or_nil(under)
local above = pointed_thing.above
local oldnode_above = minetest.get_node_or_nil(above)
local playername = user_name(placer)
local log = make_log(playername)
if not oldnode_under or not oldnode_above then
log("info", playername .. " tried to place"
.. " node in unloaded position " .. minetest.pos_to_string(above))
return itemstack
end
local olddef_under = minetest.registered_nodes[oldnode_under.name]
olddef_under = olddef_under or minetest.nodedef_default
local olddef_above = minetest.registered_nodes[oldnode_above.name]
olddef_above = olddef_above or minetest.nodedef_default
if not olddef_above.buildable_to and not olddef_under.buildable_to then
log("info", playername .. " tried to place"
.. " node in invalid position " .. minetest.pos_to_string(above)
.. ", replacing " .. oldnode_above.name)
return itemstack
end
---------------------
-- CUSTOMIZED CODE --
---------------------
-- Place above pointed node
local place_to = vector.copy(above)
-- If node under is buildable_to, check for callback result and place into it instead
if olddef_under.buildable_to and not func(oldnode_under.name) then
log("info", "node under is buildable to")
place_to = vector.copy(under)
end
-------------------
-- MINETEST CODE --
-------------------
if minetest.is_protected(place_to, playername) then
log("action", playername
.. " tried to place " .. def.name
.. " at protected position "
.. minetest.pos_to_string(place_to))
minetest.record_protection_violation(place_to, playername)
return itemstack
end
local oldnode = minetest.get_node(place_to)
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
-- Calculate direction for wall mounted stuff like torches and signs
if def.place_param2 ~= nil then
newnode.param2 = def.place_param2
elseif (def.paramtype2 == "wallmounted" or
def.paramtype2 == "colorwallmounted") and not param2 then
local dir = vector.subtract(under, above)
newnode.param2 = minetest.dir_to_wallmounted(dir)
-- Calculate the direction for furnaces and chests and stuff
elseif (def.paramtype2 == "facedir" or
def.paramtype2 == "colorfacedir" or
def.paramtype2 == "4dir" or
def.paramtype2 == "color4dir") and not param2 then
local placer_pos = placer and placer:get_pos()
if placer_pos then
local dir = vector.subtract(above, placer_pos)
newnode.param2 = minetest.dir_to_facedir(dir)
log("info", "facedir: " .. newnode.param2)
end
end
local metatable = itemstack:get_meta():to_table().fields
-- Transfer color information
if metatable.palette_index and not def.place_param2 then
local color_divisor = nil
if def.paramtype2 == "color" then
color_divisor = 1
elseif def.paramtype2 == "colorwallmounted" then
color_divisor = 8
elseif def.paramtype2 == "colorfacedir" then
color_divisor = 32
elseif def.paramtype2 == "color4dir" then
color_divisor = 4
elseif def.paramtype2 == "colordegrotate" then
color_divisor = 32
end
if color_divisor then
local color = math.floor(metatable.palette_index / color_divisor)
local other = newnode.param2 % color_divisor
newnode.param2 = color * color_divisor + other
end
end
-- Check if the node is attached and if it can be placed there
local an = minetest.get_item_group(def.name, "attached_node")
if an ~= 0 and
not check_attached_node(place_to, newnode, an) then
log("action", "attached node " .. def.name ..
" cannot be placed at " .. minetest.pos_to_string(place_to))
return itemstack
end
log("action", playername .. " places node "
.. def.name .. " at " .. minetest.pos_to_string(place_to))
-- Add node and update
minetest.add_node(place_to, newnode)
-- Play sound if it was done by a player
if playername ~= "" and def.sounds and def.sounds.place then
minetest.sound_play(def.sounds.place, {
pos = place_to,
exclude_player = playername,
}, true)
end
local take_item = true
-- Run callback
if def.after_place_node then
-- Deepcopy place_to and pointed_thing because callback can modify it
local place_to_copy = vector.copy(place_to)
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
if def.after_place_node(place_to_copy, placer, itemstack,
pointed_thing_copy) then
take_item = false
end
end
-- Run script hook
for _, callback in ipairs(minetest.registered_on_placenodes) do
-- Deepcopy pos, node and pointed_thing because callback can modify them
local place_to_copy = vector.copy(place_to)
local newnode_copy = {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2}
local oldnode_copy = {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2}
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
take_item = false
end
end
if take_item then
itemstack:take_item()
end
return itemstack
end
end
--[[Check for a protection violation in a given area.
--
-- Applies is_protected() to a 3D lattice of points in the defined volume. The points are spaced
-- evenly throughout the volume and have a spacing similar to, but no larger than, "interval".
--
-- @param pos1 A position table of the area volume's first edge.
-- @param pos2 A position table of the area volume's second edge.
-- @param player The player performing the action.
-- @param interval Optional. Max spacing between checked points at the volume.
-- Default: Same as minetest.is_area_protected.
--
-- @return true on protection violation detection. false otherwise.
--
-- @notes *All corners and edges of the defined volume are checked.
]]
function mcl_util.check_area_protection(pos1, pos2, player, interval)
local name = player and player:get_player_name() or ""
local protected_pos = minetest.is_area_protected(pos1, pos2, name, interval)
if protected_pos then
minetest.record_protection_violation(protected_pos, name)
return true
end
return false
end
--[[Check for a protection violation on a single position.
--
-- @param position A position table to check for protection violation.
-- @param player The player performing the action.
--
-- @return true on protection violation detection. false otherwise.
]]
function mcl_util.check_position_protection(position, player)
local name = player and player:get_player_name() or ""
if minetest.is_protected(position, name) then
minetest.record_protection_violation(position, name)
return true
end
return false
end

View File

@ -1,13 +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=スニークで降りる
Obsidian Boat=黒曜石のボート

View File

@ -7,24 +7,31 @@ local pool = {}
local tick = false
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_item_entities", false)
local function mcl_log(message)
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_item_entities",false)
local function mcl_log (message)
if LOGGING_ON then
mcl_util.mcl_log(message, "[Item Entities]", true)
mcl_util.mcl_log (message, "[Item Entities]", true)
end
end
minetest.register_on_joinplayer(function(player)
pool[player:get_player_name()] = 0
local name
name = player:get_player_name()
pool[name] = 0
end)
minetest.register_on_leaveplayer(function(player)
pool[player:get_player_name()] = nil
local name
name = player:get_player_name()
pool[name] = nil
end)
local has_awards = minetest.get_modpath("awards")
mcl_item_entity = {}
local mcl_item_entity = {}
--basic settings
local item_drop_settings = {} --settings table
@ -39,29 +46,22 @@ item_drop_settings.random_item_velocity = true --this sets random item velocity
item_drop_settings.drop_single_item = false --if true, the drop control drops 1 item instead of the entire stack, and sneak+drop drops the stack
-- drop_single_item is disabled by default because it is annoying to throw away items from the intentory screen
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 function get_gravity()
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
end
mcl_item_entity.registered_pickup_achievement = {}
local registered_pickup_achievement = {}
---Register an achievement that will be unlocked on pickup.
---
---TODO: remove limitation of 1 award per itemname
---@param itemname string
---@param award string
--TODO: remove limitation of 1 award per itemname
function mcl_item_entity.register_pickup_achievement(itemname, award)
if not has_awards then
minetest.log("warning",
"[mcl_item_entity] Trying to register pickup achievement [" .. award .. "] for [" ..
itemname .. "] while awards missing")
elseif mcl_item_entity.registered_pickup_achievement[itemname] then
minetest.log("error",
"[mcl_item_entity] Trying to register already existing pickup achievement [" .. award .. "] for [" .. itemname .. "]")
minetest.log("warning", "[mcl_item_entity] Trying to register pickup achievement ["..award.."] for ["..itemname.."] while awards missing")
elseif registered_pickup_achievement[itemname] then
minetest.log("error", "[mcl_item_entity] Trying to register already existing pickup achievement ["..award.."] for ["..itemname.."]")
else
mcl_item_entity.registered_pickup_achievement[itemname] = award
registered_pickup_achievement[itemname] = award
end
end
@ -74,13 +74,11 @@ mcl_item_entity.register_pickup_achievement("mcl_nether:ancient_debris", "mcl:hi
mcl_item_entity.register_pickup_achievement("mcl_end:dragon_egg", "mcl:PickUpDragonEgg")
mcl_item_entity.register_pickup_achievement("mcl_armor:elytra", "mcl:skysTheLimit")
---@param object ObjectRef
---@param player ObjectRef
local function check_pickup_achievements(object, player)
if has_awards then
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
local playername = player:get_player_name()
for name, award in pairs(mcl_item_entity.registered_pickup_achievement) do
for name,award in pairs(registered_pickup_achievement) do
if itemname == name or minetest.get_item_group(itemname, name) ~= 0 then
awards.unlock(playername, award)
end
@ -88,23 +86,16 @@ local function check_pickup_achievements(object, player)
end
end
---@param object ObjectRef
---@param luaentity Luaentity
---@param ignore_check? boolean
local function enable_physics(object, luaentity, ignore_check)
if luaentity.physical_state == false or ignore_check == true then
luaentity.physical_state = true
object:set_properties({
physical = true
})
object:set_acceleration(vector.new(0, -get_gravity(), 0))
object:set_acceleration({x=0,y=-get_gravity(),z=0})
end
end
---@param object ObjectRef
---@param luaentity Luaentity
---@param ignore_check? boolean
---@param reset_movement? boolean
local function disable_physics(object, luaentity, ignore_check, reset_movement)
if luaentity.physical_state == true or ignore_check == true then
luaentity.physical_state = false
@ -112,16 +103,17 @@ local function disable_physics(object, luaentity, ignore_check, reset_movement)
physical = false
})
if reset_movement ~= false then
object:set_velocity(vector.zero())
object:set_acceleration(vector.zero())
object:set_velocity({x=0,y=0,z=0})
object:set_acceleration({x=0,y=0,z=0})
end
end
end
minetest.register_globalstep(function(_)
minetest.register_globalstep(function(dtime)
tick = not tick
for _, player in pairs(minetest.get_connected_players()) do
for _,player in pairs(minetest.get_connected_players()) do
if player:get_hp() > 0 or not minetest.settings:get_bool("enable_damage") then
local name = player:get_player_name()
@ -133,7 +125,7 @@ minetest.register_globalstep(function(_)
pos = pos,
gain = 0.3,
max_hear_distance = 16,
pitch = math.random(70, 110) / 100
pitch = math.random(70,110)/100
})
if pool[name] > 6 then
pool[name] = 6
@ -143,18 +135,15 @@ minetest.register_globalstep(function(_)
end
local inv = player:get_inventory()
local checkpos = vector.offset(pos, 0, item_drop_settings.player_collect_height, 0)
local checkpos = {x=pos.x,y=pos.y + item_drop_settings.player_collect_height,z=pos.z}
--magnet and collection
for _, object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
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
for _,object in pairs(minetest.get_objects_inside_radius(checkpos, item_drop_settings.xp_radius_magnet)) do
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
-- Collection
if not object:get_luaentity()._removed then
@ -169,8 +158,8 @@ minetest.register_globalstep(function(_)
object:get_luaentity().target = checkpos
object:get_luaentity()._removed = true
object:set_velocity(vector.zero())
object:set_acceleration(vector.zero())
object:set_velocity({x=0,y=0,z=0})
object:set_acceleration({x=0,y=0,z=0})
object:move_to(checkpos)
@ -190,6 +179,7 @@ minetest.register_globalstep(function(_)
local entity = object:get_luaentity()
entity.collector = player:get_player_name()
entity.collected = true
end
end
@ -204,11 +194,6 @@ end)
local tmp_id = 0
---@param drop string|drop_definition
---@param toolname string
---@param param2 integer
---@param paramtype2 paramtype2
---@return string[]
local function get_drops(drop, toolname, param2, paramtype2)
tmp_id = tmp_id + 1
local tmp_node_name = "mcl_item_entity:" .. tmp_id
@ -217,7 +202,7 @@ local function get_drops(drop, toolname, param2, paramtype2)
drop = drop,
paramtype2 = paramtype2
}
local drops = minetest.get_node_drops({ name = tmp_node_name, param2 = param2 }, toolname)
local drops = minetest.get_node_drops({name = tmp_node_name, param2 = param2}, toolname)
minetest.registered_nodes[tmp_node_name] = nil
return drops
end
@ -280,7 +265,7 @@ function minetest.handle_node_drops(pos, drops, digger)
* table: Drop every itemstring in this table when dug by shears _mcl_silk_touch_drop
]]
local enchantments = tool and mcl_enchanting.get_enchantments(tool)
local enchantments = tool and mcl_enchanting.get_enchantments(tool, "silk_touch")
local silk_touch_drop = false
local nodedef = minetest.registered_nodes[dug_node.name]
@ -309,8 +294,7 @@ function minetest.handle_node_drops(pos, drops, digger)
local max_count = fortune_drop.max_count + fortune_level * (fortune_drop.factor or 1)
local chance = fortune_drop.chance or fortune_drop.get_chance and fortune_drop.get_chance(fortune_level)
if not chance or math.random() < chance then
drops = discrete_uniform_distribution(fortune_drop.multiply and drops or fortune_drop.items, min_count, max_count,
fortune_drop.cap)
drops = discrete_uniform_distribution(fortune_drop.multiply and drops or fortune_drop.items, min_count, max_count, fortune_drop.cap)
elseif fortune_drop.override then
drops = {}
end
@ -322,13 +306,13 @@ function minetest.handle_node_drops(pos, drops, digger)
end
if digger and mcl_experience.throw_xp and not silk_touch_drop then
local experience_amount = minetest.get_item_group(dug_node.name, "xp")
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
if experience_amount > 0 then
mcl_experience.throw_xp(pos, experience_amount)
end
end
for _, item in ipairs(drops) do
for _,item in ipairs(drops) do
local count
if type(item) == "string" then
count = ItemStack(item):get_count()
@ -337,7 +321,7 @@ function minetest.handle_node_drops(pos, drops, digger)
end
local drop_item = ItemStack(item)
drop_item:set_count(1)
for i = 1, count do
for i=1,count do
local dpos = table.copy(pos)
-- Apply offset for plantlike_rooted nodes because of their special shape
if nodedef and nodedef.drawtype == "plantlike_rooted" and nodedef.walkable then
@ -364,7 +348,7 @@ end
function minetest.item_drop(itemstack, dropper, pos)
if dropper and dropper:is_player() then
local v = dropper:get_look_dir()
local p = vector.offset(pos, 0, 1.2, 0)
local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
local cs = itemstack:get_count()
if dropper:get_player_control().sneak then
cs = 1
@ -372,9 +356,9 @@ function minetest.item_drop(itemstack, dropper, pos)
local item = itemstack:take_item(cs)
local obj = minetest.add_item(p, item)
if obj then
v.x = v.x * 4
v.y = v.y * 4 + 2
v.z = v.z * 4
v.x = v.x*4
v.y = v.y*4 + 2
v.z = v.z*4
obj:set_velocity(v)
-- Force collection delay
obj:get_luaentity()._insta_collect = false
@ -392,16 +376,16 @@ end
local function cxcz(o, cw, one, zero)
if cw < 0 then
table.insert(o, { [one] = 1, y = 0, [zero] = 0 })
table.insert(o, { [one] = -1, y = 0, [zero] = 0 })
table.insert(o, { [one]=1, y=0, [zero]=0 })
table.insert(o, { [one]=-1, y=0, [zero]=0 })
else
table.insert(o, { [one] = -1, y = 0, [zero] = 0 })
table.insert(o, { [one] = 1, y = 0, [zero] = 0 })
table.insert(o, { [one]=-1, y=0, [zero]=0 })
table.insert(o, { [one]=1, y=0, [zero]=0 })
end
return o
end
local function hopper_take_item(self, pos)
local function hopper_take_item (self, pos)
--mcl_log("self.itemstring: ".. self.itemstring)
--mcl_log("self.itemstring: ".. minetest.pos_to_string(pos))
@ -410,17 +394,17 @@ local function hopper_take_item(self, pos)
if objs and self.itemstring then
--mcl_log("there is an itemstring. Number of objs: ".. #objs)
for k, v in pairs(objs) do
for k,v in pairs(objs) do
local ent = v:get_luaentity()
-- Don't forget actual hoppers
if ent and ent.name == "mcl_minecarts:hopper_minecart" then
local taken_items = false
mcl_log("ent.name: " .. tostring(ent.name))
mcl_log("ent pos: " .. tostring(ent.object:get_pos()))
mcl_log("ent.name: ".. tostring(ent.name))
mcl_log("ent pos: ".. tostring(ent.object:get_pos()))
local inv = mcl_entity_invs.load_inv(ent, 5)
local inv = mcl_entity_invs.load_inv(ent,5)
if not inv then
mcl_log("No inv")
@ -444,7 +428,7 @@ local function hopper_take_item(self, pos)
local items_remaining = current_itemstack:get_count()
-- This will take part of a floating item stack if no slot can hold the full amount
for i = 1, ent._inv_size, 1 do
for i = 1, ent._inv_size,1 do
local stack = inv:get_stack("main", i)
mcl_log("i: " .. tostring(i))
@ -516,13 +500,13 @@ minetest.register_entity(":__builtin:item", {
hp_max = 1,
physical = true,
collide_with_objects = false,
collisionbox = { -0.3, -0.3, -0.3, 0.3, 0.3, 0.3 },
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
pointable = false,
visual = "wielditem",
visual_size = { x = 0.4, y = 0.4 },
textures = { "" },
spritediv = { x = 1, y = 1 },
initial_sprite_basepos = { x = 0, y = 0 },
visual_size = {x = 0.4, y = 0.4},
textures = {""},
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = false,
infotext = "",
},
@ -560,11 +544,11 @@ minetest.register_entity(":__builtin:item", {
if vel and vel.x == 0 and vel.z == 0 and self.random_velocity > 0 then
local v = self.random_velocity
local x = math.random(5, 10) / 10 * v
if math.random(0, 10) < 5 then x = -x end
if math.random(0,10) < 5 then x = -x end
local z = math.random(5, 10) / 10 * v
if math.random(0, 10) < 5 then z = -z end
local y = math.random(2, 4)
self.object:set_velocity(vector.new(x, y, z))
if math.random(0,10) < 5 then z = -z end
local y = math.random(2,4)
self.object:set_velocity({x=x, y=y, z=z})
end
self.random_velocity = 0
end,
@ -592,7 +576,7 @@ minetest.register_entity(":__builtin:item", {
local max_count = stack:get_stack_max()
if count > max_count then
count = max_count
self.itemstring = stack:get_name() .. " " .. max_count
self.itemstring = stack:get_name().." "..max_count
end
local itemtable = stack:to_table()
local itemname = nil
@ -613,9 +597,9 @@ minetest.register_entity(":__builtin:item", {
local prop = {
is_visible = true,
visual = "wielditem",
textures = { itemname },
visual_size = { x = s, y = s },
collisionbox = { -c, -c, -c, c, c, c },
textures = {itemname},
visual_size = {x = s, y = s},
collisionbox = {-c, -c, -c, c, c, c},
automatic_rotate = math.pi * 0.5,
infotext = description,
glow = glow,
@ -711,9 +695,9 @@ minetest.register_entity(":__builtin:item", {
self._forcestart = nil
self._forcetimer = 0
self.object:set_armor_groups({ immortal = 1 })
-- self.object:set_velocity(vector.new(0, 2, 0))
self.object:set_acceleration(vector.new(0, -get_gravity(), 0))
self.object:set_armor_groups({immortal = 1})
-- self.object:set_velocity({x = 0, y = 2, z = 0})
self.object:set_acceleration({x = 0, y = -get_gravity(), z = 0})
self:set_item(self.itemstring)
end,
@ -726,9 +710,9 @@ minetest.register_entity(":__builtin:item", {
local stack = ItemStack(entity.itemstring)
local name = stack:get_name()
if own_stack:get_name() ~= name or
own_stack:get_meta() ~= stack:get_meta() or
own_stack:get_wear() ~= stack:get_wear() or
own_stack:get_free_space() == 0 then
own_stack:get_meta() ~= stack:get_meta() or
own_stack:get_wear() ~= stack:get_wear() or
own_stack:get_free_space() == 0 then
-- Can not merge different or full stack
return false
end
@ -761,8 +745,8 @@ minetest.register_entity(":__builtin:item", {
self.object:set_properties({
physical = false
})
self.object:set_velocity(vector.zero())
self.object:set_acceleration(vector.zero())
self.object:set_velocity({x=0,y=0,z=0})
self.object:set_acceleration({x=0,y=0,z=0})
return
end
self.age = self.age + dtime
@ -777,22 +761,21 @@ minetest.register_entity(":__builtin:item", {
-- Delete corrupted item entities. The itemstring MUST be non-empty on its first step,
-- otherwise there might have some data corruption.
if self.itemstring == "" then
minetest.log("warning",
"Item entity with empty itemstring found at " .. minetest.pos_to_string(self.object:get_pos()) ..
"! Deleting it now.")
minetest.log("warning", "Item entity with empty itemstring found at "..minetest.pos_to_string(self.object:get_pos()).. "! Deleting it now.")
self._removed = true
self.object:remove()
return
end
local p = self.object:get_pos()
-- If hopper has taken item, it has gone, and no operations should be conducted on this item
if hopper_take_item(self, p) then
return
end
local node = minetest.get_node(p)
local in_unloaded = node.name == "ignore"
local node = minetest.get_node_or_nil(p)
local in_unloaded = (node == nil)
if in_unloaded then
-- Don't infinetly fall into unloaded map
@ -802,27 +785,27 @@ minetest.register_entity(":__builtin:item", {
if self.is_clock then
self.object:set_properties({
textures = { "mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame) }
textures = {"mcl_clock:clock_" .. (mcl_worlds.clock_works(p) and mcl_clock.old_time or mcl_clock.random_frame)}
})
end
local nn = node.name
local is_in_water = (minetest.get_item_group(nn, "liquid") ~= 0)
local nn_above = minetest.get_node(vector.offset(p, 0, 0.1, 0)).name
local nn_above = minetest.get_node({x=p.x, y=p.y+0.1, z=p.z}).name
-- make sure it's more or less stationary and is at water level
local sleep_threshold = 0.3
local is_floating = false
local is_stationary = math.abs(self.object:get_velocity().x) < sleep_threshold
and math.abs(self.object:get_velocity().y) < sleep_threshold
and math.abs(self.object:get_velocity().z) < sleep_threshold
and math.abs(self.object:get_velocity().y) < sleep_threshold
and math.abs(self.object:get_velocity().z) < sleep_threshold
if is_in_water and is_stationary then
is_floating = (is_in_water
and (minetest.get_item_group(nn_above, "liquid") == 0))
end
if is_floating and self.physical_state == true then
self.object:set_velocity(vector.zero())
self.object:set_acceleration(vector.zero())
self.object:set_velocity({x = 0, y = 0, z = 0})
self.object:set_acceleration({x = 0, y = 0, z = 0})
disable_physics(self.object, self)
end
-- If no collector was found for a long enough time, declare the magnet as disabled
@ -842,7 +825,7 @@ minetest.register_entity(":__builtin:item", {
--Wait 2 seconds to allow mob drops to be cooked, & picked up instead of instantly destroyed.
if self.age > 2 and minetest.get_item_group(self.itemstring, "fire_immune") == 0 then
if dg ~= 2 then
minetest.sound_play("builtin_item_lava", { pos = self.object:get_pos(), gain = 0.5 })
minetest.sound_play("builtin_item_lava", {pos = self.object:get_pos(), gain = 0.5})
end
self._removed = true
self.object:remove()
@ -882,7 +865,7 @@ minetest.register_entity(":__builtin:item", {
end
-- Check which one of the 4 sides is free
for o = 1, #order do
for o=1, #order do
local nn = minetest.get_node(vector.add(p, order[o])).name
local def = minetest.registered_nodes[nn]
if def and def.walkable == false and nn ~= "ignore" then
@ -892,7 +875,7 @@ minetest.register_entity(":__builtin:item", {
end
-- If none of the 4 sides is free, shoot upwards
if shootdir == nil then
shootdir = vector.new(0, 1, 0)
shootdir = { x=0, y=1, z=0 }
local nn = minetest.get_node(vector.add(p, shootdir)).name
if nn == "ignore" then
-- Do not push into ignore
@ -902,7 +885,7 @@ minetest.register_entity(":__builtin:item", {
-- Set new item moving speed accordingly
local newv = vector.multiply(shootdir, 3)
self.object:set_acceleration(vector.zero())
self.object:set_acceleration({x = 0, y = 0, z = 0})
self.object:set_velocity(newv)
disable_physics(self.object, self, false, false)
@ -924,10 +907,10 @@ minetest.register_entity(":__builtin:item", {
if self._forcetimer > 0 then
local cbox = self.object:get_properties().collisionbox
local ok = false
if self._force.x > 0 and (p.x > (self._forcestart.x + 0.5 + (cbox[4] - cbox[1]) / 2)) then ok = true
elseif self._force.x < 0 and (p.x < (self._forcestart.x + 0.5 - (cbox[4] - cbox[1]) / 2)) then ok = true
elseif self._force.z > 0 and (p.z > (self._forcestart.z + 0.5 + (cbox[6] - cbox[3]) / 2)) then ok = true
elseif self._force.z < 0 and (p.z < (self._forcestart.z + 0.5 - (cbox[6] - cbox[3]) / 2)) then ok = true end
if self._force.x > 0 and (p.x > (self._forcestart.x + 0.5 + (cbox[4] - cbox[1])/2)) then ok = true
elseif self._force.x < 0 and (p.x < (self._forcestart.x + 0.5 - (cbox[4] - cbox[1])/2)) then ok = true
elseif self._force.z > 0 and (p.z > (self._forcestart.z + 0.5 + (cbox[6] - cbox[3])/2)) then ok = true
elseif self._force.z < 0 and (p.z < (self._forcestart.z + 0.5 - (cbox[6] - cbox[3])/2)) then ok = true end
-- Item was successfully forced out. No more pushing
if ok then
self._forcetimer = -1
@ -958,7 +941,7 @@ minetest.register_entity(":__builtin:item", {
-- Set new item moving speed into the direciton of the liquid
local newv = vector.multiply(vec, f)
-- Swap to acceleration instead of a static speed to better mimic MC mechanics.
self.object:set_acceleration(vector.new(newv.x, -0.22, newv.z))
self.object:set_acceleration({x = newv.x, y = -0.22, z = newv.z})
self.physical_state = true
self._flowing = true
@ -971,10 +954,9 @@ minetest.register_entity(":__builtin:item", {
local cur_vec = self.object:get_velocity()
-- apply some acceleration in the opposite direction so it doesn't slide forever
local vec = {
x = 0 - cur_vec.x * 0.9,
y = 3 - cur_vec.y * 0.9,
z = 0 - cur_vec.z * 0.9
}
x = 0 -cur_vec.x*0.9,
y = 3 -cur_vec.y*0.9,
z = 0 -cur_vec.z*0.9}
self.object:set_acceleration(vec)
-- slow down the item in water
local vel = self.object:get_velocity()
@ -998,20 +980,20 @@ minetest.register_entity(":__builtin:item", {
end
-- If node is not registered or node is walkably solid and resting on nodebox
local nn = minetest.get_node(vector.offset(p, 0, -0.5, 0)).name
local nn = minetest.get_node({x=p.x, y=p.y-0.5, z=p.z}).name
local def = minetest.registered_nodes[nn]
local v = self.object:get_velocity()
local is_on_floor = def and (def.walkable
and not def.groups.slippery and v.y == 0)
if not minetest.registered_nodes[nn]
or is_floating or is_on_floor then
or is_floating or is_on_floor then
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
-- Merge with close entities of the same item
for _, object in pairs(minetest.get_objects_inside_radius(p, 0.8)) do
local obj = object:get_luaentity()
if obj and obj.name == "__builtin:item"
and obj.physical_state == false then
and obj.physical_state == false then
if self:try_merge_with(own_stack, object, obj) then
return
end

View File

@ -1,36 +0,0 @@
# textdomain: mcl_minecarts
Minecart=トロッコ
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.=トロッコはレール上にしか乗らない為、常に線路に沿って走ります。直進できない丁字路では、取り敢えず左折します。速度はレールの種類によって異なります。
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.=トロッコを入手するには、スニークキーを押しながらパンチします。
A minecart with TNT is an explosive vehicle that travels on rail.=TNT付きトロッコは、レール上を行きかう爆薬車両です。
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.=レール上に配置。パンチで移動。TNTが着火するのは、火打石と打金を使った時か、稼動中のアクティベーターレール上にトロッコが乗った時です。
To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited.=トロッコとTNTを入手するには、スニークキーを押しながらパンチしてください。TNTに火が着いていた場合は、無理です。
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.=レールの上に置きます。石炭を与えると、かまどが長時間燃え続け、トロッコが自走可能になります。パンチすると動き出します。
To obtain the minecart and furnace, punch them while holding down the sneak key.=トロッコとかまどを入手するには、スニークキーを押しながらパンチします。
Minecart with Chest=チェスト付きトロッコ
Minecart with Furnace=かまど付きトロッコ
Minecart with Command Block=コマンドブロック付きトロッコ
Minecart with Hopper=ホッパー付きトロッコ
Minecart with TNT=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.=地面に置いて線路を作ると、レール同士が自動的につながり、必要に応じてカーブや丁字路、踏切、坂道などに変化します。
Rail=レール
Rails can be used to build transport tracks for minecarts. Normal rails slightly slow down minecarts due to friction.=レールを利用して、トロッコの輸送路が敷けます。普通のレールは、摩擦の関係でトロッコが少しずつ減速していきます。
Powered Rail=パワードレール
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.=レッドストーン動力なしだと、このレールはトロッコにブレーキをかけます。このレールでトロッコを加速させるには、レッドストーン動力を供給してください。
Activator Rail=アクティベーターレール
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.=このレールでトロッコを作動させるには、レッドストーン動力を与えたレール上にトロッコを送り込みます。
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.=レールを利用して、トロッコの輸送路が敷けます。ディテクターレールは、その上にあるトロッコを検知でき、その際レッドストーン機構の動力源となります。
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=トロッコ用の線路
Speed up when powered, slow down when not powered=稼動中は加速、非稼動中は減速
Activates minecarts when powered=稼動中はトロッコを作動
Emits redstone power when a minecart is detected=トロッコを検知するとレッドストーン動力を放出
Vehicle for fast travel on rails=レール上を快速移動するための車両
Can be ignited by tools or powered activator rail=道具や稼動中のアクティベーターレールにより着火が可能
Sneak to dismount=スニークで降りる

View File

@ -2,6 +2,8 @@ local mob_class = mcl_mobs.mob_class
local mob_class_meta = {__index = mcl_mobs.mob_class}
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
-- API for Mobs Redo: MineClone 2 Edition (MRM)
local MAX_MOB_NAME_LENGTH = 30
local DEFAULT_FALL_SPEED = -9.81*1.5
local PATHFINDING = "gowp"
@ -15,9 +17,21 @@ local function mcl_log (message)
end
end
-- Invisibility mod check
mcl_mobs.invis = {}
-- localize math functions
local atann = math.atan
local function atan(x)
if not x or x ~= x then
return 0
else
return atann(x)
end
end
local remove_far = true
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
@ -283,65 +297,51 @@ function mob_class:mob_activate(staticdata, def, dtime)
end
end
-- execute current state (stand, walk, run, attacks)
-- returns true if mob has died
function mob_class:do_states(dtime)
--if self.can_open_doors then check_doors(self) end
if self.state == "stand" then
self:do_states_stand()
elseif self.state == PATHFINDING then
self:check_gowp(dtime)
elseif self.state == "walk" then
self:do_states_walk()
elseif self.state == "runaway" then
-- runaway when punched
self:do_states_runaway()
elseif self.state == "attack" then
-- attack routines (explode, dogfight, shoot, dogshoot)
if self:do_states_attack(dtime) then
return true
end
end
end
local function update_timers (self, dtime)
-- knockback timer. set in on_punch
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
return true
end
-- attack timer
self.timer = self.timer + dtime
if self.state ~= "attack" and self.state ~= PATHFINDING then
if self.timer < 1 then
return true
end
self.timer = 0
end
-- never go over 100
if self.timer > 100 then
self.timer = 1
end
end
-- main mob function
function mob_class:on_step(dtime)
self.lifetimer = self.lifetimer - dtime
local pos = self.object:get_pos()
if not pos then return end
if self:check_despawn(pos) then return true end
if self:check_despawn(pos, dtime) then return true end
local d = 0.85
if self:check_dying() then d = 0.92 end
local v = self.object:get_velocity()
if v then
--diffuse object velocity
self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d})
end
self:slow_mob()
if self:falling(pos) then return end
self:check_suspend()
self:check_suspend()
self:check_water_flow()
self:env_danger_movement_checks (dtime)
local yaw = 0
if self:is_at_water_danger() and self.state ~= "attack" then
if math.random(1, 10) <= 6 then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
else
if self.move_in_group ~= false then
self:check_herd(dtime)
end
end
if self:is_at_cliff_or_danger() then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
end
if not self.fire_resistant then
mcl_burning.tick(self.object, dtime, self)
@ -353,17 +353,21 @@ function mob_class:on_step(dtime)
if self.state == "die" then return end
self:follow_flop() -- Mob following code.
self:set_animation_speed() -- set animation speed relitive to velocity
self:check_smooth_rotation(dtime)
self:check_head_swivel(dtime)
if self.jump_sound_cooloff > 0 then
self.jump_sound_cooloff = self.jump_sound_cooloff - dtime
end
self:do_jump()
if self.opinion_sound_cooloff > 0 then
self.opinion_sound_cooloff = self.opinion_sound_cooloff - dtime
end
--Mob following code.
self:follow_flop()
--set animation speed relitive to velocity
self:set_animation_speed()
self:check_smooth_rotation(dtime)
self:check_head_swivel(dtime)
self:do_jump()
self:set_armor_texture()
self:check_runaway_from()
@ -374,26 +378,63 @@ function mob_class:on_step(dtime)
-- run custom function (defined in mob lua file)
if self.do_custom then
-- when false skip going any further
if self.do_custom(self, dtime) == false then
return
end
end
if update_timers(self, dtime) then return end
-- knockback timer
if self.pause_timer > 0 then
self.pause_timer = self.pause_timer - dtime
return
end
-- attack timer
self.timer = self.timer + dtime
if self.state ~= "attack" and self.state ~= PATHFINDING then
if self.timer < 1 then
return
end
self.timer = 0
end
self:check_particlespawners(dtime)
self:check_item_pickup()
if self.opinion_sound_cooloff > 0 then
self.opinion_sound_cooloff = self.opinion_sound_cooloff - dtime
-- never go over 100
if self.timer > 100 then
self.timer = 1
end
-- mob plays random sound at times. Should be 120. Zombie and mob farms are ridiculous
-- mob plays random sound at times
if math.random(1, 70) == 1 then
self:mob_sound("random", true)
end
if self:env_damage (dtime, pos) then return end
if self:do_states(dtime) then return end
-- environmental damage timer (every 1 second)
self.env_damage_timer = self.env_damage_timer + dtime
if (self.state == "attack" and self.env_damage_timer > 1)
or self.state ~= "attack" then
self:check_entity_cramming()
self.env_damage_timer = 0
-- check for environmental damage (water, fire, lava etc.)
if self:do_env_damage() then
return
end
-- node replace check (cow eats grass etc.)
self:replace(pos)
end
if self:do_states(dtime) then
return
end
if not self.object:get_luaentity() then
return false

View File

@ -1,8 +1,9 @@
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
local mob_class = mcl_mobs.mob_class
local HORNY_TIME = 30*20
local HORNY_AGAIN_TIME = 30*20 -- was 300 or 15*20
local MAX_MOB_NAME_LENGTH = 30
local HORNY_TIME = 30
local HORNY_AGAIN_TIME = 300
local CHILD_GROW_TIME = 60*20
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
@ -178,7 +179,7 @@ function mob_class:check_breeding()
-- jump when fully grown so as not to fall into ground
self.object:set_velocity({
x = 0,
y = self.jump_height,
y = self.jump_height*3,
z = 0
})
end
@ -190,16 +191,18 @@ function mob_class:check_breeding()
end
return
else
-- horny animal can mate for HORNY_TIME seconds,
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
if self.horny == true then
self.hornytimer = self.hornytimer + 1
end
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
self.hornytimer = 0
self.horny = false
end
-- horny animal can mate for HORNY_TIME seconds,
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
if self.horny == true
and self.hornytimer < HORNY_TIME + HORNY_AGAIN_TIME then
self.hornytimer = self.hornytimer + 1
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
self.hornytimer = 0
self.horny = false
end
end

View File

@ -8,18 +8,6 @@ local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
local stuck_path_timeout = 10 -- how long will mob follow path before giving up
local enable_pathfinding = true
local atann = math.atan
local function atan(x)
if not x or x ~= x then
return 0
else
return atann(x)
end
end
-- check if daytime and also if mob is docile during daylight hours
function mob_class:day_docile()
if self.docile_by_day == false then
@ -806,397 +794,3 @@ function mob_class:check_aggro(dtime)
end
self._check_aggro_timer = self._check_aggro_timer + dtime
end
function mob_class:do_states_attack (dtime)
local yaw = self.object:get_yaw() or 0
local s = self.object:get_pos()
local p = self.attack:get_pos() or s
-- stop attacking if player invisible or out of range
if not self.attack
or not self.attack:get_pos()
or not self:object_in_range(self.attack)
or self.attack:get_hp() <= 0
or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then
self.state = "stand"
self:set_velocity( 0)
self:set_animation( "stand")
self.attack = nil
self.v_start = false
self.timer = 0
self.blinktimer = 0
self.path.way = nil
return
end
-- calculate distance from mob and enemy
local dist = vector.distance(p, s)
if self.attack_type == "explode" then
local vec = {
x = p.x - s.x,
z = p.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if p.x > s.x then yaw = yaw +math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
local node_break_radius = self.explosion_radius or 1
local entity_damage_radius = self.explosion_damage_radius
or (node_break_radius * 2)
-- start timer when in reach and line of sight
if not self.v_start
and dist <= self.reach
and self:line_of_sight( s, p, 2) then
self.v_start = true
self.timer = 0
self.blinktimer = 0
self:mob_sound("fuse", nil, false)
-- stop timer if out of reach or direct line of sight
elseif self.allow_fuse_reset
and self.v_start
and (dist >= self.explosiontimer_reset_radius
or not self:line_of_sight( s, p, 2)) then
self.v_start = false
self.timer = 0
self.blinktimer = 0
self.blinkstatus = false
self:remove_texture_mod("^[brighten")
end
-- walk right up to player unless the timer is active
if self.v_start and (self.stop_to_explode or dist < self.reach) then
self:set_velocity( 0)
else
self:set_velocity( self.run_velocity)
end
if self.animation and self.animation.run_start then
self:set_animation( "run")
else
self:set_animation( "walk")
end
if self.v_start then
self.timer = self.timer + dtime
self.blinktimer = (self.blinktimer or 0) + dtime
if self.blinktimer > 0.2 then
self.blinktimer = 0
if self.blinkstatus then
self:remove_texture_mod("^[brighten")
else
self:add_texture_mod("^[brighten")
end
self.blinkstatus = not self.blinkstatus
end
if self.timer > self.explosion_timer then
local pos = self.object:get_pos()
if mobs_griefing and not minetest.is_protected(pos, "") then
mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object)
else
minetest.sound_play(self.sounds.explode, {
pos = pos,
gain = 1.0,
max_hear_distance = self.sounds.distance or 32
}, true)
self:entity_physics(pos,entity_damage_radius)
mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0)
end
mcl_burning.extinguish(self.object)
self.object:remove()
return true
end
end
elseif self.attack_type == "dogfight"
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy)
or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then
if self.fly
and dist > self.reach then
local p1 = s
local me_y = math.floor(p1.y)
local p2 = p
local p_y = math.floor(p2.y + 1)
local v = self.object:get_velocity()
if self:flight_check( s) then
if me_y < p_y then
self.object:set_velocity({
x = v.x,
y = 1 * self.walk_velocity,
z = v.z
})
elseif me_y > p_y then
self.object:set_velocity({
x = v.x,
y = -1 * self.walk_velocity,
z = v.z
})
end
else
if me_y < p_y then
self.object:set_velocity({
x = v.x,
y = 0.01,
z = v.z
})
elseif me_y > p_y then
self.object:set_velocity({
x = v.x,
y = -0.01,
z = v.z
})
end
end
end
-- rnd: new movement direction
if self.path.following
and self.path.way
and self.attack_type ~= "dogshoot" then
-- no paths longer than 50
if #self.path.way > 50
or dist < self.reach then
self.path.following = false
return
end
local p1 = self.path.way[1]
if not p1 then
self.path.following = false
return
end
if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then
-- reached waypoint, remove it from queue
table.remove(self.path.way, 1)
end
-- set new temporary target
p = {x = p1.x, y = p1.y, z = p1.z}
end
local vec = {
x = p.x - s.x,
z = p.z - s.z
}
yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate
if p.x > s.x then yaw = yaw + math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
-- move towards enemy if beyond mob reach
if dist > self.reach then
-- path finding by rnd
if self.pathfinding -- only if mob has pathfinding enabled
and enable_pathfinding then
self:smart_mobs(s, p, dist, dtime)
end
if self:is_at_cliff_or_danger() then
self:set_velocity( 0)
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
else
if self.path.stuck then
self:set_velocity( self.walk_velocity)
else
self:set_velocity( self.run_velocity)
end
if self.animation and self.animation.run_start then
self:set_animation( "run")
else
self:set_animation( "walk")
end
end
else -- rnd: if inside reach range
self.path.stuck = false
self.path.stuck_timer = 0
self.path.following = false -- not stuck anymore
self:set_velocity( 0)
if not self.custom_attack then
if self.timer > 1 then
self.timer = 0
if self.double_melee_attack
and math.random(1, 2) == 1 then
self:set_animation( "punch2")
else
self:set_animation( "punch")
end
local p2 = p
local s2 = s
p2.y = p2.y + .5
s2.y = s2.y + .5
if self:line_of_sight( p2, s2) == true then
-- play attack sound
self:mob_sound("attack")
-- punch player (or what player is attached to)
local attached = self.attack:get_attach()
if attached then
self.attack = attached
end
self.attack:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy = self.damage}
}, nil)
end
end
else -- call custom attack every second
if self.custom_attack
and self.timer > 1 then
self.timer = 0
self.custom_attack(self, p)
end
end
end
elseif self.attack_type == "shoot"
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1)
or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then
p.y = p.y - .5
s.y = s.y + .5
local dist = vector.distance(p, s)
local vec = {
x = p.x - s.x,
y = p.y - s.y,
z = p.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if p.x > s.x then yaw = yaw +math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
local stay_away_from_player = vector.new(0,0,0)
--strafe back and fourth
--stay away from player so as to shoot them
if dist < self.avoid_distance and self.shooter_avoid_enemy then
self:set_animation( "shoot")
stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33)
end
if self.strafes then
if not self.strafe_direction then
self.strafe_direction = 1.57
end
if math.random(40) == 1 then
self.strafe_direction = self.strafe_direction*-1
end
self.acc = vector.add(vector.multiply(vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction), 0.3*self.walk_velocity), stay_away_from_player)
else
self:set_velocity( 0)
end
local p = self.object:get_pos()
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2
if self.shoot_interval
and self.timer > self.shoot_interval
and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next()
and math.random(1, 100) <= 60 then
self.timer = 0
self:set_animation( "shoot")
-- play shoot attack sound
self:mob_sound("shoot_attack")
-- Shoot arrow
if minetest.registered_entities[self.arrow] then
local arrow, ent
local v = 1
if not self.shoot_arrow then
self.firing = true
minetest.after(1, function()
self.firing = false
end)
arrow = minetest.add_entity(p, self.arrow)
ent = arrow:get_luaentity()
if ent.velocity then
v = ent.velocity
end
ent.switch = 1
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
-- important for mcl_shields
ent._shooter = self.object
ent._saved_shooter_pos = self.object:get_pos()
end
local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5
-- offset makes shoot aim accurate
vec.y = vec.y + self.shoot_offset
vec.x = vec.x * (v / amount)
vec.y = vec.y * (v / amount)
vec.z = vec.z * (v / amount)
if self.shoot_arrow then
vec = vector.normalize(vec)
self:shoot_arrow(p, vec)
else
arrow:set_velocity(vec)
end
end
end
else
end
end

View File

@ -116,7 +116,6 @@ function mob_class:mob_sound(soundname, is_opinion, fixed_pitch)
-- randomize the pitch a bit
pitch = pitch + math.random(-10, 10) * 0.005
end
-- Should be 0.1 to 0.2 for mobs. Cow and zombie farms loud. At least have cool down.
minetest.sound_play(sound, {
object = self.object,
gain = 1.0,
@ -280,55 +279,30 @@ local function dir_to_pitch(dir)
return -math.atan2(-dir.y, xz)
end
local function who_are_you_looking_at (self)
function mob_class:check_head_swivel(dtime)
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
local final_rotation = vector.new(0,0,0)
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
local pos = self.object:get_pos()
local stop_look_at_player_chance = math.random(833/self.curiosity)
-- was 10000 - div by 12 for avg entities as outside loop
local stop_look_at_player = stop_look_at_player_chance == 1
if self.attack or self.following then
self._locked_object = self.attack or self.following
elseif self._locked_object then
if stop_look_at_player then
--minetest.log("Stop look: ".. self.name)
self._locked_object = nil
end
elseif not self._locked_object then
if math.random(1, 30) then
--minetest.log("Change look check: ".. self.name)
local look_at_player_chance = math.random(20/self.curiosity)
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
-- then div by 20 as less freq lookup
local look_at_player = look_at_player_chance == 1
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 8)) do
if obj:is_player() and vector.distance(pos,obj:get_pos()) < 4 then
--minetest.log("Change look to player: ".. self.name)
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 10)) do
if obj:is_player() and not self.attack or obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity() then
if not self._locked_object then
if math.random(5000/self.curiosity) == 1 or vector.distance(pos,obj:get_pos())<4 and obj:is_player() then
self._locked_object = obj
break
elseif obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity()) then
if look_at_player then
--minetest.log("Change look to mob: ".. self.name)
self._locked_object = obj
break
end
end
else
if math.random(10000/self.curiosity) == 1 then
self._locked_object = nil
end
end
end
end
end
function mob_class:check_head_swivel(dtime)
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
who_are_you_looking_at (self)
local final_rotation = vector.new(0,0,0)
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
if self.attack or self.following then
self._locked_object = self.attack or self.following
end
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
local _locked_object_eye_height = 1.5
@ -343,7 +317,6 @@ function mob_class:check_head_swivel(dtime)
if self.object:get_attach() then
self_rot = self.object:get_attach():get_rotation()
end
if self.rot then
local player_pos = self._locked_object:get_pos()
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
@ -372,7 +345,7 @@ function mob_class:check_head_swivel(dtime)
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
final_rotation = vector.multiply(oldr, 0.9)
else
--final_rotation = vector.new(0,0,0)
final_rotation = vector.new(0,0,0)
end
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), final_rotation)

View File

@ -1,11 +0,0 @@
# textdomain: mcl_mobs
Peaceful mode active! No monsters will spawn.=ピースフルモード有効! モンスターは出現しません。
This allows you to place a single mob.=これにより、1体の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.=MOBを出現させたい場所に、置くだけです。スニークキーを押しながら配置しない限り、動物は飼いならされた状態でスポーンします。MOBスポナーに設置すると、スポーンするMOBが変わります。
You need the “maphack” privilege to change the mob spawner.=MOBスポナーを変更するには、"maphack"権限が必要です。
Name Tag=名札
A name tag is an item to name a mob.=名札は、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.=名札を使用する前に、金床で名前の設定をする必要があります。その後、MOBに名前をつけるために名札が使えます。これで名札は使い切ります。
Only peaceful mobs allowed!=平和的なMOBのみ許可
Give names to mobs=MOBに名前を付与
Set name at anvil=金床で名前の設定

View File

@ -4,6 +4,7 @@ local DEFAULT_FALL_SPEED = -9.81*1.5
local FLOP_HEIGHT = 6
local FLOP_HOR_SPEED = 1.5
local PATHFINDING = "gowp"
local enable_pathfinding = true
local node_ice = "mcl_core:ice"
local node_snowblock = "mcl_core:snowblock"
@ -268,36 +269,12 @@ function mob_class:is_at_water_danger()
return false
end
function mob_class:env_danger_movement_checks(dtime)
local yaw = 0
if self:is_at_water_danger() and self.state ~= "attack" then
if math.random(1, 10) <= 6 then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
else
if self.move_in_group ~= false then
self:check_herd(dtime)
end
end
if self:is_at_cliff_or_danger() then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
end
end
-- jump if facing a solid node (not fences or gates)
function mob_class:do_jump()
if not self.jump
or self.jump_height == 0
or self.fly
or (self.child and self.type ~= "monster")
or self.order == "stand" then
return false
end
@ -793,190 +770,591 @@ function mob_class:teleport(target)
end
end
function mob_class:do_states_walk()
-- execute current state (stand, walk, run, attacks)
-- returns true if mob has died
function mob_class:do_states(dtime)
--if self.can_open_doors then check_doors(self) end
local yaw = self.object:get_yaw() or 0
local s = self.object:get_pos()
local lp = nil
-- is there something I need to avoid?
if (self.water_damage > 0
and self.lava_damage > 0)
or self.breath_max ~= -1 then
lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"})
elseif self.water_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:water"})
elseif self.lava_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:lava"})
elseif self.fire_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:fire"})
end
local is_in_danger = false
if lp then
-- If mob in or on dangerous block, look for land
if (self:is_node_dangerous(self.standing_in) or
self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then
is_in_danger = true
-- If mob in or on dangerous block, look for land
if is_in_danger then
-- Better way to find shore - copied from upstream
lp = minetest.find_nodes_in_area_under_air(
{x = s.x - 5, y = s.y - 0.5, z = s.z - 5},
{x = s.x + 5, y = s.y + 1, z = s.z + 5},
{"group:solid"})
lp = #lp > 0 and lp[math.random(#lp)]
-- did we find land?
if lp then
local vec = {
x = lp.x - s.x,
z = lp.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if lp.x > s.x then yaw = yaw +math.pi end
-- look towards land and move in that direction
yaw = self:set_yaw( yaw, 6)
self:set_velocity(self.walk_velocity)
if self.state == "stand" then
if math.random(1, 4) == 1 then
local s = self.object:get_pos()
local objs = minetest.get_objects_inside_radius(s, 3)
local lp
for n = 1, #objs do
if objs[n]:is_player() then
lp = objs[n]:get_pos()
break
end
end
-- A danger is near but mob is not inside
else
-- look at any players nearby, otherwise turn randomly
if lp and self.look_at_players then
-- Randomly turn
if math.random(1, 100) <= 30 then
local vec = {
x = lp.x - s.x,
z = lp.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if lp.x > s.x then yaw = yaw +math.pi end
else
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
yaw = self:set_yaw( yaw, 8)
end
if self.order == "sit" then
self:set_animation( "sit")
self:set_velocity(0)
else
self:set_animation( "stand")
self:set_velocity(0)
end
-- npc's ordered to stand stay standing
if self.order == "stand" or self.order == "sleep" or self.order == "work" then
else
if self.walk_chance ~= 0
and self.facing_fence ~= true
and math.random(1, 100) <= self.walk_chance
and self:is_at_cliff_or_danger() == false then
self:set_velocity(self.walk_velocity)
self.state = "walk"
self:set_animation( "walk")
end
end
yaw = self:set_yaw( yaw, 8)
elseif self.state == PATHFINDING then
self:check_gowp(dtime)
elseif self.state == "walk" then
local s = self.object:get_pos()
local lp = nil
-- is there something I need to avoid?
if (self.water_damage > 0
and self.lava_damage > 0)
or self.breath_max ~= -1 then
lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"})
elseif self.water_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:water"})
elseif self.lava_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:lava"})
elseif self.fire_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:fire"})
end
local is_in_danger = false
if lp then
-- If mob in or on dangerous block, look for land
if (self:is_node_dangerous(self.standing_in) or
self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then
is_in_danger = true
-- If mob in or on dangerous block, look for land
if is_in_danger then
-- Better way to find shore - copied from upstream
lp = minetest.find_nodes_in_area_under_air(
{x = s.x - 5, y = s.y - 0.5, z = s.z - 5},
{x = s.x + 5, y = s.y + 1, z = s.z + 5},
{"group:solid"})
lp = #lp > 0 and lp[math.random(#lp)]
-- did we find land?
if lp then
local vec = {
x = lp.x - s.x,
z = lp.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if lp.x > s.x then yaw = yaw +math.pi end
-- look towards land and move in that direction
yaw = self:set_yaw( yaw, 6)
self:set_velocity(self.walk_velocity)
end
end
-- A danger is near but mob is not inside
else
-- Randomly turn
if math.random(1, 100) <= 30 then
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
end
yaw = self:set_yaw( yaw, 8)
-- otherwise randomly turn
elseif math.random(1, 100) <= 30 then
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
-- stand for great fall or danger or fence in front
local cliff_or_danger = false
if is_in_danger then
cliff_or_danger = self:is_at_cliff_or_danger()
end
if self.facing_fence == true
or cliff_or_danger
or math.random(1, 100) <= 30 then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
else
self:set_velocity(self.walk_velocity)
if self:flight_check()
and self.animation
and self.animation.fly_start
and self.animation.fly_end then
self:set_animation( "fly")
else
self:set_animation( "walk")
elseif math.random(1, 100) <= 30 then
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
end
end
end
function mob_class:do_states_stand()
local yaw = self.object:get_yaw() or 0
-- stand for great fall or danger or fence in front
local cliff_or_danger = false
if is_in_danger then
cliff_or_danger = self:is_at_cliff_or_danger()
end
if self.facing_fence == true
or cliff_or_danger
or math.random(1, 100) <= 30 then
if math.random(1, 4) == 1 then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
else
local s = self.object:get_pos()
local objs = minetest.get_objects_inside_radius(s, 3)
local lp
for n = 1, #objs do
if objs[n]:is_player() then
lp = objs[n]:get_pos()
break
self:set_velocity(self.walk_velocity)
if self:flight_check()
and self.animation
and self.animation.fly_start
and self.animation.fly_end then
self:set_animation( "fly")
else
self:set_animation( "walk")
end
end
-- look at any players nearby, otherwise turn randomly
if lp and self.look_at_players then
-- runaway when punched
elseif self.state == "runaway" then
self.runaway_timer = self.runaway_timer + 1
-- stop after 5 seconds or when at cliff
if self.runaway_timer > 5
or self:is_at_cliff_or_danger() then
self.runaway_timer = 0
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
else
self:set_velocity( self.run_velocity)
self:set_animation( "run")
end
-- attack routines (explode, dogfight, shoot, dogshoot)
elseif self.state == "attack" then
local s = self.object:get_pos()
local p = self.attack:get_pos() or s
-- stop attacking if player invisible or out of range
if not self.attack
or not self.attack:get_pos()
or not self:object_in_range(self.attack)
or self.attack:get_hp() <= 0
or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then
self.state = "stand"
self:set_velocity( 0)
self:set_animation( "stand")
self.attack = nil
self.v_start = false
self.timer = 0
self.blinktimer = 0
self.path.way = nil
return
end
-- calculate distance from mob and enemy
local dist = vector.distance(p, s)
if self.attack_type == "explode" then
local vec = {
x = lp.x - s.x,
z = lp.z - s.z
x = p.x - s.x,
z = p.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if lp.x > s.x then yaw = yaw +math.pi end
if p.x > s.x then yaw = yaw +math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
local node_break_radius = self.explosion_radius or 1
local entity_damage_radius = self.explosion_damage_radius
or (node_break_radius * 2)
-- start timer when in reach and line of sight
if not self.v_start
and dist <= self.reach
and self:line_of_sight( s, p, 2) then
self.v_start = true
self.timer = 0
self.blinktimer = 0
self:mob_sound("fuse", nil, false)
-- stop timer if out of reach or direct line of sight
elseif self.allow_fuse_reset
and self.v_start
and (dist >= self.explosiontimer_reset_radius
or not self:line_of_sight( s, p, 2)) then
self.v_start = false
self.timer = 0
self.blinktimer = 0
self.blinkstatus = false
self:remove_texture_mod("^[brighten")
end
-- walk right up to player unless the timer is active
if self.v_start and (self.stop_to_explode or dist < self.reach) then
self:set_velocity( 0)
else
self:set_velocity( self.run_velocity)
end
if self.animation and self.animation.run_start then
self:set_animation( "run")
else
self:set_animation( "walk")
end
if self.v_start then
self.timer = self.timer + dtime
self.blinktimer = (self.blinktimer or 0) + dtime
if self.blinktimer > 0.2 then
self.blinktimer = 0
if self.blinkstatus then
self:remove_texture_mod("^[brighten")
else
self:add_texture_mod("^[brighten")
end
self.blinkstatus = not self.blinkstatus
end
if self.timer > self.explosion_timer then
local pos = self.object:get_pos()
if mobs_griefing and not minetest.is_protected(pos, "") then
mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object)
else
minetest.sound_play(self.sounds.explode, {
pos = pos,
gain = 1.0,
max_hear_distance = self.sounds.distance or 32
}, true)
self:entity_physics(pos,entity_damage_radius)
mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0)
end
mcl_burning.extinguish(self.object)
self.object:remove()
return true
end
end
elseif self.attack_type == "dogfight"
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy)
or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then
if self.fly
and dist > self.reach then
local p1 = s
local me_y = math.floor(p1.y)
local p2 = p
local p_y = math.floor(p2.y + 1)
local v = self.object:get_velocity()
if self:flight_check( s) then
if me_y < p_y then
self.object:set_velocity({
x = v.x,
y = 1 * self.walk_velocity,
z = v.z
})
elseif me_y > p_y then
self.object:set_velocity({
x = v.x,
y = -1 * self.walk_velocity,
z = v.z
})
end
else
if me_y < p_y then
self.object:set_velocity({
x = v.x,
y = 0.01,
z = v.z
})
elseif me_y > p_y then
self.object:set_velocity({
x = v.x,
y = -0.01,
z = v.z
})
end
end
end
-- rnd: new movement direction
if self.path.following
and self.path.way
and self.attack_type ~= "dogshoot" then
-- no paths longer than 50
if #self.path.way > 50
or dist < self.reach then
self.path.following = false
return
end
local p1 = self.path.way[1]
if not p1 then
self.path.following = false
return
end
if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then
-- reached waypoint, remove it from queue
table.remove(self.path.way, 1)
end
-- set new temporary target
p = {x = p1.x, y = p1.y, z = p1.z}
end
local vec = {
x = p.x - s.x,
z = p.z - s.z
}
yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate
if p.x > s.x then yaw = yaw + math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
-- move towards enemy if beyond mob reach
if dist > self.reach then
-- path finding by rnd
if self.pathfinding -- only if mob has pathfinding enabled
and enable_pathfinding then
self:smart_mobs(s, p, dist, dtime)
end
if self:is_at_cliff_or_danger() then
self:set_velocity( 0)
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
else
if self.path.stuck then
self:set_velocity( self.walk_velocity)
else
self:set_velocity( self.run_velocity)
end
if self.animation and self.animation.run_start then
self:set_animation( "run")
else
self:set_animation( "walk")
end
end
else -- rnd: if inside reach range
self.path.stuck = false
self.path.stuck_timer = 0
self.path.following = false -- not stuck anymore
self:set_velocity( 0)
if not self.custom_attack then
if self.timer > 1 then
self.timer = 0
if self.double_melee_attack
and math.random(1, 2) == 1 then
self:set_animation( "punch2")
else
self:set_animation( "punch")
end
local p2 = p
local s2 = s
p2.y = p2.y + .5
s2.y = s2.y + .5
if self:line_of_sight( p2, s2) == true then
-- play attack sound
self:mob_sound("attack")
-- punch player (or what player is attached to)
local attached = self.attack:get_attach()
if attached then
self.attack = attached
end
self.attack:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy = self.damage}
}, nil)
end
end
else -- call custom attack every second
if self.custom_attack
and self.timer > 1 then
self.timer = 0
self.custom_attack(self, p)
end
end
end
elseif self.attack_type == "shoot"
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1)
or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then
p.y = p.y - .5
s.y = s.y + .5
local dist = vector.distance(p, s)
local vec = {
x = p.x - s.x,
y = p.y - s.y,
z = p.z - s.z
}
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
if p.x > s.x then yaw = yaw +math.pi end
yaw = self:set_yaw( yaw, 0, dtime)
local stay_away_from_player = vector.new(0,0,0)
--strafe back and fourth
--stay away from player so as to shoot them
if dist < self.avoid_distance and self.shooter_avoid_enemy then
self:set_animation( "shoot")
stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33)
end
if self.strafes then
if not self.strafe_direction then
self.strafe_direction = 1.57
end
if math.random(40) == 1 then
self.strafe_direction = self.strafe_direction*-1
end
self.acc = vector.add(vector.multiply(vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction), 0.3*self.walk_velocity), stay_away_from_player)
else
self:set_velocity( 0)
end
local p = self.object:get_pos()
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2
if self.shoot_interval
and self.timer > self.shoot_interval
and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next()
and math.random(1, 100) <= 60 then
self.timer = 0
self:set_animation( "shoot")
-- play shoot attack sound
self:mob_sound("shoot_attack")
-- Shoot arrow
if minetest.registered_entities[self.arrow] then
local arrow, ent
local v = 1
if not self.shoot_arrow then
self.firing = true
minetest.after(1, function()
self.firing = false
end)
arrow = minetest.add_entity(p, self.arrow)
ent = arrow:get_luaentity()
if ent.velocity then
v = ent.velocity
end
ent.switch = 1
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
-- important for mcl_shields
ent._shooter = self.object
ent._saved_shooter_pos = self.object:get_pos()
end
local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5
-- offset makes shoot aim accurate
vec.y = vec.y + self.shoot_offset
vec.x = vec.x * (v / amount)
vec.y = vec.y * (v / amount)
vec.z = vec.z * (v / amount)
if self.shoot_arrow then
vec = vector.normalize(vec)
self:shoot_arrow(p, vec)
else
arrow:set_velocity(vec)
end
end
end
else
yaw = yaw + math.random(-0.5, 0.5)
end
yaw = self:set_yaw( yaw, 8)
end
if self.order == "sit" then
self:set_animation( "sit")
self:set_velocity(0)
else
self:set_animation( "stand")
self:set_velocity(0)
end
-- npc's ordered to stand stay standing
if self.order == "stand" or self.order == "sleep" or self.order == "work" then
else
if self.walk_chance ~= 0
and self.facing_fence ~= true
and math.random(1, 100) <= self.walk_chance
and self:is_at_cliff_or_danger() == false then
self:set_velocity(self.walk_velocity)
self.state = "walk"
self:set_animation( "walk")
end
end
end
function mob_class:do_states_runaway()
local yaw = self.object:get_yaw() or 0
self.runaway_timer = self.runaway_timer + 1
-- stop after 5 seconds or when at cliff
if self.runaway_timer > 5
or self:is_at_cliff_or_danger() then
self.runaway_timer = 0
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
else
self:set_velocity( self.run_velocity)
self:set_animation( "run")
end
end
function mob_class:check_smooth_rotation(dtime)
-- smooth rotation by ThomasMonroe314
if self._turn_to then
@ -1020,7 +1398,7 @@ function mob_class:check_smooth_rotation(dtime)
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
end
self.object:set_yaw(yaw)
--self:update_roll()
self:update_roll()
end
-- end rotation
end

View File

@ -1,24 +1,11 @@
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
local mob_class = mcl_mobs.mob_class
local PATHFINDING_FAIL_THRESHOLD = 100 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door
local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
local PATHFINDING = "gowp"
local enable_pathfinding = true
local one_down = vector.new(0,-1,0)
local one_up = vector.new(0,1,0)
local plane_adjacents = {
vector.new(1,0,0),
vector.new(-1,0,0),
vector.new(0,0,1),
vector.new(0,0,-1),
}
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false)
local LOG_MODULE = "[Mobs Pathfinding]"
local LOG_MODULE = "[Mobs]"
local function mcl_log (message)
if LOGGING_ON and message then
minetest.log(LOG_MODULE .. " " .. message)
@ -34,7 +21,7 @@ function output_table (wp)
end
function append_paths (wp1, wp2)
--mcl_log("Start append")
mcl_log("Start append")
if not wp1 or not wp2 then
mcl_log("Cannot append wp's")
return
@ -44,7 +31,7 @@ function append_paths (wp1, wp2)
for _,a in pairs (wp2) do
table.insert(wp1, a)
end
--mcl_log("End append")
mcl_log("End append")
end
local function output_enriched (wp_out)
@ -56,12 +43,11 @@ local function output_enriched (wp_out)
local action = outy["action"]
if action then
--mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
mcl_log("type: " .. action["type"])
mcl_log("action: " .. action["action"])
mcl_log("target: " .. minetest.pos_to_string(action["target"]))
end
--mcl_log("failed attempts: " .. outy["failed_attempts"])
mcl_log("failed attempts: " .. outy["failed_attempts"])
end
end
@ -70,26 +56,34 @@ end
-- an action, such as to open or close a door where we know that pos requires that action
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
local wp_out = {}
-- TODO Just pass in door position and the index before is open, the index after is close
local current_door_index = -1
for i, cur_pos in pairs(wp_in) do
local action = nil
local one_down = vector.new(0,-1,0)
local cur_pos_to_add = vector.add(cur_pos, one_down)
if door_open_pos and vector.equals (cur_pos, door_open_pos) then
mcl_log ("Door open match")
action = {type = "door", action = "open", target = cur_door_pos}
--action = {type = "door", action = "open"}
action = {}
action["type"] = "door"
action["action"] = "open"
action["target"] = cur_door_pos
cur_pos_to_add = vector.add(cur_pos, one_down)
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
mcl_log ("Door close match")
action = {type = "door", action = "close", target = cur_door_pos}
--action = {type = "door", action = "closed"}
action = {}
action["type"] = "door"
action["action"] = "close"
action["target"] = cur_door_pos
cur_pos_to_add = vector.add(cur_pos, one_down)
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
mcl_log("Current door pos")
action = {type = "door", action = "open", target = cur_door_pos}
cur_pos_to_add = vector.add(cur_pos, one_down)
action = {}
action["type"] = "door"
action["action"] = "open"
action["target"] = cur_door_pos
else
cur_pos_to_add = cur_pos
--mcl_log ("Pos doesn't match")
@ -107,156 +101,106 @@ local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_
return wp_out
end
function mob_class:ready_to_path()
mcl_log("Check ready to path")
if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then
mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
return false
else
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
return true
end
end
local plane_adjacents = {
vector.new(1,0,0),
vector.new(-1,0,0),
vector.new(0,0,1),
vector.new(0,0,-1),
}
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
local function calculate_path_through_door (p, cur_door_pos, t)
if t then
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
else
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p))
end
local function calculate_path_through_door (p, t, target)
-- target is the same as t, just 1 square difference. Maybe we don't need target
mcl_log("Plot route from mob: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
local enriched_path = nil
local wp, prospective_wp
local cur_door_pos = nil
local pos_closest_to_door = nil
local other_side_of_door = nil
if cur_door_pos then
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
--Path to door first
local wp = minetest.find_path(p,t,150,1,4)
if not wp then
mcl_log("No direct path. Path through door")
for _,v in pairs(plane_adjacents) do
pos_closest_to_door = vector.add(cur_door_pos,v)
other_side_of_door = vector.add(cur_door_pos,-v)
-- This could improve. There could be multiple doors. Check you can path from door to target first.
local cur_door_pos = minetest.find_node_near(target,16,{"group:door"})
if cur_door_pos then
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
for _,v in pairs(plane_adjacents) do
pos_closest_to_door = vector.add(cur_door_pos,v)
local n = minetest.get_node(pos_closest_to_door)
local n = minetest.get_node(pos_closest_to_door)
if n.name == "air" then
wp = minetest.find_path(p,pos_closest_to_door,150,1,4)
if wp then
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
other_side_of_door = vector.add(cur_door_pos,-v)
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
if n.name == "air" then
mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
prospective_wp = minetest.find_path(p,pos_closest_to_door,150,1,4)
if prospective_wp then
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
table.insert(prospective_wp, cur_door_pos)
if t then
mcl_log("We have t, lets go from door to target")
local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4)
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
append_paths (prospective_wp, wp_otherside_door_to_target)
wp = prospective_wp
table.insert(wp, cur_door_pos)
append_paths (wp, wp_otherside_door_to_target)
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
mcl_log("We have a path from outside door to target")
else
mcl_log("We cannot path from outside door to target")
end
else
mcl_log("No t, just add other side of door")
table.insert(prospective_wp, other_side_of_door)
wp = prospective_wp
end
if wp then
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
break
else
mcl_log("This block next to door doesn't work.")
end
else
mcl_log("Cannot path to this air block next to door.")
mcl_log("Block is not air, it is: ".. n.name)
end
end
else
mcl_log("No door found")
end
else
mcl_log("No door found")
mcl_log("We have a direct route")
end
if wp and not enriched_path then
mcl_log("Wp but not enriched")
enriched_path = generate_enriched_path(wp)
end
return enriched_path
end
local gopath_last = os.time()
function mob_class:gopath(target,callback_arrived)
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
if not self:ready_to_path() then return end
if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then
mcl_log("We are not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
return
else
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
end
--if os.time() - gopath_last < 5 then
-- mcl_log("Not ready to path yet")
-- return
--end
--gopath_last = os.time()
self.order = nil
local p = self.object:get_pos()
local t = vector.offset(target,0,1,0)
--Check direct route
local wp = minetest.find_path(p,t,150,1,4)
if not wp then
mcl_log("### No direct path. Path through door closest to target.")
local door_near_target = minetest.find_node_near(target, 16, {"group:door"})
wp = calculate_path_through_door(p, door_near_target, t)
if not wp then
mcl_log("### No path though door closest to target. Try door closest to origin.")
local door_closest = minetest.find_node_near(p, 16, {"group:door"})
wp = calculate_path_through_door(p, door_closest, t)
-- Path through 2 doors
if not wp then
mcl_log("### Still not wp. Need to path through 2 doors.")
local path_through_closest_door = calculate_path_through_door(p, door_closest)
if path_through_closest_door and #path_through_closest_door > 0 then
mcl_log("We have path through first door")
mcl_log("Number of pos in path through door: " .. tostring(#path_through_closest_door))
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
if pos_after_door_entry then
local pos_after_door = vector.add(pos_after_door_entry["pos"], one_up)
mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door))
local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t)
if path_after_door and #path_after_door > 1 then
mcl_log("We have path after first door")
table.remove(path_after_door, 1) -- Remove duplicate
wp = path_through_closest_door
append_paths (wp, path_after_door)
else
mcl_log("Path after door is not good")
end
else
mcl_log("No pos after door")
end
else
mcl_log("Path through closest door empty or null")
end
else
mcl_log("ok, we have a path through 1 door")
end
end
else
wp = generate_enriched_path(wp)
mcl_log("We have a direct route")
end
local wp = calculate_path_through_door(p, t, target)
if not wp then
mcl_log("Could not calculate path")
self._pf_last_failed = os.time()
-- If cannot path, don't immediately try again
-- Cover for a flaw in pathfind where it chooses the wrong door and gets stuck. Take a break, allow others.
end
--output_table(wp)
if wp and #wp > 0 then
--output_table(wp)
self._target = t
self.callback_arrived = callback_arrived
local current_location = table.remove(wp,1)
@ -325,7 +269,17 @@ function mob_class:do_pathfind_action(action)
end
end
local gowp_etime = 0
function mob_class:check_gowp(dtime)
gowp_etime = gowp_etime + dtime
-- 0.1 is optimal.
--less frequently = villager will get sent back after passing a point.
--more frequently = villager will fail points they shouldn't they just didn't get there yet
--if gowp_etime < 0.05 then return end
--gowp_etime = 0
local p = self.object:get_pos()
-- no destination
@ -338,7 +292,7 @@ function mob_class:check_gowp(dtime)
-- arrived at location, finish gowp
local distance_to_targ = vector.distance(p,self._target)
--mcl_log("Distance to targ: ".. tostring(distance_to_targ))
if distance_to_targ < 1.8 then
if distance_to_targ < 2 then
mcl_log("Arrived at _target")
self.waypoints = nil
self._target = nil
@ -349,8 +303,6 @@ function mob_class:check_gowp(dtime)
self.object:set_acceleration({x = 0, y = 0, z = 0})
if self.callback_arrived then return self.callback_arrived(self) end
return true
elseif not self.current_target then
mcl_log("Not close enough to targ: ".. tostring(distance_to_targ))
end
-- More pathing to be done
@ -363,7 +315,7 @@ function mob_class:check_gowp(dtime)
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < 0.9 ) then
-- We have waypoints, and are at current_target or have no current target. We need a new current_target.
-- We have waypoints, and no current target, or we're at it. We need a new current_target.
self:do_pathfind_action (self.current_target["action"])
local failed_attempts = self.current_target["failed_attempts"]
@ -373,11 +325,10 @@ function mob_class:check_gowp(dtime)
self:go_to_pos(self.current_target["pos"])
return
elseif self.current_target and self.current_target["pos"] then
-- No waypoints left, but have current target and not close enough. Potentially last waypoint to go to.
-- No waypoints left, but have current target. Potentially last waypoint to go to.
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
local failed_attempts = self.current_target["failed_attempts"]
if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then
if failed_attempts >= 50 then
mcl_log("Failed to reach position (" .. minetest.pos_to_string(self.current_target["pos"]) .. ") too many times. Abandon route. Times tried: " .. failed_attempts)
self.state = "stand"
self.current_target = nil
@ -397,22 +348,9 @@ function mob_class:check_gowp(dtime)
-- Is a little sensitive and could take 1 - 7 times. A 10 fail count might be a good exit condition.
mcl_log("We don't have waypoints or a current target. Let's try to path to target")
if self.waypoints then
mcl_log("WP: " .. tostring(self.waypoints))
mcl_log("WP num: " .. tostring(#self.waypoints))
else
mcl_log("No wp set")
end
if self.current_target then
mcl_log("Current target: " .. tostring(self.current_target))
else
mcl_log("No current target")
end
local final_wp = minetest.find_path(p,self._target,150,1,4)
if final_wp then
mcl_log("We can get to target here.")
mcl_log("We might be able to get to target here.")
-- self.waypoints = final_wp
self:go_to_pos(self._target)
else
@ -436,9 +374,9 @@ function mob_class:check_gowp(dtime)
self:go_to_pos(self._current_target)
else
mcl_log("close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
mcl_log("target is: ".. minetest.pos_to_string(self._target))
self.current_target = nil
end
return
end
end

View File

@ -5,7 +5,8 @@ local ENTITY_CRAMMING_MAX = 24
local CRAMMING_DAMAGE = 3
local DEATH_DELAY = 0.5
local DEFAULT_FALL_SPEED = -9.81*1.5
local FLOP_HEIGHT = 6
local FLOP_HOR_SPEED = 1.5
local PATHFINDING = "gowp"
local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
@ -182,17 +183,6 @@ function mob_class:collision()
return({x,z})
end
function mob_class:slow_mob()
local d = 0.85
if self:check_dying() then d = 0.92 end
local v = self.object:get_velocity()
if v then
--diffuse object velocity
self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d})
end
end
-- move mob in facing direction
function mob_class:set_velocity(v)
local c_x, c_y = 0, 0
@ -364,7 +354,7 @@ function mob_class:set_yaw(yaw, delay, dtime)
if self.shaking and dtime then
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
end
--self:update_roll()
self:update_roll()
return yaw
end
@ -788,25 +778,6 @@ function mob_class:do_env_damage()
return self:check_for_death("", {type = "unknown"})
end
function mob_class:env_damage (dtime, pos)
-- environmental damage timer (every 1 second)
self.env_damage_timer = self.env_damage_timer + dtime
if (self.state == "attack" and self.env_damage_timer > 1)
or self.state ~= "attack" then
self:check_entity_cramming()
self.env_damage_timer = 0
-- check for environmental damage (water, fire, lava etc.)
if self:do_env_damage() then
return true
end
-- node replace check (cow eats grass etc.)
self:replace(pos)
end
end
function mob_class:damage_mob(reason,damage)
if not self.health then return end
damage = math.floor(damage)

View File

@ -169,8 +169,6 @@ local list_of_all_biomes = {
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore",
"BambooJungleM_shore",
"BambooJungle_shore",
"MangroveSwamp_shore",
-- dimension biome:
@ -218,10 +216,6 @@ local list_of_all_biomes = {
"JungleEdge",
"SavannaM",
"MangroveSwamp",
"BambooJungle",
"BambooJungleEdge",
"BambooJungleEdgeM",
"BambooJungleM",
}
-- count how many mobs are in an area
@ -250,24 +244,6 @@ local function count_mobs_total(mob_type)
return num
end
local function count_mobs_all()
local mobs_found = {}
local num = 0
for _,entity in pairs(minetest.luaentities) do
if entity.is_mob then
local mob_type = entity.type -- animal / monster / npc
local mob_name = entity.name
if mobs_found[mob_name] then
mobs_found[mob_name] = mobs_found[mob_name] + 1
else
mobs_found[mob_name] = 1
end
num = num + 1
end
end
return mobs_found, num
end
local function count_mobs_total_cap(mob_type)
local num = 0
for _,l in pairs(minetest.luaentities) do
@ -426,9 +402,11 @@ local two_pi = 2 * math.pi
local function get_next_mob_spawn_pos(pos)
local distance = math_random(25, 32)
local angle = math_random() * two_pi
local xoff = math_round(distance * math_cos(angle))
local yoff = math_round(distance * math_sin(angle))
return vector.offset(pos, xoff, 0, yoff)
return {
x = math_round(pos.x + distance * math_cos(angle)),
y = pos.y,
z = math_round(pos.z + distance * math_sin(angle))
}
end
local function decypher_limits(posy)
@ -581,7 +559,7 @@ local S = minetest.get_translator("mcl_mobs")
minetest.register_chatcommand("spawn_mob",{
privs = { debug = true },
description=S("spawn_mob is a chatcommand that allows you to type in the name of a mob without 'typing mobs_mc:' all the time like so; 'spawn_mob spider'. however, there is more you can do with this special command, currently you can edit any number, boolean, and string variable you choose with this format: spawn_mob 'any_mob:var<mobs_variable=variable_value>:'. any_mob being your mob of choice, mobs_variable being the variable, and variable value being the value of the chosen variable. and example of this format: \n spawn_mob skeleton:var<passive=true>:\n this would spawn a skeleton that wouldn't attack you. REMEMBER-THIS> when changing a number value always prefix it with 'NUM', example: \n spawn_mob skeleton:var<jump_height=NUM10>:\n this setting the skelly's jump height to 10. if you want to make multiple changes to a mob, you can, example: \n spawn_mob skeleton:var<passive=true>::var<jump_height=NUM10>::var<fly_in=air>::var<fly=true>:\n etc."),
description=S("spawn_mob is a chatcommand that allows you to type in the name of a mob without 'typing mobs_mc:' all the time like so; 'spawn_mob spider'. however, there is more you can do with this special command, currently you can edit any number, boolian, and string variable you choose with this format: spawn_mob 'any_mob:var<mobs_variable=variable_value>:'. any_mob being your mob of choice, mobs_variable being the variable, and variable value being the value of the chosen variable. and example of this format: \n spawn_mob skeleton:var<passive=true>:\n this would spawn a skeleton that wouldn't attack you. REMEMBER-THIS> when changing a number value always prefix it with 'NUM', example: \n spawn_mob skeleton:var<jump_height=NUM10>:\n this setting the skelly's jump height to 10. if you want to make multiple changes to a mob, you can, example: \n spawn_mob skeleton:var<passive=true>::var<jump_height=NUM10>::var<fly_in=air>::var<fly=true>:\n etc."),
func = function(n,param)
local pos = minetest.get_player_by_name(n):get_pos()
@ -601,14 +579,15 @@ minetest.register_chatcommand("spawn_mob",{
local mob = mcl_mobs.spawn(pos,mobname)
if mob then
for c=1, #modifiers do
modifs = modifiers[c]
for c=1, #modifiers do
modifs = modifiers[c]
local mod1 = string.find(modifs, ":")
local mod_start = string.find(modifs, "<")
local mod_vals = string.find(modifs, "=")
local mod_end = string.find(modifs, ">")
local mod1 = string.find(modifs, ":")
local mod_start = string.find(modifs, "<")
local mod_vals = string.find(modifs, "=")
local mod_end = string.find(modifs, ">")
local mod_end = string.find(modifs, ">")
if mob then
local mob_entity = mob:get_luaentity()
if string.sub(modifs, mod1+1, mod1+3) == "var" then
if mod1 and mod_start and mod_vals and mod_end then
@ -639,12 +618,14 @@ minetest.register_chatcommand("spawn_mob",{
minetest.log("warning", n.." couldn't modify "..mobname.." at "..minetest.pos_to_string(pos).. ", missing modification type")
end
end
minetest.log("action", n.." spawned "..mobname.." at "..minetest.pos_to_string(pos))
return true, mobname.." spawned at "..minetest.pos_to_string(pos)
else
return false, "Couldn't spawn "..mobname
end
if mob then
return true, mobname.." spawned at "..minetest.pos_to_string(pos),
minetest.log("action", n.." spawned "..mobname.." at "..minetest.pos_to_string(pos))
end
return false, "Couldn't spawn "..mobname
end
})
@ -652,9 +633,6 @@ if mobs_spawn then
local perlin_noise
-- Get pos to spawn, x and z are randomised, y is range
local function spawn_a_mob(pos, dimension, y_min, y_max)
--create a disconnected clone of the spawn dictionary
--prevents memory leak
@ -749,9 +727,7 @@ if mobs_spawn then
end)
end
function mob_class:check_despawn(pos, dtime)
self.lifetimer = self.lifetimer - dtime
function mob_class:check_despawn(pos)
-- Despawning: when lifetimer expires, remove mob
if remove_far
and self.can_despawn == true
@ -784,18 +760,5 @@ minetest.register_chatcommand("mobstats",{
minetest.chat_send_player(n,"total mobs:"..count_mobs_total())
minetest.chat_send_player(n,"spawning attempts since server start:"..dbg_spawn_attempts)
minetest.chat_send_player(n,"successful spawns since server start:"..dbg_spawn_succ)
local mob_counts, total_mobs = count_mobs_all()
if (total_mobs) then
minetest.log("action", "Total mobs found: " .. total_mobs)
end
if mob_counts then
for k, v1 in pairs(mob_counts) do
minetest.log("action", "k: " .. tostring(k))
minetest.log("action", "v1: " .. tostring(v1))
end
end
end
})

View File

@ -1,2 +0,0 @@
# textdomain:mcl_paintings
Painting=絵画

View File

@ -65,7 +65,7 @@ local axolotl = {
awards.unlock(clicker:get_player_name(), "mcl:cutestPredator")
return
end
if self:feed_tame(clicker, 1, true, false) then return end
if mcl_mobs:feed_tame(self, clicker, 1, true, false) then return end
end,
makes_footstep_sound = false,
fly = true,

View File

@ -12,13 +12,13 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
description = S("Chicken"),
type = "animal",
spawn_class = "passive",
passive = true,
runaway = true,
hp_min = 4,
hp_max = 4,
xp_min = 1,
xp_max = 3,
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
runaway = true,
floats = 1,
head_swivel = "head.control",
bone_eye_height = 4,

View File

@ -64,7 +64,7 @@ local cod = {
chance = 1,
min = 1,
max = 1,},
{name = "mcl_bone_meal:bone_meal",
{name = "mcl_dye:white",
chance = 20,
min = 1,
max = 1,},

View File

@ -7,13 +7,12 @@ local cow_def = {
type = "animal",
spawn_class = "passive",
passive = true,
runaway = true,
hp_min = 10,
hp_max = 10,
xp_min = 1,
xp_max = 3,
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.39, 0.45},
spawn_in_group = 4,
spawn_in_group = 8,
spawn_in_group_min = 3,
visual = "mesh",
mesh = "mobs_mc_cow.b3d",
@ -41,6 +40,7 @@ local cow_def = {
max = 2,
looting = "common",},
},
runaway = true,
sounds = {
random = "mobs_mc_cow",
damage = "mobs_mc_cow_hurt",

View File

@ -259,7 +259,6 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
description = S("Enderman"),
type = "monster",
spawn_class = "passive",
can_despawn = true,
passive = true,
pathfinding = 1,
hp_min = 40,
@ -443,7 +442,6 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
end
end
end
-- TAKE AND PLACE STUFF BEHAVIOUR BELOW.
if not mobs_griefing then
return
@ -471,7 +469,6 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
local dug = minetest.get_node_or_nil(take_pos)
if dug and dug.name == "air" then
self._taken_node = node.name
self.can_despawn = false
local def = minetest.registered_nodes[self._taken_node]
-- Update animation and texture accordingly (adds visibly carried block)
local block_type
@ -522,7 +519,6 @@ mcl_mobs.register_mob("mobs_mc:enderman", {
if success then
local def = minetest.registered_nodes[self._taken_node]
-- Update animation accordingly (removes visible block)
self.can_despawn = true
self.animation = select_enderman_animation("normal")
self:set_animation(self.animation.current)
if def.sounds and def.sounds.place then

View File

@ -1,70 +0,0 @@
# textdomain: mobs_mc
Agent=エージェント
Axolotl=ウーパールーパー
Bat=コウモリ
Blaze=ブレイズ
Chicken=ニワトリ
Cow=ウシ
Mooshroom=ムーシュルーム
Creeper=クリーパー
Ender Dragon=エンダードラゴン
Enderman=エンダーマン
Endermite=エンダーマイト
Ghast=ガスト
Elder Guardian=エルダーガーディアン
Guardian=ガーディアン
Horse=ウマ
Skeleton Horse=スケルトンホース
Zombie Horse=ゾンビホース
Donkey=ロバ
Mule=ラバ
Iron Golem=アイアンゴーレム
Llama=ラマ
Ocelot=ヤマネコ
Parrot=オウム
Pig=ブタ
Polar Bear=シロクマ
Rabbit=ウサギ
Killer Bunny=殺人ウサギ
Sheep=ヒツジ
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=ゾンビピッグマン
Farmer=農民
Fisherman=漁師
Fletcher=矢師
Shepherd=羊飼い
Librarian=司書
Cartographer=製図家
Armorer=防具鍛冶
Leatherworker=革職人
Butcher=肉屋
Weapon Smith=武器鍛冶
Tool Smith=道具鍛冶
Cleric=聖職者
Nitwit=求職者
Cod=タラ
Salmon=サケ
Dolphin=イルカ
Pillager=ピリジャー
Tropical fish=クマノミ

View File

@ -2,4 +2,4 @@ name = mobs_mc
author = maikerumine
description = Adds Minecraft-like monsters and animals.
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, doc_items
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items

View File

@ -164,7 +164,7 @@ cat.on_spawn = function(self)
if not self._texture then
self._texture = cat.textures[math.random(#cat.textures)]
end
self.object:set_properties({textures = self._texture})
self.object:set_properties({textures = {self._texture}})
end
mcl_mobs.register_mob("mobs_mc:cat", cat)

View File

@ -6,7 +6,6 @@ mcl_mobs.register_mob("mobs_mc:pig", {
description = S("Pig"),
type = "animal",
spawn_class = "passive",
passive = true,
runaway = true,
hp_min = 10,
hp_max = 10,

View File

@ -42,7 +42,7 @@ local salmon = {
chance = 1,
min = 1,
max = 1,},
{name = "mcl_bone_meal:bone_meal",
{name = "mcl_dye:white",
chance = 20,
min = 1,
max = 1,},

View File

@ -56,7 +56,6 @@ mcl_mobs.register_mob("mobs_mc:sheep", {
description = S("Sheep"),
type = "animal",
spawn_class = "passive",
passive = true,
hp_min = 8,
hp_max = 8,
xp_min = 1,

Some files were not shown because too many files have changed in this diff Show More