· 5 years ago · Jun 28, 2020, 08:22 PM
1local tmptfm=tfm
2local tempsystem=system
3local system=tempsystem
4local tfm=tmptfm
5
6local funcname = "function"
7local booleanname = "boolean"
8local tablename = "table"
9local stringname = "string"
10local numbername = "number"
11local function fast_assert(condition, ...)
12 if not condition then
13 if next({ ... }) then
14 local s, r = pcall(function(...) return (string.format(...)) end, ...)
15 if s then
16 error("assertion failed!: " .. r, 2)
17 end
18 end
19 error("assertion failed!", 2)
20 end
21end
22local function expectedGotInfo(t, expected)
23 return "Got :" .. type(t) .. ". Expected: " .. expected
24end
25local chatmsg = tfm.exec.chatMessage
26tfm.exec.chatMessage = function(msg, pl)
27 if msg:len() < 999 then chatmsg(msg, pl) end
28end
29local newGameCounter = false
30local oldMapGame = tfm.exec.newGame
31tfm.exec.newGame = function(map)
32 if newGameCounter == true then
33 system.newTimer(function()
34 tfm.exec.newGame(map)
35 end, 3005, false)
36 else
37 -- tfm.exec.chatMessage("executing now: "..map)
38 oldMapGame(map)
39 newGameCounter = true
40 system.newTimer(function() newGameCounter = false end, 3050, false)
41 end
42end
43local oldKillPlayer=tfm.exec.killPlayer
44tfm.exec.killPlayer=function(p)
45 fast_assert(type(p) == stringname, expectedGotInfo(p, stringname))
46 oldKillPlayer(p)
47end
48local oldsetPlayerScore=tfm.exec.setPlayerScore
49tfm.exec.setPlayerScore=function(playerName,score,add)
50 playerName=playerName or ""
51 score=score or 0
52 add=add or false
53 fast_assert(type(playerName) == stringname, expectedGotInfo(playerName, stringname))
54 fast_assert(type(score) == numbername, expectedGotInfo(score, numbername))
55 fast_assert(type(add) == booleanname, expectedGotInfo(add, booleanname))
56
57 oldsetPlayerScore(playerName,score,add)
58
59end
60local oldrespawnPlayer=tfm.exec.respawnPlayer
61tfm.exec.respawnPlayer=function(playerName)
62 playerName=playerName or ""
63
64 fast_assert(type(playerName) == stringname, expectedGotInfo(playerName, stringname))
65
66 oldrespawnPlayer(playerName)
67
68end
69
70local oldgiveCheese=tfm.exec.giveCheese
71tfm.exec.giveCheese=function(playerName)
72 playerName=playerName or ""
73
74 fast_assert(type(playerName) == stringname, expectedGotInfo(playerName, stringname))
75
76 oldgiveCheese(playerName)
77
78end
79local oldplayerVictory=tfm.exec.playerVictory
80tfm.exec.playerVictory=function(playerName)
81 playerName=playerName or ""
82
83 fast_assert(type(playerName) == stringname, expectedGotInfo(playerName, stringname))
84 oldplayerVictory(playerName)
85
86end
87
88
89local xplayer = tfm.exec
90
91local updatingPos = true
92
93
94
95
96local maxFileSaveLen = 32e3
97local community = tfm.get.room.community:lower() if community == "int" then community = "en" end
98
99local moepl = {}
100
101local table = table
102local string = string
103local tfm = tfm
104local savedData
105local KEY = {}
106KEY["backspace"] = 8 KEY["tab"] = 9 KEY["enter"] = 13 KEY["shift"] = 16 KEY["ctrl"] = 17 KEY["alt"] = 18 KEY["pause"] = 19 KEY["caps lock"] = 20 KEY["esc"] = 27 KEY["spacebar"] = 32 KEY["page up"] = 33 KEY["page down"] = 34 KEY["end"] = 35 KEY["left arrow"] = 37 KEY["up arrow"] = 38 KEY["right arrow"] = 39 KEY["down arrow"] = 40 KEY["insert"] = 45 KEY["delete"] = 46 KEY["0"] = 48 KEY["1"] = 49 KEY["2"] = 50 KEY["3"] = 51 KEY["4"] = 52 KEY["5"] = 53 KEY["6"] = 54 KEY["7"] = 55 KEY["8"] = 56 KEY["9"] = 57 KEY["a"] = 65 KEY["b"] = 66 KEY["c"] = 67 KEY["d"] = 68 KEY["e"] = 69 KEY["f"] = 70 KEY["g"] = 71 KEY["h"] = 72 KEY["i"] = 73 KEY["j"] = 74 KEY["k"] = 75 KEY["l"] = 76 KEY["m"] = 77 KEY["n"] = 78 KEY["o"] = 79 KEY["p"] = 80 KEY["q"] = 81 KEY["r"] = 82 KEY["s"] = 83 KEY["t"] = 84 KEY["u"] = 85 KEY["v"] = 86 KEY["w"] = 87 KEY["x"] = 88 KEY["y"] = 89 KEY["z"] = 90 KEY["lwindows"] = 91 KEY["rwindows"] = 92 KEY["appkey"] = 93 KEY["1n"] = 97 KEY["2n"] = 98 KEY["3n"] = 99 KEY["4n"] = 100 KEY["5n"] = 101 KEY["6n"] = 102 KEY["7n"] = 103 KEY["8n"] = 104 KEY["9n"] = 105 KEY["*n"] = 106 KEY["+n"] = 107 KEY["-n"] = 109 KEY[".n"] = 110 KEY["/n"] = 111 KEY["f1"] = 112 KEY["f2"] = 113 KEY["f3"] = 114 KEY["f4"] = 115 KEY["f5"] = 116 KEY["f6"] = 117 KEY["f7"] = 118 KEY["f8"] = 119 KEY["f9"] = 120 KEY["f10"] = 121 KEY["f11"] = 122 KEY["f12"] = 123 KEY["numlock"] = 144 KEY["scrolllock"] = 145 KEY[" "] = 186 KEY["="] = 187 KEY[","] = 188 KEY["-"] = 189 KEY["."] = 190
107
108xplayer.chatMessage("<J>Starting initialization...")
109
110debug.disableEventLog(true)
111admin = {}
112local function main()
113 --[[
114 local savedData={}
115 local games={}
116 local traitor={}
117 traitor["maps"]={"@297547","@310723","@3057710","@3068103","@2872064","@2890428","@3769399",0}
118 games["traitor"]=traitor
119 savedData["games"]=games
120 local stringtable=moepl.stringify(savedData)
121 print(system.saveFile(stringtable))
122 --]]
123
124
125 admin = adminModule.new()
126end
127
128
129local function _AdminModule()
130 adminModule = {}
131 --- Admin module class
132 -- This class acts as a frontend gui-alike to run, stop and administer modules registered to it
133 -- @class adminModule
134 adminModule.new = function()
135 local self = {}
136 local initloadev
137 local admins = { "Moepl", "Impuredeath" }
138 local shouldsave = false
139 local cansave = true
140 local dataloaded = false
141 local helpdata = {
142 { "Command", "Action" },
143 { "!admin help", "This help" },
144 { "!admin list", "List modules" },
145 { "!admin run number", "Activates the module with that number" },
146 { "!admin stop number", "Stops the module with that number" },
147 { "!admin fullstop", "Halts" },
148 { "!admin get modNr listmaps", "Shows the maplist (if its exists)" },
149 { "!admin get modNr amaps m1 m2...", "Adds map(s) to the module" },
150 { "!admin get modNR rmaps m1 m2...", "Removes map(s) from the module" }
151 }
152 local watchedModules = {}
153 local HELPTEXT = string.monoSpaceTransform("\n<font size=\"10\">" .. table.getSpaceIdentedString(helpdata) .. "</font>\n")
154
155 --- saves the settings to the server
156 --
157 local savedata = function()
158 if cansave and dataloaded then
159 local savestring = moepl.save(savedData)
160 if #savestring < maxFileSaveLen then
161 tfm.exec.chatMessage("saving: " .. maxFileSaveLen - string.len(savestring) .. " chars left")
162 system.saveFile(savestring, 2)
163 shouldsave = false
164 cansave = false
165 system.newTimer(function() cansave = true end, 1000 * 60 * 10, false)
166 end
167 end
168 end
169
170 system.newTimer(function()
171 if (shouldsave and dataloaded and cansave) then
172 savedata()
173 else
174 print("loading file")
175 system.loadFile(2)
176 end
177 end, 1000 * 60 * 30, true)
178
179 self.savedata = function()
180 savedata()
181 end
182 self.registerEvent = function(eventName, func)
183 return tfm.moepl.registerEvent(eventName, func, true)
184 end
185
186
187
188 initloadev = self.registerEvent("eventFileLoaded", function(filename, file)
189 print("loaded: " .. filename)
190 dataloaded = true
191 --
192 savedData = moepl.load(file)
193 local roomname=tfm.get.room.name
194 local triberoom=false
195
196 if tfm.get.room.community:lower()=="int" then
197 triberoom=string.sub(roomname,2,2)~='#'
198 else
199 triberoom=string.sub(roomname,4,4)~='#'
200 end
201
202 for i, v in pairs(savedData["games"]) do
203
204 if i == "testModule" and TestModule then
205 self.addModule(TestModule.new())
206 elseif i == "towerdefense" and TowerDefenseModule then
207 self.addModule(TowerDefenseModule.new())
208 elseif i == "traitor" and TraitorModule then
209 if triberoom==true then
210 tfm.exec.chatMessage("<ROSE>Sorry, traitor is only fun in big rooms. Try /room #traitor")
211 break;
212 end
213 local maps = table.getValuesOnly(savedData["games"]["traitor"]["maps"])
214 local strings = savedData["games"][i]["strings"]
215 if not (community == "fr" or community == "tr" or community == "es" or community == "br" or community == "ru") then
216 community = "en"
217 end
218 self.addModule(TraitorModule.new(maps, strings[community]))
219 elseif i == "swap" and SwapModule then
220 local maps = { "@690952", "@693165" }
221 self.addModule(SwapModule.new(maps))
222 elseif i == "tribewar" and TribewarModule then
223 self.addModule(TribewarModule.new())
224 else
225 xplayer.chatMessage(string.format("<J>Found data for %s. But <BV>%s<J> is not installed :(", i, i))
226 end
227 end
228 --]]
229 --[[
230 savedData={}
231 savedData["games"]={}
232 savedData["games"]["traitor"]={}
233 savedData["games"]["tribewar"]={}
234 savedata()
235 --]]
236 --[[
237local langData={}
238 langData["STATBADSUSPICIONS"] = "П л о х и е п о д о з р е н и я "
239 langData["GOTSUSPICION"] = "В а с н а ч а л и п о д о з р е в а т ь , у б и в %s (%s)!"
240 langData["INNOCENTNOTIFY"] = "В ы %s! Н а й д и т е %s!"
241 langData["STATKILLS"] = "У б и й с т в а "
242 langData["TRAITORSWON"] = "П р е д а т е л и (%s) в ы й г р а л и э т у и г р у !"
243 langData["INNOCENT"] = "н е в и н о в н ы й "
244 langData["NOTFOUND"] = "%s н е т у в к о м н а т е "
245 langData["NOTSUSPECTINGANYMORE"] = "В ы б о л ь ш е н е п о д о з р е в а е т е %s."
246 langData["KILLEDTOOMANYINNOCENTS"] = "И з в и н и т е , в ы у б и л и с л и ш к о м м н о г о н е в и н н ы х :("
247 langData["STATNAME"] = "И м я "
248 langData["TRAITORS"] = "п р е д а т е л и "
249 langData["WELCOME"] = "Д о б р о п о ж а л о в а т ь в м и н и -и г р у TRAITOR. Н а ж м и т е к н о п к у I д л я п о л у ч е н и я д о п о л н и т е л ь н о й и н ф о р м а ц и и ."
250 langData["KILLEDANINNOCENT"] = "О х н е т ! %s у б и л н е в и н о в н о г о %s :("
251 langData["BEINGSUSPECTED"] = "%s н а ч и н а ю т п о д о з р е в а т ь , ч т о о н %s (%d/%d)"
252 langData["TRAITORDIED"] = "П р е д а т е л ь %s у м е р ."
253 langData["STATTRAITOR"] = "Б ы л п р е д а т е л е м "
254 langData["KILLEDATRAITOR"] = "%s у б и л п р е д а т е л я (%s)!"
255 langData["CREATEDBY"] = "С о з д а т е л и %s и %s."
256 langData["BETTERSTATS"] = "Х о р о ш а я с т а т и с т и к а м о ж е т ч у т ь -ч у т ь у в е л и ч и т ь в а ш и ш а н с ы с т а т ь п р е д а т е л е м !"
257 langData["CANTBEKILLEDANYMORE"] = "%s н е м о г у т б ы т ь у б и т ы б о л ь ш е (%d/%d)."
258 langData["TRAITORBLOODLUST"] = "У в а с о с т а л о с ь %s, ч т о б ы у б и т ь к о г о н и б у д ь , п о к а в ы с а м и н е у м е р л и !"
259 langData["TRAITORNOTIFY"] = "В ы %s! У б и в а й т е и г р о к о в н а ж и м а я н а п р о б е л ."
260 langData["BITEAGAIN"] = "В ы с н о в а м о ж е т е у к у с и т ь :)"
261 langData["ALREADYSUSPECTED"] = "В ы у ж е п о д о з р е в а е т е %s."
262 langData["INNOCENTSWON"] = "Н е в и н о в н ы е п о б е д и л и !"
263 langData["STATGOODSUSPICIONS"] = "Х о р о ш и е п о д о з р е н и я "
264 langData["HASBEENKILLED"] = "%s б у л у б и т !"
265 langData["GUESTSNOTALLOWED"] = "П р о с т и т е , г о с т и н е м о г у т и г р а т ь :("
266 langData["GAMEEXPLAINATION"] = "Н е к о т о р ы е и з в а с я в л я ю т с я п р е д а т е л я м и ! О н и м о г у т у к у с и т ь в а с и ч е р е з н е с к о л ь к о с е к у н д в ы у м р е т е !\nП о п ы т а й т е с ь в ы ч и с л и т ь п р е д а т е л е й и у б и т ь и х , о т п р а в и в !Nickname в ч а т (г д е Nickname - н и к п о д о з р е в а е м о г о ). Е с л и 3 и л и б о л ь ш е и г р о к о в с д е л а ю т т о ж е с а м о е , т о е г о м о ж н о б у д е т у б и т ь н а ж а в н а п р о б е л . И в о з м о ж н о в ы у б ь е т е и м е н н о п р е д а т е л я :)\n Н а ж а в н а TAB в ы м о ж е т е с н я т ь в а ш е п о д о з р е н и я с и г р о к а .\nП о с м о т р е т ь с т а т и с т и к у п р е д ы д у щ е г о р а у н д а м о ж н о н а ж а в н а к н о п к у \"o\".\n%s"
267 langData["STATISTICSLASTROUND"] = "С т а т и с т и к а п р е д ы д у щ е г о р а у н д а "
268 langData["TRAITORSWON"] = "П р е д а т е л и (%s) в ы й г р а л и э т у и г р у !"
269 langData["YOUBIT"] = "В ы у к у с и л и %s, о н а у м р е т ч е р е з 3 с е к у н д ы . В ы м о ж е т е у к у с и т ь о д и н р а з в 10 с е к у н д ."
270 langData["TRAITOR"] = "п р е д а т е л ь "
271
272
273
274
275
276 savedData["games"]["traitor"]["strings"]["ru"]=langData
277
278savedata()
279 --]]
280
281 --[[
282
283 local TOWERDEFENSEMAPS = { { number = "@1010829", ptx = 60, pty = 200 }, { number = "@1048633", ptx = 110, pty = 230 }, { number = "@1066135", ptx = 28, pty = 311 }, { number = "@1097739", ptx = 240, pty = 360 }, { number = "@1344299", ptx = 38, pty = 349 }, { number = "@1385250", ptx = 12, pty = 350 }, { number = "@1387919", ptx = 12, pty = 350 }, { number = "@1389077", ptx = 12, pty = 350 }, { number = "@1392979", ptx = 12, pty = 362 }, { number = "@1395396", ptx = 25, pty = 236 }, { number = "@1396273", ptx = 25, pty = 297 }, { number = "@1415547", ptx = 12, pty = 350 }, { number = "@1415797", ptx = 12, pty = 350 }, { number = "@1437664", ptx = 30, pty = 212 }, { number = "@1526121", ptx = 30, pty = 365 }, { number = "@1528709", ptx = 160, pty = 178 }, { number = "@1565707", ptx = 12, pty = 370 }, { number = "@1583750", ptx = 12, pty = 350 }, { number = "@1617965", ptx = 25, pty = 365 }, { number = "@1663278", ptx = 30, pty = 355 }, { number = "@1675985", ptx = 28, pty = 375 }, { number = "@2031613", ptx = 38, pty = 242 }, { number = "@2059472", ptx = 270, pty = 385 }, { number = "@2088704", ptx = 25, pty = 290 }, { number = "@2243918", ptx = 25, pty = 365 }, { number = "@2277944", ptx = 25, pty = 365 }, { number = "@2340661", ptx = 40, pty = 250 }, { number = "@2344789", ptx = 40, pty = 360 }, { number = "@2349366", ptx = 18, pty = 363 }, { number = "@2349975", ptx = 26, pty = 157 }, { number = "@2350753", ptx = 50, pty = 321 }, { number = "@2351372", ptx = 40, pty = 360 }, { number = "@2351671", ptx = 40, pty = 360 }, { number = "@2353976", ptx = 25, pty = 350 }, { number = "@2354015", ptx = 20, pty = 320 }, { number = "@2357729", ptx = 37, pty = 372 }, { number = "@2359797", ptx = 350, pty = 360 }, { number = "@2360360", ptx = 12, pty = 350 }, { number = "@2361789", ptx = 295, pty = 354 }, { number = "@2362402", ptx = 59, pty = 205 }, { number = "@2362647", ptx = 12, pty = 350 }, { number = "@2362715", ptx = 26, pty = 358 }, { number = "@2362849", ptx = 12, pty = 350 }, { number = "@2363538", ptx = 30, pty = 365 }, { number = "@2363902", ptx = 40, pty = 360 }, { number = "@2363984", ptx = 145, pty = 155 }, { number = "@2365518", ptx = 30, pty = 275 }, { number = "@2366673", ptx = 30, pty = 235 }, { number = "@2366964", ptx = 15, pty = 345 }, { number = "@2367081", ptx = 40, pty = 355 }, { number = "@2367195", ptx = 25, pty = 365 }, { number = "@2368073", ptx = 40, pty = 355 }, { number = "@2368847", ptx = 25, pty = 350 }, { number = "@2369472", ptx = 140, pty = 360 }, { number = "@2369495", ptx = 37, pty = 348 }, { number = "@2372315", ptx = 200, pty = 345 }, { number = "@2372637", ptx = 30, pty = 280 }, { number = "@2373644", ptx = 190, pty = 360 }, { number = "@2373927", ptx = 12, pty = 350 }, { number = "@2374169", ptx = 135, pty = 352 }, { number = "@2374713", ptx = 25, pty = 350 }, { number = "@2375804", ptx = 120, pty = 359 }, { number = "@2376489", ptx = 25, pty = 204 }, { number = "@2378080", ptx = 150, pty = 345 }, { number = "@2378273", ptx = 28, pty = 199 }, { number = "@2382583", ptx = 240, pty = 345 }, { number = "@2385557", ptx = 570, pty = 360 }, { number = "@2387985", ptx = 40, pty = 267 }, { number = "@2388233", ptx = 40, pty = 355 }, { number = "@2388756", ptx = 60, pty = 345 }, { number = "@2389760", ptx = 131, pty = 366 }, { number = "@2393063", ptx = 30, pty = 320 }, { number = "@2396120", ptx = 250, pty = 355 }, { number = "@2399511", ptx = 292, pty = 370 }, { number = "@2399863", ptx = 30, pty = 355 }, { number = "@2400127", ptx = 40, pty = 194 }, { number = "@2401945", ptx = 295, pty = 359 }, { number = "@2402074", ptx = 101, pty = 310 }, { number = "@2402215", ptx = 12, pty = 350 }, { number = "@2404655", ptx = 15, pty = 360 }, { number = "@2405021", ptx = 240, pty = 370 }, { number = "@2405537", ptx = 280, pty = 364 }, { number = "@2409911", ptx = 40, pty = 265 }, { number = "@2414383", ptx = 180, pty = 365 }, { number = "@2417265", ptx = 130, pty = 225 }, { number = "@2418099", ptx = 183, pty = 374 }, { number = "@2419631", ptx = 28, pty = 374 }, { number = "@2421481", ptx =
284140, pty = 355 }, { number = "@2422291", ptx = 50, pty = 365 }, { number = "@2423431", ptx = 12, pty = 350 }, { number = "@2424646", ptx = 200, pty = 318 }, { number = "@2426003", ptx = 495, pty = 350 }, { number = "@2426008", ptx = 25, pty = 260 }, { number = "@2426010", ptx = 25, pty = 285 }, { number = "@2426011", ptx = 25, pty = 245 }, { number = "@2426016", ptx = 25, pty = 282 }, { number = "@2426023", ptx = 215, pty = 352 }, { number = "@2426026", ptx = 25, pty = 332 }, { number = "@2429484", ptx = 20, pty = 335 }, { number = "@2429929", ptx = 40, pty = 315 }, { number = "@2430170", ptx = 60, pty = 240 }, { number = "@2432746", ptx = 25, pty = 262 }, { number = "@2432765", ptx = 107, pty = 239 }, { number = "@2433428", ptx = 116, pty = 352 }, { number = "@2435194", ptx = 50, pty = 351 }, { number = "@2435573", ptx = 175, pty = 365 }, { number = "@2439582", ptx = 25, pty = 272 }, { number = "@2439792", ptx = 25, pty = 352 }, { number = "@2440231", ptx = 100, pty = 360 }, { number = "@2441233", ptx = 29, pty = 344 }, { number = "@2441647", ptx = 225, pty = 362 }, { number = "@2443103", ptx = 141, pty = 361 }, { number = "@2445303", ptx = 180, pty = 355 }, { number = "@2448988", ptx = 60, pty = 357 }, { number = "@2453506", ptx = 119, pty = 356 }, { number = "@2455263", ptx = 81, pty = 359 }, { number = "@2457578", ptx = 214, pty = 361 }, { number = "@2469950", ptx = 25, pty = 282 }, { number = "@2471736", ptx = 20, pty = 134 }, { number = "@527941", ptx = 60, pty = 200 }, { number = "@541734", ptx = 60, pty = 250 }, { number = "@542540", ptx = 180, pty = 360 }, { number = "@568611", ptx = 30, pty = 315 }, { number = "@569123", ptx = 30, pty = 320 }, { number = "@592501", ptx = 35, pty = 379 }, { number = "@649174", ptx = 35, pty = 200 }, { number = "@708355", ptx = 25, pty = 340 }, { number = "@765989", ptx = 55, pty = 200 }, { number = "@803298", ptx = 800, pty = 340 }, { number = "@807863", ptx = 40, pty = 320 }, { number = "@875944", ptx = 25, pty = 360 }, { number = "@892675", ptx = 32, pty = 336 }, { number = "@913212", ptx = 155, pty = 360 }, { number = "@953868", ptx = 25, pty = 301 }, { number = "@960547", ptx = 30, pty = 310 }, { number = "@970700", ptx = 181, pty = 279 }, { number = "@975416", ptx = 110, pty = 315 }, { number = "@977332", ptx = 35, pty = 376 }, { number = "@977337", ptx = 28, pty = 354 }, { number = "@978675", ptx = 175, pty = 385 }, { number = "@989760", ptx = 60, pty = 360 } }
285 savedData["games"]["testModule"]={}
286 -- savedData["games"]["towerdefense"]["maps"]=TOWERDEFENSEMAPS
287
288savedata()
289
290 --]]
291 --
292 --[[
293 -- printf(file)
294 savedData["games"]["traitor"]["strings"]["en"]["TRAITORNOTIFY"] ="You are a %s! Kill players with spacebar. You can chat with other traitors using \"!tc yourtext\""
295
296 savedata()
297 --]]
298 --[[
299 savedData["games"]["tribewar"]={}
300 savedata()
301 --]]
302
303 system.newTimer(function()
304 if self.getModule() then
305 self.getModule().startModule()
306 end
307 self.listModules()
308 end, 1000, false)
309
310 tfm.moepl.unregisterEvent(initloadev)
311 self.registerEvent("eventFileLoaded", function(filename, file)
312 if not shouldsave then
313 end
314 end)
315 end)
316
317
318 self.getAllWatchedModules = function() return watchedModules end
319 --- Gets a module by the number it was inserted to the admin module
320 -- @param number the module which belongs to it
321 --
322 self.getModule = function(number)
323 if number == nil or number < 1 or number > table.size(watchedModules) then
324 if table.size(watchedModules) == 0 then
325 return nil
326 end
327 return watchedModules[table.size(watchedModules)]
328 else
329 return watchedModules[number]
330 end
331 end
332 --- prints the help text
333 -- @param playerName prints the text to playerName only
334 --
335 self.showHelp = function(playerName) xplayer.chatMessage(HELPTEXT) end
336 --- starts a module
337 -- @param number the number to start. If nil then theh last added module will be started
338 --
339 self.startModule = function(number) local mod = self.getModule(number) if mod and mod.isRunning() == false then mod.startModule() end end
340 --- stops a module
341 -- @param number the number to stop. If nil then the last added module will be stopped
342 --
343 self.stopModule = function(number) local mod = self.getModule(number) if mod and mod.isRunning() then mod.stopModule() end end
344 --- stops every module currently active
345 --
346 self.stopAllModules = function() for i, _v in pairs(watchedModules) do self.stopModule(i) end end
347 --- adds a module to the adminmodule
348 -- @param module the module to add
349 --
350 self.addModule = function(module)
351 table.insert(watchedModules, module)
352 return module
353 end
354 --- Removes an module from the adminmodule
355 -- @param number the module to remove
356 --
357 self.removeModule = function(number)
358 local mod = self.getModule(number)
359 if mod then
360 mod.stopModule()
361 end
362 end
363 --- Lists every module registered in a table-like list
364 -- @param playerName fill in playerName if it should only be displayed to that person
365 --
366 self.listModules = function(playerName)
367 local result = ""
368 local data = { { numbername, "ModuleName", "Running" } }
369 for i, v in pairs(watchedModules) do
370 local running = [[<font color="#40FF00">true</font>]]
371 if v.isRunning() == false then running = [[<font color="#DF7401">false</font>]] end
372 table.insert(data, { i, "<font color=\"#01A9DB\">" .. v.getName() .. "</font>", running })
373 end
374 result = string.monoSpaceTransform(table.getSpaceIdentedString(data))
375 xplayer.chatMessage(result)
376 end
377 local function isAdmin(playerName)
378 return table.contains(admins, playerName)
379 end
380
381 local commander = commandHandler.new(false, self)
382 commander.addCommand("admin help", 0, function(playerName) if isAdmin(playerName) then self.showHelp() end end)
383 commander.addCommand("admin list", 0, function(playerName) if isAdmin(playerName) then self.listModules() end end)
384 commander.addCommand("admin fullstop", 0, function(playerName) if isAdmin(playerName) then self.stopAllModules() self.listModules() end end)
385 commander.addCommand("admin run", 1, function(playerName, msg, modulenr) if isAdmin(playerName) then self.startModule(tonumber(modulenr)) self.listModules() end end)
386 commander.addCommand("admin stop", 1, function(playerName, msg, modulenr) if isAdmin(playerName) then self.stopModule(tonumber(modulenr)) self.listModules() end end)
387 commander.addCommand("admin get", 2, function(playerName, msg, modulenr, get)
388 if isAdmin(playerName) then
389 local mod = self.getModule(tonumber(modulenr))
390 if (get == "listmaps") and mod.showMaps then
391 xplayer.chatMessage("<J>" .. mod.showMaps())
392 elseif get == "amaps" then
393 local maps = string.split(msg)
394 for _, m in pairs(maps) do
395 mod.addMap(m)
396 xplayer.chatMessage(string.format("<J>Added %s", m))
397 end
398 savedData["games"][mod.getName()]["maps"] = mod.getMaps()
399 shouldsave = true
400 savedata()
401 elseif get == "rmaps" then
402 local maps = string.split(msg)
403 for _, m in pairs(maps) do
404 if mod.removeMap(m) then
405 xplayer.chatMessage(string.format("<J>removed %s", m))
406 else
407 xplayer.chatMessage(string.format("<J>failure %s", m))
408 end
409 savedData["games"][mod.getName()]["maps"] = mod.getMaps()
410 shouldsave = true
411 savedata()
412 end
413 end
414 end
415 end)
416
417
418 xplayer.chatMessage("<J>Requesting saved data from server...")
419 system.loadFile(2)
420 return self
421 end
422end
423
424--- Our main wrapper for Tigrounette's api
425local function moeplCommandModule()
426 local tfmMoepl = {}
427 tfmMoepl.new = function()
428 local self = {}
429 local playersize
430
431
432 local registeredEvents = {}
433 --- modules have to register events to receive them.
434 -- @param eventName the kind of the event to receive. E.g "eventLoop"
435 -- @param func the reference of the function that shall be executed on the event
436 -- @param beforeModule boolean if it should be the first module in the list. Should be false for minigames
437 -- @return the function - should be used to unregister the event again
438 self.registerEvent = function(eventName, func, beforeModule)
439 beforeModule = beforeModule or false
440 fast_assert(type(eventName) == stringname, expectedGotInfo(eventName, stringname))
441 fast_assert(type(func) == funcname, expectedGotInfo(func, funcname))
442 fast_assert(type(beforeModule) == booleanname, expectedGotInfo(beforeModule, booleanname))
443 local value = func
444 if (registeredEvents[eventName] == nil) then
445 registeredEvents[eventName] = {}
446 end
447 if (beforeModule == true) then
448 table.insert(registeredEvents[eventName], 1, value)
449 else
450 table.insert(registeredEvents[eventName], value)
451 end
452 return value
453 end
454 --- a module can unregister an event again
455 -- @param eventConsumer the function - should be the one that was returned on register event
456 --
457 self.unregisterEvent = function(eventConsumer)
458
459 local removeInfo = {}
460 for i, v in pairs(registeredEvents) do
461 for k, v in pairs(v) do
462 if v == eventConsumer then
463 table.insert(removeInfo, { i, k })
464 end
465 end
466 end
467 if #removeInfo > 0 then
468 table.remove(registeredEvents[removeInfo[1][1]], removeInfo[1][2])
469 end
470 if #removeInfo > 1 then
471 self.unregisterEvent(eventConsumer)
472 end
473 end
474 --- internal function. Should consume all server-events to be able to distribute them to module
475 -- @param functionName
476 -- @param funcTable
477 --
478 self.consumeEvent = function(functionName, funcTable)
479 if registeredEvents[functionName] then
480 for _, func in pairs(registeredEvents[functionName]) do
481 func(moepl.unpack(funcTable))
482 end
483 end
484
485 end
486
487
488 self.getPlayerSize = function() return playersize end
489
490
491 self.registerEvent("eventNewGame", function()
492 playersize = table.size(tfm.get.room.playerList)
493 end)
494
495 return self
496 end
497
498
499 TimerTask = {}
500 --- Java-alike-timertask class.
501 -- @param func the function to run
502 -- @param timeMS time in milliseconds to wait before run
503 -- @param repeating boolean - if it should loop
504 --
505 TimerTask.new = function(func, timeMS, repeating)
506 local self = {}
507 func = func or function() end
508 timeMS = timeMS or 0
509 repeating = repeating or false
510 local alreadyrun = false
511 local lasttime = os.time()
512 local sortTime = lasttime + timeMS
513 fast_assert(type(func) == funcname, expectedGotInfo(func, funcname))
514 fast_assert(type(timeMS) == numbername, expectedGotInfo(timeMS, numbername))
515 fast_assert(type(repeating) == booleanname, expectedGotInfo(repeating, booleanname))
516
517 self.getFunc = function() return func end
518 self.getTimeMS = function() return timeMS end
519 self.isRepeating = function() return repeating end
520 self.getSortTime = function() return sortTime end
521
522 self.prompt = function(timenow)
523 timenow = timenow or os.time()
524 if os.difftime(timenow, lasttime) >= timeMS then
525 lasttime = timenow
526 if (not repeating and not alreadyrun) or repeating then
527 func()
528 end
529 alreadyrun = true
530 if not repeating then
531 return self
532 else
533 sortTime = timenow + timeMS
534 return self
535 end
536 end
537 end
538
539 return self
540 end
541
542 local Timer = {}
543 --- Timer class.
544 -- It uses syste.newTimer as a backend but provides unlimitted timers.
545 -- Holds timers in a sorted list to reduce runtime.
546 Timer.new = function()
547 local self = {}
548 local activeTimers = {}
549 local nowtime = os.time()
550 --todo use a better data structure for this
551 local sortedTimers = insertionSortList.new(function(a, b)
552 if a.getSortTime() < b.getSortTime() then
553 return 1;
554 elseif (a.getSortTime() == b.getSortTime()) then
555 return 0
556 else
557 return -1;
558 end
559 end)
560
561 --used makinit's idea to create lots of timers
562 local TICKS = 14
563 local s = 1000 / TICKS
564 local function loop()
565 nowtime = nowtime + s
566
567 local i = 1
568 local tbl = sortedTimers.getList();
569 for i, v in pairs(tbl) do
570 end
571 while i <= #sortedTimers.getList() do
572 if activeTimers[sortedTimers.getList()[i]] == nil then
573 sortedTimers.removeElement(i)
574 elseif sortedTimers.getList()[i].prompt(nowtime) then
575 if (sortedTimers.getList()[i].isRepeating()) then
576 sortedTimers.addElement(sortedTimers.removeElement(i));
577 else
578 activeTimers[sortedTimers.getList()[i]] = nil
579 sortedTimers.removeElement(i)
580 end
581 else
582 break
583 end
584 end
585 end
586
587 for t = 0, 1000 - s, s do
588 system.newTimer(function() system.newTimer(loop, 1000, true) end, 1000 + t, false)
589 end
590 system.newTimer(function() nowtime = os.time() end, 3000, true)
591
592 self.addTimerTask = function(timerTask)
593 fast_assert(type(timerTask) == tablename)
594 activeTimers[timerTask] = {}
595 sortedTimers.addElement(timerTask)
596 return timerTask
597 end
598 self.removeTimerTask = function(timerTask)
599 fast_assert(type(timerTask) == tablename)
600 activeTimers[timerTask] = nil
601 end
602
603 return self
604 end
605
606 local mainmodule = tfmMoepl.new()
607 tfm.moepl = mainmodule
608 tfm.moepl.timer = Timer.new()
609
610 function eventLoop(current, remaining) mainmodule.consumeEvent("eventLoop", { current, remaining }) end
611
612 function eventChatCommand(player, message) mainmodule.consumeEvent("eventChatCommand", { player, message }) end
613
614 function eventEmotePlayed(playerName, emoteId) mainmodule.consumeEvent("eventEmotePlayed", { playerName, emoteId }) end
615
616 function eventKeyboard(playerName, keyCode, down, x, y)
617 local function updatepos(playerName, x, y)
618 tfm.get.room.playerList[playerName].x = x
619 tfm.get.room.playerList[playerName].y = y
620 end
621
622 if updatingPos then
623 updatepos(playerName, x, y)
624 end
625 mainmodule.consumeEvent("eventKeyboard", { playerName, keyCode, down, x, y })
626 end
627
628 function eventNewGame() mainmodule.consumeEvent("eventNewGame", {}) end
629
630 function eventNewPlayer(playerName)
631 --to update the mice pos more often
632 --
633 local keys = { KEY["a"], KEY["w"], KEY["s"], KEY["d"], KEY["left arrow"], KEY["up arrow"], KEY["down arrow"], KEY["right arrow"] }
634 for _, k in pairs(keys) do
635 xplayer.bindKeyboard(playerName, k, true, true)
636 xplayer.bindKeyboard(playerName, k, false, true)
637 end
638 --mice update pos end
639 --]]
640 mainmodule.consumeEvent("eventNewPlayer", { playerName })
641 end
642
643 function eventPlayerDied(playerName) mainmodule.consumeEvent("eventPlayerDied", { playerName }) end
644
645 function eventPlayerGetCheese(playerName) mainmodule.consumeEvent("eventPlayerGetCheese", { playerName }) end
646
647 function eventPlayerLeft(playerName) mainmodule.consumeEvent("eventPlayerLeft", { playerName }) end
648
649 function eventPlayerVampire(playerName) mainmodule.consumeEvent("eventPlayerVampire", { playerName }) end
650
651 function eventPlayerWon(playerName) mainmodule.consumeEvent("eventPlayerWon", { playerName }) end
652
653 function eventSummoningStart(playerName, objectId, x, y, angle) mainmodule.consumeEvent("eventSummoningStart", { playerName, objectId, x, y, angle }) end
654
655 function eventSummoningCancel(playerName) mainmodule.consumeEvent("eventSummoningCancel", { playerName }) end
656
657 function eventSummoningEnd(playerName, objectType, x, y, angle, xSpeed, ySpeed, other) mainmodule.consumeEvent("eventSummoningEnd", { playerName, objectType, x, y, angle, xSpeed, ySpeed, other }) end
658
659 function eventFileSaved(fileName) print("saved: " .. fileName) mainmodule.consumeEvent("eventFileSaved", { fileName }) end
660
661 function eventFileLoaded(fileName, file) mainmodule.consumeEvent("eventFileLoaded", { fileName, file }) end
662
663
664 tfmMoeplModule = {}
665 --- Template Class for minigames
666 -- @param name name of the minigame
667 -- @param startFunc the function that will be run when the minigame is activated
668 -- @param stopFunc the function that shall be run when the minigame is stopped
669 tfmMoeplModule.new = function(name, startFunc, stopFunc)
670 name = name or "notNamed"
671 startFunc = startFunc or function() end
672 stopFunc = stopFunc or function() end
673 local self = {}
674
675 fast_assert(type(name) == stringname, expectedGotInfo(name, stringname))
676 fast_assert(type(startFunc) == funcname, expectedGotInfo(startFunc, funcname))
677 fast_assert(type(stopFunc) == funcname, expectedGotInfo(stopFunc, funcname))
678
679 local registeredEvents = {}
680 local running = false
681 local myTimers = {}
682 local bindedKeysFuncs = {}
683 local globaleventKeyBoard
684 local globalNewGameEvent
685 local globalPlayerLeftEvent
686
687 self.getName = function() return name end
688 self.isRunning = function() return running end
689 self.getStartFunc = function() return startFunc end
690 self.getStopFunc = function() return stopFunc end
691 self.getAllTimers = function() return myTimers end
692
693 self.setStartFunc = function(newStartFunc) fast_assert(type(newStartFunc) == funcname) startFunc = newStartFunc end
694 self.setStopFunc = function(newStopFunc) fast_assert(type(newStopFunc) == funcname) stopFunc = newStopFunc end
695 self.setName = function(newName) fast_assert(type(newName) == stringname) name = newName end
696 --- adds a timerTask. All timertasks will be stopped when the module is stopped automatically
697 -- @param func the function to run
698 -- @param timeMS time to wait in milliseconds
699 -- @param repeating boolean if it should loop
700 --
701 self.addTimerTask = function(func, timeMS, repeating)
702 repeating = repeating or false
703 timeMS = timeMS or 1000
704 func = func or function() end
705 --
706 fast_assert(type(func) == funcname, expectedGotInfo(func, funcname))
707 fast_assert(type(timeMS) == numbername, expectedGotInfo(timeMS, numbername))
708 fast_assert(type(repeating) == booleanname, expectedGotInfo(repeating, booleanname))
709 --
710 local task = TimerTask.new(func, timeMS, repeating)
711 myTimers[task] = {}
712 mainmodule.timer.addTimerTask(task)
713 return task
714 end
715 --- removes a timertask
716 -- @param timerTask the reference of the timerTask function
717 --
718 self.removeTimerTask = function(timerTask)
719 fast_assert(type(timerTask) == tablename, expectedGotInfo(timerTask, tablename))
720 myTimers[timerTask] = nil
721 end
722 --- removes all timertasks from the module
723 --
724 self.removeAllTimerTasks = function()
725
726 for i, _ in pairs(myTimers) do
727 mainmodule.timer.removeTimerTask(i)
728 end
729 myTimers = {}
730 end
731 --- starts the module
732 --
733 self.startModule = function() startFunc() running = true
734 globaleventKeyBoard = mainmodule.registerEvent("eventKeyboard", function(playerName, key, down, x, y)
735 if (bindedKeysFuncs[playerName]) then
736 for i, v in pairs(bindedKeysFuncs[playerName]) do
737 if (table.contains(v.keys, key)) then
738 v.func(down, x, y)
739 end
740 end
741 end
742 end, true)
743 globalNewGameEvent = mainmodule.registerEvent("eventNewGame", function()
744 self.removeKeyFuncs(false)
745 end, true)
746 globalPlayerLeftEvent = mainmodule.registerEvent("eventPlayerLeft", function(playerName)
747 self.removeKeyFuncsByPlayer(playerName)
748 end, true)
749 end
750 --- removes binded keyfunction that were binded to players by the module
751 -- @param all boolean - if true then every bind will be remove, else only keyfunctions
752 -- that should only hold one round will be deleted
753 --
754 self.removeKeyFuncs = function(all)
755 fast_assert(type(all) == booleanname, expectedGotInfo(all, booleanname))
756 if (all == true) then
757 bindedKeysFuncs = {}
758 else
759 local toRemoveTable = {}
760 for p, k in pairs(bindedKeysFuncs) do
761 for i, v in pairs(bindedKeysFuncs[p]) do
762 if (v.stayRounds == false) then
763 if toRemoveTable[p] == nil then toRemoveTable[p] = {} end
764 table.insert(toRemoveTable[p], v)
765 end
766 end
767 end
768 for playerName, v in pairs(toRemoveTable) do
769 for _, t in pairs(toRemoveTable[playerName]) do
770 table.remove(bindedKeysFuncs[playerName], table.getIndexOf(bindedKeysFuncs[playerName], t))
771 end
772 end
773 end
774 end
775 --- removes key functions by a player
776 -- @param playerName the name of the player
777 --
778 self.removeKeyFuncsByPlayer = function(playerName)
779 fast_assert(type(playerName) == stringname)
780 local playerfuncs = {}
781 bindedKeysFuncs[playerName] = nil
782 end
783 --- stops the mdoule
784 --
785 self.stopModule = function() stopFunc() self.unregisterAllEvents() self.removeAllTimerTasks() running = false self.removeKeyFuncs(true)
786 if (globaleventKeyBoard ~= nil) then
787 mainmodule.unregisterEvent(globaleventKeyBoard)
788 globaleventKeyBoard = nil
789 end
790 if (globalNewGameEvent ~= nil) then
791 mainmodule.unregisterEvent(globalNewGameEvent)
792 globalNewGameEvent = nil
793 end
794 if (globalPlayerLeftEvent ~= nil) then
795 mainmodule.unregisterEvent(globalPlayerLeftEvent)
796 globalPlayerLeftEvent = nil
797 end
798 end
799 --- registers an event to the module. Every event will be automatically unregisteres when the module is stopped
800 -- @param eventName the kind of the event to register. E.g "eventLoop"
801 -- @param func the function to run when the event is triggered
802 --
803 self.registerEvent = function(eventName, func)
804 fast_assert(type(eventName) == stringname, expectedGotInfo(eventName, stringname))
805 fast_assert(type(func) == funcname, expectedGotInfo(func, funcname))
806 mainmodule.registerEvent(eventName, func, false)
807 table.insert(registeredEvents, func)
808 return func
809 end
810 --- unregisters an event
811 -- @param eventConsumer the reference of the function that should be run on an event
812 --
813 self.unregisterEvent = function(eventConsumer)
814 fast_assert(type(eventConsumer) == funcname, expectedGotInfo(eventConsumer, funcname))
815 mainmodule.unregisterEvent(eventConsumer)
816 if table.contains(registeredEvents, eventConsumer) then
817 repeat
818 table.remove(registeredEvents, table.getIndexOf(registeredEvents, eventConsumer))
819 until table.contains(registeredEvents, eventConsumer) == false
820 end
821 end
822 --- unregisters every event that was reigstered by this module
823 --
824 self.unregisterAllEvents = function()
825
826 repeat self.unregisterEvent(registeredEvents[1])
827
828 until table.size(registeredEvents) == 0
829 end
830 --- binds a function to a player
831 -- @param playerName the name of the player
832 -- @param func the function to run when a key is pressed or released
833 -- @param stayRounds if this binding should persist or only stay one round
834 -- @param ... all the keys that shall be watched over
835 --
836 self.bindKeys = function(playerName, func, stayRounds, ...)
837 local keys = {}
838 fast_assert(type(playerName) == stringname, expectedGotInfo(playerName, stringname))
839 fast_assert(type(func) == funcname, expectedGotInfo(func, funcname))
840 fast_assert(type(stayRounds) == booleanname, expectedGotInfo(stayRounds, booleanname))
841 for i, v in pairs(arg) do
842 -- fast_assert(type(v)==numbername,expectedGotInfo(v,numbername))
843 xplayer.bindKeyboard(playerName, v, false, true)
844 xplayer.bindKeyboard(playerName, v, true, true)
845 table.insert(keys, v)
846 end
847 if bindedKeysFuncs[playerName] == nil then bindedKeysFuncs[playerName] = {} end
848 table.insert(bindedKeysFuncs[playerName], { keys = keys, func = func, stayRounds = stayRounds })
849 end
850 self.unbindKeys = function(table, playerName)
851 fast_assert(type(table) == tablename, expectedGotInfo(table, tablename))
852 fast_assert(type(playerName) == stringname, expectedGotInfo(playerName, stringname))
853 if bindedKeysFuncs[playerName] then
854 local pos = table.getIndexOf(bindedKeysFuncs[playerName], table)
855 if pos then
856 table.remove(bindedKeysFuncs[playerName], pos)
857 end
858 end
859 end
860
861 return self
862 end
863 commandHandler = {}
864 --- Command handler to easily check for commands in the chat meant for the script
865 -- @param caseSensitive boolean - if it should be case-sensitive
866 -- @param tfmMod table - the module reference the commandHandler should itself register to
867 --
868 commandHandler.new = function(caseSensitive, tfmMod)
869 local self = {}
870
871 caseSensitive = caseSensitive or false
872 fast_assert(type(caseSensitive) == booleanname, expectedGotInfo(caseSensitive, booleanname))
873 fast_assert(type(tfmMod) == tablename, expectedGotInfo(tfmMod, tablename))
874 local running = true
875 local triggers = {}
876
877 local function chatter(playerName, message)
878 if running then
879 for trigger, tbls in pairs(triggers) do
880 for _, tbl in pairs(tbls) do
881 local paras = tbl[1]
882 local func = tbl[2]
883 if (((string.len(message) == string.len(trigger)) and paras == 0) or (paras > 0 and string.len(message) >= string.len(trigger))) then
884 local msgsplit = { string.sub(message, 0, string.len(trigger)), string.sub(message, string.len(trigger) + 1) }
885 if ((caseSensitive and msgsplit[1] == trigger) or (not caseSensitive and string.equalsIgnoreCase(msgsplit[1], trigger))) then
886 if paras == 0 then paras = nil end
887 local restmsg = string.split(msgsplit[2])
888 local toremovewords = paras or 0
889 for i = 1, toremovewords, 1 do
890 table.remove(restmsg, 1)
891 end
892 func(playerName, table.concat(restmsg, " "), moepl.unpack(string.split(msgsplit[2]), 1, paras))
893 end
894 end
895 end
896 end
897 end
898 end
899
900 tfmMod.registerEvent("eventChatCommand", chatter)
901
902 --- adds a command that shall be watched
903 -- @param triggerPattern the pattern that shall be watched
904 -- @param paras integer - number of parameters
905 -- @param func the function to run e.g function(playerName,restmsg,para1,para2,para3)
906 -- @return a table-reference of the command. Save it to be able to remove the command later again
907 self.addCommand = function(triggerPattern, paras, func)
908 fast_assert(type(paras) == numbername, expectedGotInfo(paras, numbername))
909 if triggers[triggerPattern] == nil then triggers[triggerPattern] = {} end
910 local value = { paras, func, triggerPattern }
911 table.insert(triggers[triggerPattern], value)
912 return value
913 end
914 --- removes a command
915 -- @param commandAddValue the table-reference that was returned on registerEvent
916 --
917 self.removeCommand = function(commandAddValue)
918 fast_assert(type(commandAddValue) == tablename, expectedGotInfo(commandAddValue, tablename))
919 for i, v in pairs(triggers[commandAddValue[3]]) do
920 if (v == commandAddValue) then
921 triggers[commandAddValue[3]] = nil
922 break
923 end
924 end
925 end
926 --- Unregisters the whole commandHandler
927 --
928 self.deRegisterCommander = function()
929 tfmMod.unregisterEvent("eventChatCommand", chatter)
930 end
931 self.setRunning = function(runningbool) running = runningbool end
932 return self
933 end
934 mapUtils = {}
935 --- a maprotation-handler class
936 -- @param maps table of maps
937 --
938 mapUtils.new = function(maps)
939 local self = {}
940 local _myMaps = maps
941 local mapsQueue = {}
942 local currentMap
943 self.getCurrentMap = function()
944 return currentMap
945 end
946 --- gets a random map of the list
947 --
948 self.getNextMap = function()
949 if mapsQueue == nil or mapsQueue.value == nil then
950 self.createMapList()
951 end
952 local map = mapsQueue.value
953 currentMap = map
954 mapsQueue = mapsQueue.next
955 return map
956 end
957 --- adds a map to the list
958 -- @param map the map to add.
959 --
960 self.addMap = function(map)
961 table.insert(_myMaps, map)
962 end
963 --- removes a map from the list again
964 self.removeMap = function(map)
965 if (table.contains(_myMaps, map)) then
966 table.remove(_myMaps, table.getIndexOf(_myMaps, map))
967 return true
968 end
969 return false
970 end
971 --- gets all maps from the list
972 --
973 self.getMaps = function()
974 return _myMaps
975 end
976 --- overrides the maplist
977 -- @param maps
978 --
979 self.setMaps = function(maps)
980 _myMaps = maps
981 end
982
983 self.createMapList = function()
984 table.shuffle(_myMaps)
985 mapsQueue = nil
986 for p, v in pairs(_myMaps) do
987 mapsQueue = { next = mapsQueue, value = v }
988 end
989 end
990 return self
991 end
992end
993
994
995
996
997--- Some extra helper functions
998--
999local function helperFunctions()
1000 --- removes the last char of a string
1001 -- @param self the string
1002 --
1003 function string.removeLastChar(self)
1004 local len = string.len(self)
1005 if len > 0 then
1006 self = string.sub(self, 0, len - 1)
1007 end
1008 return self
1009 end
1010
1011 --- returns the length of a string without its html tags
1012 -- @param self the string
1013 --
1014 function string.lenWithOutTags(self)
1015 return string.len(string.gsub(self, "(<%s*[^>]*>)", ""))
1016 end
1017
1018 function string.trim(s)
1019 return (s:gsub("^%s*(.-)%s*$", "%1"))
1020 end
1021
1022 --- adds a html-color-tag to the string
1023 -- @param self the string
1024 -- @param color string - the color in hex
1025 --
1026 function string.tagColor(self, color)
1027 fast_assert(type(color) == stringname, expectedGotInfo(color, stringname))
1028 return string.format("<font color=\"%s\">" .. tostring(self) .. "</font>", color)
1029 end
1030
1031 --- tags the string with a font-face tag with some monospace fonts
1032 -- @param self the reference of the string
1033 --
1034 function string.monoSpaceTransform(self)
1035 return "<font face=\"Monospace,Courier New,BatangChe,Andale Mono\" color=\"#BABD2F\">" .. self .. "</font>"
1036 end
1037
1038 --- useful to transform an unindexed table to an indexed one
1039 -- @param tbl the table to transform
1040 --
1041 function table.getValuesOnly(tbl)
1042 local result = {}
1043 for i, v in pairs(tbl) do
1044 table.insert(result, v)
1045 end
1046 return result
1047 end
1048
1049 --- turns a table into a formatted string - using spaces. Used as a workaround for the missing html-tables
1050 -- @param self table - the table to format
1051 --
1052 function table.getSpaceIdentedString(self)
1053
1054 local lengthTable = {}
1055
1056 local result = ""
1057
1058 for i, v in pairs(self) do
1059 local t = 1
1060 for k, l in pairs(v) do
1061 local cur = lengthTable[t]
1062 local len = string.lenWithOutTags(tostring(l))
1063 if cur ~= nil then
1064 if cur < len then
1065 lengthTable[t] = len
1066 end
1067 else
1068 lengthTable[t] = len
1069 end
1070
1071 t = t + 1
1072 end
1073 end
1074 for k, l in pairs(self) do
1075 if (k ~= 1) then result = result .. "\n" end
1076 for i, v in pairs(l) do
1077 local str = tostring(l[i])
1078 local strl = string.lenWithOutTags(str)
1079 local remaining = lengthTable[i] - strl
1080 local idention = ""
1081 if #lengthTable ~= i then
1082 for r = 0, remaining + 1, 1 do
1083 idention = idention .. " "
1084 end
1085 end
1086 result = result .. l[i] .. idention
1087 end
1088 end
1089 return result
1090 end
1091
1092 --- port of javas string.equalIgnoreCase method
1093 -- @param a string a
1094 -- @param b string b
1095 --
1096 function string.equalsIgnoreCase(a, b)
1097 return string.lower(tostring(a)) == string.lower(tostring(b))
1098 end
1099
1100 --- splits a string
1101 -- @param input the string
1102 -- @param seperator the seperator - if nil then a space (" ") will be used
1103 --
1104 function string.split(input, seperator)
1105 seperator = seperator or " "
1106 local res = {}
1107 for part in string.gmatch(input, "[^" .. seperator .. "]+") do
1108 table.insert(res, part)
1109 end
1110 return res
1111 end
1112
1113 --- checks if a table contains value val
1114 -- @param self the table
1115 -- @param val the value to check for
1116 --
1117 function table.contains(self, val)
1118 if table.getIndexOf(self, val) then
1119
1120 return true
1121 else
1122 return false
1123 end
1124 end
1125
1126 --- gets the index number of val in an indexed table
1127 -- @param self the table
1128 -- @param val the value to search for
1129 --
1130 function table.getIndexOf(self, val)
1131 for k, v in ipairs(self) do
1132 if v == val then return k end
1133 end
1134 end
1135
1136 --- checks if an table is empty or not
1137 -- @param self the table
1138 --
1139 function table.empty(self)
1140 for _, _ in pairs(self) do
1141 return false
1142 end
1143 return true
1144 end
1145
1146 --- clears a table of all entries
1147 -- @param self the table
1148 --
1149 function table.clear(self)
1150 self = {}
1151 end
1152
1153 --- method to check how many entries a table has
1154 -- @param self the table
1155 -- @return number of elements in the table
1156 function table.size(self)
1157 if self then
1158 local nr = #self
1159 if nr == 0 then
1160 for _, _ in pairs(self) do
1161 nr = nr + 1
1162 end
1163 return nr
1164 end
1165 return nr
1166 end
1167 return 0
1168 end
1169
1170 --- shuffles the elements in an indexed table
1171 -- @param self the table to shuffle
1172 --
1173 function table.shuffle(self)
1174 math.randomseed(os.time())
1175 local arrayCount = #self
1176 for i = arrayCount, 2, -1 do
1177 local j = math.random(1, i)
1178 self[i], self[j] = self[j], self[i]
1179 end
1180 end
1181
1182 function moepl.unpack(a, i, m)
1183 i = i or 1
1184 if (m ~= nil and i <= m) or (not m) then
1185 if a[i] ~= nil then
1186 return a[i], moepl.unpack(a, i + 1, m)
1187 end
1188 end
1189 end
1190
1191 function moepl.save(tbl)
1192 local charS, charE = " ", "\n"
1193 local output = ""
1194 local function exportstring(s)
1195 return string.format("%q", s)
1196 end
1197
1198 local write = function(t) output = output .. t end
1199
1200 -- initiate variables for save procedure
1201 local tables, lookup = { tbl }, { [tbl] = 1 }
1202
1203 for idx, t in ipairs(tables) do
1204 write("-- Table: {" .. idx .. "}" .. charE)
1205 write("{" .. charE)
1206 local thandled = {}
1207
1208 for i, v in ipairs(t) do
1209 thandled[i] = true
1210 local stype = type(v)
1211 -- only handle value
1212 if stype == "table" then
1213 if not lookup[v] then
1214 table.insert(tables, v)
1215 lookup[v] = #tables
1216 end
1217 write(charS .. "{" .. lookup[v] .. "}," .. charE)
1218 elseif stype == "string" then
1219 write(charS .. exportstring(v) .. "," .. charE)
1220 elseif stype == "number" then
1221 write(charS .. tostring(v) .. "," .. charE)
1222 end
1223 end
1224
1225 for i, v in pairs(t) do
1226 -- escape handled values
1227 if (not thandled[i]) then
1228
1229 local str = ""
1230 local stype = type(i)
1231 -- handle index
1232 if stype == "table" then
1233 if not lookup[i] then
1234 table.insert(tables, i)
1235 lookup[i] = #tables
1236 end
1237 str = charS .. "[{" .. lookup[i] .. "}]="
1238 elseif stype == "string" then
1239 str = charS .. "[" .. exportstring(i) .. "]="
1240 elseif stype == "number" then
1241 str = charS .. "[" .. tostring(i) .. "]="
1242 end
1243
1244 if str ~= "" then
1245 stype = type(v)
1246 -- handle value
1247 if stype == "table" then
1248 if not lookup[v] then
1249 table.insert(tables, v)
1250 lookup[v] = #tables
1251 end
1252 write(str .. "{" .. lookup[v] .. "}," .. charE)
1253 elseif stype == "string" then
1254 write(str .. exportstring(v) .. "," .. charE)
1255 elseif stype == "number" then
1256 write(str .. tostring(v) .. "," .. charE)
1257 end
1258 end
1259 end
1260 end
1261 write("}," .. charE)
1262 end
1263 return output
1264 end
1265
1266
1267 function moepl.loadstring(str)
1268 local function trim(s)
1269 return (s:gsub("^%s*(.-)%s*$", "%1"))
1270 end
1271
1272 local lines = string.split(str, "\n")
1273 local tblstr = {}
1274 local output = {}
1275 local currenttbl = nil
1276 for i, v in pairs(lines) do
1277 if v:find("%-") == 1 then
1278 if (currenttbl ~= nil) then
1279 table.insert(tblstr, currenttbl)
1280 end
1281 currenttbl = { name = v:sub(12, v:len() - 1) }
1282 elseif (v ~= "{" and v ~= "},") then
1283 table.insert(currenttbl, v)
1284 end
1285 end
1286
1287 if currenttbl ~= {} then
1288 table.insert(tblstr, currenttbl)
1289 end
1290
1291 for i = #tblstr, 1, -1 do
1292 local restable = {}
1293 local tbl = tblstr[i]
1294 local tblname = tbl.name
1295 -- print("p " .. tostring(tblname))
1296
1297 for i, v in ipairs(tbl) do
1298 v = trim(v)
1299 --can be a table or a number/string with a non integer index
1300 if v:sub(1, 1) == "[" then
1301 --should be changed to a pattern to not get problems on string that have "}= in it
1302 local name = v:sub(3, v:find("\"]=") - 1)
1303 local value = (v:sub(v:find("\]=") + 2, v:len() - 1))
1304 --string
1305 if (value:sub(1, 1) == "\"") then
1306 restable[name] = tostring(value:sub(2, value:len() - 1))
1307 --table
1308 elseif (value:sub(1, 1) == "{") then
1309 restable[name] = { tonumber(value:sub(2, value:len() - 1)) }
1310 --number
1311 else
1312 restable[name] = tonumber(value)
1313 end
1314 --table with integer index
1315 elseif (v:sub(1, 1) == "{") then
1316 table.insert(restable, { tonumber(v:sub(2, v:len() - 2)) })
1317 --string with integer index
1318 elseif (v:sub(1, 1) == "\"") then
1319 table.insert(restable, tostring(v:sub(2, v:len() - 2)))
1320 --integer with integer index
1321 else
1322 local nr = tonumber(v:sub(1, v:len() - 1))
1323
1324 table.insert(restable, nr)
1325 end
1326 end
1327 table.insert(output, 1, restable)
1328 end
1329 return output
1330 end
1331
1332 --// The Load Function
1333 function moepl.load(sfile)
1334 local tables = moepl.loadstring(sfile)
1335 for idx = 1, #tables do
1336 local tolinki = {}
1337 for i, v in pairs(tables[idx]) do
1338 if type(v) == "table" then
1339 tables[idx][i] = tables[v[1]]
1340 end
1341 if type(i) == "table" and tables[i[1]] then
1342 table.insert(tolinki, { i, tables[i[1]] })
1343 end
1344 end
1345 -- link indices
1346 for _, v in ipairs(tolinki) do
1347 tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
1348 end
1349 end
1350 return tables[1]
1351 end
1352
1353
1354
1355 insertionSortList = {} --Shall be rewritten to some binary-tree structure when more chars are available
1356 --- implemention of a list that stays sorted using a insertion-sort-algorithm.
1357 -- @param sortFunc check table.sort
1358 --
1359 insertionSortList.new = function(sortFunc, list)
1360 local self = {}
1361 local list = list or {}
1362 self.isort = function()
1363 local ret = { list[1], list[2] }
1364 for i = 3, #t do
1365 table.insert(ret, self.bins(ret, list[i]), list[i])
1366 end
1367 return ret
1368 end
1369 self.bins = function(tb, val, st, en)
1370 local st, en = st or 1, en or #tb
1371 local mid = math.floor((st + en) / 2)
1372 if en == st then
1373 return sortFunc(tb[st], val) == -1 and st or st + 1
1374 else return sortFunc(tb[mid], val) == -1 and self.bins(tb, val, st, mid) or self.bins(tb, val, mid + 1, en)
1375 end
1376 end
1377 self.removeElement = function(nr)
1378 local obj = list[nr]
1379 table.remove(list, nr)
1380 return obj
1381 end
1382 --- adds an element to the list. Uses intervals to speed things up.
1383 -- @param el the element to add
1384 --
1385 self.addElement = function(el)
1386 while (true) do
1387 if (#list == 0) then
1388 return table.insert(list, el)
1389 elseif #list == 1 then
1390 return table.insert(list, sortFunc(el, list[1]) + 1, el)
1391 end
1392 return table.insert(list, self.bins(list, el), el)
1393 end
1394 end
1395
1396 self.getList = function() return list end
1397 return self
1398 end
1399end