convert into a death compass

This commit is contained in:
FaceDeer 2020-02-01 22:53:29 -07:00
parent 7117c0112c
commit 6b3c42251c
55 changed files with 139 additions and 275 deletions

View File

@ -2,12 +2,7 @@ License of source code:
-----------------------
2012 Based on Echo's compass mod https://forum.minetest.net/viewtopic.php?f=11&t=3785 (2012 / WFTPL)
2017 Forked, enhanced and relicensed to MIT by bell07
License of media:
-----------------
Textures: CC BY-SA by tacotexmex
Sound: CC0 by tacotexmex
2020 Forked, changed from ccompass to death_compass by FaceDeer
MIT License

View File

@ -1,73 +1,9 @@
# ccompass
# Death Compass
This minetest mod adds a calibratable compass to the minetest. Original mod [here](https://forum.minetest.net/viewtopic.php?f=11&t=3785)
![](./textures/death_compass_16_0.png) ![](./textures/death_compass_16_1.png) ![](./textures/death_compass_16_2.png) ![](./textures/death_compass_16_4.png) ![](./textures/death_compass_16_5.png) ![](./textures/death_compass_16_6.png) ![](./textures/death_compass_16_7.png) ![](./textures/death_compass_16_8.png) ![](./textures/death_compass_16_9.png) ![](./textures/death_compass_16_10.png) ![](./textures/death_compass_16_11.png) ![](./textures/death_compass_16_12.png) ![](./textures/death_compass_16_13.png) ![](./textures/death_compass_16_14.png) ![](./textures/death_compass_16_15.png) ![](./textures/death_compass_16_0.png)
- License: Code: MIT, textures: CC BY-SA, sound: CC0.
- Dependencies to other mods: none
- Forum: https://forum.minetest.net/viewtopic.php?f=9&t=17881
Have you ever died and been frustrated with being unable to find your way back to your corpse? Carry a death compass with you. When you die, the compass will respawn in your inventory and be magically bound to point to the location of your demise.
## For Players:
<TODO: nice screenshot>
But hurry! The server administrator may have limited the duration that the compass will function for.
1. Craft the compass as before using the crafting recipe.
The new compass points to the origin / Zero point and is already usable.
2. Punch the compass to a compatible node (all by default) and enter the target name
3. Now this compass leads you allways to this location. You can give it away to allow other users to find this place.
4. Punch a teleport compatible node (by default mese block) to teleport back to the calibrated place
## For server owners:
The mod support the next settings:
static_spawnpoint - use this position instead 0,0,0 as initial target
ccompass_recalibrate (enabled by default): If disabled each compass can be calibrated one time only
ccompass_restrict_target (Disabled by default): If enabled, only specific nodes are allowed for calibration (usable with any type waypoint nodes for example)
ccompass_restrict_target_nodes: List of technical node names allowed for compass calibration, separated by ','
ccompass_aliasses: If enabled the compas:* items will be aliased to the ccompass:* items for compatibility
ccompass_teleport_nodes: List of technical node names that triggers the teleport to destination, separated by ','
## For developers:
1. It is possible to change compass settings from other mods by changing values in global table ccompass. So it is possible for example to add a waypoint node to the target-nodes by
```
ccompass.recalibrate = true
ccompass.restrict_target = true
ccompass.restrict_target_nodes["schnitzeljagd:waypoint"] = true
ccompass.teleport_nodes["default:diamondblock"] = true
```
2. The pointed node metadata will be checked for "waypoint_name" attribute. It this attribute is set, the calibration screen take this string as proposal. This can be used for a game specific calibration node. To get it working working just set in node definition something like
```
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("waypoint_name", "the unique and wunderfull place")
meta:set_string("waypoint_pos", minetest.pos_to_string(target_pos)) -- if an other position should be the target instead of the node position
meta:set_string("waypoint_skip_namechange", "skip") -- do not ask for the waypoint name
end,
```
3. It is possible to create pre-calibrated compasses trough other mods. Just write the position to the Itemstack meta:
```
stack:get_meta():set_string("target_pos", minetest.pos_to_string(pos))
```
Recalibration related to a user should be done by function call
```
local stack = ItemStack("ccompass:0")
ccompass.set_target(stack, {
target_pos_string = minetest.pos_to_string(pos),
target_name = waypoint_name,
playername = player:get_player_name()
})
```
4. Each time the compass is updated, a hook is called, if defined in other mod. The hook is usefull to implement wear or any other compass manipulation logic.
```
function ccompass.usage_hook(compass_stack, player)
--do anything with compass_stack
return modified_compass_stack
end
```
Crafting recipe: four bones and a mese fragment.

309
init.lua
View File

@ -1,111 +1,61 @@
-- compass configuration interface - adjustable from other mods or minetest.conf settings
ccompass = {}
death_compass = {}
-- default target to static_spawnpoint or 0/0/0
ccompass.default_target = minetest.setting_get_pos("static_spawnpoint") or {x=0, y=0, z=0}
local S = minetest.get_translator("death_compass")
-- how many seconds does the death compass work for? 0 for indefinite
local duration = tonumber(minetest.settings:get("death_compass_duration")) or 0
-- Re-calibration allowed
ccompass.recalibrate = minetest.settings:get_bool("ccompass_recalibrate")
if ccompass.recalibrate == nil then
ccompass.recalibrate = true
end
-- Target restriction
ccompass.restrict_target = minetest.settings:get_bool("ccompass_restrict_target")
ccompass.restrict_target_nodes = {}
local nodes_setting = minetest.settings:get("ccompass_restrict_target_nodes")
if nodes_setting then
nodes_setting:gsub("[^,]+", function(z)
ccompass.restrict_target_nodes[z] = true
end)
end
-- Teleport targets
ccompass.teleport_nodes = {}
local teleport_nodes_setting = minetest.settings:get("ccompass_teleport_nodes")
if teleport_nodes_setting then
teleport_nodes_setting:gsub("[^,]+", function(z)
ccompass.teleport_nodes[z] = true
end)
else
ccompass.teleport_nodes["default:mese"] = true
end
if minetest.settings:get_bool("ccompass_aliasses") then
minetest.register_alias("compass:0", "ccompass:0")
minetest.register_alias("compass:1", "ccompass:1")
minetest.register_alias("compass:2", "ccompass:2")
minetest.register_alias("compass:3", "ccompass:3")
minetest.register_alias("compass:4", "ccompass:4")
minetest.register_alias("compass:5", "ccompass:5")
minetest.register_alias("compass:6", "ccompass:6")
minetest.register_alias("compass:7", "ccompass:7")
minetest.register_alias("compass:8", "ccompass:8")
minetest.register_alias("compass:9", "ccompass:9")
minetest.register_alias("compass:10", "ccompass:10")
minetest.register_alias("compass:11", "ccompass:11")
end
local range_to_inactivate = 5
-- set a position to the compass stack
function ccompass.set_target(stack, param)
param = param or {}
-- param.target_pos_string
-- param.target_name
-- param.playername
function set_target(stack, pos, name)
local meta=stack:get_meta()
meta:set_string("target_pos", param.target_pos_string)
if param.target_name == "" then
meta:set_string("description", "Compass to "..param.target_pos_string)
else
meta:set_string("description", "Compass to "..param.target_name)
end
if param.playername then
local player = minetest.get_player_by_name(param.playername)
minetest.chat_send_player(param.playername, "Calibration done to "..param.target_name.." "..param.target_pos_string)
minetest.sound_play({ name = "ccompass_calibrate", gain = 1 }, { pos = player:getpos(), max_hear_distance = 3 })
end
meta:set_string("target_pos", minetest.pos_to_string(pos))
meta:set_string("target_corpse", name)
meta:set_int("time_of_death", minetest.get_gametime())
end
-- Get compass target
local function get_destination(player, stack)
local posstring = stack:get_meta():get_string("target_pos")
if posstring ~= "" then
return minetest.string_to_pos(posstring)
else
return ccompass.default_target
end
end
local function teleport_above(playername, target, counter)
local player = minetest.get_player_by_name(playername)
if not player then
return
-- looped ticking sound if there's a duration on this
local player_ticking = {}
local function start_ticking(player_name)
if not player_ticking[player_name] then
player_ticking[player_name] = minetest.sound_play("death_compass_tick_tock",
{to_player = player_name, gain = 0.125, loop = true})
end
for i = (counter or 1), 160 do
local nodename = minetest.get_node(target).name
if nodename == "ignore" then
minetest.emerge_area(target, target)
minetest.after(0.1, teleport_above, playername, target, i)
return
end
if nodename ~= 'air' then
target.y = target.y + 1
else
break
end
end
local function stop_ticking(player_name)
local tick_tock_handle = player_ticking[player_name]
if tick_tock_handle then
minetest.sound_stop(tick_tock_handle)
player_ticking[player_name] = nil
end
player:setpos(target)
return
end
-- get right image number for players compas
-- get right image number for players compass
local function get_compass_stack(player, stack)
local target = get_destination(player, stack)
local pos = player:getpos()
if not target then
return ItemStack("death_compass:inactive")
end
local pos = player:get_pos()
local dist = vector.distance(pos, target)
local player_name = player:get_player_name()
if dist < range_to_inactivate then
stop_ticking(player_name)
minetest.sound_play("death_compass_bone_crunch", {to_player=player_name, gain = 1.0})
return ItemStack("death_compass:inactive")
end
local dir = player:get_look_horizontal()
local angle_north = math.deg(math.atan2(target.x - pos.x, target.z - pos.z))
if angle_north < 0 then
@ -117,134 +67,125 @@ local function get_compass_stack(player, stack)
-- create new stack with metadata copied
local metadata = stack:get_meta():to_table()
local newstack = ItemStack("ccompass:"..compass_image)
local meta_fields = metadata.fields
local time_of_death = tonumber(meta_fields.time_of_death)
if duration > 0 then
local remaining = time_of_death + duration - minetest.get_gametime()
if remaining < 0 then
stop_ticking(player_name)
minetest.sound_play("death_compass_bone_crunch", {to_player=player_name, gain = 1.0})
return ItemStack("death_compass:inactive")
end
start_ticking(player_name)
meta_fields.description = S("@1m to @2's corpse, @3s remaining",
math.floor(dist), meta_fields.target_corpse, remaining)
else
meta_fields.description = S("@1m to @2's corpse, died @3s ago",
math.floor(dist), meta_fields.target_corpse, minetest.get_gametime() - time_of_death)
end
local newstack = ItemStack("death_compass:dir"..compass_image)
if metadata then
newstack:get_meta():from_table(metadata)
end
if ccompass.usage_hook then
newstack = ccompass.usage_hook(newstack, player) or newstack
end
return newstack
end
-- Calibrate compass on pointed_thing
local function on_use_function(itemstack, player, pointed_thing)
-- possible only on nodes
if pointed_thing.type ~= "node" then --support nodes only for destination
minetest.chat_send_player(player:get_player_name(), "Calibration can be done on nodes only")
return
end
local nodepos = minetest.get_pointed_thing_position(pointed_thing)
local node = minetest.get_node(nodepos)
-- Do teleport to target
if ccompass.teleport_nodes[node.name] then
teleport_above(player:get_player_name(), get_destination(player, itemstack))
return
end
-- recalibration allowed?
if not ccompass.recalibrate then
local destination = itemstack:get_meta():get_string("target_pos")
if destination ~= "" then
minetest.chat_send_player(player:get_player_name(), "Compass already calibrated")
return
end
end
-- target nodes restricted?
if ccompass.restrict_target then
if not ccompass.restrict_target_nodes[node.name] then
minetest.chat_send_player(player:get_player_name(), "Calibration on this node not possible")
return
end
end
-- check if waypoint name set in target node
local nodepos_string = minetest.pos_to_string(nodepos)
local nodemeta = minetest.get_meta(nodepos)
local waypoint_name = nodemeta:get_string("waypoint_name")
local waypoint_pos = nodemeta:get_string("waypoint_pos")
local skip_namechange = nodemeta:get_string("waypoint_skip_namechange")
local itemmeta=itemstack:get_meta()
if waypoint_pos and waypoint_pos ~= "" then
nodepos_string = waypoint_pos
end
if skip_namechange ~= "" then
ccompass.set_target(itemstack, {
target_pos_string = nodepos_string,
target_name = waypoint_name,
playername = player:get_player_name()
})
else
-- show the formspec to player
itemmeta:set_string("tmp_target_pos", nodepos_string) --just save temporary
minetest.show_formspec(player:get_player_name(), "ccompass",
"size[10,2.5]" ..
"field[1,1;8,1;name;Destination name:;"..waypoint_name.."]"..
"button_exit[0.7,2;3,1;cancel;Cancel]"..
"button_exit[3.7,2;5,1;ok;Calibrate]" )
end
return itemstack
end
-- Process the calibration using entered data
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "ccompass" and fields.name and (fields.ok or fields.key_enter) then
local stack=player:get_wielded_item()
local meta=stack:get_meta()
ccompass.set_target(stack, {
target_pos_string = meta:get_string("tmp_target_pos"),
target_name = fields.name,
playername = player:get_player_name()
})
meta:set_string("tmp_target_pos", "")
player:set_wielded_item(stack)
end
end)
-- update inventory
minetest.register_globalstep(function(dtime)
for i,player in ipairs(minetest.get_connected_players()) do
local player_name = player:get_player_name()
if player:get_inventory() then
for i,stack in ipairs(player:get_inventory():get_list("main")) do
if i > 8 then
break
end
if string.sub(stack:get_name(), 0, 9) == "ccompass:" then
if string.sub(stack:get_name(), 0, 17) == "death_compass:dir" then
player:get_inventory():set_stack("main", i, get_compass_stack(player, stack))
player_name = nil -- don't stop the sound playing
end
end
end
if player_name then
stop_ticking(player_name)
end
end
end)
-- register items
for i = 0, 15 do
local image = "ccompass_16_"..i..".png"
local groups = {}
if i > 0 then
groups.not_in_creative_inventory = 1
end
minetest.register_tool("ccompass:"..i, {
description = "Compass",
local image = "death_compass_16_"..i..".png"
local groups = {death_compass = 1, not_in_creative_inventory = 1}
minetest.register_tool("death_compass:dir"..i, {
description = S("Death Compass"),
inventory_image = image,
wield_image = image,
stack_max = 1,
groups = groups,
on_use = on_use_function,
})
end
minetest.register_tool("death_compass:inactive", {
description = S("Inactive Death Compass"),
inventory_image = "death_compass_inactive.png",
wield_image = "death_compass_inactive.png",
stack_max = 1,
groups = {death_compass = 1},
})
minetest.register_craft({
output = 'ccompass:0',
output = 'death_compass:inactive',
recipe = {
{'', 'default:steel_ingot', ''},
{'default:steel_ingot', 'default:mese_crystal_fragment', 'default:steel_ingot'},
{'', 'default:steel_ingot', ''}
{'', 'bones:bones', ''},
{'bones:bones', 'default:mese_crystal_fragment', 'bones:bones'},
{'', 'bones:bones', ''}
}
})
local player_death_location = {}
minetest.register_on_dieplayer(function(player, reason)
local player_name = player:get_player_name()
local inv = minetest.get_inventory({type="player", name=player:get_player_name()})
local list = inv:get_list("main")
local count = 0
for i, itemstack in pairs(list) do
if minetest.get_item_group(itemstack:get_name(), "death_compass") > 0 then
count = count + itemstack:get_count()
list[i] = ItemStack("")
end
end
if count > 0 then
inv:set_list("main", list)
player_death_location[player_name] = {count=count,pos=player:get_pos()}
end
end)
-- Called when a player dies
-- `reason`: a PlayerHPChangeReason table, see register_on_player_hpchange
minetest.register_on_respawnplayer(function(player)
local player_name = player:get_player_name()
local compasses = player_death_location[player_name]
if compasses then
local inv = minetest.get_inventory({type="player", name=player_name})
-- Remove any death compasses they might still have for some reason
local current = inv:get_list("main")
for i, item in pairs(current) do
if minetest.get_item_group(item:get_name(), "death_compass") > 0 then
current[i] = ItemStack("")
end
end
inv:set_list("main", current)
-- give them new compasses pointing to their place of death
for i = 1, compasses.count do
local compass = ItemStack("death_compass:dir0")
set_target(compass, compasses.pos, player_name)
inv:add_item("main", compass)
end
end
return false
end)
-- * Called when player is to be respawned
-- * Called _before_ repositioning of player occurs
-- * return true in func to disable regular player placement

View File

@ -1,2 +1,2 @@
name = ccompass
description = Adds a calibratable compass to Minetest.
name = death_compass
description = A compass that points to the last place you died

View File

@ -1,14 +1,3 @@
# If disabled each compass can be calibrated one time only
ccompass_recalibrate (Allow compass recalibration) bool true
# If disabled (default) all nodes are allowed to be compass target
ccompass_restrict_target (Restrict nodes usable for calibration) bool false
# List of technical node names allowed for compass calibration, separated by ','
ccompass_restrict_target_nodes (Nodes list allowed for calibration) string
# Enable aliasses to replace other compass mods
ccompass_aliasses (Enable compatibility aliasses) bool false
# Nodes able to teleport to destination on punch, separated by ','
ccompass_teleport_nodes (Nodes list for teleport on punch) string default:mese
# The number of seconds the death compass will remain active for.
# Set this to 0 to let the death compass continue to be active indefinitely.
death_compass_duration (Death compass duration) int 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

2
sounds/license.txt Normal file
View File

@ -0,0 +1,2 @@
death_compass_tick_tock is from https://freesound.org/people/abyeditsound/sounds/450509/ by abyeditsound under the CC0 public domain license
death_bell_bone_crunch is from https://freesound.org/people/carlito62/sounds/176745/ by carlito62 under the CC0 public domain license

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

1
textures/license.txt Normal file
View File

@ -0,0 +1 @@
All death_compass textures created by FaceDeer in 2020 and released under the CC0 public domain license