forked from minetest-mods/nether
Implement sound and events in API
This commit is contained in:
parent
0c7a6e95c5
commit
e2666146ca
8
init.lua
8
init.lua
@ -47,8 +47,8 @@ nether.register_portal_ignition_item("default:mese_crystal_fragment")
|
|||||||
nether.register_portal("nether_portal", {
|
nether.register_portal("nether_portal", {
|
||||||
shape = nether.PortalShape_Traditional,
|
shape = nether.PortalShape_Traditional,
|
||||||
frame_node_name = "default:obsidian",
|
frame_node_name = "default:obsidian",
|
||||||
wormhole_node_name = "nether:portal",
|
|
||||||
wormhole_node_color = 0, -- 0 is magenta
|
wormhole_node_color = 0, -- 0 is magenta
|
||||||
|
|
||||||
-- Warning: "Four per Em" spaces have been used to align the diagram in this text, rather
|
-- Warning: "Four per Em" spaces have been used to align the diagram in this text, rather
|
||||||
-- than ASCII spaces. If Minetest changes font this may need to be updated.
|
-- than ASCII spaces. If Minetest changes font this may need to be updated.
|
||||||
book_of_portals_pagetext = S([[ ──══♦♦♦◊ The Nether ◊♦♦♦══──
|
book_of_portals_pagetext = S([[ ──══♦♦♦◊ The Nether ◊♦♦♦══──
|
||||||
@ -66,10 +66,6 @@ This opens to a truly hellish place, though for small mercies the air there is s
|
|||||||
|
|
||||||
The expedition parties have found no diamonds or gold, and after an experienced search party failed to return from the trail of a missing expedition party, I must conclude this is a dangerous place.
|
The expedition parties have found no diamonds or gold, and after an experienced search party failed to return from the trail of a missing expedition party, I must conclude this is a dangerous place.
|
||||||
]], 10 * nether.FASTTRAVEL_FACTOR),
|
]], 10 * nether.FASTTRAVEL_FACTOR),
|
||||||
sound_ambient = "nether_portal_hum",
|
|
||||||
sound_ignite = "",
|
|
||||||
sound_extinguish = "",
|
|
||||||
sound_teleport = "",
|
|
||||||
|
|
||||||
within_realm = function(pos) -- return true if pos is inside the Nether
|
within_realm = function(pos) -- return true if pos is inside the Nether
|
||||||
return pos.y < nether.DEPTH
|
return pos.y < nether.DEPTH
|
||||||
@ -113,4 +109,4 @@ The expedition parties have found no diamonds or gold, and after an experienced
|
|||||||
return destination_pos
|
return destination_pos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
@ -528,6 +528,10 @@ local function build_portal(portal_definition, anchorPos, orientation, destinati
|
|||||||
if DEBUG then minetest.chat_send_all("Placed portal schematic at " .. minetest.pos_to_string(portal_definition.shape.get_schematicPos_from_anchorPos(anchorPos, orientation)) .. ", orientation " .. orientation) end
|
if DEBUG then minetest.chat_send_all("Placed portal schematic at " .. minetest.pos_to_string(portal_definition.shape.get_schematicPos_from_anchorPos(anchorPos, orientation)) .. ", orientation " .. orientation) end
|
||||||
|
|
||||||
set_portal_metadata(portal_definition, anchorPos, orientation, destination_wormholePos)
|
set_portal_metadata(portal_definition, anchorPos, orientation, destination_wormholePos)
|
||||||
|
|
||||||
|
if portal_definition.on_created ~= nil then
|
||||||
|
portal_definition.on_created(portal_definition, anchorPos, orientation)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -770,11 +774,48 @@ local function ignite_portal(ignition_pos, ignition_node_name)
|
|||||||
-- ignition/BURN_BABY_BURN
|
-- ignition/BURN_BABY_BURN
|
||||||
set_portal_metadata_and_ignite(portal_definition, anchorPos, orientation, destination_wormholePos)
|
set_portal_metadata_and_ignite(portal_definition, anchorPos, orientation, destination_wormholePos)
|
||||||
|
|
||||||
|
if portal_definition.sounds.ignite ~= nil then
|
||||||
|
local local_wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(anchorPos, orientation)
|
||||||
|
minetest.sound_play(portal_definition.sounds.ignite, {pos = local_wormholePos, max_hear_distance = 20})
|
||||||
|
end
|
||||||
|
|
||||||
|
if portal_definition.on_ignite ~= nil then
|
||||||
|
portal_definition.on_ignite(portal_definition, anchorPos, orientation)
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- the timerNode is used to keep the metadata as that node already needs to be known any time a portal is stopped or run
|
||||||
|
-- see also ambient_sound_stop()
|
||||||
|
function ambient_sound_play(portal_definition, soundPos, timerNodeMeta)
|
||||||
|
if portal_definition.sounds.ambient ~= nil then
|
||||||
|
local soundLength = portal_definition.sounds.ambient.length
|
||||||
|
if soundLength == nil then soundLength = 3 end
|
||||||
|
local lastPlayed = timerNodeMeta:get_int("ambient_sound_last_played")
|
||||||
|
|
||||||
|
-- Using "os.time() % soundLength == 0" is lightweight but means delayed starts, so trying a stored lastPlayed
|
||||||
|
if os.time() >= lastPlayed + soundLength then
|
||||||
|
local soundHandle = minetest.sound_play(portal_definition.sounds.ambient, {pos = soundPos, max_hear_distance = 6})
|
||||||
|
if timerNodeMeta ~= nil then
|
||||||
|
timerNodeMeta:set_int("ambient_sound_handle", soundHandle)
|
||||||
|
timerNodeMeta:set_int("ambient_sound_last_played", os.time())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- the timerNode is used to keep the metadata as that node already needs to be known any time a portal is stopped or run
|
||||||
|
-- see also ambient_sound_play()
|
||||||
|
function ambient_sound_stop(timerNodeMeta)
|
||||||
|
if timerNodeMeta ~= nil then
|
||||||
|
local soundHandle = timerNodeMeta:get_int("ambient_sound_handle")
|
||||||
|
minetest.sound_stop(soundHandle)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- WARNING - this is invoked by on_destruct, so you can't assume there's an accesible node at pos
|
-- WARNING - this is invoked by on_destruct, so you can't assume there's an accesible node at pos
|
||||||
extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned rather than declared because extinguish_portal is already declared, for use by earlier functions in the file.
|
extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned rather than declared because extinguish_portal is already declared, for use by earlier functions in the file.
|
||||||
@ -796,6 +837,15 @@ extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned ra
|
|||||||
minetest.log("error", "extinguish_portal() invoked on " .. node_name .. " but no registered portal is constructed from " .. node_name)
|
minetest.log("error", "extinguish_portal() invoked on " .. node_name .. " but no registered portal is constructed from " .. node_name)
|
||||||
return -- no portal frames are made from this type of node
|
return -- no portal frames are made from this type of node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if portal_definition.sounds.extinguish ~= nil then
|
||||||
|
minetest.sound_play(portal_definition.sounds.extinguish, {pos = p1})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- stop timer and ambient sound
|
||||||
|
local timerPos = get_timerPos_from_p1_and_p2(p1, p2)
|
||||||
|
minetest.get_node_timer(timerPos):stop()
|
||||||
|
ambient_sound_stop(minetest.get_meta(timerPos))
|
||||||
|
|
||||||
-- update the ignition state in the portal location info
|
-- update the ignition state in the portal location info
|
||||||
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
||||||
@ -808,7 +858,6 @@ extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned ra
|
|||||||
local frame_node_name = portal_definition.frame_node_name
|
local frame_node_name = portal_definition.frame_node_name
|
||||||
local wormhole_node_name = portal_definition.wormhole_node_name
|
local wormhole_node_name = portal_definition.wormhole_node_name
|
||||||
|
|
||||||
minetest.get_node_timer(get_timerPos_from_p1_and_p2(p1, p2)):stop()
|
|
||||||
|
|
||||||
for x = p1.x, p2.x do
|
for x = p1.x, p2.x do
|
||||||
for y = p1.y, p2.y do
|
for y = p1.y, p2.y do
|
||||||
@ -891,12 +940,22 @@ local function ensure_remote_portal_then_teleport(player, portal_definition, loc
|
|||||||
|
|
||||||
if DEBUG then minetest.chat_send_all("Teleporting player from wormholePos" .. minetest.pos_to_string(local_wormholePos) .. " to wormholePos" .. minetest.pos_to_string(destination_wormholePos)) end
|
if DEBUG then minetest.chat_send_all("Teleporting player from wormholePos" .. minetest.pos_to_string(local_wormholePos) .. " to wormholePos" .. minetest.pos_to_string(destination_wormholePos)) end
|
||||||
|
|
||||||
|
-- play the teleport sound
|
||||||
|
if portal_definition.sounds.teleport ~= nil then
|
||||||
|
minetest.sound_play(portal_definition.sounds.teleport, {to_player = player.name})
|
||||||
|
end
|
||||||
|
|
||||||
-- rotate the player if the destination portal is a different orientation
|
-- rotate the player if the destination portal is a different orientation
|
||||||
local rotation_angle = math.rad(destination_orientation - local_orientation)
|
local rotation_angle = math.rad(destination_orientation - local_orientation)
|
||||||
local offset = vector.subtract(playerPos, local_wormholePos) -- preserve player's position in the portal
|
local offset = vector.subtract(playerPos, local_wormholePos) -- preserve player's position in the portal
|
||||||
local rotated_offset = {x = math.cos(rotation_angle) * offset.x - math.sin(rotation_angle) * offset.z, y = offset.y, z = math.sin(rotation_angle) * offset.x + math.cos(rotation_angle) * offset.z}
|
local rotated_offset = {x = math.cos(rotation_angle) * offset.x - math.sin(rotation_angle) * offset.z, y = offset.y, z = math.sin(rotation_angle) * offset.x + math.cos(rotation_angle) * offset.z}
|
||||||
player:setpos(vector.add(destination_wormholePos, rotated_offset))
|
local new_playerPos = vector.add(destination_wormholePos, rotated_offset)
|
||||||
|
player:setpos(new_playerPos)
|
||||||
player:set_look_horizontal(player:get_look_horizontal() + rotation_angle)
|
player:set_look_horizontal(player:get_look_horizontal() + rotation_angle)
|
||||||
|
|
||||||
|
if portal_definition.on_player_teleported ~= nil then
|
||||||
|
portal_definition.on_player_teleported(portal_definition, player, playerPos, new_playerPos)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
-- no wormhole node at destination - destination portal either needs to be built or ignited
|
-- no wormhole node at destination - destination portal either needs to be built or ignited
|
||||||
-- A very rare edge-case that takes time to set up:
|
-- A very rare edge-case that takes time to set up:
|
||||||
@ -930,8 +989,8 @@ end
|
|||||||
|
|
||||||
|
|
||||||
-- run_wormhole() is invoked once per second per portal, handling teleportation and particle effects.
|
-- run_wormhole() is invoked once per second per portal, handling teleportation and particle effects.
|
||||||
-- See get_timerPos_from_p1_and_p2() for an explanation of where pos will be
|
-- See get_timerPos_from_p1_and_p2() for an explanation of the timerPos location
|
||||||
function run_wormhole(pos, time_elapsed)
|
function run_wormhole(timerPos, time_elapsed)
|
||||||
|
|
||||||
local portal_definition -- will be used inside run_wormhole_node_func()
|
local portal_definition -- will be used inside run_wormhole_node_func()
|
||||||
|
|
||||||
@ -989,7 +1048,7 @@ function run_wormhole(pos, time_elapsed)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local p1, p2, frame_node_name
|
local p1, p2, frame_node_name
|
||||||
local meta = minetest.get_meta(pos)
|
local meta = minetest.get_meta(timerPos)
|
||||||
if meta ~= nil then
|
if meta ~= nil then
|
||||||
p1 = minetest.string_to_pos(meta:get_string("p1"))
|
p1 = minetest.string_to_pos(meta:get_string("p1"))
|
||||||
p2 = minetest.string_to_pos(meta:get_string("p2"))
|
p2 = minetest.string_to_pos(meta:get_string("p2"))
|
||||||
@ -997,10 +1056,10 @@ function run_wormhole(pos, time_elapsed)
|
|||||||
end
|
end
|
||||||
if p1 ~= nil and p2 ~= nil then
|
if p1 ~= nil and p2 ~= nil then
|
||||||
-- look up the portal shape by what it's built from, so we know where the wormhole nodes will be located
|
-- look up the portal shape by what it's built from, so we know where the wormhole nodes will be located
|
||||||
if frame_node_name == nil then frame_node_name = minetest.get_node(pos).name end -- pos should be a frame node
|
if frame_node_name == nil then frame_node_name = minetest.get_node(timerPos).name end -- timerPos should be a frame node if the shape is traditionalPortalShape
|
||||||
portal_definition = get_portal_definition(frame_node_name, p1, p2)
|
portal_definition = get_portal_definition(frame_node_name, p1, p2)
|
||||||
if portal_definition == nil then
|
if portal_definition == nil then
|
||||||
minetest.log("error", "No portal with a \"" .. frame_node_name .. "\" frame is registered. run_wormhole" .. minetest.pos_to_string(pos) .. " was invoked but that location contains \"" .. frame_node_name .. "\"")
|
minetest.log("error", "No portal with a \"" .. frame_node_name .. "\" frame is registered. run_wormhole" .. minetest.pos_to_string(timerPos) .. " was invoked but that location contains \"" .. frame_node_name .. "\"")
|
||||||
else
|
else
|
||||||
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
||||||
portal_definition.shape.apply_func_to_wormhole_nodes(anchorPos, orientation, run_wormhole_node_func)
|
portal_definition.shape.apply_func_to_wormhole_nodes(anchorPos, orientation, run_wormhole_node_func)
|
||||||
@ -1008,6 +1067,9 @@ function run_wormhole(pos, time_elapsed)
|
|||||||
if portal_definition.on_run_wormhole ~= nil then
|
if portal_definition.on_run_wormhole ~= nil then
|
||||||
portal_definition.on_run_wormhole(portal_definition, anchorPos, orientation)
|
portal_definition.on_run_wormhole(portal_definition, anchorPos, orientation)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(anchorPos, orientation)
|
||||||
|
ambient_sound_play(portal_definition, wormholePos, meta)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1250,10 +1312,12 @@ local portaldef_default = {
|
|||||||
wormhole_node_color = 0,
|
wormhole_node_color = 0,
|
||||||
frame_node_name = "default:obsidian",
|
frame_node_name = "default:obsidian",
|
||||||
particle_texture = "nether_particle.png",
|
particle_texture = "nether_particle.png",
|
||||||
sound_ambient = "nether_portal_hum",
|
sounds = {
|
||||||
sound_ignite = "",
|
ambient = {name = "nether_portal_ambient", gain = 0.6, length = 3},
|
||||||
sound_extinguish = "",
|
ignite = {name = "nether_portal_ignite", gain = 0.3},
|
||||||
sound_teleport = "",
|
extinguish = {name = "nether_portal_extinguish", gain = 0.3},
|
||||||
|
teleport = {name = "nether_portal_teleport", gain = 0.3}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1268,7 +1332,8 @@ function nether.register_portal(name, portaldef)
|
|||||||
|
|
||||||
portaldef.name = name
|
portaldef.name = name
|
||||||
|
|
||||||
-- use portaldef_default for any values missing from portaldef
|
-- use portaldef_default for any values missing from portaldef or portaldef.sounds
|
||||||
|
if portaldef.sounds ~= nil then setmetatable(portaldef.sounds, {__index = portaldef_default.sounds}) end
|
||||||
setmetatable(portaldef, {__index = portaldef_default})
|
setmetatable(portaldef, {__index = portaldef_default})
|
||||||
|
|
||||||
if portaldef.particle_color == nil then
|
if portaldef.particle_color == nil then
|
||||||
|
@ -4,6 +4,11 @@ Portal API Reference
|
|||||||
The portal system used to get to the Nether can be used to create portals
|
The portal system used to get to the Nether can be used to create portals
|
||||||
to other realms.
|
to other realms.
|
||||||
|
|
||||||
|
Portal code is more efficient when each type of portal uses a different type
|
||||||
|
of node to build its frame out of, however it is possible to register more than
|
||||||
|
one kind of portal with the same frame material — such as obsidian — provided
|
||||||
|
the size of the PortalShape is distinct from any other type of portal that is
|
||||||
|
using the same node as its frame.
|
||||||
|
|
||||||
|
|
||||||
Helper functions
|
Helper functions
|
||||||
@ -16,8 +21,8 @@ Helper functions
|
|||||||
|
|
||||||
* `nether.find_surface_target_y(target_x, target_z, portal_name)`: returns a
|
* `nether.find_surface_target_y(target_x, target_z, portal_name)`: returns a
|
||||||
suitable anchorPos
|
suitable anchorPos
|
||||||
* Can be used to implement custom find_surface_anchorPos() functions
|
* Can be used when implementing custom find_surface_anchorPos() functions
|
||||||
* portal_name is optional, and providing it allows existing portals on the
|
* portal_name is optional, providing it allows existing portals on the
|
||||||
surface to be reused.
|
surface to be reused.
|
||||||
|
|
||||||
* `nether.find_nearest_working_portal(portal_name, anchorPos, distance_limit, y_factor)`: returns
|
* `nether.find_nearest_working_portal(portal_name, anchorPos, distance_limit, y_factor)`: returns
|
||||||
@ -33,9 +38,9 @@ Helper functions
|
|||||||
API functions
|
API functions
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Call these functions only at load time!
|
Call these functions only at load time:
|
||||||
|
|
||||||
* `nether.register_portal(name, portal definition)`
|
* `nether.register_portal(name, portal_definition)`
|
||||||
* Returns true on success. Can return false if the portal definition
|
* Returns true on success. Can return false if the portal definition
|
||||||
clashes with a portal already registered by another mod, e.g. if the size
|
clashes with a portal already registered by another mod, e.g. if the size
|
||||||
and frame node is not unique.
|
and frame node is not unique.
|
||||||
@ -73,10 +78,15 @@ Used by `nether.register_portal`.
|
|||||||
-- Required. For best results, have your portal constructed of a
|
-- Required. For best results, have your portal constructed of a
|
||||||
-- material nobody else is using.
|
-- material nobody else is using.
|
||||||
|
|
||||||
sound_ambient = "nether_portal_hum",
|
sounds = {
|
||||||
sound_ignite = "",
|
ambient = <SimpleSoundSpec>,
|
||||||
sound_extinguish = "",
|
-- if the ambient SimpleSoundSpec is a table it can also contain a
|
||||||
sound_teleport = "",
|
-- "length" int, which is the number of seconds to wait before
|
||||||
|
-- repeating the ambient sound. Default is 3.
|
||||||
|
ignite = <SimpleSoundSpec>,
|
||||||
|
extinguish = <SimpleSoundSpec>,
|
||||||
|
teleport = <SimpleSoundSpec>,
|
||||||
|
}
|
||||||
|
|
||||||
within_realm = function(pos),
|
within_realm = function(pos),
|
||||||
-- Required. Return true if a portal at pos is in the realm, rather
|
-- Required. Return true if a portal at pos is in the realm, rather
|
||||||
@ -90,19 +100,22 @@ Used by `nether.register_portal`.
|
|||||||
-- portal will be used.
|
-- portal will be used.
|
||||||
|
|
||||||
find_surface_anchorPos = function(realm_anchorPos),
|
find_surface_anchorPos = function(realm_anchorPos),
|
||||||
-- Optional. If you don't use this then a position near the surface
|
-- Optional. If you don't implement this then a position near the
|
||||||
-- will be picked.
|
-- surface will be picked.
|
||||||
-- Return an anchorPos or anchorPos, orientation
|
-- Return an anchorPos or (anchorPos, orientation)
|
||||||
-- If orientation is not specified then the orientation of the realm
|
-- If orientation is not specified then the orientation of the realm
|
||||||
-- portal will be used.
|
-- portal will be used.
|
||||||
|
|
||||||
on_run_wormhole = function(portalDef, anochorPos, orientation)
|
on_run_wormhole = function(portalDef, anochorPos, orientation),
|
||||||
-- invoked once per second per portal
|
-- invoked once per second per portal
|
||||||
on_extinguish = function(portalDef, anochorPos, orientation)
|
on_extinguish = function(portalDef, anochorPos, orientation),
|
||||||
-- invoked when a portal is extinguished, including when the portal
|
-- invoked when a portal is extinguished, including when the portal
|
||||||
-- it connected to was extinguished.
|
-- it connected to was extinguished.
|
||||||
|
on_player_teleported = function(portalDef, player, oldPos, newPos),
|
||||||
todo: on_ignite = function(portalDef, anochorPos, orientation)
|
-- invoked immediately after a player is teleported
|
||||||
todo: on_player_teleported = function(portalDef, oldPos, newPos)
|
on_ignite = function(portalDef, anochorPos, orientation)
|
||||||
todo: on_created = function(portalDef, anochorPos, orientation)
|
-- invoked when a player or mesecon ignites a portal
|
||||||
|
on_created = function(portalDef, anochorPos, orientation)
|
||||||
|
-- invoked when a portal's remote twin is created, usually when a
|
||||||
|
-- player travels through a portal for the first time.
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user