Merge branch 'master' into totem_particles
|
@ -52,17 +52,17 @@ Each mod must provide `mod.conf`.
|
||||||
Each mod which add API functions should store functions inside a global table named like the mod.
|
Each mod which add API functions should store functions inside a global table named like the mod.
|
||||||
Public functions should not use self references but rather just access the table directly.
|
Public functions should not use self references but rather just access the table directly.
|
||||||
Functions should be defined in this way:
|
Functions should be defined in this way:
|
||||||
```
|
```lua
|
||||||
function mcl_xyz.stuff(param) end
|
function mcl_xyz.stuff(param) end
|
||||||
```
|
```
|
||||||
Insteed of this way:
|
Insteed of this way:
|
||||||
```
|
```lua
|
||||||
mcl_xyz.stuff = function(param) end
|
mcl_xyz.stuff = function(param) end
|
||||||
```
|
```
|
||||||
Indentation must be unified, more likely with tabs.
|
Indentation must be unified, more likely with tabs.
|
||||||
|
|
||||||
Time sensitive mods should make a local copy of most used API functions to improve performances.
|
Time sensitive mods should make a local copy of most used API functions to improve performances.
|
||||||
```
|
```lua
|
||||||
local vector = vector
|
local vector = vector
|
||||||
local get_node = minetest.get_node
|
local get_node = minetest.get_node
|
||||||
```
|
```
|
||||||
|
|
|
@ -538,3 +538,12 @@ function mcl_util.get_object_name(object)
|
||||||
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
|
return luaentity.nametag and luaentity.nametag ~= "" and luaentity.nametag or luaentity.description or luaentity.name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mcl_util.replace_mob(obj, mob)
|
||||||
|
local rot = obj:get_yaw()
|
||||||
|
local pos = obj:get_pos()
|
||||||
|
obj:remove()
|
||||||
|
obj = minetest.add_entity(pos, mob)
|
||||||
|
obj:set_yaw(rot)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
|
@ -84,7 +84,7 @@ local function attach_object(self, obj)
|
||||||
end
|
end
|
||||||
end, name)
|
end, name)
|
||||||
obj:set_look_horizontal(yaw)
|
obj:set_look_horizontal(yaw)
|
||||||
mcl_tmp_message.message(obj, S("Sneak to dismount"))
|
mcl_title.set(obj, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
||||||
else
|
else
|
||||||
obj:get_luaentity()._old_visual_size = visual_size
|
obj:get_luaentity()._old_visual_size = visual_size
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ Boats are used to travel on the surface of water.=Les bateaux sont utilisés pou
|
||||||
Dark Oak Boat=Bateau en Chêne Noir
|
Dark Oak Boat=Bateau en Chêne Noir
|
||||||
Jungle Boat=Bateau en Acajou
|
Jungle Boat=Bateau en Acajou
|
||||||
Oak Boat=Bateau en Chêne
|
Oak Boat=Bateau en Chêne
|
||||||
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Rightclick the boat again to leave it, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Cliquez de nouveau avec le bouton droit sur le bateau pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
Rightclick on a water source to place the boat. Rightclick the boat to enter it. Use [Left] and [Right] to steer, [Forwards] to speed up and [Backwards] to slow down or move backwards. Use [Sneak] to leave the boat, punch the boat to make it drop as an item.=Faites un clic droit sur une source d'eau pour placer le bateau. Faites un clic droit sur le bateau pour y entrer. Utilisez [Gauche] et [Droite] pour diriger, [Avant] pour accélérer et [Arrière] pour ralentir ou reculer. Utilisez [Sneak] pour le quitter, frappez le bateau pour le faire tomber en tant qu'objet.
|
||||||
Spruce Boat=Bateau en Sapin
|
Spruce Boat=Bateau en Sapin
|
||||||
Water vehicle=Véhicule aquatique
|
Water vehicle=Véhicule aquatique
|
||||||
|
Sneak to dismount=
|
|
@ -1,7 +1,7 @@
|
||||||
name = mcl_boats
|
name = mcl_boats
|
||||||
author = PilzAdam
|
author = PilzAdam
|
||||||
description = Adds drivable boats.
|
description = Adds drivable boats.
|
||||||
depends = mcl_player, flowlib
|
depends = mcl_player, flowlib, mcl_title
|
||||||
optional_depends = mcl_core, doc_identifier
|
optional_depends = mcl_core, doc_identifier
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -67,14 +67,9 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not storage.burn_time or burn_time >= storage.burn_time then
|
if not storage.burn_time or burn_time >= storage.burn_time then
|
||||||
if obj:is_player() and not storage.fire_hud_id then
|
if obj:is_player() then
|
||||||
storage.fire_hud_id = obj:hud_add({
|
mcl_burning.channels[obj]:send_all(tostring(mcl_burning.animation_frames))
|
||||||
hud_elem_type = "image",
|
mcl_burning.channels[obj]:send_all("start")
|
||||||
position = {x = 0.5, y = 0.5},
|
|
||||||
scale = {x = -100, y = -100},
|
|
||||||
text = "mcl_burning_entity_flame_animated.png^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. 1,
|
|
||||||
z_index = 1000,
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
storage.burn_time = burn_time
|
storage.burn_time = burn_time
|
||||||
storage.fire_damage_timer = 0
|
storage.fire_damage_timer = 0
|
||||||
|
@ -95,7 +90,6 @@ function mcl_burning.set_on_fire(obj, burn_time)
|
||||||
fire_entity:set_properties({visual_size = size})
|
fire_entity:set_properties({visual_size = size})
|
||||||
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
fire_entity:set_attach(obj, "", offset, {x = 0, y = 0, z = 0})
|
||||||
local fire_luaentity = fire_entity:get_luaentity()
|
local fire_luaentity = fire_entity:get_luaentity()
|
||||||
fire_luaentity:update_frame(obj, storage)
|
|
||||||
|
|
||||||
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
for _, other in pairs(minetest.get_objects_inside_radius(fire_entity:get_pos(), 0)) do
|
||||||
local other_luaentity = other:get_luaentity()
|
local other_luaentity = other:get_luaentity()
|
||||||
|
@ -111,9 +105,7 @@ function mcl_burning.extinguish(obj)
|
||||||
if mcl_burning.is_burning(obj) then
|
if mcl_burning.is_burning(obj) then
|
||||||
local storage = mcl_burning.get_storage(obj)
|
local storage = mcl_burning.get_storage(obj)
|
||||||
if obj:is_player() then
|
if obj:is_player() then
|
||||||
if storage.fire_hud_id then
|
mcl_burning.channels[obj]:send_all("stop")
|
||||||
obj:hud_remove(storage.fire_hud_id)
|
|
||||||
end
|
|
||||||
mcl_burning.storage[obj] = {}
|
mcl_burning.storage[obj] = {}
|
||||||
else
|
else
|
||||||
storage.burn_time = nil
|
storage.burn_time = nil
|
||||||
|
@ -143,4 +135,4 @@ function mcl_burning.tick(obj, dtime, storage)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ local get_item_group = minetest.get_item_group
|
||||||
|
|
||||||
mcl_burning = {
|
mcl_burning = {
|
||||||
storage = {},
|
storage = {},
|
||||||
|
channels = {},
|
||||||
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
animation_frames = tonumber(minetest.settings:get("fire_animation_frames")) or 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,12 +55,11 @@ minetest.register_on_joinplayer(function(player)
|
||||||
end
|
end
|
||||||
|
|
||||||
mcl_burning.storage[player] = storage
|
mcl_burning.storage[player] = storage
|
||||||
|
mcl_burning.channels[player] = minetest.mod_channel_join("mcl_burning:" .. player:get_player_name())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
minetest.register_on_leaveplayer(function(player)
|
||||||
local storage = mcl_burning.storage[player]
|
player:get_meta():set_string("mcl_burning:data", minetest.serialize(mcl_burning.storage[player]))
|
||||||
storage.fire_hud_id = nil
|
|
||||||
player:get_meta():set_string("mcl_burning:data", minetest.serialize(storage))
|
|
||||||
mcl_burning.storage[player] = nil
|
mcl_burning.storage[player] = nil
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -68,27 +68,28 @@ minetest.register_entity("mcl_burning:fire", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
physical = false,
|
physical = false,
|
||||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||||
visual = "cube",
|
visual = "upright_sprite",
|
||||||
|
textures = {
|
||||||
|
name = "mcl_burning_entity_flame_animated.png",
|
||||||
|
animation = {
|
||||||
|
type = "vertical_frames",
|
||||||
|
aspect_w = 16,
|
||||||
|
aspect_h = 16,
|
||||||
|
length = 1.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spritediv = {x = 1, y = mcl_burning.animation_frames},
|
||||||
pointable = false,
|
pointable = false,
|
||||||
glow = -1,
|
glow = -1,
|
||||||
backface_culling = false,
|
backface_culling = false,
|
||||||
},
|
},
|
||||||
animation_frame = 0,
|
animation_frame = 0,
|
||||||
animation_timer = 0,
|
animation_timer = 0,
|
||||||
on_step = function(self, dtime)
|
on_activate = function(self)
|
||||||
local parent, storage = self:sanity_check()
|
self.object:set_sprite({x = 0, y = 0}, mcl_burning.animation_frames, 1.0 / mcl_burning.animation_frames)
|
||||||
|
end,
|
||||||
if parent then
|
on_step = function(self)
|
||||||
self.animation_timer = self.animation_timer + dtime
|
if not self:sanity_check() then
|
||||||
if self.animation_timer >= 0.1 then
|
|
||||||
self.animation_timer = 0
|
|
||||||
self.animation_frame = self.animation_frame + 1
|
|
||||||
if self.animation_frame > mcl_burning.animation_frames - 1 then
|
|
||||||
self.animation_frame = 0
|
|
||||||
end
|
|
||||||
self:update_frame(parent, storage)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -96,23 +97,15 @@ minetest.register_entity("mcl_burning:fire", {
|
||||||
local parent = self.object:get_attach()
|
local parent = self.object:get_attach()
|
||||||
|
|
||||||
if not parent then
|
if not parent then
|
||||||
return
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local storage = mcl_burning.get_storage(parent)
|
local storage = mcl_burning.get_storage(parent)
|
||||||
|
|
||||||
if not storage or not storage.burn_time then
|
if not storage or not storage.burn_time then
|
||||||
return
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return parent, storage
|
return true
|
||||||
end,
|
|
||||||
update_frame = function(self, parent, storage)
|
|
||||||
local frame_overlay = "^[opacity:180^[verticalframe:" .. mcl_burning.animation_frames .. ":" .. self.animation_frame
|
|
||||||
local fire_texture = "mcl_burning_entity_flame_animated.png" .. frame_overlay
|
|
||||||
self.object:set_properties({textures = {"blank.png", "blank.png", fire_texture, fire_texture, fire_texture, fire_texture}})
|
|
||||||
if parent:is_player() then
|
|
||||||
parent:hud_change(storage.fire_hud_id, "text", "mcl_burning_hud_flame_animated.png" .. frame_overlay)
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
|
@ -480,7 +480,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
get_staticdata = function(self)
|
get_staticdata = function(self)
|
||||||
return minetest.serialize({
|
local data = minetest.serialize({
|
||||||
itemstring = self.itemstring,
|
itemstring = self.itemstring,
|
||||||
always_collect = self.always_collect,
|
always_collect = self.always_collect,
|
||||||
age = self.age,
|
age = self.age,
|
||||||
|
@ -488,6 +488,39 @@ minetest.register_entity(":__builtin:item", {
|
||||||
_flowing = self._flowing,
|
_flowing = self._flowing,
|
||||||
_removed = self._removed,
|
_removed = self._removed,
|
||||||
})
|
})
|
||||||
|
-- sfan5 guessed that the biggest serializable item
|
||||||
|
-- entity would have a size of 65530 bytes. This has
|
||||||
|
-- been experimentally verified to be still too large.
|
||||||
|
--
|
||||||
|
-- anon5 has calculated that the biggest serializable
|
||||||
|
-- item entity has a size of exactly 65487 bytes:
|
||||||
|
--
|
||||||
|
-- 1. serializeString16 can handle max. 65535 bytes.
|
||||||
|
-- 2. The following engine metadata is always saved:
|
||||||
|
-- • 1 byte (version)
|
||||||
|
-- • 2 byte (length prefix)
|
||||||
|
-- • 14 byte “__builtin:item”
|
||||||
|
-- • 4 byte (length prefix)
|
||||||
|
-- • 2 byte (health)
|
||||||
|
-- • 3 × 4 byte = 12 byte (position)
|
||||||
|
-- • 4 byte (yaw)
|
||||||
|
-- • 1 byte (version 2)
|
||||||
|
-- • 2 × 4 byte = 8 byte (pitch and roll)
|
||||||
|
-- 3. This leaves 65487 bytes for the serialization.
|
||||||
|
if #data > 65487 then -- would crash the engine
|
||||||
|
local stack = ItemStack(self.itemstring)
|
||||||
|
stack:get_meta():from_table(nil)
|
||||||
|
self.itemstring = stack:to_string()
|
||||||
|
minetest.log(
|
||||||
|
"warning",
|
||||||
|
"Overlong item entity metadata removed: “" ..
|
||||||
|
self.itemstring ..
|
||||||
|
"” had serialized length of " ..
|
||||||
|
#data
|
||||||
|
)
|
||||||
|
return self:get_staticdata()
|
||||||
|
end
|
||||||
|
return data
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_activate = function(self, staticdata, dtime_s)
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
@ -575,7 +608,7 @@ minetest.register_entity(":__builtin:item", {
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dtime, moveresult)
|
||||||
if self._removed then
|
if self._removed then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
physical = false
|
physical = false
|
||||||
|
@ -642,6 +675,18 @@ minetest.register_entity(":__builtin:item", {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Destroy item when it collides with a cactus
|
||||||
|
if moveresult and moveresult.collides then
|
||||||
|
for _, collision in pairs(moveresult.collisions) do
|
||||||
|
local pos = collision.node_pos
|
||||||
|
if collision.type == "node" and minetest.get_node(pos).name == "mcl_core:cactus" then
|
||||||
|
self._removed = true
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Push item out when stuck inside solid opaque node
|
-- Push item out when stuck inside solid opaque node
|
||||||
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
if def and def.walkable and def.groups and def.groups.opaque == 1 then
|
||||||
local shootdir
|
local shootdir
|
||||||
|
|
|
@ -646,7 +646,7 @@ register_minecart(
|
||||||
if player then
|
if player then
|
||||||
mcl_player.player_set_animation(player, "sit" , 30)
|
mcl_player.player_set_animation(player, "sit" , 30)
|
||||||
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
|
player:set_eye_offset({x=0, y=-5.5, z=0},{x=0, y=-4, z=0})
|
||||||
mcl_tmp_message.message(clicker, S("Sneak to dismount"))
|
mcl_title.set(clicker, "actionbar", {text=S("Sneak to dismount"), color="white", stay=60})
|
||||||
end
|
end
|
||||||
end, name)
|
end, name)
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,3 +33,4 @@ Activates minecarts when powered=Active les wagonnets lorsqu'il est alimenté
|
||||||
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
Emits redstone power when a minecart is detected=Émet de l'énergie redstone lorsqu'un wagonnet est détecté
|
||||||
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
Vehicle for fast travel on rails=Véhicule pour voyager rapidement sur rails
|
||||||
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
Can be ignited by tools or powered activator rail=Peut être allumé par des outils ou un rail d'activation motorisé
|
||||||
|
Sneak to dismount=
|
|
@ -1,6 +1,6 @@
|
||||||
name = mcl_minecarts
|
name = mcl_minecarts
|
||||||
author = Krock
|
author = Krock
|
||||||
description = Minecarts are vehicles to move players quickly on rails.
|
description = Minecarts are vehicles to move players quickly on rails.
|
||||||
depends = mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
depends = mcl_title, mcl_explosions, mcl_core, mcl_sounds, mcl_player, mcl_achievements, mcl_chests, mcl_furnaces, mesecons_commandblock, mcl_hoppers, mcl_tnt, mesecons
|
||||||
optional_depends = doc_identifier, mcl_wip
|
optional_depends = doc_identifier, mcl_wip
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ Pig=
|
||||||
Polar Bear=
|
Polar Bear=
|
||||||
Rabbit=
|
Rabbit=
|
||||||
Killer Bunny=
|
Killer Bunny=
|
||||||
|
The Killer Bunny=
|
||||||
Sheep=
|
Sheep=
|
||||||
Shulker=
|
Shulker=
|
||||||
Silverfish=
|
Silverfish=
|
||||||
|
|
|
@ -233,4 +233,4 @@ mobs:spawn(spawn_grass)
|
||||||
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
mobs:register_egg("mobs_mc:rabbit", S("Rabbit"), "mobs_mc_spawn_icon_rabbit.png", 0)
|
||||||
|
|
||||||
-- Note: This spawn egg does not exist in Minecraft
|
-- Note: This spawn egg does not exist in Minecraft
|
||||||
mobs:register_egg("mobs_mc:killer_bunny", S("Killer Bunny"), "mobs_mc_spawn_icon_rabbit.png^[colorize:#FF0000:192", 0) -- TODO: Update inventory image
|
mobs:register_egg("mobs_mc:killer_bunny", S("Killer Bunny"), "mobs_mc_spawn_icon_rabbit_caerbannog.png", 0)
|
||||||
|
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,31 @@
|
||||||
|
# lightning
|
||||||
|
Lightning mod for MineClone2 with the following API:
|
||||||
|
|
||||||
|
## lightning.register_on_strike(function(pos, pos2, objects))
|
||||||
|
Custom function called when a lightning strikes.
|
||||||
|
|
||||||
|
* `pos`: impact position
|
||||||
|
* `pos2`: rounded node position where fire is placed
|
||||||
|
* `objects`: table with ObjectRefs of all objects within a radius of 3.5 around pos2
|
||||||
|
|
||||||
|
## lightning.strike(pos)
|
||||||
|
Let a lightning strike.
|
||||||
|
|
||||||
|
* `pos`: optional, if not given a random pos will be chosen
|
||||||
|
* `returns`: bool - success if a strike happened
|
||||||
|
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
|
```
|
||||||
|
lightning.register_on_strike(function(pos, pos2, objects)
|
||||||
|
for _, obj in pairs(objects) do
|
||||||
|
obj:remove()
|
||||||
|
end
|
||||||
|
minetest.add_entity(pos, "mobs_mc:sheep")
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_respawnplayer(function(player)
|
||||||
|
lightning.strike(player:get_pos())
|
||||||
|
end)
|
||||||
|
```
|
|
@ -24,13 +24,14 @@ local get_objects_inside_radius = minetest.get_objects_inside_radius
|
||||||
local get_item_group = minetest.get_item_group
|
local get_item_group = minetest.get_item_group
|
||||||
|
|
||||||
lightning = {
|
lightning = {
|
||||||
interval_low = 17,
|
interval_low = 17,
|
||||||
interval_high = 503,
|
interval_high = 503,
|
||||||
range_h = 100,
|
range_h = 100,
|
||||||
range_v = 50,
|
range_v = 50,
|
||||||
size = 100,
|
size = 100,
|
||||||
-- disable this to stop lightning mod from striking
|
-- disable this to stop lightning mod from striking
|
||||||
auto = true,
|
auto = true,
|
||||||
|
on_strike_functions = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local rng = PcgRandom(32321123312123)
|
local rng = PcgRandom(32321123312123)
|
||||||
|
@ -54,6 +55,18 @@ end
|
||||||
|
|
||||||
minetest.register_globalstep(revertsky)
|
minetest.register_globalstep(revertsky)
|
||||||
|
|
||||||
|
-- lightning strike API
|
||||||
|
|
||||||
|
-- See API.md
|
||||||
|
--[[
|
||||||
|
lightning.register_on_strike(function(pos, pos2, objects)
|
||||||
|
-- code
|
||||||
|
end)
|
||||||
|
]]
|
||||||
|
function lightning.register_on_strike(func)
|
||||||
|
table.insert(lightning.on_strike_functions, func)
|
||||||
|
end
|
||||||
|
|
||||||
-- select a random strike point, midpoint
|
-- select a random strike point, midpoint
|
||||||
local function choose_pos(pos)
|
local function choose_pos(pos)
|
||||||
if not pos then
|
if not pos then
|
||||||
|
@ -79,14 +92,14 @@ local function choose_pos(pos)
|
||||||
pos.z = math.floor(pos.z - (lightning.range_h / 2) + rng:next(1, lightning.range_h))
|
pos.z = math.floor(pos.z - (lightning.range_h / 2) + rng:next(1, lightning.range_h))
|
||||||
end
|
end
|
||||||
|
|
||||||
local b, pos2 = line_of_sight(pos, {x = pos.x, y = pos.y - lightning.range_v, z = pos.z}, 1)
|
local b, pos2 = line_of_sight(pos, { x = pos.x, y = pos.y - lightning.range_v, z = pos.z }, 1)
|
||||||
|
|
||||||
-- nothing but air found
|
-- nothing but air found
|
||||||
if b then
|
if b then
|
||||||
return nil, nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local n = get_node({x = pos2.x, y = pos2.y - 1/2, z = pos2.z})
|
local n = get_node({ x = pos2.x, y = pos2.y - 1/2, z = pos2.z })
|
||||||
if n.name == "air" or n.name == "ignore" then
|
if n.name == "air" or n.name == "ignore" then
|
||||||
return nil, nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
@ -94,7 +107,6 @@ local function choose_pos(pos)
|
||||||
return pos, pos2
|
return pos, pos2
|
||||||
end
|
end
|
||||||
|
|
||||||
-- lightning strike API
|
|
||||||
-- * pos: optional, if not given a random pos will be chosen
|
-- * pos: optional, if not given a random pos will be chosen
|
||||||
-- * returns: bool - success if a strike happened
|
-- * returns: bool - success if a strike happened
|
||||||
function lightning.strike(pos)
|
function lightning.strike(pos)
|
||||||
|
@ -108,21 +120,28 @@ function lightning.strike(pos)
|
||||||
if not pos then
|
if not pos then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
local objects = get_objects_inside_radius(pos2, 3.5)
|
||||||
|
if lightning.on_strike_functions then
|
||||||
|
for _, func in pairs(lightning.on_strike_functions) do
|
||||||
|
func(pos, pos2, objects)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
lightning.register_on_strike(function(pos, pos2, objects)
|
||||||
|
local particle_pos = vector.offset(pos2, 0, (lightning.size / 2) + 0.5, 0)
|
||||||
|
local particle_size = lightning.size * 10
|
||||||
|
local time = 0.2
|
||||||
add_particlespawner({
|
add_particlespawner({
|
||||||
amount = 1,
|
amount = 1,
|
||||||
time = 0.2,
|
time = time,
|
||||||
-- make it hit the top of a block exactly with the bottom
|
-- make it hit the top of a block exactly with the bottom
|
||||||
minpos = {x = pos2.x, y = pos2.y + (lightning.size / 2) + 1/2, z = pos2.z },
|
minpos = particle_pos,
|
||||||
maxpos = {x = pos2.x, y = pos2.y + (lightning.size / 2) + 1/2, z = pos2.z },
|
maxpos = particle_pos,
|
||||||
minvel = {x = 0, y = 0, z = 0},
|
minexptime = time,
|
||||||
maxvel = {x = 0, y = 0, z = 0},
|
maxexptime = time,
|
||||||
minacc = {x = 0, y = 0, z = 0},
|
minsize = particle_size,
|
||||||
maxacc = {x = 0, y = 0, z = 0},
|
maxsize = particle_size,
|
||||||
minexptime = 0.2,
|
|
||||||
maxexptime = 0.2,
|
|
||||||
minsize = lightning.size * 10,
|
|
||||||
maxsize = lightning.size * 10,
|
|
||||||
collisiondetection = true,
|
collisiondetection = true,
|
||||||
vertical = true,
|
vertical = true,
|
||||||
-- to make it appear hitting the node that will get set on fire, make sure
|
-- to make it appear hitting the node that will get set on fire, make sure
|
||||||
|
@ -135,44 +154,27 @@ function lightning.strike(pos)
|
||||||
sound_play({ name = "lightning_thunder", gain = 10 }, { pos = pos, max_hear_distance = 500 }, true)
|
sound_play({ name = "lightning_thunder", gain = 10 }, { pos = pos, max_hear_distance = 500 }, true)
|
||||||
|
|
||||||
-- damage nearby objects, transform mobs
|
-- damage nearby objects, transform mobs
|
||||||
-- TODO: use an API insteed of hardcoding this behaviour
|
for _, obj in pairs(objects) do
|
||||||
local objs = get_objects_inside_radius(pos2, 3.5)
|
|
||||||
for o=1, #objs do
|
|
||||||
local obj = objs[o]
|
|
||||||
local lua = obj:get_luaentity()
|
local lua = obj:get_luaentity()
|
||||||
-- pig → zombie pigman (no damage)
|
if lua and lua._on_strike then
|
||||||
|
lua._on_strike(lua, pos, pos2, objects)
|
||||||
|
end
|
||||||
|
-- remove this when mob API is done
|
||||||
if lua and lua.name == "mobs_mc:pig" then
|
if lua and lua.name == "mobs_mc:pig" then
|
||||||
local rot = obj:get_yaw()
|
mcl_util.replace_mob(obj, "mobs_mc:pigman")
|
||||||
obj:remove()
|
|
||||||
obj = add_entity(pos2, "mobs_mc:pigman")
|
|
||||||
obj:set_yaw(rot)
|
|
||||||
-- mooshroom: toggle color red/brown (no damage)
|
|
||||||
elseif lua and lua.name == "mobs_mc:mooshroom" then
|
elseif lua and lua.name == "mobs_mc:mooshroom" then
|
||||||
if lua.base_texture[1] == "mobs_mc_mooshroom.png" then
|
if lua.base_texture[1] == "mobs_mc_mooshroom.png" then
|
||||||
lua.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" }
|
lua.base_texture = { "mobs_mc_mooshroom_brown.png", "mobs_mc_mushroom_brown.png" }
|
||||||
else
|
else
|
||||||
lua.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" }
|
lua.base_texture = { "mobs_mc_mooshroom.png", "mobs_mc_mushroom_red.png" }
|
||||||
end
|
end
|
||||||
obj:set_properties({textures = lua.base_texture})
|
obj:set_properties({ textures = lua.base_texture })
|
||||||
-- villager → witch (no damage)
|
elseif lua and lua.name == "mobs_mc:villager" then
|
||||||
--elseif lua and lua.name == "mobs_mc:villager" then
|
mcl_util.replace_mob(obj, "mobs_mc:witch")
|
||||||
-- Witches are incomplete, this code is unused
|
|
||||||
-- TODO: Enable this code when witches are working.
|
|
||||||
--[[
|
|
||||||
local rot = obj:get_yaw()
|
|
||||||
obj:remove()
|
|
||||||
obj = minetest.add_entity(pos2, "mobs_mc:witch")
|
|
||||||
obj:set_yaw(rot)
|
|
||||||
]]
|
|
||||||
-- charged creeper
|
|
||||||
elseif lua and lua.name == "mobs_mc:creeper" then
|
elseif lua and lua.name == "mobs_mc:creeper" then
|
||||||
local rot = obj:get_yaw()
|
mcl_util.replace_mob(obj, "mobs_mc:creeper_charged")
|
||||||
obj:remove()
|
|
||||||
obj = add_entity(pos2, "mobs_mc:creeper_charged")
|
|
||||||
obj:set_yaw(rot)
|
|
||||||
-- Other objects: Just damage
|
|
||||||
else
|
else
|
||||||
mcl_util.deal_damage(obj, 5, {type = "lightning_bolt"})
|
mcl_util.deal_damage(obj, 5, { type = "lightning_bolt" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -186,7 +188,7 @@ function lightning.strike(pos)
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
if ps[name] == nil then
|
if ps[name] == nil then
|
||||||
ps[name] = {p = player, sky = sky}
|
ps[name] = {p = player, sky = sky}
|
||||||
mcl_weather.skycolor.add_layer("lightning", {{r=255,g=255,b=255}}, true)
|
mcl_weather.skycolor.add_layer("lightning", { { r = 255, g = 255, b = 255 } }, true)
|
||||||
mcl_weather.skycolor.active = true
|
mcl_weather.skycolor.active = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -201,7 +203,7 @@ function lightning.strike(pos)
|
||||||
if rng:next(1,100) <= 3 then
|
if rng:next(1,100) <= 3 then
|
||||||
skeleton_lightning = true
|
skeleton_lightning = true
|
||||||
end
|
end
|
||||||
if get_item_group(get_node({x = pos2.x, y = pos2.y - 1, z = pos2.z}).name, "liquid") < 1 then
|
if get_item_group(get_node({ x = pos2.x, y = pos2.y - 1, z = pos2.z }).name, "liquid") < 1 then
|
||||||
if get_node(pos2).name == "air" then
|
if get_node(pos2).name == "air" then
|
||||||
-- Low chance for a lightning to spawn skeleton horse + skeletons
|
-- Low chance for a lightning to spawn skeleton horse + skeletons
|
||||||
if skeleton_lightning then
|
if skeleton_lightning then
|
||||||
|
@ -210,7 +212,7 @@ function lightning.strike(pos)
|
||||||
local angle, posadd
|
local angle, posadd
|
||||||
angle = math.random(0, math.pi*2)
|
angle = math.random(0, math.pi*2)
|
||||||
for i=1,3 do
|
for i=1,3 do
|
||||||
posadd = {x=math.cos(angle),y=0,z=math.sin(angle)}
|
posadd = { x=math.cos(angle),y=0,z=math.sin(angle) }
|
||||||
posadd = vector.normalize(posadd)
|
posadd = vector.normalize(posadd)
|
||||||
local mob = add_entity(vector.add(pos2, posadd), "mobs_mc:skeleton")
|
local mob = add_entity(vector.add(pos2, posadd), "mobs_mc:skeleton")
|
||||||
mob:set_yaw(angle-math.pi/2)
|
mob:set_yaw(angle-math.pi/2)
|
||||||
|
@ -219,12 +221,11 @@ function lightning.strike(pos)
|
||||||
|
|
||||||
-- Cause a fire
|
-- Cause a fire
|
||||||
else
|
else
|
||||||
set_node(pos2, {name = "mcl_fire:fire"})
|
set_node(pos2, { name = "mcl_fire:fire" })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end)
|
||||||
end
|
|
||||||
|
|
||||||
-- if other mods disable auto lightning during initialization, don't trigger the first lightning.
|
-- if other mods disable auto lightning during initialization, don't trigger the first lightning.
|
||||||
after(5, function(dtime)
|
after(5, function(dtime)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Using it as fuel turns it into: @1.=L'utiliser comme combustible le transforme en : @1.
|
Using it as fuel turns it into: @1.=L'utiliser comme combustible le transforme en : @1.
|
||||||
@1 seconds=@1 secondes
|
@1 seconds=@1 secondes
|
||||||
# Item count times item name
|
# Item count times item name
|
||||||
%@1×@2=%@1×@
|
@1×@2=@1×@
|
||||||
# Itemname (25%)
|
# Itemname (25%)
|
||||||
@1 (@2%)=@1 (@2%)
|
@1 (@2%)=@1 (@2%)
|
||||||
# Itemname (<0.5%)
|
# Itemname (<0.5%)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# mcl_item_id
|
||||||
|
Show the item ID of an item in the description.
|
||||||
|
With this API, you can register a different name space than "mineclone" for your mod.
|
||||||
|
|
||||||
|
## mcl_item_id.set_mod_namespace(modname, namespace)
|
||||||
|
Set a name space for all items in a mod.
|
||||||
|
|
||||||
|
* param1: the modname
|
||||||
|
* param2: (optional) string of the desired name space, if nil, it is the name of the mod
|
||||||
|
|
||||||
|
## mcl_item_id.get_mod_namespace(modname)
|
||||||
|
Get the name space of a mod registered with mcl_item_id.set_mod_namespace(modname, namespace).
|
||||||
|
|
||||||
|
* param1: the modname
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
|
The name of the mod is "mod" which registered an item called "mod:itemname".
|
||||||
|
|
||||||
|
* mcl_item_id.set_mod_namespace("mod", "mymod") will show "mymod:itemname" in the description of "mod:itemname"
|
||||||
|
* mcl_item_id.set_mod_namespace(minetest.get_current_modname()) will show "mod:itemname" in the description of "mod:itemname"
|
||||||
|
* mcl_item_id.get_mod_namespace(minetest.get_current_modname()) will return "mod"
|
||||||
|
|
||||||
|
(If no namespace is set by a mod, mcl_item_id.get_mod_namespace(minetest.get_current_modname()) will return "mineclone")
|
|
@ -0,0 +1,62 @@
|
||||||
|
mcl_item_id = {
|
||||||
|
mod_namespaces = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local game = "mineclone"
|
||||||
|
|
||||||
|
function mcl_item_id.set_mod_namespace(modname, namespace)
|
||||||
|
local namespace = namespace or modname
|
||||||
|
mcl_item_id.mod_namespaces[modname] = namespace
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_item_id.get_mod_namespace(modname)
|
||||||
|
local namespace = mcl_item_id.mod_namespaces[modname]
|
||||||
|
if namespace then
|
||||||
|
return namespace
|
||||||
|
else
|
||||||
|
return game
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local same_id = {
|
||||||
|
enchanting = { "table" },
|
||||||
|
experience = { "bottle" },
|
||||||
|
heads = { "skeleton", "zombie", "creeper", "wither_skeleton" },
|
||||||
|
mobitems = { "rabbit", "chicken" },
|
||||||
|
walls = {
|
||||||
|
"andesite", "brick", "cobble", "diorite", "endbricks",
|
||||||
|
"granite", "mossycobble", "netherbrick", "prismarine",
|
||||||
|
"rednetherbrick", "redsandstone", "sandstone",
|
||||||
|
"stonebrick", "stonebrickmossy",
|
||||||
|
},
|
||||||
|
wool = {
|
||||||
|
"black", "blue", "brown", "cyan", "green",
|
||||||
|
"grey", "light_blue", "lime", "magenta", "orange",
|
||||||
|
"pink", "purple", "red", "silver", "white", "yellow",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tt.register_snippet(function(itemstring)
|
||||||
|
local def = minetest.registered_items[itemstring]
|
||||||
|
local item_split = itemstring:find(":")
|
||||||
|
local id_string = itemstring:sub(item_split)
|
||||||
|
local id_modname = itemstring:sub(1, item_split - 1)
|
||||||
|
local new_id = game .. id_string
|
||||||
|
local mod_namespace = mcl_item_id.get_mod_namespace(id_modname)
|
||||||
|
for mod, ids in pairs(same_id) do
|
||||||
|
for _, id in pairs(ids) do
|
||||||
|
if itemstring == "mcl_" .. mod .. ":" .. id then
|
||||||
|
new_id = game .. ":" .. id .. "_" .. mod:gsub("s", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if mod_namespace ~= game then
|
||||||
|
new_id = mod_namespace .. id_string
|
||||||
|
end
|
||||||
|
if mod_namespace ~= id_modname then
|
||||||
|
minetest.register_alias_force(new_id, itemstring)
|
||||||
|
end
|
||||||
|
if minetest.settings:get_bool("mcl_item_id_debug", false) then
|
||||||
|
return new_id, "#555555"
|
||||||
|
end
|
||||||
|
end)
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = mcl_item_id
|
||||||
|
author = NO11
|
||||||
|
depends = tt
|
|
@ -45,3 +45,4 @@ Mining durability: @1=Grabehaltbarkeit: @1
|
||||||
Block breaking strength: @1=Blockbruchstärke: @1
|
Block breaking strength: @1=Blockbruchstärke: @1
|
||||||
@1 uses=@1 Verwendungen
|
@1 uses=@1 Verwendungen
|
||||||
Unlimited uses=Unbegrenzte Verwendungen
|
Unlimited uses=Unbegrenzte Verwendungen
|
||||||
|
Durability: @1=Haltbarkeit: @1
|
||||||
|
|
|
@ -45,3 +45,4 @@ Mining durability: @1=
|
||||||
Block breaking strength: @1=
|
Block breaking strength: @1=
|
||||||
@1 uses=
|
@1 uses=
|
||||||
Unlimited uses=
|
Unlimited uses=
|
||||||
|
Durability: @1=
|
||||||
|
|
|
@ -107,3 +107,8 @@ tt.register_snippet(function(itemstring)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
tt.register_snippet(function(itemstring, _, itemstack)
|
||||||
|
if itemstring:sub(1, 23) == "mcl_fishing:fishing_rod" or itemstring:sub(1, 12) == "mcl_bows:bow" then
|
||||||
|
return S("Durability: @1", S("@1 uses", mcl_util.calculate_durability(itemstack or ItemStack(itemstring))))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
|
@ -425,6 +425,7 @@ function hb.hide_hudbar(player, identifier)
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
local hudtable = hb.get_hudtable(identifier)
|
local hudtable = hb.get_hudtable(identifier)
|
||||||
if hudtable == nil then return false end
|
if hudtable == nil then return false end
|
||||||
|
if hudtable.hudstate[name].hidden == true then return true end
|
||||||
if hb.settings.bar_type == "progress_bar" then
|
if hb.settings.bar_type == "progress_bar" then
|
||||||
if hudtable.hudids[name].icon then
|
if hudtable.hudids[name].icon then
|
||||||
player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
|
player:hud_change(hudtable.hudids[name].icon, "scale", {x=0,y=0})
|
||||||
|
@ -443,6 +444,7 @@ function hb.unhide_hudbar(player, identifier)
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
local hudtable = hb.get_hudtable(identifier)
|
local hudtable = hb.get_hudtable(identifier)
|
||||||
if hudtable == nil then return false end
|
if hudtable == nil then return false end
|
||||||
|
if hudtable.hudstate[name].hidden == false then return true end
|
||||||
local value = hudtable.hudstate[name].value
|
local value = hudtable.hudstate[name].value
|
||||||
local max = hudtable.hudstate[name].max
|
local max = hudtable.hudstate[name].max
|
||||||
if hb.settings.bar_type == "progress_bar" then
|
if hb.settings.bar_type == "progress_bar" then
|
||||||
|
|
|
@ -7,6 +7,7 @@ Creator of MineClone2=Schöpfer von MineClone2
|
||||||
Developers=Entwickler
|
Developers=Entwickler
|
||||||
Jump to speed up (additionally sprint)=Springen, um zu beschleunigen (zusätzlich sprinten)
|
Jump to speed up (additionally sprint)=Springen, um zu beschleunigen (zusätzlich sprinten)
|
||||||
Maintainers=Betreuer
|
Maintainers=Betreuer
|
||||||
|
MineClone5=MineClone5
|
||||||
Original Mod Authors=Original-Mod-Autoren
|
Original Mod Authors=Original-Mod-Autoren
|
||||||
Sneak to skip=Schleichen zum Überspringen
|
Sneak to skip=Schleichen zum Überspringen
|
||||||
Textures=Texturen
|
Textures=Texturen
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# textdomain: mcl_credits
|
||||||
|
3D Models=
|
||||||
|
A faithful Open Source clone of Minecraft=
|
||||||
|
Contributors=
|
||||||
|
Creator of MineClone=
|
||||||
|
Creator of MineClone2=
|
||||||
|
Developers=
|
||||||
|
Jump to speed up (additionally sprint)=
|
||||||
|
Maintainers=
|
||||||
|
MineClone5=
|
||||||
|
Original Mod Authors=
|
||||||
|
Sneak to skip=
|
||||||
|
Textures=
|
||||||
|
Translations=
|
|
@ -0,0 +1,14 @@
|
||||||
|
# textdomain: mcl_credits
|
||||||
|
3D Models=Modèles 3D
|
||||||
|
A faithful Open Source clone of Minecraft=Un clone open source de Minecraft
|
||||||
|
Contributors=Contributeurs
|
||||||
|
Creator of MineClone=Créateur de MineClone
|
||||||
|
Creator of MineClone2=Créateur de MineClone2
|
||||||
|
Developers=Développeurs
|
||||||
|
Jump to speed up (additionally sprint)=Saut pour accélérer (peut être combiné avec sprint)
|
||||||
|
Maintainers=Mainteneurs
|
||||||
|
MineClone5=MineClone5
|
||||||
|
Original Mod Authors=Auteurs des mods originaux
|
||||||
|
Sneak to skip=Shift pour passer
|
||||||
|
Textures=Textures
|
||||||
|
Translations=Traductions
|
|
@ -0,0 +1,14 @@
|
||||||
|
# textdomain: mcl_credits
|
||||||
|
3D Models=
|
||||||
|
A faithful Open Source clone of Minecraft=
|
||||||
|
Contributors=
|
||||||
|
Creator of MineClone=
|
||||||
|
Creator of MineClone2=
|
||||||
|
Developers=
|
||||||
|
Jump to speed up (additionally sprint)=
|
||||||
|
Maintainers=
|
||||||
|
MineClone5=
|
||||||
|
Original Mod Authors=
|
||||||
|
Sneak to skip=
|
||||||
|
Textures=
|
||||||
|
Translations=
|
|
@ -0,0 +1,14 @@
|
||||||
|
# textdomain: mcl_credits
|
||||||
|
3D Models=
|
||||||
|
A faithful Open Source clone of Minecraft=
|
||||||
|
Contributors=
|
||||||
|
Creator of MineClone=
|
||||||
|
Creator of MineClone2=
|
||||||
|
Developers=
|
||||||
|
Jump to speed up (additionally sprint)=
|
||||||
|
Maintainers=
|
||||||
|
MineClone5=
|
||||||
|
Original Mod Authors=
|
||||||
|
Sneak to skip=
|
||||||
|
Textures=
|
||||||
|
Translations=
|
|
@ -0,0 +1,50 @@
|
||||||
|
# mcl_title
|
||||||
|
|
||||||
|
Allow mods to show messages in the hud of players.
|
||||||
|
|
||||||
|
## mcl_title.set(player, type, data)
|
||||||
|
|
||||||
|
Show a hud message of `type` to player `player` with `data` as params.
|
||||||
|
|
||||||
|
The element will stay for the per-player param `stay` or `data.stay` (in gametick which is 1/20 second).
|
||||||
|
|
||||||
|
Here is a usage exemple:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
--show a title in the HUD with minecraft color "gold"
|
||||||
|
mcl_title.set(player, "title", {text="dummy text", color="gold"})
|
||||||
|
|
||||||
|
--show a subtitle in the HUD with hex color "#612D2D"
|
||||||
|
mcl_title.set(player, "subtitle", {text="dummy subtitle", color="#612D2D"})
|
||||||
|
|
||||||
|
--show an actionbar in the HUD (above the hotbar) with minecraft color "red"
|
||||||
|
mcl_title.set(player, "subtitle", {text="dummy actionbar", color="red"})
|
||||||
|
|
||||||
|
--show a title in the HUD with minecraft color "gold" staying for 3 seconds (override stay setting)
|
||||||
|
mcl_title.set(player, "title", {text="dummy text", color="gold", stay=60})
|
||||||
|
```
|
||||||
|
|
||||||
|
## mcl_title.remove(player, type)
|
||||||
|
|
||||||
|
Hide HUD element of type `type` for player `player`.
|
||||||
|
|
||||||
|
## mcl_title.clear(player)
|
||||||
|
|
||||||
|
Remove every title/subtitle/actionbar from a player.
|
||||||
|
Basicaly run `mcl_title.remove(player, type)` for every type.
|
||||||
|
|
||||||
|
## mcl_title.params_set(player, params)
|
||||||
|
|
||||||
|
Allow mods to set `stay` and upcomming `fadeIn`/`fadeOut` params.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
mcl_title.params_set(player, {stay = 600}) --elements with no 'data.stay' field will stay during 30s (600/20)
|
||||||
|
```
|
||||||
|
|
||||||
|
## mcl_title.params_get(player)
|
||||||
|
|
||||||
|
Get `stay` and upcomming `fadeIn` and `fadeOut` params of a player as a table.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
mcl_title.params_get(player)
|
||||||
|
```
|
|
@ -0,0 +1,236 @@
|
||||||
|
--Based on:
|
||||||
|
--https://www.digminecraft.com/game_commands/title_command.php
|
||||||
|
--https://youtu.be/oVrtQRO2hpY
|
||||||
|
|
||||||
|
--TODO: use SSCSM to reduce lag and network trafic (just send modchannel messages)
|
||||||
|
--TODO: fadeIn and fadeOut animation (needs engine change: SSCSM or native support)
|
||||||
|
--TODO: allow obfuscating text (needs engine change: SSCSM or native support)
|
||||||
|
--TODO: allow colorizing and styling of part of the text (NEEDS ENGINE CHANGE!!!)
|
||||||
|
--TODO: exactly mc like layout
|
||||||
|
|
||||||
|
--Note that the table storing timeouts use playername as index insteed of player objects (faster)
|
||||||
|
--This is intended in order to speedup the process of removing HUD elements the the timeout is up
|
||||||
|
|
||||||
|
local huds_idx = {}
|
||||||
|
|
||||||
|
local hud_hide_timeouts = {}
|
||||||
|
|
||||||
|
hud_hide_timeouts.title = {}
|
||||||
|
hud_hide_timeouts.subtitle = {}
|
||||||
|
hud_hide_timeouts.actionbar = {}
|
||||||
|
|
||||||
|
huds_idx.title = {}
|
||||||
|
huds_idx.subtitle = {}
|
||||||
|
huds_idx.actionbar = {}
|
||||||
|
|
||||||
|
mcl_title = {}
|
||||||
|
mcl_title.defaults = {fadein = 10, stay = 70, fadeout = 20}
|
||||||
|
mcl_title.layout = {}
|
||||||
|
mcl_title.layout.title = {position = {x = 0.5, y = 0.5}, alignment = {x = 0, y = -1.3}, size = 7}
|
||||||
|
mcl_title.layout.subtitle = {position = {x = 0.5, y = 0.5}, alignment = {x = 0, y = 1.7}, size = 4}
|
||||||
|
mcl_title.layout.actionbar = {position = {x = 0.5, y = 1}, alignment = {x = 0, y = 0}, size = 1}
|
||||||
|
|
||||||
|
local get_color = mcl_util.get_color
|
||||||
|
|
||||||
|
--local string = string
|
||||||
|
local pairs = pairs
|
||||||
|
|
||||||
|
local function gametick_to_secondes(gametick)
|
||||||
|
if gametick then
|
||||||
|
return gametick / 20
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--https://github.com/minetest/minetest/blob/b3b075ea02034306256b486dd45410aa765f035a/doc/lua_api.txt#L8477
|
||||||
|
--[[
|
||||||
|
local function style_to_bits(bold, italic)
|
||||||
|
if bold then
|
||||||
|
if italic then
|
||||||
|
return 3
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if italic then
|
||||||
|
return 2
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
]]
|
||||||
|
|
||||||
|
--PARAMS SYSTEM
|
||||||
|
local player_params = {}
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
--local playername = player:get_player_name()
|
||||||
|
player_params[player] = {
|
||||||
|
stay = mcl_title.defaults.stay,
|
||||||
|
--fadeIn = mcl_title.defaults.fadein,
|
||||||
|
--fadeOut = mcl_title.defaults.fadeout,
|
||||||
|
}
|
||||||
|
local _, hex_color = get_color("white")
|
||||||
|
huds_idx.title[player] = player:hud_add({
|
||||||
|
hud_elem_type = "text",
|
||||||
|
position = mcl_title.layout.title.position,
|
||||||
|
alignment = mcl_title.layout.title.alignment,
|
||||||
|
text = "",
|
||||||
|
--style = 0,
|
||||||
|
size = {x = mcl_title.layout.title.size},
|
||||||
|
number = hex_color,
|
||||||
|
z_index = 100,
|
||||||
|
})
|
||||||
|
huds_idx.subtitle[player] = player:hud_add({
|
||||||
|
hud_elem_type = "text",
|
||||||
|
position = mcl_title.layout.subtitle.position,
|
||||||
|
alignment = mcl_title.layout.subtitle.alignment,
|
||||||
|
text = "",
|
||||||
|
--style = 0,
|
||||||
|
size = {x = mcl_title.layout.subtitle.size},
|
||||||
|
number = hex_color,
|
||||||
|
z_index = 100,
|
||||||
|
})
|
||||||
|
huds_idx.actionbar[player] = player:hud_add({
|
||||||
|
hud_elem_type = "text",
|
||||||
|
position = mcl_title.layout.actionbar.position,
|
||||||
|
offset = {x = 0, y = -210},
|
||||||
|
alignment = mcl_title.layout.actionbar.alignment,
|
||||||
|
--style = 0,
|
||||||
|
text = "",
|
||||||
|
size = {x = mcl_title.layout.actionbar.size},
|
||||||
|
number = hex_color,
|
||||||
|
z_index = 100,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
local playername = player:get_player_name()
|
||||||
|
|
||||||
|
--remove player params from the list
|
||||||
|
player_params[player] = nil
|
||||||
|
|
||||||
|
--remove HUD idx from the list (HUD elements are removed by the engine)
|
||||||
|
huds_idx.title[player] = nil
|
||||||
|
huds_idx.subtitle[player] = nil
|
||||||
|
huds_idx.actionbar[player] = nil
|
||||||
|
|
||||||
|
--remove timers from list
|
||||||
|
hud_hide_timeouts.title[playername] = nil
|
||||||
|
hud_hide_timeouts.subtitle[playername] = nil
|
||||||
|
hud_hide_timeouts.actionbar[playername] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
function mcl_title.params_set(player, data)
|
||||||
|
player_params[player] = {
|
||||||
|
stay = data.stay or mcl_title.defaults.stay,
|
||||||
|
--fadeIn = data.fadeIn or mcl_title.defaults.fadein,
|
||||||
|
--fadeOut = data.fadeOut or mcl_title.defaults.fadeout,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_title.params_get(player)
|
||||||
|
return player_params[player]
|
||||||
|
end
|
||||||
|
|
||||||
|
--API FUNCTIONS
|
||||||
|
|
||||||
|
function mcl_title.set(player, type, data)
|
||||||
|
if not data.color then
|
||||||
|
data.color = "white"
|
||||||
|
end
|
||||||
|
local _, hex_color = get_color(data.color)
|
||||||
|
if not hex_color then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
player:hud_change(huds_idx[type][player], "text", data.text)
|
||||||
|
player:hud_change(huds_idx[type][player], "number", hex_color)
|
||||||
|
|
||||||
|
--apply bold and italic
|
||||||
|
--player:hud_change(huds_idx[type][player], "style", style_to_bits(data.bold, data.italic))
|
||||||
|
|
||||||
|
hud_hide_timeouts[type][player:get_player_name()] = gametick_to_secondes(data.stay) or gametick_to_secondes(mcl_title.params_get(player).stay)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_title.remove(player, type)
|
||||||
|
if player then
|
||||||
|
player:hud_change(huds_idx[type][player], "text", "")
|
||||||
|
--player:hud_change(huds_idx[type][player], "style", 0) --no styling
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_title.clear(player)
|
||||||
|
mcl_title.remove(player, "title")
|
||||||
|
mcl_title.remove(player, "subtitle")
|
||||||
|
mcl_title.remove(player, "actionbar")
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_dieplayer(function(player)
|
||||||
|
mcl_title.clear(player)
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
local new_timeouts = {
|
||||||
|
title = {},
|
||||||
|
subtitle = {},
|
||||||
|
actionbar = {},
|
||||||
|
}
|
||||||
|
for element, content in pairs(hud_hide_timeouts) do
|
||||||
|
for name, timeout in pairs(content) do
|
||||||
|
timeout = timeout - dtime
|
||||||
|
if timeout <= 0 then
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
mcl_title.remove(player, element)
|
||||||
|
else
|
||||||
|
new_timeouts[element][name] = timeout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
hud_hide_timeouts = new_timeouts
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
--DEBUG STUFF!!
|
||||||
|
--[[
|
||||||
|
minetest.register_chatcommand("title", {
|
||||||
|
func = function(name, param)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
mcl_title.set(player, "title", {text=param, color="gold", bold=true, italic=true})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("subtitle", {
|
||||||
|
func = function(name, param)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
mcl_title.set(player, "subtitle", {text=param, color="gold"})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("actionbar", {
|
||||||
|
func = function(name, param)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
mcl_title.set(player, "actionbar", {text=param, color="gold"})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("timeout", {
|
||||||
|
func = function(name, param)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
mcl_title.params_set(player, {stay = 600})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_chatcommand("all", {
|
||||||
|
func = function(name, param)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
mcl_title.params_set(player, {stay = 600})
|
||||||
|
mcl_title.set(player, "title", {text=param, color="gold"})
|
||||||
|
mcl_title.set(player, "subtitle", {text=param, color="gold"})
|
||||||
|
mcl_title.set(player, "actionbar", {text=param, color="gold"})
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
]]
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_title
|
||||||
|
description = Add an API to add in HUD title
|
||||||
|
depends = mcl_colors
|
||||||
|
author = AFCMS
|
|
@ -1,7 +0,0 @@
|
||||||
# mcl_temp_message
|
|
||||||
|
|
||||||
Allow mods to show short messages in the hud of players.
|
|
||||||
|
|
||||||
## mcl_tmp_message.message(player, message)
|
|
||||||
|
|
||||||
Show above the hotbar a hud message <message> to player <player>.
|
|
|
@ -1,44 +0,0 @@
|
||||||
mcl_tmp_message = {}
|
|
||||||
|
|
||||||
local huds = {}
|
|
||||||
local hud_hide_timeouts = {}
|
|
||||||
|
|
||||||
function mcl_tmp_message.message(player, message)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
player:hud_change(huds[name], "text", message)
|
|
||||||
hud_hide_timeouts[name] = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
huds[player:get_player_name()] = player:hud_add({
|
|
||||||
hud_elem_type = "text",
|
|
||||||
position = {x=0.5, y=1},
|
|
||||||
offset = {x = 0, y = -210},
|
|
||||||
alignment = {x=0, y=0},
|
|
||||||
number = 0xFFFFFF ,
|
|
||||||
text = "",
|
|
||||||
z_index = 100,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
huds[name] = nil
|
|
||||||
hud_hide_timeouts[name] = nil
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
|
||||||
local new_timeouts = {}
|
|
||||||
for name, timeout in pairs(hud_hide_timeouts) do
|
|
||||||
timeout = timeout - dtime
|
|
||||||
if timeout <= 0 then
|
|
||||||
local player = minetest.get_player_by_name(name)
|
|
||||||
if player then
|
|
||||||
player:hud_change(huds[name], "text", "")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
new_timeouts[name] = timeout
|
|
||||||
end
|
|
||||||
end
|
|
||||||
hud_hide_timeouts = new_timeouts
|
|
||||||
end)
|
|
|
@ -1,3 +0,0 @@
|
||||||
name = mcl_tmp_message
|
|
||||||
author = Fleckenstein
|
|
||||||
description = A simple API to show a temporary message to a player
|
|
|
@ -168,6 +168,56 @@ local dispenserdef = {
|
||||||
end
|
end
|
||||||
|
|
||||||
inv:set_stack("main", stack_id, stack)
|
inv:set_stack("main", stack_id, stack)
|
||||||
|
|
||||||
|
-- Use shears on sheeps
|
||||||
|
elseif igroups.shears then
|
||||||
|
for _, obj in pairs(minetest.get_objects_inside_radius(droppos, 1)) do
|
||||||
|
local entity = obj:get_luaentity()
|
||||||
|
if entity and not entity.child and not entity.gotten then
|
||||||
|
local entname = entity.name
|
||||||
|
local pos = obj:get_pos()
|
||||||
|
local used, texture = false
|
||||||
|
if entname == "mobs_mc:sheep" then
|
||||||
|
minetest.add_item(pos, entity.drops[2].name .. " " .. math.random(1, 3))
|
||||||
|
if not entity.color then
|
||||||
|
entity.color = "unicolor_white"
|
||||||
|
end
|
||||||
|
entity.base_texture = { "blank.png", "mobs_mc_sheep.png" }
|
||||||
|
texture = entity.base_texture
|
||||||
|
entity.drops = {
|
||||||
|
{ name = mobs_mc.items.mutton_raw, chance = 1, min = 1, max = 2 },
|
||||||
|
}
|
||||||
|
used = true
|
||||||
|
elseif entname == "mobs_mc:snowman" then
|
||||||
|
texture = {
|
||||||
|
"mobs_mc_snowman.png",
|
||||||
|
"blank.png", "blank.png",
|
||||||
|
"blank.png", "blank.png",
|
||||||
|
"blank.png", "blank.png",
|
||||||
|
}
|
||||||
|
used = true
|
||||||
|
elseif entname == "mobs_mc:mooshroom" then
|
||||||
|
local droppos = vector.offset(pos, 0, 1.4, 0)
|
||||||
|
if entity.base_texture[1] == "mobs_mc_mooshroom_brown.png" then
|
||||||
|
minetest.add_item(droppos, mobs_mc.items.mushroom_brown .. " 5")
|
||||||
|
else
|
||||||
|
minetest.add_item(droppos, mobs_mc.items.mushroom_red .. " 5")
|
||||||
|
end
|
||||||
|
obj = mcl_util.replace_mob(obj, "mobs_mc:cow")
|
||||||
|
entity = obj:get_luaentity()
|
||||||
|
used = true
|
||||||
|
end
|
||||||
|
if used then
|
||||||
|
obj:set_properties({ textures = texture })
|
||||||
|
entity.gotten = true
|
||||||
|
minetest.sound_play("mcl_tools_shears_cut", { pos = pos }, true)
|
||||||
|
stack:add_wear(65535 / stackdef._mcl_diggroups.shearsy.uses)
|
||||||
|
inv:set_stack("main", stack_id, stack)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Spawn Egg
|
-- Spawn Egg
|
||||||
elseif igroups.spawn_egg then
|
elseif igroups.spawn_egg then
|
||||||
-- Spawn mob
|
-- Spawn mob
|
||||||
|
|
|
@ -53,6 +53,15 @@ local function get_consumed_materials(tool, material)
|
||||||
return materials_used
|
return materials_used
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function contains(table, value)
|
||||||
|
for _, i in pairs(table) do
|
||||||
|
if i == value then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- Given 2 input stacks, tells you which is the tool and which is the material.
|
-- Given 2 input stacks, tells you which is the tool and which is the material.
|
||||||
-- Returns ("tool", input1, input2) if input1 is tool and input2 is material.
|
-- Returns ("tool", input1, input2) if input1 is tool and input2 is material.
|
||||||
-- Returns ("material", input2, input1) if input1 is material and input2 is tool.
|
-- Returns ("material", input2, input1) if input1 is material and input2 is tool.
|
||||||
|
@ -60,9 +69,15 @@ end
|
||||||
local function distinguish_tool_and_material(input1, input2)
|
local function distinguish_tool_and_material(input1, input2)
|
||||||
local def1 = input1:get_definition()
|
local def1 = input1:get_definition()
|
||||||
local def2 = input2:get_definition()
|
local def2 = input2:get_definition()
|
||||||
if def1.type == "tool" and def1._repair_material then
|
local r1 = def1._repair_material
|
||||||
|
local r2 = def2._repair_material
|
||||||
|
if def1.type == "tool" and r1 and type(r1) == "table" and contains(r1, input2) then
|
||||||
return "tool", input1, input2
|
return "tool", input1, input2
|
||||||
elseif def2.type == "tool" and def2._repair_material then
|
elseif def2.type == "tool" and r2 and type(r2) == "table" and contains(r2, input1) then
|
||||||
|
return "material", input2, input1
|
||||||
|
elseif def1.type == "tool" and r1 then
|
||||||
|
return "tool", input1, input2
|
||||||
|
elseif def2.type == "tool" and r2 then
|
||||||
return "material", input2, input1
|
return "material", input2, input1
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
|
@ -121,11 +136,28 @@ local function update_anvil_slots(meta)
|
||||||
local distinguished, tool, material = distinguish_tool_and_material(input1, input2)
|
local distinguished, tool, material = distinguish_tool_and_material(input1, input2)
|
||||||
if distinguished then
|
if distinguished then
|
||||||
local tooldef = tool:get_definition()
|
local tooldef = tool:get_definition()
|
||||||
|
local repair = tooldef._repair_material
|
||||||
local has_correct_material = false
|
local has_correct_material = false
|
||||||
if string.sub(tooldef._repair_material, 1, 6) == "group:" then
|
local material_name = material:get_name()
|
||||||
has_correct_material = minetest.get_item_group(material:get_name(), string.sub(tooldef._repair_material, 7)) ~= 0
|
if type(repair) == "string" then
|
||||||
elseif material:get_name() == tooldef._repair_material then
|
if string.sub(repair, 1, 6) == "group:" then
|
||||||
has_correct_material = true
|
has_correct_material = minetest.get_item_group(material_name, string.sub(repair, 7)) ~= 0
|
||||||
|
elseif material_name == repair then
|
||||||
|
has_correct_material = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if contains(repair, material_name) then
|
||||||
|
has_correct_material = true
|
||||||
|
else
|
||||||
|
for _, r in pairs(repair) do
|
||||||
|
if string.sub(r, 1, 6) == "group:" then
|
||||||
|
if minetest.get_item_group(material_name, string.sub(r, 7)) ~= 0 then
|
||||||
|
has_correct_material = true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if has_correct_material and tool:get_wear() > 0 then
|
if has_correct_material and tool:get_wear() > 0 then
|
||||||
local materials_used = get_consumed_materials(tool, material)
|
local materials_used = get_consumed_materials(tool, material)
|
||||||
|
@ -284,6 +316,12 @@ local function damage_anvil_by_falling(pos, distance)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local anvilbox = {
|
||||||
|
type = "fixed",
|
||||||
|
fixed = {
|
||||||
|
{ -8 / 16, -8 / 16, -6 / 16, 8 / 16, 8 / 16, 6 / 16 },
|
||||||
|
},
|
||||||
|
}
|
||||||
local anvildef = {
|
local anvildef = {
|
||||||
groups = {pickaxey=1, falling_node=1, falling_node_damage=1, crush_after_fall=1, deco_block=1, anvil=1},
|
groups = {pickaxey=1, falling_node=1, falling_node_damage=1, crush_after_fall=1, deco_block=1, anvil=1},
|
||||||
tiles = {"mcl_anvils_anvil_top_damaged_0.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"},
|
tiles = {"mcl_anvils_anvil_top_damaged_0.png^[transformR90", "mcl_anvils_anvil_base.png", "mcl_anvils_anvil_side.png"},
|
||||||
|
@ -297,11 +335,14 @@ local anvildef = {
|
||||||
node_box = {
|
node_box = {
|
||||||
type = "fixed",
|
type = "fixed",
|
||||||
fixed = {
|
fixed = {
|
||||||
{-8/16, 2/16, -5/16, 8/16, 8/16, 5/16}, -- top
|
{ -6 / 16, -8 / 16, -6 / 16, 6 / 16, -4 / 16, 6 / 16 },
|
||||||
{-5/16, -4/16, -2/16, 5/16, 5/16, 2/16}, -- middle
|
{ -5 / 16, -4 / 16, -4 / 16, 5 / 16, -3 / 16, 4 / 16 },
|
||||||
{-8/16, -8/16, -5/16, 8/16, -4/16, 5/16}, -- base
|
{ -4 / 16, -3 / 16, -2 / 16, 4 / 16, 2 / 16, 2 / 16 },
|
||||||
|
{ -8 / 16, 2 / 16, -5 / 16, 8 / 16, 8 / 16, 5 / 16 },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
selection_box = anvilbox,
|
||||||
|
collision_box = anvilbox,
|
||||||
sounds = mcl_sounds.node_sound_metal_defaults(),
|
sounds = mcl_sounds.node_sound_metal_defaults(),
|
||||||
_mcl_blast_resistance = 1200,
|
_mcl_blast_resistance = 1200,
|
||||||
_mcl_hardness = 5,
|
_mcl_hardness = 5,
|
||||||
|
|
Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 5.7 KiB |
|
@ -1,5 +1,5 @@
|
||||||
name = mcl_armor
|
name = mcl_armor
|
||||||
author = stu
|
author = stu
|
||||||
description = Adds craftable armor that is visible to other players.
|
description = Adds craftable armor that is visible to other players.
|
||||||
depends = mcl_core, mcl_player, mcl_enchanting
|
depends = mcl_core, mcl_player, mcl_enchanting, mcl_damage
|
||||||
optional_depends = mcl_fire, ethereal, bakedclay
|
optional_depends = mcl_fire, ethereal, bakedclay
|
||||||
|
|
Before Width: | Height: | Size: 167 B After Width: | Height: | Size: 409 B |
|
@ -12,15 +12,7 @@ Authors of media (textures)
|
||||||
BlockMen (CC BY-SA 3.0)
|
BlockMen (CC BY-SA 3.0)
|
||||||
|
|
||||||
This mod adds a bed to Minetest which allows to skip the night.
|
This mod adds a bed to Minetest which allows to skip the night.
|
||||||
To sleep, rightclick the bed. If playing in singleplayer mode the night gets skipped
|
To sleep, rightclick the bed.
|
||||||
immediately. If playing multiplayer you get shown how many other players are in bed too,
|
Another feature is a controlled respawning. If you have slept in bed your respawn point is set to the beds location and you will respawn there after
|
||||||
if all players are sleeping the night gets skipped. The night skip can be forced if more
|
|
||||||
than 50% of the players are lying in bed and use this option.
|
|
||||||
|
|
||||||
Another feature is a controlled respawning. If you have slept in bed (not just lying in
|
|
||||||
it) your respawn point is set to the beds location and you will respawn there after
|
|
||||||
death.
|
death.
|
||||||
You can disable the respawn at beds by setting "enable_bed_respawn = false" in
|
Use the mcl_playersSleepingPercentage setting to enable/disable night skipping or set a percentage of how many players need to sleep to skip the night.
|
||||||
minetest.conf.
|
|
||||||
You can disable the night skip feature by setting "enable_bed_night_skip = false" in
|
|
||||||
minetest.conf or by using the /set command in-game.
|
|
|
@ -14,39 +14,34 @@ local worlds_mod = minetest.get_modpath("mcl_worlds")
|
||||||
|
|
||||||
local function get_look_yaw(pos)
|
local function get_look_yaw(pos)
|
||||||
local n = minetest.get_node(pos)
|
local n = minetest.get_node(pos)
|
||||||
if n.param2 == 1 then
|
local param = n.param2
|
||||||
return math.pi / 2, n.param2
|
if param == 1 then
|
||||||
elseif n.param2 == 3 then
|
return math.pi / 2, param
|
||||||
return -math.pi / 2, n.param2
|
elseif param == 3 then
|
||||||
elseif n.param2 == 0 then
|
return -math.pi / 2, param
|
||||||
return math.pi, n.param2
|
elseif param == 0 then
|
||||||
|
return math.pi, param
|
||||||
else
|
else
|
||||||
return 0, n.param2
|
return 0, param
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function players_in_bed_setting()
|
||||||
|
return tonumber(minetest.settings:get("mcl_playersSleepingPercentage")) or 100
|
||||||
|
end
|
||||||
|
|
||||||
local function is_night_skip_enabled()
|
local function is_night_skip_enabled()
|
||||||
local enable_night_skip = minetest.settings:get_bool("enable_bed_night_skip")
|
return players_in_bed_setting() <= 100
|
||||||
if enable_night_skip == nil then
|
|
||||||
enable_night_skip = true
|
|
||||||
end
|
|
||||||
return enable_night_skip
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_in_beds(players)
|
local function check_in_beds(players)
|
||||||
local in_bed = mcl_beds.player
|
|
||||||
if not players then
|
if not players then
|
||||||
players = minetest.get_connected_players()
|
players = minetest.get_connected_players()
|
||||||
end
|
end
|
||||||
|
if player_in_bed <= 0 then
|
||||||
for n, player in pairs(players) do
|
return false
|
||||||
local name = player:get_player_name()
|
|
||||||
if not in_bed[name] then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
return players_in_bed_setting() <= (player_in_bed * 100) / #players
|
||||||
return #players > 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- These monsters do not prevent sleep
|
-- These monsters do not prevent sleep
|
||||||
|
@ -198,7 +193,7 @@ end
|
||||||
local function update_formspecs(finished, ges)
|
local function update_formspecs(finished, ges)
|
||||||
local ges = ges or #minetest.get_connected_players()
|
local ges = ges or #minetest.get_connected_players()
|
||||||
local form_n = "size[12,5;true]"
|
local form_n = "size[12,5;true]"
|
||||||
local all_in_bed = ges == player_in_bed
|
local all_in_bed = players_in_bed_setting() <= (player_in_bed * 100) / ges
|
||||||
local night_skip = is_night_skip_enabled()
|
local night_skip = is_night_skip_enabled()
|
||||||
local button_leave = "button_exit[4,3;4,0.75;leave;"..F(S("Leave bed")).."]"
|
local button_leave = "button_exit[4,3;4,0.75;leave;"..F(S("Leave bed")).."]"
|
||||||
local button_abort = "button_exit[4,3;4,0.75;leave;"..F(S("Abort sleep")).."]"
|
local button_abort = "button_exit[4,3;4,0.75;leave;"..F(S("Abort sleep")).."]"
|
||||||
|
@ -221,7 +216,13 @@ local function update_formspecs(finished, ges)
|
||||||
form_n = form_n .. bg_sleep
|
form_n = form_n .. bg_sleep
|
||||||
form_n = form_n .. button_abort
|
form_n = form_n .. button_abort
|
||||||
else
|
else
|
||||||
text = text .. "\n" .. S("You will fall asleep when all players are in bed.")
|
local comment = "You will fall asleep when "
|
||||||
|
if players_in_bed_setting() == 100 then
|
||||||
|
comment = S(comment .. "all players are in bed.")
|
||||||
|
else
|
||||||
|
comment = S(comment .. "@1% of all players are in bed.", players_in_bed_setting())
|
||||||
|
end
|
||||||
|
text = text .. "\n" .. comment
|
||||||
form_n = form_n .. bg_presleep
|
form_n = form_n .. bg_presleep
|
||||||
form_n = form_n .. button_leave
|
form_n = form_n .. button_leave
|
||||||
end
|
end
|
||||||
|
@ -330,7 +331,7 @@ function mcl_beds.on_rightclick(pos, player, is_top)
|
||||||
message = select(2, lay_down(player, ppos, other))
|
message = select(2, lay_down(player, ppos, other))
|
||||||
end
|
end
|
||||||
if message then
|
if message then
|
||||||
mcl_tmp_message.message(player, message)
|
mcl_title.set(player, "actionbar", {text=message, color="white", stay=60})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
lay_down(player, nil, nil, false)
|
lay_down(player, nil, nil, false)
|
||||||
|
@ -349,7 +350,6 @@ function mcl_beds.on_rightclick(pos, player, is_top)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Callbacks
|
-- Callbacks
|
||||||
minetest.register_on_joinplayer(function(player)
|
minetest.register_on_joinplayer(function(player)
|
||||||
local meta = player:get_meta()
|
local meta = player:get_meta()
|
||||||
|
|
|
@ -37,5 +37,6 @@ Players in bed: @1/@2=Spieler im Bett: @1/@2
|
||||||
Note: Night skip is disabled.=Anmerkung: Überspringen der Nacht deaktiviert.
|
Note: Night skip is disabled.=Anmerkung: Überspringen der Nacht deaktiviert.
|
||||||
You're sleeping.=Sie schlafen.
|
You're sleeping.=Sie schlafen.
|
||||||
You will fall asleep when all players are in bed.=Sie werden einschlafen, wenn alle Spieler im Bett sind.
|
You will fall asleep when all players are in bed.=Sie werden einschlafen, wenn alle Spieler im Bett sind.
|
||||||
|
You will fall asleep when @1% of all players are in bed.=Sie werden einschlafen, wenn @1% der Spieler im Bett sind.
|
||||||
You're in bed.=Sie sind im Bett.
|
You're in bed.=Sie sind im Bett.
|
||||||
Allows you to sleep=Zum Einschafen
|
Allows you to sleep=Zum Einschafen
|
||||||
|
|
|
@ -37,5 +37,6 @@ Players in bed: @1/@2=
|
||||||
Note: Night skip is disabled.=
|
Note: Night skip is disabled.=
|
||||||
You're sleeping.=
|
You're sleeping.=
|
||||||
You will fall asleep when all players are in bed.=
|
You will fall asleep when all players are in bed.=
|
||||||
|
You will fall asleep when @1% of all players are in bed.=
|
||||||
You're in bed.=
|
You're in bed.=
|
||||||
Allows you to sleep=
|
Allows you to sleep=
|
||||||
|
|
|
@ -43,7 +43,7 @@ S("An arrow fired from a bow has a regular damage of 1-9. At full charge, there'
|
||||||
S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."),
|
S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."),
|
||||||
_doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."),
|
_doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."),
|
||||||
inventory_image = "mcl_bows_arrow_inv.png",
|
inventory_image = "mcl_bows_arrow_inv.png",
|
||||||
groups = { ammo=1, ammo_bow=1, ammo_bow_regular=1 },
|
groups = { ammo=1, ammo_bow=1, ammo_bow_regular=1, ammo_crossbow=1 },
|
||||||
_on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir)
|
_on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir)
|
||||||
-- Shoot arrow
|
-- Shoot arrow
|
||||||
local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51))
|
local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51))
|
||||||
|
@ -324,7 +324,9 @@ function ARROW_ENTITY.on_step(self, dtime)
|
||||||
end
|
end
|
||||||
if not obj:is_player() then
|
if not obj:is_player() then
|
||||||
mcl_burning.extinguish(self.object)
|
mcl_burning.extinguish(self.object)
|
||||||
self.object:remove()
|
if self._piercing == 0 then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,454 @@
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
|
mcl_bows_s = {}
|
||||||
|
|
||||||
|
-- local arrows = {
|
||||||
|
-- ["mcl_bows:arrow"] = "mcl_bows:arrow_entity",
|
||||||
|
-- }
|
||||||
|
|
||||||
|
local GRAVITY = 9.81
|
||||||
|
local BOW_DURABILITY = 385
|
||||||
|
|
||||||
|
-- Charging time in microseconds
|
||||||
|
local _BOW_CHARGE_TIME_HALF = 350000 -- bow level 1
|
||||||
|
local _BOW_CHARGE_TIME_FULL = 900000 -- bow level 2 (full charge)
|
||||||
|
|
||||||
|
local BOW_CHARGE_TIME_HALF = 350000 -- bow level 1
|
||||||
|
local BOW_CHARGE_TIME_FULL = 900000 -- bow level 2 (full charge)
|
||||||
|
|
||||||
|
-- Factor to multiply with player speed while player uses bow
|
||||||
|
-- This emulates the sneak speed.
|
||||||
|
local PLAYER_USE_CROSSBOW_SPEED = tonumber(minetest.settings:get("movement_speed_crouch")) / tonumber(minetest.settings:get("movement_speed_walk"))
|
||||||
|
|
||||||
|
-- TODO: Use Minecraft speed (ca. 53 m/s)
|
||||||
|
-- Currently nerfed because at full speed the arrow would easily get out of the range of the loaded map.
|
||||||
|
local BOW_MAX_SPEED = 68
|
||||||
|
|
||||||
|
local function play_load_sound(id, pos)
|
||||||
|
minetest.sound_play("mcl_bows_crossbow_drawback_"..id, {pos=pos, max_hear_distance=12}, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ Store the charging state of each player.
|
||||||
|
keys: player name
|
||||||
|
value:
|
||||||
|
nil = not charging or player not existing
|
||||||
|
number: currently charging, the number is the time from minetest.get_us_time
|
||||||
|
in which the charging has started
|
||||||
|
]]
|
||||||
|
local bow_load = {}
|
||||||
|
|
||||||
|
-- Another player table, this one stores the wield index of the bow being charged
|
||||||
|
local bow_index = {}
|
||||||
|
|
||||||
|
function mcl_bows_s.shoot_arrow_crossbow(arrow_item, pos, dir, yaw, shooter, power, damage, is_critical, crossbow_stack, collectable)
|
||||||
|
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, arrow_item.."_entity")
|
||||||
|
if power == nil then
|
||||||
|
power = BOW_MAX_SPEED --19
|
||||||
|
end
|
||||||
|
if damage == nil then
|
||||||
|
damage = 3
|
||||||
|
end
|
||||||
|
local knockback
|
||||||
|
if crossbow_stack then
|
||||||
|
local enchantments = mcl_enchanting.get_enchantments(crossbow_stack)
|
||||||
|
if enchantments.piercing then
|
||||||
|
obj:get_luaentity()._piercing = 1 * enchantments.piercing
|
||||||
|
else
|
||||||
|
obj:get_luaentity()._piercing = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
obj:set_velocity({x=dir.x*power, y=dir.y*power, z=dir.z*power})
|
||||||
|
obj:set_acceleration({x=0, y=-GRAVITY, z=0})
|
||||||
|
obj:set_yaw(yaw-math.pi/2)
|
||||||
|
local le = obj:get_luaentity()
|
||||||
|
le._shooter = shooter
|
||||||
|
le._source_object = shooter
|
||||||
|
le._damage = damage
|
||||||
|
le._is_critical = is_critical
|
||||||
|
le._startpos = pos
|
||||||
|
le._knockback = knockback
|
||||||
|
le._collectable = collectable
|
||||||
|
minetest.sound_play("mcl_bows_crossbow_shoot", {pos=pos, max_hear_distance=16}, true)
|
||||||
|
if shooter and shooter:is_player() then
|
||||||
|
if obj:get_luaentity().player == "" then
|
||||||
|
obj:get_luaentity().player = shooter
|
||||||
|
end
|
||||||
|
obj:get_luaentity().node = shooter:get_inventory():get_stack("main", 1):get_name()
|
||||||
|
end
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_arrow(player)
|
||||||
|
local inv = player:get_inventory()
|
||||||
|
local arrow_stack, arrow_stack_id
|
||||||
|
for i=1, inv:get_size("main") do
|
||||||
|
local it = inv:get_stack("main", i)
|
||||||
|
if not it:is_empty() and minetest.get_item_group(it:get_name(), "ammo_crossbow") ~= 0 then
|
||||||
|
arrow_stack = it
|
||||||
|
arrow_stack_id = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return arrow_stack, arrow_stack_id
|
||||||
|
end
|
||||||
|
|
||||||
|
local function player_shoot_arrow(itemstack, player, power, damage, is_critical)
|
||||||
|
local has_multishot_enchantment = mcl_enchanting.has_enchantment(player:get_wielded_item(), "multishot")
|
||||||
|
local arrow_itemstring = wielditem:get_meta():get("arrow")
|
||||||
|
|
||||||
|
if not arrow_itemstring then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local playerpos = player:get_pos()
|
||||||
|
local dir = player:get_look_dir()
|
||||||
|
local yaw = player:get_look_horizontal()
|
||||||
|
|
||||||
|
if has_multishot_enchantment then
|
||||||
|
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, {x=dir.x, y=dir.y, z=dir.z + .2}, yaw, player, power, damage, is_critical, player:get_wielded_item(), false)
|
||||||
|
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, {x=dir.x, y=dir.y, z=dir.z - .2}, yaw, player, power, damage, is_critical, player:get_wielded_item(), false)
|
||||||
|
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item(), true)
|
||||||
|
else
|
||||||
|
mcl_bows_s.shoot_arrow_crossbow(arrow_itemstring, {x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, dir, yaw, player, power, damage, is_critical, player:get_wielded_item(), true)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bow item, uncharged state
|
||||||
|
minetest.register_tool("mcl_bows:crossbow", {
|
||||||
|
description = S("Corssbow"),
|
||||||
|
_tt_help = S("Launches arrows"),
|
||||||
|
_doc_items_longdesc = S("Bows are ranged weapons to shoot arrows at your foes.").."\n"..
|
||||||
|
S("The speed and damage of the arrow increases the longer you charge. The regular damage of the arrow is between 1 and 9. At full charge, there's also a 20% of a critical hit, dealing 10 damage instead."),
|
||||||
|
_doc_items_usagehelp = S("To use the bow, you first need to have at least one arrow anywhere in your inventory (unless in Creative Mode). Hold down the right mouse button to charge, release to shoot."),
|
||||||
|
_doc_items_durability = BOW_DURABILITY,
|
||||||
|
inventory_image = "mcl_bows_crossbow.png",
|
||||||
|
wield_scale = mcl_vars.tool_wield_scale,
|
||||||
|
stack_max = 1,
|
||||||
|
range = 4,
|
||||||
|
-- Trick to disable digging as well
|
||||||
|
on_use = function() return end,
|
||||||
|
on_place = function(itemstack, player, pointed_thing)
|
||||||
|
if pointed_thing and pointed_thing.type == "node" then
|
||||||
|
-- Call on_rightclick if the pointed node defines it
|
||||||
|
local node = minetest.get_node(pointed_thing.under)
|
||||||
|
if player and not player:get_player_control().sneak then
|
||||||
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||||
|
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, player, itemstack) or itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
itemstack:get_meta():set_string("active", "true")
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
on_secondary_use = function(itemstack)
|
||||||
|
itemstack:get_meta():set_string("active", "true")
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
groups = {weapon=1,weapon_ranged=1,crossbow=1,enchantability=1},
|
||||||
|
_mcl_uses = 326,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_tool("mcl_bows:crossbow_loaded", {
|
||||||
|
description = S("Corssbow"),
|
||||||
|
_tt_help = S("Launches arrows"),
|
||||||
|
_doc_items_longdesc = S("Corssbow are ranged weapons to shoot arrows at your foes.").."\n"..
|
||||||
|
S("The speed and damage of the arrow increases the longer you charge. The regular damage of the arrow is between 1 and 9. At full charge, there's also a 20% of a critical hit, dealing 10 damage instead."),
|
||||||
|
_doc_items_usagehelp = S("To use the corssbow, you first need to have at least one arrow anywhere in your inventory (unless in Creative Mode). Hold down the right mouse button to charge, release to load an arrow into the chamber, then to shoot press left mouse."),
|
||||||
|
_doc_items_durability = BOW_DURABILITY,
|
||||||
|
inventory_image = "mcl_bows_crossbow_3.png",
|
||||||
|
wield_scale = mcl_vars.tool_wield_scale,
|
||||||
|
stack_max = 1,
|
||||||
|
range = 4,
|
||||||
|
-- Trick to disable digging as well
|
||||||
|
on_use = function() return end,
|
||||||
|
on_place = function(itemstack, player, pointed_thing)
|
||||||
|
if pointed_thing and pointed_thing.type == "node" then
|
||||||
|
-- Call on_rightclick if the pointed node defines it
|
||||||
|
local node = minetest.get_node(pointed_thing.under)
|
||||||
|
if player and not player:get_player_control().sneak then
|
||||||
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||||
|
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, player, itemstack) or itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
itemstack:get_meta():set_string("active", "true")
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
on_secondary_use = function(itemstack)
|
||||||
|
itemstack:get_meta():set_string("active", "true")
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
groups = {weapon=1,weapon_ranged=1,crossbow=1,enchantability=1},
|
||||||
|
_mcl_uses = 326,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Iterates through player inventory and resets all the bows in "charging" state back to their original stage
|
||||||
|
local function reset_bows(player)
|
||||||
|
local inv = player:get_inventory()
|
||||||
|
local list = inv:get_list("main")
|
||||||
|
for place, stack in pairs(list) do
|
||||||
|
if stack:get_name() == "mcl_bows:crossbow" or stack:get_name() == "mcl_bows:crossbow_enchanted" then
|
||||||
|
stack:get_meta():set_string("active", "")
|
||||||
|
elseif stack:get_name()=="mcl_bows:crossbow_0" or stack:get_name()=="mcl_bows:crossbow_1" or stack:get_name()=="mcl_bows:crossbow_2" then
|
||||||
|
stack:set_name("mcl_bows:crossbow")
|
||||||
|
stack:get_meta():set_string("active", "")
|
||||||
|
list[place] = stack
|
||||||
|
elseif stack:get_name()=="mcl_bows:crossbow_0_enchanted" or stack:get_name()=="mcl_bows:crossbow_1_enchanted" or stack:get_name()=="mcl_bows:crossbow_2_enchanted" then
|
||||||
|
stack:set_name("mcl_bows:crossbow_enchanted")
|
||||||
|
stack:get_meta():set_string("active", "")
|
||||||
|
list[place] = stack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
inv:set_list("main", list)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Resets the bow charging state and player speed. To be used when the player is no longer charging the bow
|
||||||
|
local function reset_bow_state(player, also_reset_bows)
|
||||||
|
bow_load[player:get_player_name()] = nil
|
||||||
|
bow_index[player:get_player_name()] = nil
|
||||||
|
if minetest.get_modpath("playerphysics") then
|
||||||
|
playerphysics.remove_physics_factor(player, "speed", "mcl_bows:use_crossbow")
|
||||||
|
end
|
||||||
|
if also_reset_bows then
|
||||||
|
reset_bows(player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Bow in charging state
|
||||||
|
for level=0, 2 do
|
||||||
|
minetest.register_tool("mcl_bows:crossbow_"..level, {
|
||||||
|
description = S("Crossbow"),
|
||||||
|
_doc_items_create_entry = false,
|
||||||
|
inventory_image = "mcl_bows_crossbow_"..level..".png",
|
||||||
|
wield_scale = mcl_vars.tool_wield_scale,
|
||||||
|
stack_max = 1,
|
||||||
|
range = 0, -- Pointing range to 0 to prevent punching with bow :D
|
||||||
|
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, enchantability=1},
|
||||||
|
-- Trick to disable digging as well
|
||||||
|
on_use = function() return end,
|
||||||
|
on_drop = function(itemstack, dropper, pos)
|
||||||
|
reset_bow_state(dropper)
|
||||||
|
itemstack:get_meta():set_string("active", "")
|
||||||
|
if mcl_enchanting.is_enchanted(itemstack:get_name()) then
|
||||||
|
itemstack:set_name("mcl_bows:crossbow_enchanted")
|
||||||
|
else
|
||||||
|
itemstack:set_name("mcl_bows:crossbow")
|
||||||
|
end
|
||||||
|
minetest.item_drop(itemstack, dropper, pos)
|
||||||
|
itemstack:take_item()
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
-- Prevent accidental interaction with itemframes and other nodes
|
||||||
|
on_place = function(itemstack)
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
_mcl_uses = 385,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
controls.register_on_release(function(player, key, time)
|
||||||
|
if key~="RMB" then return end
|
||||||
|
--local inv = minetest.get_inventory({type="player", name=player:get_player_name()})
|
||||||
|
local wielditem = player:get_wielded_item()
|
||||||
|
if wielditem:get_name()=="mcl_bows:crossbow_2" and get_arrow(player) or wielditem:get_name()=="mcl_bows:crossbow_2" and minetest.is_creative_enabled(player:get_player_name()) or wielditem:get_name()=="mcl_bows:crossbow_2_enchanted" and get_arrow(player) or wielditem:get_name()=="mcl_bows:crossbow_2_enchanted" and minetest.is_creative_enabled(player:get_player_name()) then
|
||||||
|
local arrow_stack, arrow_stack_id = get_arrow(player)
|
||||||
|
local arrow_itemstring
|
||||||
|
|
||||||
|
if minetest.is_creative_enabled(player:get_player_name()) then
|
||||||
|
if arrow_stack then
|
||||||
|
arrow_itemstring = arrow_stack:get_name()
|
||||||
|
else
|
||||||
|
arrow_itemstring = "mcl_bows:arrow"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
arrow_itemstring = arrow_stack:get_name()
|
||||||
|
arrow_stack:take_item()
|
||||||
|
player:get_inventory():set_stack("main", arrow_stack_id, arrow_stack)
|
||||||
|
end
|
||||||
|
|
||||||
|
wielditem:get_meta():set_string("arrow", arrow_itemstring)
|
||||||
|
|
||||||
|
if wielditem:get_name()=="mcl_bows:crossbow_2" then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_loaded")
|
||||||
|
else
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_loaded_enchanted")
|
||||||
|
end
|
||||||
|
player:set_wielded_item(wielditem)
|
||||||
|
minetest.sound_play("mcl_bows_crossbow_load", {pos=player:get_pos(), max_hear_distance=16}, true)
|
||||||
|
else
|
||||||
|
reset_bow_state(player, true)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
controls.register_on_press(function(player, key, time)
|
||||||
|
if key~="LMB" then return end
|
||||||
|
wielditem = player:get_wielded_item()
|
||||||
|
if wielditem:get_name()=="mcl_bows:crossbow_loaded" or wielditem:get_name()=="mcl_bows:crossbow_loaded_enchanted" then
|
||||||
|
local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name())
|
||||||
|
local speed, damage
|
||||||
|
local p_load = bow_load[player:get_player_name()]
|
||||||
|
local charge
|
||||||
|
-- Type sanity check
|
||||||
|
if type(p_load) == "number" then
|
||||||
|
charge = minetest.get_us_time() - p_load
|
||||||
|
else
|
||||||
|
-- In case something goes wrong ...
|
||||||
|
-- Just assume minimum charge.
|
||||||
|
charge = 0
|
||||||
|
minetest.log("warning", "[mcl_bows] Player "..player:get_player_name().." fires arrow with non-numeric bow_load!")
|
||||||
|
end
|
||||||
|
charge = math.max(math.min(charge, BOW_CHARGE_TIME_FULL), 0)
|
||||||
|
|
||||||
|
local charge_ratio = charge / BOW_CHARGE_TIME_FULL
|
||||||
|
charge_ratio = math.max(math.min(charge_ratio, 1), 0)
|
||||||
|
|
||||||
|
-- Calculate damage and speed
|
||||||
|
-- Fully charged
|
||||||
|
local is_critical = false
|
||||||
|
speed = BOW_MAX_SPEED
|
||||||
|
local r = math.random(1,5)
|
||||||
|
if r == 1 then
|
||||||
|
-- 20% chance for critical hit
|
||||||
|
damage = 10
|
||||||
|
is_critical = true
|
||||||
|
else
|
||||||
|
damage = 9
|
||||||
|
end
|
||||||
|
|
||||||
|
local has_shot = player_shoot_arrow(wielditem, player, speed, damage, is_critical)
|
||||||
|
|
||||||
|
if enchanted then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_enchanted")
|
||||||
|
else
|
||||||
|
wielditem:set_name("mcl_bows:crossbow")
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_shot and not minetest.is_creative_enabled(player:get_player_name()) then
|
||||||
|
local durability = BOW_DURABILITY
|
||||||
|
local unbreaking = mcl_enchanting.get_enchantment(wielditem, "unbreaking")
|
||||||
|
local multishot = mcl_enchanting.get_enchantment(wielditem, "multishot")
|
||||||
|
if unbreaking > 0 then
|
||||||
|
durability = durability * (unbreaking + 1)
|
||||||
|
end
|
||||||
|
if multishot then
|
||||||
|
durability = durability / 3
|
||||||
|
end
|
||||||
|
wielditem:add_wear(65535/durability)
|
||||||
|
end
|
||||||
|
player:set_wielded_item(wielditem)
|
||||||
|
reset_bow_state(player, true)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
controls.register_on_hold(function(player, key, time)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local creative = minetest.is_creative_enabled(name)
|
||||||
|
if key ~= "RMB" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
--local inv = minetest.get_inventory({type="player", name=name})
|
||||||
|
local wielditem = player:get_wielded_item()
|
||||||
|
local enchantments = mcl_enchanting.get_enchantments(wielditem)
|
||||||
|
if enchantments.quick_charge then
|
||||||
|
BOW_CHARGE_TIME_HALF = _BOW_CHARGE_TIME_HALF - (enchantments.quick_charge * 0.13 * 1000000 * .5)
|
||||||
|
BOW_CHARGE_TIME_FULL = _BOW_CHARGE_TIME_FULL - (enchantments.quick_charge * 0.13 * 1000000)
|
||||||
|
else
|
||||||
|
BOW_CHARGE_TIME_HALF = _BOW_CHARGE_TIME_HALF
|
||||||
|
BOW_CHARGE_TIME_FULL = _BOW_CHARGE_TIME_FULL
|
||||||
|
end
|
||||||
|
|
||||||
|
if bow_load[name] == nil and (wielditem:get_name()=="mcl_bows:crossbow" or wielditem:get_name()=="mcl_bows:crossbow_enchanted") and wielditem:get_meta():get("active") and (creative or get_arrow(player)) then
|
||||||
|
local enchanted = mcl_enchanting.is_enchanted(wielditem:get_name())
|
||||||
|
if enchanted then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_0_enchanted")
|
||||||
|
play_load_sound(0, player:get_pos())
|
||||||
|
else
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_0")
|
||||||
|
play_load_sound(0, player:get_pos())
|
||||||
|
end
|
||||||
|
player:set_wielded_item(wielditem)
|
||||||
|
if minetest.get_modpath("playerphysics") then
|
||||||
|
-- Slow player down when using bow
|
||||||
|
playerphysics.add_physics_factor(player, "speed", "mcl_bows:use_crossbow", PLAYER_USE_CROSSBOW_SPEED)
|
||||||
|
end
|
||||||
|
bow_load[name] = minetest.get_us_time()
|
||||||
|
bow_index[name] = player:get_wield_index()
|
||||||
|
else
|
||||||
|
if player:get_wield_index() == bow_index[name] then
|
||||||
|
if type(bow_load[name]) == "number" then
|
||||||
|
if wielditem:get_name() == "mcl_bows:crossbow_0" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_HALF then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_1")
|
||||||
|
play_load_sound(1, player:get_pos())
|
||||||
|
elseif wielditem:get_name() == "mcl_bows:crossbow_0_enchanted" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_HALF then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_1_enchanted")
|
||||||
|
play_load_sound(1, player:get_pos())
|
||||||
|
elseif wielditem:get_name() == "mcl_bows:crossbow_1" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_FULL then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_2")
|
||||||
|
play_load_sound(2, player:get_pos())
|
||||||
|
elseif wielditem:get_name() == "mcl_bows:crossbow_1_enchanted" and minetest.get_us_time() - bow_load[name] >= BOW_CHARGE_TIME_FULL then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_2_enchanted")
|
||||||
|
play_load_sound(2, player:get_pos())
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if wielditem:get_name() == "mcl_bows:crossbow_0" or wielditem:get_name() == "mcl_bows:crossbow_1" or wielditem:get_name() == "mcl_bows:crossbow_2" then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow")
|
||||||
|
play_load_sound(1, player:get_pos())
|
||||||
|
elseif wielditem:get_name() == "mcl_bows:crossbow_0_enchanted" or wielditem:get_name() == "mcl_bows:crossbow_1_enchanted" or wielditem:get_name() == "mcl_bows:crossbow_2_enchanted" then
|
||||||
|
wielditem:set_name("mcl_bows:crossbow_enchanted")
|
||||||
|
play_load_sound(1, player:get_pos())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player:set_wielded_item(wielditem)
|
||||||
|
else
|
||||||
|
reset_bow_state(player, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
for _, player in pairs(minetest.get_connected_players()) do
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local wielditem = player:get_wielded_item()
|
||||||
|
local wieldindex = player:get_wield_index()
|
||||||
|
--local controls = player:get_player_control()
|
||||||
|
if type(bow_load[name]) == "number" and ((wielditem:get_name()~="mcl_bows:crossbow_0" and wielditem:get_name()~="mcl_bows:crossbow_1" and wielditem:get_name()~="mcl_bows:crossbow_2" and wielditem:get_name()~="mcl_bows:crossbow_0_enchanted" and wielditem:get_name()~="mcl_bows:crossbow_1_enchanted" and wielditem:get_name()~="mcl_bows:crossbow_2_enchanted") or wieldindex ~= bow_index[name]) then
|
||||||
|
reset_bow_state(player, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
reset_bows(player)
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
reset_bow_state(player, true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "mcl_bows:crossbow",
|
||||||
|
recipe = {
|
||||||
|
{"mcl_core:stick", "mcl_core:iron_ingot", "mcl_core:stick"},
|
||||||
|
{"mcl_mobitems:string", "mcl_bows:arrow", "mcl_mobitems:string"},
|
||||||
|
{"", "mcl_core:stick", ""},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_craft({
|
||||||
|
type = "fuel",
|
||||||
|
recipe = "group:bow",
|
||||||
|
burntime = 15,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Add entry aliases for the Help
|
||||||
|
if minetest.get_modpath("doc") then
|
||||||
|
doc.add_entry_alias("tools", "mcl_bows:crossbow", "tools", "mcl_bows:crossbow_0")
|
||||||
|
doc.add_entry_alias("tools", "mcl_bows:crossbow", "tools", "mcl_bows:crossbow_1")
|
||||||
|
doc.add_entry_alias("tools", "mcl_bows:crossbow", "tools", "mcl_bows:crossbow_2")
|
||||||
|
end
|
|
@ -1,5 +1,11 @@
|
||||||
|
--Bow
|
||||||
dofile(minetest.get_modpath("mcl_bows") .. "/arrow.lua")
|
dofile(minetest.get_modpath("mcl_bows") .. "/arrow.lua")
|
||||||
dofile(minetest.get_modpath("mcl_bows") .. "/bow.lua")
|
dofile(minetest.get_modpath("mcl_bows") .. "/bow.lua")
|
||||||
|
dofile(minetest.get_modpath("mcl_bows") .. "/rocket.lua")
|
||||||
|
|
||||||
|
--Crossbow
|
||||||
|
dofile(minetest.get_modpath("mcl_bows") .. "/crossbow.lua")
|
||||||
|
|
||||||
|
--Compatiblility with older MineClone worlds
|
||||||
minetest.register_alias("mcl_throwing:bow", "mcl_bows:bow")
|
minetest.register_alias("mcl_throwing:bow", "mcl_bows:bow")
|
||||||
minetest.register_alias("mcl_throwing:arrow", "mcl_bows:arrow")
|
minetest.register_alias("mcl_throwing:arrow", "mcl_bows:arrow")
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Blender MTL File: 'None'
|
||||||
|
# Material Count: 1
|
||||||
|
|
||||||
|
newmtl None
|
||||||
|
Ns 500
|
||||||
|
Ka 0.8 0.8 0.8
|
||||||
|
Kd 0.8 0.8 0.8
|
||||||
|
Ks 0.8 0.8 0.8
|
||||||
|
d 1
|
||||||
|
illum 2
|
|
@ -0,0 +1,706 @@
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
|
local math = math
|
||||||
|
local vector = vector
|
||||||
|
|
||||||
|
-- Time in seconds after which a stuck arrow is deleted
|
||||||
|
local ARROW_TIMEOUT = 1
|
||||||
|
-- Time after which stuck arrow is rechecked for being stuck
|
||||||
|
local STUCK_RECHECK_TIME = 0.1
|
||||||
|
|
||||||
|
--local GRAVITY = 9.81
|
||||||
|
|
||||||
|
local YAW_OFFSET = -math.pi/2
|
||||||
|
|
||||||
|
local function dir_to_pitch(dir)
|
||||||
|
--local dir2 = vector.normalize(dir)
|
||||||
|
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||||
|
return -math.atan2(-dir.y, xz)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function random_arrow_positions(positions, placement)
|
||||||
|
if positions == "x" then
|
||||||
|
return math.random(-4, 4)
|
||||||
|
elseif positions == "y" then
|
||||||
|
return math.random(0, 10)
|
||||||
|
end
|
||||||
|
if placement == "front" and positions == "z" then
|
||||||
|
return 3
|
||||||
|
elseif placement == "back" and positions == "z" then
|
||||||
|
return -3
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function damage_explosion(self, damagemulitplier)
|
||||||
|
mcl_explosions.explode(self.object:get_pos(), 3, {})
|
||||||
|
local objects = minetest.get_objects_inside_radius(self.object:get_pos(), 8)
|
||||||
|
for _,obj in pairs(objects) do
|
||||||
|
if obj:is_player() then
|
||||||
|
mcl_util.deal_damage(obj, damagemulitplier - vector.distance(self.object:get_pos(), obj:get_pos()), {type = "explosion"})
|
||||||
|
elseif obj:get_luaentity()._cmi_is_mob then
|
||||||
|
obj:punch(self.object, 1.0, {
|
||||||
|
full_punch_interval=1.0,
|
||||||
|
damage_groups={fleshy=damagemulitplier - vector.distance(self.object:get_pos(), obj:get_pos())},
|
||||||
|
}, self.object:get_velocity())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function particle_explosion(self)
|
||||||
|
local particle_pattern = math.random(1, 3)
|
||||||
|
local fpitch = 0
|
||||||
|
local true_type = ""
|
||||||
|
local type = math.random(1,2)
|
||||||
|
local size = math.random(1,3)
|
||||||
|
local colors = {"red", "yellow", "blue", "green", "white"}
|
||||||
|
local this_colors = {colors[math.random(#colors)], colors[math.random(#colors)], colors[math.random(#colors)]}
|
||||||
|
|
||||||
|
if size == 1 then
|
||||||
|
fpitch = math.random(200, 300)
|
||||||
|
elseif size == 2 then
|
||||||
|
fpitch = math.random(100, 130)
|
||||||
|
else
|
||||||
|
fpitch = math.random(60, 70)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type == 1 then
|
||||||
|
true_type = "Popper"
|
||||||
|
else
|
||||||
|
true_type = "Floof"
|
||||||
|
end
|
||||||
|
|
||||||
|
if type == 1 then
|
||||||
|
minetest.sound_play("mcl_bows_firework", {
|
||||||
|
pos = self.object:get_pos(),
|
||||||
|
max_hear_distance = 100,
|
||||||
|
gain = 3.0,
|
||||||
|
pitch = fpitch/100
|
||||||
|
}, true)
|
||||||
|
else
|
||||||
|
minetest.sound_play("mcl_bows_firework_soft", {
|
||||||
|
pos = self.object:get_pos(),
|
||||||
|
max_hear_distance = 100,
|
||||||
|
gain = 4.0,
|
||||||
|
pitch = fpitch/100
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
if particle_pattern == 1 then
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 400 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-7 * size,-7 * size,-7 * size),
|
||||||
|
maxvel = vector.new(7 * size,7 * size,7 * size),
|
||||||
|
minexptime = .6 * size / 2,
|
||||||
|
maxexptime = .9 * size / 2,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[1]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 400 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-2 * size,-2 * size,-2 * size),
|
||||||
|
maxvel = vector.new(2 * size,2 * size,2 * size),
|
||||||
|
minexptime = .6 * size / 2,
|
||||||
|
maxexptime = .9 * size / 2,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[2]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 100 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-14 * size,-14 * size,-14 * size),
|
||||||
|
maxvel = vector.new(14 * size,14 * size,14 * size),
|
||||||
|
minexptime = .6 * size / 2,
|
||||||
|
maxexptime = .9 * size / 2,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[3]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
elseif particle_pattern == 2 then
|
||||||
|
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 240 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-5 * size,-5 * size,-5 * size),
|
||||||
|
maxvel = vector.new(5 * size,5 * size,5 * size),
|
||||||
|
minexptime = .6 * size / 2,
|
||||||
|
maxexptime = .9 * size / 2,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[1]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 500 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-2 * size,-2 * size,-2 * size),
|
||||||
|
maxvel = vector.new(2 * size,2 * size,2 * size),
|
||||||
|
minexptime = .6 * size / 2,
|
||||||
|
maxexptime = .9 * size / 2,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[2]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 350 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-3 * size,-3 * size,-3 * size),
|
||||||
|
maxvel = vector.new(3 * size,3 * size,3 * size),
|
||||||
|
minexptime = .6 * size / 2,
|
||||||
|
maxexptime = .9 * size / 2,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[3]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
elseif particle_pattern == 3 then
|
||||||
|
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 400 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-6 * size,-4 * size,-6 * size),
|
||||||
|
maxvel = vector.new(6 * size,4 * size,6 * size),
|
||||||
|
minexptime = .6 * size,
|
||||||
|
maxexptime = .9 * size,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[1]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 120 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-8 * size,6 * size,-8 * size),
|
||||||
|
maxvel = vector.new(8 * size,6 * size,8 * size),
|
||||||
|
minexptime = .6 * size,
|
||||||
|
maxexptime = .9 * size,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[2]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 130 * size,
|
||||||
|
time = 0.0001,
|
||||||
|
minpos = self.object:get_pos(),
|
||||||
|
maxpos = self.object:get_pos(),
|
||||||
|
minvel = vector.new(-3 * size,3 * size,-3 * size),
|
||||||
|
maxvel = vector.new(3 * size,3 * size,3 * size),
|
||||||
|
minexptime = .6 * size,
|
||||||
|
maxexptime = .9 * size,
|
||||||
|
minsize = 2 * size,
|
||||||
|
maxsize = 3 * size,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_firework_"..this_colors[3]..".png",
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
local mod_awards = minetest.get_modpath("awards") and minetest.get_modpath("mcl_achievements")
|
||||||
|
local mod_button = minetest.get_modpath("mesecons_button")
|
||||||
|
|
||||||
|
minetest.register_craftitem("mcl_bows:rocket", {
|
||||||
|
description = S("Arrow"),
|
||||||
|
_tt_help = S("Ammunition").."\n"..S("Damage from bow: 1-10").."\n"..S("Damage from dispenser: 3"),
|
||||||
|
_doc_items_longdesc = S("Arrows are ammunition for bows and dispensers.").."\n"..
|
||||||
|
S("An arrow fired from a bow has a regular damage of 1-9. At full charge, there's a 20% chance of a critical hit dealing 10 damage instead. An arrow fired from a dispenser always deals 3 damage.").."\n"..
|
||||||
|
S("Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons."),
|
||||||
|
_doc_items_usagehelp = S("To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it."),
|
||||||
|
inventory_image = "mcl_bows_rocket.png",
|
||||||
|
groups = { ammo=1, ammo_crossbow=1, ammo_bow_regular=1 },
|
||||||
|
_on_dispense = function(itemstack, dispenserpos, droppos, dropnode, dropdir)
|
||||||
|
-- Shoot arrow
|
||||||
|
local shootpos = vector.add(dispenserpos, vector.multiply(dropdir, 0.51))
|
||||||
|
local yaw = math.atan2(dropdir.z, dropdir.x) + YAW_OFFSET
|
||||||
|
mcl_bows.shoot_arrow(itemstack:get_name(), shootpos, dropdir, yaw, nil, 19, 3)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local ARROW_ENTITY={
|
||||||
|
physical = true,
|
||||||
|
pointable = false,
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "mcl_bows_rocket.obj",
|
||||||
|
visual_size = {x=2.5, y=2.5},
|
||||||
|
textures = {"mcl_bows_rocket.png"},
|
||||||
|
collisionbox = {-0.19, -0.125, -0.19, 0.19, 0.125, 0.19},
|
||||||
|
collide_with_objects = false,
|
||||||
|
_fire_damage_resistant = true,
|
||||||
|
|
||||||
|
_lastpos={},
|
||||||
|
_startpos=nil,
|
||||||
|
_damage=1, -- Damage on impact
|
||||||
|
_is_critical=false, -- Whether this arrow would deal critical damage
|
||||||
|
_stuck=false, -- Whether arrow is stuck
|
||||||
|
_fuse=nil,-- Amount of time (in seconds) the arrow has been stuck so far
|
||||||
|
_fuserechecktimer=nil,-- An additional timer for periodically re-checking the stuck status of an arrow
|
||||||
|
_stuckin=nil, --Position of node in which arow is stuck.
|
||||||
|
_shooter=nil, -- ObjectRef of player or mob who shot it
|
||||||
|
_is_arrow = true,
|
||||||
|
|
||||||
|
_viscosity=0, -- Viscosity of node the arrow is currently in
|
||||||
|
_deflection_cooloff=0, -- Cooloff timer after an arrow deflection, to prevent many deflections in quick succession
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Destroy arrow entity self at pos and drops it as an item
|
||||||
|
local function spawn_item(self, pos)
|
||||||
|
if not minetest.is_creative_enabled("") then
|
||||||
|
local item = minetest.add_item(pos, "mcl_bows:rocket")
|
||||||
|
item:set_velocity({x=0, y=0, z=0})
|
||||||
|
item:set_yaw(self.object:get_yaw())
|
||||||
|
end
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function damage_particles(pos, is_critical)
|
||||||
|
if is_critical then
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 15,
|
||||||
|
time = 0.1,
|
||||||
|
minpos = {x=pos.x-0.5, y=pos.y-0.5, z=pos.z-0.5},
|
||||||
|
maxpos = {x=pos.x+0.5, y=pos.y+0.5, z=pos.z+0.5},
|
||||||
|
minvel = {x=-0.1, y=-0.1, z=-0.1},
|
||||||
|
maxvel = {x=0.1, y=0.1, z=0.1},
|
||||||
|
minacc = {x=0, y=0, z=0},
|
||||||
|
maxacc = {x=0, y=0, z=0},
|
||||||
|
minexptime = 1,
|
||||||
|
maxexptime = 2,
|
||||||
|
minsize = 1.5,
|
||||||
|
maxsize = 1.5,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ARROW_ENTITY.on_step(self, dtime)
|
||||||
|
mcl_burning.tick(self.object, dtime, self)
|
||||||
|
|
||||||
|
self._time_in_air = self._time_in_air + .001
|
||||||
|
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local dpos = table.copy(pos) -- digital pos
|
||||||
|
dpos = vector.round(dpos)
|
||||||
|
local node = minetest.get_node(dpos)
|
||||||
|
|
||||||
|
if not self._fuse then
|
||||||
|
self._fuse = 0
|
||||||
|
end
|
||||||
|
if not self._fuserechecktimer then
|
||||||
|
self._fuserechecktimer = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
self._fuse = self._fuse + dtime
|
||||||
|
self._fuserechecktimer = self._fuserechecktimer + dtime
|
||||||
|
|
||||||
|
if self._fuse > ARROW_TIMEOUT then
|
||||||
|
self._stuck = true
|
||||||
|
end
|
||||||
|
if self._stuck then
|
||||||
|
if self._fuse > ARROW_TIMEOUT then
|
||||||
|
local eploded_particle = particle_explosion(self)
|
||||||
|
damage_explosion(self, eploded_particle * 17)
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- Drop arrow as item when it is no longer stuck
|
||||||
|
-- FIXME: Arrows are a bit slow to react and continue to float in mid air for a few seconds.
|
||||||
|
if self._fuserechecktimer > STUCK_RECHECK_TIME then
|
||||||
|
local stuckin_def
|
||||||
|
if self._stuckin then
|
||||||
|
stuckin_def = minetest.registered_nodes[minetest.get_node(self._stuckin).name]
|
||||||
|
end
|
||||||
|
-- TODO: In MC, arrow just falls down without turning into an item
|
||||||
|
if stuckin_def and stuckin_def.walkable == false then
|
||||||
|
spawn_item(self, pos)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self._fuserechecktimer = 0
|
||||||
|
end
|
||||||
|
-- Pickup arrow if player is nearby (not in Creative Mode)
|
||||||
|
local objects = minetest.get_objects_inside_radius(pos, 1)
|
||||||
|
for _,obj in ipairs(objects) do
|
||||||
|
if obj:is_player() then
|
||||||
|
if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then
|
||||||
|
if obj:get_inventory():room_for_item("main", "mcl_bows:rocket") then
|
||||||
|
obj:get_inventory():add_item("main", "mcl_bows:rocket")
|
||||||
|
minetest.sound_play("item_drop_pickup", {
|
||||||
|
pos = pos,
|
||||||
|
max_hear_distance = 16,
|
||||||
|
gain = 1.0,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for object "collision". Done every tick (hopefully this is not too stressing)
|
||||||
|
else
|
||||||
|
|
||||||
|
if self._in_player == false then
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 1,
|
||||||
|
time = .0001,
|
||||||
|
minpos = pos,
|
||||||
|
maxpos = pos,
|
||||||
|
minvel = vector.new(-0.1,-0.1,-0.1),
|
||||||
|
maxvel = vector.new(0.1,0.1,0.1),
|
||||||
|
minexptime = 0.5,
|
||||||
|
maxexptime = 0.5,
|
||||||
|
minsize = 2,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = false,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_bows_rocket_particle.png",
|
||||||
|
glow = 1,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
-- We just check for any hurtable objects nearby.
|
||||||
|
-- The radius of 3 is fairly liberal, but anything lower than than will cause
|
||||||
|
-- arrow to hilariously go through mobs often.
|
||||||
|
-- TODO: Implement an ACTUAL collision detection (engine support needed).
|
||||||
|
local objs = minetest.get_objects_inside_radius(pos, 1.5)
|
||||||
|
local closest_object
|
||||||
|
local closest_distance
|
||||||
|
|
||||||
|
if self._deflection_cooloff > 0 then
|
||||||
|
self._deflection_cooloff = self._deflection_cooloff - dtime
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Iterate through all objects and remember the closest attackable object
|
||||||
|
for k, obj in pairs(objs) do
|
||||||
|
local ok = false
|
||||||
|
-- Arrows can only damage players and mobs
|
||||||
|
if obj:is_player() then
|
||||||
|
ok = true
|
||||||
|
elseif obj:get_luaentity() then
|
||||||
|
if (obj:get_luaentity()._cmi_is_mob or obj:get_luaentity()._hittable_by_projectile) then
|
||||||
|
ok = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
local dist = vector.distance(pos, obj:get_pos())
|
||||||
|
if not closest_object or not closest_distance then
|
||||||
|
closest_object = obj
|
||||||
|
closest_distance = dist
|
||||||
|
elseif dist < closest_distance then
|
||||||
|
closest_object = obj
|
||||||
|
closest_distance = dist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If an attackable object was found, we will damage the closest one only
|
||||||
|
|
||||||
|
if closest_object then
|
||||||
|
local obj = closest_object
|
||||||
|
local is_player = obj:is_player()
|
||||||
|
local lua = obj:get_luaentity()
|
||||||
|
if obj == self._shooter and self._time_in_air > 1.02 or obj ~= self._shooter and (is_player or (lua and (lua._cmi_is_mob or lua._hittable_by_projectile))) then
|
||||||
|
if obj:get_hp() > 0 then
|
||||||
|
-- Check if there is no solid node between arrow and object
|
||||||
|
local ray = minetest.raycast(self.object:get_pos(), obj:get_pos(), true)
|
||||||
|
for pointed_thing in ray do
|
||||||
|
if pointed_thing.type == "object" and pointed_thing.ref == closest_object then
|
||||||
|
-- Target reached! We can proceed now.
|
||||||
|
break
|
||||||
|
elseif pointed_thing.type == "node" then
|
||||||
|
local nn = minetest.get_node(minetest.get_pointed_thing_position(pointed_thing)).name
|
||||||
|
local def = minetest.registered_nodes[nn]
|
||||||
|
if (not def) or def.walkable then
|
||||||
|
-- There's a node in the way. Delete arrow without damage
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Punch target object but avoid hurting enderman.
|
||||||
|
if not lua or lua.name ~= "mobs_mc:enderman" then
|
||||||
|
if self._in_player == false then
|
||||||
|
damage_particles(self.object:get_pos(), self._is_critical)
|
||||||
|
end
|
||||||
|
if mcl_burning.is_burning(self.object) then
|
||||||
|
mcl_burning.set_on_fire(obj, 5)
|
||||||
|
end
|
||||||
|
if self._in_player == false then
|
||||||
|
obj:punch(self.object, 1.0, {
|
||||||
|
full_punch_interval=1.0,
|
||||||
|
damage_groups={fleshy=self._damage},
|
||||||
|
}, self.object:get_velocity())
|
||||||
|
if obj:is_player() then
|
||||||
|
local eploded_particle = particle_explosion(self)
|
||||||
|
damage_explosion(self, eploded_particle * 17)
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if is_player then
|
||||||
|
if self._shooter and self._shooter:is_player() and self._in_player == false then
|
||||||
|
-- “Ding” sound for hitting another player
|
||||||
|
minetest.sound_play({name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter:get_player_name()}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if lua then
|
||||||
|
local entity_name = lua.name
|
||||||
|
-- Achievement for hitting skeleton, wither skeleton or stray (TODO) with an arrow at least 50 meters away
|
||||||
|
-- NOTE: Range has been reduced because mobs unload much earlier than that ... >_>
|
||||||
|
-- TODO: This achievement should be given for the kill, not just a hit
|
||||||
|
if self._shooter and self._shooter:is_player() and vector.distance(pos, self._startpos) >= 20 then
|
||||||
|
if mod_awards and (entity_name == "mobs_mc:skeleton" or entity_name == "mobs_mc:stray" or entity_name == "mobs_mc:witherskeleton") then
|
||||||
|
awards.unlock(self._shooter:get_player_name(), "mcl:snipeSkeleton")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self._in_player == false then
|
||||||
|
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not obj:is_player() then
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
if self._piercing == 0 then
|
||||||
|
local eploded_particle = particle_explosion(self)
|
||||||
|
damage_explosion(self, eploded_particle * 17)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for node collision
|
||||||
|
if self._lastpos.x~=nil and not self._stuck then
|
||||||
|
local def = minetest.registered_nodes[node.name]
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
-- Arrow has stopped in one axis, so it probably hit something.
|
||||||
|
-- This detection is a bit clunky, but sadly, MT does not offer a direct collision detection for us. :-(
|
||||||
|
if (math.abs(vel.x) < 0.0001) or (math.abs(vel.z) < 0.0001) or (math.abs(vel.y) < 0.00001) then
|
||||||
|
-- Check for the node to which the arrow is pointing
|
||||||
|
local dir
|
||||||
|
if math.abs(vel.y) < 0.00001 then
|
||||||
|
if self._lastpos.y < pos.y then
|
||||||
|
dir = {x=0, y=1, z=0}
|
||||||
|
else
|
||||||
|
dir = {x=0, y=-1, z=0}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
dir = minetest.facedir_to_dir(minetest.dir_to_facedir(minetest.yaw_to_dir(self.object:get_yaw()-YAW_OFFSET)))
|
||||||
|
end
|
||||||
|
self._stuckin = vector.add(dpos, dir)
|
||||||
|
local snode = minetest.get_node(self._stuckin)
|
||||||
|
local sdef = minetest.registered_nodes[snode.name]
|
||||||
|
|
||||||
|
-- If node is non-walkable, unknown or ignore, don't make arrow stuck.
|
||||||
|
-- This causes a deflection in the engine.
|
||||||
|
if not sdef or sdef.walkable == false or snode.name == "ignore" then
|
||||||
|
self._stuckin = nil
|
||||||
|
if self._deflection_cooloff <= 0 then
|
||||||
|
-- Lose 1/3 of velocity on deflection
|
||||||
|
local newvel = vector.multiply(vel, 0.6667)
|
||||||
|
|
||||||
|
self.object:set_velocity(newvel)
|
||||||
|
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
|
||||||
|
self._deflection_cooloff = 1.0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Node was walkable, make arrow stuck
|
||||||
|
self._stuck = true
|
||||||
|
self._fuserechecktimer = 0
|
||||||
|
|
||||||
|
self.object:set_velocity({x=0, y=0, z=0})
|
||||||
|
self.object:set_acceleration({x=0, y=0, z=0})
|
||||||
|
|
||||||
|
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
||||||
|
|
||||||
|
if mcl_burning.is_burning(self.object) and snode.name == "mcl_tnt:tnt" then
|
||||||
|
tnt.ignite(self._stuckin)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Push the button! Push, push, push the button!
|
||||||
|
if mod_button and minetest.get_item_group(node.name, "button") > 0 and minetest.get_item_group(node.name, "button_push_by_arrow") == 1 then
|
||||||
|
local bdir = minetest.wallmounted_to_dir(node.param2)
|
||||||
|
-- Check the button orientation
|
||||||
|
if vector.equals(vector.add(dpos, bdir), self._stuckin) then
|
||||||
|
mesecon.push_button(dpos, node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif (def and def.liquidtype ~= "none") then
|
||||||
|
-- Slow down arrow in liquids
|
||||||
|
local v = def.liquid_viscosity
|
||||||
|
if not v then
|
||||||
|
v = 0
|
||||||
|
end
|
||||||
|
--local old_v = self._viscosity
|
||||||
|
self._viscosity = v
|
||||||
|
local vpenalty = math.max(0.1, 0.98 - 0.1 * v)
|
||||||
|
if math.abs(vel.x) > 0.001 then
|
||||||
|
vel.x = vel.x * vpenalty
|
||||||
|
end
|
||||||
|
if math.abs(vel.z) > 0.001 then
|
||||||
|
vel.z = vel.z * vpenalty
|
||||||
|
end
|
||||||
|
self.object:set_velocity(vel)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update yaw
|
||||||
|
if not self._stuck then
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
local yaw = minetest.dir_to_yaw(vel)+YAW_OFFSET
|
||||||
|
local pitch = dir_to_pitch(vel)
|
||||||
|
self.object:set_rotation({ x = 0, y = yaw, z = pitch })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update internal variable
|
||||||
|
self._lastpos={x=pos.x, y=pos.y, z=pos.z}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Force recheck of stuck arrows when punched.
|
||||||
|
-- Otherwise, punching has no effect.
|
||||||
|
function ARROW_ENTITY.on_punch(self)
|
||||||
|
if self._stuck then
|
||||||
|
self._fuserechecktimer = STUCK_RECHECK_TIME
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ARROW_ENTITY.get_staticdata(self)
|
||||||
|
local out = {
|
||||||
|
lastpos = self._lastpos,
|
||||||
|
startpos = self._startpos,
|
||||||
|
damage = self._damage,
|
||||||
|
is_critical = self._is_critical,
|
||||||
|
stuck = self._stuck,
|
||||||
|
stuckin = self._stuckin,
|
||||||
|
}
|
||||||
|
if self._stuck then
|
||||||
|
-- If _fuse is missing for some reason, assume the maximum
|
||||||
|
if not self._fuse then
|
||||||
|
self._fuse = ARROW_TIMEOUT
|
||||||
|
end
|
||||||
|
out.stuckstarttime = minetest.get_gametime() - self._fuse
|
||||||
|
end
|
||||||
|
if self._shooter and self._shooter:is_player() then
|
||||||
|
out.shootername = self._shooter:get_player_name()
|
||||||
|
end
|
||||||
|
return minetest.serialize(out)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ARROW_ENTITY.on_activate(self, staticdata, dtime_s)
|
||||||
|
self._time_in_air = 1.0
|
||||||
|
self._in_player = false
|
||||||
|
local data = minetest.deserialize(staticdata)
|
||||||
|
if data then
|
||||||
|
self._stuck = data.stuck
|
||||||
|
if data.stuck then
|
||||||
|
if data.stuckstarttime then
|
||||||
|
-- First, check if the stuck arrow is aleady past its life timer.
|
||||||
|
-- If yes, delete it.
|
||||||
|
self._fuse = minetest.get_gametime() - data.stuckstarttime
|
||||||
|
if self._fuse > ARROW_TIMEOUT then
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self._fuse = 2
|
||||||
|
-- Perform a stuck recheck on the next step.
|
||||||
|
self._fuserechecktimer = STUCK_RECHECK_TIME
|
||||||
|
|
||||||
|
self._stuckin = data.stuckin
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the remaining arrow state
|
||||||
|
self._lastpos = data.lastpos
|
||||||
|
self._startpos = data.startpos
|
||||||
|
self._damage = data.damage
|
||||||
|
self._is_critical = data.is_critical
|
||||||
|
if data.shootername then
|
||||||
|
local shooter = minetest.get_player_by_name(data.shootername)
|
||||||
|
if shooter and shooter:is_player() then
|
||||||
|
self._shooter = shooter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.object:set_armor_groups({ immortal = 1 })
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_bows:rocket_entity", ARROW_ENTITY)
|
||||||
|
|
||||||
|
if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then
|
||||||
|
minetest.register_craft({
|
||||||
|
output = "mcl_bows:rocket 1",
|
||||||
|
recipe = {
|
||||||
|
{"mcl_core:paper"},
|
||||||
|
{"mcl_fireworks:rocket_2"},
|
||||||
|
{"mcl_bows:arrow"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if minetest.get_modpath("doc_identifier") then
|
||||||
|
doc.sub.identifier.register_object("mcl_bows:rocket_entity", "craftitems", "mcl_bows:rocket")
|
||||||
|
end
|
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 630 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 622 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.6 KiB |
|
@ -1,4 +1,4 @@
|
||||||
name = mcl_brewing
|
name = mcl_brewing
|
||||||
author = bzoss
|
author = bzoss
|
||||||
depends = mcl_init, mcl_formspec, mcl_sounds, mcl_potions, mcl_mobitems
|
depends = mcl_init, mcl_formspec, mcl_sounds, mcl_potions, mcl_mobitems
|
||||||
optional_depends = mcl_core, doc, screwdriver
|
optional_depends = mcl_core, doc, screwdriver, mesecons_mvps
|
||||||
|
|
|
@ -9,13 +9,46 @@ Accept folowing params:
|
||||||
* string: name of the node to place
|
* string: name of the node to place
|
||||||
* function(pos): will returns name of the node to place with pos being the placement position
|
* function(pos): will returns name of the node to place with pos being the placement position
|
||||||
* source_take: table of liquid source node names to take
|
* source_take: table of liquid source node names to take
|
||||||
* itemname: itemstring of the new bucket item (or nil if liquid is not takeable)
|
* bucketname: itemstring of the new bucket item
|
||||||
* inventory_image: texture of the new bucket item (ignored if itemname == nil)
|
* inventory_image: texture of the new bucket item (ignored if itemname == nil)
|
||||||
* name: user-visible bucket description
|
* name: user-visible bucket description
|
||||||
* longdesc: long explanatory description (for help)
|
* longdesc: long explanatory description (for help)
|
||||||
* usagehelp: short usage explanation (for help)
|
* usagehelp: short usage explanation (for help)
|
||||||
* tt_help: very short tooltip help
|
* tt_help: very short tooltip help
|
||||||
* extra_check(pos, placer): (optional) function(pos) which can returns false to avoid placing the liquid. Placer is object/player who is placing the liquid, can be nil.
|
* extra_check(pos, placer): (optional) function(pos)
|
||||||
* groups: optional list of item groups
|
* groups: optional list of item groups
|
||||||
|
|
||||||
This function can be called from any mod (which depends on this one)
|
|
||||||
|
**Usage exemple:**
|
||||||
|
```lua
|
||||||
|
mcl_buckets.register_liquid({
|
||||||
|
bucketname = "dummy:bucket_dummy",
|
||||||
|
--source_place = "dummy:dummy_source",
|
||||||
|
source_place = function(pos)
|
||||||
|
if condition then
|
||||||
|
return "dummy:dummy_source"
|
||||||
|
else
|
||||||
|
return "dummy:dummy_source_nether"
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
source_take = {"dummy:dummy_source"},
|
||||||
|
inventory_image = "bucket_dummy.png",
|
||||||
|
name = S("Dummy liquid Bucket"),
|
||||||
|
longdesc = S("This bucket is filled with a dummy liquid."),
|
||||||
|
usagehelp = S("Place it to empty the bucket and create a dummy liquid source."),
|
||||||
|
tt_help = S("Places a dummy liquid source"),
|
||||||
|
extra_check = function(pos, placer)
|
||||||
|
--pos = pos where the liquid should be placed
|
||||||
|
--placer people who tried to place the bucket (can be nil)
|
||||||
|
|
||||||
|
--no liquid node will be placed
|
||||||
|
--the bucket will not be emptied
|
||||||
|
--return false, false
|
||||||
|
|
||||||
|
--liquid node will be placed
|
||||||
|
--the bucket will be emptied
|
||||||
|
return true, true
|
||||||
|
end,
|
||||||
|
groups = { dummy_group = 123 },
|
||||||
|
})
|
||||||
|
```
|
|
@ -1,9 +1,12 @@
|
||||||
Bucket mod.
|
# MineClone2 Bucket (`mcl_bucket`)
|
||||||
Originally taken from Minetest Game, adapted for MineClone 2.
|
Originally taken from Minetest Game, adapted for MineClone2.
|
||||||
|
|
||||||
|
This mod add buckets to the game, including an API to register your own (see `API.md`).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
License of source code:
|
|
||||||
-----------------------
|
|
||||||
Copyright (C) 2011-2012 Kahrl <kahrl@gmx.net>
|
Copyright (C) 2011-2012 Kahrl <kahrl@gmx.net>
|
||||||
|
|
||||||
Copyright (C) 2011-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
Copyright (C) 2011-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
|
@ -3,6 +3,7 @@ local modname = minetest.get_current_modname()
|
||||||
local S = minetest.get_translator(modname)
|
local S = minetest.get_translator(modname)
|
||||||
local modpath = minetest.get_modpath(modname)
|
local modpath = minetest.get_modpath(modname)
|
||||||
|
|
||||||
|
-- Compatibility with old bucket mod
|
||||||
minetest.register_alias("bucket:bucket_empty", "mcl_buckets:bucket_empty")
|
minetest.register_alias("bucket:bucket_empty", "mcl_buckets:bucket_empty")
|
||||||
minetest.register_alias("bucket:bucket_water", "mcl_buckets:bucket_water")
|
minetest.register_alias("bucket:bucket_water", "mcl_buckets:bucket_water")
|
||||||
minetest.register_alias("bucket:bucket_lava", "mcl_buckets:bucket_lava")
|
minetest.register_alias("bucket:bucket_lava", "mcl_buckets:bucket_lava")
|
||||||
|
@ -11,13 +12,24 @@ local mod_doc = minetest.get_modpath("doc")
|
||||||
local mod_mcl_core = minetest.get_modpath("mcl_core")
|
local mod_mcl_core = minetest.get_modpath("mcl_core")
|
||||||
--local mod_mclx_core = minetest.get_modpath("mclx_core")
|
--local mod_mclx_core = minetest.get_modpath("mclx_core")
|
||||||
|
|
||||||
|
-- Localize some functions for faster access
|
||||||
|
local vector = vector
|
||||||
|
local math = math
|
||||||
|
local string = string
|
||||||
|
|
||||||
|
local raycast = minetest.raycast
|
||||||
|
local get_node = minetest.get_node
|
||||||
|
local add_node = minetest.add_node
|
||||||
|
local add_item = minetest.add_item
|
||||||
|
|
||||||
|
|
||||||
if mod_mcl_core then
|
if mod_mcl_core then
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = "mcl_buckets:bucket_empty 1",
|
output = "mcl_buckets:bucket_empty 1",
|
||||||
recipe = {
|
recipe = {
|
||||||
{"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
|
{"mcl_core:iron_ingot", "", "mcl_core:iron_ingot"},
|
||||||
{"", "mcl_core:iron_ingot", ""},
|
{"", "mcl_core:iron_ingot", ""},
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,147 +37,218 @@ mcl_buckets = {}
|
||||||
mcl_buckets.liquids = {}
|
mcl_buckets.liquids = {}
|
||||||
|
|
||||||
-- Sound helper functions for placing and taking liquids
|
-- Sound helper functions for placing and taking liquids
|
||||||
local sound_place = function(itemname, pos)
|
local function sound_place(itemname, pos)
|
||||||
local def = minetest.registered_nodes[itemname]
|
local def = minetest.registered_nodes[itemname]
|
||||||
if def and def.sounds and def.sounds.place then
|
if def and def.sounds and def.sounds.place then
|
||||||
minetest.sound_play(def.sounds.place, {gain=1.0, pos = pos, pitch = 1 + math.random(-10, 10)*0.005}, true)
|
minetest.sound_play(def.sounds.place, {gain=1.0, pos = pos, pitch = 1 + math.random(-10, 10)*0.005}, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local sound_take = function(itemname, pos)
|
local function sound_take(itemname, pos)
|
||||||
local def = minetest.registered_nodes[itemname]
|
local def = minetest.registered_nodes[itemname]
|
||||||
if def and def.sounds and def.sounds.dug then
|
if def and def.sounds and def.sounds.dug then
|
||||||
minetest.sound_play(def.sounds.dug, {gain=1.0, pos = pos, pitch = 1 + math.random(-10, 10)*0.005}, true)
|
minetest.sound_play(def.sounds.dug, {gain=1.0, pos = pos, pitch = 1 + math.random(-10, 10)*0.005}, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local place_liquid = function(pos, itemstring)
|
local function place_liquid(pos, itemstring)
|
||||||
local fullness = minetest.registered_nodes[itemstring].liquid_range
|
local fullness = minetest.registered_nodes[itemstring].liquid_range
|
||||||
sound_place(itemstring, pos)
|
sound_place(itemstring, pos)
|
||||||
minetest.add_node(pos, {name=itemstring, param2=fullness})
|
minetest.add_node(pos, {name=itemstring, param2=fullness})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function give_bucket(new_bucket, itemstack, user)
|
||||||
|
local inv = user:get_inventory()
|
||||||
|
if minetest.is_creative_enabled(user:get_player_name()) then
|
||||||
|
--TODO: is a full bucket added if inv doesn't contain one?
|
||||||
|
return itemstack
|
||||||
|
else
|
||||||
|
if itemstack:get_count() == 1 then
|
||||||
|
return new_bucket
|
||||||
|
else
|
||||||
|
if inv:room_for_item("main", new_bucket) then
|
||||||
|
inv:add_item("main", new_bucket)
|
||||||
|
else
|
||||||
|
add_item(user:get_pos(), new_bucket)
|
||||||
|
end
|
||||||
|
itemstack:take_item()
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local pointable_sources = {}
|
||||||
|
|
||||||
|
local function bucket_raycast(user)
|
||||||
|
--local pos = user:get_pos()
|
||||||
|
local pos = user:get_pos()
|
||||||
|
--local pos = vector.add(user:get_pos(), user:get_bone_position("Head_Control"))
|
||||||
|
pos.y = pos.y + user:get_properties().eye_height
|
||||||
|
local look_dir = user:get_look_dir()
|
||||||
|
look_dir = vector.multiply(look_dir, 5)
|
||||||
|
local pos2 = vector.add(pos, look_dir)
|
||||||
|
|
||||||
|
local ray = raycast(pos, pos2, false, true)
|
||||||
|
if ray then
|
||||||
|
for pointed_thing in ray do
|
||||||
|
if pointed_thing and pointable_sources[get_node(pointed_thing.above).name] then
|
||||||
|
--minetest.chat_send_all("found!")
|
||||||
|
return {under=pointed_thing.under,above=pointed_thing.above}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_node_place(source_place, place_pos)
|
||||||
|
local node_place
|
||||||
|
if type(source_place) == "function" then
|
||||||
|
node_place = source_place(place_pos)
|
||||||
|
else
|
||||||
|
node_place = source_place
|
||||||
|
end
|
||||||
|
return node_place
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_extra_check(check, pos, user)
|
||||||
|
local result
|
||||||
|
local take_bucket
|
||||||
|
if check then
|
||||||
|
result, take_bucket = check(pos, user)
|
||||||
|
if result == nil then result = true end
|
||||||
|
if take_bucket == nil then take_bucket = true end
|
||||||
|
else
|
||||||
|
result = true
|
||||||
|
take_bucket = true
|
||||||
|
end
|
||||||
|
return result, take_bucket
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_bucket_drop(itemstack, user, take_bucket)
|
||||||
|
-- Handle bucket item and inventory stuff
|
||||||
|
if take_bucket and not minetest.is_creative_enabled(user:get_player_name()) then
|
||||||
|
-- Add empty bucket and put it into inventory, if possible.
|
||||||
|
-- Drop empty bucket otherwise.
|
||||||
|
local new_bucket = ItemStack("mcl_buckets:bucket_empty")
|
||||||
|
if itemstack:get_count() == 1 then
|
||||||
|
return new_bucket
|
||||||
|
else
|
||||||
|
local inv = user:get_inventory()
|
||||||
|
if inv:room_for_item("main", new_bucket) then
|
||||||
|
inv:add_item("main", new_bucket)
|
||||||
|
else
|
||||||
|
add_item(user:get_pos(), new_bucket)
|
||||||
|
end
|
||||||
|
itemstack:take_item()
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function mcl_buckets.register_liquid(def)
|
function mcl_buckets.register_liquid(def)
|
||||||
for i=1, #def.source_take do
|
for _,source in ipairs(def.source_take) do
|
||||||
mcl_buckets.liquids[def.source_take[i]] = {
|
mcl_buckets.liquids[source] = {
|
||||||
source_place = def.source_place,
|
source_place = def.source_place,
|
||||||
source_take = def.source_take[i],
|
source_take = source,
|
||||||
on_take = def.on_take,
|
on_take = def.on_take,
|
||||||
itemname = def.itemname,
|
bucketname = def.bucketname,
|
||||||
}
|
}
|
||||||
|
pointable_sources[source] = true
|
||||||
if type(def.source_place) == "string" then
|
if type(def.source_place) == "string" then
|
||||||
mcl_buckets.liquids[def.source_place] = mcl_buckets.liquids[def.source_take[i]]
|
mcl_buckets.liquids[def.source_place] = mcl_buckets.liquids[source]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if def.itemname then
|
if def.bucketname == nil or def.bucketname == "" then
|
||||||
minetest.register_craftitem(def.itemname, {
|
error(string.format("[mcl_bucket] Invalid itemname then registering [%s]!", def.name))
|
||||||
description = def.name,
|
end
|
||||||
_doc_items_longdesc = def.longdesc,
|
|
||||||
_doc_items_usagehelp = def.usagehelp,
|
|
||||||
_tt_help = def.tt_help,
|
|
||||||
inventory_image = def.inventory_image,
|
|
||||||
stack_max = 1,
|
|
||||||
groups = def.groups,
|
|
||||||
on_place = function(itemstack, user, pointed_thing)
|
|
||||||
-- Must be pointing to node
|
|
||||||
if pointed_thing.type ~= "node" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local node = minetest.get_node(pointed_thing.under)
|
minetest.register_craftitem(def.bucketname, {
|
||||||
local place_pos = pointed_thing.under
|
description = def.name,
|
||||||
local nn = node.name
|
_doc_items_longdesc = def.longdesc,
|
||||||
local nodedef = minetest.registered_nodes[nn]
|
_doc_items_usagehelp = def.usagehelp,
|
||||||
-- Call on_rightclick if the pointed node defines it
|
_tt_help = def.tt_help,
|
||||||
if user and not user:get_player_control().sneak then
|
inventory_image = def.inventory_image,
|
||||||
if nodedef and nodedef.on_rightclick then
|
stack_max = 1,
|
||||||
return nodedef.on_rightclick(place_pos, node, user, itemstack) or itemstack
|
groups = def.groups,
|
||||||
end
|
on_place = function(itemstack, user, pointed_thing)
|
||||||
end
|
-- Must be pointing to node
|
||||||
|
if pointed_thing.type ~= "node" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- Call on_rightclick if the pointed node defines it
|
||||||
|
local new_stack = mcl_util.call_on_rightclick(itemstack, user, pointed_thing)
|
||||||
|
if new_stack then
|
||||||
|
return new_stack
|
||||||
|
end
|
||||||
|
|
||||||
local node_place
|
local undernode = get_node(pointed_thing.under)
|
||||||
if type(def.source_place) == "function" then
|
local abovenode = get_node(pointed_thing.above)
|
||||||
node_place = def.source_place(place_pos)
|
local buildable1 = minetest.registered_nodes[undernode.name] and minetest.registered_nodes[undernode.name].buildable_to
|
||||||
else
|
local buildable2 = minetest.registered_nodes[abovenode.name] and minetest.registered_nodes[abovenode.name].buildable_to
|
||||||
node_place = def.source_place
|
if not buildable1 and not buildable2 then return itemstack end --if both nodes aren't buildable_to, skip
|
||||||
end
|
|
||||||
-- Check if pointing to a buildable node
|
|
||||||
--local item = itemstack:get_name()
|
|
||||||
|
|
||||||
if def.extra_check and def.extra_check(place_pos, user) == true and nodedef and nodedef.buildable_to then
|
if buildable1 then
|
||||||
-- buildable; replace the node
|
local result, take_bucket = get_extra_check(def.extra_check, pointed_thing.under, user)
|
||||||
|
if result then
|
||||||
|
local node_place = get_node_place(def.source_place, pointed_thing.under)
|
||||||
local pns = user:get_player_name()
|
local pns = user:get_player_name()
|
||||||
if minetest.is_protected(place_pos, pns) then
|
|
||||||
minetest.record_protection_violation(place_pos, pns)
|
-- Check protection
|
||||||
|
if minetest.is_protected(pointed_thing.under, pns) then
|
||||||
|
minetest.record_protection_violation(pointed_thing.under, pns)
|
||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
place_liquid(place_pos, node_place)
|
|
||||||
|
-- Place liquid
|
||||||
|
place_liquid(pointed_thing.under, node_place)
|
||||||
|
|
||||||
|
-- Update doc mod
|
||||||
if mod_doc and doc.entry_exists("nodes", node_place) then
|
if mod_doc and doc.entry_exists("nodes", node_place) then
|
||||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", node_place)
|
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", node_place)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
-- not buildable to; place the liquid above
|
|
||||||
-- check if the node above can be replaced
|
|
||||||
local abovenode = minetest.get_node(pointed_thing.above)
|
|
||||||
if minetest.registered_nodes[abovenode.name] and minetest.registered_nodes[abovenode.name].buildable_to then
|
|
||||||
local pn = user:get_player_name()
|
|
||||||
if minetest.is_protected(pointed_thing.above, pn) then
|
|
||||||
minetest.record_protection_violation(pointed_thing.above, pn)
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
place_liquid(pointed_thing.above, node_place)
|
|
||||||
if mod_doc and doc.entry_exists("nodes", node_place) then
|
|
||||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", node_place)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- do not remove the bucket with the liquid
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
return get_bucket_drop(itemstack, user, take_bucket)
|
||||||
|
elseif buildable2 then
|
||||||
|
local result, take_bucket = get_extra_check(def.extra_check, pointed_thing.above, user)
|
||||||
|
if result then
|
||||||
|
local node_place = get_node_place(def.source_place, pointed_thing.above)
|
||||||
|
local pns = user:get_player_name()
|
||||||
|
|
||||||
-- Handle bucket item and inventory stuff
|
-- Check protection
|
||||||
if not minetest.is_creative_enabled(user:get_player_name()) then
|
if minetest.is_protected(pointed_thing.above, pns) then
|
||||||
-- Add empty bucket and put it into inventory, if possible.
|
minetest.record_protection_violation(pointed_thing.above, pns)
|
||||||
-- Drop empty bucket otherwise.
|
|
||||||
local new_bucket = ItemStack("mcl_buckets:bucket_empty")
|
|
||||||
if itemstack:get_count() == 1 then
|
|
||||||
return new_bucket
|
|
||||||
else
|
|
||||||
local inv = user:get_inventory()
|
|
||||||
if inv:room_for_item("main", new_bucket) then
|
|
||||||
inv:add_item("main", new_bucket)
|
|
||||||
else
|
|
||||||
minetest.add_item(user:get_pos(), new_bucket)
|
|
||||||
end
|
|
||||||
itemstack:take_item()
|
|
||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
_on_dispense = function(stack, pos, droppos, dropnode, dropdir)
|
|
||||||
--local iname = stack:get_name()
|
|
||||||
local buildable = minetest.registered_nodes[dropnode.name].buildable_to or dropnode.name == "mcl_portals:portal"
|
|
||||||
|
|
||||||
--if def.extra_check and def.extra_check(droppos, nil) == false then
|
-- Place liquid
|
||||||
-- Fail placement of liquid
|
place_liquid(pointed_thing.above, node_place)
|
||||||
if def.extra_check and def.extra_check(droppos, nil) == true and buildable then
|
|
||||||
-- buildable; replace the node
|
-- Update doc mod
|
||||||
local node_place
|
if mod_doc and doc.entry_exists("nodes", node_place) then
|
||||||
if type(def.source_place) == "function" then
|
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", node_place)
|
||||||
node_place = def.source_place(droppos)
|
|
||||||
else
|
|
||||||
node_place = def.source_place
|
|
||||||
end
|
end
|
||||||
place_liquid(droppos, node_place)
|
|
||||||
stack:set_name("mcl_buckets:bucket_empty")
|
|
||||||
end
|
end
|
||||||
return stack
|
return get_bucket_drop(itemstack, user, take_bucket)
|
||||||
end,
|
else
|
||||||
})
|
return itemstack
|
||||||
end
|
end
|
||||||
|
end,
|
||||||
|
_on_dispense = function(stack, pos, droppos, dropnode, dropdir)
|
||||||
|
local buildable = minetest.registered_nodes[dropnode.name].buildable_to or dropnode.name == "mcl_portals:portal"
|
||||||
|
if not buildable then return stack end
|
||||||
|
local result, take_bucket = get_extra_check(def.extra_check, droppos, nil)
|
||||||
|
if result then -- Fail placement of liquid if result is false
|
||||||
|
place_liquid(droppos, get_node_place(def.source_place, droppos))
|
||||||
|
end
|
||||||
|
if take_bucket then
|
||||||
|
stack:set_name("mcl_buckets:bucket_empty")
|
||||||
|
end
|
||||||
|
return stack
|
||||||
|
end,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
||||||
|
@ -173,8 +256,7 @@ minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
||||||
_doc_items_longdesc = S("A bucket can be used to collect and release liquids."),
|
_doc_items_longdesc = S("A bucket can be used to collect and release liquids."),
|
||||||
_doc_items_usagehelp = S("Punch a liquid source to collect it. You can then use the filled bucket to place the liquid somewhere else."),
|
_doc_items_usagehelp = S("Punch a liquid source to collect it. You can then use the filled bucket to place the liquid somewhere else."),
|
||||||
_tt_help = S("Collects liquids"),
|
_tt_help = S("Collects liquids"),
|
||||||
|
--liquids_pointable = true,
|
||||||
liquids_pointable = true,
|
|
||||||
inventory_image = "bucket.png",
|
inventory_image = "bucket.png",
|
||||||
stack_max = 16,
|
stack_max = 16,
|
||||||
on_place = function(itemstack, user, pointed_thing)
|
on_place = function(itemstack, user, pointed_thing)
|
||||||
|
@ -184,74 +266,70 @@ minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Call on_rightclick if the pointed node defines it
|
-- Call on_rightclick if the pointed node defines it
|
||||||
|
local new_stack = mcl_util.call_on_rightclick(itemstack, user, pointed_thing)
|
||||||
|
if new_stack then
|
||||||
|
return new_stack
|
||||||
|
end
|
||||||
|
|
||||||
local node = minetest.get_node(pointed_thing.under)
|
local node = minetest.get_node(pointed_thing.under)
|
||||||
local nn = node.name
|
local nn = node.name
|
||||||
if user and not user:get_player_control().sneak then
|
|
||||||
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then
|
|
||||||
return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Can't steal liquids
|
|
||||||
if minetest.is_protected(pointed_thing.above, user:get_player_name()) then
|
|
||||||
minetest.record_protection_violation(pointed_thing.under, user:get_player_name())
|
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if pointing to a liquid source
|
|
||||||
local liquiddef = mcl_buckets.liquids[nn]
|
|
||||||
local new_bucket
|
local new_bucket
|
||||||
if liquiddef and liquiddef.itemname and (nn == liquiddef.source_take) then
|
local liquid_node = bucket_raycast(user)
|
||||||
|
if liquid_node then
|
||||||
-- Fill bucket, but not in Creative Mode
|
if minetest.is_protected(liquid_node.above, user:get_player_name()) then
|
||||||
if not minetest.is_creative_enabled(user:get_player_name()) then
|
minetest.record_protection_violation(liquid_node.above, user:get_player_name())
|
||||||
new_bucket = ItemStack({name = liquiddef.itemname})
|
|
||||||
if liquiddef.on_take then
|
|
||||||
liquiddef.on_take(user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
local liquid_name = get_node(liquid_node.above).name
|
||||||
|
if liquid_name then
|
||||||
|
local liquid_def = mcl_buckets.liquids[liquid_name]
|
||||||
|
if liquid_def then
|
||||||
|
--minetest.chat_send_all("test")
|
||||||
|
-- Fill bucket, but not in Creative Mode
|
||||||
|
-- FIXME: remove this line
|
||||||
|
--if not minetest.is_creative_enabled(user:get_player_name()) then
|
||||||
|
if not false then
|
||||||
|
new_bucket = ItemStack({name = liquid_def.bucketname})
|
||||||
|
if liquid_def.on_take then
|
||||||
|
liquid_def.on_take(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
add_node(liquid_node.above, {name="air"})
|
||||||
|
sound_take(nn, liquid_node.above)
|
||||||
|
|
||||||
minetest.add_node(pointed_thing.under, {name="air"})
|
if mod_doc and doc.entry_exists("nodes", liquid_name) then
|
||||||
sound_take(nn, pointed_thing.under)
|
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", liquid_name)
|
||||||
|
end
|
||||||
if mod_doc and doc.entry_exists("nodes", nn) then
|
if new_bucket then
|
||||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", nn)
|
return give_bucket(new_bucket, itemstack, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif nn == "mcl_cauldrons:cauldron_3" then
|
|
||||||
-- Take water out of full cauldron
|
|
||||||
minetest.set_node(pointed_thing.under, {name="mcl_cauldrons:cauldron"})
|
|
||||||
if not minetest.is_creative_enabled(user:get_player_name()) then
|
|
||||||
new_bucket = ItemStack("mcl_buckets:bucket_water")
|
|
||||||
end
|
|
||||||
sound_take("mcl_core:water_source", pointed_thing.under)
|
|
||||||
elseif nn == "mcl_cauldrons:cauldron_3r" then
|
|
||||||
-- Take river water out of full cauldron
|
|
||||||
minetest.set_node(pointed_thing.under, {name="mcl_cauldrons:cauldron"})
|
|
||||||
if not minetest.is_creative_enabled(user:get_player_name()) then
|
|
||||||
new_bucket = ItemStack("mcl_buckets:bucket_river_water")
|
|
||||||
end
|
|
||||||
sound_take("mclx_core:river_water_source", pointed_thing.under)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add liquid bucket and put it into inventory, if possible.
|
|
||||||
-- Drop new bucket otherwise.
|
|
||||||
if new_bucket then
|
|
||||||
if itemstack:get_count() == 1 then
|
|
||||||
return new_bucket
|
|
||||||
else
|
|
||||||
local inv = user:get_inventory()
|
|
||||||
if inv:room_for_item("main", new_bucket) then
|
|
||||||
inv:add_item("main", new_bucket)
|
|
||||||
else
|
else
|
||||||
minetest.add_item(user:get_pos(), new_bucket)
|
minetest.log("error", string.format("[mcl_buckets] Node [%s] has invalid group [_mcl_bucket_pointable]!", liquid_name))
|
||||||
end
|
end
|
||||||
if not minetest.is_creative_enabled(user:get_player_name()) then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
return itemstack
|
|
||||||
end
|
end
|
||||||
end
|
return itemstack
|
||||||
|
else
|
||||||
|
-- FIXME: replace this ugly code by cauldrons API
|
||||||
|
if nn == "mcl_cauldrons:cauldron_3" then
|
||||||
|
-- Take water out of full cauldron
|
||||||
|
minetest.set_node(pointed_thing.under, {name="mcl_cauldrons:cauldron"})
|
||||||
|
if not minetest.is_creative_enabled(user:get_player_name()) then
|
||||||
|
new_bucket = ItemStack("mcl_buckets:bucket_water")
|
||||||
|
end
|
||||||
|
sound_take("mcl_core:water_source", pointed_thing.under)
|
||||||
|
elseif nn == "mcl_cauldrons:cauldron_3r" then
|
||||||
|
-- Take river water out of full cauldron
|
||||||
|
minetest.set_node(pointed_thing.under, {name="mcl_cauldrons:cauldron"})
|
||||||
|
if not minetest.is_creative_enabled(user:get_player_name()) then
|
||||||
|
new_bucket = ItemStack("mcl_buckets:bucket_river_water")
|
||||||
|
end
|
||||||
|
sound_take("mclx_core:river_water_source", pointed_thing.under)
|
||||||
|
end
|
||||||
|
if new_bucket then
|
||||||
|
return give_bucket(new_bucket, itemstack, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
end,
|
end,
|
||||||
_on_dispense = function(stack, pos, droppos, dropnode, dropdir)
|
_on_dispense = function(stack, pos, droppos, dropnode, dropdir)
|
||||||
-- Fill empty bucket with liquid or drop bucket if no liquid
|
-- Fill empty bucket with liquid or drop bucket if no liquid
|
||||||
|
@ -259,9 +337,9 @@ minetest.register_craftitem("mcl_buckets:bucket_empty", {
|
||||||
|
|
||||||
local liquiddef = mcl_buckets.liquids[dropnode.name]
|
local liquiddef = mcl_buckets.liquids[dropnode.name]
|
||||||
local new_bucket
|
local new_bucket
|
||||||
if liquiddef and liquiddef.itemname and (dropnode.name == liquiddef.source_take) then
|
if liquiddef and liquiddef.bucketname and (dropnode.name == liquiddef.source_take) then
|
||||||
-- Fill bucket
|
-- Fill bucket
|
||||||
new_bucket = ItemStack({name = liquiddef.itemname})
|
new_bucket = ItemStack({name = liquiddef.bucketname})
|
||||||
sound_take(dropnode.name, droppos)
|
sound_take(dropnode.name, droppos)
|
||||||
collect_liquid = true
|
collect_liquid = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name = mcl_buckets
|
name = mcl_buckets
|
||||||
author = Kahrl
|
author = Kahrl
|
||||||
description =
|
description =
|
||||||
depends = mcl_worlds
|
depends = mcl_worlds, mcl_util
|
||||||
optional_depends = mcl_core, mclx_core, doc
|
optional_depends = mcl_core, mclx_core, doc
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ local mod_mcl_core = minetest.get_modpath("mcl_core")
|
||||||
local mod_mclx_core = minetest.get_modpath("mclx_core")
|
local mod_mclx_core = minetest.get_modpath("mclx_core")
|
||||||
local has_awards = minetest.get_modpath("awards")
|
local has_awards = minetest.get_modpath("awards")
|
||||||
|
|
||||||
local sound_place = function(itemname, pos)
|
local function sound_place(itemname, pos)
|
||||||
local def = minetest.registered_nodes[itemname]
|
local def = minetest.registered_nodes[itemname]
|
||||||
if def and def.sounds and def.sounds.place then
|
if def and def.sounds and def.sounds.place then
|
||||||
minetest.sound_play(def.sounds.place, {gain=1.0, pos = pos, pitch = 1 + math.random(-10, 10)*0.005}, true)
|
minetest.sound_play(def.sounds.place, {gain=1.0, pos = pos, pitch = 1 + math.random(-10, 10)*0.005}, true)
|
||||||
|
@ -34,7 +34,7 @@ if mod_mcl_core then
|
||||||
awards.unlock(user:get_player_name(), "mcl:hotStuff")
|
awards.unlock(user:get_player_name(), "mcl:hotStuff")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
itemname = "mcl_buckets:bucket_lava",
|
bucketname = "mcl_buckets:bucket_lava",
|
||||||
inventory_image = "bucket_lava.png",
|
inventory_image = "bucket_lava.png",
|
||||||
name = S("Lava Bucket"),
|
name = S("Lava Bucket"),
|
||||||
longdesc = S("A bucket can be used to collect and release liquids. This one is filled with hot lava, safely contained inside. Use with caution."),
|
longdesc = S("A bucket can be used to collect and release liquids. This one is filled with hot lava, safely contained inside. Use with caution."),
|
||||||
|
@ -46,22 +46,13 @@ if mod_mcl_core then
|
||||||
mcl_buckets.register_liquid({
|
mcl_buckets.register_liquid({
|
||||||
source_place = "mcl_core:water_source",
|
source_place = "mcl_core:water_source",
|
||||||
source_take = {"mcl_core:water_source"},
|
source_take = {"mcl_core:water_source"},
|
||||||
itemname = "mcl_buckets:bucket_water",
|
bucketname = "mcl_buckets:bucket_water",
|
||||||
inventory_image = "bucket_water.png",
|
inventory_image = "bucket_water.png",
|
||||||
name = S("Water Bucket"),
|
name = S("Water Bucket"),
|
||||||
longdesc = S("A bucket can be used to collect and release liquids. This one is filled with water."),
|
longdesc = S("A bucket can be used to collect and release liquids. This one is filled with water."),
|
||||||
usagehelp = S("Place it to empty the bucket and create a water source."),
|
usagehelp = S("Place it to empty the bucket and create a water source."),
|
||||||
tt_help = S("Places a water source"),
|
tt_help = S("Places a water source"),
|
||||||
extra_check = function(pos, placer)
|
extra_check = function(pos, placer)
|
||||||
-- Check protection
|
|
||||||
local placer_name = ""
|
|
||||||
if placer then
|
|
||||||
placer_name = placer:get_player_name()
|
|
||||||
end
|
|
||||||
if placer and minetest.is_protected(pos, placer_name) then
|
|
||||||
minetest.record_protection_violation(pos, placer_name)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local nn = minetest.get_node(pos).name
|
local nn = minetest.get_node(pos).name
|
||||||
-- Pour water into cauldron
|
-- Pour water into cauldron
|
||||||
if minetest.get_item_group(nn, "cauldron") ~= 0 then
|
if minetest.get_item_group(nn, "cauldron") ~= 0 then
|
||||||
|
@ -70,13 +61,13 @@ if mod_mcl_core then
|
||||||
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3"})
|
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3"})
|
||||||
end
|
end
|
||||||
sound_place("mcl_core:water_source", pos)
|
sound_place("mcl_core:water_source", pos)
|
||||||
return false
|
return false, true
|
||||||
-- Evaporate water if used in Nether (except on cauldron)
|
-- Evaporate water if used in Nether (except on cauldron)
|
||||||
else
|
else
|
||||||
local dim = mcl_worlds.pos_to_dimension(pos)
|
local dim = mcl_worlds.pos_to_dimension(pos)
|
||||||
if dim == "nether" then
|
if dim == "nether" then
|
||||||
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
|
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
|
||||||
return false
|
return false, true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -89,22 +80,13 @@ if mod_mclx_core then
|
||||||
mcl_buckets.register_liquid({
|
mcl_buckets.register_liquid({
|
||||||
source_place = "mclx_core:river_water_source",
|
source_place = "mclx_core:river_water_source",
|
||||||
source_take = {"mclx_core:river_water_source"},
|
source_take = {"mclx_core:river_water_source"},
|
||||||
itemname = "mcl_buckets:bucket_river_water",
|
bucketname = "mcl_buckets:bucket_river_water",
|
||||||
inventory_image = "bucket_river_water.png",
|
inventory_image = "bucket_river_water.png",
|
||||||
name = S("River Water Bucket"),
|
name = S("River Water Bucket"),
|
||||||
longdesc = S("A bucket can be used to collect and release liquids. This one is filled with river water."),
|
longdesc = S("A bucket can be used to collect and release liquids. This one is filled with river water."),
|
||||||
usagehelp = S("Place it to empty the bucket and create a river water source."),
|
usagehelp = S("Place it to empty the bucket and create a river water source."),
|
||||||
tt_help = S("Places a river water source"),
|
tt_help = S("Places a river water source"),
|
||||||
extra_check = function(pos, placer)
|
extra_check = function(pos, placer)
|
||||||
-- Check protection
|
|
||||||
local placer_name = ""
|
|
||||||
if placer then
|
|
||||||
placer_name = placer:get_player_name()
|
|
||||||
end
|
|
||||||
if placer and minetest.is_protected(pos, placer_name) then
|
|
||||||
minetest.record_protection_violation(pos, placer_name)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local nn = minetest.get_node(pos).name
|
local nn = minetest.get_node(pos).name
|
||||||
-- Pour into cauldron
|
-- Pour into cauldron
|
||||||
if minetest.get_item_group(nn, "cauldron") ~= 0 then
|
if minetest.get_item_group(nn, "cauldron") ~= 0 then
|
||||||
|
@ -113,13 +95,13 @@ if mod_mclx_core then
|
||||||
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3r"})
|
minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3r"})
|
||||||
end
|
end
|
||||||
sound_place("mcl_core:water_source", pos)
|
sound_place("mcl_core:water_source", pos)
|
||||||
return false
|
return false, true
|
||||||
else
|
else
|
||||||
-- Evaporate water if used in Nether (except on cauldron)
|
-- Evaporate water if used in Nether (except on cauldron)
|
||||||
local dim = mcl_worlds.pos_to_dimension(pos)
|
local dim = mcl_worlds.pos_to_dimension(pos)
|
||||||
if dim == "nether" then
|
if dim == "nether" then
|
||||||
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
|
minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true)
|
||||||
return false
|
return false, true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -1133,7 +1133,7 @@ for color, desc in pairs(boxtypes) do
|
||||||
if mod_doc then
|
if mod_doc then
|
||||||
if is_canonical then
|
if is_canonical then
|
||||||
longdesc = S("A shulker box is a portable container which provides 27 inventory slots for any item except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes as well as their contents can be taken as a single item. Shulker boxes come in many different colors.")
|
longdesc = S("A shulker box is a portable container which provides 27 inventory slots for any item except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes as well as their contents can be taken as a single item. Shulker boxes come in many different colors.")
|
||||||
usagehelp = S("To access the inventory of a shulker box, place and right-click it. To take a shulker box and its contents with you, just break and collect it, the items will not fall out.")
|
usagehelp = S("To access the inventory of a shulker box, place and right-click it. To take a shulker box and its contents with you, just break and collect it, the items will not fall out. Place the shulker box again to be able to retrieve its contents.")
|
||||||
entry_name = S("Shulker Box")
|
entry_name = S("Shulker Box")
|
||||||
else
|
else
|
||||||
create_entry = false
|
create_entry = false
|
||||||
|
|
|
@ -24,7 +24,7 @@ Red Shulker Box=
|
||||||
Grey Shulker Box=
|
Grey Shulker Box=
|
||||||
Black Shulker Box=
|
Black Shulker Box=
|
||||||
A shulker box is a portable container which provides 27 inventory slots for any item except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes as well as their contents can be taken as a single item. Shulker boxes come in many different colors.=
|
A shulker box is a portable container which provides 27 inventory slots for any item except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes as well as their contents can be taken as a single item. Shulker boxes come in many different colors.=
|
||||||
To access the inventory of a shulker box, place and right-click it. To take a shulker box and its contents with you, just break and collect it, the items will not fall out.=
|
To access the inventory of a shulker box, place and right-click it. To take a shulker box and its contents with you, just break and collect it, the items will not fall out. Place the shulker box again to be able to retrieve its contents.=
|
||||||
Shulker Box=
|
Shulker Box=
|
||||||
Large Chest=
|
Large Chest=
|
||||||
Inventory=
|
Inventory=
|
||||||
|
|
|
@ -93,7 +93,7 @@ minetest.register_craftitem("mcl_core:gold_ingot", {
|
||||||
|
|
||||||
minetest.register_craftitem("mcl_core:emerald", {
|
minetest.register_craftitem("mcl_core:emerald", {
|
||||||
description = S("Emerald"),
|
description = S("Emerald"),
|
||||||
_doc_items_longdesc = S("Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting."),
|
_doc_items_longdesc = S("Emeralds are used in villager trades as currency."),
|
||||||
inventory_image = "mcl_core_emerald.png",
|
inventory_image = "mcl_core_emerald.png",
|
||||||
stack_max = 64,
|
stack_max = 64,
|
||||||
groups = { craftitem=1 },
|
groups = { craftitem=1 },
|
||||||
|
|
|
@ -184,6 +184,7 @@ minetest.register_abm({
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
-- Cactus mechanisms
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Cactus growth",
|
label = "Cactus growth",
|
||||||
nodenames = {"mcl_core:cactus"},
|
nodenames = {"mcl_core:cactus"},
|
||||||
|
@ -195,18 +196,31 @@ minetest.register_abm({
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Make cactus destroy items
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Cactus destroy items",
|
label = "Cactus mechanisms",
|
||||||
nodenames = {"mcl_core:cactus"},
|
nodenames = {"mcl_core:cactus"},
|
||||||
interval = 1,
|
interval = 1,
|
||||||
chance = 1,
|
chance = 1,
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, 0.9)) do
|
for _, object in pairs(minetest.get_objects_inside_radius(pos, 0.9)) do
|
||||||
if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then
|
local entity = object:get_luaentity()
|
||||||
|
if entity and entity.name == "__builtin:item" then
|
||||||
object:remove()
|
object:remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local posses = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }
|
||||||
|
for _, p in pairs(posses) do
|
||||||
|
if minetest.registered_nodes[minetest.get_node(vector.new(pos.x + p[1], pos.y, pos.z + p[2])).name].walkable then
|
||||||
|
local posy = pos.y
|
||||||
|
while minetest.get_node(vector.new(pos.x, posy, pos.z)).name == "mcl_core:cactus" do
|
||||||
|
local pos = vector.new(pos.x, posy, pos.z)
|
||||||
|
minetest.remove_node(pos)
|
||||||
|
minetest.add_item(vector.offset(pos, math.random(-0.5, 0.5), 0, math.random(-0.5, 0.5)), "mcl_core:cactus")
|
||||||
|
posy = posy + 1
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ Dirt acts as a soil for a few plants. When in light, this block may grow a grass
|
||||||
Emerald=Smaragd
|
Emerald=Smaragd
|
||||||
Emerald Ore=Smaragderz
|
Emerald Ore=Smaragderz
|
||||||
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Smaragderz ist das Erz von Smaragden. Es ist sehr selten und kann nur einzeln gefunden werden, nicht in Ansammlungen.
|
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Smaragderz ist das Erz von Smaragden. Es ist sehr selten und kann nur einzeln gefunden werden, nicht in Ansammlungen.
|
||||||
Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting.=Smaragde sind nicht besonders nützlich, aber man kann sie in der Fertigung durch Goldbarren eintauschen.
|
Emeralds are used in villager trades as currency.=
|
||||||
Flint=Feuerstein
|
Flint=Feuerstein
|
||||||
Flint is a raw material.=Feuerstein ist ein Rohstoff.
|
Flint is a raw material.=Feuerstein ist ein Rohstoff.
|
||||||
Flowing Lava=Fließende Lava
|
Flowing Lava=Fließende Lava
|
||||||
|
|
|
@ -95,7 +95,7 @@ Dirt acts as a soil for a few plants. When in light, this block may grow a grass
|
||||||
Emerald=Esmeralda
|
Emerald=Esmeralda
|
||||||
Emerald Ore=Mena de esmeralda
|
Emerald Ore=Mena de esmeralda
|
||||||
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=El mineral esmeralda es el mineral de las esmeraldas. Es muy raro y se puede encontrar solo, no en grupos.
|
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=El mineral esmeralda es el mineral de las esmeraldas. Es muy raro y se puede encontrar solo, no en grupos.
|
||||||
Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting.=Las esmeraldas no son muy útiles por sí mismas, pero pueden cambiarse por lingotes de oro haciendo artesanías.
|
Emeralds are used in villager trades as currency.=
|
||||||
Flint=Pedernal
|
Flint=Pedernal
|
||||||
Flint is a raw material.=El pedernal es una materia prima.
|
Flint is a raw material.=El pedernal es una materia prima.
|
||||||
Flowing Lava=Lava que fluye
|
Flowing Lava=Lava que fluye
|
||||||
|
|
|
@ -95,7 +95,7 @@ Dirt acts as a soil for a few plants. When in light, this block may grow a grass
|
||||||
Emerald=Emeraude
|
Emerald=Emeraude
|
||||||
Emerald Ore=Minerai d'Emeraude
|
Emerald Ore=Minerai d'Emeraude
|
||||||
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Le minerai d'émeraude produit des émeraudes. Il est très rare et peut être trouvé seul, pas en filons.
|
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Le minerai d'émeraude produit des émeraudes. Il est très rare et peut être trouvé seul, pas en filons.
|
||||||
Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting.=Les émeraudes ne sont pas très utiles seules, mais elles peuvent être échangées contre des lingots d'or.
|
Emeralds are used in villager trades as currency.=Les émeraudes sont utilisées pour faire des échanges avec les villageois.
|
||||||
Flint=Silex
|
Flint=Silex
|
||||||
Flint is a raw material.=Le silex est une matière première.
|
Flint is a raw material.=Le silex est une matière première.
|
||||||
Flowing Lava=Lave qui coule
|
Flowing Lava=Lave qui coule
|
||||||
|
|
|
@ -95,7 +95,7 @@ Dirt acts as a soil for a few plants. When in light, this block may grow a grass
|
||||||
Emerald=Szmaragd
|
Emerald=Szmaragd
|
||||||
Emerald Ore=Ruda szmaragdu
|
Emerald Ore=Ruda szmaragdu
|
||||||
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Ruda szmaragdu jest bardzo rzadka i występuje samotnie, nie w grupach.
|
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Ruda szmaragdu jest bardzo rzadka i występuje samotnie, nie w grupach.
|
||||||
Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting.=Szmaragdy nie są zbyt użyteczne same w sobie, ale można z nich wytworzyć sztabki złota.
|
Emeralds are used in villager trades as currency.=
|
||||||
Flint=Krzemień
|
Flint=Krzemień
|
||||||
Flint is a raw material.=Krzemień jest surowym materiałem.
|
Flint is a raw material.=Krzemień jest surowym materiałem.
|
||||||
Flowing Lava=Płynąca lawa
|
Flowing Lava=Płynąca lawa
|
||||||
|
|
|
@ -95,7 +95,7 @@ Dirt acts as a soil for a few plants. When in light, this block may grow a grass
|
||||||
Emerald=Изумруд
|
Emerald=Изумруд
|
||||||
Emerald Ore=Изумрудная руда
|
Emerald Ore=Изумрудная руда
|
||||||
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Изумрудная руда встречается очень редко и всегда по одному блоку.
|
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=Изумрудная руда встречается очень редко и всегда по одному блоку.
|
||||||
Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting.=Изумруды не очень полезны сами по себе, но их можно обменять на золотые слитки.
|
Emeralds are used in villager trades as currency.=
|
||||||
Flint=Кремень
|
Flint=Кремень
|
||||||
Flint is a raw material.=Кремень это необработанный материал.
|
Flint is a raw material.=Кремень это необработанный материал.
|
||||||
Flowing Lava=Текущая лава
|
Flowing Lava=Текущая лава
|
||||||
|
|
|
@ -95,7 +95,7 @@ Dirt acts as a soil for a few plants. When in light, this block may grow a grass
|
||||||
Emerald=
|
Emerald=
|
||||||
Emerald Ore=
|
Emerald Ore=
|
||||||
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=
|
Emerald ore is the ore of emeralds. It is very rare and can be found alone, not in clusters.=
|
||||||
Emeralds are not very useful on their own, but they can exchanged for gold ingots by crafting.=
|
Emeralds are used in villager trades as currency.=
|
||||||
Flint=
|
Flint=
|
||||||
Flint is a raw material.=
|
Flint is a raw material.=
|
||||||
Flowing Lava=
|
Flowing Lava=
|
||||||
|
|
|
@ -379,8 +379,8 @@ mcl_enchanting.enchantments.mending = {
|
||||||
inv_tool_tab = true,
|
inv_tool_tab = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- requires missing MineClone2 feature
|
|
||||||
--[[mcl_enchanting.enchantments.multishot = {
|
mcl_enchanting.enchantments.multishot = {
|
||||||
name = S("Multishot"),
|
name = S("Multishot"),
|
||||||
max_level = 1,
|
max_level = 1,
|
||||||
primary = {crossbow = true},
|
primary = {crossbow = true},
|
||||||
|
@ -396,10 +396,10 @@ mcl_enchanting.enchantments.mending = {
|
||||||
power_range_table = {{20, 50}},
|
power_range_table = {{20, 50}},
|
||||||
inv_combat_tab = true,
|
inv_combat_tab = true,
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}]]--
|
}
|
||||||
|
|
||||||
-- requires missing MineClone2 feature
|
-- requires missing MineClone2 feature
|
||||||
--[[mcl_enchanting.enchantments.piercing = {
|
mcl_enchanting.enchantments.piercing = {
|
||||||
name = S("Piercing"),
|
name = S("Piercing"),
|
||||||
max_level = 4,
|
max_level = 4,
|
||||||
primary = {crossbow = true},
|
primary = {crossbow = true},
|
||||||
|
@ -415,7 +415,7 @@ mcl_enchanting.enchantments.mending = {
|
||||||
power_range_table = {{1, 50}, {11, 50}, {21, 50}, {31, 50}},
|
power_range_table = {{1, 50}, {11, 50}, {21, 50}, {31, 50}},
|
||||||
inv_combat_tab = true,
|
inv_combat_tab = true,
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}]]--
|
}
|
||||||
|
|
||||||
-- implemented in mcl_bows
|
-- implemented in mcl_bows
|
||||||
mcl_enchanting.enchantments.power = {
|
mcl_enchanting.enchantments.power = {
|
||||||
|
@ -456,7 +456,7 @@ mcl_enchanting.enchantments.punch = {
|
||||||
}
|
}
|
||||||
|
|
||||||
-- requires missing MineClone2 feature
|
-- requires missing MineClone2 feature
|
||||||
--[[mcl_enchanting.enchantments.quick_charge = {
|
mcl_enchanting.enchantments.quick_charge = {
|
||||||
name = S("Quick Charge"),
|
name = S("Quick Charge"),
|
||||||
max_level = 3,
|
max_level = 3,
|
||||||
primary = {crossbow = true},
|
primary = {crossbow = true},
|
||||||
|
@ -472,7 +472,7 @@ mcl_enchanting.enchantments.punch = {
|
||||||
power_range_table = {{12, 50}, {32, 50}, {52, 50}},
|
power_range_table = {{12, 50}, {32, 50}, {52, 50}},
|
||||||
inv_combat_tab = true,
|
inv_combat_tab = true,
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}]]--
|
}
|
||||||
|
|
||||||
-- unimplemented
|
-- unimplemented
|
||||||
--[[mcl_enchanting.enchantments.respiration = {
|
--[[mcl_enchanting.enchantments.respiration = {
|
||||||
|
|
|
@ -270,8 +270,14 @@ function mcl_enchanting.initialize()
|
||||||
new_def.groups.not_in_creative_inventory = 1
|
new_def.groups.not_in_creative_inventory = 1
|
||||||
new_def.groups.not_in_craft_guide = 1
|
new_def.groups.not_in_craft_guide = 1
|
||||||
new_def.groups.enchanted = 1
|
new_def.groups.enchanted = 1
|
||||||
new_def._mcl_armor_texture = new_def._mcl_armor_texture and new_def._mcl_armor_texture .. mcl_enchanting.overlay
|
|
||||||
new_def._mcl_armor_preview = new_def._mcl_armor_preview and new_def._mcl_armor_preview .. mcl_enchanting.overlay
|
if new_def._mcl_armor_texture and not type(new_def._mcl_armor_texture) == "function" then
|
||||||
|
new_def._mcl_armor_texture = new_def._mcl_armor_texture .. mcl_enchanting.overlay
|
||||||
|
end
|
||||||
|
if new_def._mcl_armor_preview and not type(new_def._mcl_armor_preview) == "function" then
|
||||||
|
new_def._mcl_armor_preview = new_def._mcl_armor_preview .. mcl_enchanting.overlay
|
||||||
|
end
|
||||||
|
|
||||||
new_def._mcl_enchanting_enchanted_tool = new_name
|
new_def._mcl_enchanting_enchanted_tool = new_name
|
||||||
new_def.after_use = get_after_use_callback(itemdef)
|
new_def.after_use = get_after_use_callback(itemdef)
|
||||||
local register_list = register_item_list
|
local register_list = register_item_list
|
||||||
|
|
|
@ -453,7 +453,7 @@ function mcl_end.grow_chorus_plant_step(pos, node, pr)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- ABM ---
|
--- ABM ---
|
||||||
local seed = minetest.get_mapgen_params().seed
|
local seed = minetest.get_mapgen_setting("seed")
|
||||||
local pr = PseudoRandom(seed)
|
local pr = PseudoRandom(seed)
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Chorus plant growth",
|
label = "Chorus plant growth",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_fireworks
|
||||||
|
Firework Rocket=
|
||||||
|
Flight Duration:=
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_fireworks
|
||||||
|
Firework Rocket=Fusée
|
||||||
|
Flight Duration:=Durée de vol :
|
|
@ -0,0 +1,3 @@
|
||||||
|
# textdomain: mcl_fireworks
|
||||||
|
Firework Rocket=
|
||||||
|
Flight Duration:=
|
|
@ -1,5 +1,8 @@
|
||||||
# textdomain: mcl_maps
|
# textdomain: mcl_maps
|
||||||
Empty Map=Carte Vierge
|
Empty Map=Carte Vierge
|
||||||
Empty maps are not useful as maps, but they can be stacked and turned to maps which can be used.=Les cartes vierges ne sont pas utiles en tant que cartes, mais elles peuvent être empilées et transformées en cartes utilisables.
|
Empty maps are not useful as maps, but they can be stacked and turned to maps which can be used.=Les cartes vierges ne sont pas utiles en tant que cartes, mais elles peuvent être empilées et transformées en cartes utilisables.
|
||||||
Rightclick to start using the map (which can't be stacked anymore).=Clic droit pour commencer à utiliser la carte (qui ne peut plus être empilée).
|
Rightclick to create a filled map (which can't be stacked anymore).=Clic droit pour créer une carte remplie (qui ne peut plus être empilée).
|
||||||
Map=Carte
|
Map=Carte
|
||||||
|
Shows a map image.=Affiche une carte.
|
||||||
|
When created, the map saves the nearby area as an image that can be viewed any time by holding the map.=Lors de sa création, la carte sauvegarde le terrain proche sous forme d'image qui peut être consultée n'importe quand en tenant la carte dans la main.
|
||||||
|
Hold the map in your hand. This will display a map on your screen.=Tenez la carte dans votre main. Cela affichera la carte à l'écran.
|
||||||
|
|
|
@ -230,7 +230,7 @@ local function spawn_mobs(pos, elapsed)
|
||||||
|
|
||||||
-- spawn up to 4 mobs in random air blocks
|
-- spawn up to 4 mobs in random air blocks
|
||||||
if air then
|
if air then
|
||||||
local max = 4
|
local max = 200
|
||||||
if spawn_count_overrides[mob] then
|
if spawn_count_overrides[mob] then
|
||||||
max = spawn_count_overrides[mob]
|
max = spawn_count_overrides[mob]
|
||||||
end
|
end
|
||||||
|
@ -387,4 +387,3 @@ minetest.register_lbm({
|
||||||
respawn_doll(pos)
|
respawn_doll(pos)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name = mcl_portals
|
name = mcl_portals
|
||||||
description = Adds buildable portals to the Nether and End dimensions.
|
description = Adds buildable portals to the Nether and End dimensions.
|
||||||
depends = mcl_nether, mcl_end, mcl_particles, mcl_spawn
|
depends = mcl_nether, mcl_end, mcl_particles, mcl_spawn, mcl_credits
|
||||||
optional_depends = awards, doc
|
optional_depends = awards, doc
|
||||||
|
|
|
@ -4,6 +4,8 @@ local table = table
|
||||||
local vector = vector
|
local vector = vector
|
||||||
local math = math
|
local math = math
|
||||||
|
|
||||||
|
local has_doc = minetest.get_modpath("doc")
|
||||||
|
|
||||||
-- Parameters
|
-- Parameters
|
||||||
--local SPAWN_MIN = mcl_vars.mg_end_min+70
|
--local SPAWN_MIN = mcl_vars.mg_end_min+70
|
||||||
--local SPAWN_MAX = mcl_vars.mg_end_min+98
|
--local SPAWN_MAX = mcl_vars.mg_end_min+98
|
||||||
|
@ -339,7 +341,7 @@ minetest.register_node("mcl_portals:end_portal_frame_eye", {
|
||||||
_mcl_hardness = -1,
|
_mcl_hardness = -1,
|
||||||
})
|
})
|
||||||
|
|
||||||
if minetest.get_modpath("doc") then
|
if has_doc then
|
||||||
doc.add_entry_alias("nodes", "mcl_portals:end_portal_frame", "nodes", "mcl_portals:end_portal_frame_eye")
|
doc.add_entry_alias("nodes", "mcl_portals:end_portal_frame", "nodes", "mcl_portals:end_portal_frame_eye")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -366,7 +368,7 @@ minetest.override_item("mcl_end:ender_eye", {
|
||||||
end
|
end
|
||||||
minetest.set_node(pointed_thing.under, { name = "mcl_portals:end_portal_frame_eye", param2 = node.param2 })
|
minetest.set_node(pointed_thing.under, { name = "mcl_portals:end_portal_frame_eye", param2 = node.param2 })
|
||||||
|
|
||||||
if minetest.get_modpath("doc") then
|
if has_doc then
|
||||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:end_portal_frame")
|
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:end_portal_frame")
|
||||||
end
|
end
|
||||||
minetest.sound_play(
|
minetest.sound_play(
|
||||||
|
@ -381,7 +383,7 @@ minetest.override_item("mcl_end:ender_eye", {
|
||||||
-- Epic 'portal open' sound effect that can be heard everywhere
|
-- Epic 'portal open' sound effect that can be heard everywhere
|
||||||
minetest.sound_play("mcl_portals_open_end_portal", {gain=0.8}, true)
|
minetest.sound_play("mcl_portals_open_end_portal", {gain=0.8}, true)
|
||||||
end_portal_area(ppos)
|
end_portal_area(ppos)
|
||||||
if minetest.get_modpath("doc") then
|
if has_doc then
|
||||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end")
|
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", "mcl_portals:portal_end")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -389,7 +391,3 @@ minetest.override_item("mcl_end:ender_eye", {
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
minetest.override_item("mcl_core:bedrock", {
|
|
||||||
after_destruct = destroy_portal,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,13 @@ local function potions_init_icons(player)
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
icon_ids[name] = {}
|
icon_ids[name] = {}
|
||||||
for e=1, EFFECT_TYPES do
|
for e=1, EFFECT_TYPES do
|
||||||
local x = -7 + -38 * e
|
local x = -52 * e - 2
|
||||||
local id = player:hud_add({
|
local id = player:hud_add({
|
||||||
hud_elem_type = "image",
|
hud_elem_type = "image",
|
||||||
text = "blank.png",
|
text = "blank.png",
|
||||||
position = { x = 1, y = 0 },
|
position = { x = 1, y = 0 },
|
||||||
offset = { x = x, y = 272 },
|
offset = { x = x, y = 3 },
|
||||||
scale = { x = 2, y = 2 },
|
scale = { x = 3, y = 3 },
|
||||||
alignment = { x = 1, y = 1 },
|
alignment = { x = 1, y = 1 },
|
||||||
z_index = 100,
|
z_index = 100,
|
||||||
})
|
})
|
||||||
|
|
|
@ -463,4 +463,4 @@ function mcl_potions.register_arrow(name, desc, color, def)
|
||||||
if minetest.get_modpath("doc_identifier") then
|
if minetest.get_modpath("doc_identifier") then
|
||||||
doc.sub.identifier.register_object("mcl_bows:arrow_entity", "craftitems", "mcl_bows:arrow")
|
doc.sub.identifier.register_object("mcl_bows:arrow_entity", "craftitems", "mcl_bows:arrow")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|