add a recipe, tweak documentation, break hard dependency on default

master
FaceDeer 2020-11-30 00:56:56 -07:00
parent 190b274be8
commit 2f87ac6621
8 changed files with 127 additions and 77 deletions

View File

@ -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
-----------------------------

View File

@ -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
View File

@ -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

View File

@ -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.=

View File

@ -1,2 +1,3 @@
name = warpfield
depends = default
description = A teleport tool that moves players along a randomly-generated warp field
optional_depends = default

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -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.