commit fe768104598fdcbf85a3122eacec7ab54f304a63 Author: rainer Date: Thu Feb 12 12:41:24 2026 +0100 init, changed a few things for GEIGERnet-Server, added sounds for receiving RDS message diff --git a/.README.md.kate-swp b/.README.md.kate-swp new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..74eeb80 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright 2019 Serhii "techniX" Mozhaiskyi + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..37e508b --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +# Ham Radio + +![Ham Radio screenshot](screenshot.png?raw=true) + +This mod brings radio transmitters and receivers to the Minetest world. + +Dependencies: +``` +default +basic_materials? +technic? +digilines? +``` +Craft recipes depend of the mods installed. + +## Transmitter + +Craft a transmitter and place it in the world. Right click on transmitter to open configuration dialog, then set frequency and RDS message. +- Empty frequency turns transmitter off. +- Transmitter information is displayed as info text when player points at it. +- RDS message can be multiline. However, it is transmitted line by line. +- RDS message and frequency can be set via digiline. Also, you can read transmitter configuration via digiline too. + +## Beacon + +Beacon is a simplified transmitter. After placement it automatically tunes on a random unoccupied frequency from predefined range. Beacon frequency range is determined by `beacon_frequency` setting. +- Beacon frequency is displayed as info text when player points at it. + +## Receiver + +Handheld receiver is a wielded tool. + +- Left click opens configuration dialog to set frequency. Empty string turns receiver off. +- Shift + left click toggles reception of RDS messages. + +When receiver is tuned to a frequency where at least one transmitter is present, HUD signal meter bar shows signal power. The signal power depends on distance and direction to the transmitter. + +If RDS reception is toggled on, the RDS messages from all transmitters on this frequency are enqueued and will be send one by one as a chat messages to the player with 10 seconds interval. When RDS message queue becomes empty, it refills and starts over again. + +## Stationary Receiver + +Right click on receiver opens configuration window to set frequency. Receiver displays RDS messages as infotext in the same way as handheld receiver. It does not have signal power meter. +- You can operate the receiver via digiline in the same way as the transmitter. + +## Digiline + +```lua +-- channel "ham_radio_rds" accepts plain text +digiline.send('ham_radio_rds', 'new RDS message') + +-- get transmitter info +digiline.send('ham_radio', { command = 'get' }) +-- returns { frequency = 12345, rds_message = 'text' } + +-- set frequency +digiline.send('ham_radio', { command = 'set_frequency', value = '12345' }) +-- returns { update = 'frequency', success = true/false, message = errorMessage } + +-- set RDS message +digiline.send('ham_radio', { command = 'set_rds_message', value = 'new RDS message' }) +-- returns { update = 'rds_message', success = true } + +-- get receiver info +digiline.send('ham_radio_receiver', { command = 'get' }) +-- returns { frequency = 12345, rds_message = 'text' } + +-- set receiver frequency +digiline.send('ham_radio_receiver', { command = 'set_frequency', value = '12345' }) +-- returns { update = 'frequency', success = true/false, message = errorMessage } +``` + +## Config + +See `config.lua` to see current parameters. You can edit this file to change them. + +Default parameters: + - Frequency range: 0 - 9999999 + - Locked frequency range: 100000 - 9999999 + - Only one transmitter is allowed for the frequency in this range. + - Beacon frequency range: 1000000 - 99999999 + - Beacon frequency is auto-assigned from this range. + - Please note, this range overlaps with locked frequency range to ensure each beacon receives unique frequency. + - RDS interval: 10 seconds + - This setting affects handheld receivers only. The interval should be high enough to avoid spamming chat with repeated messages. + - RDS interval for stationary receiver is 5 seconds and can't be changed. + +## What's next? + +- Place beacons or transmitters anywhere in the world, give frequency to other players and let them search for them +- Pick a frequency which all players can use for their announcements to organize radio bulletin board +- Operate your transmitters with digiline to receive notification on radio +- ??? +- PROFIT + +## Author and license + +(c) techniX 2019 + +Source code: MIT + +Textures: CC BY-SA 3.0 + +Sounds: cut from "G32-20-Tuning Radio" by craigsmith, CC 0 diff --git a/beacon.lua b/beacon.lua new file mode 100644 index 0000000..2ff8fcc --- /dev/null +++ b/beacon.lua @@ -0,0 +1,41 @@ +minetest.register_node("ham_radio:beacon", { + description = "Radio Beacon", + tiles = { + "ham_radio_transmitter_top.png", + "ham_radio_transmitter_top.png", + "ham_radio_transmitter_side.png", + "ham_radio_transmitter_side.png", + "ham_radio_transmitter_side.png", + "ham_radio_beacon_front.png" + }, + groups = {cracky=2,oddly_breakable_by_hand=2}, + sounds = default.node_sound_metal_defaults(), + paramtype2 = "facedir", + drawtype = "nodebox", + paramtype = "light", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + }, + light_source = 3, + after_place_node = function(pos, placer) + local meta = minetest.get_meta(pos); + if minetest.is_player(placer) then + local name = placer:get_player_name() + meta:set_string('operated_by', name) + ham_radio.play_tuning_sound(placer) + end + meta:set_string("frequency", ham_radio.find_free_frequency(ham_radio.settings.beacon_frequency)) + ham_radio.transmitter_update_infotext(meta) + ham_radio.save_transmitter(pos, meta) + end, + can_dig = function(pos,player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + local name = player:get_player_name() + return inv:is_empty("main") and not minetest.is_protected(pos, name) + end, + after_dig_node = function(pos, oldnode, oldmetadata, player) + ham_radio.delete_transmitter(pos) + end, +}); diff --git a/config.lua b/config.lua new file mode 100644 index 0000000..1e59674 --- /dev/null +++ b/config.lua @@ -0,0 +1,32 @@ +ham_radio.settings = { + -- color of RDS messages + rds_color = '#4863A0', + -- interval between RDS messages (seconds) + rds_interval = 10, + -- receiver hud position + hud_pos = { x = 0.5, y = 0.8 }, + -- hud message color + hud_color = { + active = "0xFCAD00", + inactive = "0x999999" + }, + -- radio frequency range + frequency = { + min = 100000, + max = 999999 + }, + -- range where only one transmitter is permitted + locked_frequency = { + min = 200000, + max = 999999 + }, + -- sub-range of locked frequency range + beacon_frequency = { + min = 500000, + max = 999999 + }, + -- digiline config + digiline_channel = "ham_radio", + digiline_rds_channel = "ham_radio_rds", + digiline_receiver_channel = "ham_radio_receiver", +} diff --git a/craft.lua b/craft.lua new file mode 100644 index 0000000..849d892 --- /dev/null +++ b/craft.lua @@ -0,0 +1,67 @@ +local circuit = 'default:mese_crystal' +local body = 'default:steel_ingot' +local wires = 'default:copper_ingot' +local glass = 'default:glass' +local antenna = wires +local battery = 'default:mese_crystal' + +if minetest.get_modpath("basic_materials") then + circuit = 'basic_materials:ic' + body = 'basic_materials:plastic_sheet' + wires = 'basic_materials:copper_wire' + antenna = wires + battery = 'basic_materials:energy_crystal_simple' +end + +if minetest.get_modpath("technic") then + antenna = 'technic:copper_coil' + battery = 'technic:battery' +end + +minetest.register_craftitem("ham_radio:circuit", { + description = "Radio Circuit", + inventory_image = "ham_radio_circuit.png", +}) + +minetest.register_craft({ + output = "ham_radio:circuit", + recipe = { + {circuit, wires, circuit}, + {body, battery, body}, + } +}) + +minetest.register_craft({ + output = "ham_radio:handheld_receiver", + recipe = { + {'', antenna, ''}, + {'','ham_radio:circuit', ''}, + {body, body, body} + } +}) + +minetest.register_craft({ + output = "ham_radio:receiver", + recipe = { + {body, antenna, body}, + {glass,'ham_radio:circuit', glass}, + {body, body, body} + } +}) + +minetest.register_craft({ + output = "ham_radio:transmitter", + recipe = { + {wires, antenna, wires}, + {glass, 'ham_radio:circuit', glass}, + {body, body, body} + } +}) + +minetest.register_craft({ + output = "ham_radio:beacon", + recipe = { + {antenna, body}, + {wires, 'ham_radio:circuit'}, + } +}) diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..4c36019 --- /dev/null +++ b/depends.txt @@ -0,0 +1,4 @@ +default +basic_materials? +technic? +digilines? \ No newline at end of file diff --git a/digiline.lua b/digiline.lua new file mode 100644 index 0000000..7569802 --- /dev/null +++ b/digiline.lua @@ -0,0 +1,93 @@ +ham_radio.digiline_effector_transmitter = function(pos, _, channel, msg) + -- static channels for transmitter + local command_channel = ham_radio.settings.digiline_channel + local rds_channel = ham_radio.settings.digiline_rds_channel + + if channel ~= command_channel and channel ~= rds_channel then + return + end + + local meta = minetest.get_meta(pos) + + -- RDS channel - text message + if channel == rds_channel then + if type(msg) == "string" then + meta:set_string("rds_message", msg) + ham_radio.transmitter_update_infotext(meta) + ham_radio.save_transmitter(pos, meta) + end + return + end + + -- command channel + + if type(msg) ~= "table" then + return + end + + if msg.command == "get" then + digilines.receptor_send(pos, digilines.rules.default, command_channel, { + frequency = meta:get_string("frequency"), + rds_message = meta:get_string("rds_message"), + }) + + elseif msg.command == "frequency" or msg.command == "set_frequency" then + local new_frequency = msg.value + local validate = ham_radio.validate_frequency(new_frequency) + if validate.result then + meta:set_string("frequency", new_frequency) + ham_radio.transmitter_update_infotext(meta) + ham_radio.save_transmitter(pos, meta) + end + digilines.receptor_send(pos, digilines.rules.default, command_channel, { + update = 'frequency', + success = validate.result, + message = validate.message + }) + + elseif msg.command == "rds" or msg.command == "message" or msg.command == "rds_message" or msg.command == "set_rds_message" then + meta:set_string("rds_message", msg.value) + ham_radio.transmitter_update_infotext(meta) + ham_radio.save_transmitter(pos, meta) + digilines.receptor_send(pos, digilines.rules.default, command_channel, { + update = 'rds_message', + success = true + }) + end +end + + +ham_radio.digiline_effector_receiver = function(pos, _, channel, msg) + -- static channel for receiver + local command_channel = ham_radio.settings.digiline_receiver_channel + + if channel ~= command_channel or type(msg) ~= "table" then + return + end + + local meta = minetest.get_meta(pos) + + if msg.command == "get" then + digilines.receptor_send(pos, digilines.rules.default, command_channel, { + frequency = meta:get_string("frequency"), + rds_message = meta:get_string("rds_message"), + }) + + elseif msg.command == "frequency" or msg.command == "set_frequency" then + local new_frequency = msg.value + local validate = ham_radio.validate_frequency(new_frequency, true) + if validate.result then + meta:set_string("frequency", new_frequency) + -- load new RDS messages + local poshash = minetest.pos_to_string(pos, 0) + ham_radio.receiver_rds[poshash] = ham_radio.get_rds_messages(new_frequency, true) + ham_radio.get_next_rds_message(poshash, meta) + end + digilines.receptor_send(pos, digilines.rules.default, command_channel, { + update = 'frequency', + success = validate.result, + message = validate.message + }) + end + +end \ No newline at end of file diff --git a/helpers.lua b/helpers.lua new file mode 100644 index 0000000..65f1bb4 --- /dev/null +++ b/helpers.lua @@ -0,0 +1,43 @@ +function ham_radio.validate_frequency(frequency, is_receiver) + if frequency == "" then + return { result = true, message = '' } -- empty frequency is allowed to disable transmitter/receiver + end + local transmission_is_allowed = true + local num_freq = tonumber(frequency) + local freq = tostring(num_freq) + if is_receiver == nil and next(ham_radio.find_transmitters(frequency)) then + if num_freq >= ham_radio.settings.locked_frequency.min + and num_freq <= ham_radio.settings.locked_frequency.max then + -- transmitter is in locked frequency range + transmission_is_allowed = false + end + end + local result = true + local message = '' + if freq ~= frequency or num_freq ~= math.floor(num_freq) then + result = false + message = 'Error: invalid frequency value.' + elseif num_freq == nil then + result = false + message = 'Error: frequency should be numeric.' + elseif num_freq < ham_radio.settings.frequency.min or num_freq > ham_radio.settings.frequency.max then + result = false + message = 'Error: frequency is out of range.' + elseif transmission_is_allowed == false then + result = false + message = 'Error: frequency is occupied by other transmitter.' + end + return { result = result, message = message } +end + +function ham_radio.find_free_frequency(range) + local frequency = -1 + while frequency == -1 do + frequency = tostring(math.floor(math.random(range.min, range.max))); + local are_there_transmitters = ham_radio.find_transmitters(frequency) + if next(are_there_transmitters) then + frequency = -1 + end + end + return frequency +end diff --git a/hud.lua b/hud.lua new file mode 100644 index 0000000..d1e8aac --- /dev/null +++ b/hud.lua @@ -0,0 +1,128 @@ + +function ham_radio.toggle_hud(player) + local name = player:get_player_name() + local item = player:get_wielded_item() + + -- remove hud and RDS if user does not wield a receiver + if item:get_name() ~= "ham_radio:handheld_receiver" then + if ham_radio.is_receiver_wielded[name] then + for hud_id, hud_handler in pairs(ham_radio.playerhuds[name]) do + player:hud_remove(hud_handler) + end + ham_radio.playerhuds[name] = nil + ham_radio.is_receiver_wielded[name] = false + ham_radio.player_rds[name] = nil + end + return false + end + + -- if hud is already enabled, pass + if ham_radio.is_receiver_wielded[name] then + return true + end + + -- create hud + ham_radio.is_receiver_wielded[name] = true + + local hud_pos = ham_radio.settings.hud_pos + local hud_color = ham_radio.settings.hud_color + + ham_radio.playerhuds[name] = { + background = player:hud_add({ + hud_elem_type = "image", + position = hud_pos, + offset = { x = -250, y = 20 }, + text = "ham_radio_hud_bg.png", + scale = { x = 2, y = 2 }, + alignment = { x = 1, y = 0 }, + }), + frequency = player:hud_add({ + hud_elem_type = "text", + text = "", + position = hud_pos, + offset = { x = -220, y = 5 }, + alignment = { x = 1, y = 0}, + number = hud_color.inactive, + scale= { x = 100, y = 20 }, + }), + rds = player:hud_add({ + hud_elem_type = "text", + text = "", + position = hud_pos, + offset = { x = 220, y = 5 }, + alignment = { x = -1, y = 0}, + number = hud_color.inactive, + scale= { x = 100, y = 20 }, + }), + signal_meter = player:hud_add({ + hud_elem_type = "image", + position = hud_pos, + offset = { x = -220, y = 35 }, + text = "ham_radio_hud_indicator_empty.png", + scale = { x = 2, y = 1 }, + alignment = { x = 1, y = 0 }, + }), + signal_level = player:hud_add({ + hud_elem_type = "image", + position = hud_pos, + offset = { x = -220, y = 35 }, + text = "ham_radio_hud_indicator_full.png", + scale = { x = 0, y = 1 }, + alignment = { x = 1, y = 0 }, + }) + } + return true +end + + +function ham_radio:update_hud_display(player) + + if not ham_radio.toggle_hud(player) then + return + end + + local signal_power = 0 + local name = player:get_player_name() + local meta = player:get_wielded_item():get_meta() + local frequency = meta:get_string("frequency") + + if frequency ~= nil and frequency ~= "" then + local transmitters = self.find_transmitters(frequency) + for position, transmitter in pairs(transmitters) do + local transmitter_signal = self:locate_transmitter(player, minetest.string_to_pos(position)) + if transmitter_signal > signal_power then + -- use max power from transmitters nearby + signal_power = transmitter_signal + end + end + end + + local hud_color = ham_radio.settings.hud_color + + -- update frequency hud + local frequency_text = "FQ ---" + local frequency_color = hud_color.inactive + if frequency ~= "" then + frequency_text = "FQ "..frequency + frequency_color = hud_color.active + end + player:hud_change(self.playerhuds[name].frequency, "text", frequency_text) + player:hud_change(self.playerhuds[name].frequency, "number", frequency_color) + + -- update RDS hud + local rds_text = "RDS off" + local rds_color = hud_color.inactive + if meta:get_string("rds_disabled") == "" then + rds_text = "RDS ON" + rds_color = hud_color.active + end + player:hud_change(self.playerhuds[name].rds, "text", rds_text) + player:hud_change(self.playerhuds[name].rds, "number", rds_color) + + -- update signal level hud + player:hud_change( + self.playerhuds[name].signal_level, + "scale", + { x = signal_power/50 or 0.1, y = 1 } -- x scale should be 0-2 + ) +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..3a3f336 --- /dev/null +++ b/init.lua @@ -0,0 +1,101 @@ +local modpath = minetest.get_modpath("ham_radio") +local mod_storage = minetest.get_mod_storage() + +ham_radio = rawget(_G, "ham_radio") or {} + +ham_radio = { + playerhuds = {}, + player_rds = {}, + receiver_rds = {}, + is_receiver_wielded = {}, + transmitters = {}, +} + +function ham_radio.find_transmitters(frequency) + local transmitter_list = {} + local all_transmitters = mod_storage:to_table().fields + for key, transmitter_data in pairs(all_transmitters) do + local transmitter = minetest.parse_json(transmitter_data) + if transmitter.frequency == frequency then + transmitter_list[key] = transmitter + end + end + return transmitter_list +end + +function ham_radio.save_transmitter(pos, meta) + local transmitter_properties = { + frequency = meta:get_string("frequency"), + rds_message = meta:get_string("rds_message"), + operated_by = meta:get_string("operated_by") + } + local key = minetest.pos_to_string(pos, 0) + mod_storage:set_string(key, minetest.write_json(transmitter_properties)) -- storage +end + +function ham_radio.delete_transmitter(pos) + local key = minetest.pos_to_string(pos, 0) + mod_storage:set_string(key, '') -- storage +end + +function ham_radio.play_tuning_sound(player) + minetest.sound_play( + {name = "ham_radio_tuning"..math.random(1,5)}, + {to_player = player:get_player_name()} + ) +end + +function ham_radio.play_rds_sound(player) + minetest.sound_play( + {name = "ham_radio_rds_msg"}, + {to_player = player:get_player_name()} + ) +end + +function ham_radio.errormsg(player, message) + minetest.chat_send_player(player:get_player_name(), minetest.colorize("#FCAD00", message)) +end + +dofile(modpath.."/config.lua") + +dofile(modpath.."/helpers.lua") +dofile(modpath.."/craft.lua") +dofile(modpath.."/digiline.lua") +dofile(modpath.."/transmitter.lua") +dofile(modpath.."/receiver.lua") +dofile(modpath.."/beacon.lua") +dofile(modpath.."/rds.lua") +dofile(modpath.."/receiver_station.lua") +dofile(modpath.."/hud.lua") + +-- globals + +minetest.register_on_newplayer(ham_radio.toggle_hud) +minetest.register_on_joinplayer(ham_radio.toggle_hud) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + ham_radio.is_receiver_wielded[name] = false + ham_radio.playerhuds[name] = nil +end) + +local updatetimer = 0 +local rds_timer = 0 +minetest.register_globalstep(function(dtime) + updatetimer = updatetimer + dtime + rds_timer = rds_timer + dtime + if updatetimer > 0.1 then + local players = minetest.get_connected_players() + for i=1, #players do + ham_radio:update_hud_display(players[i]) + end + updatetimer = 0 + -- rds update timer + if rds_timer > ham_radio.settings.rds_interval then + for i=1, #players do + ham_radio:update_rds(players[i]) + end + rds_timer = 0 + end + end +end) diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..c4a667c --- /dev/null +++ b/mod.conf @@ -0,0 +1,7 @@ +name = ham_radio +description = Adds radio transmitters, beacons, and receivers. +depends = default +optional_depends = basic_materials, technic, digilines +release = 5373 +author = techniX +title = Ham Radio diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 0000000..2252ac6 --- /dev/null +++ b/patches/README.md @@ -0,0 +1,3 @@ +# jumpdrive_compat + +Compat patch for jumpdrive mod to make transmitters and beacons work correctly on jumpdrive ships. diff --git a/patches/jumpdrive_compat/compat.lua.patch b/patches/jumpdrive_compat/compat.lua.patch new file mode 100644 index 0000000..fe20b2e --- /dev/null +++ b/patches/jumpdrive_compat/compat.lua.patch @@ -0,0 +1,28 @@ +--- compat.lua 2019-12-09 17:28:52.811852063 +0000 ++++ compat.lua 2019-12-09 17:29:24.255956113 +0000 +@@ -8,6 +8,7 @@ + local has_pipeworks_mod = minetest.get_modpath("pipeworks") + local has_beds_mod = minetest.get_modpath("beds") + local has_ropes_mod = minetest.get_modpath("ropes") ++local has_ham_radio_mod = minetest.get_modpath("ham_radio") + + dofile(MP.."/compat/travelnet.lua") + dofile(MP.."/compat/locator.lua") +@@ -18,6 +19,7 @@ + dofile(MP.."/compat/telemosaic.lua") + dofile(MP.."/compat/beds.lua") + dofile(MP.."/compat/ropes.lua") ++dofile(MP.."/compat/ham_radio.lua") + + if has_pipeworks_mod then + dofile(MP.."/compat/teleporttube.lua") +@@ -37,6 +39,9 @@ + elseif name == "telemosaic:beacon" or name == "telemosaic:beacon_protected" then + jumpdrive.telemosaic_compat(source_pos, target_pos) + ++ elseif (name == "ham_radio:transmitter" or name == "ham_radio:beacon") and has_ham_radio_mod then ++ jumpdrive.ham_radio_compat(source_pos, target_pos) ++ + end + end + diff --git a/patches/jumpdrive_compat/ham_radio.lua b/patches/jumpdrive_compat/ham_radio.lua new file mode 100644 index 0000000..dc8897f --- /dev/null +++ b/patches/jumpdrive_compat/ham_radio.lua @@ -0,0 +1,7 @@ + +jumpdrive.ham_radio_compat = function(from, to) + local meta = minetest.get_meta(to) + ham_radio.delete_transmitter(from) + ham_radio.save_transmitter(to, meta) +end + diff --git a/rds.lua b/rds.lua new file mode 100644 index 0000000..a1cc918 --- /dev/null +++ b/rds.lua @@ -0,0 +1,64 @@ +function ham_radio.get_rds_messages(frequency, is_receiver_station) + local transmitters = ham_radio.find_transmitters(frequency) + local rds_messages = {} + for position, transmitter in pairs(transmitters) do + if transmitter.rds_message ~= "" and transmitter.rds_message ~= nil then + for rds_message_line in transmitter.rds_message:gmatch("[^\n]+") do + -- construct message + local message = table.concat({ + '[ Radio ] ', + rds_message_line, + }, "") + if is_receiver_station then + message = table.concat({ + '[ Radio ] ', + rds_message_line + }, "") + end + table.insert(rds_messages, 1, message) + end + end + end + return rds_messages +end + + +function ham_radio:update_rds(player) + local name = player:get_player_name() + local item = player:get_wielded_item() + + if item:get_name() ~= "ham_radio:handheld_receiver" then + return + end + + local meta = item:get_meta() + local frequency = meta:get_string("frequency") + local rds_disabled = meta:get_string("rds_disabled") + + if frequency == "" then + return + end + + if rds_disabled == "true" then + -- disabled receiving RDS messages + ham_radio.player_rds[name] = nil + return + end + + if ham_radio.player_rds[name] == nil then + ham_radio.player_rds[name] = ham_radio.get_rds_messages(frequency) + end + + local message = table.remove(ham_radio.player_rds[name]) + if message ~= nil then + minetest.chat_send_player(player:get_player_name(), minetest.colorize(ham_radio.settings.rds_color, message)) + + -- play radio sound + ham_radio.play_rds_sound(player) + + -- when all RDS messages are shown, reload them again + if not next(ham_radio.player_rds[name]) then + ham_radio.player_rds[name] = ham_radio.get_rds_messages(frequency) + end + end +end diff --git a/receiver.lua b/receiver.lua new file mode 100644 index 0000000..cd248cc --- /dev/null +++ b/receiver.lua @@ -0,0 +1,86 @@ +minetest.register_tool("ham_radio:handheld_receiver", { + description = "Handheld Radio Receiver", + wield_image = "ham_radio_receiver_handheld.png", + inventory_image = "ham_radio_receiver_handheld.png", + groups = { disable_repair = 1 }, + -- left click - change frequency + on_use = function(itemstack, user, pointed_thing) + local keys = user:get_player_control() + local meta = itemstack:get_meta() + if keys.sneak then + -- left click with shift - RDS on/off + local is_rds_disabled = meta:get_string("rds_disabled") + if is_rds_disabled == "" then + meta:set_string("rds_disabled", "true") + else + meta:set_string("rds_disabled", "") + end + return itemstack + end + local frequency = meta:get_string("frequency") + minetest.show_formspec(user:get_player_name(), "ham_radio:configure_handheld_receiver", + table.concat({ + "size[3,4]", + "image[1,0;1,1;ham_radio_receiver_handheld.png]", + "field[0.25,2;3,1;frequency;Frequency;",tostring(frequency),"]", + "tooltip[frequency;Integer number ", + ham_radio.settings.frequency.min,"-", + ham_radio.settings.frequency.max, "]", + "button_exit[0,3.5;3,1;;Done]" + },'') + ) + return itemstack + end +}) + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "ham_radio:configure_handheld_receiver" or not minetest.is_player(player) then + return false + end + if fields.frequency == nil then + -- form is not sent + return + end + local is_frequency_valid = ham_radio.validate_frequency(fields.frequency, true) + if is_frequency_valid.result == false then + ham_radio.errormsg(player, is_frequency_valid.message) + return false + end + local item = player:get_wielded_item() + local meta = item:get_meta() + meta:set_string("frequency", fields.frequency) + -- play radio sound + ham_radio.play_tuning_sound(player) + -- replace wielded item with new metadata + player:set_wielded_item(item) + -- reset rds messages + ham_radio.player_rds[player:get_player_name()] = nil + return true +end) + + +function ham_radio:locate_transmitter(player, transmitter_pos) + local player_pos = player:get_pos() + local player_look_vector = player:get_look_dir() + local player_direction = vector.add(player_pos, player_look_vector) + + local coeff = 0.9 + local distance_to_target = 0 + + local distance = vector.distance(player_pos, transmitter_pos) + if distance < 3 then + distance_to_target = 100 + coeff = 0.99 + else + distance_to_target = -0.0000000001*math.pow(distance,3)+0.00000145*math.pow(distance,2)-0.03*distance+100 + if distance_to_target < 3 then + distance_to_target = 3 + end + end + + local distance2 = vector.distance(player_direction, transmitter_pos) + local signal_power = distance - distance2; + + -- 0-100 + return distance_to_target * coeff + distance_to_target * (1 - coeff) * signal_power; +end diff --git a/receiver_station.lua b/receiver_station.lua new file mode 100644 index 0000000..1e36fa9 --- /dev/null +++ b/receiver_station.lua @@ -0,0 +1,136 @@ +ham_radio.receiver_update_infotext = function(meta, pos) + local frequency = meta:get_string("frequency") + local rds_message = meta:get_string("rds_message") + + local display_freq = (frequency ~= "") and frequency or "---" + local infotext = "Radio Receiver [" .. display_freq .. " kHz]" + + + if rds_message ~= "" then + infotext = infotext .. "\n" .. rds_message + + -- play rds receiving sound + minetest.sound_play("ham_radio_rds_msg", { + pos = pos, + max_hear_distance = 8, + gain = 0.8, +}) + + end + meta:set_string("infotext", infotext) +end + +minetest.register_node("ham_radio:receiver", { + description = "Ham Radio Receiver", + tiles = { + "ham_radio_receiver_top.png", + "ham_radio_receiver_top.png", + "ham_radio_receiver_side.png", + "ham_radio_receiver_side.png", + "ham_radio_receiver_side.png", + "ham_radio_receiver_front.png" + }, + groups = {cracky=2,oddly_breakable_by_hand=2}, + sounds = default.node_sound_metal_defaults(), + paramtype2 = "facedir", + drawtype = "nodebox", + paramtype = "light", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5}, + }, + light_source = 3, + after_place_node = function(pos, placer) + local meta = minetest.get_meta(pos); + local name = placer:get_player_name() + meta:set_string("formspec", + table.concat({ + "size[7,4]", + "image[0,0;1,1;ham_radio_receiver_front.png]", + "field[0.25,2;7,1;frequency;Frequency;${frequency}]", + "tooltip[frequency;Integer number ", + ham_radio.settings.frequency.min,"-", + ham_radio.settings.frequency.max, "]", + "button_exit[2,3.5;3,1;;Done]" + },'') + ) + meta:set_string("infotext", 'Radio Receiver') + end, + on_receive_fields = function(pos, formname, fields, sender) + if not minetest.is_player(sender) then + return + end + + if ( + fields.quit ~= "true" + or minetest.is_protected(pos, sender:get_player_name()) + ) then + return + end + + if fields.frequency ~= nil then + local is_frequency_valid = ham_radio.validate_frequency(fields.frequency, true) + if is_frequency_valid.result == false then + ham_radio.errormsg(sender, is_frequency_valid.message) + else + local meta = minetest.get_meta(pos) + meta:set_string("frequency", fields.frequency) + meta:set_string("rds_message", "") + ham_radio.reset_receiver(pos) + ham_radio.receiver_update_infotext(meta) + ham_radio.play_tuning_sound(sender) + end + end + end, + can_dig = function(pos,player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + local name = player:get_player_name() + return inv:is_empty("main") and not minetest.is_protected(pos, name) + end, + -- digiline + digiline = { + receptor = {action = function() end}, + effector = { + action = ham_radio.digiline_effector_receiver + }, + }, +}); + +ham_radio.reset_receiver = function (pos) + local poshash = minetest.pos_to_string(pos, 0) + ham_radio.receiver_rds[poshash] = nil +end + +minetest.register_abm( + { + label = "Listen Ham Radion Broadcast", + nodenames = {"ham_radio:receiver"}, + interval = 10, + chance = 1, + catch_up = false, + action = function(pos, node) + local meta = minetest.get_meta(pos) + local frequency = meta:get_string("frequency") + + if frequency == "" then + return + end + + local poshash = minetest.pos_to_string(pos, 0) + if ham_radio.receiver_rds[poshash] == nil or not next(ham_radio.receiver_rds[poshash]) then + -- when all RDS messages are shown, reload them again + ham_radio.receiver_rds[poshash] = ham_radio.get_rds_messages(frequency, true) + end + ham_radio.get_next_rds_message(poshash, meta, pos) + end + } +); + +ham_radio.get_next_rds_message = function (poshash, meta, pos) + local message = table.remove(ham_radio.receiver_rds[poshash]) + if message ~= nil then + meta:set_string('rds_message', message) + ham_radio.receiver_update_infotext(meta, pos) + end +end diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..da2fc67 Binary files /dev/null and b/screenshot.png differ diff --git a/textures/ham_radio_beacon_front.png b/textures/ham_radio_beacon_front.png new file mode 100644 index 0000000..432db38 Binary files /dev/null and b/textures/ham_radio_beacon_front.png differ diff --git a/textures/ham_radio_circuit.png b/textures/ham_radio_circuit.png new file mode 100644 index 0000000..541eced Binary files /dev/null and b/textures/ham_radio_circuit.png differ diff --git a/textures/ham_radio_hud_bg.png b/textures/ham_radio_hud_bg.png new file mode 100644 index 0000000..9ed15cc Binary files /dev/null and b/textures/ham_radio_hud_bg.png differ diff --git a/textures/ham_radio_hud_indicator_empty.png b/textures/ham_radio_hud_indicator_empty.png new file mode 100644 index 0000000..22cddb7 Binary files /dev/null and b/textures/ham_radio_hud_indicator_empty.png differ diff --git a/textures/ham_radio_hud_indicator_full.png b/textures/ham_radio_hud_indicator_full.png new file mode 100644 index 0000000..8a05e88 Binary files /dev/null and b/textures/ham_radio_hud_indicator_full.png differ diff --git a/textures/ham_radio_rds_msg.ogg b/textures/ham_radio_rds_msg.ogg new file mode 100644 index 0000000..72d9fad Binary files /dev/null and b/textures/ham_radio_rds_msg.ogg differ diff --git a/textures/ham_radio_receiver_front.png b/textures/ham_radio_receiver_front.png new file mode 100644 index 0000000..6f79ff5 Binary files /dev/null and b/textures/ham_radio_receiver_front.png differ diff --git a/textures/ham_radio_receiver_handheld.png b/textures/ham_radio_receiver_handheld.png new file mode 100644 index 0000000..46d107c Binary files /dev/null and b/textures/ham_radio_receiver_handheld.png differ diff --git a/textures/ham_radio_receiver_side.png b/textures/ham_radio_receiver_side.png new file mode 100644 index 0000000..8505c29 Binary files /dev/null and b/textures/ham_radio_receiver_side.png differ diff --git a/textures/ham_radio_receiver_top.png b/textures/ham_radio_receiver_top.png new file mode 100644 index 0000000..3654319 Binary files /dev/null and b/textures/ham_radio_receiver_top.png differ diff --git a/textures/ham_radio_transmitter_front.png b/textures/ham_radio_transmitter_front.png new file mode 100644 index 0000000..4df3a8b Binary files /dev/null and b/textures/ham_radio_transmitter_front.png differ diff --git a/textures/ham_radio_transmitter_side.png b/textures/ham_radio_transmitter_side.png new file mode 100644 index 0000000..f79208f Binary files /dev/null and b/textures/ham_radio_transmitter_side.png differ diff --git a/textures/ham_radio_transmitter_top.png b/textures/ham_radio_transmitter_top.png new file mode 100644 index 0000000..1788ed3 Binary files /dev/null and b/textures/ham_radio_transmitter_top.png differ diff --git a/textures/ham_radio_tuning1.ogg b/textures/ham_radio_tuning1.ogg new file mode 100644 index 0000000..7bddeb6 Binary files /dev/null and b/textures/ham_radio_tuning1.ogg differ diff --git a/textures/ham_radio_tuning2.ogg b/textures/ham_radio_tuning2.ogg new file mode 100644 index 0000000..c12e1f9 Binary files /dev/null and b/textures/ham_radio_tuning2.ogg differ diff --git a/textures/ham_radio_tuning3.ogg b/textures/ham_radio_tuning3.ogg new file mode 100644 index 0000000..888f1bf Binary files /dev/null and b/textures/ham_radio_tuning3.ogg differ diff --git a/textures/ham_radio_tuning4.ogg b/textures/ham_radio_tuning4.ogg new file mode 100644 index 0000000..521cd5d Binary files /dev/null and b/textures/ham_radio_tuning4.ogg differ diff --git a/textures/ham_radio_tuning5.ogg b/textures/ham_radio_tuning5.ogg new file mode 100644 index 0000000..e0ef0d3 Binary files /dev/null and b/textures/ham_radio_tuning5.ogg differ diff --git a/transmitter.lua b/transmitter.lua new file mode 100644 index 0000000..4a00f0f --- /dev/null +++ b/transmitter.lua @@ -0,0 +1,109 @@ + +ham_radio.transmitter_update_infotext = function(meta) + local operated_by = meta:get_string("operated_by") + local frequency = meta:get_string("frequency") + local rds_message = meta:get_string("rds_message") + if frequency == "" then + frequency = "--" + rds_message = "" + end + local infotext = { + 'Radio Transmitter\n', + 'Operated by: ', operated_by, '\n', + 'Frequency: ', frequency + } + if rds_message ~= "" then + table.insert(infotext, '\nRDS message: "') + table.insert(infotext, rds_message) + table.insert(infotext, '"') + end + meta:set_string("infotext", table.concat(infotext, '')) +end + +minetest.register_node("ham_radio:transmitter", { + description = "Radio Transmitter", + tiles = { + "ham_radio_transmitter_top.png", + "ham_radio_transmitter_top.png", + "ham_radio_transmitter_side.png", + "ham_radio_transmitter_side.png", + "ham_radio_transmitter_side.png", + "ham_radio_transmitter_front.png" + }, + groups = {cracky=2,oddly_breakable_by_hand=2}, + sounds = default.node_sound_metal_defaults(), + paramtype2 = "facedir", + light_source = 3, + after_place_node = function(pos, placer) + local meta = minetest.get_meta(pos); + local name = placer:get_player_name() + meta:set_string('operated_by', name) + meta:set_string('rds_message', "") + meta:set_string("formspec", + table.concat({ + "size[7,8]", + "image[0,0;1,1;ham_radio_transmitter_front.png]", + "label[1,0;Transmitter operated by: ",minetest.formspec_escape(name),"]", + "field[0.25,2;7,1;frequency;Frequency;${frequency}]", + "tooltip[frequency;Integer number ", + ham_radio.settings.frequency.min,"-", + ham_radio.settings.frequency.max, "]", + "textarea[0.25,3.5;7,4;rds_message;RDS message;${rds_message}]", + "button_exit[2,7.5;3,1;;Done]" + },'') + ) + ham_radio.transmitter_update_infotext(meta) + end, + on_receive_fields = function(pos, formname, fields, sender) + if not minetest.is_player(sender) then + return + end + + if ( + fields.quit ~= "true" + or minetest.is_protected(pos, sender:get_player_name()) + ) then + return + end + + local meta = minetest.get_meta(pos) + local transmitter_is_updated = false + + if fields.frequency ~= nil and fields.frequency ~= meta:get_string("frequency") then + local is_frequency_valid = ham_radio.validate_frequency(fields.frequency) + if is_frequency_valid.result == false then + ham_radio.errormsg(sender, is_frequency_valid.message) + else + meta:set_string("frequency", fields.frequency) + transmitter_is_updated = true + end + end + + if fields.rds_message ~= nil and fields.rds_message ~= meta:get_string("rds_message") then + meta:set_string("rds_message", fields.rds_message) + transmitter_is_updated = true + end + + if transmitter_is_updated then + ham_radio.transmitter_update_infotext(meta) + ham_radio.save_transmitter(pos, meta) + ham_radio.play_tuning_sound(sender) + end + end, + can_dig = function(pos,player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + local name = player:get_player_name() + return inv:is_empty("main") and not minetest.is_protected(pos, name) + end, + after_dig_node = function(pos, oldnode, oldmetadata, player) + ham_radio.delete_transmitter(pos) + end, + -- digiline + digiline = { + receptor = {action = function() end}, + effector = { + action = ham_radio.digiline_effector_transmitter + }, + }, +});