· 8 years ago · Feb 22, 2018, 02:58 AM
1--Automated Star Wars Edge Of The Empire tabletop RPG character sheet
2--Made by Didero
3--Character sheet by LambMower
4--Heavily inspired by the scripted EotE sheet by Marlow: http://steamcommunity.com/sharedfiles/filedetails/?id=672608406
5--Dice images by Gordon: http://steamcommunity.com/sharedfiles/filedetails/?id=624350397
6--Version RC3
7
8--VARIABLE DECLARATION
9spawnedDice = {}
10areClearingDice = false --Used in the 'OnObjectDestroy' check, to prevent frequent dice display updates on dice clear
11--Character sheet stuff. All use the same index. So characterSheetsData[2] refers to the data of characterSheetList[2]
12characterSheets = {} --A list of references to the character sheet object list
13characterSheetsGUIDs = {} --A list with the GUIDs of the character sheet objects, so we can find them again
14characterSheetsData = {} --A table that will contain tables of character sheet values
15
16characterDescriptorNotepads = {} --A table that will store the notepads per character sheet, so we know if it exists
17
18--Dice description data
19dicenames = {"Ability", "Proficiency", "Boost", "Difficulty", "Challenge", "Setback"}
20-- the next dice tables are in the same order as the 'dicenames' table
21dicecounts = {0, 0, 0, 0, 0, 0}
22-- {{dicetype, imageUrl}}, where 'dicetype' is the internal type number. 0 is 4-sided, 1 is 6-sided, etc
23dicedata = {{2,"http://i.imgur.com/wmnD7f3.png"}, {4,"http://i.imgur.com/ZmKYETO.png"}, {1,"http://i.imgur.com/BATmCZ9.png"},
24 {2, "http://i.imgur.com/apULLij.png"}, {4, "http://i.imgur.com/fHWIxAY.png"}, {1, "http://i.imgur.com/rZXn6z5.png"}
25}
26-- Convert normal dice numbers to EotE values like 'success', 'fault', ...
27-- There's six values: success, advantage, triumph, failure, threat, despair
28-- failure cancels success, threat cancels advantage, triumph and despair can co-exist
29-- Since triumph and despair are only on one side of one dice each, no sense storing that for every side
30--'dicevalues' is {dicetype={successOnSide1,advantageOnSide1, successOnSide2,advantageOnSide2, ..}}
31-- failure is just negative success, threat is negative advantage
32-- triumphs and despairs are counted here as a success roll, and with a special check in the 'getDiceValues' function
33-- (dice values don't correspond to the values on page 12 of the rulebook since they're placed differently in the dice mod)
34dicevalues = {{0,0, 1,0, 0,1, 0,2, 1,1, 1,0, 0,1, 2,0},
35 {0,0, 0,2, 2,0, 1,0, 1,1, 1,0, 1,1, 0,1, 2,0, 1,0, 1,1, 0,2},
36 {0,0, 1,0, 1,1, 0,2, 0,1, 0,0},
37 {0,0, 0,-1, 0,-1, -2,0, -1,-1, 0,-1, -1,0, 0,-2},
38 {0,0, -2,0, -2,0, -1,0, -1,-1, -1,0, -1,0, 0,-1, 0,-2, -1,-1, 0,-1, 0,-2},
39 {0,0, 0,0, 0,-1, -1,0, 0,-1, -1,0}
40}
41
42--Descriptive variables, used to find the index of fields
43--Character description strings
44characterDescriptors = {"playername", "charactername", "species", "career", "specialization"}
45--Skill names
46characteristics = {"Brawn", "Agility", "Intellect", "Cunning", "Willpower", "Presence"}
47--First 22 skills (up to 'Vigilance') are general skills, then 5 combat skills (up to 'Ranged - Heavy'), the rest are knowledge skills
48skills = {"Astrogation", "Athletics", "Charm", "Coercion", "Computers", "Cool", "Coordination", "Deception", "Discipline", "Leadership", "Mechanics", "Medicine", "Negotiation", "Perception", "Piloting - Planetary", "Piloting - Space", "Resilience", "Skulduggery", "Stealth", "Streetwise", "Survival", "Vigilance", "Brawl", "Gunnery", "Melee", "Ranged - Light", "Ranged - Heavy", "Lightsaber", "Core Worlds", "Education", "Lore", "Outer Rim", "Underworld", "Xenology", "Other"}
49battleFields = {"Soak", "WoundsThreshold", "WoundsCurrent", "StrainThreshold", "StrainCurrent", "DefenseRanged", "DefenseMelee", "CritWound"}
50--What characteristic the skill depends on, as an index to the Characteristics table
51characteristicInfluencingSkills = {3, 1, 6, 5, 3, 6, 2, 4, 5, 6, 3, 3, 6, 4, 2, 2, 1, 4, 2, 4, 4, 5, 1, 2, 1, 2, 2, 1, 3, 3, 3, 3, 3, 3, 3}
52
53
54--UTILITY FUNCTIONS
55function printError(msg)
56 printToAll("ERROR: " .. msg, {1, 0, 0})
57end
58
59--For some reason this isn't built into Lua?
60function findIndexOfValue(array, valueToFind)
61 --Loop over the array until we find the value we want or we run out of array
62 for index, value in ipairs(array) do
63 if value == valueToFind then
64 return index
65 end
66 end
67 return nil
68end
69
70function sanitizeStringForFunctionName(s)
71 --Remove spaces and dashes so the string will work as a function name
72 s = string.gsub(s, " ", "")
73 s = string.gsub(s, "-", "_")
74 return s
75end
76
77function calculateButtonWidth(label)
78 return string.len(label) * 35 + 30
79end
80
81function createButton(targetObject, onClickFunctionName, label, posX, posZ, fontSize, buttonHeight, buttonWidth)
82 if fontSize == nil then fontSize = 75 end
83 if buttonHeight == nil then buttonHeight = fontSize end
84 if buttonWidth == nil then
85 if buttonHeight == 0 then
86 buttonWidth = 0
87 else
88 buttonWidth = calculateButtonWidth(label)
89 end
90 end
91 targetObject.createButton({function_owner=self, click_function=onClickFunctionName, label=label, position={posX, 0.25, posZ}, height=buttonHeight, width=buttonWidth, font_size=fontSize, rotation={0, 180, 0}})
92end
93
94function getButtonIndex(buttonOwner, clickFunctionToFind)
95 for i, button in ipairs(buttonOwner.getButtons()) do
96 if button["click_function"] == clickFunctionToFind then
97 return button["index"]
98 end
99 end
100 return nil
101end
102
103function updateButtonText(buttonOwner, clickFunctionToFind, newLabelText, shouldAdjustButtonWidth)
104 local index = getButtonIndex(buttonOwner, clickFunctionToFind)
105 if index ~= nil then
106 local newButtonParams = {index=index, label=newLabelText}
107 if shouldAdjustButtonWidth == true then
108 newButtonParams["width"] = calculateButtonWidth(newLabelText)
109 end
110 buttonOwner.editButton(newButtonParams)
111 end
112end
113
114function updateDicecountDisplay(displayOwner, diceIndex)
115 updateButtonText(displayOwner, dicenames[diceIndex] .. "Count", tostring(dicecounts[diceIndex]))
116end
117function updateDicecountDisplays(diceIndex)
118 --Update the central dice display
119 updateDicecountDisplay(self, diceIndex)
120 --And the displays on all the character sheets
121 for i,charsheet in ipairs(characterSheets) do
122 updateDicecountDisplay(charsheet, diceIndex)
123 end
124end
125
126
127--SET UP CODE
128function onLoad(savestate)
129 if savestate ~= "" then
130 local savedata = JSON.decode(savestate)
131 if savedata["saveversion"] ~= 1 then
132 printError("Expected savedata version 1, but found " .. tostring(savedata["saveversion"]))
133 else
134 --For each stored GUID, check if the sheet still exists. If it does, store it
135 for i, guid in ipairs(savedata.characterSheetsGUIDs) do
136 local guidObject = getObjectFromGUID(guid)
137 if guidObject ~= nil then
138 table.insert(characterSheetsGUIDs, guid)
139 table.insert(characterSheets, guidObject)
140 table.insert(characterSheetsData, savedata.characterSheetsData[i])
141 end
142 end
143 end
144 end
145 rebuildAllSheetDisplays()
146 rebuildTrayDisplay()
147end
148
149function onSave()
150 local savedata = {saveversion=1, characterSheetsData=characterSheetsData, characterSheetsGUIDs=characterSheetsGUIDs}
151 return JSON.encode(savedata)
152end
153
154
155--CHARACTER SHEET FUNCTIONS
156function createCharacterSheet()
157 --First create the physical object
158 local ourPos = self.getPosition()
159 local ourRot = self.getRotation()
160 --Since not everything can be done with a newly spawned object, register a finishing function to be called when spawning and internal set-up is done
161 -- Pass along the index in the character sheet list this sheet will be saved in, so we know which sheet we're talking about
162 local charsheet = spawnObject({type="Custom_Model", position={ourPos.x, ourPos.y + 1, ourPos.z}, rotation={ourRot.x, ourRot.y, ourRot.z},
163 callback="finishCharacterSheetCreation", callback_owner=self, params={#characterSheets + 1},
164 })
165 charsheet.setCustomObject({mesh="http://pastebin.com/raw/zkq8DdFz", diffuse="https://i.imgur.com/yuSKhuE.png", material=3})
166 table.insert(characterSheets, charsheet)
167 --Set the stats to their defaults for now
168 local charsheetVars = {}
169 charsheetVars.characterDescriptorsValues = {}
170 for i = 1,#characterDescriptors do
171 charsheetVars.characterDescriptorsValues[i] = ""
172 end
173 charsheetVars.characteristicsValues = {}
174 for i = 1,#characteristics do
175 charsheetVars.characteristicsValues[i] = 2 --2 is defined as 'average'
176 end
177 charsheetVars.skillsValues = {}
178 for i = 1,#skills do
179 charsheetVars.skillsValues[i] = 0
180 end
181 charsheetVars.battleFieldsValues = {}
182 for i = 1,#battleFields do
183 charsheetVars.battleFieldsValues[i] = 0
184 end
185 --Career skills is a table with indices to the skill array, if the index is in here that skill is a career skill
186 charsheetVars.careerSkills = {}
187 --Store a reference to the character descriptors edit notecard here, so we can know if it's spawned and read it out
188 charsheetVars.characterDescriptorsNotecard = nil
189 --XP values
190 charsheetVars.totalXP = 0
191 charsheetVars.availableXP = 0
192 charsheetVars.ForceRank = 0
193 charsheetVars.ForceCommitted = 0
194 charsheetVars.ForceAvailable = 0
195 charsheetVars.EncumbranceThreshold = 0
196 charsheetVars.Encumbrance = 0
197 --Possible display modes are 'edit' (all buttons), 'play' (just the most-used buttons), 'view' (no buttons, just labels)
198 charsheetVars.displaymode = "Edit"
199 table.insert(characterSheetsData, charsheetVars)
200end
201function finishCharacterSheetCreation(finishedCharacterSheet, characterSheetIndexTable)
202 --This gets called by the spawnObject callback parameter, so it gets passed a table argument. Index is table[1]
203 --Update the GUID list
204 updateCharacterSheetGUIDs()
205 --Create buttons on this new character sheet
206 rebuildSheetDisplay(characterSheetIndexTable[1])
207end
208
209function updateCharacterSheetGUIDs()
210 --Rebuild the list of character sheet GUIDs
211 -- Use a local variable first to prevent race conditions
212 local guidList = {}
213 for i = 1,#characterSheets do
214 table.insert(guidList, characterSheets[i].getGUID())
215 end
216 --Now overwrite the global list in a single step
217 characterSheetsGUIDs = guidList
218end
219
220function rebuildSheetDisplay(characterSheetIndex)
221 characterSheets[characterSheetIndex].clearButtons()
222 createDisplayLabels(characterSheetIndex)
223 createSkillsDisplay(characterSheetIndex)
224 if characterSheetsData[characterSheetIndex].displaymode ~= "View" then
225 createEditButtons(characterSheetIndex)
226 createDiceDisplay(characterSheets[characterSheetIndex])
227 end
228 createButton(characterSheets[characterSheetIndex], "toggleDisplayMode",
229 characterSheetsData[characterSheetIndex].displaymode .. " mode", 0, -6.55, 300, 300, 1500
230 )
231end
232function rebuildAllSheetDisplays()
233 for i = 1,#characterSheets do
234 rebuildSheetDisplay(i)
235 end
236end
237
238function rebuildTrayDisplay()
239 self.clearButtons()
240 --Create a button to create a new sheet
241 createButton(self, "createCharacterSheet", "New Sheet", 0, 3, 100, 100, 500)
242 --The tray also needs dice count labels and dice spawning buttons
243 createDiceDisplay(self, 1.8, -3)
244end
245
246function createDisplayLabels(characterSheetIndex)
247 --Create non-button (or more accurately fake-button) labels to display stats
248 local characterSheet = characterSheets[characterSheetIndex]
249 local charSheetValues = characterSheetsData[characterSheetIndex]
250 -- Character descriptors, like name and career
251 -- First the player name, since that's to the side
252 createButton(characterSheet, characterDescriptors[1], charSheetValues.characterDescriptorsValues[1], -3.35, 5.4, 125, 0, 0)
253 -- Then the other descriptors
254 local currentZ = 6.3
255 for i=2,#characterDescriptors do
256 createButton(characterSheet, characterDescriptors[i], charSheetValues.characterDescriptorsValues[i], 0, currentZ, 125, 0, 0)
257 currentZ = currentZ - 0.3
258 end
259 -- Battle stats (soak, wounds, etc)
260 -- Start with 'soak value', since that's the only one with a single value
261 createButton(characterSheet, battleFields[1], tostring(charSheetValues.battleFieldsValues[1]), 3.35, 4.45, 350, 0, 0)
262 createButton(characterSheet, battleFields[8], tostring(charSheetValues.battleFieldsValues[8]), -2.00, 5.28, 250, 0, 0)
263 -- Then iterate over the pairs of values
264 local currentX = 1.5
265 for i=2,7,2 do
266 createButton(characterSheet, battleFields[i], tostring(charSheetValues.battleFieldsValues[i]), currentX, 4.45, 350, 0, 0)
267 createButton(characterSheet, battleFields[i+1], tostring(charSheetValues.battleFieldsValues[i+1]), currentX - 0.8, 4.45, 350, 0, 0)
268 currentX = currentX - 2.3
269 end
270 -- Characteristics fields
271 currentX = 3.82
272 for i=1,#characteristics do
273 createButton(characterSheet, characteristics[i], tostring(charSheetValues.characteristicsValues[i]), currentX, 3.05, 400, 0, 0)
274 currentX = currentX - 1.55
275 end
276 -- total and available XP
277 createButton(characterSheet, "totalXP", tostring(charSheetValues.totalXP), 3.7, -6.5, 275, 0, 0)
278 createButton(characterSheet, "availableXP", tostring(charSheetValues.availableXP), -3.75, -6.5, 275, 0, 0)
279 -- Force Rank related stat displays
280 createButton(characterSheet, "ForceRank", tostring(charSheetValues.ForceRank), -3.1925, -5.05, 275, 0, 0)
281 createButton(characterSheet, "ForceCommitted", tostring(charSheetValues.ForceCommitted), -0.9, -5.25, 275, 0, 0)
282 createButton(characterSheet, "ForceAvailable", tostring(charSheetValues.ForceAvailable), -1.7, -5.25, 275, 0, 0)
283 createButton(characterSheet, "EncumbranceThreshold", tostring(charSheetValues.EncumbranceThreshold), 3.4, -5.25, 275, 0, 0)
284 createButton(characterSheet, "Encumbrance", tostring(charSheetValues.Encumbrance), 2.75, -5.25, 275, 0, 0)
285end
286
287function createEditButtons(characterSheetIndex)
288 local charsheet = characterSheets[characterSheetIndex]
289 local charSheetValues = characterSheetsData[characterSheetIndex]
290 --Create all the edit buttons...
291 if charSheetValues.displaymode == "Edit" then
292 -- ...for editing character descriptors like name and species
293 local charDescriptorEditButtonText = "Edit"
294 if charSheetValues.characterDescriptorsNotecard ~= nil then charDescriptorEditButtonText = "Save" end
295 createButton(charsheet, "editCharacterDescriptors", charDescriptorEditButtonText, 5.1, 6, 225, 250, 550)
296 -- ...a button to claim this sheet
297 createButton(charsheet, "setOwnerToClickedPlayer", "Claim", -5.2, 5.4, 175, 200, 500)
298 end
299 -- ...for battle stats
300 createButton(charsheet, "decreaseSoak", "-", 4.2, 4.3, 125, 125, 125)
301 createButton(charsheet, "increaseSoak", "+", 2.55, 4.3, 125, 125, 125)
302 createButton(charsheet, "decreaseCritWound", "-", -2.4, 5.25, 75, 75, 75)
303 createButton(charsheet, "increaseCritWound", "+", -2.4, 5.45, 75, 75, 75)
304 local currentX = 1.95
305 local Xdecreases = {1.7, 0.57}
306 local currentXdecrease = Xdecreases[1]
307 for i=2,7 do
308 createButton(charsheet, "decrease" .. battleFields[i], "-", currentX, 4.3, 125, 100, 100)
309 createButton(charsheet, "increase" .. battleFields[i], "+", currentX, 5, 125, 100, 100)
310 currentX = currentX - currentXdecrease
311 --Switch between short and long horizontal jumps
312 if currentXdecrease == Xdecreases[1] then currentXdecrease = Xdecreases[2] else currentXdecrease = Xdecreases[1] end
313 end
314 -- ...for characteristics
315 if charSheetValues.displaymode == "Edit" then
316 local currentX = 4.3
317 for i = 1,#characteristics do
318 --minus and plus buttons below the values
319 createButton(charsheet, "decrease".. characteristics[i], "-", currentX, 2.7, 100, 100, 100)
320 createButton(charsheet, "increase".. characteristics[i], "+", currentX - 0.92, 2.7, 100, 100, 100)
321 currentX = currentX - 1.55
322 end
323 createButton(charsheet, "decreaseForceRank", "-", -3.7, -5.3, 75, 75, 75)
324 createButton(charsheet, "increaseForceRank", "+", -3.7, -4.75, 75, 75, 75)
325 end
326 --Force Committed +/- buttons
327 createButton(charsheet, "decreaseForceCommitted", "-", -0.3, -5.3, 75, 75, 75)
328 createButton(charsheet, "increaseForceCommitted", "+", -0.3, -4.75, 75, 75, 75)
329 --Encumbrance buttons
330 createButton(charsheet, "decreaseEncumbranceThreshold", "-", 4.1, -5.3, 75, 75, 75)
331 createButton(charsheet, "increaseEncumbranceThreshold", "+", 4.1, -4.75, 75, 75, 75)
332 createButton(charsheet, "decreaseEncumbrance", "-", 2.05, -5.3, 75, 75, 75)
333 createButton(charsheet, "increaseEncumbrance", "+", 2.05, -4.75, 75, 75, 75)
334 --...for total XP
335 -- Each gets a 1, 5, 10, 50, 100 button, both plus and minus
336 -- Minus on the left, plus on the right, stacked
337 createButton(charsheet, "decreaseTotalXPBy5", "-5", 5, -6.3, 75, 75, 200)
338 createButton(charsheet, "decreaseTotalXPBy50", "-50", 5, -6.6, 75, 75, 200)
339 createButton(charsheet, "decreaseTotalXPBy500", "-500", 5, -6.9, 75, 75, 200)
340 createButton(charsheet, "decreaseTotalXPBy1", "-1", 4.65, -6.3, 75, 75, 200)
341 createButton(charsheet, "decreaseTotalXPBy10", "-10", 4.65, -6.6, 75, 75, 200)
342 createButton(charsheet, "decreaseTotalXPBy100", "-100", 4.65, -6.9, 75, 75, 200)
343 createButton(charsheet, "increaseTotalXPBy1", "+1", 2.75, -6.3, 75, 75, 200)
344 createButton(charsheet, "increaseTotalXPBy10", "+10", 2.75, -6.6, 75, 75, 200)
345 createButton(charsheet, "increaseTotalXPBy100", "+100", 2.75, -6.9, 75, 75, 200)
346 createButton(charsheet, "increaseTotalXPBy5", "+5", 2.4, -6.3, 75, 75, 200)
347 createButton(charsheet, "increaseTotalXPBy50", "+50", 2.4, -6.6, 75, 75, 200)
348 createButton(charsheet, "increaseTotalXPBy500", "+500", 2.4, -6.9, 75, 75, 200)
349 --...for available XP
350 createButton(charsheet, "decreaseAvailableXPBy1", "-1", -2.8, -6.3, 75, 75, 200)
351 createButton(charsheet, "decreaseAvailableXPBy10", "-10", -2.8, -6.6, 75, 75, 200)
352 createButton(charsheet, "decreaseAvailableXPBy100", "-100", -2.8, -6.9, 75, 75, 200)
353 createButton(charsheet, "decreaseAvailableXPBy5", "-5", -2.45, -6.3, 75, 75, 200)
354 createButton(charsheet, "decreaseAvailableXPBy50", "-50", -2.45, -6.6, 75, 75, 200)
355 createButton(charsheet, "decreaseAvailableXPBy500", "-500", -2.45, -6.9, 75, 75, 200)
356 createButton(charsheet, "increaseAvailableXPBy1", "+1", -4.7, -6.3, 75, 75, 200)
357 createButton(charsheet, "increaseAvailableXPBy10", "+10", -4.7, -6.6, 75, 75, 200)
358 createButton(charsheet, "increaseAvailableXPBy100", "+100", -4.7, -6.9, 75, 75, 200)
359 createButton(charsheet, "increaseAvailableXPBy5", "+5", -5.05, -6.3, 75, 75, 200)
360 createButton(charsheet, "increaseAvailableXPBy50", "+50", -5.05, -6.6, 75, 75, 200)
361 createButton(charsheet, "increaseAvailableXPBy500", "+500", -5.05, -6.9, 75, 75, 200)
362end
363
364--Separate function for the skill labels and buttons because sometimes the layout differs a bit between view modes
365-- and this seemed neater than looping through a few times depending on view mode
366function createSkillsDisplay(characterSheetIndex)
367 local charsheet = characterSheets[characterSheetIndex]
368 local charSheetValues = characterSheetsData[characterSheetIndex]
369 --Some buttons should only be pressable in edit mode, otherwise they should be size 0, so invisible, leaving only the text
370 local skillChangeButtonSize = {0, 0}
371 if charSheetValues.displaymode == "Edit" then skillChangeButtonSize = {75, 100} end
372 local diceSpawnButtonSize = 75
373 if charSheetValues.displaymode == "View" then diceSpawnButtonSize = 0 end
374
375 local startZ = 1.72
376 local currentZ = startZ
377 local stepZ = 0.252
378 local currentX = 2.9
379 for i = 1,#skills do
380 local skillName = sanitizeStringForFunctionName(skills[i])
381 --Create a 'career' toggle, to make this a career skill
382 local careerSkillButtonText = "N"
383 if findIndexOfValue(charSheetValues.careerSkills, i) ~= nil then
384 careerSkillButtonText = "Y"
385 end
386 createButton(charsheet, "toggle"..skillName.."AsCareerSkill", careerSkillButtonText, currentX, currentZ, 75, skillChangeButtonSize[1], skillChangeButtonSize[2])
387 --Create a 'spawn dicepool' button
388 createButton(charsheet, "spawnDicepoolFor"..skillName, getDicepoolDisplayString(determineDicepoolForSkill(characterSheetIndex, i)), currentX - 0.75, currentZ, 75, diceSpawnButtonSize)
389 --Skill rank display
390 createButton(charsheet, skillName, tostring(charSheetValues.skillsValues[i]), currentX - 2, currentZ, 75, 0, 0)
391
392 --Add buttons to in- and decrease rank, if needed
393 if charSheetValues.displaymode == "Edit" then
394 createButton(charsheet, "decreaseRankIn"..skillName, "-", currentX - 1.7, currentZ, 75, 75, 75)
395 createButton(charsheet, "increaseRankIn"..skillName, "+", currentX - 2.3, currentZ, 75, 75, 75)
396 end
397
398 currentZ = currentZ - stepZ
399 --Skill 22 (Vigilance) is the last General Skill, and also the last skill on the left column
400 -- So set the x and y to the right column values
401 if i == 22 then
402 currentX = -1.85
403 currentZ = startZ
404 --There's a gap of two rows between the combat skills and the knowledge skills
405 elseif i == 28 then
406 currentZ = currentZ - 1 * stepZ
407 end
408 end
409end
410
411function createDiceDisplay(targetObject, startX, baseZ)
412 --Set the defaults for the character sheets, since that's the main way this function will be used
413 if startX == nil then startX = -0.6 end
414 if baseZ == nil then baseZ = -2.3 end
415 --Create a table. First row is dice name and count, second row is 'Add' button, third row is 'Remove' button
416 local currentX = startX
417 for i,dicename in ipairs(dicenames) do
418 --Dice name
419 createButton(targetObject, dicename, dicename, currentX, baseZ, 75, 0, 0)
420 --Dice count
421 createButton(targetObject, dicename .. "Count", tostring(dicecounts[i]), currentX, baseZ - 0.15, 75, 0, 0)
422 --'Add' and 'Remove' button
423 createButton(targetObject, "spawn" .. dicename .. "Dice", "Add", currentX, baseZ - 0.35, 75, 75, 185)
424 createButton(targetObject, "despawn" .. dicename .. "Dice", "Remove", currentX, baseZ - 0.6, 75, 74, 325)
425
426 --Move over to the next column
427 currentX = currentX - 0.7
428 end
429 --Buttons for dice use
430 createButton(targetObject, "rollDice", "Roll", startX - 1.2, baseZ - 0.9, 100, 100, 300)
431 createButton(targetObject, "startDiceReading", "Read", startX - 1.9, baseZ - 0.9, 100, 100, 300)
432 createButton(targetObject, "clearDice", "Clear", startX - 2.6, baseZ - 0.9, 100, 100, 300)
433 --Display the outcome of the last roll
434 createButton(targetObject, "dicerollOutcomeDisplay", "", startX - 1.9, baseZ - 1.15, 125, 0, 0)
435end
436
437--Handle character sheet and dice deletion by removing all references and stored data
438function onObjectDestroy(destroyedObject)
439 local index = findIndexOfValue(characterSheets, destroyedObject)
440 if index ~= nil then
441 --The destroyed object is a character sheet. Remove everything we've got stored on it
442 table.remove(characterSheets, index)
443 table.remove(characterSheetsData, index)
444 table.remove(characterSheetsGUIDs, index)
445 --Only check if it's a dice when we aren't mass-deleting them
446 elseif areClearingDice == false then
447 index = findIndexOfValue(spawnedDice, destroyedObject)
448 if index ~= nil then
449 --Dice got destroyed, remove reference to it
450 table.remove(spawnedDice, index)
451 --and update the dicecount
452 local diceIndex = findIndexOfValue(dicenames, destroyedObject.getName())
453 dicecounts[diceIndex] = dicecounts[diceIndex] - 1
454 updateDicecountDisplays(diceIndex)
455 end
456 end
457end
458
459
460--DICE-RELATED FUNCTIONS
461function determineDicepoolForSkill(characterSheetIndex, skillIndex)
462 local dicepool = {}
463 local skillValue = characterSheetsData[characterSheetIndex].skillsValues[skillIndex]
464 local characteristicValue = characterSheetsData[characterSheetIndex].characteristicsValues[characteristicInfluencingSkills[skillIndex]]
465 local dicecount = math.max(skillValue, characteristicValue)
466 dicepool["Proficiency"] = math.min(skillValue, characteristicValue)
467 dicepool["Ability"] = dicecount - dicepool["Proficiency"]
468 return dicepool
469end
470
471function getDicepoolDisplayString(dicepool)
472 --Build up the display string
473 local displaystring = ""
474 if dicepool["Proficiency"] > 0 then
475 displaystring = tostring(dicepool["Proficiency"]) .. " prof"
476 end
477 if dicepool["Proficiency"] > 0 and dicepool["Ability"] > 0 then
478 displaystring = displaystring .. ", "
479 end
480 if dicepool["Ability"] > 0 then
481 displaystring = displaystring .. tostring(dicepool["Ability"]) .. " ab"
482 end
483 return displaystring
484end
485
486function updateDicepoolDisplayForSkill(characterSheetIndex, skillIndex)
487 --Set the button text to the dicepool description
488 local labelText = getDicepoolDisplayString(determineDicepoolForSkill(characterSheetIndex, skillIndex))
489 updateButtonText(characterSheets[characterSheetIndex], "spawnDicepoolFor" .. sanitizeStringForFunctionName(skills[skillIndex]), labelText, true)
490end
491
492function recalculateDicepoolPerSkill(characterSheetIndex, characteristicIndex)
493 --Go through all the skills that are influenced by the provided characteristic and recalculate their dicepools
494 for skillIndex = 1,#characteristicInfluencingSkills do
495 if characteristicIndex == nil or characteristicInfluencingSkills[skillIndex] == characteristicIndex then
496 updateDicepoolDisplayForSkill(characterSheetIndex, skillIndex)
497 end
498 end
499end
500
501function updateDicerollOutcomeDisplays(outcomeString)
502 updateButtonText(self, "dicerollOutcomeDisplay", outcomeString)
503 for i,charsheet in ipairs(characterSheets) do
504 updateButtonText(charsheet, "dicerollOutcomeDisplay", outcomeString)
505 end
506end
507
508function spawnDicepool(characterSheet, skillIndex)
509 local characterSheetIndex = findIndexOfValue(characterSheets, characterSheet)
510 local dicepool = determineDicepoolForSkill(characterSheetIndex, skillIndex)
511 for dicename, dicecount in pairs(dicepool) do
512 --Check if we even have to spawn dice
513 if dicecount > 0 then
514 local diceIndex = findIndexOfValue(dicenames, dicename)
515 for i = 1,dicecount do
516 spawnDice(diceIndex, false, i * 2)
517 end
518 --And update the dice count displays
519 updateDicecountDisplays(diceIndex)
520 end
521 end
522end
523
524function spawnDice(diceIndex, shouldUpdateDiceDisplays, diceHeight)
525 --The collider of the tray doesn't work if the tray isn't locked, since it's convex. So make sure it's locked
526 self.setLock(true)
527 --If we haven't been specifically told not to update, update
528 if shouldUpdateDiceDisplays == nil then shouldUpdateDiceDisplays = true end
529 local pos = self.getPosition()
530 --Randomise dice position to prevent phasing through each other on spawn
531 -- Stay within -1.75 to +1.75, both on the x and z axes
532 local spawnPosition = {pos.x - 1.75 + math.random() * 3.5, pos.y + 2, pos.z - 1.75 + math.random() * 3.5}
533 if diceHeight ~= nil then
534 spawnPosition[2] = diceHeight
535 end
536 local dice = spawnObject({type="Custom_Dice", position=spawnPosition})
537 --'dicedata' is a list of two-sized tables, [1] is the dice type (0 for 4-sided, 1 for 6-sided, etc), [2] is image url
538 dice.setCustomObject({type=dicedata[diceIndex][1], image=dicedata[diceIndex][2]})
539 dice.setName(dicenames[diceIndex]) --Make it easier to determine the type of dice when reading
540 dice.tooltip = false --Hide the mouse-over text you normally see when hovering over a dice
541 --Store the dice for later checks and despawning
542 table.insert(spawnedDice, dice)
543 --Update the stored dice cound
544 dicecounts[diceIndex] = dicecounts[diceIndex] + 1
545 if shouldUpdateDiceDisplays then
546 updateDicecountDisplays(diceIndex)
547 end
548end
549function despawnDice(diceIndex)
550 for i,dice in ipairs(spawnedDice) do
551 if dice.getName() == dicenames[diceIndex] then
552 dice.destruct()
553 break
554 end
555 end
556end
557
558
559function rollDice()
560 --The tray collider acts as a box collider when unlocked, which could lead to weird roles. Make sure that doesn't happen
561 self.setLock(true)
562 for i,dice in ipairs(spawnedDice) do
563 dice.randomize()
564 end
565 startDiceReading()
566end
567
568function startDiceReading()
569 --Clear the previous diceroll outcome displays
570 updateDicerollOutcomeDisplays("")
571 --Start the dice reading function
572 local cr = coroutine.create(waitForDiceToStopRolling)
573 coroutine.resume(cr)
574end
575function waitForDiceToStopRolling(numberOfChecksLeft)
576 if numberOfChecksLeft == nil then numberOfChecksLeft = {10} end --as a table since that's what the timer passes
577 for i, dice in ipairs(spawnedDice) do
578 if dice.resting == false then
579 if numberOfChecksLeft[1] > 0 then
580 numberOfChecksLeft[1] = numberOfChecksLeft[1] - 1
581 -- Make sure the existing Timer is fully destroyed
582 Timer.destroy(self.getGUID())
583 -- Then make a new timer to try again
584 Timer.create({delay=1.0, identifier=self.getGUID(), function_name="waitForDiceToStopRolling", parameters=numberOfChecksLeft})
585 return
586 else
587 --Waited for dice to stop rolling for too long, let's just stop trying
588 break
589 end
590 end
591 end
592 if numberOfChecksLeft[1] == 0 then printToAll("WARNING: Waiting for dice roll to finish took too long, results may be inaccurate", {1,0,0}) end
593 showDiceValues()
594end
595
596function showDiceValues()
597 local successCount = 0
598 local advantageCount = 0
599 local triumphCount = 0
600 local despairCount = 0
601 for i, dice in ipairs(spawnedDice) do
602 local dicename = dice.getName()
603 local diceIndex = findIndexOfValue(dicenames, dicename)
604 --the value table stores two values per side
605 -- So 'dicevalues[valueIndex-1]' is the number of successes, 'dicevalues[valueIndex]' number of advantages
606 -- (side 1 is index 1 and 2, side 2 is 3 and 4, etc)
607 local valueIndex = dice.getValue() * 2
608 successCount = successCount + dicevalues[diceIndex][valueIndex-1]
609 advantageCount = advantageCount + dicevalues[diceIndex][valueIndex]
610 --Triumph and despair results are special cases, they're on the proficience and challenge dice respectively, and only on a 12-roll (here on side 4)
611 if dice.getValue() == 4 then
612 if dicename == "Proficiency" then
613 triumphCount = triumphCount + 1
614 elseif dicename == "Challenge" then
615 despairCount = despairCount + 1
616 end
617 end
618 end
619 --Now print the result
620 local successDescriptionString = "successes"
621 if successCount < 0 then
622 successCount = successCount * -1
623 successDescriptionString = "failures"
624 end
625 local advantageDescriptionString = "advantages"
626 if advantageCount < 0 then
627 advantageCount = advantageCount * -1
628 advantageDescriptionString = "threats"
629 end
630 local resultString = string.format("%d %s, %d %s", successCount, successDescriptionString, advantageCount, advantageDescriptionString)
631 if triumphCount > 0 then resultString = string.format("%s, %d triumphs", resultString, triumphCount) end
632 if despairCount > 0 then resultString = string.format("%s, %d despairs", resultString, despairCount) end
633 printToAll(resultString, {1,1,1})
634 --Update all the outcome displays
635 updateDicerollOutcomeDisplays(resultString)
636end
637
638function clearDice()
639 --Reset the dicecounts
640 for i = 1,#dicecounts do
641 dicecounts[i] = 0
642 updateDicecountDisplays(i)
643 end
644 --Clear the diceroll outcome displays
645 updateDicerollOutcomeDisplays("")
646 --And now actually destroy the dice
647 areClearingDice = true --Prevent frequent display updates
648 for i,dice in ipairs(spawnedDice) do
649 dice.destruct()
650 end
651 --Clear the array
652 spawnedDice = {}
653 --Done!
654 areClearingDice = false
655end
656
657
658--VALUE CHANGING FUNCTIONS
659function editCharacterDescriptors(characterSheet)
660 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
661 local charSheetValues = characterSheetsData[charSheetIndex]
662 if characterDescriptorNotepads[characterSheet] == nil then
663 --First lock the sheet, to make notepad selection easier and to make sure it stays positioned right
664 characterSheet.setLock(true)
665 --Create a notecard for users to fill in their descriptors
666 local sheetPosition = characterSheet.getPosition()
667 local sheetRotation = characterSheet.getRotation()
668 --Calculate the relative position for the notecard, taking the charsheet's rotation into account
669 local targetAngle = sheetRotation.y + 82
670 local xDistance = math.cos(math.rad(targetAngle)) * 7.27
671 local zDistance = math.sin(math.rad(targetAngle)) * 7.27
672
673 local notecardPos = {sheetPosition.x - xDistance, sheetPosition.y + 0.4, sheetPosition.z + zDistance}
674 local notecardRot = {sheetRotation.x, sheetRotation.y, sheetRotation.z}
675 characterDescriptorNotepads[characterSheet] = spawnObject({type="Notecard", position=notecardPos, rotation=notecardRot})
676 characterDescriptorNotepads[characterSheet].setLock(true)
677 characterDescriptorNotepads[characterSheet].setName("Character Descriptors")
678 local notecardText = "Right-click to edit description, fill in your info after the colons, then press 'Save'"
679 for i=2,#characterDescriptors do
680 notecardText = notecardText .. "\n[b]" .. characterDescriptors[i] .. "[/b]: " .. charSheetValues.characterDescriptorsValues[i]
681 end
682 characterDescriptorNotepads[characterSheet].setDescription(notecardText)
683 --Update the button text to say 'Save'
684 updateButtonText(characterSheet, "editCharacterDescriptors", "Save")
685 else
686 --A notecard already exists, and we need to save the data on it
687 --Clear the global reference first, so any errors don't brick this function
688 local notecard = characterDescriptorNotepads[characterSheet]
689 characterDescriptorNotepads[characterSheet] = nil
690 -- Change the button text back to 'Edit'
691 updateButtonText(characterSheet, "editCharacterDescriptors", "Edit")
692
693 --Notecard exists, read it out (Lines are '[b]descriptorName[/b]: value')
694 for descriptor, value in string.gmatch(notecard.getDescription(), "%[b%](.-)%[/b%]: ([%w%p ]+)") do
695 if descriptor ~= nil and value ~= nil then
696 value = string.gsub(value, "\n", "; ")
697 local descriptorIndex = findIndexOfValue(characterDescriptors, descriptor)
698 if descriptorIndex == nil then
699 printError("Unknown character descriptor '" .. descriptor .. "'")
700 else
701 charSheetValues.characterDescriptorsValues[descriptorIndex] = value
702 updateButtonText(characterSheet, characterDescriptors[descriptorIndex], value)
703 end
704 end
705 end
706 --We no longer need the card, destroy it
707 notecard.destruct()
708 end
709end
710
711function setOwnerToClickedPlayer(characterSheet, clickingPlayerColor)
712 local characterSheetIndex = findIndexOfValue(characterSheets, characterSheet)
713 --player name is the first entry in 'characterDescriptors
714 characterSheetsData[characterSheetIndex].characterDescriptorsValues[1] = Player[clickingPlayerColor].steam_name
715 --Update the displayed name
716 updateButtonText(characterSheet, "playername", characterSheetsData[characterSheetIndex].characterDescriptorsValues[1])
717end
718
719function changeBattleFieldValue(characterSheet, battleFieldIndex, changeAmount)
720 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
721 local charSheetValues = characterSheetsData[charSheetIndex]
722 local newValue = charSheetValues.battleFieldsValues[battleFieldIndex] + changeAmount
723 if newValue >= 0 then
724 charSheetValues.battleFieldsValues[battleFieldIndex] = newValue
725 --Update display
726 updateButtonText(characterSheet, battleFields[battleFieldIndex], tostring(newValue))
727 end
728end
729
730function changeCharacteristicValue(characterSheet, characteristicIndex, changeAmount)
731 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
732 local charSheetValues = characterSheetsData[charSheetIndex]
733 local newValue = charSheetValues.characteristicsValues[characteristicIndex] + changeAmount
734 --Characteristics can't be zero or negative
735 if newValue > 0 then
736 charSheetValues.characteristicsValues[characteristicIndex] = newValue
737 --Update the value display
738 updateButtonText(characterSheet, characteristics[characteristicIndex], tostring(newValue))
739 --Recalculate the dicepools for all the skills that depend on the characteristic we just changed
740 recalculateDicepoolPerSkill(charSheetIndex, characteristicIndex)
741 end
742end
743
744function toggleCareerSkill(characterSheet, skillIndex)
745 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
746 local charSheetValues = characterSheetsData[charSheetIndex]
747 local careerButtonLabel = ""
748 local indexInCareerSkills = findIndexOfValue(charSheetValues.careerSkills, skillIndex)
749 if indexInCareerSkills ~= nil then
750 --It exists, so remove it
751 table.remove(charSheetValues.careerSkills, indexInCareerSkills)
752 careerButtonLabel = "N"
753 else
754 --It's not there, add it
755 table.insert(charSheetValues.careerSkills, skillIndex)
756 careerButtonLabel = "Y"
757 end
758 --Now update the skill's career button
759 updateButtonText(characterSheet, "toggle" .. sanitizeStringForFunctionName(skills[skillIndex]) .. "AsCareerSkill", careerButtonLabel)
760end
761
762function changeSkillRank(characterSheet, skillIndex, changeAmount)
763 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
764 local charSheetValues = characterSheetsData[charSheetIndex]
765 local newValue = charSheetValues.skillsValues[skillIndex] + changeAmount
766 --Cap skill ranks at a minimum of 0 and a max of 5
767 newValue = math.max(newValue, 0)
768 newValue = math.min(newValue, 5)
769 --Only act if the resulting skill value actually changed
770 if newValue ~= charSheetValues.skillsValues[skillIndex] then
771 charSheetValues.skillsValues[skillIndex] = newValue
772 --Update the rank display
773 updateButtonText(characterSheet, sanitizeStringForFunctionName(skills[skillIndex]), tostring(newValue))
774 --Recalculate dicepool for this skill
775 updateDicepoolDisplayForSkill(charSheetIndex, skillIndex)
776 end
777end
778
779function changeTotalXP(characterSheet, changeAmount)
780 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
781 local charSheetValues = characterSheetsData[charSheetIndex]
782 charSheetValues.totalXP = charSheetValues.totalXP + changeAmount
783 if charSheetValues.totalXP < 0 then charSheetValues.totalXP = 0 end
784 --Update the total XP display
785 updateButtonText(characterSheet, "totalXP", tostring(charSheetValues.totalXP))
786end
787function changeAvailableXP(characterSheet, changeAmount)
788 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
789 local charSheetValues = characterSheetsData[charSheetIndex]
790 charSheetValues.availableXP = charSheetValues.availableXP + changeAmount
791 if charSheetValues.availableXP < 0 then charSheetValues.availableXP = 0 end
792 --Update the available XP display
793 updateButtonText(characterSheet, "availableXP", tostring(charSheetValues.availableXP))
794end
795function changeForceRank(characterSheet, changeAmount)
796 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
797 local charSheetValues = characterSheetsData[charSheetIndex]
798 charSheetValues.ForceRank = charSheetValues.ForceRank + changeAmount
799 if charSheetValues.ForceRank < 0 then charSheetValues.ForceRank = 0 end
800 --Update the Force Rank display
801 updateButtonText(characterSheet, "ForceRank", tostring(charSheetValues.ForceRank))
802 changeForceAvailable(characterSheet, changeAmount)
803end
804function changeForceCommitted(characterSheet, changeAmount)
805 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
806 local charSheetValues = characterSheetsData[charSheetIndex]
807 charSheetValues.ForceCommitted = charSheetValues.ForceCommitted + changeAmount
808 if charSheetValues.ForceCommitted < 0 then charSheetValues.ForceCommitted = 0
809 elseif charSheetValues.ForceCommitted > charSheetValues.ForceRank then charSheetValues.ForceCommitted = charSheetValues.ForceRank end
810 --Update the Force Committed display
811 updateButtonText(characterSheet, "ForceCommitted", tostring(charSheetValues.ForceCommitted))
812 changeForceAvailable(characterSheet, changeAmount)
813end
814function changeForceAvailable(characterSheet, changeAmount)
815 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
816 local charSheetValues = characterSheetsData[charSheetIndex]
817 charSheetValues.ForceAvailable = charSheetValues.ForceRank - charSheetValues.ForceCommitted
818 --Update the Force Available display
819 updateButtonText(characterSheet, "ForceAvailable", tostring(charSheetValues.ForceAvailable))
820end
821function changeEncumbranceThreshold(characterSheet, changeAmount)
822 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
823 local charSheetValues = characterSheetsData[charSheetIndex]
824 charSheetValues.EncumbranceThreshold = charSheetValues.EncumbranceThreshold + changeAmount
825 if charSheetValues.EncumbranceThreshold < 0 then charSheetValues.EncumbranceThreshold = 0 end
826 --Update the Force Rank display
827 updateButtonText(characterSheet, "EncumbranceThreshold", tostring(charSheetValues.EncumbranceThreshold))
828end
829function changeEncumbrance(characterSheet, changeAmount)
830 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
831 local charSheetValues = characterSheetsData[charSheetIndex]
832 charSheetValues.Encumbrance = charSheetValues.Encumbrance + changeAmount
833 if charSheetValues.Encumbrance < 0 then charSheetValues.Encumbrance = 0 end
834 --Update the Force Rank display
835 updateButtonText(characterSheet, "Encumbrance", tostring(charSheetValues.Encumbrance))
836end
837
838function toggleDisplayMode(characterSheet)
839 local charSheetIndex = findIndexOfValue(characterSheets, characterSheet)
840 local charSheetValues = characterSheetsData[charSheetIndex]
841 if charSheetValues.displaymode == "Edit" then
842 charSheetValues.displaymode = "Play"
843 elseif charSheetValues.displaymode == "Play" then
844 charSheetValues.displaymode = "View"
845 else
846 charSheetValues.displaymode = "Edit"
847 end
848 rebuildSheetDisplay(charSheetIndex)
849end
850
851
852--BUTTON FUNCTIONS
853--Force Rank values
854function decreaseForceRank(clickedCharSheet)
855 changeForceRank(clickedCharSheet, -1)
856end
857function increaseForceRank(clickedCharSheet)
858 changeForceRank(clickedCharSheet, 1)
859end
860--Force Committed Values
861function decreaseForceCommitted(clickedCharSheet)
862 changeForceCommitted(clickedCharSheet, -1)
863end
864function increaseForceCommitted(clickedCharSheet)
865 changeForceCommitted(clickedCharSheet, 1)
866end
867--Encumbrance values
868function decreaseEncumbranceThreshold(clickedCharSheet)
869 changeEncumbranceThreshold(clickedCharSheet, -1)
870end
871function increaseEncumbranceThreshold(clickedCharSheet)
872 changeEncumbranceThreshold(clickedCharSheet, 1)
873end
874function decreaseEncumbrance(clickedCharSheet)
875 changeEncumbrance(clickedCharSheet, -1)
876end
877function increaseEncumbrance(clickedCharSheet)
878 changeEncumbrance(clickedCharSheet, 1)
879end
880--Battle Fields values
881function decreaseSoak(clickedCharSheet)
882 changeBattleFieldValue(clickedCharSheet, 1, -1)
883end
884function increaseSoak(clickedCharSheet)
885 changeBattleFieldValue(clickedCharSheet, 1, 1)
886end
887function decreaseWoundsThreshold(clickedCharSheet)
888 changeBattleFieldValue(clickedCharSheet, 2, -1)
889end
890function increaseWoundsThreshold(clickedCharSheet)
891 changeBattleFieldValue(clickedCharSheet, 2, 1)
892end
893function decreaseWoundsCurrent(clickedCharSheet)
894 changeBattleFieldValue(clickedCharSheet, 3, -1)
895end
896function increaseWoundsCurrent(clickedCharSheet)
897 changeBattleFieldValue(clickedCharSheet, 3, 1)
898end
899function decreaseStrainThreshold(clickedCharSheet)
900 changeBattleFieldValue(clickedCharSheet, 4, -1)
901end
902function increaseStrainThreshold(clickedCharSheet)
903 changeBattleFieldValue(clickedCharSheet, 4, 1)
904end
905function decreaseStrainCurrent(clickedCharSheet)
906 changeBattleFieldValue(clickedCharSheet, 5, -1)
907end
908function increaseStrainCurrent(clickedCharSheet)
909 changeBattleFieldValue(clickedCharSheet, 5, 1)
910end
911function decreaseDefenseRanged(clickedCharSheet)
912 changeBattleFieldValue(clickedCharSheet, 6, -1)
913end
914function increaseDefenseRanged(clickedCharSheet)
915 changeBattleFieldValue(clickedCharSheet, 6, 1)
916end
917function decreaseDefenseMelee(clickedCharSheet)
918 changeBattleFieldValue(clickedCharSheet, 7, -1)
919end
920function increaseDefenseMelee(clickedCharSheet)
921 changeBattleFieldValue(clickedCharSheet, 7, 1)
922end
923function decreaseCritWound(clickedCharSheet)
924 changeBattleFieldValue(clickedCharSheet, 8, -1)
925end
926function increaseCritWound(clickedCharSheet)
927 changeBattleFieldValue(clickedCharSheet, 8, 1)
928end
929
930--Characteristics buttons
931function decreaseBrawn(clickedCharSheet)
932 changeCharacteristicValue(clickedCharSheet, 1, -1)
933end
934function increaseBrawn(clickedCharSheet)
935 changeCharacteristicValue(clickedCharSheet, 1, 1)
936end
937function decreaseAgility(clickedCharSheet)
938 changeCharacteristicValue(clickedCharSheet, 2, -1)
939end
940function increaseAgility(clickedCharSheet)
941 changeCharacteristicValue(clickedCharSheet, 2, 1)
942end
943function decreaseIntellect(clickedCharSheet)
944 changeCharacteristicValue(clickedCharSheet, 3, -1)
945end
946function increaseIntellect(clickedCharSheet)
947 changeCharacteristicValue(clickedCharSheet, 3, 1)
948end
949function decreaseCunning(clickedCharSheet)
950 changeCharacteristicValue(clickedCharSheet, 4, -1)
951end
952function increaseCunning(clickedCharSheet)
953 changeCharacteristicValue(clickedCharSheet, 4, 1)
954end
955function decreaseWillpower(clickedCharSheet)
956 changeCharacteristicValue(clickedCharSheet, 5, -1)
957end
958function increaseWillpower(clickedCharSheet)
959 changeCharacteristicValue(clickedCharSheet, 5, 1)
960end
961function decreasePresence(clickedCharSheet)
962 changeCharacteristicValue(clickedCharSheet, 6, -1)
963end
964function increasePresence(clickedCharSheet)
965 changeCharacteristicValue(clickedCharSheet, 6, 1)
966end
967
968--Career skill toggle buttons
969function toggleAstrogationAsCareerSkill(clickedCharSheet)
970 toggleCareerSkill(clickedCharSheet, 1)
971end
972function toggleAthleticsAsCareerSkill(clickedCharSheet)
973 toggleCareerSkill(clickedCharSheet, 2)
974end
975function toggleCharmAsCareerSkill(clickedCharSheet)
976 toggleCareerSkill(clickedCharSheet, 3)
977end
978function toggleCoercionAsCareerSkill(clickedCharSheet)
979 toggleCareerSkill(clickedCharSheet, 4)
980end
981function toggleComputersAsCareerSkill(clickedCharSheet)
982 toggleCareerSkill(clickedCharSheet, 5)
983end
984function toggleCoolAsCareerSkill(clickedCharSheet)
985 toggleCareerSkill(clickedCharSheet, 6)
986end
987function toggleCoordinationAsCareerSkill(clickedCharSheet)
988 toggleCareerSkill(clickedCharSheet, 7)
989end
990function toggleDeceptionAsCareerSkill(clickedCharSheet)
991 toggleCareerSkill(clickedCharSheet, 8)
992end
993function toggleDisciplineAsCareerSkill(clickedCharSheet)
994 toggleCareerSkill(clickedCharSheet, 9)
995end
996function toggleLeadershipAsCareerSkill(clickedCharSheet)
997 toggleCareerSkill(clickedCharSheet, 10)
998end
999function toggleMechanicsAsCareerSkill(clickedCharSheet)
1000 toggleCareerSkill(clickedCharSheet, 11)
1001end
1002function toggleMedicineAsCareerSkill(clickedCharSheet)
1003 toggleCareerSkill(clickedCharSheet, 12)
1004end
1005function toggleNegotiationAsCareerSkill(clickedCharSheet)
1006 toggleCareerSkill(clickedCharSheet, 13)
1007end
1008function togglePerceptionAsCareerSkill(clickedCharSheet)
1009 toggleCareerSkill(clickedCharSheet, 14)
1010end
1011function togglePiloting_PlanetaryAsCareerSkill(clickedCharSheet)
1012 toggleCareerSkill(clickedCharSheet, 15)
1013end
1014function togglePiloting_SpaceAsCareerSkill(clickedCharSheet)
1015 toggleCareerSkill(clickedCharSheet, 16)
1016end
1017function toggleResilienceAsCareerSkill(clickedCharSheet)
1018 toggleCareerSkill(clickedCharSheet, 17)
1019end
1020function toggleSkulduggeryAsCareerSkill(clickedCharSheet)
1021 toggleCareerSkill(clickedCharSheet, 18)
1022end
1023function toggleStealthAsCareerSkill(clickedCharSheet)
1024 toggleCareerSkill(clickedCharSheet, 19)
1025end
1026function toggleStreetwiseAsCareerSkill(clickedCharSheet)
1027 toggleCareerSkill(clickedCharSheet, 20)
1028end
1029function toggleSurvivalAsCareerSkill(clickedCharSheet)
1030 toggleCareerSkill(clickedCharSheet, 21)
1031end
1032function toggleVigilanceAsCareerSkill(clickedCharSheet)
1033 toggleCareerSkill(clickedCharSheet, 22)
1034end
1035function toggleBrawlAsCareerSkill(clickedCharSheet)
1036 toggleCareerSkill(clickedCharSheet, 23)
1037end
1038function toggleGunneryAsCareerSkill(clickedCharSheet)
1039 toggleCareerSkill(clickedCharSheet, 24)
1040end
1041function toggleMeleeAsCareerSkill(clickedCharSheet)
1042 toggleCareerSkill(clickedCharSheet, 25)
1043end
1044function toggleRanged_LightAsCareerSkill(clickedCharSheet)
1045 toggleCareerSkill(clickedCharSheet, 26)
1046end
1047function toggleRanged_HeavyAsCareerSkill(clickedCharSheet)
1048 toggleCareerSkill(clickedCharSheet, 27)
1049end
1050function toggleLightsaberAsCareerSkill(clickedCharSheet)
1051 toggleCareerSkill(clickedCharSheet, 28)
1052end
1053function toggleCoreWorldsAsCareerSkill(clickedCharSheet)
1054 toggleCareerSkill(clickedCharSheet, 29)
1055end
1056function toggleEducationAsCareerSkill(clickedCharSheet)
1057 toggleCareerSkill(clickedCharSheet, 30)
1058end
1059function toggleLoreAsCareerSkill(clickedCharSheet)
1060 toggleCareerSkill(clickedCharSheet, 31)
1061end
1062function toggleOuterRimAsCareerSkill(clickedCharSheet)
1063 toggleCareerSkill(clickedCharSheet, 32)
1064end
1065function toggleUnderworldAsCareerSkill(clickedCharSheet)
1066 toggleCareerSkill(clickedCharSheet, 33)
1067end
1068function toggleXenologyAsCareerSkill(clickedCharSheet)
1069 toggleCareerSkill(clickedCharSheet, 34)
1070end
1071function toggleOtherAsCareerSkill(clickedCharSheet)
1072 toggleCareerSkill(clickedCharSheet, 35)
1073end
1074
1075--Rank changes
1076function decreaseRankInAstrogation(clickedCharSheet)
1077 changeSkillRank(clickedCharSheet, 1, -1)
1078end
1079function increaseRankInAstrogation(clickedCharSheet)
1080 changeSkillRank(clickedCharSheet, 1, 1)
1081end
1082function decreaseRankInAthletics(clickedCharSheet)
1083 changeSkillRank(clickedCharSheet, 2, -1)
1084end
1085function increaseRankInAthletics(clickedCharSheet)
1086 changeSkillRank(clickedCharSheet, 2, 1)
1087end
1088function decreaseRankInCharm(clickedCharSheet)
1089 changeSkillRank(clickedCharSheet, 3, -1)
1090end
1091function increaseRankInCharm(clickedCharSheet)
1092 changeSkillRank(clickedCharSheet, 3, 1)
1093end
1094function decreaseRankInCoercion(clickedCharSheet)
1095 changeSkillRank(clickedCharSheet, 4, -1)
1096end
1097function increaseRankInCoercion(clickedCharSheet)
1098 changeSkillRank(clickedCharSheet, 4, 1)
1099end
1100function decreaseRankInComputers(clickedCharSheet)
1101 changeSkillRank(clickedCharSheet, 5, -1)
1102end
1103function increaseRankInComputers(clickedCharSheet)
1104 changeSkillRank(clickedCharSheet, 5, 1)
1105end
1106function decreaseRankInCool(clickedCharSheet)
1107 changeSkillRank(clickedCharSheet, 6, -1)
1108end
1109function increaseRankInCool(clickedCharSheet)
1110 changeSkillRank(clickedCharSheet, 6, 1)
1111end
1112function decreaseRankInCoordination(clickedCharSheet)
1113 changeSkillRank(clickedCharSheet, 7, -1)
1114end
1115function increaseRankInCoordination(clickedCharSheet)
1116 changeSkillRank(clickedCharSheet, 7, 1)
1117end
1118function decreaseRankInDeception(clickedCharSheet)
1119 changeSkillRank(clickedCharSheet, 8, -1)
1120end
1121function increaseRankInDeception(clickedCharSheet)
1122 changeSkillRank(clickedCharSheet, 8, 1)
1123end
1124function decreaseRankInDiscipline(clickedCharSheet)
1125 changeSkillRank(clickedCharSheet, 9, -1)
1126end
1127function increaseRankInDiscipline(clickedCharSheet)
1128 changeSkillRank(clickedCharSheet, 9, 1)
1129end
1130function decreaseRankInLeadership(clickedCharSheet)
1131 changeSkillRank(clickedCharSheet, 10, -1)
1132end
1133function increaseRankInLeadership(clickedCharSheet)
1134 changeSkillRank(clickedCharSheet, 10, 1)
1135end
1136function decreaseRankInMechanics(clickedCharSheet)
1137 changeSkillRank(clickedCharSheet, 11, -1)
1138end
1139function increaseRankInMechanics(clickedCharSheet)
1140 changeSkillRank(clickedCharSheet, 11, 1)
1141end
1142function decreaseRankInMedicine(clickedCharSheet)
1143 changeSkillRank(clickedCharSheet, 12, -1)
1144end
1145function increaseRankInMedicine(clickedCharSheet)
1146 changeSkillRank(clickedCharSheet, 12, 1)
1147end
1148function decreaseRankInNegotiation(clickedCharSheet)
1149 changeSkillRank(clickedCharSheet, 13, -1)
1150end
1151function increaseRankInNegotiation(clickedCharSheet)
1152 changeSkillRank(clickedCharSheet, 13, 1)
1153end
1154function decreaseRankInPerception(clickedCharSheet)
1155 changeSkillRank(clickedCharSheet, 14, -1)
1156end
1157function increaseRankInPerception(clickedCharSheet)
1158 changeSkillRank(clickedCharSheet, 14, 1)
1159end
1160function decreaseRankInPiloting_Planetary(clickedCharSheet)
1161 changeSkillRank(clickedCharSheet, 15, -1)
1162end
1163function increaseRankInPiloting_Planetary(clickedCharSheet)
1164 changeSkillRank(clickedCharSheet, 15, 1)
1165end
1166function decreaseRankInPiloting_Space(clickedCharSheet)
1167 changeSkillRank(clickedCharSheet, 16, -1)
1168end
1169function increaseRankInPiloting_Space(clickedCharSheet)
1170 changeSkillRank(clickedCharSheet, 16, 1)
1171end
1172function decreaseRankInResilience(clickedCharSheet)
1173 changeSkillRank(clickedCharSheet, 17, -1)
1174end
1175function increaseRankInResilience(clickedCharSheet)
1176 changeSkillRank(clickedCharSheet, 17, 1)
1177end
1178function decreaseRankInSkulduggery(clickedCharSheet)
1179 changeSkillRank(clickedCharSheet, 18, -1)
1180end
1181function increaseRankInSkulduggery(clickedCharSheet)
1182 changeSkillRank(clickedCharSheet, 18, 1)
1183end
1184function decreaseRankInStealth(clickedCharSheet)
1185 changeSkillRank(clickedCharSheet, 19, -1)
1186end
1187function increaseRankInStealth(clickedCharSheet)
1188 changeSkillRank(clickedCharSheet, 19, 1)
1189end
1190function decreaseRankInStreetwise(clickedCharSheet)
1191 changeSkillRank(clickedCharSheet, 20, -1)
1192end
1193function increaseRankInStreetwise(clickedCharSheet)
1194 changeSkillRank(clickedCharSheet, 20, 1)
1195end
1196function decreaseRankInSurvival(clickedCharSheet)
1197 changeSkillRank(clickedCharSheet, 21, -1)
1198end
1199function increaseRankInSurvival(clickedCharSheet)
1200 changeSkillRank(clickedCharSheet, 21, 1)
1201end
1202function decreaseRankInVigilance(clickedCharSheet)
1203 changeSkillRank(clickedCharSheet, 22, -1)
1204end
1205function increaseRankInVigilance(clickedCharSheet)
1206 changeSkillRank(clickedCharSheet, 22, 1)
1207end
1208function decreaseRankInBrawl(clickedCharSheet)
1209 changeSkillRank(clickedCharSheet, 23, -1)
1210end
1211function increaseRankInBrawl(clickedCharSheet)
1212 changeSkillRank(clickedCharSheet, 23, 1)
1213end
1214function decreaseRankInGunnery(clickedCharSheet)
1215 changeSkillRank(clickedCharSheet, 24, -1)
1216end
1217function increaseRankInGunnery(clickedCharSheet)
1218 changeSkillRank(clickedCharSheet, 24, 1)
1219end
1220function decreaseRankInMelee(clickedCharSheet)
1221 changeSkillRank(clickedCharSheet, 25, -1)
1222end
1223function increaseRankInMelee(clickedCharSheet)
1224 changeSkillRank(clickedCharSheet, 25, 1)
1225end
1226function decreaseRankInRanged_Light(clickedCharSheet)
1227 changeSkillRank(clickedCharSheet, 26, -1)
1228end
1229function increaseRankInRanged_Light(clickedCharSheet)
1230 changeSkillRank(clickedCharSheet, 26, 1)
1231end
1232function decreaseRankInRanged_Heavy(clickedCharSheet)
1233 changeSkillRank(clickedCharSheet, 27, -1)
1234end
1235function increaseRankInRanged_Heavy(clickedCharSheet)
1236 changeSkillRank(clickedCharSheet, 27, 1)
1237end
1238function decreaseRankInLightsaber(clickedCharSheet)
1239 changeSkillRank(clickedCharSheet, 28, -1)
1240end
1241function increaseRankInLightsaber(clickedCharSheet)
1242 changeSkillRank(clickedCharSheet, 28, 1)
1243end
1244function decreaseRankInCoreWorlds(clickedCharSheet)
1245 changeSkillRank(clickedCharSheet, 29, -1)
1246end
1247function increaseRankInCoreWorlds(clickedCharSheet)
1248 changeSkillRank(clickedCharSheet, 29, 1)
1249end
1250function decreaseRankInEducation(clickedCharSheet)
1251 changeSkillRank(clickedCharSheet, 30, -1)
1252end
1253function increaseRankInEducation(clickedCharSheet)
1254 changeSkillRank(clickedCharSheet, 30, 1)
1255end
1256function decreaseRankInLore(clickedCharSheet)
1257 changeSkillRank(clickedCharSheet, 31, -1)
1258end
1259function increaseRankInLore(clickedCharSheet)
1260 changeSkillRank(clickedCharSheet, 31, 1)
1261end
1262function decreaseRankInOuterRim(clickedCharSheet)
1263 changeSkillRank(clickedCharSheet, 32, -1)
1264end
1265function increaseRankInOuterRim(clickedCharSheet)
1266 changeSkillRank(clickedCharSheet, 32, 1)
1267end
1268function decreaseRankInUnderworld(clickedCharSheet)
1269 changeSkillRank(clickedCharSheet, 33, -1)
1270end
1271function increaseRankInUnderworld(clickedCharSheet)
1272 changeSkillRank(clickedCharSheet, 33, 1)
1273end
1274function decreaseRankInXenology(clickedCharSheet)
1275 changeSkillRank(clickedCharSheet, 34, -1)
1276end
1277function increaseRankInXenology(clickedCharSheet)
1278 changeSkillRank(clickedCharSheet, 34, 1)
1279end
1280function decreaseRankInOther(clickedCharSheet)
1281 changeSkillRank(clickedCharSheet, 35, -1)
1282end
1283function increaseRankInOther(clickedCharSheet)
1284 changeSkillRank(clickedCharSheet, 35, 1)
1285end
1286
1287--Total XP button functions
1288function increaseTotalXPBy1(clickedCharSheet)
1289 changeTotalXP(clickedCharSheet, 1)
1290end
1291function decreaseTotalXPBy1(clickedCharSheet)
1292 changeTotalXP(clickedCharSheet, -1)
1293end
1294function increaseTotalXPBy5(clickedCharSheet)
1295 changeTotalXP(clickedCharSheet, 5)
1296end
1297function decreaseTotalXPBy5(clickedCharSheet)
1298 changeTotalXP(clickedCharSheet, -5)
1299end
1300function increaseTotalXPBy10(clickedCharSheet)
1301 changeTotalXP(clickedCharSheet, 10)
1302end
1303function decreaseTotalXPBy10(clickedCharSheet)
1304 changeTotalXP(clickedCharSheet, -10)
1305end
1306function increaseTotalXPBy50(clickedCharSheet)
1307 changeTotalXP(clickedCharSheet, 50)
1308end
1309function decreaseTotalXPBy50(clickedCharSheet)
1310 changeTotalXP(clickedCharSheet, -50)
1311end
1312function increaseTotalXPBy100(clickedCharSheet)
1313 changeTotalXP(clickedCharSheet, 100)
1314end
1315function decreaseTotalXPBy100(clickedCharSheet)
1316 changeTotalXP(clickedCharSheet, -100)
1317end
1318function increaseTotalXPBy500(clickedCharSheet)
1319 changeTotalXP(clickedCharSheet, 500)
1320end
1321function decreaseTotalXPBy500(clickedCharSheet)
1322 changeTotalXP(clickedCharSheet, -500)
1323end
1324--Available XP button functions
1325function increaseAvailableXPBy1(clickedCharSheet)
1326 changeAvailableXP(clickedCharSheet, 1)
1327end
1328function decreaseAvailableXPBy1(clickedCharSheet)
1329 changeAvailableXP(clickedCharSheet, -1)
1330end
1331function increaseAvailableXPBy5(clickedCharSheet)
1332 changeAvailableXP(clickedCharSheet, 5)
1333end
1334function decreaseAvailableXPBy5(clickedCharSheet)
1335 changeAvailableXP(clickedCharSheet, -5)
1336end
1337function increaseAvailableXPBy10(clickedCharSheet)
1338 changeAvailableXP(clickedCharSheet, 10)
1339end
1340function decreaseAvailableXPBy10(clickedCharSheet)
1341 changeAvailableXP(clickedCharSheet, -10)
1342end
1343function increaseAvailableXPBy50(clickedCharSheet)
1344 changeAvailableXP(clickedCharSheet, 50)
1345end
1346function decreaseAvailableXPBy50(clickedCharSheet)
1347 changeAvailableXP(clickedCharSheet, -50)
1348end
1349function increaseAvailableXPBy100(clickedCharSheet)
1350 changeAvailableXP(clickedCharSheet, 100)
1351end
1352function decreaseAvailableXPBy100(clickedCharSheet)
1353 changeAvailableXP(clickedCharSheet, -100)
1354end
1355function increaseAvailableXPBy500(clickedCharSheet)
1356 changeAvailableXP(clickedCharSheet, 500)
1357end
1358function decreaseAvailableXPBy500(clickedCharSheet)
1359 changeAvailableXP(clickedCharSheet, -500)
1360end
1361
1362--Dice spawning
1363function spawnDicepoolForAstrogation(clickedCharSheet)
1364 spawnDicepool(clickedCharSheet, 1)
1365end
1366function spawnDicepoolForAthletics(clickedCharSheet)
1367 spawnDicepool(clickedCharSheet, 2)
1368end
1369function spawnDicepoolForCharm(clickedCharSheet)
1370 spawnDicepool(clickedCharSheet, 3)
1371end
1372function spawnDicepoolForCoercion(clickedCharSheet)
1373 spawnDicepool(clickedCharSheet, 4)
1374end
1375function spawnDicepoolForComputers(clickedCharSheet)
1376 spawnDicepool(clickedCharSheet, 5)
1377end
1378function spawnDicepoolForCool(clickedCharSheet)
1379 spawnDicepool(clickedCharSheet, 6)
1380end
1381function spawnDicepoolForCoordination(clickedCharSheet)
1382 spawnDicepool(clickedCharSheet, 7)
1383end
1384function spawnDicepoolForDeception(clickedCharSheet)
1385 spawnDicepool(clickedCharSheet, 8)
1386end
1387function spawnDicepoolForDiscipline(clickedCharSheet)
1388 spawnDicepool(clickedCharSheet, 9)
1389end
1390function spawnDicepoolForLeadership(clickedCharSheet)
1391 spawnDicepool(clickedCharSheet, 10)
1392end
1393function spawnDicepoolForMechanics(clickedCharSheet)
1394 spawnDicepool(clickedCharSheet, 11)
1395end
1396function spawnDicepoolForMedicine(clickedCharSheet)
1397 spawnDicepool(clickedCharSheet, 12)
1398end
1399function spawnDicepoolForNegotiation(clickedCharSheet)
1400 spawnDicepool(clickedCharSheet, 13)
1401end
1402function spawnDicepoolForPerception(clickedCharSheet)
1403 spawnDicepool(clickedCharSheet, 14)
1404end
1405function spawnDicepoolForPiloting_Planetary(clickedCharSheet)
1406 spawnDicepool(clickedCharSheet, 15)
1407end
1408function spawnDicepoolForPiloting_Space(clickedCharSheet)
1409 spawnDicepool(clickedCharSheet, 16)
1410end
1411function spawnDicepoolForResilience(clickedCharSheet)
1412 spawnDicepool(clickedCharSheet, 17)
1413end
1414function spawnDicepoolForSkulduggery(clickedCharSheet)
1415 spawnDicepool(clickedCharSheet, 18)
1416end
1417function spawnDicepoolForStealth(clickedCharSheet)
1418 spawnDicepool(clickedCharSheet, 19)
1419end
1420function spawnDicepoolForStreetwise(clickedCharSheet)
1421 spawnDicepool(clickedCharSheet, 20)
1422end
1423function spawnDicepoolForSurvival(clickedCharSheet)
1424 spawnDicepool(clickedCharSheet, 21)
1425end
1426function spawnDicepoolForVigilance(clickedCharSheet)
1427 spawnDicepool(clickedCharSheet, 22)
1428end
1429function spawnDicepoolForBrawl(clickedCharSheet)
1430 spawnDicepool(clickedCharSheet, 23)
1431end
1432function spawnDicepoolForGunnery(clickedCharSheet)
1433 spawnDicepool(clickedCharSheet, 24)
1434end
1435function spawnDicepoolForMelee(clickedCharSheet)
1436 spawnDicepool(clickedCharSheet, 25)
1437end
1438function spawnDicepoolForRanged_Light(clickedCharSheet)
1439 spawnDicepool(clickedCharSheet, 26)
1440end
1441function spawnDicepoolForRanged_Heavy(clickedCharSheet)
1442 spawnDicepool(clickedCharSheet, 27)
1443end
1444function spawnDicepoolForLightsaber(clickedCharSheet)
1445 spawnDicepool(clickedCharSheet, 28)
1446end
1447function spawnDicepoolForCoreWorlds(clickedCharSheet)
1448 spawnDicepool(clickedCharSheet, 29)
1449end
1450function spawnDicepoolForEducation(clickedCharSheet)
1451 spawnDicepool(clickedCharSheet, 30)
1452end
1453function spawnDicepoolForLore(clickedCharSheet)
1454 spawnDicepool(clickedCharSheet, 31)
1455end
1456function spawnDicepoolForOuterRim(clickedCharSheet)
1457 spawnDicepool(clickedCharSheet, 32)
1458end
1459function spawnDicepoolForUnderworld(clickedCharSheet)
1460 spawnDicepool(clickedCharSheet, 33)
1461end
1462function spawnDicepoolForXenology(clickedCharSheet)
1463 spawnDicepool(clickedCharSheet, 34)
1464end
1465function spawnDicepoolForOther(clickedCharSheet)
1466 spawnDicepool(clickedCharSheet, 35)
1467end
1468
1469--Dice spawning
1470function spawnAbilityDice()
1471 spawnDice(1)
1472end
1473function despawnAbilityDice()
1474 despawnDice(1)
1475end
1476function spawnProficiencyDice()
1477 spawnDice(2)
1478end
1479function despawnProficiencyDice()
1480 despawnDice(2)
1481end
1482function spawnBoostDice()
1483 spawnDice(3)
1484end
1485function despawnBoostDice()
1486 despawnDice(3)
1487end
1488function spawnDifficultyDice()
1489 spawnDice(4)
1490end
1491function despawnDifficultyDice()
1492 despawnDice(4)
1493end
1494function spawnChallengeDice()
1495 spawnDice(5)
1496end
1497function despawnChallengeDice()
1498 despawnDice(5)
1499end
1500function spawnSetbackDice()
1501 spawnDice(6)
1502end
1503function despawnSetbackDice()
1504 despawnDice(6)
1505end