Merge branch 'master' into Axolotl
|
@ -33,6 +33,7 @@
|
|||
* RandomLegoBrick
|
||||
* SumianVoice
|
||||
* MrRar
|
||||
* talamh
|
||||
|
||||
## Contributors
|
||||
* Laurent Rocher
|
||||
|
@ -66,7 +67,6 @@
|
|||
* Benjamin Schötz
|
||||
* Doloment
|
||||
* Sydney Gems
|
||||
* talamh
|
||||
* Emily2255
|
||||
* Emojigit
|
||||
* FinishedFragment
|
||||
|
@ -85,6 +85,10 @@
|
|||
* opfromthestart
|
||||
* snowyu
|
||||
* FaceDeer
|
||||
* Faerraven / Michieal
|
||||
* FossFanatic
|
||||
* Herbert West
|
||||
* GuyLiner
|
||||
|
||||
## MineClone5
|
||||
* kay27
|
||||
|
@ -95,7 +99,7 @@
|
|||
* chmodsayshello
|
||||
* 3raven
|
||||
* PrairieWind
|
||||
* Gustavo1
|
||||
* Gustavo6046 / wallabra
|
||||
* CableGuy67
|
||||
* MrRar
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
An unofficial Minecraft-like game for Minetest. Forked from MineClone by davedevils.
|
||||
Developed by many people. Not developed or endorsed by Mojang AB.
|
||||
|
||||
Version: 0.80 (in development)
|
||||
Version: 0.81 (in development)
|
||||
|
||||
### Gameplay
|
||||
You start in a randomly-generated world made entirely of cubes. You can explore
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
-- Nodes in group "supported_node" can be placed on any node that does not
|
||||
-- have the "airlike" drawtype. Carpets are an example of this type.
|
||||
|
||||
local pairs = pairs
|
||||
local math = math
|
||||
local vector = vector
|
||||
|
||||
local facedir_to_dir = minetest.facedir_to_dir
|
||||
|
@ -22,14 +24,16 @@ local add_item = minetest.add_item
|
|||
-- We need this to do the exact same dropping node handling in our override
|
||||
-- minetest.check_single_for_falling() function as in the builtin function.
|
||||
--
|
||||
---@param p Vector
|
||||
local function drop_attached_node(p)
|
||||
local n = get_node(p)
|
||||
local drops = get_node_drops(n, "")
|
||||
local def = registered_nodes[n.name]
|
||||
|
||||
if def and def.preserve_metadata then
|
||||
local oldmeta = get_meta(p):to_table().fields
|
||||
-- Copy pos and node because the callback can modify them.
|
||||
local pos_copy = vector.new(p)
|
||||
local pos_copy = vector.copy(p)
|
||||
local node_copy = { name = n.name, param1 = n.param1, param2 = n.param2 }
|
||||
local drop_stacks = {}
|
||||
for k, v in pairs(drops) do
|
||||
|
@ -38,16 +42,18 @@ local function drop_attached_node(p)
|
|||
drops = drop_stacks
|
||||
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
|
||||
end
|
||||
|
||||
if def and def.sounds and def.sounds.fall then
|
||||
core.sound_play(def.sounds.fall, {pos = p}, true)
|
||||
minetest.sound_play(def.sounds.fall, { pos = p }, true)
|
||||
end
|
||||
|
||||
remove_node(p)
|
||||
for _, item in pairs(drops) do
|
||||
local pos = {
|
||||
x = p.x + math.random()/2 - 0.25,
|
||||
y = p.y + math.random()/2 - 0.25,
|
||||
z = p.z + math.random()/2 - 0.25,
|
||||
}
|
||||
local pos = vector.offset(p,
|
||||
math.random() / 2 - 0.25,
|
||||
math.random() / 2 - 0.25,
|
||||
math.random() / 2 - 0.25
|
||||
)
|
||||
add_item(pos, item)
|
||||
end
|
||||
end
|
||||
|
@ -90,4 +96,3 @@ function minetest.check_single_for_falling(pos)
|
|||
|
||||
return false
|
||||
end
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ mcl_damage = {
|
|||
drown = {bypasses_armor = true},
|
||||
starve = {bypasses_armor = true, bypasses_magic = true},
|
||||
cactus = {},
|
||||
sweet_berry = {},
|
||||
fall = {bypasses_armor = true},
|
||||
fly_into_wall = {bypasses_armor = true}, -- unused
|
||||
out_of_world = {bypasses_armor = true, bypasses_magic = true, bypasses_invulnerability = true, bypasses_totem = true},
|
||||
|
|
|
@ -130,10 +130,10 @@ local function add_particles(pos, radius)
|
|||
time = 0.125,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minvel = {x = -radius, y = -radius, z = -radius},
|
||||
maxvel = {x = radius, y = radius, z = radius},
|
||||
minacc = vector.new(),
|
||||
maxacc = vector.new(),
|
||||
minvel = vector.new(-radius, -radius, -radius),
|
||||
maxvel = vector.new(radius, radius, radius),
|
||||
minacc = vector.zero(),
|
||||
maxacc = vector.zero(),
|
||||
minexptime = 0.5,
|
||||
maxexptime = 1.0,
|
||||
minsize = radius * 0.5,
|
||||
|
@ -333,7 +333,8 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
end
|
||||
|
||||
if sleep_formspec_doesnt_close_mt53 then
|
||||
minetest.after(0.3, function() -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
||||
minetest.after(0.3,
|
||||
function() -- 0.2 is minimum delay for closing old formspec and open died formspec -- TODO: REMOVE THIS IN THE FUTURE
|
||||
if not obj:is_player() then
|
||||
return
|
||||
end
|
||||
|
@ -396,15 +397,16 @@ local function trace_explode(pos, strength, raydirs, radius, info, direct, sourc
|
|||
-- Update falling nodes
|
||||
for a = 1, #airs do
|
||||
local p = airs[a]
|
||||
check_for_falling({x=p.x, y=p.y+1, z=p.z})
|
||||
check_for_falling(vector.offset(p, 0, 1, 0))
|
||||
end
|
||||
for f = 1, #fires do
|
||||
local p = fires[f]
|
||||
check_for_falling({x=p.x, y=p.y+1, z=p.z})
|
||||
check_for_falling(vector.offset(p, 0, 1, 0))
|
||||
end
|
||||
|
||||
-- Log explosion
|
||||
minetest.log("action", "Explosion at "..pos_to_string(pos).." with strength "..strength.." and radius "..radius)
|
||||
minetest.log("action", "Explosion at " .. pos_to_string(pos) .. " with strength " .. strength .. " and radius " ..
|
||||
radius)
|
||||
end
|
||||
|
||||
-- Create an explosion with strength at pos.
|
||||
|
@ -428,6 +430,11 @@ end
|
|||
-- griefing - If true, the explosion will destroy nodes (default: true)
|
||||
-- grief_protected - If true, the explosion will also destroy nodes which have
|
||||
-- been protected (default: false)
|
||||
---@param pos Vector
|
||||
---@param strength number
|
||||
---@param info {drop_chance: number, max_blast_resistance: number, sound: boolean, particles: boolean, fire: boolean, griefing: boolean, grief_protected: boolean}
|
||||
---@param direct? ObjectRef
|
||||
---@param source? ObjectRef
|
||||
function mcl_explosions.explode(pos, strength, info, direct, source)
|
||||
if info == nil then
|
||||
info = {}
|
||||
|
|
36
mods/ENTITIES/mcl_dripping/README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# mcl_dripping
|
||||
|
||||
Dripping Mod by kddekadenz, modified for MineClone 2 by Wuzzy, NO11 and AFCM
|
||||
|
||||
## Manual
|
||||
|
||||
- drops are generated rarely under solid nodes
|
||||
- they will stay some time at the generated block and than they fall down
|
||||
- when they collide with the ground, a sound is played and they are destroyed
|
||||
|
||||
Water and Lava have builtin drops registered.
|
||||
|
||||
## License
|
||||
|
||||
code & sounds: CC0
|
||||
|
||||
## API
|
||||
|
||||
```lua
|
||||
mcl_dripping.register_drop({
|
||||
-- The group the liquid's nodes belong to
|
||||
liquid = "water",
|
||||
-- The texture used (particles will take a random 2x2 area of it)
|
||||
texture = "default_water_source_animated.png",
|
||||
-- Define particle glow, ranges from `0` to `minetest.LIGHT_MAX`
|
||||
light = 1,
|
||||
-- The nodes (or node group) the particles will spawn under
|
||||
nodes = { "group:opaque", "group:leaves" },
|
||||
-- The sound that will be played then the particle detaches from the roof, see SimpleSoundSpec in lua_api.txt
|
||||
sound = "drippingwater_drip",
|
||||
-- The interval for the ABM to run
|
||||
interval = 60,
|
||||
-- The chance of the ABM
|
||||
chance = 10,
|
||||
})
|
||||
```
|
|
@ -3,53 +3,98 @@
|
|||
-- License of code, textures & sounds: CC0
|
||||
|
||||
local math = math
|
||||
local function make_drop(pos,liquid,sound,interval)
|
||||
|
||||
mcl_dripping = {}
|
||||
|
||||
|
||||
---@param pos Vector
|
||||
---@param liquid string
|
||||
---@param sound SimpleSoundSpec
|
||||
---@param interval integer
|
||||
---@param texture string
|
||||
local function make_drop(pos, liquid, sound, interval, texture)
|
||||
local pt = {
|
||||
velocity = vector.new(0,0,0),
|
||||
velocity = vector.zero(),
|
||||
collision_removal = false,
|
||||
}
|
||||
|
||||
local t = math.random() + math.random(1, interval)
|
||||
|
||||
minetest.after(t, function()
|
||||
local x, z = math.random(-45, 45) / 100, math.random(-45, 45) / 100
|
||||
|
||||
pt.pos = vector.offset(pos, x, -0.52, z)
|
||||
pt.acceleration = vector.new(0,0,0)
|
||||
pt.acceleration = vector.zero()
|
||||
pt.collisiondetection = false
|
||||
pt.expirationtime = t
|
||||
|
||||
pt.texture="[combine:2x2:" .. -math.random(1, 16) .. "," .. -math.random(1, 16) .. "=default_" .. liquid .. "_source_animated.png"
|
||||
pt.texture = "[combine:2x2:" ..
|
||||
-math.random(1, 16) .. "," .. -math.random(1, 16) .. "=" .. texture
|
||||
|
||||
minetest.add_particle(pt)
|
||||
|
||||
minetest.after(t, function()
|
||||
pt.acceleration = vector.new(0, -5, 0)
|
||||
pt.collisiondetection = true
|
||||
pt.expirationtime = math.random() + math.random(1, interval / 2)
|
||||
|
||||
minetest.add_particle(pt)
|
||||
minetest.sound_play({name = "drippingwater_" .. sound .. "drip"}, {pos = pos, gain = 0.5, max_hear_distance = 8}, true)
|
||||
|
||||
minetest.sound_play(sound, { pos = pos, gain = 0.5, max_hear_distance = 8 },
|
||||
true)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local function register_drop(liquid, glow, sound, nodes, interval, chance)
|
||||
---@class mcl_dripping_drop_definition
|
||||
---@field liquid string The group the liquid's nodes belong to
|
||||
---@field texture string The texture used (particles will take a random 2x2 area of it)
|
||||
---@field light integer Define particle glow, ranges from `0` to `minetest.LIGHT_MAX`
|
||||
---@field nodes string[] The nodes (or node group) the particles will spawn under
|
||||
---@field interval integer The interval for the ABM to run
|
||||
---@field chance integer The chance of the ABM
|
||||
---@field sound SimpleSoundSpec The sound that will be played then the particle detaches from the roof
|
||||
|
||||
---@param def mcl_dripping_drop_definition
|
||||
function mcl_dripping.register_drop(def)
|
||||
minetest.register_abm({
|
||||
label = "Create drops",
|
||||
nodenames = nodes,
|
||||
neighbors = {"group:" .. liquid},
|
||||
interval = interval,
|
||||
chance = chance,
|
||||
nodenames = def.nodes,
|
||||
neighbors = { "group:" .. def.liquid },
|
||||
interval = def.interval,
|
||||
chance = def.chance,
|
||||
action = function(pos)
|
||||
local r = math.ceil(interval / 20)
|
||||
local nn=minetest.find_nodes_in_area(vector.offset(pos,-r,0,-r),vector.offset(pos,r,0,r),nodes)
|
||||
local r = math.ceil(def.interval / 20)
|
||||
local nn = minetest.find_nodes_in_area(vector.offset(pos, -r, 0, -r), vector.offset(pos, r, 0, r), def.nodes)
|
||||
--start a bunch of particle cycles to be able to get away
|
||||
--with longer abm cycles
|
||||
table.shuffle(nn)
|
||||
for i = 1, math.random(#nn) do
|
||||
if minetest.get_item_group(minetest.get_node(vector.offset(nn[i], 0, 1, 0)).name, liquid) ~= 0
|
||||
if minetest.get_item_group(minetest.get_node(vector.offset(nn[i], 0, 1, 0)).name, def.liquid) ~= 0
|
||||
and minetest.get_node(vector.offset(nn[i], 0, -1, 0)).name == "air" then
|
||||
make_drop(nn[i],liquid,sound,interval)
|
||||
make_drop(nn[i], def.liquid, def.sound, def.interval, def.texture)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
register_drop("water", 1, "", {"group:opaque", "group:leaves"},60,10)
|
||||
register_drop("lava", math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3), "lava", {"group:opaque"},60,10)
|
||||
mcl_dripping.register_drop({
|
||||
liquid = "water",
|
||||
texture = "default_water_source_animated.png",
|
||||
light = 1,
|
||||
nodes = { "group:opaque", "group:leaves" },
|
||||
sound = "drippingwater_drip",
|
||||
interval = 60,
|
||||
chance = 10,
|
||||
})
|
||||
|
||||
mcl_dripping.register_drop({
|
||||
liquid = "lava",
|
||||
texture = "default_lava_source_animated.png",
|
||||
light = math.max(7, minetest.registered_nodes["mcl_core:lava_source"].light_source - 3),
|
||||
nodes = { "group:opaque" },
|
||||
sound = "drippingwater_lavadrip",
|
||||
interval = 60,
|
||||
chance = 10,
|
||||
})
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
Dripping Mod
|
||||
by kddekadenz
|
||||
|
||||
modified for MineClone 2 by Wuzzy and NO11
|
||||
|
||||
|
||||
Installing instructions:
|
||||
|
||||
1. Copy the mcl_dripping mod folder into games/gamemode/mods
|
||||
|
||||
2. Start game and enjoy :)
|
||||
|
||||
|
||||
Manual:
|
||||
|
||||
-> drops are generated rarely under solid nodes
|
||||
-> they will stay some time at the generated block and than they fall down
|
||||
-> when they collide with the ground, a sound is played and they are destroyed
|
||||
|
||||
|
||||
License:
|
||||
|
||||
code & sounds: CC0
|
||||
|
||||
|
||||
Changelog:
|
||||
|
||||
16.04.2012 - first release
|
||||
28.04.2012 - drops are now 3D; added lava drops; fixed generating of drops (not at edges now)
|
|
@ -13,9 +13,19 @@ local FLOP_HOR_SPEED = 1.5
|
|||
local ENTITY_CRAMMING_MAX = 24
|
||||
local CRAMMING_DAMAGE = 3
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
|
||||
-- Localize
|
||||
local S = minetest.get_translator("mcl_mobs")
|
||||
|
||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
||||
local LOG_MODULE = "[Mobs]"
|
||||
local function mcl_log (message)
|
||||
if LOGGING_ON and message then
|
||||
minetest.log(LOG_MODULE .. " " .. message)
|
||||
end
|
||||
end
|
||||
|
||||
local function shortest_term_of_yaw_rotatoin(self, rot_origin, rot_target, nums)
|
||||
|
||||
if not rot_origin or not rot_target then
|
||||
|
@ -404,21 +414,20 @@ local set_yaw = function(self, yaw, delay, dtime)
|
|||
|
||||
if self.noyaw then return end
|
||||
|
||||
if self._kb_turn then
|
||||
self._turn_to = yaw
|
||||
end
|
||||
|
||||
--clamp our yaw to a 360 range
|
||||
if math.deg(self.object:get_yaw()) > 360 then
|
||||
self.object:set_yaw(math.rad(10))
|
||||
self.object:set_yaw(math.rad(1))
|
||||
elseif math.deg(self.object:get_yaw()) < 0 then
|
||||
self.object:set_yaw(math.rad(350))
|
||||
self.object:set_yaw(math.rad(359))
|
||||
end
|
||||
|
||||
--calculate the shortest way to turn to find our target
|
||||
local target_shortest_path = shortest_term_of_yaw_rotatoin(self, self.object:get_yaw(), yaw, true)
|
||||
|
||||
--turn in the shortest path possible toward our target. if we are attacking, don't dance.
|
||||
if math.abs(target_shortest_path) > 100 and (self.attack and self.attack:get_pos() or self.following and self.following:get_pos()) then
|
||||
if (math.abs(target_shortest_path) > 50 and not self._kb_turn) and (self.attack and self.attack:get_pos() or self.following and self.following:get_pos()) then
|
||||
if self.following then
|
||||
target_shortest_path = shortest_term_of_yaw_rotatoin(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), true)
|
||||
else
|
||||
|
@ -435,8 +444,14 @@ local set_yaw = function(self, yaw, delay, dtime)
|
|||
if math.abs(target_shortest_path) > 280*ddtime then
|
||||
if target_shortest_path > 0 then
|
||||
self.object:set_yaw(self.object:get_yaw()+3.6*ddtime)
|
||||
if self.acc then
|
||||
self.acc=vector.rotate_around_axis(self.acc,vector.new(0,1,0), 3.6*ddtime)
|
||||
end
|
||||
else
|
||||
self.object:set_yaw(self.object:get_yaw()-3.6*ddtime)
|
||||
if self.acc then
|
||||
self.acc=vector.rotate_around_axis(self.acc,vector.new(0,1,0), -3.6*ddtime)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1059,17 +1074,87 @@ local function within_limits(pos, radius)
|
|||
return true
|
||||
end
|
||||
|
||||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
|
||||
|
||||
local can_jump_cliff = function(self)
|
||||
local yaw = self.object:get_yaw()
|
||||
local pos = self.object:get_pos()
|
||||
local v = self.object:get_velocity()
|
||||
|
||||
local v2 = abs(v.x)+abs(v.z)*.833
|
||||
local jump_c_multiplier = 1
|
||||
if v2/self.walk_velocity/2>1 then
|
||||
jump_c_multiplier = v2/self.walk_velocity/2
|
||||
end
|
||||
|
||||
-- where is front
|
||||
local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||
local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||
|
||||
--is there nothing under the block in front? if so jump the gap.
|
||||
local nodLow = node_ok({
|
||||
x = pos.x + dir_x-0.6,
|
||||
y = pos.y - 0.5,
|
||||
z = pos.z + dir_z-0.6
|
||||
}, "air")
|
||||
|
||||
local nodFar = node_ok({
|
||||
x = pos.x + dir_x*2,
|
||||
y = pos.y - 0.5,
|
||||
z = pos.z + dir_z*2
|
||||
}, "air")
|
||||
|
||||
local nodFar2 = node_ok({
|
||||
x = pos.x + dir_x*2.5,
|
||||
y = pos.y - 0.5,
|
||||
z = pos.z + dir_z*2.5
|
||||
}, "air")
|
||||
|
||||
|
||||
if minetest.registered_nodes[nodLow.name]
|
||||
and minetest.registered_nodes[nodLow.name].walkable ~= true
|
||||
|
||||
|
||||
and (minetest.registered_nodes[nodFar.name]
|
||||
and minetest.registered_nodes[nodFar.name].walkable == true
|
||||
|
||||
or minetest.registered_nodes[nodFar2.name]
|
||||
and minetest.registered_nodes[nodFar2.name].walkable == true)
|
||||
|
||||
then
|
||||
--disable fear heigh while we make our jump
|
||||
self._jumping_cliff = true
|
||||
minetest.after(1, function()
|
||||
if self and self.object then
|
||||
self._jumping_cliff = false
|
||||
end
|
||||
end)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- is mob facing a cliff or danger
|
||||
local is_at_cliff_or_danger = function(self)
|
||||
|
||||
if self.fear_height == 0 then -- 0 for no falling protection!
|
||||
if self.fear_height == 0 or can_jump_cliff(self) or self._jumping_cliff or not self.object:get_luaentity() then -- 0 for no falling protection!
|
||||
return false
|
||||
end
|
||||
|
||||
if not self.object:get_luaentity() then
|
||||
return false
|
||||
end
|
||||
local yaw = self.object:get_yaw()
|
||||
local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5)
|
||||
local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5)
|
||||
|
@ -1102,7 +1187,7 @@ end
|
|||
local is_at_water_danger = function(self)
|
||||
|
||||
|
||||
if not self.object:get_luaentity() then
|
||||
if not self.object:get_luaentity() or can_jump_cliff(self) or self._jumping_cliff then
|
||||
return false
|
||||
end
|
||||
local yaw = self.object:get_yaw()
|
||||
|
@ -1136,20 +1221,6 @@ local is_at_water_danger = function(self)
|
|||
end
|
||||
|
||||
|
||||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
|
||||
-- environmental damage (water, lava, fire, light etc.)
|
||||
local do_env_damage = function(self)
|
||||
|
||||
|
@ -1205,6 +1276,7 @@ local do_env_damage = function(self)
|
|||
end
|
||||
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||
if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (sunlight or 0) >= minetest.LIGHT_MAX and dim == "overworld" then
|
||||
if self.armor_list and not self.armor_list.helmet or not self.armor_list or self.armor_list and self.armor_list.helmet and self.armor_list.helmet == "" then
|
||||
if self.ignited_by_sunlight then
|
||||
mcl_burning.set_on_fire(self.object, 10)
|
||||
else
|
||||
|
@ -1212,6 +1284,7 @@ local do_env_damage = function(self)
|
|||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local y_level = self.collisionbox[2]
|
||||
|
||||
|
@ -1415,8 +1488,8 @@ local do_jump = function(self)
|
|||
end
|
||||
|
||||
-- where is front
|
||||
local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+.4
|
||||
local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+.4
|
||||
local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||
local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||
|
||||
-- what is in front of mob?
|
||||
nod = node_ok({
|
||||
|
@ -1433,8 +1506,9 @@ local do_jump = function(self)
|
|||
z = pos.z + dir_z
|
||||
}, "air")
|
||||
|
||||
|
||||
-- we don't attempt to jump if there's a stack of blocks blocking
|
||||
if minetest.registered_nodes[nodTop.name].walkable == true then
|
||||
if minetest.registered_nodes[nodTop.name].walkable == true and not (self.attack and self.state == "attack") then
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -1444,7 +1518,7 @@ local do_jump = function(self)
|
|||
end
|
||||
|
||||
local ndef = minetest.registered_nodes[nod.name]
|
||||
if self.walk_chance == 0 or ndef and ndef.walkable then
|
||||
if self.walk_chance == 0 or ndef and ndef.walkable or can_jump_cliff(self) then
|
||||
|
||||
if minetest.get_item_group(nod.name, "fence") == 0
|
||||
and minetest.get_item_group(nod.name, "fence_gate") == 0
|
||||
|
@ -1454,6 +1528,10 @@ local do_jump = function(self)
|
|||
|
||||
v.y = self.jump_height + 0.1 * 3
|
||||
|
||||
if can_jump_cliff(self) then
|
||||
v=vector.multiply(v, vector.new(2.8,1,2.8))
|
||||
end
|
||||
|
||||
set_animation(self, "jump") -- only when defined
|
||||
|
||||
self.object:set_velocity(v)
|
||||
|
@ -1562,6 +1640,7 @@ end
|
|||
-- find two animals of same type and breed if nearby and horny
|
||||
local breed = function(self)
|
||||
|
||||
--mcl_log("In breed function")
|
||||
-- child takes a long time before growing into adult
|
||||
if self.child == true then
|
||||
|
||||
|
@ -1619,6 +1698,8 @@ local breed = function(self)
|
|||
if self.horny == true
|
||||
and self.hornytimer <= HORNY_TIME then
|
||||
|
||||
mcl_log("In breed function. All good. Do the magic.")
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1)
|
||||
|
@ -1653,6 +1734,8 @@ local breed = function(self)
|
|||
end
|
||||
end
|
||||
|
||||
if canmate then mcl_log("In breed function. Can mate.") end
|
||||
|
||||
if ent
|
||||
and canmate == true
|
||||
and ent.horny == true
|
||||
|
@ -1667,6 +1750,8 @@ local breed = function(self)
|
|||
ent.hornytimer = HORNY_TIME + 1
|
||||
|
||||
-- spawn baby
|
||||
|
||||
|
||||
minetest.after(5, function(parent1, parent2, pos)
|
||||
if not parent1.object:get_luaentity() then
|
||||
return
|
||||
|
@ -2449,7 +2534,7 @@ local function go_to_pos(entity,b)
|
|||
local v = { x = b.x - s.x, z = b.z - s.z }
|
||||
local yaw = (atann(v.z / v.x) + pi / 2) - entity.rotate
|
||||
if b.x > s.x then yaw = yaw + pi end
|
||||
entity.object:set_yaw(yaw)
|
||||
--entity.object:set_yaw(yaw)
|
||||
set_velocity(entity,entity.follow_velocity)
|
||||
mcl_mobs:set_animation(entity, "walk")
|
||||
end
|
||||
|
@ -2463,10 +2548,11 @@ local function check_doors(self)
|
|||
if n.name:find("_b_") then
|
||||
local def = minetest.registered_nodes[n.name]
|
||||
local closed = n.name:find("_b_1")
|
||||
if t < 0.3 or t > 0.8 then
|
||||
if not closed and def.on_rightclick then def.on_rightclick(d,n,self) end
|
||||
else
|
||||
if self.state == PATHFINDING then
|
||||
if closed and def.on_rightclick then def.on_rightclick(d,n,self) end
|
||||
--if not closed and def.on_rightclick then def.on_rightclick(d,n,self) end
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -2477,37 +2563,106 @@ local gowp_etime = 0
|
|||
|
||||
local function check_gowp(self,dtime)
|
||||
gowp_etime = gowp_etime + dtime
|
||||
if gowp_etime < 0.2 then return end
|
||||
if gowp_etime < 0.1 then return end
|
||||
gowp_etime = 0
|
||||
local p = self.object:get_pos()
|
||||
if not p or not self._target then return end
|
||||
if vector.distance(p,self._target) < 1 then
|
||||
|
||||
-- no destination
|
||||
if not p or not self._target then
|
||||
mcl_log("p: ".. tostring(p))
|
||||
mcl_log("self._target: ".. tostring(self._target))
|
||||
return
|
||||
end
|
||||
|
||||
-- arrived at location, finish gowp
|
||||
local distance_to_targ = vector.distance(p,self._target)
|
||||
mcl_log("Distance to targ: ".. tostring(distance_to_targ))
|
||||
if distance_to_targ < 2 then
|
||||
mcl_log("Arrived at _target")
|
||||
self.waypoints = nil
|
||||
self._target = nil
|
||||
self.current_target = nil
|
||||
self.state = "stand"
|
||||
self.order = "stand"
|
||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
if self.callback_arrived then return self.callback_arrived(self) end
|
||||
return true
|
||||
end
|
||||
if self.waypoints and ( not self.current_target or vector.distance(p,self.current_target) < 2 ) then
|
||||
|
||||
-- More pathing to be done
|
||||
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or vector.distance(p,self.current_target) < 2 ) then
|
||||
-- We have waypoints, and no current target, or we're at it. We need a new current_target.
|
||||
|
||||
if not self.current_target then
|
||||
for i, j in pairs (self.waypoints) do
|
||||
mcl_log("Val: ".. tostring(j))
|
||||
end
|
||||
end
|
||||
|
||||
self.current_target = table.remove(self.waypoints, 1)
|
||||
--minetest.log("nextwp:".. tostring(self.current_target) )
|
||||
mcl_log("current target:".. minetest.pos_to_string(self.current_target) )
|
||||
--mcl_log("type:".. type(self.current_target) )
|
||||
go_to_pos(self,self.current_target)
|
||||
return
|
||||
elseif self.current_target then
|
||||
-- No waypoints left, but have current target. Potentially last waypoint to go to.
|
||||
|
||||
mcl_log("self.current_target: ".. minetest.pos_to_string(self.current_target))
|
||||
mcl_log("pos: ".. minetest.pos_to_string(p))
|
||||
go_to_pos(self,self.current_target)
|
||||
-- Do i just delete current_target, and return so we can find final path.
|
||||
else
|
||||
-- Not at target, no current waypoints or current_target. Through the door and should be able to path to target.
|
||||
-- Is a little sensitive and could take 1 - 7 times. A 10 fail count might be a good exit condition.
|
||||
|
||||
mcl_log("We don't have waypoints or a current target. Let's try to path to target")
|
||||
local final_wp = minetest.find_path(p,self._target,150,1,4)
|
||||
if final_wp then
|
||||
mcl_log("We might be able to get to target here.")
|
||||
self.waypoints = final_wp
|
||||
--go_to_pos(self,self._target)
|
||||
else
|
||||
mcl_log("Cannot plot final route to target")
|
||||
end
|
||||
end
|
||||
|
||||
if self.current_target and not minetest.line_of_sight(self.object:get_pos(),self.current_target) then
|
||||
self.waypoints=minetest.find_path(p,self._target,150,1,4)
|
||||
if not self.waypoints then self.state = "walk" end --give up
|
||||
--if self.current_target and not minetest.line_of_sight(self.object:get_pos(),self.current_target) then
|
||||
if self.current_target and (self.waypoints and #self.waypoints == 0) then
|
||||
local updated_p = self.object:get_pos()
|
||||
local distance_to_cur_targ = vector.distance(updated_p,self.current_target)
|
||||
|
||||
mcl_log("Distance to current target: ".. tostring(distance_to_cur_targ))
|
||||
mcl_log("Current p: ".. minetest.pos_to_string(updated_p))
|
||||
--if not minetest.line_of_sight(self.object:get_pos(),self._target) then
|
||||
|
||||
-- 1.6 is good. is 1.9 better? It could fail less, but will it path to door when it isn't after door
|
||||
if distance_to_cur_targ > 1.9 then
|
||||
mcl_log("no LOS to target: ".. minetest.pos_to_string(self.current_target))
|
||||
go_to_pos(self,self._current_target)
|
||||
else
|
||||
mcl_log("Let's go to target: ".. minetest.pos_to_string(self.current_target))
|
||||
self.current_target = nil
|
||||
--go_to_pos(self,self._target)
|
||||
self.waypoints=minetest.find_path(updated_p,self._target,150,1,4)
|
||||
--if not self.waypoints then
|
||||
--mcl_log("Give up ")
|
||||
--self.state = "walk"
|
||||
--end --give up
|
||||
end
|
||||
|
||||
--self.waypoints=minetest.find_path(p,self._target,150,1,4)
|
||||
--if not self.waypoints then
|
||||
--mcl_log("Give up ")
|
||||
--self.state = "walk"
|
||||
--end --give up
|
||||
--self.current_target = nil
|
||||
return
|
||||
end
|
||||
if not self.current_target then
|
||||
--minetest.log("no path")
|
||||
self.state = "walk"
|
||||
end
|
||||
--if not self.current_target then
|
||||
--mcl_log("no path. Give up")
|
||||
--self.state = "walk"
|
||||
--end
|
||||
end
|
||||
|
||||
-- execute current state (stand, walk, run, attacks)
|
||||
|
@ -2556,9 +2711,9 @@ local do_states = function(self, dtime)
|
|||
end
|
||||
|
||||
-- npc's ordered to stand stay standing
|
||||
if self.type ~= "npc"
|
||||
or self.order ~= "stand" then
|
||||
if self.order == "stand" or self.order == "sleep" or self.order == "work" then
|
||||
|
||||
else
|
||||
if self.walk_chance ~= 0
|
||||
and self.facing_fence ~= true
|
||||
and random(1, 100) <= self.walk_chance
|
||||
|
@ -2570,7 +2725,7 @@ local do_states = function(self, dtime)
|
|||
end
|
||||
end
|
||||
|
||||
elseif self.state == "gowp" then
|
||||
elseif self.state == PATHFINDING then
|
||||
check_gowp(self,dtime)
|
||||
|
||||
elseif self.state == "walk" then
|
||||
|
@ -3025,7 +3180,7 @@ local do_states = function(self, dtime)
|
|||
|
||||
if self.shoot_interval
|
||||
and self.timer > self.shoot_interval
|
||||
and not minetest.raycast(p, self.attack:get_pos(), false, false):next()
|
||||
and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next()
|
||||
and random(1, 100) <= 60 then
|
||||
|
||||
self.timer = 0
|
||||
|
@ -3071,6 +3226,8 @@ local do_states = function(self, dtime)
|
|||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3085,23 +3242,50 @@ local plane_adjacents = {
|
|||
|
||||
local gopath_last = os.time()
|
||||
function mcl_mobs:gopath(self,target,callback_arrived)
|
||||
if os.time() - gopath_last < 15 then return end
|
||||
if self.state == PATHFINDING then mcl_log("Already set as gowp, don't set another path until done.") return end
|
||||
|
||||
if os.time() - gopath_last < 15 then
|
||||
mcl_log("Not ready to path yet")
|
||||
return
|
||||
end
|
||||
gopath_last = os.time()
|
||||
--minetest.log("gowp")
|
||||
|
||||
self.order = nil
|
||||
|
||||
mcl_log("gowp target: " .. minetest.pos_to_string(target))
|
||||
local p = self.object:get_pos()
|
||||
local t = vector.offset(target,0,1,0)
|
||||
local wp = minetest.find_path(p,t,150,1,4)
|
||||
|
||||
--Path to door first
|
||||
if not wp then
|
||||
--mcl_log("gowp. no wp. Look for door")
|
||||
local d = minetest.find_node_near(target,16,{"group:door"})
|
||||
if d then
|
||||
--mcl_log("Found a door near")
|
||||
for _,v in pairs(plane_adjacents) do
|
||||
local pos = vector.add(d,v)
|
||||
|
||||
local n = minetest.get_node(pos)
|
||||
if n.name == "air" then
|
||||
wp = minetest.find_path(p,pos,150,1,4)
|
||||
if wp then break end
|
||||
if wp then
|
||||
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos))
|
||||
local other_side_of_door = vector.add(d,-v)
|
||||
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
||||
table.insert(wp, other_side_of_door)
|
||||
break
|
||||
|
||||
else
|
||||
--mcl_log("This block next to door doesn't work.")
|
||||
end
|
||||
else
|
||||
--mcl_log("Block is not air, it is: ".. n.name)
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
mcl_log("No door found")
|
||||
end
|
||||
end
|
||||
if wp and #wp > 0 then
|
||||
|
@ -3109,7 +3293,7 @@ function mcl_mobs:gopath(self,target,callback_arrived)
|
|||
self.callback_arrived = callback_arrived
|
||||
table.remove(wp,1)
|
||||
self.waypoints = wp
|
||||
self.state = "gowp"
|
||||
self.state = PATHFINDING
|
||||
return true
|
||||
else
|
||||
self.state = "walk"
|
||||
|
@ -3125,18 +3309,89 @@ local function player_near(pos)
|
|||
end
|
||||
end
|
||||
|
||||
local function get_armor_texture(armor_name)
|
||||
if armor_name == "" then
|
||||
return ""
|
||||
end
|
||||
if armor_name=="blank.png" then
|
||||
return "blank.png"
|
||||
end
|
||||
local seperator = string.find(armor_name, ":")
|
||||
return "mcl_armor_"..string.sub(armor_name, seperator+1, -1)..".png^"
|
||||
end
|
||||
|
||||
local function set_armor_texture(self)
|
||||
if self.armor_list then
|
||||
local chestplate=minetest.registered_items[self.armor_list.chestplate] or {name=""}
|
||||
local boots=minetest.registered_items[self.armor_list.boots] or {name=""}
|
||||
local leggings=minetest.registered_items[self.armor_list.leggings] or {name=""}
|
||||
local helmet=minetest.registered_items[self.armor_list.helmet] or {name=""}
|
||||
|
||||
if helmet.name=="" and chestplate.name=="" and leggings.name=="" and boots.name=="" then
|
||||
helmet={name="blank.png"}
|
||||
end
|
||||
local texture = get_armor_texture(chestplate.name)..get_armor_texture(helmet.name)..get_armor_texture(boots.name)..get_armor_texture(leggings.name)
|
||||
if string.sub(texture, -1,-1) == "^" then
|
||||
texture=string.sub(texture,1,-2)
|
||||
end
|
||||
if self.textures[self.wears_armor] then
|
||||
self.textures[self.wears_armor]=texture
|
||||
end
|
||||
self.object:set_properties({textures=self.textures})
|
||||
|
||||
local armor_
|
||||
if type(self.armor) == "table" then
|
||||
armor_ = table.copy(self.armor)
|
||||
armor_.immortal = 1
|
||||
else
|
||||
armor_ = {immortal=1, fleshy = self.armor}
|
||||
end
|
||||
|
||||
for _,item in pairs(self.armor_list) do
|
||||
if not item then return end
|
||||
if type(minetest.get_item_group(item, "mcl_armor_points")) == "number" then
|
||||
armor_.fleshy=armor_.fleshy-(minetest.get_item_group(item, "mcl_armor_points")*3.5)
|
||||
end
|
||||
end
|
||||
self.object:set_armor_groups(armor_)
|
||||
end
|
||||
end
|
||||
|
||||
local function check_item_pickup(self)
|
||||
if self.pick_up and #self.pick_up > 0 then
|
||||
if self.pick_up and #self.pick_up > 0 or self.wears_armor then
|
||||
local p = self.object:get_pos()
|
||||
for _,o in pairs(minetest.get_objects_inside_radius(p,2)) do
|
||||
local l=o:get_luaentity()
|
||||
if l and l.name == "__builtin:item" then
|
||||
if not player_near(p) and l.itemstring:find("mcl_armor") and self.wears_armor then
|
||||
local armor_type
|
||||
if l.itemstring:find("chestplate") then
|
||||
armor_type = "chestplate"
|
||||
elseif l.itemstring:find("boots") then
|
||||
armor_type = "boots"
|
||||
elseif l.itemstring:find("leggings") then
|
||||
armor_type = "leggings"
|
||||
elseif l.itemstring:find("helmet") then
|
||||
armor_type = "helmet"
|
||||
end
|
||||
if not armor_type then
|
||||
return
|
||||
end
|
||||
if not self.armor_list then
|
||||
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
||||
elseif self.armor_list[armor_type] and self.armor_list[armor_type] ~= "" then
|
||||
return
|
||||
end
|
||||
self.armor_list[armor_type]=ItemStack(l.itemstring):get_name()
|
||||
o:remove()
|
||||
end
|
||||
if self.pick_up then
|
||||
for k,v in pairs(self.pick_up) do
|
||||
if not player_near(p) and self.on_pick_up and l.itemstring:find(v) then
|
||||
local r = self.on_pick_up(self,l)
|
||||
if r and r:get_count() > 0 then
|
||||
if r and r.is_empty and not r:is_empty() then
|
||||
l.itemstring = r:to_string()
|
||||
else
|
||||
elseif r and r.is_empty and r:is_empty() then
|
||||
o:remove()
|
||||
end
|
||||
end
|
||||
|
@ -3145,6 +3400,7 @@ local function check_item_pickup(self)
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local check_herd_timer = 0
|
||||
local function check_herd(self,dtime)
|
||||
|
@ -3498,7 +3754,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
|||
elseif luaentity and luaentity._knockback then
|
||||
kb = kb + luaentity._knockback
|
||||
end
|
||||
--self._kb_turn = false
|
||||
self._kb_turn = true
|
||||
self._turn_to=self.object:get_yaw()-1.57
|
||||
self.frame_speed_multiplier=2.3
|
||||
if self.animation.run_end then
|
||||
|
@ -3509,7 +3765,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
|||
minetest.after(0.2, function()
|
||||
if self and self.object then
|
||||
self.frame_speed_multiplier=1
|
||||
self._kb_turn = true
|
||||
self._kb_turn = false
|
||||
end
|
||||
end)
|
||||
self.object:add_velocity({
|
||||
|
@ -3525,7 +3781,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
|||
-- if skittish then run away
|
||||
if hitter and is_player and hitter:get_pos() and not die and self.runaway == true and self.state ~= "flop" then
|
||||
|
||||
yaw = set_yaw(self, minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||
local yaw = set_yaw(self, minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||
minetest.after(0.2,function()
|
||||
if self and self.object and self.object:get_pos() and hitter and is_player and hitter:get_pos() then
|
||||
yaw = set_yaw(self, minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||
|
@ -3814,9 +4070,16 @@ local mob_activate = function(self, staticdata, def, dtime)
|
|||
self.on_spawn_run = true -- if true, set flag to run once only
|
||||
end
|
||||
end
|
||||
if not self._run_armor_init then
|
||||
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
||||
set_armor_texture(self)
|
||||
self._run_armor_init = true
|
||||
end
|
||||
|
||||
|
||||
-- run after_activate
|
||||
if def.after_activate then
|
||||
|
||||
def.after_activate(self, staticdata, def, dtime)
|
||||
end
|
||||
end
|
||||
|
@ -3931,7 +4194,11 @@ local mob_step = function(self, dtime)
|
|||
if not self.animation.walk_speed then
|
||||
self.animation.walk_speed = 25
|
||||
end
|
||||
if abs(v.x)+abs(v.z) > 0.5 then
|
||||
self.object:set_animation_frame_speed((v2/math.max(1,self.run_velocity))*self.animation.walk_speed*self.frame_speed_multiplier)
|
||||
else
|
||||
self.object:set_animation_frame_speed(25)
|
||||
end
|
||||
end
|
||||
|
||||
--set_speed
|
||||
|
@ -3989,12 +4256,13 @@ local mob_step = function(self, dtime)
|
|||
-- end rotation
|
||||
|
||||
if self.head_swivel and type(self.head_swivel) == "string" then
|
||||
local final_rotation = vector.new(0,0,0)
|
||||
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
|
||||
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 10)) do
|
||||
if obj:is_player() and not self.attack or obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity() then
|
||||
if not self._locked_object then
|
||||
if math.random(5000/self.curiosity) == 1 then
|
||||
if math.random(5000/self.curiosity) == 1 or vector.distance(pos,obj:get_pos())<4 and obj:is_player() then
|
||||
self._locked_object = obj
|
||||
end
|
||||
else
|
||||
|
@ -4005,8 +4273,8 @@ local mob_step = function(self, dtime)
|
|||
end
|
||||
end
|
||||
|
||||
if self.attack then
|
||||
self._locked_object = self.attack
|
||||
if self.attack or self.following then
|
||||
self._locked_object = self.attack or self.following
|
||||
end
|
||||
|
||||
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
|
||||
|
@ -4026,30 +4294,36 @@ local mob_step = function(self, dtime)
|
|||
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
|
||||
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset
|
||||
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier
|
||||
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.type == "monster") then
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.multiply(oldr, 0.9))
|
||||
elseif self.attack and self.type == "monster" then
|
||||
|
||||
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then
|
||||
final_rotation = vector.multiply(oldr, 0.9)
|
||||
elseif self.attack and self.state == "attack" and not self.runaway then
|
||||
if self.head_yaw == "y" then
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.new(mob_pitch, mob_yaw, 0))
|
||||
final_rotation = vector.new(mob_pitch, mob_yaw, 0)
|
||||
elseif self.head_yaw == "z" then
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.new(mob_pitch, 0, -mob_yaw))
|
||||
final_rotation = vector.new(mob_pitch, 0, -mob_yaw)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if self.head_yaw == "y" then
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0))
|
||||
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0)
|
||||
elseif self.head_yaw == "z" then
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3))
|
||||
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.multiply(oldr, 0.9))
|
||||
final_rotation = vector.multiply(oldr, 0.9)
|
||||
else
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), vector.new(0,0,0))
|
||||
final_rotation = vector.new(0,0,0)
|
||||
end
|
||||
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horrizonatal_head_height), final_rotation)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- run custom function (defined in mob lua file)
|
||||
if self.do_custom then
|
||||
|
||||
|
@ -4070,7 +4344,7 @@ local mob_step = function(self, dtime)
|
|||
-- attack timer
|
||||
self.timer = self.timer + dtime
|
||||
|
||||
if self.state ~= "attack" and self.state ~= "gowp" then
|
||||
if self.state ~= "attack" and self.state ~= PATHFINDING then
|
||||
if self.timer < 1 then
|
||||
return
|
||||
end
|
||||
|
@ -4121,6 +4395,8 @@ local mob_step = function(self, dtime)
|
|||
|
||||
do_jump(self)
|
||||
|
||||
set_armor_texture(self)
|
||||
|
||||
runaway_from(self)
|
||||
|
||||
if is_at_water_danger(self) and self.state ~= "attack" then
|
||||
|
@ -4277,6 +4553,7 @@ minetest.register_entity(name, {
|
|||
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
||||
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
||||
horrizonatal_head_height = def.horrizonatal_head_height or 0,
|
||||
wears_armor = def.wears_armor, -- a number value used to index texture slot for armor
|
||||
stepheight = def.stepheight or 0.6,
|
||||
name = name,
|
||||
description = def.description,
|
||||
|
@ -4330,6 +4607,7 @@ minetest.register_entity(name, {
|
|||
nofollow = def.nofollow,
|
||||
can_open_doors = def.can_open_doors,
|
||||
jump = def.jump ~= false,
|
||||
automatic_face_movement_max_rotation_per_sec = 300,
|
||||
walk_chance = def.walk_chance or 50,
|
||||
attacks_monsters = def.attacks_monsters or false,
|
||||
group_attack = def.group_attack or false,
|
||||
|
@ -4439,6 +4717,7 @@ minetest.register_entity(name, {
|
|||
self.object:set_properties({
|
||||
collide_with_objects = false,
|
||||
})
|
||||
|
||||
return mob_activate(self, staticdata, def, dtime)
|
||||
end,
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ local cow_def = {
|
|||
description = S("Cow"),
|
||||
type = "animal",
|
||||
spawn_class = "passive",
|
||||
passive = true,
|
||||
hp_min = 10,
|
||||
hp_max = 10,
|
||||
xp_min = 1,
|
||||
|
|
|
@ -12,6 +12,7 @@ local S = minetest.get_translator("mobs_mc")
|
|||
|
||||
-- Spider by AspireMint (fishyWET (CC-BY-SA 3.0 license for texture)
|
||||
minetest.register_entity("mobs_mc:spider_eyes", {
|
||||
pointable = false,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_spider.b3d",
|
||||
visual_size = {x=1.01/3, y=1.01/3},
|
||||
|
@ -121,8 +122,21 @@ cave_spider.textures = { {"mobs_mc_cave_spider.png^(mobs_mc_spider_eyes.png^[mak
|
|||
cave_spider.damage = 3 -- damage increased to undo non-existing poison
|
||||
cave_spider.hp_min = 1
|
||||
cave_spider.hp_max = 12
|
||||
cave_spider.collisionbox = {-0.35, -0.01, -0.35, 0.35, 0.49, 0.35}
|
||||
cave_spider.visual_size = {x=1.66666, y=1.5}
|
||||
cave_spider.collisionbox = {-0.35, -0.01, -0.35, 0.35, 0.46, 0.35}
|
||||
cave_spider.visual_size = {x=0.55,y=0.5}
|
||||
cave_spider.on_spawn = function(self)
|
||||
self.object:set_properties({visual_size={x=0.55,y=0.5}})
|
||||
local spider_eyes=false
|
||||
for n = 1, #self.object:get_children() do
|
||||
local obj = self.object:get_children()[n]
|
||||
if obj:get_luaentity() and self.object:get_luaentity().name == "mobs_mc:spider_eyes" then
|
||||
spider_eyes = true
|
||||
end
|
||||
end
|
||||
if not spider_eyes then
|
||||
minetest.add_entity(self.object:get_pos(), "mobs_mc:spider_eyes"):set_attach(self.object, "body.head", vector.new(0,-0.98,2), vector.new(90,180,180))
|
||||
end
|
||||
end
|
||||
cave_spider.walk_velocity = 1.3
|
||||
cave_spider.run_velocity = 3.2
|
||||
cave_spider.sounds = table.copy(spider.sounds)
|
||||
|
|
BIN
mods/ENTITIES/mobs_mc/textures/mobs_mc_horse_zombie.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
|
@ -15,6 +15,8 @@
|
|||
-- TODO: Internal inventory, trade with other villagers
|
||||
-- TODO: Schedule stuff (work,sleep,father)
|
||||
|
||||
local weather_mod = minetest.get_modpath("mcl_weather")
|
||||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
local N = function(s) return s end
|
||||
local F = minetest.formspec_escape
|
||||
|
@ -28,6 +30,8 @@ local DEFAULT_WALK_CHANCE = 33 -- chance to walk in percent, if no player nearby
|
|||
local PLAYER_SCAN_INTERVAL = 5 -- every X seconds, villager looks for players nearby
|
||||
local PLAYER_SCAN_RADIUS = 4 -- scan radius for looking for nearby players
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
|
||||
--[=======[ TRADING ]=======]
|
||||
|
||||
-- LIST OF VILLAGER PROFESSIONS AND TRADES
|
||||
|
@ -40,6 +44,14 @@ local PLAYER_SCAN_RADIUS = 4 -- scan radius for looking for nearby players
|
|||
-- these items should be implemented as single items, then everything
|
||||
-- will be much easier.
|
||||
|
||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
||||
local LOG_MODULE = "[Mobs - Villager]"
|
||||
local function mcl_log (message)
|
||||
if LOGGING_ON and message then
|
||||
minetest.log(LOG_MODULE .. " " .. message)
|
||||
end
|
||||
end
|
||||
|
||||
local COMPASS = "mcl_compass:compass"
|
||||
if minetest.registered_aliases[COMPASS] then
|
||||
COMPASS = minetest.registered_aliases[COMPASS]
|
||||
|
@ -492,15 +504,34 @@ local professions = {
|
|||
}
|
||||
}
|
||||
|
||||
local WORK = "work"
|
||||
local SLEEP = "sleep"
|
||||
|
||||
local profession_names = {}
|
||||
for id, _ in pairs(professions) do
|
||||
table.insert(profession_names, id)
|
||||
end
|
||||
|
||||
local jobsites={}
|
||||
for _,n in pairs(profession_names) do
|
||||
table.insert(jobsites,professions[n].jobsite)
|
||||
local function populate_jobsites (profession)
|
||||
if profession then
|
||||
mcl_log("populate_jobsites: ".. tostring(profession))
|
||||
end
|
||||
local jobsites_requested={}
|
||||
for _,n in pairs(profession_names) do
|
||||
if n and professions[n].jobsite then
|
||||
if not profession or (profession and profession == n) then
|
||||
--minetest.log("populate_jobsites. Adding: ".. tostring(n))
|
||||
table.insert(jobsites_requested,professions[n].jobsite)
|
||||
end
|
||||
end
|
||||
end
|
||||
return jobsites_requested
|
||||
end
|
||||
|
||||
jobsites = populate_jobsites()
|
||||
|
||||
local spawnable_bed={}
|
||||
table.insert(spawnable_bed, "mcl_beds:bed_red_bottom")
|
||||
|
||||
local function stand_still(self)
|
||||
self.walk_chance = 0
|
||||
|
@ -521,6 +552,11 @@ end
|
|||
|
||||
local function get_badge_textures(self)
|
||||
local t = professions[self._profession].texture
|
||||
if self._profession == "unemployed" then
|
||||
t = professions[self._profession].textures -- ideally both scenarios should be textures with a list containing 1 or multiple
|
||||
--mcl_log("t: " .. tostring(t))
|
||||
end
|
||||
|
||||
if self._profession == "unemployed" or self._profession == "nitwit" then return t end
|
||||
local tier = self._max_trade_tier or 1
|
||||
return {
|
||||
|
@ -529,27 +565,137 @@ local function get_badge_textures(self)
|
|||
end
|
||||
|
||||
local function set_textures(self)
|
||||
self.object:set_properties({textures=get_badge_textures(self)})
|
||||
local badge_textures = get_badge_textures(self)
|
||||
--mcl_log("Setting textures: " .. tostring(badge_textures))
|
||||
self.object:set_properties({textures=badge_textures})
|
||||
end
|
||||
|
||||
local function go_home(entity)
|
||||
entity.state = "go_home"
|
||||
function get_activity(tod)
|
||||
-- night hours = tod > 18541 or tod < 5458
|
||||
if not tod then
|
||||
tod = minetest.get_timeofday()
|
||||
end
|
||||
tod = ( tod * 24000 ) % 24000
|
||||
|
||||
|
||||
local lunch_start = 12000
|
||||
local lunch_end = 13500
|
||||
local work_start = 8500
|
||||
local work_end = 16500
|
||||
|
||||
|
||||
local activity = nil
|
||||
if (tod > work_start and tod < lunch_start) or (tod > lunch_end and tod < work_end) then
|
||||
activity = WORK
|
||||
elseif mcl_beds.is_night() then
|
||||
activity = SLEEP
|
||||
elseif tod > lunch_start and tod < lunch_end then
|
||||
activity = "lunch"
|
||||
else
|
||||
activity = "chill"
|
||||
end
|
||||
mcl_log("Time is " .. tod ..". Activity is: ".. activity)
|
||||
return activity
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function go_home(entity, sleep)
|
||||
local b = entity._bed
|
||||
if not b then return end
|
||||
mcl_mobs:gopath(entity,b,function(entity,b)
|
||||
if not b then
|
||||
return
|
||||
end
|
||||
|
||||
local bed_node = minetest.get_node(b)
|
||||
if not bed_node then
|
||||
entity._bed = nil
|
||||
mcl_log("Cannot find bed. Unset it")
|
||||
return
|
||||
end
|
||||
|
||||
if vector.distance(entity.object:get_pos(),b) < 2 then
|
||||
entity.state = "stand"
|
||||
set_velocity(entity,0)
|
||||
entity.object:set_pos(b)
|
||||
local n=minetest.get_node(b)
|
||||
if n and n.name ~= "mcl_beds:bed_red_bottom" then
|
||||
entity._bed=nil --the stormtroopers have killed uncle owen
|
||||
if sleep then
|
||||
entity.order = SLEEP
|
||||
mcl_log("Sleep time!")
|
||||
end
|
||||
else
|
||||
if sleep and entity.order == SLEEP then
|
||||
entity.order = nil
|
||||
return
|
||||
end
|
||||
|
||||
mcl_mobs:gopath(entity,b,function(entity,b)
|
||||
local b = entity._bed
|
||||
|
||||
if not b then
|
||||
--minetest.log("NO BED, problem")
|
||||
return false
|
||||
end
|
||||
|
||||
if not minetest.get_node(b) then
|
||||
--minetest.log("NO BED NODE, problem")
|
||||
return false
|
||||
end
|
||||
|
||||
if vector.distance(entity.object:get_pos(),b) < 2 then
|
||||
--minetest.log("Managed to walk home callback!")
|
||||
return true
|
||||
else
|
||||
--minetest.log("Need to walk to home")
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local function check_bed (entity)
|
||||
local b = entity._bed
|
||||
if not b then
|
||||
--minetest.log("No bed set on villager")
|
||||
return false
|
||||
end
|
||||
|
||||
local n = minetest.get_node(b)
|
||||
if n and n.name ~= "mcl_beds:bed_red_bottom" then
|
||||
mcl_log("Where did my bed go?!")
|
||||
entity._bed = nil --the stormtroopers have killed uncle owen
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function take_bed (entity)
|
||||
if not entity then return end
|
||||
|
||||
local p = entity.object:get_pos()
|
||||
local nn = minetest.find_nodes_in_area(vector.offset(p,-48,-48,-48), vector.offset(p,48,48,48), spawnable_bed)
|
||||
|
||||
for _,n in pairs(nn) do
|
||||
local m=minetest.get_meta(n)
|
||||
--mcl_log("Bed owner: ".. m:get_string("villager"))
|
||||
if m:get_string("villager") == "" and not (entity.state == PATHFINDING) then
|
||||
mcl_log("Can we path to bed: "..minetest.pos_to_string(n) )
|
||||
local gp = mcl_mobs:gopath(entity,n,function(self)
|
||||
if self then
|
||||
self.order = "sleep"
|
||||
mcl_log("Sleepy time" )
|
||||
else
|
||||
mcl_log("Can't sleep, no self in the callback" )
|
||||
end
|
||||
end)
|
||||
if gp then
|
||||
mcl_log("Nice bed. I'll defintely take it as I can path")
|
||||
m:set_string("villager", entity._id)
|
||||
entity._bed = n
|
||||
break
|
||||
else
|
||||
mcl_log("Awww. I can't find my bed.")
|
||||
end
|
||||
else
|
||||
mcl_log("Currently gowp, or it's taken: ".. m:get_string("villager"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function has_golem(pos)
|
||||
local r = false
|
||||
|
@ -596,6 +742,43 @@ local function check_summon(self,dtime)
|
|||
self._summon_timer = self._summon_timer + dtime
|
||||
end
|
||||
|
||||
local function has_traded (self)
|
||||
--mcl_log("Checking name: " .. self._trades)
|
||||
|
||||
if not self._trades then
|
||||
mcl_log("No trades set. has_traded is false")
|
||||
return false
|
||||
end
|
||||
|
||||
local cur_trades_tab = minetest.deserialize(self._trades)
|
||||
|
||||
if cur_trades_tab and type(cur_trades_tab) == "table" then
|
||||
for trader, trades in pairs(cur_trades_tab) do
|
||||
--mcl_log("Current record: ".. tostring(trader))
|
||||
--for tr3, tr4 in pairs (tab_val) do
|
||||
--mcl_log("Key: ".. tostring(tr3))
|
||||
--mcl_log("Value: ".. tostring(tr4))
|
||||
--end
|
||||
--mcl_log("traded once: ".. tostring(trades.traded_once))
|
||||
|
||||
if trades.traded_once then
|
||||
mcl_log("Villager has traded before. Returning true")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
mcl_log("Villager has not traded before")
|
||||
return false
|
||||
end
|
||||
|
||||
local function unlock_trades (self)
|
||||
if self then
|
||||
--mcl_log("We should now try to unlock trades")
|
||||
else
|
||||
mcl_log("Missing self")
|
||||
end
|
||||
end
|
||||
|
||||
----- JOBSITE LOGIC
|
||||
local function get_profession_by_jobsite(js)
|
||||
for k,v in pairs(professions) do
|
||||
|
@ -608,47 +791,209 @@ local function employ(self,jobsite_pos)
|
|||
local m = minetest.get_meta(jobsite_pos)
|
||||
local p = get_profession_by_jobsite(n.name)
|
||||
if p and m:get_string("villager") == "" then
|
||||
self._profession=p
|
||||
mcl_log("Taking this jobsite")
|
||||
|
||||
m:set_string("villager",self._id)
|
||||
self._jobsite = jobsite_pos
|
||||
|
||||
if not has_traded(self) then
|
||||
self._profession=p
|
||||
set_textures(self)
|
||||
end
|
||||
return true
|
||||
else
|
||||
mcl_log("I can not steal someone's job!")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function look_for_job(self, requested_jobsites)
|
||||
|
||||
--if self.last_jobhunt and os.time() - self.last_jobhunt < 15 then
|
||||
-- mcl_log("Is time less than 40?" .. tostring(os.time() - self.last_jobhunt))
|
||||
-- return
|
||||
--end
|
||||
--self.last_jobhunt = os.time() + math.random(0,30)
|
||||
|
||||
mcl_log("Looking for jobs")
|
||||
|
||||
local looking_for_type = jobsites
|
||||
if requested_jobsites then
|
||||
--mcl_log("Looking for jobs of my type: " .. tostring(requested_jobsites))
|
||||
looking_for_type = requested_jobsites
|
||||
else
|
||||
mcl_log("Looking for any job type")
|
||||
end
|
||||
|
||||
local p = self.object:get_pos()
|
||||
local nn = minetest.find_nodes_in_area(vector.offset(p,-48,-48,-48),vector.offset(p,48,48,48), looking_for_type)
|
||||
|
||||
for _,n in pairs(nn) do
|
||||
local m = minetest.get_meta(n)
|
||||
--mcl_log("Job owner: ".. m:get_string("villager"))
|
||||
|
||||
if m:get_string("villager") == "" then
|
||||
mcl_log("It's a free job for me (".. minetest.pos_to_string(p) .. ")! I might be interested: "..minetest.pos_to_string(n) )
|
||||
|
||||
local gp = mcl_mobs:gopath(self,n,function(self)
|
||||
mcl_log("Arrived at block callback")
|
||||
if self and self.state == "stand" then
|
||||
self.order = WORK
|
||||
else
|
||||
mcl_log("no self. passing param to callback failed")
|
||||
end
|
||||
|
||||
end)
|
||||
if gp then
|
||||
if n then
|
||||
mcl_log("We can path to this block.. " .. tostring(n))
|
||||
end
|
||||
return n
|
||||
else
|
||||
mcl_log("We could not path to block or it's not ready to path yet.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function get_a_job(self)
|
||||
mcl_log("I'm unemployed or lost my job block and have traded. Can I get a job?")
|
||||
--self.order = JOB_HUNTING
|
||||
|
||||
local requested_jobsites = jobsites
|
||||
if has_traded (self) then
|
||||
mcl_log("Has traded so look for job of my type")
|
||||
requested_jobsites = populate_jobsites(self._profession)
|
||||
-- Only pass in my jobsite to two functions here
|
||||
else
|
||||
mcl_log("Has not traded")
|
||||
end
|
||||
|
||||
|
||||
local p = self.object:get_pos()
|
||||
|
||||
local n = minetest.find_node_near(p,1,requested_jobsites)
|
||||
|
||||
--Ideally should check for closest available. It'll make pathing easier.
|
||||
--local n = look_for_job(self)
|
||||
|
||||
if not n then
|
||||
--mcl_log("Job hunt failed. Could not find block I have walked to")
|
||||
end
|
||||
|
||||
if n and employ(self,n) then return true end
|
||||
|
||||
if self.state ~= PATHFINDING then
|
||||
mcl_log("Nothing near. Need to look for a job")
|
||||
look_for_job(self, requested_jobsites)
|
||||
end
|
||||
end
|
||||
|
||||
local function retrieve_my_jobsite (self)
|
||||
if not self or not self._jobsite then
|
||||
--mcl_log("find_jobsite. Invalid params")
|
||||
return
|
||||
end
|
||||
|
||||
local n = mcl_vars.get_node(self._jobsite)
|
||||
local m = minetest.get_meta(self._jobsite)
|
||||
if m:get_string("villager") == self._id then
|
||||
--mcl_log("find_jobsite. is my job.")
|
||||
return n
|
||||
else
|
||||
--mcl_log("find_jobsite. Not my job")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local function validate_jobsite(self)
|
||||
if self._profession == "unemployed" then return false end
|
||||
|
||||
if not retrieve_my_jobsite (self) then
|
||||
self._jobsite = nil
|
||||
if self.order == WORK then
|
||||
self.order = nil
|
||||
end
|
||||
|
||||
if not has_traded(self) then
|
||||
mcl_log("Cannot retrieve my jobsite. I am now unemployed.")
|
||||
self._profession = "unemployed"
|
||||
self._trades = nil
|
||||
set_textures(self)
|
||||
else
|
||||
mcl_log("Cannot retrieve my jobsite but I've traded so only remove jobsite.")
|
||||
end
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function look_for_job(self)
|
||||
if self.last_jobhunt and os.time() - self.last_jobhunt < 360 then return end
|
||||
self.last_jobhunt = os.time() + math.random(0,60)
|
||||
local p = self.object:get_pos()
|
||||
local nn = minetest.find_nodes_in_area(vector.offset(p,-48,-48,-48),vector.offset(p,48,48,48),jobsites)
|
||||
for _,n in pairs(nn) do
|
||||
local m=minetest.get_meta(n)
|
||||
if m:get_string("villager") == "" then
|
||||
--minetest.log("goingt to jobsite "..minetest.pos_to_string(n) )
|
||||
local gp = mcl_mobs:gopath(self,n,function()
|
||||
--minetest.log("arrived jobsite "..minetest.pos_to_string(n) )
|
||||
end)
|
||||
if gp then return end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_a_job(self)
|
||||
|
||||
local function do_work (self)
|
||||
if self.child then return end
|
||||
local p = self.object:get_pos()
|
||||
local n = minetest.find_node_near(p,1,jobsites)
|
||||
if n and employ(self,n) then return true end
|
||||
if self.state ~= "gowp" then look_for_job(self) end
|
||||
--mcl_log("Time for work")
|
||||
|
||||
-- Don't try if looking_for_work, or gowp possibly
|
||||
if validate_jobsite(self) then
|
||||
--mcl_log("My jobsite is valid. Do i need to travel?")
|
||||
|
||||
local jobsite2 = retrieve_my_jobsite (self)
|
||||
local jobsite = self._jobsite
|
||||
|
||||
if self and jobsite2 and self._jobsite then
|
||||
|
||||
--mcl_log("Villager: ".. minetest.pos_to_string(self.object:get_pos()) .. ", jobsite: " .. minetest.pos_to_string(self._jobsite))
|
||||
if vector.distance(self.object:get_pos(),self._jobsite) < 2 then
|
||||
--mcl_log("Made it to work ok!")
|
||||
|
||||
if not (self.state == PATHFINDING) then
|
||||
--mcl_log("Setting order to work.")
|
||||
self.order = WORK
|
||||
else
|
||||
mcl_log("Not gowp. What is it: " .. self.state)
|
||||
end
|
||||
-- Once we arrive at job block, we should unlock trades
|
||||
unlock_trades(self)
|
||||
|
||||
--self.state = "stand"
|
||||
--self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
else
|
||||
mcl_log("Not at job block. Need to commute.")
|
||||
if self.order == WORK then
|
||||
self.order = nil
|
||||
return
|
||||
end
|
||||
--self.state = "go_to_work"
|
||||
mcl_mobs:gopath(self, jobsite, function(self,jobsite)
|
||||
if not self then
|
||||
--mcl_log("missing self. not good")
|
||||
return false
|
||||
end
|
||||
if not self._jobsite then
|
||||
--mcl_log("Jobsite not valid")
|
||||
return false
|
||||
end
|
||||
if vector.distance(self.object:get_pos(),self._jobsite) < 2 then
|
||||
--mcl_log("Made it to work ok callback!")
|
||||
return true
|
||||
else
|
||||
--mcl_log("Need to walk to work. Not sure we can get here.")
|
||||
end
|
||||
|
||||
local function check_jobsite(self)
|
||||
if self._traded or not self._jobsite then return end
|
||||
local n = mcl_vars.get_node(self._jobsite)
|
||||
local m = minetest.get_meta(self._jobsite)
|
||||
if m:get_string("villager") ~= self._id then
|
||||
self._profession = "unemployed"
|
||||
self._trades = nil
|
||||
set_textures(self)
|
||||
end)
|
||||
end
|
||||
end
|
||||
elseif self._profession == "unemployed" then
|
||||
get_a_job(self)
|
||||
elseif has_traded(self) then
|
||||
mcl_log("My job site is invalid or gone. I cannot work.")
|
||||
if self.order == WORK then self.order = nil end
|
||||
get_a_job(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -724,7 +1069,7 @@ local function set_trade(trader, player, inv, concrete_tradenum)
|
|||
init_trades(trader)
|
||||
trades = minetest.deserialize(trader._trades)
|
||||
if not trades then
|
||||
minetest.log("error", "[mobs_mc] Failed to select villager trade!")
|
||||
--minetest.log("error", "Failed to select villager trade!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -1118,6 +1463,7 @@ local trade_inventory = {
|
|||
-- END OF SPECIAL HANDLING FOR COMPASS
|
||||
local trader = player_trading_with[name]
|
||||
local tradenum = player_tradenum[name]
|
||||
|
||||
local trades
|
||||
trader._traded = true
|
||||
if trader and trader._trades then
|
||||
|
@ -1233,6 +1579,7 @@ mcl_mobs:register_mob("mobs_mc:villager", {
|
|||
description = S("Villager"),
|
||||
type = "npc",
|
||||
spawn_class = "passive",
|
||||
passive = true,
|
||||
hp_min = 20,
|
||||
hp_max = 20,
|
||||
head_swivel = "head.control",
|
||||
|
@ -1299,14 +1646,31 @@ mcl_mobs:register_mob("mobs_mc:villager", {
|
|||
return it
|
||||
end,
|
||||
on_rightclick = function(self, clicker)
|
||||
if self._jobsite then
|
||||
if self.child or self._profession == "unemployed" or self._profession == "nitwit" then
|
||||
self.order = nil
|
||||
return
|
||||
end
|
||||
|
||||
if self.state == PATHFINDING then
|
||||
self.state = "stand"
|
||||
end
|
||||
-- Can we remove now we possibly have fixed root cause
|
||||
if self.state == "attack" then
|
||||
mcl_log("Somehow villager got into an invalid attack state. Removed attack state.")
|
||||
-- Need to stop villager getting in attack state. This is a workaround to allow players to fix broken villager.
|
||||
self.state = "stand"
|
||||
self.attack = nil
|
||||
end
|
||||
-- Don't do at night. Go to bed? Maybe do_activity needs it's own method
|
||||
if validate_jobsite(self) then
|
||||
mcl_mobs:gopath(self,self._jobsite,function()
|
||||
--minetest.log("arrived at jobsite")
|
||||
end)
|
||||
else
|
||||
self.state = "stand" -- cancel gowp in case it has messed up
|
||||
self.order = nil -- cancel work if working
|
||||
end
|
||||
if self.child or self._profession == "unemployed" or self._profession == "nitwit" then
|
||||
return
|
||||
end
|
||||
|
||||
-- Initiate trading
|
||||
init_trader_vars(self)
|
||||
local name = clicker:get_player_name()
|
||||
|
@ -1345,18 +1709,21 @@ mcl_mobs:register_mob("mobs_mc:villager", {
|
|||
_trading_players = {}, -- list of playernames currently trading with villager (open formspec)
|
||||
do_custom = function(self, dtime)
|
||||
check_summon(self,dtime)
|
||||
|
||||
-- Stand still if player is nearby.
|
||||
if not self._player_scan_timer then
|
||||
self._player_scan_timer = 0
|
||||
end
|
||||
|
||||
self._player_scan_timer = self._player_scan_timer + dtime
|
||||
|
||||
-- Check infrequently to keep CPU load low
|
||||
if self._player_scan_timer > PLAYER_SCAN_INTERVAL then
|
||||
|
||||
self._player_scan_timer = 0
|
||||
local selfpos = self.object:get_pos()
|
||||
local objects = minetest.get_objects_inside_radius(selfpos, PLAYER_SCAN_RADIUS)
|
||||
local has_player = false
|
||||
|
||||
for o, obj in pairs(objects) do
|
||||
if obj:is_player() then
|
||||
has_player = true
|
||||
|
@ -1367,18 +1734,48 @@ mcl_mobs:register_mob("mobs_mc:villager", {
|
|||
minetest.log("verbose", "[mobs_mc] Player near villager found!")
|
||||
stand_still(self)
|
||||
else
|
||||
minetest.log("verbose", "[mobs_mc] No player near villager found!")
|
||||
--minetest.log("verbose", "[mobs_mc] No player near villager found!")
|
||||
self.walk_chance = DEFAULT_WALK_CHANCE
|
||||
self.jump = true
|
||||
end
|
||||
if self._bed and ( self.state ~= "go_home" and vector.distance(self.object:get_pos(),self._bed) > 50 ) then
|
||||
go_home(self)
|
||||
|
||||
if not self._bed then
|
||||
--mcl_log("Villager has no bed. Currently at location: "..minetest.pos_to_string(self.object:get_pos()))
|
||||
take_bed (self)
|
||||
end
|
||||
|
||||
-- Only check in day or during thunderstorm but wandered_too_far code won't work
|
||||
if check_bed (self) then
|
||||
--self.state ~= "go_home"
|
||||
local wandered_too_far = ( self.state ~= PATHFINDING ) and (vector.distance(self.object:get_pos(),self._bed) > 50 )
|
||||
|
||||
--if wandered_too_far then minetest.log("Wandered too far! Return home ") end
|
||||
if wandered_too_far then
|
||||
go_home(self, false)
|
||||
return
|
||||
elseif mcl_beds.is_night() or (weather_mod and mcl_weather.get_weather() == "thunder") then
|
||||
mcl_log("It's night or thunderstorm. Better get to bed. Weather is: " .. mcl_weather.get_weather())
|
||||
go_home(self, true)
|
||||
return
|
||||
end
|
||||
if self._profession == "unemployed" then
|
||||
get_a_job(self)
|
||||
else
|
||||
check_jobsite(self)
|
||||
--mcl_log("check bed failed ")
|
||||
end
|
||||
|
||||
-- Daytime is work and play time
|
||||
if not mcl_beds.is_night() then
|
||||
if self.order == SLEEP then self.order = nil end
|
||||
|
||||
if get_activity() == WORK then
|
||||
do_work(self)
|
||||
else
|
||||
-- gossip at town bell or stroll around
|
||||
self.order = nil
|
||||
end
|
||||
else
|
||||
if self.order == WORK then self.order = nil end
|
||||
end
|
||||
|
||||
end
|
||||
end,
|
||||
|
||||
|
@ -1408,6 +1805,19 @@ mcl_mobs:register_mob("mobs_mc:villager", {
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
local bed = self._bed
|
||||
if bed then
|
||||
local bed_meta = minetest.get_meta(bed)
|
||||
bed_meta:set_string("villager", nil)
|
||||
mcl_log("Died, so bye bye bed")
|
||||
end
|
||||
local jobsite = self._jobsite
|
||||
if jobsite then
|
||||
local jobsite_meta = minetest.get_meta(jobsite)
|
||||
jobsite_meta:set_string("villager", nil)
|
||||
mcl_log("Died, so bye bye jobsite")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ local zombie = {
|
|||
curiosity = 7,
|
||||
head_pitch_multiplier=-1,
|
||||
breath_max = -1,
|
||||
wears_armor = 1,
|
||||
armor = {undead = 90, fleshy = 90},
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.8, 0.3},
|
||||
visual = "mesh",
|
||||
|
|
|
@ -34,6 +34,7 @@ return {
|
|||
"RandomLegoBrick",
|
||||
"SumianVoice",
|
||||
"MrRar",
|
||||
"talamh",
|
||||
}},
|
||||
{S("Contributors"), 0x52FF00, {
|
||||
"Laurent Rocher",
|
||||
|
@ -67,7 +68,6 @@ return {
|
|||
"Benjamin Schötz",
|
||||
"Doloment",
|
||||
"Sydney Gems",
|
||||
"talamh",
|
||||
"Emily2255",
|
||||
"Emojigit",
|
||||
"FinishedFragment",
|
||||
|
@ -86,6 +86,10 @@ return {
|
|||
"opfromthestart",
|
||||
"snowyu",
|
||||
"FaceDeer",
|
||||
"Faerraven / Michieal",
|
||||
"FossFanatic",
|
||||
"Herbert West",
|
||||
"GuyLiner",
|
||||
}},
|
||||
{S("MineClone5"), 0xA60014, {
|
||||
"kay27",
|
||||
|
@ -96,7 +100,7 @@ return {
|
|||
"chmodsayshello",
|
||||
"3raven",
|
||||
"PrairieWind",
|
||||
"Gustavo1",
|
||||
"Gustavo6046 / wallabra",
|
||||
"CableGuy67",
|
||||
"MrRar",
|
||||
}},
|
||||
|
|
|
@ -54,7 +54,6 @@ local entity_animations = {
|
|||
minetest.register_entity("mcl_chests:chest", {
|
||||
initial_properties = {
|
||||
visual = "mesh",
|
||||
visual_size = {x = 3, y = 3},
|
||||
pointable = false,
|
||||
physical = false,
|
||||
static_save = false,
|
||||
|
@ -140,7 +139,6 @@ minetest.register_entity("mcl_chests:chest", {
|
|||
|
||||
local function get_entity_pos(pos, dir, double)
|
||||
pos = vector.new(pos)
|
||||
pos.y = pos.y - 0.49
|
||||
if double then
|
||||
local add, mul, vec, cross = vector.add, vector.multiply, vector.new, vector.cross
|
||||
pos = add(pos, mul(cross(dir, vec(0, 1, 0)), -0.5))
|
||||
|
@ -363,7 +361,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
|
|||
_doc_items_usagehelp = usagehelp,
|
||||
_doc_items_hidden = hidden,
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_chests_chest.obj",
|
||||
mesh = "mcl_chests_chest.b3d",
|
||||
tiles = small_textures,
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
paramtype = "light",
|
||||
|
@ -401,7 +399,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
|
|||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
},
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
tiles = {"blank.png^[resize:16x16"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
_chest_entity_textures = small_textures,
|
||||
_chest_entity_sound = "default_chest",
|
||||
|
@ -527,7 +525,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
|
|||
type = "fixed",
|
||||
fixed = {-0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375},
|
||||
},
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
tiles = {"blank.png^[resize:16x16"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
_chest_entity_textures = left_textures,
|
||||
_chest_entity_sound = "default_chest",
|
||||
|
@ -684,7 +682,7 @@ local function register_chest(basename, desc, longdesc, usagehelp, tt_help, tile
|
|||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.4375, 0.4375, 0.375, 0.4375},
|
||||
},
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
tiles = {"blank.png^[resize:16x16"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
groups = {handy=1,axey=1, container=6,not_in_creative_inventory=1, material_wood=1,flammable=-1,double_chest=2},
|
||||
drop = drop,
|
||||
|
@ -842,15 +840,6 @@ register_chest("chest",
|
|||
{
|
||||
small = tiles_chest_normal_small,
|
||||
double = tiles_chest_normal_double,
|
||||
inv = {"default_chest_top.png", "mcl_chests_chest_bottom.png",
|
||||
"mcl_chests_chest_right.png", "mcl_chests_chest_left.png",
|
||||
"mcl_chests_chest_back.png", "default_chest_front.png"},
|
||||
--[[left = {"default_chest_top_big.png", "default_chest_top_big.png",
|
||||
"mcl_chests_chest_right.png", "mcl_chests_chest_left.png",
|
||||
"default_chest_side_big.png^[transformFX", "default_chest_front_big.png"},
|
||||
right = {"default_chest_top_big.png^[transformFX", "default_chest_top_big.png^[transformFX",
|
||||
"mcl_chests_chest_right.png", "mcl_chests_chest_left.png",
|
||||
"default_chest_side_big.png", "default_chest_front_big.png^[transformFX"},]]--
|
||||
},
|
||||
false
|
||||
)
|
||||
|
@ -858,15 +847,6 @@ register_chest("chest",
|
|||
local traptiles = {
|
||||
small = tiles_chest_trapped_small,
|
||||
double = tiles_chest_trapped_double,
|
||||
inv = {"mcl_chests_chest_trapped_top.png", "mcl_chests_chest_trapped_bottom.png",
|
||||
"mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png",
|
||||
"mcl_chests_chest_trapped_back.png", "mcl_chests_chest_trapped_front.png"},
|
||||
--[[left = {"mcl_chests_chest_trapped_top_big.png", "mcl_chests_chest_trapped_top_big.png",
|
||||
"mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png",
|
||||
"mcl_chests_chest_trapped_side_big.png^[transformFX", "mcl_chests_chest_trapped_front_big.png"},
|
||||
right = {"mcl_chests_chest_trapped_top_big.png^[transformFX", "mcl_chests_chest_trapped_top_big.png^[transformFX",
|
||||
"mcl_chests_chest_trapped_right.png", "mcl_chests_chest_trapped_left.png",
|
||||
"mcl_chests_chest_trapped_side_big.png", "mcl_chests_chest_trapped_front_big.png^[transformFX"},]]--
|
||||
}
|
||||
|
||||
register_chest("trapped_chest",
|
||||
|
@ -992,7 +972,7 @@ minetest.register_node("mcl_chests:ender_chest", {
|
|||
_doc_items_longdesc = S("Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This inventory is the same no matter from which ender chest you access it from. If you put one item into one ender chest, you will find it in all other ender chests. Each player will only see their own items, but not the items of other players."),
|
||||
_doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."),
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_chests_chest.obj",
|
||||
mesh = "mcl_chests_chest.b3d",
|
||||
tiles = tiles_chest_ender_small,
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
paramtype = "light",
|
||||
|
@ -1034,11 +1014,8 @@ minetest.register_node("mcl_chests:ender_chest_small", {
|
|||
_chest_entity_sound = "mcl_chests_enderchest",
|
||||
_chest_entity_mesh = "mcl_chests_chest",
|
||||
_chest_entity_animation_type = "chest",
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
tiles = {"blank.png^[resize:16x16"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
--[[{"mcl_chests_ender_chest_top.png", "mcl_chests_ender_chest_bottom.png",
|
||||
"mcl_chests_ender_chest_right.png", "mcl_chests_ender_chest_left.png",
|
||||
"mcl_chests_ender_chest_back.png", "mcl_chests_ender_chest_front.png"},]]--
|
||||
-- Note: The “container” group is missing here because the ender chest does not
|
||||
-- have an inventory on its own
|
||||
groups = {pickaxey=1, deco_block=1, material_stone=1, chest_entity=1, not_in_creative_inventory=1},
|
||||
|
@ -1188,13 +1165,7 @@ for color, desc in pairs(boxtypes) do
|
|||
tiles = {mob_texture},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_chests_shulker.obj",
|
||||
--[["mcl_chests_"..color.."_shulker_box_top.png", -- top
|
||||
"[combine:16x16:-32,-28="..mob_texture, -- bottom
|
||||
"[combine:16x16:0,-36="..mob_texture..":0,-16="..mob_texture, -- side
|
||||
"[combine:16x16:-32,-36="..mob_texture..":-32,-16="..mob_texture, -- side
|
||||
"[combine:16x16:-16,-36="..mob_texture..":-16,-16="..mob_texture, -- side
|
||||
"[combine:16x16:-48,-36="..mob_texture..":-48,-16="..mob_texture, -- side]]--
|
||||
mesh = "mcl_chests_shulker.b3d",
|
||||
groups = {handy=1,pickaxey=1, container=3, deco_block=1, dig_by_piston=1, shulker_box=1, old_shulker_box_node=1},
|
||||
is_ground_content = false,
|
||||
sounds = mcl_sounds.node_sound_stone_defaults(),
|
||||
|
@ -1250,7 +1221,7 @@ for color, desc in pairs(boxtypes) do
|
|||
_doc_items_longdesc = longdesc,
|
||||
_doc_items_usagehelp = usagehelp,
|
||||
drawtype = "nodebox",
|
||||
tiles = {"mcl_chests_blank.png"},
|
||||
tiles = {"blank.png^[resize:16x16"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
_chest_entity_textures = {mob_texture},
|
||||
_chest_entity_sound = "mcl_chests_shulker",
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
# Blender v2.76 (sub 0) OBJ File: 'chest.small.facedir.blend'
|
||||
# www.blender.org
|
||||
mtllib chest.small.facedir.mtl
|
||||
o chest_upper_upper
|
||||
v 0.062513 -0.063134 -0.500468
|
||||
v 0.062513 0.186920 -0.500468
|
||||
v 0.062514 -0.063134 -0.437955
|
||||
v 0.062514 0.186920 -0.437955
|
||||
v -0.062514 -0.063134 -0.500468
|
||||
v -0.062514 0.186920 -0.500468
|
||||
v -0.062514 -0.063134 -0.437955
|
||||
v -0.062514 0.186920 -0.437955
|
||||
v 0.437907 0.061263 -0.438085
|
||||
v 0.437907 0.373830 -0.438085
|
||||
v 0.437907 0.061263 0.437729
|
||||
v 0.437907 0.373830 0.437729
|
||||
v -0.437907 0.061263 -0.438085
|
||||
v -0.437907 0.373830 -0.438085
|
||||
v -0.437907 0.061263 0.437729
|
||||
v -0.437907 0.373830 0.437729
|
||||
v 0.437595 -0.500754 -0.437772
|
||||
v 0.437595 0.124381 -0.437772
|
||||
v 0.437595 -0.500754 0.437417
|
||||
v 0.437595 0.124381 0.437417
|
||||
v -0.437595 -0.500754 -0.437772
|
||||
v -0.437595 0.124381 -0.437772
|
||||
v -0.437595 -0.500754 0.437417
|
||||
v -0.437595 0.124381 0.437417
|
||||
vt 0.015625 0.921875
|
||||
vt 0.015625 0.984375
|
||||
vt 0.000000 0.984375
|
||||
vt 0.000000 0.921875
|
||||
vt 0.093750 0.921875
|
||||
vt 0.093750 0.984375
|
||||
vt 0.062500 0.984375
|
||||
vt 0.062500 0.921875
|
||||
vt 0.046875 0.984375
|
||||
vt 0.046875 0.921875
|
||||
vt 0.078125 0.984375
|
||||
vt 0.078125 1.000000
|
||||
vt 0.046875 1.000000
|
||||
vt 0.015625 1.000000
|
||||
vt 0.218750 0.703125
|
||||
vt 0.218750 0.781250
|
||||
vt 0.000000 0.781250
|
||||
vt 0.000000 0.703125
|
||||
vt 0.875000 0.703125
|
||||
vt 0.875000 0.781250
|
||||
vt 0.656250 0.781250
|
||||
vt 0.656250 0.703125
|
||||
vt 0.437500 0.781250
|
||||
vt 0.437500 0.703125
|
||||
vt 0.656250 1.000000
|
||||
vt 0.437500 1.000000
|
||||
vt 0.218750 1.000000
|
||||
vt 0.218750 0.328125
|
||||
vt 0.218750 0.484375
|
||||
vt -0.000000 0.484375
|
||||
vt -0.000000 0.328125
|
||||
vt 0.875000 0.328125
|
||||
vt 0.875000 0.484375
|
||||
vt 0.656250 0.484375
|
||||
vt 0.656250 0.328125
|
||||
vt 0.437500 0.484375
|
||||
vt 0.437500 0.328125
|
||||
vn 1.000000 0.000000 -0.000000
|
||||
vn 0.000000 0.000000 1.000000
|
||||
vn -1.000000 0.000000 0.000000
|
||||
vn 0.000000 0.000000 -1.000000
|
||||
vn 0.000000 -1.000000 0.000000
|
||||
vn 0.000000 1.000000 0.000000
|
||||
usemtl None
|
||||
s off
|
||||
f 1/1/1 2/2/1 4/3/1 3/4/1
|
||||
f 3/5/2 4/6/2 8/7/2 7/8/2
|
||||
f 7/8/3 8/7/3 6/9/3 5/10/3
|
||||
f 5/10/4 6/9/4 2/2/4 1/1/4
|
||||
f 3/9/5 7/11/5 5/12/5 1/13/5
|
||||
f 8/13/6 4/14/6 2/2/6 6/9/6
|
||||
f 9/15/1 10/16/1 12/17/1 11/18/1
|
||||
f 11/19/2 12/20/2 16/21/2 15/22/2
|
||||
f 15/22/3 16/21/3 14/23/3 13/24/3
|
||||
f 13/24/4 14/23/4 10/16/4 9/15/4
|
||||
f 11/25/5 15/26/5 13/23/5 9/21/5
|
||||
f 16/26/6 12/27/6 10/16/6 14/23/6
|
||||
f 17/28/1 18/29/1 20/30/1 19/31/1
|
||||
f 19/32/2 20/33/2 24/34/2 23/35/2
|
||||
f 23/35/3 24/34/3 22/36/3 21/37/3
|
||||
f 21/37/4 22/36/4 18/29/4 17/28/4
|
||||
f 19/22/5 23/24/5 21/36/5 17/34/5
|
||||
f 24/24/6 20/15/6 18/29/6 22/36/6
|
|
@ -1,159 +0,0 @@
|
|||
# Blender v2.79 (sub 0) OBJ File: 'shulkerbox2.blend'
|
||||
# www.blender.org
|
||||
mtllib shulkerbox2.mtl
|
||||
o low1_Cube.006
|
||||
v -0.500000 -0.500001 0.500000
|
||||
v -0.500000 0.062499 0.500000
|
||||
v -0.500000 -0.500001 -0.500000
|
||||
v -0.500000 0.062499 -0.500000
|
||||
v 0.500000 -0.500001 0.500000
|
||||
v 0.500000 0.062499 0.500000
|
||||
v 0.500000 -0.500001 -0.500000
|
||||
v 0.500000 0.062499 -0.500000
|
||||
vt 0.250000 0.187500
|
||||
vt -0.000000 0.187500
|
||||
vt -0.000000 0.312500
|
||||
vt 0.250000 0.312500
|
||||
vt 1.000000 0.187500
|
||||
vt 0.750000 0.187500
|
||||
vt 0.750000 0.312500
|
||||
vt 1.000000 0.312500
|
||||
vt 0.500000 0.187500
|
||||
vt 0.500000 0.312500
|
||||
vt 0.750000 0.562500
|
||||
vt 0.750000 0.312500
|
||||
vt 0.500000 0.312500
|
||||
vt 0.500000 0.562500
|
||||
vt 0.500000 0.562500
|
||||
vt 0.250000 0.562500
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 1/1/1 3/2/1 4/3/1 2/4/1
|
||||
f 3/5/2 7/6/2 8/7/2 4/8/2
|
||||
f 7/6/3 5/9/3 6/10/3 8/7/3
|
||||
f 5/9/4 1/1/4 2/4/4 6/10/4
|
||||
f 3/11/5 1/12/5 5/13/5 7/14/5
|
||||
f 8/15/6 6/10/6 2/4/6 4/16/6
|
||||
o top1_Cube.005
|
||||
v -0.500313 -0.220552 0.500313
|
||||
v -0.500313 0.530073 0.500313
|
||||
v -0.500313 -0.220552 -0.500313
|
||||
v -0.500313 0.530073 -0.500313
|
||||
v 0.500313 -0.220552 0.500313
|
||||
v 0.500313 0.530073 0.500313
|
||||
v 0.500313 -0.220552 -0.500313
|
||||
v 0.500313 0.530073 -0.500313
|
||||
vt 0.250000 0.562500
|
||||
vt -0.000000 0.562500
|
||||
vt -0.000000 0.750000
|
||||
vt 0.250000 0.750000
|
||||
vt 1.000000 0.562500
|
||||
vt 0.750000 0.562500
|
||||
vt 0.750000 0.750000
|
||||
vt 1.000000 0.750000
|
||||
vt 0.500000 0.562500
|
||||
vt 0.500000 0.750000
|
||||
vt 0.750000 1.000000
|
||||
vt 0.750000 0.750000
|
||||
vt 0.500000 0.750000
|
||||
vt 0.500000 1.000000
|
||||
vt 0.500000 1.000000
|
||||
vt 0.250000 1.000000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 9/17/7 11/18/7 12/19/7 10/20/7
|
||||
f 11/21/8 15/22/8 16/23/8 12/24/8
|
||||
f 15/22/9 13/25/9 14/26/9 16/23/9
|
||||
f 13/25/10 9/17/10 10/20/10 14/26/10
|
||||
f 11/27/11 9/28/11 13/29/11 15/30/11
|
||||
f 16/31/12 14/26/12 10/20/12 12/32/12
|
||||
o top2_Cube.002
|
||||
v -0.500247 -0.220392 0.500247
|
||||
v -0.500247 0.530234 0.500247
|
||||
v -0.500247 -0.220392 -0.500378
|
||||
v -0.500247 0.530234 -0.500378
|
||||
v 0.500378 -0.220392 0.500247
|
||||
v 0.500378 0.530234 0.500247
|
||||
v 0.500378 -0.220392 -0.500378
|
||||
v 0.500378 0.530234 -0.500378
|
||||
vt 0.250000 0.562500
|
||||
vt 0.250000 0.750000
|
||||
vt -0.000000 0.750000
|
||||
vt -0.000000 0.562500
|
||||
vt 1.000000 0.562500
|
||||
vt 1.000000 0.750000
|
||||
vt 0.750000 0.750000
|
||||
vt 0.750000 0.562500
|
||||
vt 0.500000 0.750000
|
||||
vt 0.500000 0.562500
|
||||
vt 0.750000 1.000000
|
||||
vt 0.500000 1.000000
|
||||
vt 0.500000 0.750000
|
||||
vt 0.750000 0.750000
|
||||
vt 0.500000 1.000000
|
||||
vt 0.250000 1.000000
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 17/33/13 18/34/13 20/35/13 19/36/13
|
||||
f 19/37/14 20/38/14 24/39/14 23/40/14
|
||||
f 23/40/15 24/39/15 22/41/15 21/42/15
|
||||
f 21/42/16 22/41/16 18/34/16 17/33/16
|
||||
f 19/43/17 23/44/17 21/45/17 17/46/17
|
||||
f 24/47/18 20/48/18 18/34/18 22/41/18
|
||||
o low2_Cube.001
|
||||
v -0.499935 -0.499936 0.499935
|
||||
v -0.499935 0.062565 0.499935
|
||||
v -0.499935 -0.499936 -0.500066
|
||||
v -0.499935 0.062565 -0.500066
|
||||
v 0.500066 -0.499936 0.499935
|
||||
v 0.500066 0.062565 0.499935
|
||||
v 0.500066 -0.499936 -0.500066
|
||||
v 0.500066 0.062565 -0.500066
|
||||
vt 0.250000 0.187500
|
||||
vt 0.250000 0.312500
|
||||
vt -0.000000 0.312500
|
||||
vt -0.000000 0.187500
|
||||
vt 1.000000 0.187500
|
||||
vt 1.000000 0.312500
|
||||
vt 0.750000 0.312500
|
||||
vt 0.750000 0.187500
|
||||
vt 0.500000 0.312500
|
||||
vt 0.500000 0.187500
|
||||
vt 0.750000 0.562500
|
||||
vt 0.500000 0.562500
|
||||
vt 0.500000 0.312500
|
||||
vt 0.750000 0.312500
|
||||
vt 0.500000 0.562500
|
||||
vt 0.250000 0.562500
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 -1.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn 0.0000 -1.0000 0.0000
|
||||
vn 0.0000 1.0000 0.0000
|
||||
usemtl None
|
||||
s off
|
||||
f 25/49/19 26/50/19 28/51/19 27/52/19
|
||||
f 27/53/20 28/54/20 32/55/20 31/56/20
|
||||
f 31/56/21 32/55/21 30/57/21 29/58/21
|
||||
f 29/58/22 30/57/22 26/50/22 25/49/22
|
||||
f 27/59/23 31/60/23 29/61/23 25/62/23
|
||||
f 32/63/24 28/64/24 26/50/24 30/57/24
|
Before Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 224 B |
Before Width: | Height: | Size: 210 B |
Before Width: | Height: | Size: 194 B |
Before Width: | Height: | Size: 200 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 570 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 168 B |
Before Width: | Height: | Size: 196 B |
Before Width: | Height: | Size: 209 B |
Before Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 168 B |
Before Width: | Height: | Size: 239 B |
Before Width: | Height: | Size: 239 B |
Before Width: | Height: | Size: 196 B |
Before Width: | Height: | Size: 209 B |
Before Width: | Height: | Size: 210 B |
Before Width: | Height: | Size: 194 B |
Before Width: | Height: | Size: 200 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 189 B |
Before Width: | Height: | Size: 161 B |
Before Width: | Height: | Size: 199 B |
Before Width: | Height: | Size: 189 B |
Before Width: | Height: | Size: 189 B |
Before Width: | Height: | Size: 191 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 160 B |
|
@ -88,10 +88,13 @@ minetest.register_node("mcl_core:stone_with_gold", {
|
|||
})
|
||||
|
||||
local redstone_timer = 68.28
|
||||
local function redstone_ore_activate(pos)
|
||||
local function redstone_ore_activate(pos, node, puncher, pointed_thing)
|
||||
minetest.swap_node(pos, {name="mcl_core:stone_with_redstone_lit"})
|
||||
local t = minetest.get_node_timer(pos)
|
||||
t:start(redstone_timer)
|
||||
if puncher and pointed_thing then
|
||||
return minetest.node_punch(pos, node, puncher, pointed_thing)
|
||||
end
|
||||
end
|
||||
minetest.register_node("mcl_core:stone_with_redstone", {
|
||||
description = S("Redstone Ore"),
|
||||
|
@ -126,9 +129,12 @@ minetest.register_node("mcl_core:stone_with_redstone", {
|
|||
}
|
||||
})
|
||||
|
||||
local function redstone_ore_reactivate(pos)
|
||||
local function redstone_ore_reactivate(pos, node, puncher, pointed_thing)
|
||||
local t = minetest.get_node_timer(pos)
|
||||
t:start(redstone_timer)
|
||||
if puncher and pointed_thing then
|
||||
return minetest.node_punch(pos, node, puncher, pointed_thing)
|
||||
end
|
||||
end
|
||||
-- Light the redstone ore up when it has been touched
|
||||
minetest.register_node("mcl_core:stone_with_redstone_lit", {
|
||||
|
|
|
@ -116,15 +116,21 @@ end
|
|||
|
||||
local redstone_timer = 68.28
|
||||
|
||||
local function redstone_ore_activate(pos)
|
||||
local function redstone_ore_activate(pos, node, puncher, pointed_thing)
|
||||
minetest.swap_node(pos, { name = "mcl_deepslate:deepslate_with_redstone_lit" })
|
||||
local t = minetest.get_node_timer(pos)
|
||||
t:start(redstone_timer)
|
||||
if puncher and pointed_thing then
|
||||
return minetest.node_punch(pos, node, puncher, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
local function redstone_ore_reactivate(pos)
|
||||
local function redstone_ore_reactivate(pos, node, puncher, pointed_thing)
|
||||
local t = minetest.get_node_timer(pos)
|
||||
t:start(redstone_timer)
|
||||
if puncher and pointed_thing then
|
||||
return minetest.node_punch(pos, node, puncher, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("mcl_deepslate:deepslate_with_redstone", {
|
||||
|
|
|
@ -281,6 +281,13 @@ local function apply_bone_meal(pointed_thing)
|
|||
if math.random(1, 100) <= 75 then
|
||||
return mcl_farming:grow_plant("plant_beetroot", pos, n, 1, true)
|
||||
end
|
||||
elseif string.find(n.name, "mcl_farming:sweet_berry_bush_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
if n.name == "mcl_farming:sweet_berry_bush_3" then
|
||||
return minetest.add_item(vector.offset(pos,math.random()-0.5,math.random()-0.5,math.random()-0.5),"mcl_farming:sweet_berry")
|
||||
else
|
||||
return mcl_farming:grow_plant("plant_sweet_berry_bush", pos, n, 1, true)
|
||||
end
|
||||
elseif n.name == "mcl_cocoas:cocoa_1" or n.name == "mcl_cocoas:cocoa_2" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Cocoa: Advance by 1 stage
|
||||
|
|
|
@ -27,3 +27,6 @@ dofile(minetest.get_modpath("mcl_farming").."/potatoes.lua")
|
|||
|
||||
-- ========= BEETROOT =========
|
||||
dofile(minetest.get_modpath("mcl_farming").."/beetroot.lua")
|
||||
|
||||
-- ========= SWEET BERRY =========
|
||||
dofile(minetest.get_modpath("mcl_farming").."/sweet_berry.lua")
|
||||
|
|
87
mods/ITEMS/mcl_farming/sweet_berry.lua
Normal file
|
@ -0,0 +1,87 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local planton = {"mcl_core:dirt_with_grass", "mcl_core:dirt", "mcl_core:podzol", "mcl_core:coarse_dirt", "mcl_farming:soil", "mcl_farming:soil_wet", "mcl_moss:moss"}
|
||||
|
||||
for i=0, 3 do
|
||||
local texture = "mcl_farming_sweet_berry_bush_" .. i .. ".png"
|
||||
local node_name = "mcl_farming:sweet_berry_bush_" .. i
|
||||
local groups = {sweet_berry=1, dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1, flammable=3, fire_encouragement=60, fire_flammability=20, compostability=30}
|
||||
if i > 0 then
|
||||
groups.sweet_berry_thorny = 1
|
||||
end
|
||||
minetest.register_node(node_name, {
|
||||
drawtype = "plantlike",
|
||||
tiles = {texture},
|
||||
description = S("Sweet Berry Bush (Stage @1)", i),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "meshoptions",
|
||||
place_param2 = 3,
|
||||
liquid_viscosity = 15,
|
||||
liquidtype = "source",
|
||||
liquid_alternative_flowing = node_name,
|
||||
liquid_alternative_source = node_name,
|
||||
liquid_renewable = false,
|
||||
liquid_range = 0,
|
||||
walkable = false,
|
||||
drop = (i>=2) and ("mcl_farming:sweet_berry" .. (i==3 and " 3" or "")) or "",
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-6 / 16, -0.5, -6 / 16, 6 / 16, (-0.30 + (i*0.25)), 6 / 16},
|
||||
},
|
||||
inventory_image = texture,
|
||||
wield_image = texture,
|
||||
groups = groups,
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
_mcl_hardness = 0,
|
||||
})
|
||||
minetest.register_alias("mcl_sweet_berry:sweet_berry_bush_" .. i, node_name)
|
||||
end
|
||||
|
||||
minetest.register_craftitem("mcl_farming:sweet_berry", {
|
||||
description = S("Sweet Berry"),
|
||||
inventory_image = "mcl_farming_sweet_berry.png",
|
||||
_mcl_saturation = 0.4,
|
||||
groups = { food = 2, eatable = 1, compostability=30 },
|
||||
on_secondary_use = minetest.item_eat(1),
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type == "node" and table.indexof(planton,minetest.get_node(pointed_thing.under).name) ~= -1 and minetest.get_node(pointed_thing.above).name == "air" then
|
||||
minetest.set_node(pointed_thing.above,{name="mcl_farming:sweet_berry_bush_0"})
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
return minetest.do_item_eat(1, nil, itemstack, placer, pointed_thing)
|
||||
end,
|
||||
})
|
||||
minetest.register_alias("mcl_sweet_berry:sweet_berry", "mcl_farming:sweet_berry")
|
||||
|
||||
-- TODO: Find proper interval and chance values for sweet berry bushes. Current interval and chance values are copied from mcl_farming:beetroot which has similar growth stages.
|
||||
mcl_farming:add_plant("plant_sweet_berry_bush", "mcl_farming:sweet_berry_bush_3", {"mcl_farming:sweet_berry_bush_0", "mcl_farming:sweet_berry_bush_1", "mcl_farming:sweet_berry_bush_2"}, 68, 3)
|
||||
|
||||
local function berry_damage_check(obj)
|
||||
local p = obj:get_pos()
|
||||
if not p then return end
|
||||
if not minetest.find_node_near(p,0.4,{"group:sweet_berry_thorny"},true) then return end
|
||||
local v = obj:get_velocity()
|
||||
if v.x < 0.1 and v.y < 0.1 and v.z < 0.1 then return end
|
||||
|
||||
mcl_util.deal_damage(obj, 0.5, {type = "sweet_berry"})
|
||||
end
|
||||
|
||||
local etime = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
etime = dtime + etime
|
||||
if etime < 0.5 then return end
|
||||
etime = 0
|
||||
for _,pl in pairs(minetest.get_connected_players()) do
|
||||
berry_damage_check(pl)
|
||||
end
|
||||
for _,ent in pairs(minetest.luaentities) do
|
||||
if ent.is_mob then
|
||||
berry_damage_check(ent.object)
|
||||
end
|
||||
end
|
||||
end)
|
BIN
mods/ITEMS/mcl_farming/textures/mcl_farming_sweet_berry.png
Normal file
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 318 B |
After Width: | Height: | Size: 759 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 858 B |
|
@ -10,7 +10,7 @@ to play. And that's it!
|
|||
`mcl_jukebox_track_2.ogg`: “The Energetic Rat (Jordach's Mix)” by SoundHelix (CC0)
|
||||
`mcl_jukebox_track_3.ogg`: “Eastern Feeling” by Jordach (CC0)
|
||||
`mcl_jukebox_track_4.ogg`: “Minetest” by Jordach (CC0)
|
||||
`mcl_jukebox_track_5.ogg`: “Credit Roll (Jordach's HD Mix)” by Junichi Masuda (CC0)
|
||||
`mcl_jukebox_track_5.ogg`: "Soaring over the sea" by mactonite http://ccmixter.org/files/mactonite/65379 (CC-BY)
|
||||
`mcl_jukebox_track_6.ogg`: “Winter Feeling" by Tom Peter (CC BY-SA 3.0)
|
||||
`mcl_jukebox_track_7.ogg`: “Synthgroove (Jordach's Mix)” by HeroOfTheWinds (CC0)
|
||||
`mcl_jukebox_track_8.ogg`: “The Clueless Frog (Jordach's Mix)” by SoundHelix (CC0)
|
||||
|
|
|
@ -234,7 +234,7 @@ mcl_jukebox.register_record("The Evil Sister (Jordach's Mix)", "SoundHelix", "13
|
|||
mcl_jukebox.register_record("The Energetic Rat (Jordach's Mix)", "SoundHelix", "wait", "mcl_jukebox_record_wait.png", "mcl_jukebox_track_2")
|
||||
mcl_jukebox.register_record("Eastern Feeling", "Jordach", "blocks", "mcl_jukebox_record_blocks.png", "mcl_jukebox_track_3")
|
||||
mcl_jukebox.register_record("Minetest", "Jordach", "far", "mcl_jukebox_record_far.png", "mcl_jukebox_track_4")
|
||||
mcl_jukebox.register_record("Credit Roll (Jordach's HD Mix)", "Junichi Masuda", "chirp", "mcl_jukebox_record_chirp.png", "mcl_jukebox_track_5")
|
||||
mcl_jukebox.register_record("Soaring over the sea", "mactonite", "chirp", "mcl_jukebox_record_chirp.png", "mcl_jukebox_track_5")
|
||||
mcl_jukebox.register_record("Winter Feeling", "Tom Peter", "strad", "mcl_jukebox_record_strad.png", "mcl_jukebox_track_6")
|
||||
mcl_jukebox.register_record("Synthgroove (Jordach's Mix)", "HeroOfTheWinds", "mellohi", "mcl_jukebox_record_mellohi.png", "mcl_jukebox_track_7")
|
||||
mcl_jukebox.register_record("The Clueless Frog (Jordach's Mix)", "SoundHelix", "mall", "mcl_jukebox_record_mall.png", "mcl_jukebox_track_8")
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
Mod based on reworked signs mod by PilzAdam:
|
||||
|
||||
---
|
||||
# Mineclone2-Signs
|
||||
---
|
||||
A reworking of MineClone 2's mcl_signs to be colorable and made to glow. Rquires Minetest and Mineclone2.
|
||||
---
|
||||
|
||||
Created by Michieal (FaerRaven) @ DateTime: 10/14/22 4:05 PM
|
||||
|
||||
|
||||
Reworked to be an API and to allow players to color, and/or make the lettering for the signs glow (be bright at night).
|
||||
Reworked by Michieal (FaerRaven), including the sign textures batch changed to be white instead of the original black.
|
||||
|
||||
A special thanks to Cora for pointing me in the right direction (as always).
|
||||
|
||||
|
||||
The original Mod, MCL_SIGNS is based on reworked signs mod by PilzAdam:
|
||||
|
||||
https://forum.minetest.net/viewtopic.php?t=3289
|
||||
|
||||
License of code and font: MIT License
|
||||
|
@ -6,8 +23,22 @@ License of code and font: MIT License
|
|||
Font source: 04.jp.org, some modifications and additions were made (added support for Latin-1 Supplement)
|
||||
Original font license text states: “YOU MAY USE THEM AS YOU LIKE” (in about.gif file distributed with the font)
|
||||
|
||||
License of textures: See README.md in top directory of MineClone 2.
|
||||
License of textures: See README.md in top directory of MineClone 2, with the exception of the following:
|
||||
default_sign.png, default_sign_dark.png, default_sign_greyscale.png, mcl_signs_sign_dark.png,
|
||||
mcl_signs_sign_greyscale.png are licensed as follows:
|
||||
Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
Credit Michieal (Faerraven). The extra sign textures are provided for you to use, modify, etc., with the goal being to
|
||||
make the game better. (All of these textures were changed / created by me, to make them usable / better.)
|
||||
|
||||
License of models: GPLv3 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
Models author: 22i.
|
||||
|
||||
Source: https://github.com/22i/amc
|
||||
|
||||
Mineclone 2 source code:
|
||||
https://git.minetest.land/MineClone2/MineClone2
|
||||
|
||||
---
|
||||
NOTE: This MODule requires Glow Squids in order for all features to work 100% correctly. Glow Squids are currently
|
||||
in review by the MineClone 2 Team, and should be available soon after this initial release of the new signs.
|
122
mods/ITEMS/mcl_signs/SIGNS_API_DOC.txt
Normal file
|
@ -0,0 +1,122 @@
|
|||
---
|
||||
--- Generated by EmmyLua.
|
||||
--- Created by Michieal (FaerRaven).
|
||||
--- DateTime: 10/22/22 3:44 PM
|
||||
---
|
||||
|
||||
SIGNS API
|
||||
|
||||
--- How to Use:
|
||||
|
||||
The simplest way to create a new sign is to use mcl_signs.register_sign [mcl_signs.register_sign (modname, color, _name,
|
||||
ttsign)]. It's an all-in-one sign creator. It makes use of the standard textures for the signs, and colors them based on
|
||||
the color code that you give it, and names it "mcl_signs:wall_sign" + _name. So, using the spruce sign to illustrate, it
|
||||
would be named "mcl_signs:wall_sign_sprucewood", as we made _name equal to "_sprucewood" after the name of the
|
||||
registered wood.
|
||||
|
||||
To create a sign with specific textures: use the mcl_signs.register_sign_custom [mcl_signs.register_sign_custom
|
||||
(modname, _name, tiles, color, inventory_image, wield_image, ttsign)]. Like the register_sign() function, this is also an
|
||||
all-in-one sign creation function. With this function you can designate what textures to use, and give them a specified
|
||||
color. This function follows the same naming conventions.
|
||||
|
||||
If you wish to override / recreate one of the predefined signs, you may also do that. The reregister_sign() and
|
||||
reregister_sign_custom() functions will replace an existing sign's definition with a new one. Caution, ONLY use this on
|
||||
existing signs. If the sign doesn't exist, use the regular register_sign* functions.
|
||||
|
||||
--- What the parameters mean, and what they do:
|
||||
|
||||
* modname: optional (pass "" or "false" to ignore), for using mcl_signs with other mods to allow the creation of a sign
|
||||
from the mod's wood (if installed). Use this to prevent failures of the specific mod is not installed that has the needed
|
||||
information (textures, wood, etc.) Setting this is important, because it prevents items from being registered if the
|
||||
mod in not installed.
|
||||
|
||||
* tiles: the texture file to use for the sign's node.
|
||||
|
||||
* color: color the texture file to use with this color. Use white (#FFFFFF) to negate the color, and just use the
|
||||
texture as is.
|
||||
|
||||
* inventory_image: the texture file to use for the sign's display in inventory.
|
||||
|
||||
* wield_image: the texture file to use for the sign's weilded (in hand) object.
|
||||
|
||||
* _name: the sign's name suffix, such as "_dark" or "_sprucewood", etc., appended to "wall_sign" or "standing_sign"
|
||||
|
||||
* ttsign: the tool tip of the sign that gets translated. Shown when the mouse hovers the inventory sign. ttsign stands
|
||||
for translated tooltip sign.
|
||||
|
||||
* wood_item_string: example: "mcl_core:wood", "mcl_core:sprucewood" or "mymod:mywood". This is used when defining the
|
||||
recipe for the sign.
|
||||
|
||||
--- Other Functions of Importance:
|
||||
|
||||
* register_dye [mcl_signs.register_dye (modname, item_name, color_code)] -- this registers a new dye that the sign knows
|
||||
about so that the player can color their signs with the dye.
|
||||
Parameters:
|
||||
modname: your mod / module's name. make sure to use this for compatibility.
|
||||
item_name: the item_string of the dye to register.
|
||||
color_code: the hex code for the color to make the lettering. Also called HTML color code. Ex. "#FFFFFF" is white.
|
||||
|
||||
* register_sign_craft [mcl_signs.register_sign_craft(modname, wood_item_string, _name)] -- this is what creates the
|
||||
recipes for the sign, and makes the sign "burnable". Typically called right after the register_sign* functions.
|
||||
Parameters:
|
||||
_name: MUST be the same name as used for the sign. So, if your sign _name is "_sprucewood" then this should be too.
|
||||
wood_item_string: the item_string of the wood to use for the sign's recipe. Example: "mcl_core:wood" (default oak).
|
||||
modname: like with the other functions that has this parameter, used to make sure that nothing breaks.
|
||||
|
||||
* make_lbm() [mcl_signs.make_lbm()] -- This innocuous function is very important. This is the function that makes the
|
||||
signs work after reloading the game. This function is the last to be called in your sign creation work flow. Note, you
|
||||
do not need to call this function after every definition, just at the end of the last definition.
|
||||
(See Example WorkFlow below.)
|
||||
|
||||
--- Example Workflow for sign creation.
|
||||
|
||||
* these are, at the time of writing, a selection of the actual signs' definitions. Note the functions called, and when.
|
||||
|
||||
-- ---------------------------- --
|
||||
-- Register Signs for use. --
|
||||
-- ---------------------------- --
|
||||
|
||||
-- sprucewood Sign
|
||||
mcl_signs.register_sign_custom("mcl_core", "_sprucewood",
|
||||
"mcl_signs_sign_dark.png","#ffffff", "default_sign_dark.png",
|
||||
"default_sign_dark.png", "Spruce Sign"
|
||||
)
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:sprucewood", "_sprucewood")
|
||||
|
||||
-- darkwood Sign
|
||||
mcl_signs.register_sign_custom("mcl_core", "_darkwood",
|
||||
"mcl_signs_sign_greyscale.png","#856443", "default_sign_greyscale.png",
|
||||
"default_sign_greyscale.png", "Dark Oak Sign"
|
||||
)
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:darkwood", "_darkwood")
|
||||
|
||||
-- acaciawood Sign
|
||||
mcl_signs.register_sign("mcl_core", "#ea7479", "_acaciawood", "Acacia Sign")
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:acaciawood", "_acaciawood")
|
||||
|
||||
-- junglewood Sign
|
||||
mcl_signs.register_sign("mcl_core", "#866249", "_junglewood", "Jungle Sign")
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:junglewood", "_junglewood")
|
||||
|
||||
-- Register the LBMs for the created signs.
|
||||
mcl_signs.make_lbm()
|
||||
|
||||
--- -----------------------------------------------------------------------------
|
||||
|
||||
* If you wish to use a recipe other than the standard sign recipe, you will need to define your own recipe. In doing so,
|
||||
use this output line:
|
||||
output = "mcl_signs:wall_sign" .. _name .. " 3",
|
||||
where _name is the same string that you have used throughout your sign's workflow. That way, when players make the recipe,
|
||||
they get your sign (x3).
|
||||
|
||||
--- Future landmarks on the horizon for the Signs API:
|
||||
|
||||
* Once the forthcoming Hanging Signs are in Minecraft, and we implement the code for them in here, hanging signs will
|
||||
automatically exist as part of the signs' package. You won't have to change any of your code, it'll just be more
|
||||
functional. :)
|
||||
|
||||
* if you have suggestions, comments, etc., please contact me on MineClone 2's Discord server.
|
||||
|
||||
And that... is all there is to it!
|
||||
|
||||
-- written by Michieal.
|
|
@ -1,517 +1,31 @@
|
|||
---
|
||||
--- Generated by EmmyLua.
|
||||
--- Created by Michieal (FaerRaven).
|
||||
--- DateTime: 10/14/22 4:05 PM
|
||||
---
|
||||
|
||||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
-- Signs API
|
||||
dofile(modpath .. "/signs_api.lua")
|
||||
|
||||
-- LOCALIZATION
|
||||
local S = minetest.get_translator(modname)
|
||||
local F = minetest.formspec_escape
|
||||
|
||||
local table = table
|
||||
|
||||
-- Load the characters map (characters.txt)
|
||||
--[[ File format of characters.txt:
|
||||
It's an UTF-8 encoded text file that contains metadata for all supported characters. It contains a sequence of info blocks, one for each character. Each info block is made out of 3 lines:
|
||||
Line 1: The literal UTF-8 encoded character
|
||||
Line 2: Name of the texture file for this character minus the “.png” suffix; found in the “textures/” sub-directory
|
||||
Line 3: Currently ignored. Previously this was for the character width in pixels
|
||||
|
||||
After line 3, another info block may follow. This repeats until the end of the file.
|
||||
|
||||
All character files must be 5 or 6 pixels wide (5 pixels are preferred)
|
||||
]]
|
||||
|
||||
local chars_file = io.open(modpath.."/characters.txt", "r")
|
||||
-- FIXME: Support more characters (many characters are missing). Currently ASCII and Latin-1 Supplement are supported.
|
||||
local charmap = {}
|
||||
if not chars_file then
|
||||
minetest.log("error", "[mcl_signs] : character map file not found")
|
||||
else
|
||||
while true do
|
||||
local char = chars_file:read("*l")
|
||||
if char == nil then
|
||||
break
|
||||
end
|
||||
local img = chars_file:read("*l")
|
||||
chars_file:read("*l")
|
||||
charmap[char] = img
|
||||
end
|
||||
end
|
||||
|
||||
-- CONSTANTS
|
||||
local SIGN_WIDTH = 115
|
||||
|
||||
local LINE_LENGTH = 15
|
||||
local NUMBER_OF_LINES = 4
|
||||
|
||||
local LINE_HEIGHT = 14
|
||||
local CHAR_WIDTH = 5
|
||||
|
||||
|
||||
-- Helper functions
|
||||
local function round(num, idp)
|
||||
local mult = 10^(idp or 0)
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
local function string_to_array(str)
|
||||
local tab = {}
|
||||
for i=1,string.len(str) do
|
||||
table.insert(tab, string.sub(str, i,i))
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
local function string_to_line_array(str)
|
||||
local tab = {}
|
||||
local current = 1
|
||||
local linechar = 1
|
||||
tab[1] = ""
|
||||
for _,char in ipairs(string_to_array(str)) do
|
||||
-- New line
|
||||
if char == "\n" then
|
||||
current = current + 1
|
||||
tab[current] = ""
|
||||
linechar = 1
|
||||
else
|
||||
tab[current] = tab[current]..char
|
||||
linechar = linechar + 1
|
||||
end
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
local function create_lines(text)
|
||||
local line_num = 1
|
||||
local tab = {}
|
||||
for _, line in ipairs(string_to_line_array(text)) do
|
||||
if line_num > NUMBER_OF_LINES then
|
||||
break
|
||||
end
|
||||
table.insert(tab, line)
|
||||
line_num = line_num + 1
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
local function generate_line(s, ypos)
|
||||
local i = 1
|
||||
local parsed = {}
|
||||
local width = 0
|
||||
local chars = 0
|
||||
local printed_char_width = CHAR_WIDTH + 1
|
||||
while chars < LINE_LENGTH and i <= #s do
|
||||
local file
|
||||
-- Get and render character
|
||||
if charmap[s:sub(i, i)] then
|
||||
file = charmap[s:sub(i, i)]
|
||||
i = i + 1
|
||||
elseif i < #s and charmap[s:sub(i, i + 1)] then
|
||||
file = charmap[s:sub(i, i + 1)]
|
||||
i = i + 2
|
||||
else
|
||||
-- No character image found.
|
||||
-- Use replacement character:
|
||||
file = "_rc"
|
||||
i = i + 1
|
||||
minetest.log("verbose", "[mcl_signs] Unknown symbol in '"..s.."' at "..i)
|
||||
end
|
||||
if file then
|
||||
width = width + printed_char_width
|
||||
table.insert(parsed, file)
|
||||
chars = chars + 1
|
||||
end
|
||||
end
|
||||
width = width - 1
|
||||
|
||||
local texture = ""
|
||||
local xpos = math.floor((SIGN_WIDTH - width) / 2)
|
||||
for i = 1, #parsed do
|
||||
texture = texture..":"..xpos..","..ypos.."="..parsed[i]..".png"
|
||||
xpos = xpos + printed_char_width
|
||||
end
|
||||
return texture
|
||||
end
|
||||
|
||||
local function generate_texture(lines, signnodename)
|
||||
local texture = "[combine:"..SIGN_WIDTH.."x"..SIGN_WIDTH
|
||||
local ypos
|
||||
if signnodename == "mcl_signs:wall_sign" then
|
||||
ypos = 30
|
||||
else
|
||||
ypos = 0
|
||||
end
|
||||
for i = 1, #lines do
|
||||
texture = texture..generate_line(lines[i], ypos)
|
||||
ypos = ypos + LINE_HEIGHT
|
||||
end
|
||||
return texture
|
||||
end
|
||||
|
||||
local n = 23/56 - 1/128
|
||||
|
||||
local signtext_info_wall = {
|
||||
{delta = {x = 0, y = 0, z = n}, yaw = 0},
|
||||
{delta = {x = n, y = 0, z = 0}, yaw = math.pi / -2},
|
||||
{delta = {x = 0, y = 0, z = -n}, yaw = math.pi},
|
||||
{delta = {x = -n, y = 0, z = 0}, yaw = math.pi / 2},
|
||||
}
|
||||
|
||||
local signtext_info_standing = {}
|
||||
|
||||
local m = -1/16 + 1/64
|
||||
|
||||
for rot=0, 15 do
|
||||
local yaw = math.pi*2 - (((math.pi*2) / 16) * rot)
|
||||
local delta = vector.multiply(minetest.yaw_to_dir(yaw), m)
|
||||
-- Offset because sign is a bit above node boundaries
|
||||
delta.y = delta.y + 2/28
|
||||
table.insert(signtext_info_standing, { delta = delta, yaw = yaw })
|
||||
end
|
||||
|
||||
local function get_rotation_level(facedir, nodename)
|
||||
local rl = facedir * 4
|
||||
if nodename == "mcl_signs:standing_sign22_5" then
|
||||
rl = rl + 1
|
||||
elseif nodename == "mcl_signs:standing_sign45" then
|
||||
rl = rl + 2
|
||||
elseif nodename == "mcl_signs:standing_sign67_5" then
|
||||
rl = rl + 3
|
||||
end
|
||||
return rl
|
||||
end
|
||||
|
||||
local function get_wall_signtext_info(param2, nodename)
|
||||
local dir = minetest.wallmounted_to_dir(param2)
|
||||
if dir.x > 0 then
|
||||
return 2
|
||||
elseif dir.z > 0 then
|
||||
return 1
|
||||
elseif dir.x < 0 then
|
||||
return 4
|
||||
else
|
||||
return 3
|
||||
end
|
||||
end
|
||||
|
||||
local sign_groups = {handy=1,axey=1, deco_block=1, material_wood=1, attached_node=1, dig_by_piston=1, flammable=-1}
|
||||
|
||||
local function destruct_sign(pos)
|
||||
local objects = minetest.get_objects_inside_radius(pos, 0.5)
|
||||
for _, v in ipairs(objects) do
|
||||
local ent = v:get_luaentity()
|
||||
if ent and ent.name == "mcl_signs:text" then
|
||||
v:remove()
|
||||
end
|
||||
end
|
||||
local players = minetest.get_connected_players()
|
||||
for p=1, #players do
|
||||
if vector.distance(players[p]:get_pos(), pos) <= 30 then
|
||||
minetest.close_formspec(players[p]:get_player_name(), "mcl_signs:set_text_"..pos.x.."_"..pos.y.."_"..pos.z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function update_sign(pos, fields, sender, force_remove)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if not meta then
|
||||
return
|
||||
end
|
||||
local text = meta:get_string("text")
|
||||
if fields and (text == "" and fields.text) then
|
||||
meta:set_string("text", fields.text)
|
||||
text = fields.text
|
||||
end
|
||||
if text == nil then
|
||||
text = ""
|
||||
end
|
||||
|
||||
local sign_info
|
||||
local n = minetest.get_node(pos)
|
||||
local nn = n.name
|
||||
if nn == "mcl_signs:standing_sign" or nn == "mcl_signs:standing_sign22_5" or nn == "mcl_signs:standing_sign45" or nn == "mcl_signs:standing_sign67_5" then
|
||||
sign_info = signtext_info_standing[get_rotation_level(n.param2, nn) + 1]
|
||||
elseif nn == "mcl_signs:wall_sign" then
|
||||
sign_info = signtext_info_wall[get_wall_signtext_info(n.param2)]
|
||||
end
|
||||
if sign_info == nil then
|
||||
minetest.log("error", "[mcl_signs] Missing sign_info!")
|
||||
return
|
||||
end
|
||||
|
||||
local objects = minetest.get_objects_inside_radius(pos, 0.5)
|
||||
local text_entity
|
||||
for _, v in ipairs(objects) do
|
||||
local ent = v:get_luaentity()
|
||||
if ent and ent.name == "mcl_signs:text" then
|
||||
if force_remove then
|
||||
v:remove()
|
||||
else
|
||||
text_entity = v
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not text_entity then
|
||||
text_entity = minetest.add_entity({
|
||||
x = pos.x + sign_info.delta.x,
|
||||
y = pos.y + sign_info.delta.y,
|
||||
z = pos.z + sign_info.delta.z}, "mcl_signs:text")
|
||||
end
|
||||
text_entity:get_luaentity()._signnodename = nn
|
||||
text_entity:set_properties({textures={generate_texture(create_lines(text), nn)}})
|
||||
|
||||
text_entity:set_yaw(sign_info.yaw)
|
||||
end
|
||||
|
||||
local function show_formspec(player, pos)
|
||||
minetest.show_formspec(
|
||||
player:get_player_name(),
|
||||
"mcl_signs:set_text_"..pos.x.."_"..pos.y.."_"..pos.z,
|
||||
"size[6,3]textarea[0.25,0.25;6,1.5;text;"..F(S("Enter sign text:"))..";]label[0,1.5;"..F(S("Maximum line length: 15")).."\n"..F(S("Maximum lines: 4")).."]button_exit[0,2.5;6,1;submit;"..F(S("Done")).."]"
|
||||
)
|
||||
end
|
||||
|
||||
-- HANDLE THE FORMSPEC CALLBACK
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname:find("mcl_signs:set_text_") == 1 then
|
||||
local x, y, z = formname:match("mcl_signs:set_text_(.-)_(.-)_(.*)")
|
||||
local pos = { x = tonumber(x), y = tonumber(y), z = tonumber(z) }
|
||||
if not pos or not pos.x or not pos.y or not pos.z then return end
|
||||
update_sign(pos, fields, player)
|
||||
if not pos or not pos.x or not pos.y or not pos.z then
|
||||
return
|
||||
end
|
||||
mcl_signs:update_sign(pos, fields, player)
|
||||
end
|
||||
end)
|
||||
|
||||
local node_sounds
|
||||
if minetest.get_modpath("mcl_sounds") then
|
||||
node_sounds = mcl_sounds.node_sound_wood_defaults()
|
||||
end
|
||||
|
||||
minetest.register_node("mcl_signs:wall_sign", {
|
||||
description = S("Sign"),
|
||||
_tt_help = S("Can be written"),
|
||||
_doc_items_longdesc = S("Signs can be written and come in two variants: Wall sign and sign on a sign post. Signs can be placed on the top and the sides of other blocks, but not below them."),
|
||||
_doc_items_usagehelp = S("After placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. Not all characters are supported. The text can not be changed once it has been written; you have to break and place the sign again."),
|
||||
inventory_image = "default_sign.png",
|
||||
walkable = false,
|
||||
is_ground_content = false,
|
||||
wield_image = "default_sign.png",
|
||||
node_placement_prediction = "",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "wallmounted",
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_signs_signonwallmount.obj",
|
||||
selection_box = {type = "wallmounted", wall_side = {-0.5, -7/28, -0.5, -23/56, 7/28, 0.5}},
|
||||
tiles = {"mcl_signs_sign.png"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
groups = sign_groups,
|
||||
stack_max = 16,
|
||||
sounds = node_sounds,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local above = pointed_thing.above
|
||||
local under = pointed_thing.under
|
||||
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
local node_under = minetest.get_node(under)
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
|
||||
return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
|
||||
local dir = vector.subtract(under, above)
|
||||
|
||||
-- Only build when it's legal
|
||||
local abovenodedef = minetest.registered_nodes[minetest.get_node(above).name]
|
||||
if not abovenodedef or abovenodedef.buildable_to == false then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local wdir = minetest.dir_to_wallmounted(dir)
|
||||
|
||||
--local placer_pos = placer:get_pos()
|
||||
|
||||
local fdir = minetest.dir_to_facedir(dir)
|
||||
|
||||
local sign_info
|
||||
local nodeitem = ItemStack(itemstack)
|
||||
-- Ceiling
|
||||
if wdir == 0 then
|
||||
--how would you add sign to ceiling?
|
||||
return itemstack
|
||||
-- Floor
|
||||
elseif wdir == 1 then
|
||||
-- Standing sign
|
||||
|
||||
-- Determine the sign rotation based on player's yaw
|
||||
local yaw = math.pi*2 - placer:get_look_horizontal()
|
||||
|
||||
-- Select one of 16 possible rotations (0-15)
|
||||
local rotation_level = round((yaw / (math.pi*2)) * 16)
|
||||
|
||||
if rotation_level > 15 then
|
||||
rotation_level = 0
|
||||
elseif rotation_level < 0 then
|
||||
rotation_level = 15
|
||||
end
|
||||
|
||||
-- The actual rotation is a combination of predefined mesh and facedir (see node definition)
|
||||
if rotation_level % 4 == 0 then
|
||||
nodeitem:set_name("mcl_signs:standing_sign")
|
||||
elseif rotation_level % 4 == 1 then
|
||||
nodeitem:set_name("mcl_signs:standing_sign22_5")
|
||||
elseif rotation_level % 4 == 2 then
|
||||
nodeitem:set_name("mcl_signs:standing_sign45")
|
||||
elseif rotation_level % 4 == 3 then
|
||||
nodeitem:set_name("mcl_signs:standing_sign67_5")
|
||||
end
|
||||
fdir = math.floor(rotation_level / 4)
|
||||
|
||||
-- Place the node!
|
||||
local _, success = minetest.item_place_node(nodeitem, placer, pointed_thing, fdir)
|
||||
if not success then
|
||||
return itemstack
|
||||
end
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
sign_info = signtext_info_standing[rotation_level + 1]
|
||||
-- Side
|
||||
else
|
||||
-- Wall sign
|
||||
local _, success = minetest.item_place_node(itemstack, placer, pointed_thing, wdir)
|
||||
if not success then
|
||||
return itemstack
|
||||
end
|
||||
sign_info = signtext_info_wall[fdir + 1]
|
||||
end
|
||||
|
||||
-- Determine spawn position of entity
|
||||
local place_pos
|
||||
if minetest.registered_nodes[node_under.name].buildable_to then
|
||||
place_pos = under
|
||||
else
|
||||
place_pos = above
|
||||
end
|
||||
|
||||
local text_entity = minetest.add_entity({
|
||||
x = place_pos.x + sign_info.delta.x,
|
||||
y = place_pos.y + sign_info.delta.y,
|
||||
z = place_pos.z + sign_info.delta.z}, "mcl_signs:text")
|
||||
text_entity:set_yaw(sign_info.yaw)
|
||||
text_entity:get_luaentity()._signnodename = nodeitem:get_name()
|
||||
|
||||
minetest.sound_play({name="default_place_node_hard", gain=1.0}, {pos = place_pos}, true)
|
||||
|
||||
show_formspec(placer, place_pos)
|
||||
return itemstack
|
||||
end,
|
||||
on_destruct = destruct_sign,
|
||||
on_punch = function(pos, node, puncher)
|
||||
update_sign(pos)
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode)
|
||||
if mode == screwdriver.ROTATE_FACE then
|
||||
local r = screwdriver.rotate.wallmounted(pos, node, mode)
|
||||
node.param2 = r
|
||||
minetest.swap_node(pos, node)
|
||||
update_sign(pos, nil, nil, true)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end,
|
||||
_mcl_hardness = 1,
|
||||
_mcl_blast_resistance = 1,
|
||||
})
|
||||
|
||||
-- Standing sign nodes.
|
||||
-- 4 rotations at 0°, 22.5°, 45° and 67.5°.
|
||||
-- These are 4 out of 16 possible rotations.
|
||||
-- With facedir the remaining 12 rotations are constructed.
|
||||
|
||||
-- 0°
|
||||
local ssign = {
|
||||
paramtype = "light",
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
is_ground_content = false,
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_signs_sign.obj",
|
||||
selection_box = {type = "fixed", fixed = {-0.2, -0.5, -0.2, 0.2, 0.5, 0.2}},
|
||||
tiles = {"mcl_signs_sign.png"},
|
||||
groups = sign_groups,
|
||||
drop = "mcl_signs:wall_sign",
|
||||
stack_max = 16,
|
||||
sounds = node_sounds,
|
||||
|
||||
on_destruct = destruct_sign,
|
||||
on_punch = function(pos, node, puncher)
|
||||
update_sign(pos)
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode)
|
||||
if mode == screwdriver.ROTATE_FACE then
|
||||
node.name = "mcl_signs:standing_sign22_5"
|
||||
minetest.swap_node(pos, node)
|
||||
elseif mode == screwdriver.ROTATE_AXIS then
|
||||
return false
|
||||
end
|
||||
update_sign(pos, nil, nil, true)
|
||||
return true
|
||||
end,
|
||||
|
||||
_mcl_hardness = 1,
|
||||
_mcl_blast_resistance = 1,
|
||||
}
|
||||
|
||||
minetest.register_node("mcl_signs:standing_sign", ssign)
|
||||
|
||||
-- 22.5°
|
||||
local ssign22_5 = table.copy(ssign)
|
||||
ssign22_5.mesh = "mcl_signs_sign22.5.obj"
|
||||
ssign22_5.on_rotate = function(pos, node, user, mode)
|
||||
if mode == screwdriver.ROTATE_FACE then
|
||||
node.name = "mcl_signs:standing_sign45"
|
||||
minetest.swap_node(pos, node)
|
||||
elseif mode == screwdriver.ROTATE_AXIS then
|
||||
return false
|
||||
end
|
||||
update_sign(pos, nil, nil, true)
|
||||
return true
|
||||
end
|
||||
minetest.register_node("mcl_signs:standing_sign22_5", ssign22_5)
|
||||
|
||||
-- 45°
|
||||
local ssign45 = table.copy(ssign)
|
||||
ssign45.mesh = "mcl_signs_sign45.obj"
|
||||
ssign45.on_rotate = function(pos, node, user, mode)
|
||||
if mode == screwdriver.ROTATE_FACE then
|
||||
node.name = "mcl_signs:standing_sign67_5"
|
||||
minetest.swap_node(pos, node)
|
||||
elseif mode == screwdriver.ROTATE_AXIS then
|
||||
return false
|
||||
end
|
||||
update_sign(pos, nil, nil, true)
|
||||
return true
|
||||
end
|
||||
minetest.register_node("mcl_signs:standing_sign45", ssign45)
|
||||
|
||||
-- 67.5°
|
||||
local ssign67_5 = table.copy(ssign)
|
||||
ssign67_5.mesh = "mcl_signs_sign67.5.obj"
|
||||
ssign67_5.on_rotate = function(pos, node, user, mode)
|
||||
if mode == screwdriver.ROTATE_FACE then
|
||||
node.name = "mcl_signs:standing_sign"
|
||||
node.param2 = (node.param2 + 1) % 4
|
||||
minetest.swap_node(pos, node)
|
||||
elseif mode == screwdriver.ROTATE_AXIS then
|
||||
return false
|
||||
end
|
||||
update_sign(pos, nil, nil, true)
|
||||
return true
|
||||
end
|
||||
minetest.register_node("mcl_signs:standing_sign67_5", ssign67_5)
|
||||
|
||||
-- This defines the text entity for the lettering of the sign.
|
||||
-- FIXME: Prevent entity destruction by /clearobjects
|
||||
minetest.register_entity("mcl_signs:text", {
|
||||
pointable = false,
|
||||
|
@ -523,58 +37,103 @@ minetest.register_entity("mcl_signs:text", {
|
|||
_signnodename = nil, -- node name of sign node to which the text belongs
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
|
||||
local meta = minetest.get_meta(self.object:get_pos())
|
||||
local text = meta:get_string("text")
|
||||
local text_color = meta:get_string("mcl_signs:text_color")
|
||||
local glowing_sign = meta:get_string("mcl_signs:glowing_sign")
|
||||
if staticdata and staticdata ~= "" then
|
||||
local des = minetest.deserialize(staticdata)
|
||||
if des then
|
||||
self._signnodename = des._signnodename
|
||||
if des._text_color ~= nil and des._text_color ~= "" then
|
||||
self.text_color = des._text_color
|
||||
end
|
||||
if des._glowing_sign ~= nil and des._glowing_sign ~= "" then
|
||||
self.glowing_sign = des._glowing_sign
|
||||
end
|
||||
end
|
||||
local meta = minetest.get_meta(self.object:get_pos())
|
||||
local text = meta:get_string("text")
|
||||
end
|
||||
|
||||
if text_color == "" or text_color == nil then
|
||||
text_color = "#000000" -- default to black text.
|
||||
meta:set_string("mcl_signs:text_color", text_color)
|
||||
end
|
||||
|
||||
if glowing_sign == "" or glowing_sign == nil then
|
||||
glowing_sign = "false" -- default to not glowing.
|
||||
meta:set_string("mcl_signs:glowing_sign", glowing_sign)
|
||||
end
|
||||
|
||||
self.object:set_properties({
|
||||
textures={generate_texture(create_lines(text), self._signnodename)},
|
||||
textures = { mcl_signs:create_lettering(text, self._signnodename, text_color) },
|
||||
})
|
||||
if glowing_sign == "true" then
|
||||
self.object:set_properties({
|
||||
glow = 6, --sign_glow,
|
||||
})
|
||||
end
|
||||
|
||||
self.object:set_armor_groups({ immortal = 1 })
|
||||
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
local out = { _signnodename = self._signnodename }
|
||||
local out = {
|
||||
_signnodename = self._signnodename,
|
||||
}
|
||||
return minetest.serialize(out)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "mcl_signs:wall_sign",
|
||||
burntime = 10,
|
||||
})
|
||||
-- Build the signs x,y,z & rotations so that they work. (IE, do not remove!)
|
||||
mcl_signs.build_signs_info()
|
||||
|
||||
if minetest.get_modpath("mcl_core") then
|
||||
minetest.register_craft({
|
||||
output = "mcl_signs:wall_sign 3",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"", "mcl_core:stick", ""},
|
||||
}
|
||||
})
|
||||
end
|
||||
-- ---------------------------- --
|
||||
-- Register Signs for use. --
|
||||
-- ---------------------------- --
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign")
|
||||
doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign22_5")
|
||||
doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign45")
|
||||
doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign67_5")
|
||||
end
|
||||
-- Standard (original) Sign
|
||||
mcl_signs.register_sign("mcl_core", "#ffffff", "", "Sign")
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:wood", "")
|
||||
|
||||
-- birchwood Sign "#d5cb8d" / "#ffdba7"
|
||||
mcl_signs.register_sign_custom("mcl_core", "_birchwood",
|
||||
"mcl_signs_sign_greyscale.png","#ffdba7", "default_sign_greyscale.png",
|
||||
"default_sign_greyscale.png", "Birch Sign"
|
||||
)
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:birchwood", "_birchwood")
|
||||
|
||||
-- sprucewood Sign
|
||||
mcl_signs.register_sign_custom("mcl_core", "_sprucewood",
|
||||
"mcl_signs_sign_dark.png","#ffffff", "default_sign_dark.png",
|
||||
"default_sign_dark.png", "Spruce Sign"
|
||||
)
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:sprucewood", "_sprucewood")
|
||||
|
||||
-- darkwood Sign "#291f1a" / "#856443"
|
||||
mcl_signs.register_sign_custom("mcl_core", "_darkwood",
|
||||
"mcl_signs_sign_greyscale.png","#856443", "default_sign_greyscale.png",
|
||||
"default_sign_greyscale.png", "Dark Oak Sign"
|
||||
)
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:darkwood", "_darkwood")
|
||||
|
||||
-- junglewood Sign
|
||||
mcl_signs.register_sign("mcl_core", "#866249", "_junglewood", "Jungle Sign")
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:junglewood", "_junglewood")
|
||||
|
||||
-- acaciawood Sign "b8693d"
|
||||
mcl_signs.register_sign("mcl_core", "#ea7479", "_acaciawood", "Acacia Sign")
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:acaciawood", "_acaciawood")
|
||||
|
||||
-- mangrove_wood Sign "#c7545c"
|
||||
mcl_signs.register_sign("mcl_core", "#b8693d", "_mangrove_wood", "Mangrove Sign")
|
||||
mcl_signs.register_sign_craft("mcl_core", "mcl_core:mangrove_wood", "_mangrove_wood")
|
||||
|
||||
-- Register the LBMs for the created signs.
|
||||
mcl_signs.make_lbm()
|
||||
|
||||
-- really ancient compatibility.
|
||||
minetest.register_alias("signs:sign_wall", "mcl_signs:wall_sign")
|
||||
minetest.register_alias("signs:sign_yard", "mcl_signs:standing_sign")
|
||||
|
||||
minetest.register_lbm({
|
||||
name = "mcl_signs:respawn_entities",
|
||||
label = "Respawn sign text entities",
|
||||
run_at_every_load = true,
|
||||
nodenames = { "mcl_signs:wall_sign", "mcl_signs:standing_sign", "mcl_signs:standing_sign22_5", "mcl_signs:standing_sign45", "mcl_signs:standing_sign67_5" },
|
||||
action = function(pos, node)
|
||||
update_sign(pos)
|
||||
end,
|
||||
})
|
||||
minetest.register_alias("mcl_signs:wall_sign_dark", "mcl_signs:wall_sign_sprucewood")
|
||||
minetest.register_alias("mcl_signs:standing_sign_dark", "mcl_signs:standing_sign_sprucewood")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# textdomain: mcl_signs
|
||||
Sign=
|
||||
Signs can be written and come in two variants: Wall sign and sign on a sign post. Signs can be placed on the top and the sides of other blocks, but not below them.=
|
||||
After placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. Not all characters are supported. The text can not be changed once it has been written; you have to break and place the sign again.=
|
||||
After placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. Not all characters are supported. The text can not be changed once it has been written; you have to break and place the sign again. Can be colored and made to glow.=
|
||||
Enter sign text:=
|
||||
Maximum line length: 15=
|
||||
Maximum lines: 4=
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
name = mcl_signs
|
||||
optional_depends = mcl_sounds, mcl_core, doc
|
||||
description = New and Improved signs - can be colored and made to glow.
|
||||
depends = mcl_core, mcl_sounds, mcl_dye, mcl_colors
|
||||
optional_depends = doc
|
||||
|
|
1878
mods/ITEMS/mcl_signs/signs_api.lua
Normal file
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 876 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 881 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 868 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 876 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 865 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 863 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 294 B |
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 891 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 889 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 886 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 292 B |
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 1.8 KiB |