Only changes the look of the item. Original intention was to use dye to change colors (craft from different colored wool, etc), but probably not worth it.
357 lines
13 KiB
Lua
357 lines
13 KiB
Lua
-- TODO show some text on screen for a short while?
|
|
-- TODO "admin" command version with configurable pos, radius and timeout
|
|
|
|
local ANNOUNCE_PREPARE_COMMAND_NAME = "look"
|
|
local ANNOUNCE_COMMAND_NAME = "look!"
|
|
local ANNOUNCE_RADIUS = 128 -- players within this radius will see a waypoint
|
|
local ANNOUNCE_ICON = "waypoint_announce_exclamation_mark.png"
|
|
local ANNOUNCE_TIMEOUT = 20 -- time until waypoint disappears
|
|
local WAYPOINT_COLOR = 0xFFFFFF -- color of the waypoint label
|
|
local ICON_SCALE = {x=-3,y=-3} -- size is 3% of the screen
|
|
local ICON_COLOR = "#E78C13"
|
|
local ARROW_FORMAT = "waypoint_announce_arrow_%03d.png"
|
|
local ARROW_QUADS_STEPS = 9
|
|
local POINT_RANGE = 256 -- maximun range player can point at (raycast range)
|
|
local POINT_TO_OBJECTS = true
|
|
local POINT_TO_LIQUIDS = true
|
|
local CHAT_MESSAGE_COLOR = "#2B74FF" -- argument to minetest.colorize()
|
|
|
|
|
|
local waypoint_lib = dofile(minetest.get_modpath("waypoint_announce") .. DIR_DELIM .. "waypoint_lib.lua")
|
|
|
|
|
|
local function show_hud_waypoint(player, point_pos, name, icon_name)
|
|
|
|
local size = {x=-9,y=-16} -- scale for 16:9 displays
|
|
local alignment = {x=100/9,y=100/16*0.7} -- int the middle above the crosshair
|
|
|
|
local do_auto_update = true
|
|
local hud = waypoint_lib.WaypointHUD:new(player, point_pos,
|
|
name, WAYPOINT_COLOR,
|
|
icon_name, ICON_SCALE, ICON_COLOR,
|
|
size, alignment, ARROW_FORMAT, ICON_COLOR, ARROW_QUADS_STEPS,
|
|
do_auto_update)
|
|
hud:show(player)
|
|
|
|
return hud
|
|
end
|
|
|
|
|
|
local Announcement = {}
|
|
|
|
function Announcement:new(announcer_name, radius, message, icon, timeout)
|
|
local a = {
|
|
announcer_name = announcer_name,
|
|
announcer_pos = nil, -- TODO check before posting? post without?
|
|
radius = radius,
|
|
message = message,
|
|
point = nil,
|
|
icon = icon,
|
|
timeout = timeout,
|
|
players_hud = {},
|
|
timeout_job = nil,
|
|
}
|
|
self.__index = self
|
|
setmetatable(a, self)
|
|
return a
|
|
end
|
|
|
|
|
|
local prepared_announcements = {}
|
|
local function prepare_announcement(announcer_name, radius, message, icon, timeout)
|
|
--prepared_announcements[select(1,...)] = table.pack(...) -- does not work in older lua?
|
|
prepared_announcements[announcer_name] = Announcement:new(announcer_name, radius, message, icon, timeout)
|
|
end
|
|
|
|
|
|
|
|
local function remove_announcement(announcement)
|
|
for player_name, hud in pairs(announcement.players_hud) do
|
|
local player = minetest.get_player_by_name(player_name)
|
|
if player then
|
|
hud:hide(player)
|
|
end
|
|
end
|
|
announcement.timeout_job.cancel()
|
|
end
|
|
|
|
|
|
local function post_announcement(announcement)
|
|
local pointed_pos = announcement.point
|
|
local announcer_pos = announcement.announcer_pos
|
|
local announcer_name = announcement.announcer_name
|
|
|
|
if not pointed_pos or not announcer_pos or not announcer_name then
|
|
minetest.log("error","[MOD] waypoint_announce: nil required fields " .. minetest.serialize(announcement))
|
|
return nil
|
|
end
|
|
|
|
for i, object in pairs(minetest.get_objects_inside_radius(announcer_pos, announcement.radius)) do
|
|
if object:is_player() then
|
|
local player = object
|
|
local player_name = player:get_player_name()
|
|
local hud = show_hud_waypoint(player, pointed_pos, announcement.message, announcement.icon)
|
|
announcement.players_hud[player_name] = hud
|
|
end
|
|
end
|
|
|
|
local rounded_pos = vector.round(pointed_pos)
|
|
local chat_message = "[ANNOUNCE] (" ..
|
|
announcer_name ..
|
|
") Look: " ..
|
|
announcement.message .. " " ..
|
|
minetest.pos_to_string(rounded_pos)
|
|
minetest.chat_send_all(minetest.colorize(CHAT_MESSAGE_COLOR, chat_message))
|
|
|
|
announcement.timeout_job = minetest.after(announcement.timeout, remove_announcement, announcement)
|
|
|
|
for modname,func in pairs(waypoint_announce.post_announcement_hooks) do
|
|
func(announcement.announcer_name, announcement.message, announcement.point)
|
|
end
|
|
return announcement
|
|
end
|
|
|
|
|
|
local function get_pointed_and_post_announcement(announcer, announcement)
|
|
local announcer_pos = announcer:get_pos()
|
|
announcement.announcer_pos = announcer_pos
|
|
-- TODO use pointed thing to provide some description?
|
|
local pointed_pos, pointed_thing = waypoint_lib.get_pointed_position(announcer, POINT_RANGE, POINT_TO_OBJECTS, POINT_TO_LIQUIDS)
|
|
if not pointed_pos then
|
|
-- player is pointing into the void, use him as target
|
|
pointed_pos = announcer_pos
|
|
end
|
|
|
|
announcement.point = pointed_pos
|
|
|
|
post_announcement(announcement)
|
|
end
|
|
|
|
|
|
local function check_click_for_prepared_players(dtime)
|
|
for player_name, announcement in pairs(prepared_announcements) do
|
|
local player = minetest.get_player_by_name(player_name)
|
|
if player then
|
|
local player_controls = player:get_player_control()
|
|
if player_controls['dig'] then
|
|
-- place prepared announcement at pointed position
|
|
get_pointed_and_post_announcement(player, announcement)
|
|
prepared_announcements[player_name] = nil
|
|
elseif player_controls['place'] then
|
|
-- cancel prepared announcement
|
|
minetest.chat_send_player(player_name, "Announcement canceled.")
|
|
prepared_announcements[player_name] = nil
|
|
end
|
|
else
|
|
-- player is not online anymore, remove prepared announcement
|
|
prepared_announcements[player_name] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
minetest.register_globalstep(check_click_for_prepared_players)
|
|
|
|
|
|
minetest.register_chatcommand(ANNOUNCE_PREPARE_COMMAND_NAME, {
|
|
params = "<message>",
|
|
description = "Type your message, then point to somewhere and press <punch> button. This will send your message to chat and place a waypoint where you pointed. Waypoint will be visible to people around you.",
|
|
privs = {
|
|
shout = true,
|
|
},
|
|
func = function(name, param)
|
|
local player = minetest.get_player_by_name(name)
|
|
if player then -- player is online
|
|
local player_name = player:get_player_name()
|
|
-- TODO limit message length?
|
|
prepare_announcement(player_name, ANNOUNCE_RADIUS, param, ANNOUNCE_ICON, ANNOUNCE_TIMEOUT)
|
|
return true, "Prepared announcement: \"" .. param .. "\". Point and <hit> where to put it. (<place> to cancel)."
|
|
else
|
|
return false
|
|
end
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_chatcommand(ANNOUNCE_COMMAND_NAME, {
|
|
params = "<message>",
|
|
description = "Send to chat your message and place a waypoint where you are looking at. Waypoint will be visible to people around you.",
|
|
privs = {
|
|
shout = true,
|
|
},
|
|
func = function(name, param)
|
|
local player = minetest.get_player_by_name(name)
|
|
if player then -- player is online
|
|
local player_name = player:get_player_name()
|
|
local announcement = Announcement:new(player_name, ANNOUNCE_RADIUS, param, ANNOUNCE_ICON, ANNOUNCE_TIMEOUT)
|
|
get_pointed_and_post_announcement(player, announcement)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end,
|
|
})
|
|
|
|
|
|
|
|
local flag_dialog_context = {}
|
|
local function show_flag_config_dialog(itemstack, player)
|
|
local meta = itemstack:get_meta()
|
|
local formspec_head = "formspec_version[4]size[8,4]set_focus[close;]"
|
|
local message = meta:get_string("message") or "Flag point"
|
|
local field_message = ("field[0.5,0.6;7,0.7;message;Message;%s]"):format(minetest.formspec_escape(message))
|
|
local color_str = meta:get_string("color_str")
|
|
if color_str == "" then
|
|
color_str = meta:get_string("color")
|
|
if color_str == "" then
|
|
-- probably an old flag, provide default color
|
|
color_str = "red"
|
|
end
|
|
end
|
|
-- strip alpha (it should never appear here anyway)
|
|
color_str = string.match(color_str, "#[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]") or color_str
|
|
local field_color = ("field[0.5,1.75;4,0.7;color;Color;%s]"):format(
|
|
minetest.formspec_escape(color_str))
|
|
minetest.show_formspec(player:get_player_name(), "waypoint_announce:flag_edit",
|
|
formspec_head ..
|
|
field_message ..
|
|
field_color ..
|
|
--field_time .. -- TODO set time too? (within limits)
|
|
"button_exit[0.5,2.7;3,0.8;save;save]" ..
|
|
"button_exit[4.5,2.7;3,0.8;close;close]")
|
|
flag_dialog_context[player:get_player_name()] = itemstack
|
|
end
|
|
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
if formname ~= "waypoint_announce:flag_edit" then
|
|
return
|
|
end
|
|
-- FIXME has same wielded item swapping problems as compass had
|
|
if fields.save or (fields.key_enter_field and fields.quit) then
|
|
local flag_item = flag_dialog_context[player:get_player_name()]
|
|
if not flag_item then
|
|
-- should not happen normally
|
|
minetest.log("error","[MOD] waypoint_announce: " ..
|
|
player:get_player_name() ..
|
|
" Closed flag dialog without opening it?")
|
|
return
|
|
end
|
|
local meta = flag_item:get_meta()
|
|
|
|
local message = fields.message
|
|
meta:set_string("message", message)
|
|
|
|
|
|
if fields.color and #(fields.color) < 20 then
|
|
local colorstring = minetest.colorspec_to_colorstring(fields.color)
|
|
if colorstring then
|
|
-- strip alpha
|
|
colorstring = string.match(colorstring, "#[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]") or colorstring
|
|
meta:set_string("color", colorstring)
|
|
-- preserve original color name for editing
|
|
meta:set_string("color_str", fields.color)
|
|
end
|
|
-- else
|
|
-- -- something sus is going on, don't set it
|
|
-- meta:set_string("color_str", "")
|
|
end
|
|
|
|
local success = player:set_wielded_item(flag_item)
|
|
if not success then
|
|
-- no idea why this can happen
|
|
minetest.log("error","[MOD] waypoint_announce: " ..
|
|
player:get_player_name() ..
|
|
" Failed to swap flag after editing it")
|
|
end
|
|
-- assume dialog is closed and reset context
|
|
flag_dialog_context[player:get_player_name()] = nil
|
|
end
|
|
end)
|
|
|
|
|
|
local last_flag_use = {}
|
|
local FLAG_COOLDOWN = 3
|
|
|
|
local function flag_use_callback(itemstack, user, pointed_thing)
|
|
if not (user and user:is_player()) then
|
|
return
|
|
end
|
|
|
|
if user:get_player_control()["sneak"] then
|
|
show_flag_config_dialog(itemstack, user)
|
|
return
|
|
end
|
|
|
|
if not minetest.check_player_privs(user, "shout") then
|
|
return
|
|
end
|
|
|
|
local player_name = user:get_player_name()
|
|
|
|
|
|
local meta = itemstack:get_meta()
|
|
local pointed_pos = nil
|
|
if pointed_thing.type == "nothing" then
|
|
pointed_pos, pointed_thing = waypoint_lib.get_pointed_position(user, POINT_RANGE, POINT_TO_OBJECTS, POINT_TO_LIQUIDS)
|
|
else
|
|
pointed_pos = pointed_thing.above
|
|
end
|
|
|
|
if not pointed_pos then
|
|
return
|
|
end
|
|
|
|
local current_time = minetest.get_gametime()
|
|
local last_time = last_flag_use[player_name]
|
|
if last_time and current_time - last_time < FLAG_COOLDOWN then
|
|
-- FIXME check after we know we can post it?
|
|
minetest.chat_send_player(player_name, ("Don't spam, wait %ds."):format(FLAG_COOLDOWN - (current_time - last_time)))
|
|
return
|
|
end
|
|
last_flag_use[player_name] = current_time
|
|
|
|
local message = meta:get_string("message") or "Flag point"
|
|
local announcement = Announcement:new(user:get_player_name(), ANNOUNCE_RADIUS, message, ANNOUNCE_ICON, ANNOUNCE_TIMEOUT)
|
|
announcement.point = pointed_pos
|
|
announcement.announcer_pos = user:get_pos()
|
|
post_announcement(announcement) -- TODO maybe do not show the chat message?
|
|
return
|
|
end
|
|
|
|
|
|
minetest.register_tool(
|
|
"waypoint_announce:flag", {
|
|
description = "Announcement flag\n<place> to place the waypoint\n<sneak+place> to edit the message",
|
|
inventory_image = "waypoint_announce_flag.png",
|
|
wield_scale = {x = 1.5, y = 1.5, z = 1.0},
|
|
range = 2.0, -- TODO what's the good range?
|
|
on_place = function(itemstack, placer, pointed_thing)
|
|
return flag_use_callback(itemstack, placer, pointed_thing)
|
|
end,
|
|
on_secondary_use = function(itemstack, user, pointed_thing)
|
|
return flag_use_callback(itemstack, user, pointed_thing)
|
|
end,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
type = "shaped",
|
|
output = "waypoint_announce:flag",
|
|
recipe = {
|
|
{"default:stick", "wool:red", "wool:red"},
|
|
{"default:stick", "wool:red", "wool:red"},
|
|
{"default:stick", "", ""},
|
|
}
|
|
})
|
|
|
|
|
|
waypoint_announce = {
|
|
post_announcement = post_announcement,
|
|
-- table, use as:
|
|
-- hooks['modname'] = function(announcer_name, message, pointed_pos) end
|
|
post_announcement_hooks = {},
|
|
}
|
|
|
|
|
|
return waypoint_announce
|