Encoder - A tool to output morse code!

Introduction

Presenting, Encoder! Encoder is a tool that you can use to signal Morse on a part. It isn't too optimized but it should work for most games.

Documentation

To setup the module, get it into your game and then require it. After that, do Encoder.new([YourPart]).

SayMessageInEncoded

First parameter is the message, second parameter is whether you want the signal to be looped or not.

Encoder:SayMessageInEncoded("hello", true)
SayEncodedCharacter

This is used to signal a letter. The parameter is a string of morse (1 = short, 2 = long)

Encoder:SayEncodedCharacter("1111")
5 Likes

Interesting idea but I have some issues

If you set looping to true it gives the following error when looping:

Workspace.MorseEncoder:44: attempt to get length of a nil value

And if you set looping to false it doesn’t run at all.

Additionally, it sets the part back to medium stone grey no matter what colour it was before and it’d be nice if you could set a parameter for the target colour.

It’s also a little confusing as to the difference between SayMessageInEncoded and SayEncodedCharacter. If you provided the outputs in the main message it’d be easier to understand

I wouldn’t really recommend using morse code as a feature in most games because it’s kind of annoying since 99.9% of people don’t know it and have to use an online tool [look at what happened in the Ready Player One event], I guess it’d be cool as an extra secret message somewhere though.
Thanks!

Hello! Thanks for your feedback.

I can’t quite understand what you’re saying here. Can you elaborate?

Hello!

I’ve made some changes to the module and I’d like you to test it out to see if your problems are solved.

I thought this was a really cool idea, so I wrote my own version of this module. I read it over half-a-million times and I’m tired of looking at it.

Environment:
MorseEncoderEnvironment.rbxl (36.5 KB)

Module Code
-- MorseEncoder.lua
local characterToMorse = require(script.CharacterMorseMap)

local MorseEncoder = {}
MorseEncoder.__index = MorseEncoder

function MorseEncoder.new(toggleFunc, timeUnit)
	local self = {
		Toggle = toggleFunc,
		TimeUnit = 0.1
	}
	
	setmetatable(self, MorseEncoder)
	
	return self
end

local function playEncodedCharacter(self, encodedCharacter)
	if encodedCharacter == nil then
		task.wait(self.TimeUnit * 2)
	else
		for _, duration in ipairs(encodedCharacter) do
			self.Toggle(true)
			task.wait(self.TimeUnit * duration)
			self.Toggle(false)
			task.wait(self.TimeUnit)
		end
	end
	
	task.wait(self.TimeUnit * 2)
end

function MorseEncoder:Play(message)
	local lowered = string.lower(message)
	
	if not self:CanConvert(message) then
		error("Message cannot be converted due to one or more extraneous characters")
	end
	
	for i = 1, string.len(message) do
		local letter = string.sub(lowered, i, i)
		
		local encodedCharacter = characterToMorse[letter]
		playEncodedCharacter(self, encodedCharacter)
	end
end

function MorseEncoder:CanConvert(message)
	local lowered = string.lower(message)

	for i = 1, string.len(message) do
		local letter = string.sub(lowered, i, i)

		if characterToMorse[letter] == nil and string.match(letter, "%S") then
			return false
		end
	end

	return true
end

return MorseEncoder
-- MorseEncoder/CharacterMorseMap.lua
return {
	-- letters
	["a"] = {1, 3},
	["b"] = {3, 1, 1, 1},
	["c"] = {3, 1, 3, 1},
	["d"] = {3, 1, 1},
	["e"] = {1},
	["f"] = {1, 1, 3, 1},
	["g"] = {3, 3, 1},
	["h"] = {1, 1, 1, 1},
	["i"] = {1, 1},
	["j"] = {1, 3, 3, 3},
	["k"] = {3, 1, 3},
	["l"] = {1, 3, 1, 1},
	["m"] = {3, 3},
	["n"] = {3, 1},
	["o"] = {3, 3, 3},
	["p"] = {1, 3, 3, 1},
	["q"] = {3, 3, 1, 3},
	["r"] = {1, 3, 1},
	["s"] = {1, 1, 1},
	["t"] = {3},
	["u"] = {3, 3, 1},
	["v"] = {1, 1, 1, 3},
	["w"] = {1, 3, 3},
	["x"] = {3, 1, 1, 3},
	["y"] = {3, 1, 3, 3},
	["z"] = {3, 3, 1, 1},
	
	-- numbers
	["1"] = {1, 3, 3, 3, 3},
	["2"] = {1, 1, 3, 3, 3},
	["3"] = {1, 1, 1, 3, 3},
	["4"] = {1, 1, 1, 1, 3},
	["5"] = {1, 1, 1, 1, 1},
	["6"] = {3, 1, 1, 1, 1},
	["7"] = {3, 3, 1, 1, 1},
	["8"] = {3, 3, 3, 1, 1},
	["9"] = {3, 3, 3, 3, 1},
	["0"] = {3, 3, 3, 3, 3},
	
	-- punctuation, misc.
	["."] = {1, 3, 1, 3, 1, 3},
	[","] = {3, 3, 1, 1, 3, 3},
	["?"] = {1, 1, 3, 3, 1, 1},
	["'"] = {1, 3, 3, 3, 3, 1},
	["!"] = {3, 1, 3, 1, 3, 3},
	["/"] = {3, 1, 1, 3, 1},
	["("] = {3, 1, 3, 3, 1},
	[")"] = {3, 1, 3, 3, 1, 3},
	["&"] = {1, 3, 1, 1, 1},
	[":"] = {3, 3, 3, 1, 1, 1},
	[";"] = {3, 1, 3, 1, 3, 1},
	["="] = {3, 1, 1, 1, 3},
	["+"] = {1, 3, 1, 3, 1},
	["-"] = {3, 1, 1, 1, 1, 3},
	["_"] = {1, 1, 3, 3, 1, 3},
	["\""] = {1, 3, 1, 1, 3, 1},
	["$"] = {1, 1, 1, 3, 1, 1, 3},
	["@"] = {1, 3, 3, 1, 3, 1},
	
	-- non-latin letters
	-- upper and lowercase since string.lower() doesn't catch them
	["À"] = {1, 3, 3, 1, 3},
	["à"] = {1, 3, 3, 1, 3},
	["Ä"] = {1, 3, 1, 3},
	["ä"] = {1, 3, 1, 3},
	["Å"] = {1, 3, 3, 1, 3},
	["å"] = {1, 3, 3, 1, 3},
	["Ą"] = {1, 3, 1, 3},
	["ą"] = {1, 3, 1, 3},
	["Æ"] = {1, 3, 1, 3},
	["æ"] = {1, 3, 1, 3},
	["Ć"] = {3, 1, 3, 1, 1},
	["ć"] = {3, 1, 3, 1, 1},
	["Ĉ"] = {3, 1, 3, 1, 1},
	["ĉ"] = {3, 1, 3, 1, 1},
	["Ç"] = {3, 1, 3, 1, 1},
	["ç"] = {3, 1, 3, 1, 1},
	["Đ"] = {1, 1, 3, 1, 1},
	["đ"] = {1, 1, 3, 1, 1},
	["Ð"] = {1, 1, 3, 3, 1},
	["ð"] = {1, 1, 3, 3, 1},
	["É"] = {1, 1, 3, 1, 1},
	["é"] = {1, 1, 3, 1, 1},
	["È"] = {1, 3, 1, 1, 3},
	["è"] = {1, 3, 1, 1, 3},
	["Ę"] = {1, 1, 3, 1, 1},
	["ę"] = {1, 1, 3, 1, 1},
	["Ĝ"] = {3, 3, 1, 3, 1},
	["ĝ"] = {3, 3, 1, 3, 1},
	["Ĥ"] = {3, 3, 3, 3},
	["ĥ"] = {3, 3, 3, 3},
	["Ĵ"] = {1, 3, 3, 3, 1},
	["ĵ"] = {1, 3, 3, 3, 1},
	["Ł"] = {1, 3, 1, 1, 3},
	["ł"] = {1, 3, 1, 1, 3},
	["Ń"] = {3, 3, 1, 3, 3},
	["ń"] = {3, 3, 1, 3, 3},
	["Ñ"] = {3, 3, 1, 3, 3},
	["ñ"] = {3, 3, 1, 3, 3},
	["Ó"] = {3, 3, 3, 1},
	["ó"] = {3, 3, 3, 1},
	["Ö"] = {3, 3, 3, 1},
	["ö"] = {3, 3, 3, 1},
	["Ø"] = {3, 3, 3, 1},
	["ø"] = {3, 3, 3, 1},
	["Ś"] = {1, 1, 1, 3, 1, 1, 1},
	["ś"] = {1, 1, 1, 3, 1, 1, 1},
	["Ŝ"] = {1, 1, 1, 3, 1},
	["ŝ"] = {1, 1, 1, 3, 1},
	["Š"] = {3, 3, 3, 3},
	["š"] = {3, 3, 3, 3},
	["Þ"] = {1, 3, 3, 1, 1},
	["þ"] = {1, 3, 3, 1, 1},
	["Ü"] = {1, 1, 3, 3},
	["ü"] = {1, 1, 3, 3},
	["Ŭ"] = {1, 1, 3, 3},
	["ŭ"] = {1, 1, 3, 3},
	["Ź"] = {3, 3, 1, 1, 3, 1},
	["ź"] = {3, 3, 1, 1, 3, 1},
	["Ż"] = {3, 3, 1, 1, 3},
	["ż"] = {3, 3, 1, 1, 3},
}

This comes with a couple of changes:

  • The responsibility of toggling a part to “enabled” or “disabled” has been left to the script implementing Encoder because the end-user will likely want to define how things are changed by the encoder themselves.
    • This is done via the new function argument to the Encoder.new function:
-- Encoder.new(toggleFunc)
Encoder.new(function(enabled)
	if enabled then
		-- make the part bright yellow
		part.Color = Color3.fromRGB(245, 205, 48)
	else
		-- make it medium stone grey
		part.Color = Color3.fromRGB(163, 162, 165)
	end
end)

I removed the boolean looping argument because this is very easy to implement from outside Encoder.

local encoder = Encoder.new(function(enabled) ... end)

while true do
	encoder:Play("hello world")
	task.wait(3)
end

If the end-user wants to do something else in the loop, they want it to play after a different amount of time, etc., that second argument would have been useless.

  • I renamed Encoder:SayMessageInEncoded to Encoder:Play for simplicity. Maybe Encoder:Say would’ve been a better choice…

  • I added a method, Encoder:CanConvert, which describes whether the entirety of a message can be converted to morse code, barring whitespace.

    • This is used in Encoder:Play to make sure a message is converted properly. If it can’t, an error is thrown.

    • You can look for characters the encoder can’t convert by looping through your message and feeding one character to Encoder:CanConvert at a time.

  • I added an instance property, TimeUnit, which affects how fast the message is transmitted. The default is 0.1.

  • Encoded characters are now entirely private because I’m sensing this wouldn’t be a very important function to end-users - they would have to know what 1 and 2 are supposed to mean in a string, but this isn’t immediately obvious and would be a pain to learn.

    • This allowed me to convert all encoded characters into an array of durations, also for simplicity.
  • I did some research on Wikipedia and added extra character translations for punctuation, some operators, and characters with accents.

    • I also made the time units used for signaling the code comparable to the international morse code standard.
2 Likes