forked from VoxeLibre/VoxeLibre
Compare commits
14 Commits
Author | SHA1 | Date |
---|---|---|
|
8f735cc644 | |
|
422076f761 | |
|
9ce59d4438 | |
|
99357d467c | |
|
018fc64a27 | |
|
b53329b452 | |
|
c27dea59b0 | |
|
80c6b547e5 | |
|
0de53a9dcb | |
|
67dd48b06c | |
|
34c31cac1f | |
|
788b6dc080 | |
|
200ce76c70 | |
|
ca94a1c354 |
4
API.md
4
API.md
|
@ -17,10 +17,6 @@ Items can have these fields:
|
|||
anvil.
|
||||
See `mcl_banners` for an example.
|
||||
|
||||
Tools can have these fields:
|
||||
* `_mcl_diggroups`: Specifies the digging groups that a tool can dig and how
|
||||
efficiently. See `_mcl_autogroup` for more information.
|
||||
|
||||
All nodes can have these fields:
|
||||
|
||||
* `_mcl_hardness`: Hardness of the block, ranges from 0 to infinity (represented by -1). Determines digging times. Default: 0
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
# This is a game specific minetest.conf file, do not edit
|
||||
|
||||
# If any of these settings are set in your minetest.conf file in ~/.minetest (Linux) or in the root directory of the game (Run in place/Windows)
|
||||
# They will override these settings
|
||||
|
||||
# Basic game rules
|
||||
time_speed = 72
|
||||
|
||||
|
@ -36,10 +33,10 @@ mgvalleys_spflags = noaltitude_chill,noaltitude_dry,nohumid_rivers,vary_river_de
|
|||
keepInventory = false
|
||||
|
||||
# Performance settings
|
||||
dedicated_server_step = 0.05 #tick rate
|
||||
# abm_interval = 0.25
|
||||
# max_objects_per_block = 4096
|
||||
# max_packets_per_iteration = 10096
|
||||
dedicated_server_step = 0.001
|
||||
abm_interval = 0.25
|
||||
max_objects_per_block = 4096
|
||||
max_packets_per_iteration = 10096
|
||||
|
||||
# Clientmodding to support official client
|
||||
enable_client_modding = true
|
||||
|
|
|
@ -4,11 +4,6 @@ Specifically, this mod has 2 purposes:
|
|||
1) Automatically adding the group “solid” for blocks considered “solid” in Minecraft.
|
||||
2) Generating digging time group for all nodes based on node metadata (it's complicated)
|
||||
|
||||
This mod also requires another mod called “mcl_autogroup” to function properly.
|
||||
“mcl_autogroup” exposes the API used to register digging groups, while this mod
|
||||
uses those digging groups to set the digging time groups for all the nodes and
|
||||
tools.
|
||||
|
||||
See init.lua for more infos.
|
||||
|
||||
The leading underscore in the name “_mcl_autogroup” was added to force Minetest to load this mod as late as possible.
|
||||
|
|
|
@ -1,361 +1,169 @@
|
|||
--[[
|
||||
This mod implements a HACK to make 100% sure the digging times of all tools
|
||||
match Minecraft's perfectly. The digging times system of Minetest is very
|
||||
different, so this weird group trickery has to be used. In Minecraft, each
|
||||
block has a hardness and the actual Minecraft digging time is determined by
|
||||
this:
|
||||
--[[ Mining times. Yeah, mining times … Alright, this is going to be FUN!
|
||||
|
||||
This mod does include a HACK to make 100% sure the digging times of all tools match Minecraft's perfectly.
|
||||
The digging times system of Minetest is very different, so this weird group trickery has to be used.
|
||||
In Minecraft, each block has a hardness and the actual Minecraft digging time is determined by this:
|
||||
1) The block's hardness
|
||||
2) The tool being used (the tool speed and its efficiency level)
|
||||
3) Whether the tool is considered as "eligible" for the block
|
||||
2) The tool being used
|
||||
3) Whether the tool is considered as “eligible” for the block
|
||||
(e.g. only diamond pick eligible for obsidian)
|
||||
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more information.
|
||||
|
||||
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more
|
||||
information.
|
||||
In MineClone 2, all diggable node have the hardness set in the custom field “_mcl_hardness” (0 by default).
|
||||
The nodes are also required to specify the “eligible” tools in groups like “pickaxey”, “shovely”, etc.
|
||||
This mod then calculates the real digging time based on the node meta data. The real digging times
|
||||
are then added into mcl_autogroup.digtimes where the table indices are group rating and the values are the
|
||||
digging times in seconds. These digging times can be then added verbatim into the tool definitions.
|
||||
|
||||
How the mod is used
|
||||
===================
|
||||
Example:
|
||||
mcl_autogroup.digtimes.pickaxey_dig_diamond[1] = 0.2
|
||||
|
||||
In MineClone 2, all diggable nodes have the hardness set in the custom field
|
||||
"_mcl_hardness" (0 by default). These values are used together with digging
|
||||
groups by this mod to create the correct digging times for nodes. Digging
|
||||
groups are registered using the following code:
|
||||
→ This means that when a node has been assigned the group “pickaxey_dig_diamond=1”, it can be dug by the
|
||||
diamond pickaxe in 0.2 seconds.
|
||||
|
||||
mcl_autogroup.register_diggroup("shovely")
|
||||
mcl_autogroup.register_diggroup("pickaxey", {
|
||||
levels = { "wood", "gold", "stone", "iron", "diamond" }
|
||||
})
|
||||
|
||||
The first line registers a simple digging group. The second line registers a
|
||||
digging group with 5 different levels (in this case one for each material of a
|
||||
pickaxes).
|
||||
|
||||
Nodes indicate that they belong to a particular digging group by being member of
|
||||
the digging group in their node definition. "mcl_core:dirt" for example has
|
||||
shovely=1 in its groups. If the digging group has multiple levels the value of
|
||||
the group indicates which digging level the node requires.
|
||||
"mcl_core:stone_with_gold" for example has pickaxey=4 because it requires a
|
||||
pickaxe of level 4 be mined.
|
||||
This strange setup with mcl_autogroup has been done to minimize the amount of required digging times
|
||||
a single tool needs to use. If this is not being done, the loading time will increase considerably
|
||||
(>10s).
|
||||
|
||||
For tools to be able to dig nodes of digging groups they need to use the have
|
||||
the custom field "_mcl_diggroups" function to get the groupcaps. The value of
|
||||
this field is a table which defines which groups the tool can dig and how
|
||||
efficiently.
|
||||
]]
|
||||
|
||||
_mcl_diggroups = {
|
||||
handy = { speed = 1, level = 1, uses = 0 },
|
||||
pickaxey = { speed = 1, level = 0, uses = 0 },
|
||||
}
|
||||
local materials = { "wood", "gold", "stone", "iron", "diamond" }
|
||||
local basegroups = { "pickaxey", "axey", "shovely" }
|
||||
local minigroups = { "handy", "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
|
||||
local divisors = {
|
||||
["wood"] = 2,
|
||||
["gold"] = 12,
|
||||
["stone"] = 4,
|
||||
["iron"] = 6,
|
||||
["diamond"] = 8,
|
||||
["handy"] = 1,
|
||||
["shearsy"] = 15,
|
||||
["swordy"] = 1.5,
|
||||
["shearsy_wool"] = 5,
|
||||
["swordy_cobweb"] = 15,
|
||||
}
|
||||
local max_efficiency_level = 5
|
||||
|
||||
The "uses" field indicate how many uses (0 for infinite) a tool has when used on
|
||||
the specified digging group. The "speed" field is a multiplier to the dig speed
|
||||
on that digging group.
|
||||
mcl_autogroup = {}
|
||||
mcl_autogroup.digtimes = {}
|
||||
mcl_autogroup.creativetimes = {} -- Copy of digtimes, except that all values are 0. Used for creative mode
|
||||
|
||||
The "level" field indicates which levels of the group the tool can harvest. A
|
||||
level of 0 means that the tool cannot harvest blocks of that node. A level of 1
|
||||
or above means that the tool can harvest nodes with that level or below. See
|
||||
"mcl_tools/init.lua" for examples on how "_mcl_diggroups" is used in practice.
|
||||
|
||||
Information about the mod
|
||||
=========================
|
||||
|
||||
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
|
||||
mcl_autogroup contains the API functions used to register custom digging groups.
|
||||
_mcl_autogroup contains most of the code. The leading underscore in the name
|
||||
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
|
||||
as possible. Minetest loads mods in reverse alphabetical order.
|
||||
|
||||
This also means that it is very important that no mod adds _mcl_autogroup as a
|
||||
dependency.
|
||||
--]]
|
||||
|
||||
assert(minetest.get_modpath("mcl_autogroup"), "This mod requires the mod mcl_autogroup to function")
|
||||
|
||||
-- Returns a table containing the unique "_mcl_hardness" for nodes belonging to
|
||||
-- each diggroup.
|
||||
local function get_hardness_values_for_groups()
|
||||
local maps = {}
|
||||
local values = {}
|
||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||
maps[g] = {}
|
||||
values[g] = {}
|
||||
end
|
||||
|
||||
for _, ndef in pairs(minetest.registered_nodes) do
|
||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||
if ndef.groups[g] ~= nil then
|
||||
maps[g][ndef._mcl_hardness or 0] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for g, map in pairs(maps) do
|
||||
for k, _ in pairs(map) do
|
||||
table.insert(values[g], k)
|
||||
end
|
||||
end
|
||||
|
||||
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
|
||||
table.sort(values[g])
|
||||
end
|
||||
return values
|
||||
end
|
||||
|
||||
-- Returns a table containing a table indexed by "_mcl_hardness_value" to get
|
||||
-- its index in the list of unique hardnesses for each diggroup.
|
||||
local function get_hardness_lookup_for_groups(hardness_values)
|
||||
local map = {}
|
||||
for g, values in pairs(hardness_values) do
|
||||
map[g] = {}
|
||||
for k, v in pairs(values) do
|
||||
map[g][v] = k
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
-- Array of unique hardness values for each group which affects dig time.
|
||||
local hardness_values = get_hardness_values_for_groups()
|
||||
|
||||
-- Map indexed by hardness values which return the index of that value in
|
||||
-- hardness_value. Used for quick lookup.
|
||||
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
|
||||
|
||||
local function compute_creativetimes(group)
|
||||
local creativetimes = {}
|
||||
|
||||
for index, hardness in pairs(hardness_values[group]) do
|
||||
table.insert(creativetimes, 0)
|
||||
end
|
||||
|
||||
return creativetimes
|
||||
end
|
||||
|
||||
-- Get the list of digging times for using a specific tool on a specific
|
||||
-- diggroup.
|
||||
--
|
||||
-- Parameters:
|
||||
-- group - the group which it is digging
|
||||
-- can_harvest - if the tool can harvest the block
|
||||
-- speed - dig speed multiplier for tool (default 1)
|
||||
-- efficiency - efficiency level for the tool if applicable
|
||||
local function get_digtimes(group, can_harvest, speed, efficiency)
|
||||
local speed = speed or 1
|
||||
if efficiency then
|
||||
speed = speed + efficiency * efficiency + 1
|
||||
end
|
||||
|
||||
local digtimes = {}
|
||||
|
||||
for index, hardness in pairs(hardness_values[group]) do
|
||||
local digtime = (hardness or 0) / speed
|
||||
if can_harvest then
|
||||
digtime = digtime * 1.5
|
||||
else
|
||||
digtime = digtime * 5
|
||||
end
|
||||
|
||||
if digtime <= 0.05 then
|
||||
digtime = 0
|
||||
else
|
||||
digtime = math.ceil(digtime * 20) / 20
|
||||
end
|
||||
table.insert(digtimes, digtime)
|
||||
end
|
||||
|
||||
return digtimes
|
||||
end
|
||||
|
||||
-- Get one groupcap field for using a specific tool on a specific group.
|
||||
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
||||
return {
|
||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||
uses = uses,
|
||||
maxlevel = 0,
|
||||
}
|
||||
end
|
||||
|
||||
-- Add the groupcaps from a field in "_mcl_diggroups" to the groupcaps of a
|
||||
-- tool.
|
||||
local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
|
||||
if not groupcaps_def then
|
||||
return
|
||||
end
|
||||
|
||||
for g, capsdef in pairs(groupcaps_def) do
|
||||
local mult = capsdef.speed or 1
|
||||
local uses = capsdef.uses
|
||||
local def = mcl_autogroup.registered_diggroups[g]
|
||||
local max_level = def.levels and #def.levels or 1
|
||||
|
||||
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
|
||||
local level = math.min(capsdef.level, max_level)
|
||||
|
||||
if def.levels then
|
||||
groupcaps[g .. "_dig_default"] = get_groupcap(g, false, mult, efficiency, uses)
|
||||
if level > 0 then
|
||||
groupcaps[g .. "_dig_" .. def.levels[level]] = get_groupcap(g, true, mult, efficiency, uses)
|
||||
end
|
||||
else
|
||||
groupcaps[g .. "_dig"] = get_groupcap(g, level > 0, mult, efficiency, uses)
|
||||
for m=1, #materials do
|
||||
for g=1, #basegroups do
|
||||
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m]] = {}
|
||||
mcl_autogroup.creativetimes[basegroups[g].."_dig_"..materials[m]] = {}
|
||||
for e=1, max_efficiency_level do
|
||||
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m].."_efficiency_"..e] = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks if the given node would drop its useful drop if dug by a given tool.
|
||||
-- Returns true if it will yield its useful drop, false otherwise.
|
||||
function mcl_autogroup.can_harvest(nodename, toolname)
|
||||
local ndef = minetest.registered_nodes[nodename]
|
||||
|
||||
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
|
||||
return true
|
||||
for g=1, #minigroups do
|
||||
mcl_autogroup.digtimes[minigroups[g].."_dig"] = {}
|
||||
mcl_autogroup.creativetimes[minigroups[g].."_dig"] = {}
|
||||
for e=1, max_efficiency_level do
|
||||
mcl_autogroup.digtimes[minigroups[g].."_dig_efficiency_"..e] = {}
|
||||
mcl_autogroup.creativetimes[minigroups[g].."_dig_efficiency_"..e] = {}
|
||||
end
|
||||
|
||||
-- Check if it can be dug by tool
|
||||
local tdef = minetest.registered_tools[toolname]
|
||||
if tdef and tdef._mcl_diggroups then
|
||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
||||
if ndef.groups[g] then
|
||||
if ndef.groups[g] <= gdef.level then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if it can be dug by hand
|
||||
local tdef = minetest.registered_tools[""]
|
||||
if tdef then
|
||||
for g, gdef in pairs(tdef._mcl_diggroups) do
|
||||
if ndef.groups[g] then
|
||||
if ndef.groups[g] <= gdef.level then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Get one groupcap field for using a specific tool on a specific group.
|
||||
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
|
||||
return {
|
||||
times = get_digtimes(group, can_harvest, multiplier, efficiency),
|
||||
uses = uses,
|
||||
maxlevel = 0,
|
||||
}
|
||||
end
|
||||
|
||||
-- Returns the tool_capabilities from a tool definition or a default set of
|
||||
-- tool_capabilities
|
||||
local function get_tool_capabilities(tdef)
|
||||
if tdef.tool_capabilities then
|
||||
return tdef.tool_capabilities
|
||||
end
|
||||
|
||||
-- If the damage group and punch interval from hand is not included,
|
||||
-- then the user will not be able to attack with the tool.
|
||||
local hand_toolcaps = minetest.registered_tools[""].tool_capabilities
|
||||
return {
|
||||
full_punch_interval = hand_toolcaps.full_punch_interval,
|
||||
damage_groups = hand_toolcaps.damage_groups
|
||||
}
|
||||
end
|
||||
|
||||
-- Get the groupcaps for a tool. This function returns "groupcaps" table of
|
||||
-- digging which should be put in the "tool_capabilities" of the tool definition
|
||||
-- or in the metadata of an enchanted tool.
|
||||
--
|
||||
-- Parameters:
|
||||
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
||||
-- efficiency - The efficiency level the tool is enchanted with (default 0)
|
||||
--
|
||||
-- NOTE:
|
||||
-- This function can only be called after mod initialization. Otherwise a mod
|
||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||
-- loading order.
|
||||
function mcl_autogroup.get_groupcaps(toolname, efficiency)
|
||||
local tdef = minetest.registered_tools[toolname]
|
||||
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
|
||||
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
|
||||
return groupcaps
|
||||
end
|
||||
|
||||
-- Get the wear from using a tool on a digging group.
|
||||
--
|
||||
-- Parameters
|
||||
-- toolname - Name of the tool used
|
||||
-- diggroup - The name of the diggroup the tool is used on
|
||||
--
|
||||
-- NOTE:
|
||||
-- This function can only be called after mod initialization. Otherwise a mod
|
||||
-- would have to add _mcl_autogroup as a dependency which would break the mod
|
||||
-- loading order.
|
||||
function mcl_autogroup.get_wear(toolname, diggroup)
|
||||
local tdef = minetest.registered_tools[toolname]
|
||||
local uses = tdef._mcl_diggroups[diggroup].uses
|
||||
return math.ceil(65535 / uses)
|
||||
end
|
||||
|
||||
local overwrite = function()
|
||||
for nname, ndef in pairs(minetest.registered_nodes) do
|
||||
local groups_changed = false
|
||||
local newgroups = table.copy(ndef.groups)
|
||||
if (nname ~= "ignore" and ndef.diggable) then
|
||||
-- Automatically assign the "solid" group for solid nodes
|
||||
-- Automatically assign the “solid” group for solid nodes
|
||||
if (ndef.walkable == nil or ndef.walkable == true)
|
||||
and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
|
||||
and (ndef.node_box == nil or ndef.node_box.type == "regular")
|
||||
and (ndef.groups.not_solid == 0 or ndef.groups.not_solid == nil) then
|
||||
newgroups.solid = 1
|
||||
groups_changed = true
|
||||
end
|
||||
-- Automatically assign the "opaque" group for opaque nodes
|
||||
-- Automatically assign the “opaque” group for opaque nodes
|
||||
if (not (ndef.paramtype == "light" or ndef.sunlight_propagates)) and
|
||||
(ndef.groups.not_opaque == 0 or ndef.groups.not_opaque == nil) then
|
||||
newgroups.opaque = 1
|
||||
groups_changed = true
|
||||
end
|
||||
|
||||
local creative_breakable = false
|
||||
local function calculate_group(hardness, material, diggroup, newgroups, actual_rating, expected_rating, efficiency)
|
||||
local time, validity_factor
|
||||
if actual_rating >= expected_rating then
|
||||
-- Valid tool
|
||||
validity_factor = 1.5
|
||||
else
|
||||
-- Wrong tool (higher digging time)
|
||||
validity_factor = 5
|
||||
end
|
||||
local speed_multiplier = divisors[material]
|
||||
if efficiency then
|
||||
speed_multiplier = speed_multiplier + efficiency * efficiency + 1
|
||||
end
|
||||
time = (hardness * validity_factor) / speed_multiplier
|
||||
if time <= 0.05 then
|
||||
time = 0
|
||||
else
|
||||
time = math.ceil(time * 20) / 20
|
||||
end
|
||||
table.insert(mcl_autogroup.digtimes[diggroup], time)
|
||||
if not efficiency then
|
||||
table.insert(mcl_autogroup.creativetimes[diggroup], 0)
|
||||
end
|
||||
newgroups[diggroup] = #mcl_autogroup.digtimes[diggroup]
|
||||
return newgroups
|
||||
end
|
||||
|
||||
-- Assign groups used for digging this node depending on
|
||||
-- the registered digging groups
|
||||
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
|
||||
creative_breakable = true
|
||||
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
|
||||
if ndef.groups[g] then
|
||||
if gdef.levels then
|
||||
newgroups[g .. "_dig_default"] = index
|
||||
-- Hack in digging times
|
||||
local hardness = ndef._mcl_hardness
|
||||
if not hardness then
|
||||
hardness = 0
|
||||
end
|
||||
|
||||
for i = ndef.groups[g], #gdef.levels do
|
||||
newgroups[g .. "_dig_" .. gdef.levels[i]] = index
|
||||
-- Handle pickaxey, axey and shovely
|
||||
for _, basegroup in pairs(basegroups) do
|
||||
if (hardness ~= -1 and ndef.groups[basegroup]) then
|
||||
for g=1,#materials do
|
||||
local diggroup = basegroup.."_dig_"..materials[g]
|
||||
newgroups = calculate_group(hardness, materials[g], diggroup, newgroups, g, ndef.groups[basegroup])
|
||||
for e=1,max_efficiency_level do
|
||||
newgroups = calculate_group(hardness, materials[g], diggroup .. "_efficiency_" .. e, newgroups, g, ndef.groups[basegroup], e)
|
||||
end
|
||||
else
|
||||
newgroups[g .. "_dig"] = index
|
||||
groups_changed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for m=1, #minigroups do
|
||||
local minigroup = minigroups[m]
|
||||
if hardness ~= -1 then
|
||||
local diggroup = minigroup.."_dig"
|
||||
-- actual rating
|
||||
local ar = ndef.groups[minigroup]
|
||||
if ar == nil then
|
||||
ar = 0
|
||||
end
|
||||
if (minigroup == "handy")
|
||||
or
|
||||
(ndef.groups.shearsy_wool and minigroup == "shearsy_wool" and ndef.groups.wool)
|
||||
or
|
||||
(ndef.groups.swordy_cobweb and minigroup == "swordy_cobweb" and nname == "mcl_core:cobweb")
|
||||
or
|
||||
(ndef.groups[minigroup] and minigroup ~= "swordy_cobweb" and minigroup ~= "shearsy_wool") then
|
||||
newgroups = calculate_group(hardness, minigroup, diggroup, newgroups, ar, 1)
|
||||
for e=1,max_efficiency_level do
|
||||
newgroups = calculate_group(hardness, minigroup, diggroup .. "_efficiency_" .. e, newgroups, ar, 1, e)
|
||||
end
|
||||
groups_changed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Automatically assign the node to the
|
||||
-- creative_breakable group if it belongs to any digging
|
||||
-- group.
|
||||
newgroups["creative_breakable"] = 1
|
||||
|
||||
minetest.override_item(nname, {
|
||||
groups = newgroups
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for tname, tdef in pairs(minetest.registered_tools) do
|
||||
-- Assign groupcaps for digging the registered digging groups
|
||||
-- depending on the _mcl_diggroups in the tool definition
|
||||
if tdef._mcl_diggroups then
|
||||
local toolcaps = table.copy(get_tool_capabilities(tdef))
|
||||
toolcaps.groupcaps = mcl_autogroup.get_groupcaps(tname)
|
||||
|
||||
minetest.override_item(tname, {
|
||||
tool_capabilities = toolcaps
|
||||
})
|
||||
if groups_changed then
|
||||
minetest.override_item(nname, {
|
||||
groups = newgroups
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name = _mcl_autogroup
|
||||
author = ryvnf
|
||||
author = Wuzzy
|
||||
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
local Object = {}
|
||||
|
||||
-- Define a getter that caches the result for the next time it is called
|
||||
-- This is a static method (self = the class); in this class system static methods start with __ by convention
|
||||
function Object:__cache_getter(name, func)
|
||||
-- cache key: prevent overriding the getter function itself
|
||||
local key = "_" .. name
|
||||
-- add a function to the class
|
||||
self[name] = function(self)
|
||||
-- check if the value is present in the cache
|
||||
local value = self[key]
|
||||
|
||||
-- `== nil` instead of `not value` to allow caching boolean values
|
||||
if value == nil then
|
||||
-- call the getter function
|
||||
value = func(self)
|
||||
end
|
||||
|
||||
-- store result in cache
|
||||
self[key] = value
|
||||
|
||||
-- return result
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
-- Define a getter / setter
|
||||
-- If no argument is specified, it will act as a getter, else as a setter
|
||||
-- The specified function MUST return the new value, if it returns nil, nil will be used as new value
|
||||
-- Optionally works in combination with a previously defined cache getter and only really makes sense in that context
|
||||
function Object:__setter(name, func)
|
||||
-- since the function is overridden, we need to store the old one in case a cache getter is defined
|
||||
local cache_getter = self[name]
|
||||
-- use same key as cache getter to modify getter cache if present
|
||||
local key = "_" .. name
|
||||
|
||||
self[name] = function(self, new)
|
||||
-- check whether an argument was specified
|
||||
if new == nil then
|
||||
if cache_getter then
|
||||
-- call the cache getter if present
|
||||
return cache_getter(self)
|
||||
else
|
||||
-- return the value else
|
||||
return self[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- call the setter and set the new value to the result
|
||||
self[key] = func(self, new)
|
||||
end
|
||||
end
|
||||
|
||||
-- Define a comparator function
|
||||
-- Acts like a setter, except that it does not set the new value but rather compares the present and specified values and returns whether they are equal or not
|
||||
-- Incompatible with setter
|
||||
-- The function is optional. The == operator is used else.
|
||||
function Object:__comparator(name, func)
|
||||
local cache_getter = self[name]
|
||||
local key = "_" .. name
|
||||
|
||||
self[name] = function(self, expected)
|
||||
-- the current value is needed everytime, no matter whether there is an argument or not
|
||||
local actual
|
||||
|
||||
if cache_getter then
|
||||
-- call the cache getter if present
|
||||
actual = cache_getter(self)
|
||||
else
|
||||
-- use the value else
|
||||
actual = self[key]
|
||||
end
|
||||
|
||||
-- act as a getter if there is no argument
|
||||
if expected == nil then
|
||||
return actual
|
||||
end
|
||||
|
||||
if func then
|
||||
-- if a function as specified, call it
|
||||
return func(actual, expected)
|
||||
else
|
||||
-- else, use the == operator to compare the expected value to the actual
|
||||
return actual == expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Override an already existing function in a way that the old function is called
|
||||
-- If nil is returned, the old function is called. Else the return value is returned. (Only the first return value is taken into concern here, multiple are supported tho)
|
||||
-- This works even if it is applied to the instance of a class when the function is defined by the class
|
||||
-- It also works with overriding functions that are located in superclasses
|
||||
function Object:__override(name, func)
|
||||
-- store the old function
|
||||
local old_func = self[name]
|
||||
|
||||
-- redefine the function with variable arguments
|
||||
self[name] = function(...)
|
||||
-- call the new function and store the return values in a table
|
||||
local rvals = {func(...)}
|
||||
-- if nil was returned, fall back to the old function
|
||||
if rvals[1] == nil then
|
||||
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||
return old_func(...)
|
||||
else
|
||||
-- return the values from the new function else
|
||||
return unpack(rvals)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Works like override except that the new function does not modify the output of the old function but rather the input
|
||||
-- The new function can decide with what arguments by returing them, including the `self` reference
|
||||
-- If the "self" arg is not returned the old function is not called
|
||||
-- Note that this way the new function cannot change the return value of the old function
|
||||
function Object:__pipe(name, func)
|
||||
local old_func = self[name]
|
||||
|
||||
self[name] = function(self, ...)
|
||||
local rvals = {func(self, ...)}
|
||||
-- check if self was returned properly
|
||||
if rvals[1] then
|
||||
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||
return old_func(unpack(rvals))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Make class available as table to distribute the Object table
|
||||
class = setmetatable({Object = Object}, {
|
||||
-- Create a new class by calling class() with an optional superclass argument
|
||||
__call = function(super)
|
||||
return setmetatable({}, {
|
||||
-- Create a new instance of the class when the class is called
|
||||
__call = function(_class, ...)
|
||||
-- Check whether the first argument is an instance of the class
|
||||
-- If that is the case, just return it - this is to allow "making sure something is the instance of a class" by calling the constructor
|
||||
local argtbl = {...}
|
||||
local first_arg = args[1]
|
||||
if first_arg and type(first_arg) == "table" and inst.CLASS = _class then
|
||||
return inst
|
||||
end
|
||||
-- set the metatable and remember which class the object belongs to
|
||||
local instance = setmetatable({CLASS = _class}, {
|
||||
__index = _class,
|
||||
})
|
||||
-- call the constructor if present
|
||||
if instance.constructor then
|
||||
instance:constructor(...)
|
||||
end
|
||||
-- return the created instance
|
||||
return instance
|
||||
end,
|
||||
-- Object as superclass of all classes that dont have a different one
|
||||
__index = super or Object,
|
||||
})
|
||||
end
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
name = class
|
||||
author = Fleckenstein
|
||||
description = A class system for MineClone2
|
|
@ -1,23 +0,0 @@
|
|||
# controls
|
||||
|
||||
## controls.players
|
||||
Table containing player controls at runtime.
|
||||
WARNING: Never use this table in writing
|
||||
|
||||
## controls.register_on_press(func)
|
||||
Register a function that will be executed with (player, keyname) every time a player press a key.
|
||||
|
||||
## controls.registered_on_press
|
||||
Table containing functions registered with controls.register_on_press().
|
||||
|
||||
## controls.register_on_release(func)
|
||||
Register a function that will be executed with (player, keyname, clock_from_last_press) every time a player release a key.
|
||||
|
||||
## controls.registered_on_release
|
||||
Table containing functions registered with controls.register_on_release().
|
||||
|
||||
## controls.register_on_hold(func)
|
||||
Register a function that will be executed with (player, keyname, clock_from_start_hold) every time a player hold a key.
|
||||
|
||||
## controls.registered_on_hold
|
||||
Table containing functions registered with controls.register_on_hold().
|
|
@ -1,45 +0,0 @@
|
|||
# flowlib
|
||||
Simple flow functions.
|
||||
|
||||
## flowlib.is_touching(realpos, nodepos, radius)
|
||||
Return true if a sphere of <radius> at <realpos> collide with node at <nodepos>.
|
||||
* realpos: position
|
||||
* nodepos: position
|
||||
* radius: number
|
||||
|
||||
## flowlib.is_water(pos)
|
||||
Return true if node at <pos> is water, false overwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_water(node)
|
||||
Return true if <node> is water, false overwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.is_lava(pos)
|
||||
Return true if node at <pos> is lava, false overwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_lava(node)
|
||||
Return true if <node> is lava, false overwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.is_liquid(pos)
|
||||
Return true if node at <pos> is liquid, false overwise.
|
||||
* pos: position
|
||||
|
||||
## flowlib.node_is_liquid(node)
|
||||
Return true if <node> is liquid, false overwise.
|
||||
* node: node
|
||||
|
||||
## flowlib.quick_flow(pos, node)
|
||||
Return direction where the water is flowing (to be use to push mobs, items...).
|
||||
* pos: position
|
||||
* node: node
|
||||
|
||||
## flowlib.move_centre(pos, realpos, node, radius)
|
||||
Return the pos of the nearest not water block near from <pos> in a sphere of <radius> at <realpos>.
|
||||
WARNING: This function is never used in mcl2, use at your own risk. The informations described here may be wrong.
|
||||
* pos: position
|
||||
* realpos: position, position of the entity
|
||||
* node: node
|
||||
* radius: number
|
|
@ -1,27 +0,0 @@
|
|||
# mcl_autogroup
|
||||
This mod emulate digging times from mc.
|
||||
|
||||
## mcl_autogroup.can_harvest(nodename, toolname)
|
||||
Return true if <nodename> can be dig with <toolname>.
|
||||
* nodename: string, valid nodename
|
||||
* toolname: (optional) string, valid toolname
|
||||
|
||||
## mcl_autogroup.get_groupcaps(toolname, efficiency)
|
||||
This function is used to calculate diggroups for tools.
|
||||
WARNING: This function can only be called after mod initialization.
|
||||
* toolname: string, name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
|
||||
* efficiency: (optional) integer, the efficiency level the tool is enchanted with (default 0)
|
||||
|
||||
## mcl_autogroup.get_wear(toolname, diggroup)
|
||||
Return the max wear of <toolname> with <diggroup>
|
||||
WARNING: This function can only be called after mod initialization.
|
||||
* toolname: string, name of the tool used
|
||||
* diggroup: string, the name of the diggroup the tool is used on
|
||||
|
||||
## mcl_autogroup.register_diggroup(group, def)
|
||||
* group: string, name of the group to register as a digging group
|
||||
* def: (optional) table, table with information about the diggroup (defaults to {} if unspecified)
|
||||
* level: (optional) string, if specified it is an array containing the names of the different digging levels the digging group supports
|
||||
|
||||
## mcl_autogroup.registered_diggroups
|
||||
List of registered diggroups, indexed by name.
|
|
@ -1,28 +0,0 @@
|
|||
--[[
|
||||
This is one part of a mod to replicate the digging times from Minecraft. This
|
||||
part only exposes a function to register digging groups. The rest of the mod is
|
||||
implemented and documented in the _mcl_autogroup.
|
||||
|
||||
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
|
||||
mcl_autogroup contains the API functions used to register custom digging groups.
|
||||
_mcl_autogroup contains most of the code. The leading underscore in the name
|
||||
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
|
||||
as possible. Minetest loads mods in reverse alphabetical order.
|
||||
--]]
|
||||
mcl_autogroup = {}
|
||||
mcl_autogroup.registered_diggroups = {}
|
||||
|
||||
assert(minetest.get_modpath("_mcl_autogroup"), "This mod requires the mod _mcl_autogroup to function")
|
||||
|
||||
-- Register a group as a digging group.
|
||||
--
|
||||
-- Parameters:
|
||||
-- group - Name of the group to register as a digging group
|
||||
-- def - Table with information about the diggroup (defaults to {} if unspecified)
|
||||
--
|
||||
-- Values in def:
|
||||
-- level - If specified it is an array containing the names of the different
|
||||
-- digging levels the digging group supports.
|
||||
function mcl_autogroup.register_diggroup(group, def)
|
||||
mcl_autogroup.registered_diggroups[group] = def or {}
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
name = mcl_autogroup
|
||||
author = ryvnf
|
||||
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.
|
|
@ -117,10 +117,6 @@ function mcl_burning.damage(obj)
|
|||
end
|
||||
|
||||
function mcl_burning.set_on_fire(obj, burn_time, reason)
|
||||
if obj:get_hp() < 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.fire_resistant then
|
||||
return
|
||||
|
@ -149,7 +145,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
end
|
||||
|
||||
if old_burn_time <= burn_time then
|
||||
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
||||
local sound_id = mcl_burning.get(obj, "int", "sound_id")
|
||||
if sound_id == 0 then
|
||||
sound_id = minetest.sound_play("fire_fire", {
|
||||
object = obj,
|
||||
|
@ -157,7 +153,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
max_hear_distance = 16,
|
||||
loop = true,
|
||||
}) + 1
|
||||
end]]--
|
||||
end
|
||||
|
||||
local hud_id
|
||||
if obj:is_player() then
|
||||
|
@ -167,7 +163,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
hud_elem_type = "image",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
scale = {x = -100, y = -100},
|
||||
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
||||
text = "fire_basic_flame.png",
|
||||
z_index = 1000,
|
||||
}) + 1
|
||||
end
|
||||
|
@ -175,7 +171,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
|
|||
mcl_burning.set(obj, "float", "burn_time", burn_time)
|
||||
mcl_burning.set(obj, "string", "reason", reason)
|
||||
mcl_burning.set(obj, "int", "hud_id", hud_id)
|
||||
--mcl_burning.set(obj, "int", "sound_id", sound_id)
|
||||
mcl_burning.set(obj, "int", "sound_id", sound_id)
|
||||
|
||||
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
|
||||
local minp, maxp = mcl_burning.get_collisionbox(obj)
|
||||
|
@ -198,8 +194,8 @@ end
|
|||
|
||||
function mcl_burning.extinguish(obj)
|
||||
if mcl_burning.is_burning(obj) then
|
||||
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
||||
--minetest.sound_stop(sound_id)
|
||||
local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
|
||||
minetest.sound_stop(sound_id)
|
||||
|
||||
if obj:is_player() then
|
||||
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
|
||||
|
@ -210,7 +206,7 @@ function mcl_burning.extinguish(obj)
|
|||
mcl_burning.set(obj, "float", "burn_time")
|
||||
mcl_burning.set(obj, "float", "damage_timer")
|
||||
mcl_burning.set(obj, "int", "hud_id")
|
||||
--mcl_burning.set(obj, "int", "sound_id")
|
||||
mcl_burning.set(obj, "int", "sound_id")
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# mcl_colors
|
||||
Mod providing global table containing legacity minecraft colors to be used in mods.
|
||||
|
||||
## mcl_colors.*
|
||||
Colors by upper name, in hex value.
|
||||
|
||||
## mcl_colors.background.*
|
||||
Background colors by upper name, in hex value.
|
|
@ -0,0 +1,29 @@
|
|||
MCLDamageSource = class()
|
||||
|
||||
function MCLDamageSource:constructor(tbl)
|
||||
if tbl then
|
||||
for k, v in pairs(tbl) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MCLDamageSource:from_mt(reason)
|
||||
|
||||
end
|
||||
|
||||
MCLDamageSource:__getter("direct_object", function(self)
|
||||
local obj = self.raw_source_object
|
||||
if not obj then
|
||||
return
|
||||
end
|
||||
return mcl_object_mgr.get(obj)
|
||||
end)
|
||||
|
||||
MCLDamageSource:__getter("source_object", function(self)
|
||||
local direct = self:direct_object()
|
||||
if not direct then
|
||||
return
|
||||
end
|
||||
return direct.source_object or direct
|
||||
end)
|
|
@ -0,0 +1,44 @@
|
|||
MCLEntity = class(MCLObject)
|
||||
|
||||
MCLEntity:__getter("meta", MCLMetadata)
|
||||
|
||||
local last_inv_id = 0
|
||||
|
||||
MCLEntity:__getter("inventory", function(self)
|
||||
local info = self.inventory_info
|
||||
if not info then
|
||||
return
|
||||
end
|
||||
self.inventory_id = "mcl_entity:" .. last_inv_id
|
||||
last_inv_id = last_inv_id + 1
|
||||
local inv = minetest.create_detached_inventory(self.inventory_id, self.inventory_callbacks)
|
||||
for list, size in pairs(data.sizes) do
|
||||
inv:set_size(list, size)
|
||||
end
|
||||
for list, liststr in pairs(data.lists) do
|
||||
inv:set_list(list, liststr)
|
||||
end
|
||||
return inv
|
||||
end)
|
||||
|
||||
function MCLEntity:on_activate(staticdata)
|
||||
local data = minetest.deserialize(staticdata)
|
||||
if data then
|
||||
self:meta():from_table(data)
|
||||
self.inventory_info = data.inventory
|
||||
end
|
||||
end
|
||||
|
||||
function MCLEntity:get_staticdata()
|
||||
local data = self:meta():to_table()
|
||||
local inventory_info = self.inventory_info
|
||||
|
||||
if inventory_info then
|
||||
data.inventory = {
|
||||
sizes = inventory_info.sizes,
|
||||
lists = self:inventory():get_lists()
|
||||
}
|
||||
end
|
||||
|
||||
return minetest.serialize(data)
|
||||
end
|
|
@ -0,0 +1,103 @@
|
|||
MCLEquipment = class()
|
||||
|
||||
function MCLEquipment:constructor(inv, idx)
|
||||
self.inv = inv
|
||||
self.idx = idx
|
||||
end
|
||||
|
||||
MCLEquipment:__cache_getter("has_main", function(self)
|
||||
return self.inv and self.idx and self.inv:get_list("main") and true or false
|
||||
end)
|
||||
|
||||
MCLEquipment:__cache_getter("has_right", function(self)
|
||||
return self.inv and self.inv:get_list("right_hand") and true or false
|
||||
end)
|
||||
|
||||
MCLEquipment:__cache_getter("has_left", function(self)
|
||||
return self.inv and self.inv:get_list("left_hand") and true or false
|
||||
end)
|
||||
|
||||
MCLEquipment:__cache_getter("has_armor", function(self)
|
||||
return self.inv and self.inv:get_list("armor") and true or false
|
||||
end)
|
||||
|
||||
function MCLEquipment:mainhand()
|
||||
if self:has_main() then
|
||||
return self.inv:get_stack("main", self.idx)
|
||||
elseif self:has_right() then
|
||||
return self.inv:get_stack("right_hand", 1)
|
||||
else
|
||||
return ItemStack()
|
||||
end
|
||||
end
|
||||
|
||||
MCLEquipment:__setter("mainhand", function(self, new)
|
||||
if self:has_main() then
|
||||
self.inv:set_stack("main", self.idx, stack)
|
||||
elseif self:has_right() then
|
||||
self.inv:set_stack("right_hand", 1, stack)
|
||||
end
|
||||
end)
|
||||
|
||||
function MCLEquipment:offhand()
|
||||
if self:has_left() then
|
||||
return self.inv:get_stack("left_hand", 1)
|
||||
else
|
||||
return ItemStack()
|
||||
end
|
||||
end
|
||||
|
||||
MCLEquipment:__setter("offhand", function(self, new)
|
||||
if self:has_left() then
|
||||
self.inv:set_stack("left_hand", 1, new)
|
||||
end
|
||||
end)
|
||||
|
||||
function MCLEquipment:__armor(idx, name)
|
||||
self[name] = function(self)
|
||||
if self:has_armor() then
|
||||
return self.inv:get_stack("armor", idx)
|
||||
else
|
||||
return ItemStack()
|
||||
end
|
||||
end
|
||||
|
||||
self:__setter(name, function(self, new)
|
||||
if self:has_armor() then
|
||||
self.inv:set_stack("armor", idx, new)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local armor_slots = {"helmet", "chestplace", "leggings", "boots"}
|
||||
|
||||
for i, name in ipairs(armor_slots) do
|
||||
MCLEquipment:__armor(idx, name)
|
||||
end
|
||||
|
||||
local function insert(tbl, key, stack)
|
||||
if stack:get_name() ~= "" then
|
||||
tbl[key] = stack
|
||||
end
|
||||
end
|
||||
|
||||
function MCLEquipment:get_armor()
|
||||
local tbl = {}
|
||||
if self:has_armor() then
|
||||
for i, name in ipairs(armor_slots) do
|
||||
insert(tbl, name, self.inv:get_stack("armor", i))
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
function MCLEquipment:get_all()
|
||||
local tbl = {}
|
||||
insert(tbl, "mainhand", self:mainhand())
|
||||
insert(tbl, "offhand", self:offhand())
|
||||
for k, v in pairs(self:get_armor()) do
|
||||
tbl[k] = v
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# mcl_explosions
|
||||
This mod provide helper functions to create explosions.
|
||||
|
||||
## mcl_explosions.explode(pos, strength, info, puncher)
|
||||
* pos: position, initial position of the explosion
|
||||
* strenght: number, radius of the explosion
|
||||
* info: table, explosion informations:
|
||||
* drop_chance: number, if specified becomes the drop chance of all nodes in the explosion (default: 1.0 / strength)
|
||||
* max_blast_resistance: int, if specified the explosion will treat all non-indestructible nodes as having a blast resistance of no more than this value
|
||||
* sound: bool, if true, the explosion will play a sound (default: true)
|
||||
* particles: bool, if true, the explosion will create particles (default: true)
|
||||
* fire: bool, if true, 1/3 nodes become fire (default: false)
|
||||
* griefing: bool, if true, the explosion will destroy nodes (default: true)
|
||||
* grief_protected: bool, if true, the explosion will also destroy nodes which have been protected (default: false)
|
||||
* puncher: (optional) entity, will be used as source for damage done by the explosion
|
|
@ -0,0 +1,66 @@
|
|||
mcl_gamerules = {
|
||||
__defaults = {},
|
||||
__rules = {},
|
||||
}
|
||||
|
||||
setmetatable(mcl_gamerules, {__index = mcl_gamerules.__rules})
|
||||
|
||||
local worldpath = minetest.get_worldpath()
|
||||
|
||||
function mcl_gamerules.__load()
|
||||
local file = io.open(worldpath .. "gamerules.json", "r")
|
||||
if file then
|
||||
local contents = file:read("*all")
|
||||
file:close()
|
||||
local data = minetest.parse_json(contents)
|
||||
local rules = mcl_gamerules.__rules
|
||||
for rule, default in pairs(mcl_gamerules.__defaults) do
|
||||
local value = data[rule]
|
||||
if value == nil then
|
||||
value = default
|
||||
end
|
||||
rules[rule] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_gamerules.__save()
|
||||
local file = io.open(worldpath .. "gamerules.json", "w")
|
||||
file:write(minetest.write_json(mcl_gamerules.__rules, true))
|
||||
file:close()
|
||||
end
|
||||
|
||||
function mcl_gamerules.__set(rule, value)
|
||||
if not mcl_gamerules.__defaults[rule] then
|
||||
return false
|
||||
end
|
||||
mcl_gamerules.__rules[rule] = value
|
||||
mcl_gamerules.__save()
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_gamerules.__register(rule, default)
|
||||
mcl_gamerules.__defaults[rule] = default
|
||||
end
|
||||
|
||||
mcl_gamerules.__register("announceAdvancements", true)
|
||||
mcl_gamerules.__register("commandBlockOutput", true)
|
||||
mcl_gamerules.__register("doDaylightCycle", true)
|
||||
mcl_gamerules.__register("doFireTick", true)
|
||||
mcl_gamerules.__register("doImmediateRespawn", false)
|
||||
mcl_gamerules.__register("doMobLoot", true)
|
||||
mcl_gamerules.__register("doMobSpawning", true)
|
||||
mcl_gamerules.__register("doTileDrops", true)
|
||||
mcl_gamerules.__register("doWeatherCycle", true)
|
||||
mcl_gamerules.__register("drowningDamage", true)
|
||||
mcl_gamerules.__register("fallDamage", true)
|
||||
mcl_gamerules.__register("fireDamage", true)
|
||||
mcl_gamerules.__register("keepInventory", false)
|
||||
mcl_gamerules.__register("logAdminCommands", true)
|
||||
mcl_gamerules.__register("mobGriefing", true)
|
||||
mcl_gamerules.__register("naturalRegeneration", true)
|
||||
mcl_gamerules.__register("pvp", true)
|
||||
mcl_gamerules.__register("showDeathMessages", true)
|
||||
mcl_gamerules.__register("tntExplodes", true)
|
||||
|
||||
minetest.register_on_mods_loaded(mcl_gamerules.__load)
|
|
@ -0,0 +1,32 @@
|
|||
mcl_groupcache = {
|
||||
cache = {},
|
||||
}
|
||||
|
||||
local function check_insert(item, group, cache)
|
||||
if minetest.get_item_group(item, group) ~= 0 then
|
||||
table.insert(cache, item)
|
||||
end
|
||||
end
|
||||
|
||||
local old_register_item = minetest.register_item
|
||||
|
||||
function minetest.register_item(name, def)
|
||||
old_register_item(name, def)
|
||||
for group, cache in pairs(mcl_groupcache.cache) do
|
||||
check_insert(item, group, cache)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_groupcache.init_cache(group)
|
||||
local cache = {}
|
||||
for item in pairs(minetest.registered_items) do
|
||||
check_insert(item, group, cache)
|
||||
end
|
||||
return cache
|
||||
end
|
||||
|
||||
function mcl_groupcache.get_items_in_group(group)
|
||||
local cache = mcl_groupcache.cache[group] or mcl_groupcache.init_cache(group)
|
||||
mcl_groupcache.cache[group] = cache
|
||||
return cache
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
name = mcl_groupcache
|
||||
author = Fleckenstein
|
||||
description = Keep track of items with certain groups
|
|
@ -21,9 +21,6 @@ mcl_vars.gui_bg_img = "background9[1,1;1,1;mcl_base_textures_background9.png;tru
|
|||
-- Legacy
|
||||
mcl_vars.inventory_header = ""
|
||||
|
||||
-- Tool wield size
|
||||
mcl_vars.tool_wield_scale = { x = 1.8, y = 1.8, z = 1 }
|
||||
|
||||
-- Mapgen variables
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
local minecraft_height_limit = 256
|
||||
|
@ -36,26 +33,25 @@ mcl_vars.MAP_BLOCKSIZE = math.max(1, core.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, core.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 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 central_chunk_max_pos = central_chunk_min_pos + 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_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
|
||||
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
|
||||
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
|
||||
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
|
||||
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes
|
||||
|
||||
local function coordinate_to_block(x)
|
||||
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
|
||||
end
|
||||
|
||||
local function coordinate_to_chunk(x)
|
||||
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
|
||||
return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize)
|
||||
end
|
||||
|
||||
function mcl_vars.pos_to_block(pos)
|
||||
|
@ -74,7 +70,7 @@ function mcl_vars.pos_to_chunk(pos)
|
|||
}
|
||||
end
|
||||
|
||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
|
||||
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes)
|
||||
local k_positive_z = k_positive * 2
|
||||
local k_positive_y = k_positive_z * k_positive_z
|
||||
|
||||
|
@ -178,86 +174,3 @@ minetest.craftitemdef_default.stack_max = 64
|
|||
-- Set random seed for all other mods (Remember to make sure no other mod calls this function)
|
||||
math.randomseed(os.time())
|
||||
|
||||
local chunks = {} -- intervals of chunks generated
|
||||
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
|
||||
prev[2] = d[2]
|
||||
table.remove(chunks, i)
|
||||
return
|
||||
end
|
||||
d[1] = n
|
||||
return
|
||||
end
|
||||
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
|
||||
return
|
||||
end
|
||||
prev = d
|
||||
end
|
||||
chunks[#chunks+1] = {n, n}
|
||||
end
|
||||
function mcl_vars.is_generated(pos)
|
||||
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
|
||||
for i, d in pairs(chunks) do
|
||||
if n <= d[2] then
|
||||
return (n >= d[1])
|
||||
end
|
||||
end
|
||||
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.
|
||||
-- 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 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(p)
|
||||
if node.name ~= "ignore" then
|
||||
return node
|
||||
end
|
||||
|
||||
-- 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, 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)
|
||||
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)
|
||||
else
|
||||
minetest.emerge_area(pos, pos)
|
||||
end
|
||||
|
||||
local t = minetest.get_us_time()
|
||||
|
||||
node = minetest.get_node(pos)
|
||||
|
||||
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do
|
||||
node = minetest.get_node(pos)
|
||||
end
|
||||
|
||||
return node
|
||||
-- it still can return "ignore", LOL, even if force = true, but only after time out
|
||||
end
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
MCLItemStack = class()
|
||||
|
||||
function MCLItemStack:constructor(stack)
|
||||
self.stack = stack
|
||||
end
|
||||
|
||||
MCLItemStack:__getter("enchantments", function(self)
|
||||
return mcl_enchanting.get_enchantments(self.stack)
|
||||
end)
|
||||
MCLItemStack:__comparator("enchantments", mcl_types.match_enchantments)
|
||||
|
||||
function MCLItemStack:meta()
|
||||
return self.stack:get_meta()
|
||||
end
|
||||
MCLItemStack:__comparator("meta", mcl_types.match_meta)
|
||||
|
||||
function MCLItemStack:get_enchantment(name)
|
||||
return self:enchantments()[name] or 0
|
||||
end
|
||||
|
||||
function MCLItemStack:has_enchantment(name)
|
||||
return self:get_enchantment(name) > 0
|
||||
end
|
||||
|
||||
function MCLItemStack:total_durability()
|
||||
end
|
||||
|
||||
function MCLItemStack:durability()
|
||||
local def = self.stack:get_definition()
|
||||
if def then
|
||||
local base_uses = def._durability
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function MCLItemStack:use_durability()
|
||||
end
|
||||
|
||||
function MCLItemStack:restore_durability()
|
||||
end
|
||||
|
||||
function MCLItemStack:get_group()
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
MCLMetadata = class()
|
||||
|
||||
function MCLMetadata:constructor()
|
||||
self.fields = {}
|
||||
end
|
||||
|
||||
for _type, default in pairs({string = "", float = 0.0, int = 0}) do
|
||||
MCLMetadata["set_" .. _type] = function(name, value) do
|
||||
if value == default then
|
||||
value = nil
|
||||
end
|
||||
self.fields[name] = value
|
||||
end
|
||||
MCLMetadata["get_" .. _type] = function(name) do
|
||||
return self.fields[name] or default
|
||||
end
|
||||
end
|
||||
|
||||
function MCLMetadata:to_table()
|
||||
return table.copy(self)
|
||||
end
|
||||
|
||||
function MCLMetadata:from_table(tbl)
|
||||
self.fields = table.copy(tbl.fields)
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
MCLMob = class(MCLEntity)
|
||||
|
||||
function MCLMob:get_hp()
|
||||
self:meta():get_float("hp")
|
||||
end
|
||||
|
||||
function MCLMob:set_hp()
|
||||
self:meta():set_float("hp", hp)
|
||||
end
|
||||
|
||||
function MCLMob:on_damage(damage, source)
|
||||
MCLEntity.on_damage(self, damage, source)
|
||||
|
||||
local new_hp = self:get_hp()
|
||||
if new_hp <= 0 and new_hp + damage > 0 then
|
||||
self:on_death(source)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,105 @@
|
|||
MCLObject = class()
|
||||
|
||||
function MCLObject:constructor(obj)
|
||||
self.object = obj.object or obj
|
||||
self.IS_MCL_OBJECT = true
|
||||
end
|
||||
|
||||
function MCLObject:on_punch(hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
||||
local source = MCLDamageSource({is_punch = true, raw_source_object = hitter})
|
||||
|
||||
local knockback = {
|
||||
hitter = hitter,
|
||||
time_from_last_punch = time_from_last_punch,
|
||||
tool_capabilities = tool_capabilities,
|
||||
dir = dir,
|
||||
}
|
||||
|
||||
self:damage(damage, source, knockback)
|
||||
return true
|
||||
end
|
||||
|
||||
-- use this function to deal regular damage to an object (do NOT use :punch() unless toolcaps need to be handled)
|
||||
function MCLObject:damage(damage, source, knockback)
|
||||
damage = self:damage_modifier(damage, source) or damage
|
||||
self:set_hp(self:get_hp() - damage)
|
||||
|
||||
if type(knockback) == "table" then
|
||||
knockback = self:calculate_knockback(
|
||||
knockback.hitter,
|
||||
knockback.time_from_last_punch,
|
||||
knockback.tool_capabilities,
|
||||
knockback.dir or vector.direction(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||
knockback.distance or vector.distance(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||
damage,
|
||||
source)
|
||||
end
|
||||
|
||||
table.insert(self.damage_info, {
|
||||
damage = damage,
|
||||
source = source,
|
||||
knockback = knockback,
|
||||
})
|
||||
|
||||
return damage
|
||||
end
|
||||
|
||||
function MCLObject:wield_index()
|
||||
end
|
||||
|
||||
MCLObject:__getter("equipment", function(self)
|
||||
return MCLEquipment(self:inventory(), self:wield_index())
|
||||
end)
|
||||
|
||||
function MCLObject:get_hp()
|
||||
return self.object:get_hp()
|
||||
end
|
||||
|
||||
function MCLObject:set_hp(hp)
|
||||
self.object:set_hp(hp)
|
||||
end
|
||||
|
||||
function MCLObject:death_drop(inventory, listname, index, stack)
|
||||
minetest.add_item(self.object:get_pos(), stack)
|
||||
inventory:set_stack(listname, index, nil)
|
||||
end
|
||||
|
||||
function MCLObject:on_death(source)
|
||||
local inventory = self:inventory()
|
||||
if inventory then
|
||||
for listname, list in pairs(inventory:get_lists()) do
|
||||
for index, stack in pairs(list) do
|
||||
if stack:get_name() ~= "" and then
|
||||
self:death_drop(inventory, listname, index, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MCLObject:damage_modifier(damage, source)
|
||||
if self.invulnerable and not source.bypasses_invulnerability then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function MCLObject:on_damage(damage, source, knockback)
|
||||
end
|
||||
|
||||
MCLObject.calculate_knockback = minetest.calculate_knockback
|
||||
|
||||
function minetest.calculate_knockback()
|
||||
return 0
|
||||
end
|
||||
|
||||
function MCLObject:on_step()
|
||||
local damage_info = self.damage_info
|
||||
if damage_info then
|
||||
self.damage_info = nil
|
||||
self:on_damage(damage_info.damage, damage_info.source)
|
||||
|
||||
if damage_info.knockback then
|
||||
self.object:add_velocity(damage_info.knockback)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,121 @@
|
|||
mcl_object_mgr = {
|
||||
players = {}
|
||||
}
|
||||
|
||||
-- functions
|
||||
|
||||
function mcl_object_mgr.get(obj)
|
||||
local rval
|
||||
|
||||
if mcl_object_mgr.is_mcl_object(obj) then
|
||||
rval = obj
|
||||
elseif mcl_object_mgr.is_player(obj) then
|
||||
rval = mcl_object_mgr.get_player(obj)
|
||||
elseif mcl_object_mgr.is_entity(obj) then
|
||||
rval = mcl_object_mgr.get_entity(obj)
|
||||
end
|
||||
|
||||
return assert(rval, "No matching MCLObject found. This is most likely an error caused by custom mods.")
|
||||
end
|
||||
|
||||
function mcl_object_mgr.is_mcl_object(obj)
|
||||
return type(obj) == "table" and obj.IS_MCL_OBJECT
|
||||
end
|
||||
|
||||
function mcl_object_mgr.is_player(obj)
|
||||
return type(obj) == "string" or type(obj) == "userdata" and obj:is_player()
|
||||
end
|
||||
|
||||
function mcl_object_mgr.is_is_entity(obj)
|
||||
return type(obj) == "table" and obj.object or type(obj) == "userdata" and obj:get_luaentity()
|
||||
end
|
||||
|
||||
function mcl_object_mgr.get_entity(ent)
|
||||
if type(ent) == "userdata" then
|
||||
ent = ent:get_luaentity()
|
||||
end
|
||||
return ent.mcl_entity
|
||||
end
|
||||
|
||||
function mcl_object_mgr.get_player(name)
|
||||
if type(name) == "userdata" then
|
||||
name = name:get_player_name()
|
||||
end
|
||||
return mcl_player_mgr.players[name]
|
||||
end
|
||||
|
||||
-- entity wrappers
|
||||
|
||||
local function add_entity_wrapper(def, name)
|
||||
def[name] = function(luaentity, ...)
|
||||
local func = self.mcl_entity[name]
|
||||
if func then
|
||||
return func(self.mcl_entity, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_object_mgr.register_entity(name, initial_properties, base_class)
|
||||
local def = {
|
||||
initial_properties = initial_properties,
|
||||
|
||||
on_activate = function(self, ...)
|
||||
local entity = base_class(self.object)
|
||||
self.mcl_entity = entity
|
||||
if entity.on_activate then
|
||||
entity:on_activate(...)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
add_entity_wrapper(def, "on_deactivate")
|
||||
add_entity_wrapper(def, "on_step")
|
||||
add_entity_wrapper(def, "on_punch")
|
||||
add_entity_wrapper(def, "on_death")
|
||||
add_entity_wrapper(def, "on_rightclick")
|
||||
add_entity_wrapper(def, "on_attach_child")
|
||||
add_entity_wrapper(def, "on_detach_child")
|
||||
add_entity_wrapper(def, "on_detach")
|
||||
add_entity_wrapper(def, "get_staticdata")
|
||||
|
||||
minetest.register_entity(name, def)
|
||||
end
|
||||
|
||||
-- player wrappers
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
mcl_player_mgr.players[name] = MCLPlayer(player)
|
||||
mcl_player_mgr.players[name]:on_join()
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
mcl_player_mgr.players[name]:on_leave()
|
||||
mcl_player_mgr.players[name] = nil
|
||||
end)
|
||||
|
||||
local function add_player_wrapper(wrapper, regfunc)
|
||||
minetest[regfunc or "register_" .. wrapper .. "player"](function(player, ...)
|
||||
local mclplayer = mcl_player_mgr.players[player:get_player_name()]
|
||||
local func = mclplayer[funcname or wrapper]
|
||||
if func then
|
||||
func(mclplayer, ...)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
add_player_wrapper("on_punch")
|
||||
add_player_wrapper("on_rightclick")
|
||||
add_player_wrapper("on_death", "register_on_dieplayer")
|
||||
add_player_wrapper("on_respawn")
|
||||
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||
if not reason.auto then
|
||||
local mclplayer = mcl_object_mgr.get(player)
|
||||
local source = MCLDamageSource():from_mt(reason)
|
||||
|
||||
mclplayer:damage(hp_change, source)
|
||||
return true
|
||||
end
|
||||
end, true)
|
|
@ -0,0 +1,19 @@
|
|||
MCLPlayer = class(MCLObject)
|
||||
|
||||
MCLPlayer:__cache_getter("meta", function(self)
|
||||
return self.object:get_meta()
|
||||
end)
|
||||
|
||||
MCLPlayer:__cache_getter("inventory", function(self)
|
||||
return self.object:get_inventory()
|
||||
end)
|
||||
|
||||
MCLPlayer:__override_pipe("death_drop", function(self, inventory, listname, index, stack)
|
||||
if not mcl_gamerules.keepInventory then
|
||||
return self, inventory, listname, index, stack
|
||||
end
|
||||
end)
|
||||
|
||||
function MCLPlayer:on_damage(damage, source, knockback)
|
||||
MCLObject.on_damage(self, damage, source, knockback)
|
||||
end
|
|
@ -410,7 +410,7 @@ function mcl_util.get_color(colorstr)
|
|||
local mc_color = mcl_colors[colorstr:upper()]
|
||||
if mc_color then
|
||||
colorstr = mc_color
|
||||
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#" then
|
||||
elseif #colorstr ~= 7 or colorstr:sub(1, 1) ~= "#"then
|
||||
return
|
||||
end
|
||||
local hex = tonumber(colorstr:sub(2, 7), 16)
|
||||
|
@ -418,3 +418,31 @@ function mcl_util.get_color(colorstr)
|
|||
return colorstr, hex
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_util.registration_function(tbl, func)
|
||||
return function(name, def)
|
||||
if func then
|
||||
local res = func(name, def)
|
||||
if res == false then
|
||||
return
|
||||
elseif res ~= nil then
|
||||
def = res
|
||||
end
|
||||
end
|
||||
tbl[name] = def
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_util.rand(pr, ...)
|
||||
return pr and pr:next(...) or math.random(...)
|
||||
end
|
||||
|
||||
function mcl_util.rand_bool(probability, pr)
|
||||
if probability >= 1 then
|
||||
return true
|
||||
elseif probability <= 0 then
|
||||
return false
|
||||
else
|
||||
return mcl_util.rand(pr, 0, 32767) < probability * 32768
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
# mcl_worlds
|
||||
This mod provides utility functions about positions and dimensions.
|
||||
|
||||
## mcl_worlds.is_in_void(pos)
|
||||
This function returns:
|
||||
|
||||
* true, true: if pos is in deep void (deadly)
|
||||
* true, false: if the pos is in void (non deadly)
|
||||
* false, false: owerwise
|
||||
|
||||
Params:
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.y_to_layer(y)
|
||||
This function is used to calculate the minetest y layer and dimension of the given <y> minecraft layer.
|
||||
Mainly used for ore generation.
|
||||
Takes an Y coordinate as input and returns:
|
||||
|
||||
* The corresponding Minecraft layer (can be nil if void)
|
||||
* The corresponding Minecraft dimension ("overworld", "nether" or "end") or "void" if <y> is in the void
|
||||
If the Y coordinate is not located in any dimension, it will return: nil, "void"
|
||||
|
||||
Params:
|
||||
|
||||
* y: int
|
||||
|
||||
## mcl_worlds.pos_to_dimension(pos)
|
||||
This function return the Minecraft dimension of <pos> ("overworld", "nether" or "end") or "void" if <y> is in the void.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.layer_to_y(layer, mc_dimension)
|
||||
Takes a Minecraft layer and a “dimension” name and returns the corresponding Y coordinate for MineClone 2.
|
||||
mc_dimension can be "overworld", "nether", "end" (default: "overworld").
|
||||
|
||||
* layer: int
|
||||
* mc_dimension: string
|
||||
|
||||
## mcl_worlds.has_weather(pos)
|
||||
Returns true if <pos> can have weather, false owerwise.
|
||||
Weather can be only in the overworld.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.has_dust(pos)
|
||||
Returns true if <pos> can have nether dust, false owerwise.
|
||||
Nether dust can be only in the nether.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.compass_works(pos)
|
||||
Returns true if compasses are working at <pos>, false owerwise.
|
||||
In mc, you cant use compass in the nether and the end.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.compass_works(pos)
|
||||
Returns true if clock are working at <pos>, false owerwise.
|
||||
In mc, you cant use clock in the nether and the end.
|
||||
|
||||
* pos: position
|
||||
|
||||
## mcl_worlds.register_on_dimension_change(function(player, dimension))
|
||||
Register a callback function func(player, dimension).
|
||||
It will be called whenever a player changes between dimensions.
|
||||
The void counts as dimension.
|
||||
|
||||
* player: player, the player who changed the dimension
|
||||
* dimension: position, The new dimension of the player ("overworld", "nether", "end", "void").
|
||||
|
||||
|
||||
## mcl_worlds.registered_on_dimension_change
|
||||
Table containing all function registered with mcl_worlds.register_on_dimension_change()
|
||||
|
||||
## mcl_worlds.dimension_change(player, dimension)
|
||||
Notify this mod of a dimmension change of <player> to <dimension>
|
||||
|
||||
* player: player, player who changed the dimension
|
||||
* dimension: string, new dimension ("overworld", "nether", "end", "void")
|
|
@ -6,7 +6,7 @@ mcl_worlds = {}
|
|||
function mcl_worlds.is_in_void(pos)
|
||||
local void =
|
||||
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
|
||||
(pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
|
||||
(pos.y < mcl_vars.mg_nether_max and pos.y > mcl_vars.mg_nether_min) or
|
||||
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
|
||||
|
||||
local void_deadly = false
|
||||
|
@ -15,11 +15,11 @@ function mcl_worlds.is_in_void(pos)
|
|||
-- Overworld → Void → End → Void → Nether → Void
|
||||
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
|
||||
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
|
||||
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
|
||||
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then
|
||||
-- The void between End and Nether. Like usual, but here, the void
|
||||
-- *above* the Nether also has a small tolerance area, so player
|
||||
-- can fly above the Nether without getting hurt instantly.
|
||||
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
|
||||
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance)
|
||||
elseif pos.y < mcl_vars.mg_nether_min then
|
||||
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
|
||||
end
|
||||
|
@ -35,7 +35,7 @@ end
|
|||
function mcl_worlds.y_to_layer(y)
|
||||
if y >= mcl_vars.mg_overworld_min then
|
||||
return y - mcl_vars.mg_overworld_min, "overworld"
|
||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
|
||||
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max then
|
||||
return y - mcl_vars.mg_nether_min, "nether"
|
||||
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
|
||||
return y - mcl_vars.mg_end_min, "end"
|
||||
|
@ -73,7 +73,7 @@ end
|
|||
-- Takes a position and returns true if this position can have Nether dust
|
||||
function mcl_worlds.has_dust(pos)
|
||||
-- Weather in the Overworld and the high part of the void below
|
||||
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
|
||||
return pos.y <= mcl_vars.mg_nether_max + 64 and pos.y >= mcl_vars.mg_nether_min - 64
|
||||
end
|
||||
|
||||
-- Takes a position (pos) and returns true if compasses are working here
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
mcl_loottables.register_entry = mcl_util.registration_function(mcl_loottables.entries)
|
||||
mcl_loottables.register_table = mcl_util.registration_function(mcl_loottables.tables, function(name, def)
|
||||
local function set_parents(parent)
|
||||
for _, child in ipairs(parent.children or parent.entries or parent.pools or {}) do
|
||||
child.parent = parent
|
||||
set_parents(child)
|
||||
end
|
||||
end
|
||||
set_parents(def)
|
||||
end)
|
||||
|
||||
function mcl_loottables.get_entry_type(entry)
|
||||
return mcl_loottables.entries[entry.type]
|
||||
end
|
||||
|
||||
function mcl_loottables.get_candidates(entries, data, func)
|
||||
local candidates = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
local success = mcl_predicates.do_predicates(entry.conditions, data)
|
||||
|
||||
if success then
|
||||
local children = entry.children
|
||||
|
||||
if children then
|
||||
table.insert_all(candidates, mcl_loottables.get_candidates(children, data, mcl_loottables.get_entry_type(entry).preprocess))
|
||||
else
|
||||
table.insert(candidates, entry)
|
||||
end
|
||||
end
|
||||
|
||||
if func and func(success, data) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return candidates
|
||||
end
|
||||
|
||||
function mcl_loottables.do_item_modifiers(itemstack, node, data)
|
||||
if node then
|
||||
mcl_item_modifiers.do_item_modifiers(itemstack, node.functions, data)
|
||||
mcl_loottables.do_item_modifiers(itemstack, node.parent, data)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_loottables.do_pools(pools, functions, data)
|
||||
local luck = data.luck or 0
|
||||
local stacks = {}
|
||||
for _, pool in ipairs(pools or {}) do
|
||||
if mcl_predicates.do_predicates(pool.conditions, data) do
|
||||
local rolls = mcl_numbers.get_number(pool.rolls, data) + mcl_numbers.get_number(pool.bonus_rolls, data) * luck
|
||||
for i = 1, rolls do
|
||||
local candidates = mcl_loottables.get_candidates(pool.entries, data)
|
||||
|
||||
if #candidates > 0 then
|
||||
local total_weight = 0
|
||||
local weights = {}
|
||||
for _, candidate in ipairs(candidates)
|
||||
total_weight = total_weight + math.floor((candidate.weight or 1) + (candidate.quality or 0) * luck)
|
||||
table.insert(weights, total_weight)
|
||||
end
|
||||
|
||||
local selected
|
||||
local rnd = mcl_util.rand(data.pr, 0, weight - 1)
|
||||
for i, w in ipairs(weights) do
|
||||
if rnd < w then
|
||||
selected = candidates[i]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local pool_stacks = mcl_loottables.get_entry_type(entry).process(selected, data)
|
||||
|
||||
for _, stack in ipairs(pool_stacks) do
|
||||
mcl_item_modifiers.do_item_modifiers(stack, selected, data)
|
||||
end
|
||||
table.insert_all(stacks, pool_stacks)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return stacks
|
||||
end
|
||||
|
||||
function mcl_loottables.get_loot(def, data)
|
||||
if type(def) == "string" then
|
||||
def = mcl_loottables.tables[def]
|
||||
end
|
||||
return mcl_loottables.do_pools(def.pools)
|
||||
end
|
||||
|
||||
function mcl_loottables.drop_loot(def, data)
|
||||
local loot = mcl_loottables.get_loot(def, data)
|
||||
local old_loot = table.copy(loot)
|
||||
for _, stack in ipairs(old_loot) do
|
||||
local max_stack = stack:get_stack_max()
|
||||
while max_stack < stack:get_count() do
|
||||
table.insert(loot, stack:take_items(max_stack))
|
||||
end
|
||||
end
|
||||
return loot
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
mcl_loottables.register_entry("mcl_loottables:alternatives", {
|
||||
preprocess = function(success, data)
|
||||
return success
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:group", {
|
||||
preprocess = function(success, data)
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:sequence", {
|
||||
preprocess = function(success, data)
|
||||
return not success
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:tag", function(entry, data)
|
||||
local stacks = mcl_groupcache.get_items_in_group(entry.name)
|
||||
if entry.expand then
|
||||
stacks = {stacks[pr:next(1, #stacks)]}
|
||||
end
|
||||
return stacks
|
||||
end)
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:loot_table", {
|
||||
process = function(entry, data)
|
||||
return mcl_loottables.get_loot(entry.name, data)
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:empty", {
|
||||
process = function(entry, data)
|
||||
return {}
|
||||
end,
|
||||
})
|
||||
|
||||
mcl_loottables.register_entry("mcl_loottables:item", {
|
||||
process = function(entry, data)
|
||||
return {item = ItemStack(entry.name)}
|
||||
end,
|
||||
})
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
mcl_loottables = {
|
||||
tables = {},
|
||||
entries = {},
|
||||
}
|
||||
|
||||
local modpath = minetest.get_modpath("mcl_loottables")
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
dofile(modpath .. "/entries.lua")
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_loottables
|
||||
author = Fleckenstein
|
||||
description = Provides Minecraft-like loot table definitions
|
||||
depends = mcl_util, mcl_predicates, mcl_item_modifiers, mcl_groupcache
|
|
@ -0,0 +1,19 @@
|
|||
mcl_numbers.register_provider = mcl_util.registration_function(mcl_numbers.providers)
|
||||
|
||||
function mcl_numbers.get_number(provider, data)
|
||||
if type(provider) == "number" then
|
||||
return provider
|
||||
else
|
||||
mcl_numbers.providers[data.type](provider, data)
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_numbers.match_bounds(actual, expected, data)
|
||||
if type(expected) == "table" then
|
||||
expected = {
|
||||
min = mcl_numbers.get_number(expected.min, data),
|
||||
max = mcl_numbers.get_number(expected.max, data)
|
||||
}
|
||||
end
|
||||
return mcl_util.match_bounds(actual, expected)
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
mcl_numbers = {
|
||||
providers = {},
|
||||
}
|
||||
|
||||
dofile(minetest.get_modpath("mcl_numbers") .. "/api.lua")
|
||||
|
||||
mcl_numbers.register_provider("mcl_numbers:constant", function(provider)
|
||||
return provider.value
|
||||
end)
|
||||
|
||||
mcl_numbers.register_provider("mcl_numbers:uniform", function(provider, data)
|
||||
return mcl_util.rand(data.pr, mcl_numbers.get_number(provider.min, data), mcl_numbers.get_number(provider.max, data))
|
||||
end)
|
||||
|
||||
mcl_numbers.register_provider("mcl_numbers:binomial", function(provider, data)
|
||||
local n = mcl_numbers.get_number(provider.n, data)
|
||||
local num = 0
|
||||
for i = 1, n do
|
||||
if mcl_util.rand_bool(mcl_numbers.get_number(provider.p, data), data.pr) then
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
return num
|
||||
end)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_numbers
|
||||
author = Fleckenstein
|
||||
description = Minecraft-like number providers
|
||||
depends = mcl_util
|
|
@ -0,0 +1,15 @@
|
|||
mcl_predicates.register_predicate = mcl_util.registration_function(mcl_predicates.predicates)
|
||||
|
||||
function mcl_predicates.do_predicates(predicates, data, or_mode)
|
||||
or_mode = or_mode or false
|
||||
for _, func in ipairs(predicates) do
|
||||
if type(func) == "string" then
|
||||
func = mcl_predicates.predicates[func]
|
||||
end
|
||||
local failure = func and not func(def, data) or false
|
||||
if or_mode ~= failure then
|
||||
return or_mode
|
||||
end
|
||||
end
|
||||
return not or_mode or #predicates == 0
|
||||
end
|
|
@ -0,0 +1,104 @@
|
|||
mcl_predicates = {
|
||||
predicates = {},
|
||||
}
|
||||
|
||||
local modpath = minetest.get_modpath("mcl_predicates")
|
||||
|
||||
dofile(modpath .. "/api.lua")
|
||||
|
||||
mcl_predicates.register_predicate("alternative", function(predicate, data)
|
||||
return mcl_predicates.do_predicates(predicate.terms, data, true)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("match_node", function(predicate, data)
|
||||
return mcl_types.match_node(data.node or minetest.get_node(data.pos), predicate, data.pos, data.nodemeta)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("damage_source_properties", function(predicate, data)
|
||||
-- ToDo: damage source abstraction
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("entity_properties", function(predicate, data)
|
||||
local entity = predicate.entity
|
||||
if not entity or (entity ~= "this" and entity ~= "killer" and entity ~= "killer_player") then
|
||||
return false
|
||||
end
|
||||
|
||||
local ref = data[entity]
|
||||
if not ref then
|
||||
return false
|
||||
end
|
||||
|
||||
-- ToDo: entity / player abstraction
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("entity_scores", function(predicate, data)
|
||||
-- ToDo: scoreboards
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("inverted", function(predicate, data)
|
||||
return not mcl_predicates.do_predicates({predicate.term}, data)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("killed_by_player", function(predicate, data)
|
||||
return not predicate.inverse ~= not data.killer_player
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("location_check", function(predicate, data)
|
||||
local pos = vector.add(data.pos, vector.new(predicate.offset_x or 0, predicate.offset_y or 0, predicate.offset_z or 0))
|
||||
return mcl_location(pos, data.nodemeta):match(predicate.predicate)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("match_tool", function(predicate, data)
|
||||
return mcl_predicates.match_tool(data.tool, predicate.predicate)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("random_chance", function(predicate, data)
|
||||
return mcl_util.rand_bool(predicate.chance, data.pr)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("random_chance_with_looting", function(predicate, data)
|
||||
local chance = predicate.chance -- + (looting_level * looting_multiplier)
|
||||
|
||||
-- ToDo: entity / player abstraction
|
||||
return mcl_util.rand_bool(chance, data.pr)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("reference", function(predicate, data)
|
||||
-- ToDo: needs research
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("survives_explosion", function(predicate, data)
|
||||
return mcl_util.rand_bool(data.drop_chance or 1, data.pr)
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("table_bonus", function(predicate, data)
|
||||
-- ToDo: entity / player abstraction
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("time_check", function(predicate, data)
|
||||
-- ToDo: needs research
|
||||
return nil
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("weather_check", function(predicate, data)
|
||||
local weather = mcl_weather.get_weather()
|
||||
|
||||
if predicate.thundering then
|
||||
return weather == "thunder"
|
||||
elseif predicate.raining then
|
||||
return weather == "rain"
|
||||
else
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
mcl_predicates.register_predicate("value_check", function(predicate, data)
|
||||
local value = mcl_numbers.get_number(predicate.value, data)
|
||||
return mcl_numbers.check_in_bounds(value, predicate.range, data)
|
||||
end)
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_predicates
|
||||
author = Fleckenstein
|
||||
description = Provides MC-like predicates
|
||||
depends = mcl_util, mcl_numbers
|
|
@ -0,0 +1,88 @@
|
|||
mcl_types = {}
|
||||
|
||||
function mcl_types.match_bounds(actual, expected)
|
||||
if type(expected) == "table" then
|
||||
return actual <= expected.max and actual >= expected.min
|
||||
else
|
||||
return actual == expected
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_types.match_meta(actual, expected)
|
||||
for k, v in pairs(expected) do
|
||||
if actual:get_string(k) ~= v then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_types.match_vector(actual, expected)
|
||||
for k, v in pairs(expected) do
|
||||
if not mcl_types.match_bounds(actual[k], expected[k]) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_types.match_enchantments(actual, expected)
|
||||
for _, v in ipairs(expected) do
|
||||
if not mcl_types.match_bounds(actual[v.enchantment] or 0, v.levels or {min = 1, max = math.huge}) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function mcl_types.match_item(actual, expected)
|
||||
actual = actual or ItemStack()
|
||||
|
||||
if expected.item and actual:get_name() ~= expected.item then
|
||||
return false
|
||||
elseif expected.group and minetest.get_item_group(actual:get_name(), expected.group) == 0 then
|
||||
return false
|
||||
elseif expected.count and not mcl_types.match_bounds(actual:get_count(), expected.count) then
|
||||
return false
|
||||
elseif expected.wear and not mcl_types.match_bounds(actual:get_wear(), expected.wear) then
|
||||
return false
|
||||
elseif expected.enchantments and not mcl_types.match_enchantments(mcl_enchanting.get_enchantments(actual), expected.enchantments) then
|
||||
return false
|
||||
elseif expected.meta and not mcl_types.match_meta(actual:get_meta(), expected.meta) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_types.match_node(actual, expected, pos, meta)
|
||||
if expected.node and actual.name ~= expected.node then
|
||||
return false
|
||||
elseif expected.group and minetest.get_item_group(actual.name, expected.group) == 0 then
|
||||
return false
|
||||
elseif expected.param1 and actual.param1 ~= compare.param1 then
|
||||
return false
|
||||
elseif expected.param2 and actual.param2 ~= compare.param2 then
|
||||
return false
|
||||
elseif expected.meta and not mcl_types.match_meta(meta or minetest.get_meta(pos), expected.meta) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_types.match_pos(actual, expected, meta)
|
||||
if expected.pos and not mcl_types.match_vector(actual, expected.pos) then
|
||||
return false
|
||||
elseif expected.dimension and mcl_worlds.pos_to_dimension(actual) ~= expected.dimension then
|
||||
return false
|
||||
elseif expected.biome and minetest.get_biome_name(minetest.get_biome_data(actual).biome) ~= expected.biome then
|
||||
return false
|
||||
elseif expected.node and not mcl_types.match_node(minetest.get_node(actual), expected.node, actual, meta) then
|
||||
return false
|
||||
elseif expected.light and not mcl_types.match_bounds(minetest.get_node_light(actual), expected.light) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
name = mcl_types
|
||||
author = Fleckenstein
|
||||
description = Offers compare functions for many types, used for MineClone2 datapacks
|
||||
depends =
|
|
@ -0,0 +1,2 @@
|
|||
name = DATA
|
||||
description = Meta-modpack containing the datapack system for MineClone 2
|
|
@ -1,6 +1,6 @@
|
|||
local S = minetest.get_translator("mcl_boats")
|
||||
|
||||
local boat_visual_size = {x = 1, y = 1, z = 1}
|
||||
local boat_visual_size = {x = 3, y = 3, z = 3}
|
||||
local paddling_speed = 22
|
||||
local boat_y_offset = 0.35
|
||||
local boat_y_offset_ground = boat_y_offset + 0.6
|
||||
|
@ -12,7 +12,9 @@ local function is_group(pos, group)
|
|||
return minetest.get_item_group(nn, group) ~= 0
|
||||
end
|
||||
|
||||
local is_water = flowlib.is_water
|
||||
local function is_water(pos)
|
||||
return is_group(pos, "water")
|
||||
end
|
||||
|
||||
local function is_ice(pos)
|
||||
return is_group(pos, "ice")
|
||||
|
@ -245,7 +247,7 @@ function boat.on_step(self, dtime, moveresult)
|
|||
else
|
||||
local ctrl = self._passenger:get_player_control()
|
||||
if ctrl and ctrl.sneak then
|
||||
detach_object(self._passenger, true)
|
||||
detach_player(self._passenger, true)
|
||||
self._passenger = nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
name = mcl_boats
|
||||
author = PilzAdam
|
||||
description = Adds drivable boats.
|
||||
depends = mcl_player, flowlib
|
||||
depends = mcl_player
|
||||
optional_depends = mcl_core, doc_identifier
|
||||
|
||||
|
||||
|
|
Binary file not shown.
|
@ -1,8 +1,5 @@
|
|||
local S = minetest.get_translator("mcl_falling_nodes")
|
||||
local dmes = minetest.get_modpath("mcl_death_messages") ~= nil
|
||||
local has_mcl_armor = minetest.get_modpath("mcl_armor")
|
||||
|
||||
local is_creative_enabled = minetest.is_creative_enabled
|
||||
|
||||
local get_falling_depth = function(self)
|
||||
if not self._startpos then
|
||||
|
@ -16,8 +13,9 @@ local deal_falling_damage = function(self, dtime)
|
|||
if minetest.get_item_group(self.node.name, "falling_node_damage") == 0 then
|
||||
return
|
||||
end
|
||||
-- Cause damage to any entity it hits.
|
||||
-- Cause damage to any player it hits.
|
||||
-- Algorithm based on MC anvils.
|
||||
-- TODO: Support smashing other objects, too.
|
||||
local pos = self.object:get_pos()
|
||||
if not self._startpos then
|
||||
-- Fallback
|
||||
|
@ -25,39 +23,30 @@ local deal_falling_damage = function(self, dtime)
|
|||
end
|
||||
local objs = minetest.get_objects_inside_radius(pos, 1)
|
||||
for _,v in ipairs(objs) do
|
||||
if v:is_player() then
|
||||
local hp = v:get_hp()
|
||||
local hp = v:get_hp()
|
||||
if v:is_player() and hp ~= 0 then
|
||||
if not self._hit_players then
|
||||
self._hit_players = {}
|
||||
end
|
||||
local name = v:get_player_name()
|
||||
if hp ~= 0 then
|
||||
if not self._hit_players then
|
||||
self._hit_players = {}
|
||||
local hit = false
|
||||
for _,v in ipairs(self._hit_players) do
|
||||
if name == v then
|
||||
hit = true
|
||||
end
|
||||
local hit = false
|
||||
for _,v in ipairs(self._hit_players) do
|
||||
if name == v then
|
||||
hit = true
|
||||
end
|
||||
if not hit then
|
||||
table.insert(self._hit_players, name)
|
||||
local way = self._startpos.y - pos.y
|
||||
local damage = (way - 1) * 2
|
||||
damage = math.min(40, math.max(0, damage))
|
||||
if damage >= 1 then
|
||||
hp = hp - damage
|
||||
if hp < 0 then
|
||||
hp = 0
|
||||
end
|
||||
end
|
||||
if not hit then
|
||||
table.insert(self._hit_players, name)
|
||||
local way = self._startpos.y - pos.y
|
||||
local damage = (way - 1) * 2
|
||||
damage = math.min(40, math.max(0, damage))
|
||||
if damage >= 1 then
|
||||
hp = hp - damage
|
||||
if hp < 0 then
|
||||
hp = 0
|
||||
end
|
||||
-- Reduce damage if wearing a helmet
|
||||
local inv = v:get_inventory()
|
||||
local helmet = inv:get_stack("armor", 2)
|
||||
if has_mcl_armor and not helmet:is_empty() then
|
||||
hp = hp/4*3
|
||||
if not is_creative_enabled(name) then
|
||||
helmet:add_wear(65535/helmet:get_definition().groups.mcl_armor_uses) --TODO: be sure damage is exactly like mc (informations are missing in the mc wiki)
|
||||
inv:set_stack("armor", 2, helmet)
|
||||
end
|
||||
end
|
||||
if v:is_player() then
|
||||
-- TODO: Reduce damage if wearing a helmet
|
||||
local msg
|
||||
if minetest.get_item_group(self.node.name, "anvil") ~= 0 then
|
||||
msg = S("@1 was smashed by a falling anvil.", v:get_player_name())
|
||||
|
@ -67,35 +56,8 @@ local deal_falling_damage = function(self, dtime)
|
|||
if dmes then
|
||||
mcl_death_messages.player_damage(v, msg)
|
||||
end
|
||||
v:set_hp(hp, { type = "punch", from = "mod" })
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local hp = v:get_luaentity().health
|
||||
if hp and hp ~= 0 then
|
||||
if not self._hit_mobs then
|
||||
self._hit_mobs = {}
|
||||
end
|
||||
local hit = false
|
||||
for _,mob in ipairs(self._hit_mobs) do
|
||||
if v == mob then
|
||||
hit = true
|
||||
end
|
||||
end
|
||||
--TODO: reduce damage for mobs then they will be able to wear armor
|
||||
if not hit then
|
||||
table.insert(self._hit_mobs, v)
|
||||
local way = self._startpos.y - pos.y
|
||||
local damage = (way - 1) * 2
|
||||
damage = math.min(40, math.max(0, damage))
|
||||
if damage >= 1 then
|
||||
hp = hp - damage
|
||||
if hp < 0 then
|
||||
hp = 0
|
||||
end
|
||||
v:get_luaentity().health = hp
|
||||
end
|
||||
v:set_hp(hp, { type = "punch", from = "mod" })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,36 +1,10 @@
|
|||
--these are lua locals, used for higher performance
|
||||
local minetest,math,vector,ipairs = minetest,math,vector,ipairs
|
||||
|
||||
--this is used for the player pool in the sound buffer
|
||||
local pool = {}
|
||||
|
||||
local tick = false
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name
|
||||
name = player:get_player_name()
|
||||
pool[name] = 0
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name
|
||||
name = player:get_player_name()
|
||||
pool[name] = nil
|
||||
end)
|
||||
|
||||
|
||||
local has_awards = minetest.get_modpath("awards")
|
||||
|
||||
local mcl_item_entity = {}
|
||||
|
||||
--basic settings
|
||||
local item_drop_settings = {} --settings table
|
||||
item_drop_settings.dug_buffer = 0.65 -- the warm up period before a dug item can be collected
|
||||
item_drop_settings.age = 1.0 --how old a dropped item (_insta_collect==false) has to be before collecting
|
||||
item_drop_settings.radius_magnet = 2.0 --radius of item magnet. MUST BE LARGER THAN radius_collect!
|
||||
item_drop_settings.xp_radius_magnet = 7.25 --radius of xp magnet. MUST BE LARGER THAN radius_collect!
|
||||
item_drop_settings.radius_collect = 0.2 --radius of collection
|
||||
item_drop_settings.player_collect_height = 0.8 --added to their pos y value
|
||||
item_drop_settings.player_collect_height = 1.0 --added to their pos y value
|
||||
item_drop_settings.collection_safety = false --do this to prevent items from flying away on laggy servers
|
||||
item_drop_settings.random_item_velocity = true --this sets random item velocity if velocity is 0
|
||||
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
|
||||
|
@ -42,33 +16,16 @@ local get_gravity = function()
|
|||
return tonumber(minetest.settings:get("movement_gravity")) or 9.81
|
||||
end
|
||||
|
||||
local registered_pickup_achievement = {}
|
||||
|
||||
--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 registered_pickup_achievement[itemname] then
|
||||
minetest.log("error", "[mcl_item_entity] Trying to register already existing pickup achievement ["..award.."] for ["..itemname.."]")
|
||||
else
|
||||
registered_pickup_achievement[itemname] = award
|
||||
end
|
||||
end
|
||||
|
||||
mcl_item_entity.register_pickup_achievement("tree", "mcl:mineWood")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:blaze_rod", "mcl:blazeRod")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_mobitems:leather", "mcl:killCow")
|
||||
mcl_item_entity.register_pickup_achievement("mcl_core:diamond", "mcl:diamonds")
|
||||
|
||||
local check_pickup_achievements = function(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(registered_pickup_achievement) do
|
||||
if itemname == name or minetest.get_item_group(itemname, name) ~= 0 then
|
||||
awards.unlock(playername, award)
|
||||
end
|
||||
end
|
||||
local itemname = ItemStack(object:get_luaentity().itemstring):get_name()
|
||||
if minetest.get_item_group(itemname, "tree") ~= 0 then
|
||||
awards.unlock(player:get_player_name(), "mcl:mineWood")
|
||||
elseif itemname == "mcl_mobitems:blaze_rod" then
|
||||
awards.unlock(player:get_player_name(), "mcl:blazeRod")
|
||||
elseif itemname == "mcl_mobitems:leather" then
|
||||
awards.unlock(player:get_player_name(), "mcl:killCow")
|
||||
elseif itemname == "mcl_core:diamond" then
|
||||
awards.unlock(player:get_player_name(), "mcl:diamonds")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -96,71 +53,103 @@ local disable_physics = function(object, luaentity, ignore_check, reset_movement
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
|
||||
tick = not tick
|
||||
|
||||
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()
|
||||
|
||||
local pos = player:get_pos()
|
||||
|
||||
if tick == true and pool[name] > 0 then
|
||||
minetest.sound_play("item_drop_pickup", {
|
||||
pos = pos,
|
||||
gain = 0.7,
|
||||
max_hear_distance = 16,
|
||||
pitch = math.random(70,110)/100
|
||||
})
|
||||
if pool[name] > 6 then
|
||||
pool[name] = 6
|
||||
else
|
||||
pool[name] = pool[name] - 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local inv = player:get_inventory()
|
||||
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
|
||||
|
||||
object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
|
||||
local collected = false
|
||||
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
|
||||
if vector.distance(checkpos, object:get_pos()) <= item_drop_settings.radius_collect and not object:get_luaentity()._removed then
|
||||
-- Ignore if itemstring is not set yet
|
||||
if object:get_luaentity().itemstring ~= "" then
|
||||
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
|
||||
|
||||
minetest.sound_play("item_drop_pickup", {
|
||||
pos = pos,
|
||||
max_hear_distance = 16,
|
||||
gain = 1.0,
|
||||
}, true)
|
||||
check_pickup_achievements(object, player)
|
||||
|
||||
|
||||
-- Destroy entity
|
||||
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
|
||||
object:get_luaentity().target = checkpos
|
||||
object:get_luaentity()._removed = true
|
||||
|
||||
object:set_velocity({x=0,y=0,z=0})
|
||||
object:set_acceleration({x=0,y=0,z=0})
|
||||
|
||||
object:move_to(checkpos)
|
||||
|
||||
pool[name] = pool[name] + 1
|
||||
|
||||
minetest.after(0.25, function()
|
||||
--safety check
|
||||
if object and object:get_luaentity() then
|
||||
object:remove()
|
||||
end
|
||||
end)
|
||||
object:remove()
|
||||
collected = true
|
||||
end
|
||||
|
||||
-- Magnet
|
||||
else
|
||||
|
||||
object:get_luaentity()._magnet_active = true
|
||||
object:get_luaentity()._collector_timer = 0
|
||||
|
||||
-- Move object to player
|
||||
disable_physics(object, object:get_luaentity())
|
||||
|
||||
local opos = object:get_pos()
|
||||
local vec = vector.subtract(checkpos, opos)
|
||||
vec = vector.add(opos, vector.divide(vec, 2))
|
||||
object:move_to(vec)
|
||||
|
||||
|
||||
--fix eternally falling items
|
||||
minetest.after(0, function(object)
|
||||
local lua = object:get_luaentity()
|
||||
if lua then
|
||||
object:set_acceleration({x=0, y=0, z=0})
|
||||
end
|
||||
end, object)
|
||||
|
||||
|
||||
--this is a safety to prevent items flying away on laggy servers
|
||||
if item_drop_settings.collection_safety == true then
|
||||
if object:get_luaentity().init ~= true then
|
||||
object:get_luaentity().init = true
|
||||
minetest.after(1, function(args)
|
||||
local playername = args[1]
|
||||
local player = minetest.get_player_by_name(playername)
|
||||
local object = args[2]
|
||||
local lua = object:get_luaentity()
|
||||
if player == nil or not player:is_player() or object == nil or lua == nil or lua.itemstring == nil then
|
||||
return
|
||||
end
|
||||
if inv:room_for_item("main", ItemStack(object:get_luaentity().itemstring)) then
|
||||
inv:add_item("main", ItemStack(object:get_luaentity().itemstring))
|
||||
if not object:get_luaentity()._removed then
|
||||
minetest.sound_play("item_drop_pickup", {
|
||||
pos = pos,
|
||||
max_hear_distance = 16,
|
||||
gain = 1.0,
|
||||
}, true)
|
||||
end
|
||||
check_pickup_achievements(object, player)
|
||||
object:get_luaentity()._removed = true
|
||||
object:remove()
|
||||
else
|
||||
enable_physics(object, object:get_luaentity())
|
||||
end
|
||||
end, {player:get_player_name(), object})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not collected then
|
||||
if object:get_luaentity()._magnet_timer > 1 then
|
||||
object:get_luaentity()._magnet_timer = -item_drop_settings.magnet_time
|
||||
object:get_luaentity()._magnet_active = false
|
||||
elseif object:get_luaentity()._magnet_timer < 0 then
|
||||
object:get_luaentity()._magnet_timer = object:get_luaentity()._magnet_timer + dtime
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -176,6 +165,66 @@ minetest.register_globalstep(function(dtime)
|
|||
end
|
||||
end)
|
||||
|
||||
local minigroups = { "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
|
||||
local basegroups = { "pickaxey", "axey", "shovely" }
|
||||
local materials = { "wood", "gold", "stone", "iron", "diamond" }
|
||||
|
||||
-- Checks if the given node would drop its useful drop if dug by a tool
|
||||
-- with the given tool capabilities. Returns true if it will yield its useful
|
||||
-- drop, false otherwise.
|
||||
local check_can_drop = function(node_name, tool_capabilities)
|
||||
local handy = minetest.get_item_group(node_name, "handy")
|
||||
local dig_immediate = minetest.get_item_group(node_name, "dig_immediate")
|
||||
if handy == 1 or dig_immediate == 2 or dig_immediate == 3 then
|
||||
return true
|
||||
else
|
||||
local toolgroupcaps
|
||||
if tool_capabilities then
|
||||
toolgroupcaps = tool_capabilities.groupcaps
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
-- Compare node groups with tool capabilities
|
||||
for m=1, #minigroups do
|
||||
local minigroup = minigroups[m]
|
||||
local g = minetest.get_item_group(node_name, minigroup)
|
||||
if g ~= 0 then
|
||||
local plus = minigroup .. "_dig"
|
||||
if toolgroupcaps[plus] then
|
||||
return true
|
||||
end
|
||||
for e=1,5 do
|
||||
local effplus = plus .. "_efficiency_" .. e
|
||||
if toolgroupcaps[effplus] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for b=1, #basegroups do
|
||||
local basegroup = basegroups[b]
|
||||
local g = minetest.get_item_group(node_name, basegroup)
|
||||
if g ~= 0 then
|
||||
for m=g, #materials do
|
||||
local plus = basegroup .. "_dig_"..materials[m]
|
||||
if toolgroupcaps[plus] then
|
||||
return true
|
||||
end
|
||||
for e=1,5 do
|
||||
local effplus = plus .. "_efficiency_" .. e
|
||||
if toolgroupcaps[effplus] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Stupid workaround to get drops from a drop table:
|
||||
-- Create a temporary table in minetest.registered_nodes that contains the proper drops,
|
||||
-- because unfortunately minetest.get_node_drops needs the drop table to be inside a registered node definition
|
||||
|
@ -220,33 +269,29 @@ local function get_fortune_drops(fortune_drops, fortune_level)
|
|||
return drop or {}
|
||||
end
|
||||
|
||||
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true)
|
||||
|
||||
function minetest.handle_node_drops(pos, drops, digger)
|
||||
-- NOTE: This function override allows digger to be nil.
|
||||
-- This means there is no digger. This is a special case which allows this function to be called
|
||||
-- by hand. Creative Mode is intentionally ignored in this case.
|
||||
|
||||
local doTileDrops = minetest.settings:get_bool("mcl_doTileDrops", true)
|
||||
if (digger and digger:is_player() and minetest.is_creative_enabled(digger:get_player_name())) or doTileDrops == false then
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if node will yield its useful drop by the digger's tool
|
||||
local dug_node = minetest.get_node(pos)
|
||||
local tooldef
|
||||
local toolcaps
|
||||
local tool
|
||||
if digger ~= nil then
|
||||
tool = digger:get_wielded_item()
|
||||
tooldef = minetest.registered_tools[tool:get_name()]
|
||||
toolcaps = tool:get_tool_capabilities()
|
||||
|
||||
if not mcl_autogroup.can_harvest(dug_node.name, tool:get_name()) then
|
||||
if not check_can_drop(dug_node.name, toolcaps) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local diggroups = tooldef and tooldef._mcl_diggroups
|
||||
local shearsy_level = diggroups and diggroups.shearsy and diggroups.shearsy.level
|
||||
|
||||
--[[ Special node drops when dug by shears by reading _mcl_shears_drop or with a silk touch tool reading _mcl_silk_touch_drop
|
||||
from the node definition.
|
||||
Definition of _mcl_shears_drop / _mcl_silk_touch_drop:
|
||||
|
@ -258,7 +303,7 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
|
||||
local silk_touch_drop = false
|
||||
local nodedef = minetest.registered_nodes[dug_node.name]
|
||||
if shearsy_level and shearsy_level > 0 and nodedef._mcl_shears_drop then
|
||||
if toolcaps ~= nil and toolcaps.groupcaps and toolcaps.groupcaps.shearsy_dig and nodedef._mcl_shears_drop then
|
||||
if nodedef._mcl_shears_drop == true then
|
||||
drops = { dug_node.name }
|
||||
else
|
||||
|
@ -326,10 +371,6 @@ function minetest.handle_node_drops(pos, drops, digger)
|
|||
z = -z
|
||||
end
|
||||
obj:set_velocity({x=1/x, y=obj:get_velocity().y, z=1/z})
|
||||
|
||||
obj:get_luaentity().age = item_drop_settings.dug_buffer
|
||||
|
||||
obj:get_luaentity()._insta_collect = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -396,9 +437,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
-- Number of seconds this item entity has existed so far
|
||||
age = 0,
|
||||
|
||||
-- How old it has become in the collection animation
|
||||
collection_age = 0,
|
||||
|
||||
set_item = function(self, itemstring)
|
||||
self.itemstring = itemstring
|
||||
if self.itemstring == "" then
|
||||
|
@ -564,11 +602,6 @@ minetest.register_entity(":__builtin:item", {
|
|||
|
||||
on_step = function(self, dtime)
|
||||
if self._removed then
|
||||
self.object:set_properties({
|
||||
physical = false
|
||||
})
|
||||
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
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Item_Drop_Pickup - https://freesound.org/people/benniknop/sounds/317848/ (License: CC0)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,7 +1,5 @@
|
|||
local S = minetest.get_translator("mcl_minecarts")
|
||||
|
||||
local has_mcl_wip = minetest.get_modpath("mcl_wip")
|
||||
|
||||
mcl_minecarts = {}
|
||||
mcl_minecarts.modpath = minetest.get_modpath("mcl_minecarts")
|
||||
mcl_minecarts.speed_max = 10
|
||||
|
@ -664,6 +662,8 @@ register_minecart(
|
|||
"mcl_minecarts_minecart_chest.png",
|
||||
{"mcl_minecarts:minecart", "mcl_chests:chest"},
|
||||
nil, nil, false)
|
||||
|
||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||
|
||||
-- Minecart with Furnace
|
||||
register_minecart(
|
||||
|
@ -719,6 +719,8 @@ register_minecart(
|
|||
end, nil, false
|
||||
)
|
||||
|
||||
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
||||
|
||||
-- Minecart with Command Block
|
||||
register_minecart(
|
||||
"mcl_minecarts:command_block_minecart",
|
||||
|
@ -740,6 +742,8 @@ register_minecart(
|
|||
nil, nil, false
|
||||
)
|
||||
|
||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||
|
||||
-- Minecart with Hopper
|
||||
register_minecart(
|
||||
"mcl_minecarts:hopper_minecart",
|
||||
|
@ -758,6 +762,8 @@ register_minecart(
|
|||
nil, nil, false
|
||||
)
|
||||
|
||||
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
||||
|
||||
-- Minecart with TNT
|
||||
register_minecart(
|
||||
"mcl_minecarts:tnt_minecart",
|
||||
|
@ -818,34 +824,29 @@ minetest.register_craft({
|
|||
|
||||
-- TODO: Re-enable crafting of special minecarts when they have been implemented
|
||||
if false then
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:furnace_minecart",
|
||||
recipe = {
|
||||
{"mcl_furnaces:furnace"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:hopper_minecart",
|
||||
recipe = {
|
||||
{"mcl_hoppers:hopper"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:furnace_minecart",
|
||||
recipe = {
|
||||
{"mcl_furnaces:furnace"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:hopper_minecart",
|
||||
recipe = {
|
||||
{"mcl_hoppers:hopper"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:chest_minecart",
|
||||
recipe = {
|
||||
{"mcl_chests:chest"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_minecarts:chest_minecart",
|
||||
recipe = {
|
||||
{"mcl_chests:chest"},
|
||||
{"mcl_minecarts:minecart"},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
if has_mcl_wip then
|
||||
mcl_wip.register_wip_item("mcl_minecarts:chest_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:furnace_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:command_block_minecart")
|
||||
mcl_wip.register_wip_item("mcl_minecarts:hopper_minecart")
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
name = mcl_minecarts
|
||||
author = Krock
|
||||
description = Minecarts are vehicles to move players quickly on rails.
|
||||
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
||||
optional_depends = doc_identifier, mcl_wip
|
||||
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons, mcl_wip
|
||||
optional_depends = doc_identifier
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ end
|
|||
|
||||
-- Load settings
|
||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||
|
||||
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
||||
local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||
|
@ -82,6 +84,11 @@ if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
|||
end)
|
||||
end
|
||||
|
||||
-- calculate aoc range for mob count
|
||||
local aosrb = tonumber(minetest.settings:get("active_object_send_range_blocks"))
|
||||
local abr = tonumber(minetest.settings:get("active_block_range"))
|
||||
local aoc_range = max(aosrb, abr) * 16
|
||||
|
||||
-- pathfinding settings
|
||||
local enable_pathfinding = true
|
||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||
|
@ -276,33 +283,6 @@ local get_velocity = function(self)
|
|||
return 0
|
||||
end
|
||||
|
||||
local function update_roll(self)
|
||||
local is_Fleckenstein = self.nametag == "Fleckenstein"
|
||||
local was_Fleckenstein = false
|
||||
|
||||
local rot = self.object:get_rotation()
|
||||
rot.z = is_Fleckenstein and pi or 0
|
||||
self.object:set_rotation(rot)
|
||||
|
||||
local cbox = table.copy(self.collisionbox)
|
||||
local acbox = self.object:get_properties().collisionbox
|
||||
|
||||
if math.abs(cbox[2] - acbox[2]) > 0.1 then
|
||||
was_Fleckenstein = true
|
||||
end
|
||||
|
||||
if is_Fleckenstein ~= was_Fleckenstein then
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + (acbox[2] + acbox[5])
|
||||
self.object:set_pos(pos)
|
||||
end
|
||||
|
||||
if is_Fleckenstein then
|
||||
cbox[2], cbox[5] = -cbox[5], -cbox[2]
|
||||
end
|
||||
|
||||
self.object:set_properties({collisionbox = cbox})
|
||||
end
|
||||
|
||||
-- set and return valid yaw
|
||||
local set_yaw = function(self, yaw, delay, dtime)
|
||||
|
@ -318,7 +298,6 @@ local set_yaw = function(self, yaw, delay, dtime)
|
|||
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
||||
end
|
||||
self.object:set_yaw(yaw)
|
||||
update_roll(self)
|
||||
return yaw
|
||||
end
|
||||
|
||||
|
@ -666,9 +645,9 @@ local update_tag = function(self)
|
|||
nametag = tag,
|
||||
})
|
||||
|
||||
update_roll(self)
|
||||
end
|
||||
|
||||
|
||||
-- drop items
|
||||
local item_drop = function(self, cooked, looting_level)
|
||||
|
||||
|
@ -728,9 +707,7 @@ local item_drop = function(self, cooked, looting_level)
|
|||
end
|
||||
|
||||
-- add item if it exists
|
||||
for x = 1, num do
|
||||
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
||||
end
|
||||
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
|
||||
|
||||
if obj and obj:get_luaentity() then
|
||||
|
||||
|
@ -859,12 +836,10 @@ local check_for_death = function(self, cause, cmi_cause)
|
|||
remove_texture_mod(self, "^[colorize:#FF000040")
|
||||
remove_texture_mod(self, "^[brighten")
|
||||
self.passive = true
|
||||
|
||||
self.object:set_properties({
|
||||
pointable = false,
|
||||
collide_with_objects = false,
|
||||
})
|
||||
|
||||
set_velocity(self, 0)
|
||||
local acc = self.object:get_acceleration()
|
||||
acc.x, acc.y, acc.z = 0, DEFAULT_FALL_SPEED, 0
|
||||
|
@ -2814,10 +2789,6 @@ local do_states = function(self, dtime)
|
|||
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
|
||||
|
@ -2855,7 +2826,7 @@ local falling = function(self, pos)
|
|||
end
|
||||
|
||||
if mcl_portals ~= nil then
|
||||
if mcl_portals.nether_portal_cooloff(self.object) then
|
||||
if mcl_portals.nether_portal_cooloff[self.object] then
|
||||
return false -- mob has teleported through Nether portal - it's 99% not falling
|
||||
end
|
||||
end
|
||||
|
@ -2885,18 +2856,6 @@ local falling = function(self, pos)
|
|||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
end
|
||||
|
||||
if minetest.registered_nodes[node_ok(pos).name].groups.lava then
|
||||
|
||||
if self.floats_on_lava == 1 then
|
||||
|
||||
self.object:set_acceleration({
|
||||
x = 0,
|
||||
y = -self.fall_speed / (max(1, v.y) ^ 2),
|
||||
z = 0
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- in water then float up
|
||||
if minetest.registered_nodes[node_ok(pos).name].groups.water then
|
||||
|
||||
|
@ -3516,11 +3475,18 @@ local mob_step = function(self, dtime)
|
|||
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
||||
end
|
||||
self.object:set_yaw(yaw)
|
||||
update_roll(self)
|
||||
end
|
||||
|
||||
-- end rotation
|
||||
|
||||
-- knockback timer
|
||||
if self.pause_timer > 0 then
|
||||
|
||||
self.pause_timer = self.pause_timer - dtime
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- run custom function (defined in mob lua file)
|
||||
if self.do_custom then
|
||||
|
||||
|
@ -3530,14 +3496,6 @@ local mob_step = function(self, dtime)
|
|||
end
|
||||
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
|
||||
|
||||
|
@ -3556,7 +3514,7 @@ local mob_step = function(self, dtime)
|
|||
end
|
||||
|
||||
-- mob plays random sound at times
|
||||
if random(1, 70) == 1 then
|
||||
if random(1, 100) == 1 then
|
||||
mob_sound(self, "random", true)
|
||||
end
|
||||
|
||||
|
@ -3736,8 +3694,6 @@ function mobs:register_mob(name, def)
|
|||
local can_despawn
|
||||
if def.can_despawn ~= nil then
|
||||
can_despawn = def.can_despawn
|
||||
elseif def.spawn_class == "passive" then
|
||||
can_despawn = false
|
||||
else
|
||||
can_despawn = true
|
||||
end
|
||||
|
@ -3817,7 +3773,6 @@ minetest.register_entity(name, {
|
|||
knock_back = def.knock_back ~= false,
|
||||
shoot_offset = def.shoot_offset or 0,
|
||||
floats = def.floats or 1, -- floats in water by default
|
||||
floats_on_lava = def.floats_on_lava or 0,
|
||||
replace_rate = def.replace_rate,
|
||||
replace_what = def.replace_what,
|
||||
replace_with = def.replace_with,
|
||||
|
@ -3901,12 +3856,6 @@ minetest.register_entity(name, {
|
|||
on_detach_child = mob_detach_child,
|
||||
|
||||
on_activate = function(self, staticdata, dtime)
|
||||
--this is a temporary hack so mobs stop
|
||||
--glitching and acting really weird with the
|
||||
--default built in engine collision detection
|
||||
self.object:set_properties({
|
||||
collide_with_objects = false,
|
||||
})
|
||||
return mob_activate(self, staticdata, def, dtime)
|
||||
end,
|
||||
|
||||
|
@ -3925,6 +3874,289 @@ end
|
|||
end -- END mobs:register_mob function
|
||||
|
||||
|
||||
-- count how many mobs of one type are inside an area
|
||||
local count_mobs = function(pos, mobtype)
|
||||
|
||||
local num = 0
|
||||
local objs = minetest.get_objects_inside_radius(pos, aoc_range)
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
local obj = objs[n]:get_luaentity()
|
||||
|
||||
if obj and obj.name and obj._cmi_is_mob then
|
||||
|
||||
-- count passive mobs only
|
||||
if mobtype == "!passive" then
|
||||
if obj.spawn_class == "passive" then
|
||||
num = num + 1
|
||||
end
|
||||
-- count hostile mobs only
|
||||
elseif mobtype == "!hostile" then
|
||||
if obj.spawn_class == "hostile" then
|
||||
num = num + 1
|
||||
end
|
||||
-- count ambient mobs only
|
||||
elseif mobtype == "!ambient" then
|
||||
if obj.spawn_class == "ambient" then
|
||||
num = num + 1
|
||||
end
|
||||
-- count water mobs only
|
||||
elseif mobtype == "!water" then
|
||||
if obj.spawn_class == "water" then
|
||||
num = num + 1
|
||||
end
|
||||
-- count mob type
|
||||
elseif mobtype and obj.name == mobtype then
|
||||
num = num + 1
|
||||
-- count total mobs
|
||||
elseif not mobtype then
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return num
|
||||
end
|
||||
|
||||
|
||||
-- global functions
|
||||
|
||||
function mobs:spawn_abm_check(pos, node, name)
|
||||
-- global function to add additional spawn checks
|
||||
-- return true to stop spawning mob
|
||||
end
|
||||
|
||||
|
||||
function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
|
||||
interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||
|
||||
-- Do mobs spawn at all?
|
||||
if not mobs_spawn then
|
||||
return
|
||||
end
|
||||
|
||||
-- chance/spawn number override in minetest.conf for registered mob
|
||||
local numbers = minetest.settings:get(name)
|
||||
|
||||
if numbers then
|
||||
numbers = numbers:split(",")
|
||||
chance = tonumber(numbers[1]) or chance
|
||||
aoc = tonumber(numbers[2]) or aoc
|
||||
|
||||
if chance == 0 then
|
||||
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log("action",
|
||||
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||
|
||||
end
|
||||
|
||||
local spawn_action
|
||||
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
|
||||
|
||||
local orig_pos = table.copy(pos)
|
||||
-- is mob actually registered?
|
||||
if not mobs.spawning_mobs[name]
|
||||
or not minetest.registered_entities[name] then
|
||||
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
||||
return
|
||||
end
|
||||
|
||||
-- additional custom checks for spawning mob
|
||||
if mobs:spawn_abm_check(pos, node, name) == true then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
||||
return
|
||||
end
|
||||
|
||||
-- count nearby mobs in same spawn class
|
||||
local entdef = minetest.registered_entities[name]
|
||||
local spawn_class = entdef and entdef.spawn_class
|
||||
if not spawn_class then
|
||||
if entdef.type == "monster" then
|
||||
spawn_class = "hostile"
|
||||
else
|
||||
spawn_class = "passive"
|
||||
end
|
||||
end
|
||||
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
||||
-- do not spawn if too many of same mob in area
|
||||
if active_object_count_wider >= max_per_block -- large-range mob cap
|
||||
or (not in_class_cap) -- spawn class mob cap
|
||||
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
||||
-- too many entities
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
||||
return
|
||||
end
|
||||
|
||||
-- if toggle set to nil then ignore day/night check
|
||||
if day_toggle ~= nil then
|
||||
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
|
||||
if tod > 4500 and tod < 19500 then
|
||||
-- daylight, but mob wants night
|
||||
if day_toggle == false then
|
||||
-- mob needs night
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
||||
return
|
||||
end
|
||||
else
|
||||
-- night time but mob wants day
|
||||
if day_toggle == true then
|
||||
-- mob needs day
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn above node
|
||||
pos.y = pos.y + 1
|
||||
|
||||
-- only spawn away from player
|
||||
local objs = minetest.get_objects_inside_radius(pos, 24)
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
if objs[n]:is_player() then
|
||||
-- player too close
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- mobs cannot spawn in protected areas when enabled
|
||||
if not spawn_protected
|
||||
and minetest.is_protected(pos, "") then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
||||
return
|
||||
end
|
||||
|
||||
-- are we spawning within height limits?
|
||||
if pos.y > max_height
|
||||
or pos.y < min_height then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
||||
return
|
||||
end
|
||||
|
||||
-- are light levels ok?
|
||||
local light = minetest.get_node_light(pos)
|
||||
if not light
|
||||
or light > max_light
|
||||
or light < min_light then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
||||
return
|
||||
end
|
||||
|
||||
-- do we have enough space to spawn mob?
|
||||
local ent = minetest.registered_entities[name]
|
||||
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
||||
local min_x, max_x
|
||||
if width_x % 2 == 0 then
|
||||
max_x = math.floor(width_x/2)
|
||||
min_x = -(max_x-1)
|
||||
else
|
||||
max_x = math.floor(width_x/2)
|
||||
min_x = -max_x
|
||||
end
|
||||
|
||||
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
||||
local min_z, max_z
|
||||
if width_z % 2 == 0 then
|
||||
max_z = math.floor(width_z/2)
|
||||
min_z = -(max_z-1)
|
||||
else
|
||||
max_z = math.floor(width_z/2)
|
||||
min_z = -max_z
|
||||
end
|
||||
|
||||
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
||||
|
||||
for y = 0, max_y do
|
||||
for x = min_x, max_x do
|
||||
for z = min_z, max_z do
|
||||
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
||||
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
||||
-- inside block
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
||||
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
||||
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
||||
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- tweak X/Y/Z spawn pos
|
||||
if width_x % 2 == 0 then
|
||||
pos.x = pos.x + 0.5
|
||||
end
|
||||
if width_z % 2 == 0 then
|
||||
pos.z = pos.z + 0.5
|
||||
end
|
||||
pos.y = pos.y - 0.5
|
||||
|
||||
local mob = minetest.add_entity(pos, name)
|
||||
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
||||
|
||||
if on_spawn then
|
||||
|
||||
local ent = mob:get_luaentity()
|
||||
|
||||
on_spawn(ent, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
||||
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
label = name .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = floor(max(1, chance * mobs_spawn_chance)),
|
||||
catch_up = false,
|
||||
action = spawn_abm_action,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- compatibility with older mob registration
|
||||
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
||||
|
||||
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
||||
chance, active_object_count, -31000, max_height, day_toggle)
|
||||
end
|
||||
|
||||
|
||||
-- MarkBu's spawn function
|
||||
function mobs:spawn(def)
|
||||
|
||||
local name = def.name
|
||||
local nodes = def.nodes or {"group:soil", "group:stone"}
|
||||
local neighbors = def.neighbors or {"air"}
|
||||
local min_light = def.min_light or 0
|
||||
local max_light = def.max_light or 15
|
||||
local interval = def.interval or 30
|
||||
local chance = def.chance or 5000
|
||||
local active_object_count = def.active_object_count or 1
|
||||
local min_height = def.min_height or -31000
|
||||
local max_height = def.max_height or 31000
|
||||
local day_toggle = def.day_toggle
|
||||
local on_spawn = def.on_spawn
|
||||
|
||||
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
||||
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
||||
end
|
||||
|
||||
|
||||
-- register arrow for shoot attack
|
||||
function mobs:register_arrow(name, def)
|
||||
|
||||
|
@ -3947,11 +4179,6 @@ function mobs:register_arrow(name, def)
|
|||
switch = 0,
|
||||
owner_id = def.owner_id,
|
||||
rotate = def.rotate,
|
||||
on_punch = function(self)
|
||||
local vel = self.object:get_velocity()
|
||||
self.object:set_velocity({x=vel.x * -1, y=vel.y * -1, z=vel.z * -1})
|
||||
end,
|
||||
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
||||
automatic_face_movement_dir = def.rotate
|
||||
and (def.rotate - (pi / 180)) or false,
|
||||
|
||||
|
@ -4014,7 +4241,7 @@ function mobs:register_arrow(name, def)
|
|||
|
||||
if self.hit_player or self.hit_mob or self.hit_object then
|
||||
|
||||
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
||||
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do
|
||||
|
||||
if self.hit_player
|
||||
and player:is_player() then
|
||||
|
@ -4069,7 +4296,7 @@ end
|
|||
|
||||
-- make explosion with protection and tnt mod check
|
||||
function mobs:boom(self, pos, strength, fire)
|
||||
self.object:remove()
|
||||
|
||||
if mod_explosions then
|
||||
if mobs_griefing and not minetest.is_protected(pos, "") then
|
||||
mcl_explosions.explode(pos, strength, { drop_chance = 1.0, fire = fire }, self.object)
|
||||
|
@ -4350,7 +4577,6 @@ function mobs:alias_mob(old_name, new_name)
|
|||
|
||||
end
|
||||
|
||||
--[[
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
|
@ -4367,4 +4593,3 @@ minetest.register_globalstep(function(dtime)
|
|||
end
|
||||
timer = 0
|
||||
end)
|
||||
]]--
|
|
@ -4,11 +4,8 @@ local path = minetest.get_modpath(minetest.get_current_modname())
|
|||
-- Mob API
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
-- Spawning Algorithm
|
||||
dofile(path .. "/spawning.lua")
|
||||
|
||||
-- Rideable Mobs
|
||||
dofile(path .. "/mount.lua")
|
||||
|
||||
-- Mob Items
|
||||
dofile(path .. "/crafts.lua")
|
||||
dofile(path .. "/crafts.lua")
|
||||
|
|
|
@ -168,20 +168,16 @@ function mobs.detach(player, offset)
|
|||
|
||||
mcl_player.player_set_animation(player, "stand" , 30)
|
||||
|
||||
--local pos = player:get_pos()
|
||||
local pos = player:get_pos()
|
||||
|
||||
--pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
|
||||
pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
|
||||
|
||||
player:add_velocity(vector.new(math.random(-6,6),math.random(5,8),math.random(-6,6))) --throw the rider off
|
||||
|
||||
--[[
|
||||
minetest.after(0.1, function(name, pos)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
player:set_pos(pos)
|
||||
end
|
||||
end, player:get_player_name(), pos)
|
||||
]]--
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -1,648 +0,0 @@
|
|||
--lua locals
|
||||
local get_node = minetest.get_node
|
||||
local get_item_group = minetest.get_item_group
|
||||
local get_node_light = minetest.get_node_light
|
||||
local find_nodes_in_area_under_air = minetest.find_nodes_in_area_under_air
|
||||
local new_vector = vector.new
|
||||
local math_random = math.random
|
||||
local get_biome_name = minetest.get_biome_name
|
||||
local max = math.max
|
||||
local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||
local vector_distance = vector.distance
|
||||
|
||||
-- range for mob count
|
||||
local aoc_range = 32
|
||||
--[[
|
||||
|
||||
THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
|
||||
|
||||
underground:
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",local spawning_position = spawning_position_list[math.random(1,#spawning_position_list)]
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
|
||||
ocean:
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
|
||||
water or beach?
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
|
||||
beach:
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
|
||||
dimension biome:
|
||||
"Nether",
|
||||
"End",
|
||||
|
||||
Overworld regular:
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
]]--
|
||||
|
||||
|
||||
|
||||
|
||||
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
||||
-- count how many mobs of one type are inside an area
|
||||
|
||||
local count_mobs = function(pos,mobtype)
|
||||
print(mobtype)
|
||||
local num = 0
|
||||
local objs = get_objects_inside_radius(pos, aoc_range)
|
||||
for n = 1, #objs do
|
||||
local obj = objs[n]:get_luaentity()
|
||||
if obj and obj.name and obj._cmi_is_mob then
|
||||
-- count hostile mobs only
|
||||
if mobtype == "hostile" then
|
||||
if obj.spawn_class == "hostile" then
|
||||
num = num + 1
|
||||
end
|
||||
-- count passive mobs only
|
||||
else
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return num
|
||||
end
|
||||
|
||||
|
||||
-- global functions
|
||||
|
||||
function mobs:spawn_abm_check(pos, node, name)
|
||||
-- global function to add additional spawn checks
|
||||
-- return true to stop spawning mob
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Custom elements changed:
|
||||
|
||||
name:
|
||||
the mobs name
|
||||
|
||||
dimension:
|
||||
"overworld"
|
||||
"nether"
|
||||
"end"
|
||||
|
||||
types of spawning:
|
||||
"water"
|
||||
"ground"
|
||||
"lava"
|
||||
|
||||
biomes: tells the spawner to allow certain mobs to spawn in certain biomes
|
||||
{"this", "that", "grasslands", "whatever"}
|
||||
|
||||
|
||||
what is aoc??? objects in area
|
||||
|
||||
WARNING: BIOME INTEGRATION NEEDED -> How to get biome through lua??
|
||||
]]--
|
||||
|
||||
|
||||
--this is where all of the spawning information is kept
|
||||
local spawn_dictionary = {}
|
||||
|
||||
function mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
|
||||
|
||||
--print(dump(biomes))
|
||||
|
||||
-- Do mobs spawn at all?
|
||||
if not mobs_spawn then
|
||||
return
|
||||
end
|
||||
|
||||
-- chance/spawn number override in minetest.conf for registered mob
|
||||
local numbers = minetest.settings:get(name)
|
||||
|
||||
if numbers then
|
||||
numbers = numbers:split(",")
|
||||
chance = tonumber(numbers[1]) or chance
|
||||
aoc = tonumber(numbers[2]) or aoc
|
||||
|
||||
if chance == 0 then
|
||||
minetest.log("warning", string.format("[mobs] %s has spawning disabled", name))
|
||||
return
|
||||
end
|
||||
|
||||
minetest.log("action",
|
||||
string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc))
|
||||
end
|
||||
|
||||
--[[
|
||||
local spawn_action
|
||||
spawn_action = function(pos, node, active_object_count, active_object_count_wider, name)
|
||||
|
||||
local orig_pos = table.copy(pos)
|
||||
-- is mob actually registered?
|
||||
if not mobs.spawning_mobs[name]
|
||||
or not minetest.registered_entities[name] then
|
||||
minetest.log("warning", "Mob spawn of "..name.." failed, unknown entity or mob is not registered for spawning!")
|
||||
return
|
||||
end
|
||||
|
||||
-- additional custom checks for spawning mob
|
||||
if mobs:spawn_abm_check(pos, node, name) == true then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, ABM check rejected!")
|
||||
return
|
||||
end
|
||||
|
||||
-- count nearby mobs in same spawn class
|
||||
local entdef = minetest.registered_entities[name]
|
||||
local spawn_class = entdef and entdef.spawn_class
|
||||
if not spawn_class then
|
||||
if entdef.type == "monster" then
|
||||
spawn_class = "hostile"
|
||||
else
|
||||
spawn_class = "passive"
|
||||
end
|
||||
end
|
||||
local in_class_cap = count_mobs(pos, "!"..spawn_class) < MOB_CAP[spawn_class]
|
||||
-- do not spawn if too many of same mob in area
|
||||
if active_object_count_wider >= max_per_block -- large-range mob cap
|
||||
or (not in_class_cap) -- spawn class mob cap
|
||||
or count_mobs(pos, name) >= aoc then -- per-mob mob cap
|
||||
-- too many entities
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too crowded!")
|
||||
return
|
||||
end
|
||||
|
||||
-- if toggle set to nil then ignore day/night check
|
||||
if day_toggle ~= nil then
|
||||
|
||||
local tod = (minetest.get_timeofday() or 0) * 24000
|
||||
|
||||
if tod > 4500 and tod < 19500 then
|
||||
-- daylight, but mob wants night
|
||||
if day_toggle == false then
|
||||
-- mob needs night
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs light!")
|
||||
return
|
||||
end
|
||||
else
|
||||
-- night time but mob wants day
|
||||
if day_toggle == true then
|
||||
-- mob needs day
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, mob needs daylight!")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- spawn above node
|
||||
pos.y = pos.y + 1
|
||||
|
||||
-- only spawn away from player
|
||||
local objs = minetest.get_objects_inside_radius(pos, 24)
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
if objs[n]:is_player() then
|
||||
-- player too close
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, player too close!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- mobs cannot spawn in protected areas when enabled
|
||||
if not spawn_protected
|
||||
and minetest.is_protected(pos, "") then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, position is protected!")
|
||||
return
|
||||
end
|
||||
|
||||
-- are we spawning within height limits?
|
||||
if pos.y > max_height
|
||||
or pos.y < min_height then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, out of height limit!")
|
||||
return
|
||||
end
|
||||
|
||||
-- are light levels ok?
|
||||
local light = minetest.get_node_light(pos)
|
||||
if not light
|
||||
or light > max_light
|
||||
or light < min_light then
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, bad light!")
|
||||
return
|
||||
end
|
||||
|
||||
-- do we have enough space to spawn mob?
|
||||
local ent = minetest.registered_entities[name]
|
||||
local width_x = max(1, math.ceil(ent.collisionbox[4] - ent.collisionbox[1]))
|
||||
local min_x, max_x
|
||||
if width_x % 2 == 0 then
|
||||
max_x = math.floor(width_x/2)
|
||||
min_x = -(max_x-1)
|
||||
else
|
||||
max_x = math.floor(width_x/2)
|
||||
min_x = -max_x
|
||||
end
|
||||
|
||||
local width_z = max(1, math.ceil(ent.collisionbox[6] - ent.collisionbox[3]))
|
||||
local min_z, max_z
|
||||
if width_z % 2 == 0 then
|
||||
max_z = math.floor(width_z/2)
|
||||
min_z = -(max_z-1)
|
||||
else
|
||||
max_z = math.floor(width_z/2)
|
||||
min_z = -max_z
|
||||
end
|
||||
|
||||
local max_y = max(0, math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1)
|
||||
|
||||
for y = 0, max_y do
|
||||
for x = min_x, max_x do
|
||||
for z = min_z, max_z do
|
||||
local pos2 = {x = pos.x+x, y = pos.y+y, z = pos.z+z}
|
||||
if minetest.registered_nodes[node_ok(pos2).name].walkable == true then
|
||||
-- inside block
|
||||
minetest.log("info", "Mob spawn of "..name.." at "..minetest.pos_to_string(pos).." failed, too little space!")
|
||||
if ent.spawn_small_alternative ~= nil and (not minetest.registered_nodes[node_ok(pos).name].walkable) then
|
||||
minetest.log("info", "Trying to spawn smaller alternative mob: "..ent.spawn_small_alternative)
|
||||
spawn_action(orig_pos, node, active_object_count, active_object_count_wider, ent.spawn_small_alternative)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- tweak X/Y/Z spawn pos
|
||||
if width_x % 2 == 0 then
|
||||
pos.x = pos.x + 0.5
|
||||
end
|
||||
if width_z % 2 == 0 then
|
||||
pos.z = pos.z + 0.5
|
||||
end
|
||||
pos.y = pos.y - 0.5
|
||||
|
||||
local mob = minetest.add_entity(pos, name)
|
||||
minetest.log("action", "Mob spawned: "..name.." at "..minetest.pos_to_string(pos))
|
||||
|
||||
if on_spawn then
|
||||
|
||||
local ent = mob:get_luaentity()
|
||||
|
||||
on_spawn(ent, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function spawn_abm_action(pos, node, active_object_count, active_object_count_wider)
|
||||
spawn_action(pos, node, active_object_count, active_object_count_wider, name)
|
||||
end
|
||||
]]--
|
||||
|
||||
local entdef = minetest.registered_entities[name]
|
||||
local spawn_class
|
||||
if entdef.type == "monster" then
|
||||
spawn_class = "hostile"
|
||||
else
|
||||
spawn_class = "passive"
|
||||
end
|
||||
|
||||
--load information into the spawn dictionary
|
||||
local key = #spawn_dictionary + 1
|
||||
spawn_dictionary[key] = {}
|
||||
spawn_dictionary[key]["name"] = name
|
||||
spawn_dictionary[key]["dimension"] = dimension
|
||||
spawn_dictionary[key]["type_of_spawning"] = type_of_spawning
|
||||
spawn_dictionary[key]["biomes"] = biomes
|
||||
spawn_dictionary[key]["min_light"] = min_light
|
||||
spawn_dictionary[key]["max_light"] = max_light
|
||||
spawn_dictionary[key]["interval"] = interval
|
||||
spawn_dictionary[key]["chance"] = chance
|
||||
spawn_dictionary[key]["aoc"] = aoc
|
||||
spawn_dictionary[key]["min_height"] = min_height
|
||||
spawn_dictionary[key]["max_height"] = max_height
|
||||
spawn_dictionary[key]["day_toggle"] = day_toggle
|
||||
--spawn_dictionary[key]["on_spawn"] = spawn_abm_action
|
||||
spawn_dictionary[key]["spawn_class"] = spawn_class
|
||||
|
||||
--[[
|
||||
minetest.register_abm({
|
||||
label = name .. " spawning",
|
||||
nodenames = nodes,
|
||||
neighbors = neighbors,
|
||||
interval = interval,
|
||||
chance = floor(max(1, chance * mobs_spawn_chance)),
|
||||
catch_up = false,
|
||||
action = spawn_abm_action,
|
||||
})
|
||||
]]--
|
||||
end
|
||||
|
||||
-- compatibility with older mob registration
|
||||
-- we're going to forget about this for now -j4i
|
||||
--[[
|
||||
function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle)
|
||||
|
||||
mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30,
|
||||
chance, active_object_count, -31000, max_height, day_toggle)
|
||||
end
|
||||
]]--
|
||||
|
||||
|
||||
--Don't disable this yet-j4i
|
||||
-- MarkBu's spawn function
|
||||
|
||||
function mobs:spawn(def)
|
||||
--does nothing for now
|
||||
--[[
|
||||
local name = def.name
|
||||
local nodes = def.nodes or {"group:soil", "group:stone"}
|
||||
local neighbors = def.neighbors or {"air"}
|
||||
local min_light = def.min_light or 0
|
||||
local max_light = def.max_light or 15
|
||||
local interval = def.interval or 30
|
||||
local chance = def.chance or 5000
|
||||
local active_object_count = def.active_object_count or 1
|
||||
local min_height = def.min_height or -31000
|
||||
local max_height = def.max_height or 31000
|
||||
local day_toggle = def.day_toggle
|
||||
local on_spawn = def.on_spawn
|
||||
|
||||
mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
|
||||
chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
|
||||
]]--
|
||||
end
|
||||
|
||||
|
||||
|
||||
local axis
|
||||
--inner and outer part of square donut radius
|
||||
local inner = 1
|
||||
local outer = 65
|
||||
local int = {-1,1}
|
||||
local position_calculation = function(pos)
|
||||
|
||||
pos = vector.floor(pos)
|
||||
|
||||
--this is used to determine the axis buffer from the player
|
||||
axis = math.random(0,1)
|
||||
|
||||
--cast towards the direction
|
||||
if axis == 0 then --x
|
||||
pos.x = pos.x + math.random(inner,outer)*int[math.random(1,2)]
|
||||
pos.z = pos.z + math.random(-outer,outer)
|
||||
else --z
|
||||
pos.z = pos.z + math.random(inner,outer)*int[math.random(1,2)]
|
||||
pos.x = pos.x + math.random(-outer,outer)
|
||||
end
|
||||
return(pos)
|
||||
end
|
||||
|
||||
--[[
|
||||
local decypher_limits_dictionary = {
|
||||
["overworld"] = {mcl_vars.mg_overworld_min,mcl_vars.mg_overworld_max},
|
||||
["nether"] = {mcl_vars.mg_nether_min, mcl_vars.mg_nether_max},
|
||||
["end"] = {mcl_vars.mg_end_min, mcl_vars.mg_end_max}
|
||||
}
|
||||
]]--
|
||||
|
||||
local function decypher_limits(posy)
|
||||
--local min_max_table = decypher_limits_dictionary[dimension]
|
||||
--return min_max_table[1],min_max_table[2]
|
||||
posy = math.floor(posy)
|
||||
return posy - 32, posy + 32
|
||||
end
|
||||
|
||||
--a simple helper function for mob_spawn
|
||||
local function biome_check(biome_list, biome_goal)
|
||||
for _,data in ipairs(biome_list) do
|
||||
if data == biome_goal then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--todo mob limiting
|
||||
--MAIN LOOP
|
||||
|
||||
if mobs_spawn then
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if timer >= 8 then
|
||||
timer = 0
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
for i = 1,math_random(3,8) do
|
||||
repeat -- after this line each "break" means "continue"
|
||||
local player_pos = player:get_pos()
|
||||
|
||||
local _,dimension = mcl_worlds.y_to_layer(player_pos.y)
|
||||
|
||||
if dimension == "void" or dimension == "default" then
|
||||
break -- ignore void and unloaded area
|
||||
end
|
||||
|
||||
local min,max = decypher_limits(player_pos.y)
|
||||
|
||||
local goal_pos = position_calculation(player_pos)
|
||||
|
||||
local spawning_position_list = find_nodes_in_area_under_air(new_vector(goal_pos.x,min,goal_pos.z), vector.new(goal_pos.x,max,goal_pos.z), {"group:solid", "group:water", "group:lava"})
|
||||
|
||||
--couldn't find node
|
||||
if #spawning_position_list <= 0 then
|
||||
break
|
||||
end
|
||||
|
||||
local spawning_position = spawning_position_list[math_random(1,#spawning_position_list)]
|
||||
|
||||
--Prevent strange behavior/too close to player
|
||||
if not spawning_position or vector_distance(player_pos, spawning_position) < 15 then
|
||||
break
|
||||
end
|
||||
|
||||
local gotten_node = get_node(spawning_position).name
|
||||
|
||||
if not gotten_node or gotten_node == "air" then --skip air nodes
|
||||
break
|
||||
end
|
||||
|
||||
local gotten_biome = minetest.get_biome_data(spawning_position)
|
||||
|
||||
if not gotten_biome then
|
||||
break --skip if in unloaded area
|
||||
end
|
||||
|
||||
gotten_biome = get_biome_name(gotten_biome.biome) --makes it easier to work with
|
||||
|
||||
--grab random mob
|
||||
local mob_def = spawn_dictionary[math.random(1,#spawn_dictionary)]
|
||||
|
||||
if not mob_def then
|
||||
break --skip if something ridiculous happens (nil mob def)
|
||||
end
|
||||
|
||||
--skip if not correct dimension
|
||||
if mob_def.dimension ~= dimension then
|
||||
break
|
||||
end
|
||||
|
||||
--skip if not in correct biome
|
||||
if not biome_check(mob_def.biomes, gotten_biome) then
|
||||
break
|
||||
end
|
||||
|
||||
--add this so mobs don't spawn inside nodes
|
||||
spawning_position.y = spawning_position.y + 1
|
||||
|
||||
if spawning_position.y < mob_def.min_height or spawning_position.y > mob_def.max_height then
|
||||
break
|
||||
end
|
||||
|
||||
--only need to poll for node light if everything else worked
|
||||
local gotten_light = get_node_light(spawning_position)
|
||||
|
||||
--don't spawn if not in light limits
|
||||
if gotten_light < mob_def.min_light or gotten_light > mob_def.max_light then
|
||||
break
|
||||
end
|
||||
|
||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||
|
||||
if mob_def.type_of_spawning == "ground" and is_water then
|
||||
break
|
||||
end
|
||||
|
||||
if mob_def.type_of_spawning == "ground" and is_lava then
|
||||
break
|
||||
end
|
||||
|
||||
--finally do the heavy check (for now) of mobs in area
|
||||
if count_mobs(spawning_position, mob_def.spawn_class) >= mob_def.aoc then
|
||||
break
|
||||
end
|
||||
|
||||
--adjust the position for water and lava mobs
|
||||
if mob_def.type_of_spawning == "water" or mob_def.type_of_spawning == "lava" then
|
||||
spawning_position.y = spawning_position.y - 1
|
||||
end
|
||||
|
||||
--everything is correct, spawn mob
|
||||
minetest.add_entity(spawning_position, mob_def.name)
|
||||
until true --this is a safety catch
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
|
@ -290,13 +290,13 @@ mobs_mc.spawn = {
|
|||
mobs_mc.spawn_height = {
|
||||
water = tonumber(minetest.settings:get("water_level")) or 0, -- Water level in the Overworld
|
||||
|
||||
-- Overworld boundaries (inclusive) --I adjusted this to be more reasonable
|
||||
overworld_min = -64,-- -2999,
|
||||
-- Overworld boundaries (inclusive)
|
||||
overworld_min = -2999,
|
||||
overworld_max = 31000,
|
||||
|
||||
-- Nether boundaries (inclusive)
|
||||
nether_min = -29067,-- -3369,
|
||||
nether_max = -28939,-- -3000,
|
||||
nether_min = -3369,
|
||||
nether_max = -3000,
|
||||
|
||||
-- End boundaries (inclusive)
|
||||
end_min = -6200,
|
||||
|
|
|
@ -521,7 +521,7 @@ if c("totem") then
|
|||
-- Totem of Undying
|
||||
minetest.register_craftitem("mobs_mc:totem", {
|
||||
description = S("Totem of Undying"),
|
||||
_tt_help = minetest.colorize(mcl_colors.GREEN, S("Protects you from death while wielding it")),
|
||||
_tt_help = minetest.colorize("#00FF00", S("Protects you from death while wielding it")),
|
||||
_doc_items_longdesc = S("A totem of undying is a rare artifact which may safe you from certain death."),
|
||||
_doc_items_usagehelp = S("The totem only works while you hold it in your hand. If you receive fatal damage, you are saved from death and you get a second chance with 1 HP. The totem is destroyed in the process, however."),
|
||||
inventory_image = "mcl_totems_totem.png",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--###################
|
||||
--################### AGENT - seemingly unused
|
||||
--################### AGENT
|
||||
--###################
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
|
|
@ -64,81 +64,7 @@ else
|
|||
end
|
||||
|
||||
-- Spawn on solid blocks at or below Sea level and the selected light level
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:bat",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
},
|
||||
0,
|
||||
maxlight,
|
||||
20,
|
||||
5000,
|
||||
2,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.water-1)
|
||||
mobs:spawn_specific("mobs_mc:bat", mobs_mc.spawn.solid, {"air"}, 0, maxlight, 20, 5000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water-1)
|
||||
|
||||
|
||||
-- spawn eggs
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
-- daufinsyd
|
||||
-- My work is under the LGPL terms
|
||||
-- Model and mobs_blaze.png see https://github.com/22i/minecraft-voxel-blender-models -hi 22i ~jordan4ibanez
|
||||
-- Model and mobs_blaze.png see https://github.com/22i/minecraft-voxel-blender-models
|
||||
-- blaze.lua partial copy of mobs_mc/ghast.lua
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
@ -75,71 +75,9 @@ mobs:register_mob("mobs_mc:blaze", {
|
|||
fear_height = 0,
|
||||
glow = 14,
|
||||
fire_resistant = true,
|
||||
do_custom = function(self)
|
||||
if self.state == "attack" and vector.distance(self.object:get_pos(), self.attack:get_pos()) < 1.2 then
|
||||
mcl_burning.set_on_fire(self.attack, 5)
|
||||
end
|
||||
local pos = self.object:get_pos()
|
||||
minetest.add_particle({
|
||||
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
|
||||
velocity = {x=0, y=math.random(1,1), z=0},
|
||||
expirationtime = math.random(),
|
||||
size = math.random(1, 4),
|
||||
collisiondetection = true,
|
||||
vertical = false,
|
||||
texture = "mcl_particles_smoke_anim.png^[colorize:#2c2c2c:255",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 2.05,
|
||||
},
|
||||
})
|
||||
minetest.add_particle({
|
||||
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
|
||||
velocity = {x=0, y=math.random(1,1), z=0},
|
||||
expirationtime = math.random(),
|
||||
size = math.random(1, 4),
|
||||
collisiondetection = true,
|
||||
vertical = false,
|
||||
texture = "mcl_particles_smoke_anim.png^[colorize:#424242:255",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 2.05,
|
||||
},
|
||||
})
|
||||
minetest.add_particle({
|
||||
pos = {x=pos.x+math.random(-0.7,0.7)*math.random()/2,y=pos.y+math.random(0.7,1.2),z=pos.z+math.random(-0.7,0.7)*math.random()/2},
|
||||
velocity = {x=0, y=math.random(1,1), z=0},
|
||||
expirationtime = math.random(),
|
||||
size = math.random(1, 4),
|
||||
collisiondetection = true,
|
||||
vertical = false,
|
||||
texture = "mcl_particles_smoke_anim.png^[colorize:#0f0f0f:255",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 2.05,
|
||||
},
|
||||
})
|
||||
end,
|
||||
})
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:blaze",
|
||||
"nether",
|
||||
"ground",
|
||||
{"Nether"},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
5000,
|
||||
3,
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
mobs:spawn_specific("mobs_mc:blaze", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- Blaze fireball
|
||||
mobs:register_arrow("mobs_mc:blaze_fireball", {
|
||||
|
|
|
@ -100,34 +100,7 @@ mobs:register_mob("mobs_mc:chicken", {
|
|||
})
|
||||
|
||||
--spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:chicken",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30, 17000,
|
||||
3,
|
||||
mobs_mc.spawn_height.water,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:chicken", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:chicken", S("Chicken"), "mobs_mc_spawn_icon_chicken.png", 0)
|
||||
|
|
|
@ -145,53 +145,8 @@ mobs:register_mob("mobs_mc:mooshroom", mooshroom_def)
|
|||
|
||||
|
||||
-- Spawning
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:cow",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
17000,
|
||||
10,
|
||||
mobs_mc.spawn_height.water,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:mooshroom",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"MushroomIslandShore",
|
||||
"MushroomIsland"
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
17000,
|
||||
5,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:cow", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 10, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:mooshroom", mobs_mc.spawn.mushroom_island, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 17000, 5, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn egg
|
||||
mobs:register_egg("mobs_mc:cow", S("Cow"), "mobs_mc_spawn_icon_cow.png", 0)
|
||||
|
|
|
@ -39,8 +39,6 @@ mobs:register_mob("mobs_mc:creeper", {
|
|||
runaway_from = { "mobs_mc:ocelot", "mobs_mc:cat" },
|
||||
attack_type = "explode",
|
||||
|
||||
--hssssssssssss
|
||||
|
||||
explosion_strength = 3,
|
||||
explosion_radius = 3.5,
|
||||
explosion_damage_radius = 3.5,
|
||||
|
@ -78,6 +76,7 @@ mobs:register_mob("mobs_mc:creeper", {
|
|||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||
if self._forced_explosion_countdown_timer <= 0 then
|
||||
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -140,9 +139,6 @@ mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
pathfinding = 1,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_creeper.b3d",
|
||||
|
||||
--BOOM
|
||||
|
||||
textures = {
|
||||
{"mobs_mc_creeper.png",
|
||||
"mobs_mc_creeper_charge.png"},
|
||||
|
@ -199,6 +195,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
self._forced_explosion_countdown_timer = self._forced_explosion_countdown_timer - dtime
|
||||
if self._forced_explosion_countdown_timer <= 0 then
|
||||
mobs:boom(self, mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
@ -253,158 +250,7 @@ mobs:register_mob("mobs_mc:creeper_charged", {
|
|||
glow = 3,
|
||||
})
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:creeper",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
20,
|
||||
16500,
|
||||
2,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:creeper", mobs_mc.spawn.solid, {"air"}, 0, 7, 20, 16500, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:creeper", S("Creeper"), "mobs_mc_spawn_icon_creeper.png", 0)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
mcl_mobs
|
|
@ -50,8 +50,8 @@ mobs:register_mob("mobs_mc:enderdragon", {
|
|||
arrow = "mobs_mc:dragon_fireball",
|
||||
shoot_interval = 0.5,
|
||||
shoot_offset = -1.0,
|
||||
xp_min = 500,
|
||||
xp_max = 500,
|
||||
xp_min = 12000,
|
||||
xp_max = 12000,
|
||||
animation = {
|
||||
fly_speed = 8, stand_speed = 8,
|
||||
stand_start = 0, stand_end = 20,
|
||||
|
@ -59,47 +59,15 @@ mobs:register_mob("mobs_mc:enderdragon", {
|
|||
run_start = 0, run_end = 20,
|
||||
},
|
||||
ignores_nametag = true,
|
||||
do_custom = function(self)
|
||||
mcl_bossbars.update_boss(self, "Ender Dragon", "light_purple")
|
||||
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity and luaentity.name == "mcl_end:crystal" then
|
||||
if luaentity.beam then
|
||||
if luaentity.beam == self.beam then
|
||||
break
|
||||
end
|
||||
else
|
||||
if self.beam then
|
||||
self.beam:remove()
|
||||
end
|
||||
minetest.add_entity(self.object:get_pos(), "mcl_end:crystal_beam"):get_luaentity():init(self.object, obj)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if self._portal_pos then
|
||||
-- migrate old format
|
||||
if type(self._portal_pos) == "string" then
|
||||
self._portal_pos = minetest.string_to_pos(self._portal_pos)
|
||||
end
|
||||
local portal_center = vector.add(self._portal_pos, vector.new(3, 11, 3))
|
||||
local pos = self.object:get_pos()
|
||||
if vector.distance(pos, portal_center) > 50 then
|
||||
self.object:set_pos(self._last_good_pos or portal_center)
|
||||
else
|
||||
self._last_good_pos = pos
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_die = function(self, pos)
|
||||
if self._portal_pos then
|
||||
mcl_portals.spawn_gateway_portal()
|
||||
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
|
||||
if self._initial then
|
||||
mcl_experience.throw_experience(pos, 11500) -- 500 + 11500 = 12000
|
||||
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
|
||||
end
|
||||
on_die = function(self, own_pos)
|
||||
if self._egg_spawn_pos then
|
||||
local pos = minetest.string_to_pos(self._egg_spawn_pos)
|
||||
--if minetest.get_node(pos).buildable_to then
|
||||
minetest.set_node(pos, {name = mobs_mc.items.dragon_egg})
|
||||
return
|
||||
--end
|
||||
end
|
||||
minetest.add_item(own_pos, mobs_mc.items.dragon_egg)
|
||||
end,
|
||||
fire_resistant = true,
|
||||
})
|
||||
|
|
|
@ -295,8 +295,7 @@ mobs:register_mob("mobs_mc:enderman", {
|
|||
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
|
||||
-- Check for arrows and people nearby.
|
||||
local enderpos = self.object:get_pos()
|
||||
enderpos.y = enderpos.y + 1.5
|
||||
local objs = minetest.get_objects_inside_radius(enderpos, 2)
|
||||
local objs = minetest.get_objects_inside_radius(enderpos, 4)
|
||||
for n = 1, #objs do
|
||||
local obj = objs[n]
|
||||
if obj then
|
||||
|
@ -308,7 +307,7 @@ mobs:register_mob("mobs_mc:enderman", {
|
|||
else
|
||||
local lua = obj:get_luaentity()
|
||||
if lua then
|
||||
if lua.name == "mcl_bows:arrow_entity" or lua.name == "mcl_throwing:snowball_entity" then
|
||||
if lua.name == "mcl_bows:arrow_entity" then
|
||||
self:teleport(nil)
|
||||
end
|
||||
end
|
||||
|
@ -329,44 +328,35 @@ mobs:register_mob("mobs_mc:enderman", {
|
|||
--end
|
||||
end
|
||||
-- Check to see if people are near by enough to look at us.
|
||||
for _,obj in pairs(minetest.get_connected_players()) do
|
||||
|
||||
--check if they are within radius
|
||||
local player_pos = obj:get_pos()
|
||||
if player_pos then -- prevent crashing in 1 in a million scenario
|
||||
|
||||
local ender_distance = vector.distance(enderpos, player_pos)
|
||||
if ender_distance <= 64 then
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(enderpos, 64)
|
||||
local obj
|
||||
for n = 1, #objs do
|
||||
obj = objs[n]
|
||||
if obj then
|
||||
if minetest.is_player(obj) then
|
||||
-- Check if they are looking at us.
|
||||
local player_pos = obj:get_pos()
|
||||
local look_dir_not_normalized = obj:get_look_dir()
|
||||
local look_dir = vector.normalize(look_dir_not_normalized)
|
||||
local player_eye_height = obj:get_properties().eye_height
|
||||
|
||||
--skip player if they have no data - log it
|
||||
if not player_eye_height then
|
||||
minetest.log("error", "Enderman at location: ".. dump(enderpos).." has indexed a null player!")
|
||||
else
|
||||
|
||||
--calculate very quickly the exact location the player is looking
|
||||
--within the distance between the two "heads" (player and enderman)
|
||||
local look_pos = vector.new(player_pos.x, player_pos.y + player_eye_height, player_pos.z)
|
||||
local look_pos_base = look_pos
|
||||
local ender_eye_pos = vector.new(enderpos.x, enderpos.y + 2.75, enderpos.z)
|
||||
local eye_distance_from_player = vector.distance(ender_eye_pos, look_pos)
|
||||
look_pos = vector.add(look_pos, vector.multiply(look_dir, eye_distance_from_player))
|
||||
|
||||
--if looking in general head position, turn hostile
|
||||
if minetest.line_of_sight(ender_eye_pos, look_pos_base) and vector.distance(look_pos, ender_eye_pos) <= 0.4 then
|
||||
local look_pos = vector.new({x = look_dir.x+player_pos.x, y = look_dir.y+player_pos.y + 1.5, z = look_dir.z+player_pos.z}) -- Arbitrary value (1.5) is head level according to player info mod.
|
||||
-- Cast up to 64 to see if player is looking at enderman.
|
||||
for n = 1,64,.25 do
|
||||
local node = minetest.get_node(look_pos)
|
||||
if node.name ~= "air" then
|
||||
break
|
||||
end
|
||||
if look_pos.x-1<enderpos.x and look_pos.x+1>enderpos.x and look_pos.y-2.89<enderpos.y and look_pos.y-2>enderpos.y and look_pos.z-1<enderpos.z and look_pos.z+1>enderpos.z then
|
||||
self.provoked = "staring"
|
||||
self.attack = minetest.get_player_by_name(obj:get_player_name())
|
||||
break
|
||||
else -- I'm not sure what this part does, but I don't want to break anything - jordan4ibanez
|
||||
else
|
||||
if self.provoked == "staring" then
|
||||
self.provoked = "broke_contact"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
look_pos.x = look_pos.x + (.25 * look_dir.x)
|
||||
look_pos.y = look_pos.y + (.25 * look_dir.y)
|
||||
look_pos.z = look_pos.z + (.25 * look_dir.z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -544,11 +534,9 @@ mobs:register_mob("mobs_mc:enderman", {
|
|||
--if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
|
||||
-- self:teleport(nil)
|
||||
--else
|
||||
if pr:next(1, 8) == 8 then --FIXME: real mc rate
|
||||
self:teleport(hitter)
|
||||
end
|
||||
self.attack=hitter
|
||||
self.state="attack"
|
||||
self.attack=hitter
|
||||
self.state="attack"
|
||||
--end
|
||||
end
|
||||
end,
|
||||
|
@ -561,189 +549,11 @@ mobs:register_mob("mobs_mc:enderman", {
|
|||
|
||||
|
||||
-- End spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"end",
|
||||
"ground",
|
||||
{
|
||||
"End"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
3000,
|
||||
12,
|
||||
mobs_mc.spawn_height.end_min,
|
||||
mobs_mc.spawn_height.end_max)
|
||||
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 3000, 12, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)
|
||||
-- Overworld spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
19000,
|
||||
2,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 19000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
-- Nether spawn (rare)
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:enderman",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
27500,
|
||||
4,
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 27500, 4, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)
|
||||
|
|
|
@ -63,32 +63,10 @@ mobs:register_mob("mobs_mc:ghast", {
|
|||
makes_footstep_sound = false,
|
||||
instant_death = true,
|
||||
fire_resistant = true,
|
||||
do_custom = function(self)
|
||||
if self.firing == true then
|
||||
self.base_texture = {"mobs_mc_ghast_firing.png"}
|
||||
self.object:set_properties({textures=self.base_texture})
|
||||
else
|
||||
self.base_texture = {"mobs_mc_ghast.png"}
|
||||
self.object:set_properties({textures=self.base_texture})
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:ghast",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
18000,
|
||||
2,
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
mobs:spawn_specific("mobs_mc:ghast", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 18000, 2, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- fireball (projectile)
|
||||
mobs:register_arrow("mobs_mc:fireball", {
|
||||
|
@ -96,7 +74,6 @@ mobs:register_arrow("mobs_mc:fireball", {
|
|||
visual_size = {x = 1, y = 1},
|
||||
textures = {"mcl_fire_fire_charge.png"},
|
||||
velocity = 15,
|
||||
collisionbox = {-.5, -.5, -.5, .5, .5, .5},
|
||||
|
||||
hit_player = function(self, player)
|
||||
if rawget(_G, "armor") and armor.last_damage_types then
|
||||
|
|
|
@ -106,7 +106,7 @@ mobs:register_mob("mobs_mc:guardian_elder", {
|
|||
view_range = 16,
|
||||
})
|
||||
|
||||
-- Spawning disabled due to size issues <- what do you mean? -j4i
|
||||
-- Spawning disabled due to size issues
|
||||
-- TODO: Re-enable spawning
|
||||
-- mobs:spawn_specific("mobs_mc:guardian_elder", mobs_mc.spawn.water, mobs_mc.spawn_water, 0, minetest.LIGHT_MAX+1, 30, 40000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.water-18)
|
||||
|
||||
|
|
|
@ -157,29 +157,8 @@ local horse = {
|
|||
self._regentimer = 0
|
||||
end
|
||||
|
||||
-- Some weird human is riding. Buck them off?
|
||||
if self.driver and not self.tamed and self.buck_off_time <= 0 then
|
||||
if math.random() < 0.2 then
|
||||
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
||||
-- TODO bucking animation
|
||||
else
|
||||
-- Nah, can't be bothered. Think about it again in one second
|
||||
self.buck_off_time = 20
|
||||
end
|
||||
end
|
||||
|
||||
-- Tick the timer for trying to buck the player off
|
||||
if self.buck_off_time then
|
||||
if self.driver then
|
||||
self.buck_off_time = self.buck_off_time - 1
|
||||
else
|
||||
-- Player isn't riding anymore so no need to count
|
||||
self.buck_off_time = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- if driver present and horse has a saddle allow control of horse
|
||||
if self.driver and self._saddle then
|
||||
-- if driver present allow control of horse
|
||||
if self.driver then
|
||||
|
||||
mobs.drive(self, "walk", "stand", false, dtime)
|
||||
|
||||
|
@ -212,49 +191,6 @@ local horse = {
|
|||
local item = clicker:get_wielded_item()
|
||||
local iname = item:get_name()
|
||||
local heal = 0
|
||||
|
||||
-- Taming
|
||||
self.temper = self.temper or (math.random(1,100))
|
||||
|
||||
if not self.tamed then
|
||||
local temper_increase = 0
|
||||
|
||||
-- Feeding, intentionally not using mobs:feed_tame because horse taming is
|
||||
-- different and more complicated
|
||||
if (iname == mobs_mc.items.sugar) then
|
||||
temper_increase = 3
|
||||
elseif (iname == mobs_mc.items.wheat) then
|
||||
temper_increase = 3
|
||||
elseif (iname == mobs_mc.items.apple) then
|
||||
temper_increase = 3
|
||||
elseif (iname == mobs_mc.items.golden_carrot) then
|
||||
temper_increase = 5
|
||||
elseif (iname == mobs_mc.items.golden_apple) then
|
||||
temper_increase = 10
|
||||
-- Trying to ride
|
||||
elseif not self.driver then
|
||||
self.object:set_properties({stepheight = 1.1})
|
||||
mobs.attach(self, clicker)
|
||||
self.buck_off_time = 40 -- TODO how long does it take in minecraft?
|
||||
if self.temper > 100 then
|
||||
self.tamed = true -- NOTE taming can only be finished by riding the horse
|
||||
if not self.owner or self.owner == "" then
|
||||
self.owner = clicker:get_player_name()
|
||||
end
|
||||
end
|
||||
temper_increase = 5
|
||||
|
||||
-- Clicking on the horse while riding ==> unmount
|
||||
elseif self.driver and self.driver == clicker then
|
||||
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
||||
end
|
||||
|
||||
-- If nothing happened temper_increase = 0 and addition does nothing
|
||||
self.temper = self.temper + temper_increase
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if can_breed(self.name) then
|
||||
-- Breed horse with golden apple or golden carrot
|
||||
if (iname == mobs_mc.items.golden_apple) then
|
||||
|
@ -266,8 +202,7 @@ local horse = {
|
|||
return
|
||||
end
|
||||
end
|
||||
-- Feed with anything else
|
||||
-- TODO heal amounts don't work
|
||||
-- Feed/tame with anything else
|
||||
if (iname == mobs_mc.items.sugar) then
|
||||
heal = 1
|
||||
elseif (iname == mobs_mc.items.wheat) then
|
||||
|
@ -277,7 +212,7 @@ local horse = {
|
|||
elseif (iname == mobs_mc.items.hay_bale) then
|
||||
heal = 20
|
||||
end
|
||||
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, false) then
|
||||
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, true) then
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -510,60 +445,12 @@ mobs:register_mob("mobs_mc:mule", mule)
|
|||
|
||||
--===========================
|
||||
--Spawn Function
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:horse",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
4,
|
||||
mobs_mc.spawn_height.water+3,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:donkey",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Mesa",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
4,
|
||||
mobs_mc.spawn_height.water+3,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:horse", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:donkey", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 0)
|
||||
mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
|
||||
--mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
|
||||
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "mobs_mc_spawn_icon_horse_zombie.png", 0)
|
||||
mobs:register_egg("mobs_mc:donkey", S("Donkey"), "mobs_mc_spawn_icon_donkey.png", 0)
|
||||
mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)
|
||||
|
|
|
@ -217,25 +217,7 @@ mobs:register_mob("mobs_mc:llama", {
|
|||
})
|
||||
|
||||
--spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:llama",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Mesa",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
5,
|
||||
mobs_mc.spawn_height.water+15,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:llama", mobs_mc.spawn.savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 5, mobs_mc.spawn_height.water+15, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:llama", S("Llama"), "mobs_mc_spawn_icon_llama.png", 0)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name = mobs_mc
|
||||
author = maikerumine
|
||||
description = Adds Minecraft-like monsters and animals.
|
||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_colors
|
||||
depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip
|
||||
optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, mobs_mc_gameconfig, doc_items
|
||||
|
||||
|
|
|
@ -152,25 +152,6 @@ mobs:register_mob("mobs_mc:cat", cat)
|
|||
local base_spawn_chance = 5000
|
||||
|
||||
-- Spawn ocelot
|
||||
--they get the same as the llama because I'm trying to rework so much of this code right now -j4i
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:ocelot",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Jungle",
|
||||
"JungleEdgeM",
|
||||
"JungleM",
|
||||
"JungleEdge",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
5,
|
||||
mobs_mc.spawn_height.water+15,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
--[[
|
||||
mobs:spawn({
|
||||
name = "mobs_mc:ocelot",
|
||||
nodes = mobs_mc.spawn.jungle,
|
||||
|
@ -182,8 +163,8 @@ mobs:spawn({
|
|||
min_height = mobs_mc.spawn_height.water+1, -- Right above ocean level
|
||||
max_height = mobs_mc.spawn_height.overworld_max,
|
||||
on_spawn = function(self, pos)
|
||||
Note: Minecraft has a 1/3 spawn failure rate.
|
||||
In this mod it is emulated by reducing the spawn rate accordingly (see above).
|
||||
--[[ Note: Minecraft has a 1/3 spawn failure rate.
|
||||
In this mod it is emulated by reducing the spawn rate accordingly (see above). ]]
|
||||
|
||||
-- 1/7 chance to spawn 2 ocelot kittens
|
||||
if pr:next(1,7) == 1 then
|
||||
|
@ -226,7 +207,6 @@ mobs:spawn({
|
|||
end
|
||||
end,
|
||||
})
|
||||
]]--
|
||||
|
||||
-- spawn eggs
|
||||
-- FIXME: The spawn icon shows a cat texture, not an ocelot texture
|
||||
|
|
|
@ -90,24 +90,8 @@ mobs:register_mob("mobs_mc:parrot", {
|
|||
|
||||
})
|
||||
|
||||
-- Parrots spawn rarely in jungles. TODO: Also check for jungle *biome* <- I'll get to this eventually -j4i
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:parrot",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Jungle",
|
||||
"JungleEdgeM",
|
||||
"JungleM",
|
||||
"JungleEdge",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
7,
|
||||
30000,
|
||||
1,
|
||||
mobs_mc.spawn_height.water+7,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
-- Parrots spawn rarely in jungles. TODO: Also check for jungle *biome*
|
||||
mobs:spawn_specific("mobs_mc:parrot", {"mcl_core:jungletree", "mcl_core:jungleleaves"}, {"air"}, 0, minetest.LIGHT_MAX+1, 7, 30000, 1, mobs_mc.spawn_height.water+7, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:parrot", S("Parrot"), "mobs_mc_spawn_icon_parrot.png", 0)
|
||||
|
|
|
@ -182,35 +182,7 @@ mobs:register_mob("mobs_mc:pig", {
|
|||
end,
|
||||
})
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:pig",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
8,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:pig", mobs_mc.spawn.grassland, {"air"}, 9, minetest.LIGHT_MAX+1, 30, 15000, 8, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:pig", S("Pig"), "mobs_mc_spawn_icon_pig.png", 0)
|
||||
|
|
|
@ -67,23 +67,7 @@ mobs:register_mob("mobs_mc:polar_bear", {
|
|||
})
|
||||
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:polar_bear",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"ColdTaiga",
|
||||
"IcePlainsSpikes",
|
||||
"IcePlains",
|
||||
"ExtremeHills+_snowtop",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
7000,
|
||||
3,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:polar_bear", mobs_mc.spawn.snow, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 7000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn egg
|
||||
mobs:register_egg("mobs_mc:polar_bear", S("Polar Bear"), "mobs_mc_spawn_icon_polarbear.png", 0)
|
||||
|
|
|
@ -107,39 +107,8 @@ end
|
|||
mobs:register_mob("mobs_mc:killer_bunny", killer_bunny)
|
||||
|
||||
-- Mob spawning rules.
|
||||
-- Different skins depending on spawn location <- we'll get to this when the spawning algorithm is fleshed out
|
||||
-- Different skins depending on spawn location
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:rabbit",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
9,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
8,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
--[[
|
||||
local spawn = {
|
||||
name = "mobs_mc:rabbit",
|
||||
neighbors = {"air"},
|
||||
|
@ -196,7 +165,6 @@ spawn_grass.on_spawn = function(self, pos)
|
|||
self.object:set_properties({textures = self.base_texture})
|
||||
end
|
||||
mobs:spawn(spawn_grass)
|
||||
]]--
|
||||
|
||||
-- Spawn egg
|
||||
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
||||
|
|
|
@ -25,19 +25,6 @@ local colors = {
|
|||
unicolor_black = { mobs_mc.items.wool_black, "#000000D0" },
|
||||
}
|
||||
|
||||
local rainbow_colors = {
|
||||
"unicolor_light_red",
|
||||
"unicolor_red",
|
||||
"unicolor_orange",
|
||||
"unicolor_yellow",
|
||||
"unicolor_green",
|
||||
"unicolor_dark_green",
|
||||
"unicolor_light_blue",
|
||||
"unicolor_blue",
|
||||
"unicolor_violet",
|
||||
"unicolor_red_violet"
|
||||
}
|
||||
|
||||
if minetest.get_modpath("mcl_wool") ~= nil then
|
||||
colors["unicolor_light_blue"] = { mobs_mc.items.wool_light_blue, "#5050FFD0" }
|
||||
end
|
||||
|
@ -125,7 +112,7 @@ mobs:register_mob("mobs_mc:sheep", {
|
|||
end,
|
||||
|
||||
-- Set random color on spawn
|
||||
do_custom = function(self, dtime)
|
||||
do_custom = function(self)
|
||||
if not self.initial_color_set then
|
||||
local r = math.random(0,100000)
|
||||
local textures
|
||||
|
@ -162,35 +149,8 @@ mobs:register_mob("mobs_mc:sheep", {
|
|||
}
|
||||
self.initial_color_set = true
|
||||
end
|
||||
|
||||
local is_kay27 = self.nametag == "kay27"
|
||||
|
||||
if self.color_change_timer then
|
||||
local old_color = self.color
|
||||
if is_kay27 then
|
||||
self.color_change_timer = self.color_change_timer - dtime
|
||||
if self.color_change_timer < 0 then
|
||||
self.color_change_timer = 0.5
|
||||
self.color_index = (self.color_index + 1) % #rainbow_colors
|
||||
self.color = rainbow_colors[self.color_index + 1]
|
||||
end
|
||||
else
|
||||
self.color_change_timer = nil
|
||||
self.color_index = nil
|
||||
self.color = self.initial_color
|
||||
end
|
||||
|
||||
if old_color ~= self.color then
|
||||
self.base_texture = sheep_texture(self.color)
|
||||
self.object:set_properties({textures = self.base_texture})
|
||||
end
|
||||
elseif is_kay27 then
|
||||
self.initial_color = self.color
|
||||
self.color_change_timer = 0
|
||||
self.color_index = -1
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
on_rightclick = function(self, clicker)
|
||||
local item = clicker:get_wielded_item()
|
||||
|
||||
|
@ -303,35 +263,7 @@ mobs:register_mob("mobs_mc:sheep", {
|
|||
end
|
||||
end,
|
||||
})
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:sheep",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"ColdTaiga",
|
||||
"SunflowerPlains",
|
||||
"RoofedForest",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"ExtremeHillsM",
|
||||
"BirchForestM",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
3,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:sheep", mobs_mc.spawn.grassland, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 3, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:sheep", S("Sheep"), "mobs_mc_spawn_icon_sheep.png", 0)
|
||||
|
|
|
@ -81,17 +81,4 @@ mobs:register_arrow("mobs_mc:shulkerbullet", {
|
|||
|
||||
mobs:register_egg("mobs_mc:shulker", S("Shulker"), "mobs_mc_spawn_icon_shulker.png", 0)
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:shulker",
|
||||
"end",
|
||||
"ground",
|
||||
{
|
||||
"End"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
5000,
|
||||
2,
|
||||
mobs_mc.spawn_height.end_min,
|
||||
mobs_mc.spawn_height.end_max)
|
||||
mobs:spawn_specific("mobs_mc:shulker", mobs_mc.spawn.end_city, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 5000, 2, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)
|
||||
|
|
|
@ -139,195 +139,13 @@ table.insert(stray.drops, {
|
|||
mobs:register_mob("mobs_mc:stray", stray)
|
||||
|
||||
-- Overworld spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:skeleton",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
20,
|
||||
17000,
|
||||
2,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
|
||||
mobs:spawn_specific("mobs_mc:skeleton", mobs_mc.spawn.solid, {"air"}, 0, 7, 20, 17000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
-- Nether spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:skeleton",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
10000,
|
||||
3,
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
mobs:spawn_specific("mobs_mc:skeleton", mobs_mc.spawn.nether_fortress, {"air"}, 0, 7, 30, 10000, 3, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- Stray spawn
|
||||
-- TODO: Spawn directly under the sky
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:stray",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"ColdTaiga",
|
||||
"IcePlainsSpikes",
|
||||
"IcePlains",
|
||||
"ExtremeHills+_snowtop",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
20,
|
||||
19000,
|
||||
2,
|
||||
mobs_mc.spawn_height.water,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:stray", mobs_mc.spawn.snow, {"air"}, 0, 7, 20, 19000, 2, mobs_mc.spawn_height.water, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
|
||||
-- spawn eggs
|
||||
|
|
|
@ -94,20 +94,7 @@ mobs:register_mob("mobs_mc:witherskeleton", {
|
|||
})
|
||||
|
||||
--spawn
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:witherskeleton",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
5000,
|
||||
5,
|
||||
mobs_mc.spawn_height.nether_min,
|
||||
mobs_mc.spawn_height.nether_max)
|
||||
mobs:spawn_specific("mobs_mc:witherskeleton", mobs_mc.spawn.nether_fortress, {"air"}, 0, 7, 30, 5000, 5, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "mobs_mc_spawn_icon_witherskeleton.png", 0)
|
||||
mobs:register_egg("mobs_mc:witherskeleton", S("Wither Skeleton"), "mobs_mc_spawn_icon_witherskeleton.png", 0)
|
||||
|
|
|
@ -51,6 +51,7 @@ local spawn_children_on_die = function(child_mob, children_count, spawn_distance
|
|||
end
|
||||
end, children, self.attack)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,6 +109,7 @@ local slime_big = {
|
|||
fear_height = 0,
|
||||
spawn_small_alternative = "mobs_mc:slime_small",
|
||||
on_die = spawn_children_on_die("mobs_mc:slime_small", 4, 1.0, 1.5),
|
||||
fire_resistant = true,
|
||||
use_texture_alpha = true,
|
||||
}
|
||||
mobs:register_mob("mobs_mc:slime_big", slime_big)
|
||||
|
@ -157,137 +159,9 @@ mobs:register_mob("mobs_mc:slime_tiny", slime_tiny)
|
|||
local smin = mobs_mc.spawn_height.overworld_min
|
||||
local smax = mobs_mc.spawn_height.water - 23
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:slime_tiny",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
12000,
|
||||
4,
|
||||
smin,
|
||||
smax)
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:slime_small",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
8500,
|
||||
4,
|
||||
smin,
|
||||
smax)
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:slime_big",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
10000,
|
||||
4,
|
||||
smin,
|
||||
smax)
|
||||
mobs:spawn_specific("mobs_mc:slime_tiny", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 12000, 4, smin, smax)
|
||||
mobs:spawn_specific("mobs_mc:slime_small", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 8500, 4, smin, smax)
|
||||
mobs:spawn_specific("mobs_mc:slime_big", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 10000, 4, smin, smax)
|
||||
|
||||
-- Magma cube
|
||||
local magma_cube_big = {
|
||||
|
@ -400,55 +274,13 @@ mobs:register_mob("mobs_mc:magma_cube_tiny", magma_cube_tiny)
|
|||
local mmin = mobs_mc.spawn_height.nether_min
|
||||
local mmax = mobs_mc.spawn_height.nether_max
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:magma_cube_tiny",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15000,
|
||||
4,
|
||||
mmin,
|
||||
mmax)
|
||||
mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mmin, mmax)
|
||||
mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15500, 4, mmin, mmax)
|
||||
mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 16000, 4, mmin, mmax)
|
||||
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:magma_cube_small",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
15500,
|
||||
4,
|
||||
mmin,
|
||||
mmax)
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:magma_cube_big",
|
||||
"nether",
|
||||
"ground",
|
||||
{
|
||||
"Nether"
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
16000,
|
||||
4,
|
||||
mmin,
|
||||
mmax)
|
||||
|
||||
--mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11000, 4, mmin, mmax)
|
||||
--mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11100, 4, mmin, mmax)
|
||||
--mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11200, 4, mmin, mmax)
|
||||
mobs:spawn_specific("mobs_mc:magma_cube_tiny", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11000, 4, mmin, mmax)
|
||||
mobs:spawn_specific("mobs_mc:magma_cube_small", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11100, 4, mmin, mmax)
|
||||
mobs:spawn_specific("mobs_mc:magma_cube_big", mobs_mc.spawn.nether_fortress, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 11200, 4, mmin, mmax)
|
||||
|
||||
|
||||
-- spawn eggs
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -87,158 +87,7 @@ cave_spider.sounds.base_pitch = 1.25
|
|||
mobs:register_mob("mobs_mc:cave_spider", cave_spider)
|
||||
|
||||
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:spider",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
7,
|
||||
30,
|
||||
17000,
|
||||
2,
|
||||
mobs_mc.spawn_height.overworld_min,
|
||||
mobs_mc.spawn_height.overworld_max)
|
||||
mobs:spawn_specific("mobs_mc:spider", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 17000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:spider", S("Spider"), "mobs_mc_spawn_icon_spider.png", 0)
|
||||
|
|
|
@ -62,158 +62,7 @@ mobs:register_mob("mobs_mc:squid", {
|
|||
|
||||
local water = mobs_mc.spawn_height.water
|
||||
--name, nodes, neighbours, minlight, maxlight, interval, chance, active_object_count, min_height, max_height
|
||||
mobs:spawn_specific(
|
||||
"mobs_mc:squid",
|
||||
"overworld",
|
||||
"water",
|
||||
{
|
||||
"Mesa",
|
||||
"FlowerForest",
|
||||
"Swampland",
|
||||
"Taiga",
|
||||
"ExtremeHills",
|
||||
"Jungle",
|
||||
"Savanna",
|
||||
"BirchForest",
|
||||
"MegaSpruceTaiga",
|
||||
"MegaTaiga",
|
||||
"ExtremeHills+",
|
||||
"Forest",
|
||||
"Plains",
|
||||
"Desert",
|
||||
"ColdTaiga",
|
||||
"MushroomIsland",
|
||||
"IcePlainsSpikes",
|
||||
"SunflowerPlains",
|
||||
"IcePlains",
|
||||
"RoofedForest",
|
||||
"ExtremeHills+_snowtop",
|
||||
"MesaPlateauFM_grasstop",
|
||||
"JungleEdgeM",
|
||||
"ExtremeHillsM",
|
||||
"JungleM",
|
||||
"BirchForestM",
|
||||
"MesaPlateauF",
|
||||
"MesaPlateauFM",
|
||||
"MesaPlateauF_grasstop",
|
||||
"MesaBryce",
|
||||
"JungleEdge",
|
||||
"SavannaM",
|
||||
"FlowerForest_beach",
|
||||
"Forest_beach",
|
||||
"StoneBeach",
|
||||
"ColdTaiga_beach_water",
|
||||
"Taiga_beach",
|
||||
"Savanna_beach",
|
||||
"Plains_beach",
|
||||
"ExtremeHills_beach",
|
||||
"ColdTaiga_beach",
|
||||
"Swampland_shore",
|
||||
"MushroomIslandShore",
|
||||
"JungleM_shore",
|
||||
"Jungle_shore",
|
||||
"MesaPlateauFM_sandlevel",
|
||||
"MesaPlateauF_sandlevel",
|
||||
"MesaBryce_sandlevel",
|
||||
"Mesa_sandlevel",
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean",
|
||||
"FlowerForest_underground",
|
||||
"JungleEdge_underground",
|
||||
"StoneBeach_underground",
|
||||
"MesaBryce_underground",
|
||||
"Mesa_underground",
|
||||
"RoofedForest_underground",
|
||||
"Jungle_underground",
|
||||
"Swampland_underground",
|
||||
"MushroomIsland_underground",
|
||||
"BirchForest_underground",
|
||||
"Plains_underground",
|
||||
"MesaPlateauF_underground",
|
||||
"ExtremeHills_underground",
|
||||
"MegaSpruceTaiga_underground",
|
||||
"BirchForestM_underground",
|
||||
"SavannaM_underground",
|
||||
"MesaPlateauFM_underground",
|
||||
"Desert_underground",
|
||||
"Savanna_underground",
|
||||
"Forest_underground",
|
||||
"SunflowerPlains_underground",
|
||||
"ColdTaiga_underground",
|
||||
"IcePlains_underground",
|
||||
"IcePlainsSpikes_underground",
|
||||
"MegaTaiga_underground",
|
||||
"Taiga_underground",
|
||||
"ExtremeHills+_underground",
|
||||
"JungleM_underground",
|
||||
"ExtremeHillsM_underground",
|
||||
"JungleEdgeM_underground",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
5500,
|
||||
3,
|
||||
water-16,
|
||||
water+1)
|
||||
mobs:spawn_specific("mobs_mc:squid", mobs_mc.spawn.water, {mobs_mc.items.water_source}, 0, minetest.LIGHT_MAX+1, 30, 5500, 3, water-16, water)
|
||||
|
||||
-- spawn eggs
|
||||
mobs:register_egg("mobs_mc:squid", S("Squid"), "mobs_mc_spawn_icon_squid.png", 0)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue