/ src / chat.lua
chat.lua
  1  --[[
  2      ToasterGen Spin
  3  
  4      Copyright (C) 2025 Clifton Toaster Reid <cliftontreid@duck.com>
  5  
  6      This library is free software: you can redistribute it and/or modify
  7      it under the terms of the GNU Lesser General Public License as published by
  8      the Free Software Foundation, either version 3 of the License, or
  9      (at your option) any later version.
 10  
 11      This library is distributed in the hope that it will be useful,
 12      but WITHOUT ANY WARRANTY; without even the implied warranty of
 13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14      GNU Lesser General Public License for more details.
 15  
 16      You should have received a copy of the GNU Lesser General Public License
 17      along with this library. If not, see <https://www.gnu.org/licenses/>.
 18  ]]
 19  
 20  local msg
 21  local players
 22  
 23  local function init(charBox, playerDetector)
 24  	msg = peripheral.wrap(charBox)
 25  	if msg == nil then
 26  		error("ChatBox not found", 0)
 27  	end
 28  	players = peripheral.wrap(playerDetector)
 29  	if players == nil then
 30  		error("PlayerDetector not found", 0)
 31  	end
 32  end
 33  
 34  ---@class Player
 35  ---@field name string
 36  ---@field uuid string
 37  ---@field colour number
 38  
 39  ---@type Player[]
 40  local players = {}
 41  
 42  local function addPlayer(player)
 43  	-- Check if player already exists
 44  	for _, p in ipairs(players) do
 45  		if p.uuid == player.uuid then
 46  			return
 47  		end
 48  	end
 49  
 50  	-- Define a set of available colors
 51  	local candidateColors = { 1, 2, 3, 4, 5, 6, 7, 8 }
 52  	local usedColors = {}
 53  
 54  	-- Collect colors that are already in use
 55  	for _, p in ipairs(players) do
 56  		if p.colour then
 57  			usedColors[p.colour] = true
 58  		end
 59  	end
 60  
 61  	-- Build list of colors not yet assigned
 62  	local available = {}
 63  	for _, color in ipairs(candidateColors) do
 64  		if not usedColors[color] then
 65  			table.insert(available, color)
 66  		end
 67  	end
 68  
 69  	-- If no unique color available, return an error
 70  	if #available == 0 then
 71  		error("No available colors remaining", 0)
 72  	end
 73  
 74  	-- Select a random color from the available ones
 75  	local randomIndex = math.random(#available)
 76  	player.colour = available[randomIndex]
 77  
 78  	table.insert(players, player)
 79  end
 80  
 81  local function removePlayer(player)
 82  	for i, p in ipairs(players) do
 83  		if p.uuid == player.uuid then
 84  			table.remove(players, i)
 85  			break
 86  		end
 87  	end
 88  end
 89  
 90  local function getPlayer(uuid)
 91  	for _, p in ipairs(players) do
 92  		if p.uuid == uuid then
 93  			return p
 94  		end
 95  	end
 96  	return nil
 97  end
 98  
 99  local function getPlayers()
100  	return players
101  end
102  
103  local function clearPlayers()
104  	players = {}
105  end
106  
107  ---@class tmpBet
108  ---@field amount number
109  ---@field player string | nil
110  ---@field uuid string | nil
111  
112  ---@type tmpBet
113  local tmpBet = {
114  	amount = 0,
115  	player = nil,
116  }
117  
118  local PLAYERZONE = 5
119  local COMMANDS = {
120  	"register",
121  	"redeem",
122  }
123  
124  local function has_value(tab, val)
125  	for index, value in ipairs(tab) do
126  		if value == val then
127  			return true
128  		end
129  	end
130  
131  	return false
132  end
133  
134  ---@class ChatEvent
135  ---@field type string The type of event
136  ---@field username string The username of the player who sent the message
137  ---@field uuid string The UUID of the player who sent the message
138  ---@field message string The message sent by the player
139  ---@field isHidden boolean Whether the message is hidden
140  
141  ---Handles chat events
142  ---@param event ChatEvent The chat event to handle
143  local function handleChatEvent(event)
144  	if event.type ~= "chat" then
145  		return
146  	end
147  	if not event.isHidden or has_value(COMMANDS, event.message) then
148  		print("Received invalid chat event")
149  		return
150  	end
151  	-- Check if the player is
152  	local around = players.getPlayersInCubic(PLAYERZONE, PLAYERZONE, PLAYERZONE)
153  	local isAround = false
154  	for _, player in ipairs(around) do
155  		if player.uuid == event.uuid then
156  			isAround = true
157  			break
158  		end
159  	end
160  	if not isAround then
161  		error("Player '" .. event.username .. "' not in range", 0)
162  		return
163  	end
164  
165  	if event.message == "register" then
166  		if tmpBet.player ~= nil then
167  			if tmpBet.player == event.username then
168  				msg.sendMessageToPlayer("You are already registered, you may proceed to place a bet", event.username)
169  			elseif tmpBet.player ~= event.username then
170  				msg.sendMessageToPlayer(
171  					"A player is currently registered, please wait for them to place a bet",
172  					event.username
173  				)
174  			end
175  			return
176  		end
177  		tmpBet.player = event.username
178  		tmpBet.uuid = event.uuid
179  		msg.sendMessageToPlayer("You have been registered, you may now place a bet", event.username)
180  	elseif event.message == "redeem" then
181  		msg.sendMessageToPlayer("Please visit the chute to redeem your coins", event.username)
182  	end
183  end
184  
185  ---@param event string
186  local function handleCoin(event)
187  	if event ~= "coin" then
188  		return
189  	end
190  	tmpBet.amount = tmpBet.amount + 1
191  	if tmpBet.player == nil then
192  		msg.sendMessage(
193  			"No player registered, please use '$register' before placing a bet, there are currently "
194  				.. tmpBet.amount
195  				.. " coins without an owner"
196  		)
197  		return -1
198  	end
199  	msg.sendMessageToPlayer(
200  		"Coin received, a total of " .. tmpBet.amount .. " coins are now in your account",
201  		tmpBet.player
202  	)
203  	return 0
204  end
205  
206  local function getBet()
207  	if tmpBet.player == nil then
208  		msg.sendMessage(
209  			"No player is currently registered, please use '$register' before placing a bet, there are "
210  				.. tmpBet.amount
211  				.. " coins without an owner"
212  		)
213  		return nil
214  	end
215  	local bet = tmpBet
216  	tmpBet = {
217  		amount = 0,
218  		player = nil,
219  	}
220  	return bet
221  end
222  
223  local msgFuncs = {}
224  
225  msgFuncs.handleChatEvent = handleChatEvent
226  msgFuncs.handleCoin = handleCoin
227  msgFuncs.getBet = getBet
228  msgFuncs.addPlayer = addPlayer
229  msgFuncs.removePlayer = removePlayer
230  msgFuncs.getPlayer = getPlayer
231  msgFuncs.getPlayers = getPlayers
232  msgFuncs.clearPlayers = clearPlayers
233  msgFuncs.addPlayer = addPlayer
234  msgFuncs.removePlayer = removePlayer
235  msgFuncs.init = init
236  
237  --- Sends a message to all players
238  ---@param message string The message to send
239  ---@return nil
240  msgFuncs.sendMessage = function(message)
241  	if msg then
242  		msg.sendMessage(message, "SoSpin")
243  	else
244  		error("ChatBox not initialized", 0)
245  	end
246  end
247  
248  --- Sends a message to a player
249  ---@param message string The message to send
250  ---@param player string The player to send the message to
251  ---@return nil
252  msgFuncs.sendMessageToPlayer = function(message, player)
253  	if msg then
254  		msg.sendMessageToPlayer(message, player, "SoSpin")
255  	else
256  		error("ChatBox not initialized", 0)
257  	end
258  end
259  
260  return msgFuncs