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) 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 2017 Forked, enhanced and relicensed to MIT by bell07
2020 Forked, changed from ccompass to death_compass by FaceDeer
License of media:
-----------------
Textures: CC BY-SA by tacotexmex
Sound: CC0 by tacotexmex
MIT License 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. 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.
- Dependencies to other mods: none
- Forum: https://forum.minetest.net/viewtopic.php?f=9&t=17881
## For Players: But hurry! The server administrator may have limited the duration that the compass will function for.
<TODO: nice screenshot>
1. Craft the compass as before using the crafting recipe. Crafting recipe: four bones and a mese fragment.
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
```

309
init.lua
View File

@ -1,111 +1,61 @@
-- compass configuration interface - adjustable from other mods or minetest.conf settings -- compass configuration interface - adjustable from other mods or minetest.conf settings
ccompass = {} death_compass = {}
-- default target to static_spawnpoint or 0/0/0 local S = minetest.get_translator("death_compass")
ccompass.default_target = minetest.setting_get_pos("static_spawnpoint") or {x=0, y=0, z=0}
-- 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 local range_to_inactivate = 5
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
-- set a position to the compass stack -- set a position to the compass stack
function ccompass.set_target(stack, param) function set_target(stack, pos, name)
param = param or {}
-- param.target_pos_string
-- param.target_name
-- param.playername
local meta=stack:get_meta() local meta=stack:get_meta()
meta:set_string("target_pos", param.target_pos_string) meta:set_string("target_pos", minetest.pos_to_string(pos))
if param.target_name == "" then meta:set_string("target_corpse", name)
meta:set_string("description", "Compass to "..param.target_pos_string) meta:set_int("time_of_death", minetest.get_gametime())
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
end end
-- Get compass target -- Get compass target
local function get_destination(player, stack) local function get_destination(player, stack)
local posstring = stack:get_meta():get_string("target_pos") local posstring = stack:get_meta():get_string("target_pos")
if posstring ~= "" then if posstring ~= "" then
return minetest.string_to_pos(posstring) return minetest.string_to_pos(posstring)
else
return ccompass.default_target
end end
end end
local function teleport_above(playername, target, counter) -- looped ticking sound if there's a duration on this
local player = minetest.get_player_by_name(playername) local player_ticking = {}
if not player then local function start_ticking(player_name)
return 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 end
end
for i = (counter or 1), 160 do local function stop_ticking(player_name)
local nodename = minetest.get_node(target).name local tick_tock_handle = player_ticking[player_name]
if nodename == "ignore" then if tick_tock_handle then
minetest.emerge_area(target, target) minetest.sound_stop(tick_tock_handle)
minetest.after(0.1, teleport_above, playername, target, i) player_ticking[player_name] = nil
return
end
if nodename ~= 'air' then
target.y = target.y + 1
else
break
end
end end
player:setpos(target)
return
end end
-- get right image number for players compas -- get right image number for players compass
local function get_compass_stack(player, stack) local function get_compass_stack(player, stack)
local target = get_destination(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 dir = player:get_look_horizontal()
local angle_north = math.deg(math.atan2(target.x - pos.x, target.z - pos.z)) local angle_north = math.deg(math.atan2(target.x - pos.x, target.z - pos.z))
if angle_north < 0 then if angle_north < 0 then
@ -117,134 +67,125 @@ local function get_compass_stack(player, stack)
-- create new stack with metadata copied -- create new stack with metadata copied
local metadata = stack:get_meta():to_table() local metadata = stack:get_meta():to_table()
local meta_fields = metadata.fields
local newstack = ItemStack("ccompass:"..compass_image) 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 if metadata then
newstack:get_meta():from_table(metadata) newstack:get_meta():from_table(metadata)
end end
if ccompass.usage_hook then
newstack = ccompass.usage_hook(newstack, player) or newstack
end
return newstack return newstack
end 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 -- update inventory
minetest.register_globalstep(function(dtime) minetest.register_globalstep(function(dtime)
for i,player in ipairs(minetest.get_connected_players()) do for i,player in ipairs(minetest.get_connected_players()) do
local player_name = player:get_player_name()
if player:get_inventory() then if player:get_inventory() then
for i,stack in ipairs(player:get_inventory():get_list("main")) do for i,stack in ipairs(player:get_inventory():get_list("main")) do
if i > 8 then if i > 8 then
break break
end 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:get_inventory():set_stack("main", i, get_compass_stack(player, stack))
player_name = nil -- don't stop the sound playing
end end
end end
end end
if player_name then
stop_ticking(player_name)
end
end end
end) end)
-- register items -- register items
for i = 0, 15 do for i = 0, 15 do
local image = "ccompass_16_"..i..".png" local image = "death_compass_16_"..i..".png"
local groups = {} local groups = {death_compass = 1, not_in_creative_inventory = 1}
if i > 0 then minetest.register_tool("death_compass:dir"..i, {
groups.not_in_creative_inventory = 1 description = S("Death Compass"),
end
minetest.register_tool("ccompass:"..i, {
description = "Compass",
inventory_image = image, inventory_image = image,
wield_image = image, wield_image = image,
stack_max = 1,
groups = groups, groups = groups,
on_use = on_use_function,
}) })
end 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({ minetest.register_craft({
output = 'ccompass:0', output = 'death_compass:inactive',
recipe = { recipe = {
{'', 'default:steel_ingot', ''}, {'', 'bones:bones', ''},
{'default:steel_ingot', 'default:mese_crystal_fragment', 'default:steel_ingot'}, {'bones:bones', 'default:mese_crystal_fragment', 'bones:bones'},
{'', 'default:steel_ingot', ''} {'', '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 name = death_compass
description = Adds a calibratable compass to Minetest. 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 # The number of seconds the death compass will remain active for.
ccompass_recalibrate (Allow compass recalibration) bool true # Set this to 0 to let the death compass continue to be active indefinitely.
death_compass_duration (Death compass duration) int 0
# 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

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