forked from minetest-mods/nether
Implement sound and events in API
This commit is contained in:
parent
0c7a6e95c5
commit
e2666146ca
6
init.lua
6
init.lua
@ -47,8 +47,8 @@ nether.register_portal_ignition_item("default:mese_crystal_fragment")
|
||||
nether.register_portal("nether_portal", {
|
||||
shape = nether.PortalShape_Traditional,
|
||||
frame_node_name = "default:obsidian",
|
||||
wormhole_node_name = "nether:portal",
|
||||
wormhole_node_color = 0, -- 0 is magenta
|
||||
|
||||
-- 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.
|
||||
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.
|
||||
]], 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
|
||||
return pos.y < nether.DEPTH
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -770,11 +774,48 @@ local function ignite_portal(ignition_pos, ignition_node_name)
|
||||
-- ignition/BURN_BABY_BURN
|
||||
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
|
||||
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
|
||||
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.
|
||||
@ -797,6 +838,15 @@ extinguish_portal = function(pos, node_name, frame_was_destroyed) -- assigned ra
|
||||
return -- no portal frames are made from this type of node
|
||||
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
|
||||
local anchorPos, orientation = portal_definition.shape.get_anchorPos_and_orientation_from_p1_and_p2(p1, p2)
|
||||
if frame_was_destroyed then
|
||||
@ -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 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 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
|
||||
|
||||
-- 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
|
||||
local rotation_angle = math.rad(destination_orientation - local_orientation)
|
||||
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}
|
||||
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)
|
||||
|
||||
if portal_definition.on_player_teleported ~= nil then
|
||||
portal_definition.on_player_teleported(portal_definition, player, playerPos, new_playerPos)
|
||||
end
|
||||
else
|
||||
-- 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:
|
||||
@ -930,8 +989,8 @@ end
|
||||
|
||||
|
||||
-- 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
|
||||
function run_wormhole(pos, time_elapsed)
|
||||
-- See get_timerPos_from_p1_and_p2() for an explanation of the timerPos location
|
||||
function run_wormhole(timerPos, time_elapsed)
|
||||
|
||||
local portal_definition -- will be used inside run_wormhole_node_func()
|
||||
|
||||
@ -989,7 +1048,7 @@ function run_wormhole(pos, time_elapsed)
|
||||
end
|
||||
|
||||
local p1, p2, frame_node_name
|
||||
local meta = minetest.get_meta(pos)
|
||||
local meta = minetest.get_meta(timerPos)
|
||||
if meta ~= nil then
|
||||
p1 = minetest.string_to_pos(meta:get_string("p1"))
|
||||
p2 = minetest.string_to_pos(meta:get_string("p2"))
|
||||
@ -997,10 +1056,10 @@ function run_wormhole(pos, time_elapsed)
|
||||
end
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
@ -1008,6 +1067,9 @@ function run_wormhole(pos, time_elapsed)
|
||||
if portal_definition.on_run_wormhole ~= nil then
|
||||
portal_definition.on_run_wormhole(portal_definition, anchorPos, orientation)
|
||||
end
|
||||
|
||||
local wormholePos = portal_definition.shape.get_wormholePos_from_anchorPos(anchorPos, orientation)
|
||||
ambient_sound_play(portal_definition, wormholePos, meta)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1250,10 +1312,12 @@ local portaldef_default = {
|
||||
wormhole_node_color = 0,
|
||||
frame_node_name = "default:obsidian",
|
||||
particle_texture = "nether_particle.png",
|
||||
sound_ambient = "nether_portal_hum",
|
||||
sound_ignite = "",
|
||||
sound_extinguish = "",
|
||||
sound_teleport = "",
|
||||
sounds = {
|
||||
ambient = {name = "nether_portal_ambient", gain = 0.6, length = 3},
|
||||
ignite = {name = "nether_portal_ignite", gain = 0.3},
|
||||
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
|
||||
|
||||
-- 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})
|
||||
|
||||
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
|
||||
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
|
||||
@ -16,8 +21,8 @@ Helper functions
|
||||
|
||||
* `nether.find_surface_target_y(target_x, target_z, portal_name)`: returns a
|
||||
suitable anchorPos
|
||||
* Can be used to implement custom find_surface_anchorPos() functions
|
||||
* portal_name is optional, and providing it allows existing portals on the
|
||||
* Can be used when implementing custom find_surface_anchorPos() functions
|
||||
* portal_name is optional, providing it allows existing portals on the
|
||||
surface to be reused.
|
||||
|
||||
* `nether.find_nearest_working_portal(portal_name, anchorPos, distance_limit, y_factor)`: returns
|
||||
@ -33,9 +38,9 @@ Helper 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
|
||||
clashes with a portal already registered by another mod, e.g. if the size
|
||||
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
|
||||
-- material nobody else is using.
|
||||
|
||||
sound_ambient = "nether_portal_hum",
|
||||
sound_ignite = "",
|
||||
sound_extinguish = "",
|
||||
sound_teleport = "",
|
||||
sounds = {
|
||||
ambient = <SimpleSoundSpec>,
|
||||
-- if the ambient SimpleSoundSpec is a table it can also contain a
|
||||
-- "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),
|
||||
-- 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.
|
||||
|
||||
find_surface_anchorPos = function(realm_anchorPos),
|
||||
-- Optional. If you don't use this then a position near the surface
|
||||
-- will be picked.
|
||||
-- Return an anchorPos or anchorPos, orientation
|
||||
-- Optional. If you don't implement this then a position near the
|
||||
-- surface will be picked.
|
||||
-- Return an anchorPos or (anchorPos, orientation)
|
||||
-- If orientation is not specified then the orientation of the realm
|
||||
-- portal will be used.
|
||||
|
||||
on_run_wormhole = function(portalDef, anochorPos, orientation)
|
||||
on_run_wormhole = function(portalDef, anochorPos, orientation),
|
||||
-- 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
|
||||
-- it connected to was extinguished.
|
||||
|
||||
todo: on_ignite = function(portalDef, anochorPos, orientation)
|
||||
todo: on_player_teleported = function(portalDef, oldPos, newPos)
|
||||
todo: on_created = function(portalDef, anochorPos, orientation)
|
||||
on_player_teleported = function(portalDef, player, oldPos, newPos),
|
||||
-- invoked immediately after a player is teleported
|
||||
on_ignite = 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