add a recipe, tweak documentation, break hard dependency on default
parent
190b274be8
commit
2f87ac6621
|
@ -1,5 +1,7 @@
|
|||
Sounds and textures are under various licenses, see the license.txt file in the corresponding directory for details.
|
||||
|
||||
"screenshot.png" is from https://commons.wikimedia.org/wiki/File:OPENSCAD_SurfaceData_heightmap.png by RoCMP, released under the Creative Commons Attribution-Share Alike 4.0 International license.
|
||||
|
||||
License for Code
|
||||
-----------------------------
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
The world you can see with normal vision is a straightforward Euclidian grid. All angles are right angles, and all distances match up nicely - travel ten meters north, ten meters east, ten meters south, and then ten meters west and you will always wind up exactly where you started. Gravity is always downward, and of uniform strength.
|
||||
|
||||
On a deeper layer, however, there is a warped grid to reality where distance no longer quite so consistent and "down" varies both in location and magnitude. A Warpfield Trigger is an arcane tool that can be used to measure this field, giving the strength and direction of the bias it imposes on reality. And, with the application of a little energy, a Warpfield Trigger can briefly loosen the operator's hold on the normal world and allow them to "fall" along the warp field's bent lines.
|
||||
On a deeper layer, however, there is a warped grid to reality where distance no longer quite so consistent and "down" varies both in location and magnitude. Imagine a landscape of invisible and intangible hills and valleys. You may think you're standing on flat, solid ground, but in this invisible world of the warp you may be standing on a steep slope. A Warpfield Trigger is an arcane tool that can be used to measure this field, giving the strength and direction of the invisible slope it possesses at your location. And, with the application of a little energy, a Warpfield Trigger can briefly loosen the operator's hold on the normal world and allow them to "fall" along the warp field's bent lines.
|
||||
|
||||
While wielding a Warpfield Trigger a HUD will be shown with an X, Y and Z displacement value. When "used" the Trigger will cause its holder to teleport in that given direction. Note that there is no guarantee that the destination location will be on solid ground - or in open air, for that matter. Be prepared either to deal with a fall or to dig your way out of entombment.
|
||||
While wielding a Warpfield Trigger a HUD will be shown with an X, Y and Z displacement value. When "used" the Trigger will cause its holder to teleport in that direction and to that distance. Note that there is no guarantee that the destination location will be on solid ground - or in open air, for that matter. You'll need to be prepared either to deal with a fall or to dig your way out of entombment.
|
||||
|
||||
Using a Warpfield Trigger to travel like this is stressful on the mechanism and it will eventually break. It also can't be done too frequently, even with multiple different trigger devices - travel through warp leaves you momentarily in an unstable state that can't easily be forced back into the warpfield for a few seconds.
|
||||
|
||||
|
@ -16,8 +16,10 @@ Warp field travel is not a highly practical thing but creative players will find
|
|||
|
||||
If trigger wear is low (or disabled) and cooldown is short (or disabled) then you may find that repeated warp field jumps will eventually cause you to settle into specific locations where the strength of the field is nearly zero in all directions. These warp field "minima" are sargassos of a sort, the bottom of a hole into which all manner of dimensional detritus may settle. In a game where warp field travel is common these might serve as useful landmarks or gathering spots.
|
||||
|
||||
Conversely, there are warp field "maxima" that all warp field slopes lead away from. These are much harder to find (unless you've got the server-admin-only chat command designed for locating them) but may represent the only impregnable places on a map with this mod installed.
|
||||
|
||||
## API
|
||||
|
||||
``warpfield.get_warp_at(pos)`` will return the value of the warp field's displacement vector at any given point. You could perhaps use this to cause certain types of monsters to appear in warp field minima, or even allow them to teleport themselves when injured or threatened.
|
||||
|
||||
There's no way to "backtrace" from a location except via a brute force search. It's possible that more than one place will have a warp field displacement that leads to a particular destination pos.
|
||||
There's no way to "backtrace" from a location except via a brute force search. It's possible that more than one place will have a warp field displacement that leads to a particular destination.
|
171
init.lua
171
init.lua
|
@ -2,7 +2,6 @@ warpfield = {}
|
|||
|
||||
local S = minetest.get_translator()
|
||||
|
||||
local warpfield_trigger_breaks_sound = "default_tool_breaks"
|
||||
local warpfield_trigger_uses = tonumber(minetest.settings:get("warpfield_trigger_uses")) or 0
|
||||
local warpfield_trigger_cooldown = tonumber(minetest.settings:get("warpfield_cooldown")) or 10
|
||||
|
||||
|
@ -120,7 +119,7 @@ local trigger_def = {
|
|||
stack_max = trigger_stack_size,
|
||||
tool_capabilites = trigger_tool_capabilities,
|
||||
sound = {
|
||||
breaks = warpfield_trigger_breaks_sound,
|
||||
breaks = "warpfield_trigger_break",
|
||||
},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
|
||||
|
@ -252,23 +251,22 @@ else
|
|||
minetest.register_craftitem("warpfield:trigger", trigger_def)
|
||||
end
|
||||
|
||||
--minetest.register_craft({
|
||||
-- output = "warpfield:trigger",
|
||||
-- recipe = {
|
||||
-- {"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
|
||||
-- {"default:mese_crystal_fragment", warpfield_displaced_name, "default:mese_crystal_fragment"},
|
||||
-- {"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
|
||||
-- }
|
||||
--})
|
||||
|
||||
local number_of_attempts_to_use = 100
|
||||
local precision = 0.1
|
||||
|
||||
local find_minimum = function(pos, max_tries)
|
||||
local find_minimum = function(pos, max_tries, direction)
|
||||
direction = direction or 1
|
||||
local dir_func
|
||||
if direction > 0 then
|
||||
dir_func = vector.add
|
||||
else
|
||||
dir_func = vector.subtract
|
||||
end
|
||||
|
||||
local last_jump = vector.new(pos)
|
||||
for i = 1, max_tries do
|
||||
local local_warp = get_warp_at(last_jump)
|
||||
local new_jump = vector.add(last_jump, local_warp)
|
||||
local new_jump = dir_func(last_jump, local_warp)
|
||||
if vector.distance(new_jump, last_jump) < precision then
|
||||
return last_jump, i, true
|
||||
end
|
||||
|
@ -279,7 +277,7 @@ end
|
|||
|
||||
minetest.register_chatcommand("find_warp_minimum", {
|
||||
params = "[<pos>]",
|
||||
description = S("locate the nearest warpfield minimum by following the field downhill from the provided location, or from the player's location if not provided."),
|
||||
description = S("locate the nearest warpfield minimum by following the field downhill from the provided location, or from the player's location if not provided. This is where a player starting at that position will eventually wind up if they repeatedly travel by warp, not counting any falls along the way."),
|
||||
privs = {server=true}, -- Require the "privs" privilege to run
|
||||
func = function(name, param)
|
||||
local pos = nil
|
||||
|
@ -300,70 +298,99 @@ minetest.register_chatcommand("find_warp_minimum", {
|
|||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("find_warp_minima", {
|
||||
params = "<minpos> <maxpos> <step_size> [<rounded_to_nearest>]",
|
||||
description = S("Find all warp minima accessible from within the given volume, starting from test points separated by step_size."),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local p1, p2, step_size, round_to_nearest
|
||||
local args = param:split(" ")
|
||||
if #args == 3 or #args == 4 then
|
||||
p1 = minetest.string_to_pos(args[1])
|
||||
p2 = minetest.string_to_pos(args[2])
|
||||
step_size = tonumber(args[3])
|
||||
round_to_nearest = 1
|
||||
if #args == 4 then
|
||||
round_to_nearest = tonumber(args[4]) or 1
|
||||
end
|
||||
local follow_field_array = function(name, param, direction)
|
||||
local p1, p2, step_size, round_to_nearest
|
||||
local args = param:split(" ")
|
||||
if #args == 3 or #args == 4 then
|
||||
p1 = minetest.string_to_pos(args[1])
|
||||
p2 = minetest.string_to_pos(args[2])
|
||||
step_size = tonumber(args[3])
|
||||
round_to_nearest = 1
|
||||
if #args == 4 then
|
||||
round_to_nearest = tonumber(args[4]) or 1
|
||||
end
|
||||
if p1 == nil or p2 == nil or step_size == nil then
|
||||
minetest.chat_send_player(name, S('Incorrect argument format. Expected: "(x1,y1,z1) (x2,y2,z2) number [number]"'))
|
||||
return
|
||||
end
|
||||
|
||||
local minima_hashes = {}
|
||||
local failures = 0
|
||||
local successes = 0
|
||||
for x = math.min(p1.x, p2.x), math.max(p1.x, p2.x), math.abs(step_size) do
|
||||
for y = math.min(p1.y, p2.y), math.max(p1.y, p2.y), math.abs(step_size) do
|
||||
for z = math.min(p1.z, p2.z), math.max(p1.z, p2.z), math.abs(step_size) do
|
||||
local minimum, tries, success = find_minimum({x=x,y=y,z=z}, number_of_attempts_to_use)
|
||||
if success then
|
||||
successes = successes + 1
|
||||
minima_hashes[minetest.hash_node_position(vector.round(vector.divide(minimum, round_to_nearest)))] = true
|
||||
else
|
||||
failures = failures + 1
|
||||
end
|
||||
local total = successes + failures
|
||||
if total % 1000 == 0 then
|
||||
minetest.chat_send_player(name, S("Tested @1 starting points...", total))
|
||||
end
|
||||
end
|
||||
if p1 == nil or p2 == nil or step_size == nil then
|
||||
minetest.chat_send_player(name, S('Incorrect argument format. Expected: "(x1,y1,z1) (x2,y2,z2) number [number]"'))
|
||||
return
|
||||
end
|
||||
|
||||
local minima_hashes = {}
|
||||
local failures = 0
|
||||
local successes = 0
|
||||
for x = math.min(p1.x, p2.x), math.max(p1.x, p2.x), math.abs(step_size) do
|
||||
for y = math.min(p1.y, p2.y), math.max(p1.y, p2.y), math.abs(step_size) do
|
||||
for z = math.min(p1.z, p2.z), math.max(p1.z, p2.z), math.abs(step_size) do
|
||||
local minimum, tries, success = find_minimum({x=x,y=y,z=z}, number_of_attempts_to_use, direction)
|
||||
if success then
|
||||
successes = successes + 1
|
||||
minima_hashes[minetest.hash_node_position(vector.round(vector.divide(minimum, round_to_nearest)))] = true
|
||||
else
|
||||
failures = failures + 1
|
||||
end
|
||||
local total = successes + failures
|
||||
if total % 1000 == 0 then
|
||||
minetest.chat_send_player(name, S("Tested @1 starting points...", total))
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(name, S("With @1 successful runs and @2 failed runs found the following minima (rounded to @3m):", successes, failures, round_to_nearest))
|
||||
local sorted_minima = {}
|
||||
for hash, _ in pairs(minima_hashes) do
|
||||
table.insert(sorted_minima, vector.multiply(minetest.get_position_from_hash(hash), round_to_nearest))
|
||||
end
|
||||
table.sort(sorted_minima, function(p1, p2)
|
||||
if p1.x < p2.x then
|
||||
return true
|
||||
elseif p1.x > p2.x then
|
||||
return false
|
||||
elseif p1.y < p2.y then
|
||||
return true
|
||||
elseif p1.y > p2.y then
|
||||
return false
|
||||
elseif p1.z < p2.z then
|
||||
return true
|
||||
elseif p1.z > p2.z then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local sorted_minima = {}
|
||||
for hash, _ in pairs(minima_hashes) do
|
||||
table.insert(sorted_minima, vector.multiply(minetest.get_position_from_hash(hash), round_to_nearest))
|
||||
end
|
||||
table.sort(sorted_minima, function(p1, p2)
|
||||
if p1.x < p2.x then
|
||||
return true
|
||||
elseif p1.x > p2.x then
|
||||
return false
|
||||
end)
|
||||
elseif p1.y < p2.y then
|
||||
return true
|
||||
elseif p1.y > p2.y then
|
||||
return false
|
||||
elseif p1.z < p2.z then
|
||||
return true
|
||||
elseif p1.z > p2.z then
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end)
|
||||
return successes, failures, round_to_nearest, sorted_minima
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("find_warp_minima", {
|
||||
params = "<minpos> <maxpos> <step_size> [<rounded_to_nearest>]",
|
||||
description = S("Find all warp minima accessible from within the given volume, starting from test points separated by step_size. These are locations that players who repeatedly teleport will eventually wind up."),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local successes, failures, round_to_nearest, sorted_minima = follow_field_array(name, param, 1)
|
||||
minetest.chat_send_player(name, S("With @1 successful and @2 failed runs found the following minima (rounded to @3m):", successes, failures, round_to_nearest))
|
||||
for _, pos in ipairs(sorted_minima) do
|
||||
minetest.chat_send_player(name, minetest.pos_to_string(pos))
|
||||
end
|
||||
end,
|
||||
})
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("find_warp_maxima", {
|
||||
params = "<minpos> <maxpos> <step_size> [<rounded_to_nearest>]",
|
||||
description = S("Find all warp maxima accessible from within the given volume, starting from test points separated by step_size. These are places that are difficult or impossible to reach by warpfield teleport."),
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
local successes, failures, round_to_nearest, sorted_minima = follow_field_array(name, param, -1)
|
||||
minetest.chat_send_player(name, S("With @1 successful and @2 failed runs found the following maxima (rounded to @3m):", successes, failures, round_to_nearest))
|
||||
for _, pos in ipairs(sorted_minima) do
|
||||
minetest.chat_send_player(name, minetest.pos_to_string(pos))
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
if minetest.get_modpath("default") then
|
||||
minetest.register_craft({
|
||||
output = "warpfield:trigger",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"},
|
||||
{"default:mese_crystal_fragment", "default:mese_crystal_fragment", "default:mese_crystal_fragment"},
|
||||
{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
|
|
@ -8,8 +8,24 @@
|
|||
This tool can be used @1 times before breaking.=
|
||||
A triggering device that allows teleportation via warpfield.=
|
||||
Cooldown: @1s=
|
||||
|
||||
Find all warp maxima accessible from within the given volume, starting from test points separated by step_size. These are places that are difficult or impossible to reach by warpfield teleport.=
|
||||
|
||||
Find all warp minima accessible from within the given volume, starting from test points separated by step_size. These are locations that players who repeatedly teleport will eventually wind up.=
|
||||
|
||||
Incorrect argument format. Expected: "(x1,y1,z1) (x2,y2,z2) number [number]"=
|
||||
|
||||
Local warp field: @1=
|
||||
Minimum located at @1 after @2 jumps=
|
||||
Stopped testing for minima at @1 after @2 jumps.=
|
||||
Tested @1 starting points...=
|
||||
Warpfield Trigger=
|
||||
|
||||
When triggered, this tool and its user will be displaced in accordance with the local warp field's displacement. Simply holding it makes it act as a compass of sorts, showing the current strength of the warp field.=
|
||||
|
||||
With @1 successful and @2 failed runs found the following maxima (rounded to @3m):=
|
||||
|
||||
With @1 successful and @2 failed runs found the following minima (rounded to @3m):=
|
||||
|
||||
locate the nearest warpfield minimum by following the field downhill from the provided location, or from the player's location if not provided. This is where a player starting at that position will eventually wind up if they repeatedly travel by warp, not counting any falls along the way.=
|
||||
|
||||
|
|
3
mod.conf
3
mod.conf
|
@ -1,2 +1,3 @@
|
|||
name = warpfield
|
||||
depends = default
|
||||
description = A teleport tool that moves players along a randomly-generated warp field
|
||||
optional_depends = default
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -1 +1,3 @@
|
|||
warpfield_teleport_to and warpfield_teleport_from https://freesound.org/people/michalwa2003/sounds/419375/ by michalwa2003 under the CC-BY 3.0 license
|
||||
warpfield_teleport_to and warpfield_teleport_from https://freesound.org/people/michalwa2003/sounds/419375/ by michalwa2003 under the CC-BY 3.0 license
|
||||
|
||||
warpfield_trigger_break - Lights smashed by Lloyd Jackson, recorded by Brady Leduc at Pulsworks Audio Arts. Posted at https://freesound.org/people/sandyrb/sounds/148073/ by sandryrb under the CC-BY 3.0 license
|
Binary file not shown.
Loading…
Reference in New Issue