]< SaveNameMessage from the StarsEpochTimeiDate11/7/2025 7:37:54 PMVersionNumberv13.3GameModeMessage from the StarsGameTypeGameGameComplexityLow ComplexityPlayingTime01<PlayerCounts01TagsP0 Board Games1 Party Games2Strategy Games3EnglishGravity?PlayArea?Table Table_CustomTableURLjhttps://steamusercontent-a.akamaihd.net/ugc/2532795074128174411/4F5376E33E75FDC4F52F95BCE61760802187A284/Sky Sky_MuseumSkyURLjhttps://steamusercontent-a.akamaihd.net/ugc/1004810215902243100/E890F03BD00542A575D66A7AD371A8571E67D921/NoteTabStates 0 title Vote Scriptbody Thanks for trying out my scripted voting. This notebook entry will detail its usage and features. Overview With scripted voting enabled, determining whether your team has come to a decision and what that decision is will be much more defined and cleaner. Players will be able to click on a card (or the pass button) and this will make a small marker of that player's colour appear on that card, indicating they have voted for it. Votes can be changed by clicking on a different option or retracted entirely by clicking on the same option. Once the current team's seated players who aren't blindfolded have voted on the same option, that decision will be announced. Automation may kick in at this point. Automation If you would like an extra layer of automation, you can enable this setting (recommended). Without it, the script simply serves as better substitute for traditional voting methods. With it, the codemasters need not provide any input after giving their clue. When everyone has voted on an option and automation is enabled, the appropriate action will be taken. In the case of a word being the team's vote, the corresponding colour agent will be automatically placed on that word. If the team voted to pass, the turn will be automatically passed. Settings The two settings are described in detail above. Be aware that changes in these settings will not take place until the next game has been started. If you find the script not to your liking partway through however, there is nothing stopping you from playing as if it didn't exist, with traditional voting methods and having the codemasters place the agents. Caveats The only major problem (which I'm aware of) is playing with a deck other than Hanii's or Vanilla. Currently, none of the other decks have their cards actually named, so my script has no way of knowing a card's name. The script simply announces "A card has been guessed" relying on the players and codemasters to handle the rest. There are some potential workarounds if it becomes a problem, which I don't expect since vanilla and hanii's decks comprise an overhwelming majority of players' usage. Feedback is much appreciated, whether positive or negative, as well as bug reports. You can either post a comment on the workshop submission or send a direct message through discord to me (LobstersArentImmortal#8348) (I'm also on the codenames discord, linked in the bottom right note) Have Fun, - LobstersArentImmortal (Moss) colorGreyvisibleColor&r?g?b?idGridTypeLinesColor&rgbOpacity?ThickLinesSnappingOffsetBothSnappingxSize?ySize?PosOffset&x?y?zٿLightingwLightIntensity G?LightColor&r?g?b?AmbientIntensity?AmbientTypeAmbientSkyColor&r?g?b?AmbientEquatorColor&r?g?b?AmbientGroundColor&r?g?b?ReflectionIntensity?LutIndexLutContribution /?LutURLHands*EnableDisableUnusedHidingComponentTagslabelsTurnspEnableTypeTurnOrderReverseSkipEmptyDisableInteractionsPassTurnsTurnColorCameraStates0Position&xyzRotation&x@@P@yzDistance`1Q@ZoomedAbsolutePosition&xwy~N@z= 1 2 3 4 5 6 7 8 9DecalPalletLuaScriptA--[[ Lua code. See documentation: https://api.tabletopsimulator.com/ --]] --[[ The onLoad event is called after the game save finishes loading. --]] function onLoad() --[[ print('onLoad!') --]] end --[[ The onUpdate event is called once per frame. --]] function onUpdate() --[[ print('onUpdate loop!') --]] endLuaScriptStateXmlUIRCustomUIAssetsY0QTypeNameCodenames LogoURL https://i.imgur.com/CzrymS7.pngSnapPoints0ePosition&xQG@yJ?z>Rotation&x`qy V@zf@1ePosition&x 6vGy?z@tRotation&x ]>yp@zf@ObjectStatesP( 02GUIDa1b2cdNameCustom_AssetbundleTransformposXFposY`ff@posZrotXrotYV@rotZscaleX @scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r(?g(?b(?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURLihttps://steamusercontent-a.akamaihd.net/ugc/779618900062784686/38469A6C91E6358925B6A32B70E4AF8964CDF9E1/AssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScriptLuaScriptStateXmlUI12GUID6072b1NameCustom_AssetbundleTransformposXF@posY`ff@posZrotXrotYp@rotZscaleX @scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r(?g(?b(?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURLihttps://steamusercontent-a.akamaihd.net/ugc/779618900062809504/6A4AB01DE0AF20CF8CA43E4BD99B422521B12B32/AssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScriptLuaScriptStateXmlUI22GUID05032cNameCustom_AssetbundleTransformposXposY`ff@posZ:@rotXrotY`f@rotZscaleX@scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r@&?g@&?b@&?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURLihttps://steamusercontent-a.akamaihd.net/ugc/779618900062811900/A41B090AEF8143AA0728866CD0B49DB0D64D575E/AssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScriptLuaScriptStateXmlUI32GUID6fb111NameCustom_AssetbundleTransformposXposY`ff@posZ:rotXrotYrotZscaleX@scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r&?g&?b&?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURLihttps://steamusercontent-a.akamaihd.net/ugc/779618900062811900/A41B090AEF8143AA0728866CD0B49DB0D64D575E/AssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScriptLuaScriptStateXmlUI4GUID37f70aNameFogOfWarTriggerTransformposX5@posY?posZ#rotXrotYtv@rotZscaleX@z?scaleY@z?scaleZ@z?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r ?5?g?b?a`?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBlueFogHidePointersFogReverseHidingFogSeethroughLuaScriptLuaScriptStateXmlUI5GUID9fa4c3NameFogOfWarTriggerTransformposX5posY?posZ#rotXrotYtv@rotZscaleX@z?scaleY@z?scaleZ@z?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r Zd?g ?ba?a`?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorRedFogHidePointersFogReverseHidingFogSeethroughLuaScriptLuaScriptStateXmlUI62GUID3f75b3NameCustom_AssetbundleTransformposX@4@posYposZ,@rotX@?>rotYFf@rotZh>scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r |4?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURLihttps://steamusercontent-a.akamaihd.net/ugc/869616151527842588/588906D27443BF1A6B7FC6626B27D791E55A656D/AssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScriptLuaScriptStateXmlUI7fGUID3ef1caName Custom_TokenTransformposX>posYposZ,rotX6]>rotYf@rotZf@scaleX ?scaleY?scaleZ ?NicknameWordsDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLihttps://steamusercontent-a.akamaihd.net/ugc/921428922392711928/CB0E3D547BBF4B75454AF10476653161B660B568/ImageSecondaryURLImageScalar?WidthScaleCustomTokenKThickness?MergeDistancePixels@StandUpStackableLuaScriptLuaScriptStateXmlUI8GUID43abffNameFogOfWarTriggerTransformposX{?posY?posZ $@rotXrotYrotZscaleX<@scaleY?scaleZ3@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r ?5?g?b?a`?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBlueFogHidePointersFogReverseHidingFogSeethroughLuaScriptLuaScriptStateXmlUI9GUIDd348d6NameFogOfWarTriggerTransformposX`X{?posY?posZ@#@rotXrotYPĭ7rotZscaleX<@scaleY?scaleZ 3@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r Zd?g ?ba?a`?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorRedFogHidePointersFogReverseHidingFogSeethroughLuaScriptLuaScriptStateXmlUI10\GUIDccc632Name HandTriggerTransformposX9@posYr?posZFrotXrotYrotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1rQ?g`?b?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorPinkLuaScriptLuaScriptStateXmlUI11]GUID00a2faName HandTriggerTransformposX9posYr?posZD@rotXrotYf@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1rt?gn?b?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorGreenLuaScriptLuaScriptStateXmlUI12^GUIDe7669fName HandTriggerTransformposXL@posYr?posZ2@rotXrotYp@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r?gj?b@?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorYellowLuaScriptLuaScriptStateXmlUI13\GUIDce6111Name HandTriggerTransformposXLposYr?posZ2rotXrotYV@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r ?5?g?b?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBlueLuaScriptLuaScriptStateXmlUI14]GUID87f04aName HandTriggerTransformposXLposYr?posZ2@rotXrotYV@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r?g?b?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorWhiteLuaScriptLuaScriptStateXmlUI15[GUID3ce838Name HandTriggerTransformposXL@posYr?posZ@9ѯ>rotXrotYp@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r Zd?g ?ba?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorRedLuaScriptLuaScriptStateXmlUI16^GUIDe52c8cName HandTriggerTransformposX9posYr?posZFrotXrotYrotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r@b?g?b?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorPurpleLuaScriptLuaScriptStateXmlUI17]GUID863069Name HandTriggerTransformposXL@posYr?posZ2rotXrotYp@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1rZ?gh?b`= ?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBrownLuaScriptLuaScriptStateXmlUI18^GUID9ed8f0Name HandTriggerTransformposX9@posYr?posZD@rotXrotYf@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r ?g?b?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorOrangeLuaScriptLuaScriptStateXmlUI19\GUID0832d7Name HandTriggerTransformposXLposYr?posZ>rotXrotYV@rotZscaleX$@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse1r?g?5?b l?aLayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorTealLuaScriptLuaScriptStateXmlUI20GUIDba3ec1Name Custom_TileTransformposXtC@posY`?posZrotX@v@rotY V@rotZ&scaleX@H@scaleY?scaleZ@H@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031974380/2335DA95ECB18CADEF3C803072D7BAA5A27036AB/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState {"checks":[{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.3","y":"0.31"},"fillFromDisabled":false,"font":500,"locked":false,"name":"","pos":{"x":-3.21,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,null,null,1,null,1,null,null,null,null,null,1,null,null,null,null,null,null,null,1,null,null,null,null,null,null,0,0]},{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.3","y":"0.31"},"fillFromDisabled":"True","font":500,"locked":false,"name":"","pos":{"x":-0.900000000000001,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,1,1,1,null,1,null,null,null,1,1,1,null,null,null,1,1,null,1,null,null,null,null,null,null,null,0,0]},{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.3","y":"0.31"},"fillFromDisabled":false,"font":500,"locked":false,"name":"","pos":{"x":1.41,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,null,1,1,null,1,null,null,null,null,null,1,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0,0]}],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":-3.02,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","",""]},{"align":3,"array":{"x":"2","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":-0.369999999999999,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","","X"]},{"align":3,"array":{"x":"1","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":2.27,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","X","X"]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.01,"scale":{"x":"0.5","y":"0.5"},"sheetLocked":false}XmlUI21GUID820f3eName Custom_TileTransformposX@posY@J?posZfErotX@U-rotYՀf@rotZ*hv@scaleX@scaleY?scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031972957/2FF22416BA68C49E2FBAC0E0E7F078C24C2A92C8/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState {"checks":[{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.301","y":"0.31"},"fillFromDisabled":false,"font":500,"locked":false,"name":"","pos":{"x":-3.243,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,null,null,1,null,1,null,null,null,null,null,1,null,null,null,null,null,null,null,1,null,null,null,null,null,null,0,0]},{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.301","y":"0.31"},"fillFromDisabled":"True","font":500,"locked":false,"name":"","pos":{"x":-0.932000000000001,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,1,1,1,null,1,null,null,null,1,1,1,null,null,null,1,1,null,1,null,null,null,null,null,2,null,0,0]},{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.301","y":"0.31"},"fillFromDisabled":false,"font":500,"locked":false,"name":"","pos":{"x":1.39,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,null,1,1,null,1,null,null,null,null,null,1,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0,0]}],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":-3.02,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","",""]},{"align":3,"array":{"x":"2","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":-0.369999999999999,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","","X"]},{"align":3,"array":{"x":"1","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":2.27,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","X","X"]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"scale":{"x":"0.5","y":"0.5"},"sheetLocked":false}XmlUI22GUIDd1e563Name Custom_TileTransformposXH!posYJ?posZ?rotX(7W>rotY f@rotZ`tscaleX ”@scaleY?scaleZ ”@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r`?gb?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789130840/4A39B3A231E5C6A594D2D2C640DE99EEE529E4FA/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUIAttachedSnapPoints=0ePosition&x{w?y?zZ?Rotation&x xY>y` ?z@aw1ePosition&x ?y?z`e?Rotation&x+9>y` ?zg>2ePosition&x#пy?z p?Rotation&x@5y` ?z@p>23!GUIDef2b27Name DeckCustomTransformposX \xposY?posZ`^,7@rotX`S?rotYJf@rotZf@scaleX?scaleY?scaleZ?NicknameScientist Message CardsDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsSidewaysCardDeckIDs[0d1o2l3k4n5g6j7e8m9f10i11hCustomDeck>16FaceURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789132277/23CA8BFD6708B48F1DC28C804D3902345653E31F/BackURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789131739/503AA7D813E09ABE46B084DBD4164F15BA957382/NumWidthNumHeightBackIsHiddenUniqueBackTypeLuaScript+function onLoad() self.shuffle() endLuaScriptStateXmlUIContainedObjectsy0GUID750e8bNameCardTransformposX\posYC?posZ`5@rotX 1Aa?rotY@ezf@rotZ`f@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCardIDdSidewaysCardCustomDeck>16FaceURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789132277/23CA8BFD6708B48F1DC28C804D3902345653E31F/BackURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789131739/503AA7D813E09ABE46B084DBD4164F15BA957382/NumWidthNumHeightBackIsHiddenUniqueBackTypeLuaScriptLuaScriptStateXmlUI1VGUID531a7aNameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDoSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects2VGUID96ed3aNameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDlSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects3VGUIDed77b3NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDkSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects4VGUIDaa373aNameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDnSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects5VGUID4f43b8NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDgSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects6VGUID3d81b8NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDjSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects7VGUID4f8ab0NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDeSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects8VGUID5354d7NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDmSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects9VGUIDe4a7d7NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDfSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects10VGUID92e0f2NameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDiSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects11VGUIDd0ae6cNameCardTransformposX@posY?posZ HrotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDhSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects24CGUIDa68dc3Name DeckCustomTransformposX.posY@?posZc7rotX 8>rotYf@rotZf@scaleX[?scaleY?scaleZ[?Nickname Letter CardsDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsSidewaysCardDeckIDs0,%1"%2#%3'%4%%5$%62%7%83%9/%10&%111%12)%13*%145%154%16 %17%180%19!%20-%21(%22%23+%24%25.%CustomDeck?956FaceURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789133803/73B5F496266A79D8899B9FE223CFCF04BA5B06B7/BackURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789133317/B0FEEE91861FB2ACD0003C742DC75AF68C82ED24/NumWidth NumHeightBackIsHiddenUniqueBackTypeLuaScript+function onLoad() self.shuffle() endLuaScriptStateXmlUIContainedObjectse>0VGUIDe76af3NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID,%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects1VGUID2c6d77NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID"%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects2VGUIDb5f6cbNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID#%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects3VGUIDc9442eNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID'%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects4VGUIDfbfd27NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID%%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects5VGUID7a1f17NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID$%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects6VGUIDcf637fNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID2%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects7VGUIDeb320aNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects8VGUIDad2c85NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID3%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects9VGUID88a69eNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID/%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects10VGUID8699b4NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID&%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects11VGUIDe94695NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID1%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects12VGUID5e7b98NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID)%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects13VGUID63d54cNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID*%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects14VGUID9eb974NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID5%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects15VGUID0ffc82NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID4%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects16VGUID448cc6NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID %SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects17VGUID1066e8NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects18VGUIDcba4e5NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID0%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects19VGUIDbf95d6NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID!%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects20VGUIDe7a86cNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID-%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects21VGUIDa94806NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID(%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects22VGUID1d0a68NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects23VGUIDdb68d3NameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID+%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects24GUIDff28e5NameCardTransformposX2posY?posZ@#-rotX w =rotYf@rotZ`f@scaleX[?scaleY?scaleZ[?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCardID%SidewaysCardCustomDeck>36FaceURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789133803/73B5F496266A79D8899B9FE223CFCF04BA5B06B7/BackURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789133317/B0FEEE91861FB2ACD0003C742DC75AF68C82ED24/NumWidth NumHeightBackIsHiddenUniqueBackTypeLuaScriptLuaScriptStateXmlUI25VGUIDd0459eNameCardTransformposX>~?posY?posZ_4rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardID.%SidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects25GUID12bb0fName Custom_TileTransformposX?!@posYJ?posZ?rotXyrotYf@rotZ &f>scaleX ”@scaleY?scaleZ ”@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r`?gb?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789131301/AF8F1CB3522F9717F0277BFF4FE0359080A01527/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUIAttachedSnapPoints=0ePosition&xW,?y?z`@?Rotation&x"TyZW5?z`41ePosition&x@\9?y?z `?Rotation&xmEyZW5?z`sc2ePosition&xy?z?Rotation&x T n>yZW5?z`R>26!GUID5f3b9aName DeckCustomTransformposX'@posY?posZ6rotXVrotYf@rotZf@scaleX@scaleY?scaleZ@NicknameAlien Message CardsDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsSidewaysCardDeckIDs[01234567891011CustomDeck>26FaceURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789134750/3E227CBB44202C480A82298EEFB4FC05650D7564/BackURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789134307/C3BBEA0880AE33D94A64DD6C7F63E781F879C651/NumWidthNumHeightBackIsHiddenUniqueBackTypeLuaScript+function onLoad() self.shuffle() endLuaScriptStateXmlUIContainedObjectsy0VGUID574157NameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects1VGUID3ae8ebNameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects2GUIDda6062NameCardTransformposX?posYL?posZ`,7rotX@o'rotY`f@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCardIDSidewaysCardCustomDeck>26FaceURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789134750/3E227CBB44202C480A82298EEFB4FC05650D7564/BackURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789134307/C3BBEA0880AE33D94A64DD6C7F63E781F879C651/NumWidthNumHeightBackIsHiddenUniqueBackTypeLuaScriptLuaScriptStateXmlUI3VGUID6ebc53NameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects4VGUID1e0f5bNameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects5VGUIDde2df6NameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects6VGUID40652bNameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects7VGUID5bbd6fNameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects8VGUID07537dNameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects9VGUID43c4b4NameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects10VGUIDee69f2NameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects11VGUID020ed7NameCardTransformposX Ѷ@posY?posZ7rotX`SվrotYf@rotZf@scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHandsCardIDSidewaysCardLuaScriptLuaScriptStateXmlUIContainedObjects27MGUIDaceeb7Name Custom_TileTransformposXposYJ?posZR?@rotXń>rotYf@rotZgVm>scaleXN@scaleY?scaleZN@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r`?gb?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2413452682789130258/E72F92F5C53A06D06EA2FF776B216A2BB185BD8B/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI28GUID6742d8NameDie_6TransformposX3@posY@?posZ@5rotXp@rotYC>rotZscaleX?scaleY?scaleZ?NicknameSquareDescriptionGMNotesAltLookAngle&xyzColorDiffuse&rQ?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz29GUID6cd1ffNameDie_6TransformposX`k5@posY@?posZ@5rotXp@rotY`/>rotZscaleX?scaleY?scaleZ?Nickname TriangleDescriptionGMNotesAltLookAngle&xyzColorDiffuse&rQ?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz30GUID216e77NameDie_6TransformposX@2@posY@?posZ@5rotXp@rotYZ?rotZscaleX?scaleY?scaleZ?NicknameCircleDescriptionGMNotesAltLookAngle&xyzColorDiffuse&rQ?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz31GUID0a5bb9NameDie_6TransformposX`FposY 7?posZ I rotX5~V@rotY ov@rotZscaleX?scaleY?scaleZ?NicknameSquareDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r 5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz32GUID65f26fNameDie_6TransformposXjFposY7?posZ,%rotXv@rotYssv@rotZp@scaleX?scaleY?scaleZ?Nickname triangleDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r 5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz33GUID8e8177NameDie_6TransformposX6FposY7?posZ "rotX`}?rotYpv@rotZ!w?scaleX?scaleY?scaleZ?NicknameCircleDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r 5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz34gGUIDcfc31dNameCustom_AssetbundleTransformposXNBposY+V?posZ9@rotX@W?rotYp@rotZpȈ?scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":"2","array":{"x":"1","y":"1"},"distance":{"x":1,"y":1},"fieldColor":{"a":"0","b":"0","g":"0","r":"0"},"font":"250","locked":"True","name":"Title","pos":{"x":-3.4,"y":-6.14999999999999},"role":"Object name","size":{"x":"2000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Tooltip is content","value":["Notes"]},{"align":2,"array":{"x":"1","y":"18"},"counter":"False","distance":{"x":"1","y":".74"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"500","locked":"False","name":"Score","pos":{"x":-0.299999999999999,"y":-5.4},"size":{"x":"5000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Use name as tooltip","value":["","","","","","","","","","","","","","","","","","","12","6","9","15","","","","","","","","","",""],"vsum":"False"}],"flip":"False","height":"0.5","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"1"},"sheetLocked":false}XmlUI35GUID3c9c3dName Custom_TileTransformposX`X;CposYR?posZ`>rotXbrotY@p@rotZ`-k>scaleX@6@scaleY?scaleZ@6@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031974380/2335DA95ECB18CADEF3C803072D7BAA5A27036AB/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState {"checks":[{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.3","y":"0.31"},"fillFromDisabled":false,"font":500,"locked":false,"name":"","pos":{"x":-3.21,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,null,null,1,null,1,null,null,null,null,null,1,null,null,null,null,null,null,null,1,null,null,null,null,null,null,0,0]},{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.3","y":"0.31"},"fillFromDisabled":"True","font":500,"locked":false,"name":"","pos":{"x":-0.900000000000001,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,1,1,1,null,1,null,null,null,1,1,1,null,null,null,1,1,null,1,null,null,null,null,null,null,null,0,0]},{"array":{"x":"7","y":"4"},"characters":{"disabled":"◌","empty":"○","filled":"●"},"checkColor":{"a":0,"b":1,"g":1,"r":1},"distance":{"x":"0.3","y":"0.31"},"fillFromDisabled":false,"font":500,"locked":false,"name":"","pos":{"x":1.41,"y":0.32},"separateColors":"True","size":{"x":"0.6","y":"0.6"},"textColor":{"a":1,"b":0,"g":0,"r":0},"textColorDisabled":{"a":1,"b":0.5,"g":0.5,"r":0.5},"textColorOff":{"a":1,"b":0,"g":0,"r":0},"textColorOn":{"a":0.959107875823975,"b":0,"g":0,"r":0},"tooltip":"None","value":[1,null,1,1,null,1,null,null,null,null,null,1,null,null,null,null,null,null,null,null,null,null,null,null,null,null,0,0]}],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":-3.02,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","",""]},{"align":3,"array":{"x":"2","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":-0.369999999999999,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","","X"]},{"align":3,"array":{"x":"1","y":1},"distance":{"x":"0.665","y":"1"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"300","locked":false,"name":"","pos":{"x":2.27,"y":-1.48},"role":"Normal Field","size":{"x":250,"y":250},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","X","X"]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.01,"scale":{"x":"0.5","y":"0.5"},"sheetLocked":false}XmlUI36gGUID482162NameCustom_AssetbundleTransformposX6@posYV?posZ=rotX;>rotYf@rotZ[>scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":"2","array":{"x":"1","y":"1"},"distance":{"x":1,"y":1},"fieldColor":{"a":"0","b":"0","g":"0","r":"0"},"font":"250","locked":"True","name":"Title","pos":{"x":-3.4,"y":-6.14999999999999},"role":"Object name","size":{"x":"2000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Tooltip is content","value":["Notes"]},{"align":2,"array":{"x":"1","y":"18"},"counter":"False","distance":{"x":"1","y":".74"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"500","locked":"False","name":"Score","pos":{"x":-0.299999999999999,"y":-5.4},"size":{"x":"5000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Use name as tooltip","value":["","","","","","","","","","","","","","","","","","","12","6","9","15","","","","","","","","","",""],"vsum":"False"}],"flip":"False","height":"0.5","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"1"},"sheetLocked":false}XmlUI37gGUID9624c5NameCustom_AssetbundleTransformposX@NB@posY@V?posZ=9@rotXL\rotYoV@rotZ scaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":"2","array":{"x":"1","y":"1"},"distance":{"x":1,"y":1},"fieldColor":{"a":"0","b":"0","g":"0","r":"0"},"font":"250","locked":"True","name":"Title","pos":{"x":-3.4,"y":-6.14999999999999},"role":"Object name","size":{"x":"2000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Tooltip is content","value":["Notes"]},{"align":2,"array":{"x":"1","y":"18"},"counter":"False","distance":{"x":"1","y":".74"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"500","locked":"False","name":"Score","pos":{"x":-0.299999999999999,"y":-5.4},"size":{"x":"5000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Use name as tooltip","value":["","","","","","","","","","","","","","","","","","","12","6","9","15","","","","","","","","","",""],"vsum":"False"}],"flip":"False","height":"0.5","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"1"},"sheetLocked":false}XmlUI38gGUID2316f2NameCustom_AssetbundleTransformposX6posYV?posZ=rotX@5Y>rotY f@rotZ@&fscaleX?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":"2","array":{"x":"1","y":"1"},"distance":{"x":1,"y":1},"fieldColor":{"a":"0","b":"0","g":"0","r":"0"},"font":"250","locked":"True","name":"Title","pos":{"x":-3.4,"y":-6.14999999999999},"role":"Object name","size":{"x":"2000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Tooltip is content","value":["Notes"]},{"align":2,"array":{"x":"1","y":"18"},"counter":"False","distance":{"x":"1","y":".74"},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"500","locked":"False","name":"Score","pos":{"x":-0.299999999999999,"y":-5.4},"size":{"x":"5000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"Use name as tooltip","value":["","","","","","","","","","","","","","","","","","","12","6","9","15","","","","","","","","","",""],"vsum":"False"}],"flip":"False","height":"0.5","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"1"},"sheetLocked":false}XmlUI39yGUIDbc81acName Custom_TileTransformposX`@@posY?posZ>rotXB?rotY܀f@rotZ`gc[scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031969909/1AB92FC4B988B9A7A37E8F2C65807FDE6861F780/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI40yGUIDbb946eName Custom_TileTransformposXB@posYl?posZ`*@rotXH\j?rotY@V@rotZa?scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g@!?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031970573/0298E11290F27901ACE896D78893617BEC2BC5DD/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI41yGUID00b2a0Name Custom_TileTransformposXVC@posY|?posZڛ,@rotX?rotYV@rotZ %/?scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g@!?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031970573/0298E11290F27901ACE896D78893617BEC2BC5DD/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI42yGUID51f3a0Name Custom_TileTransformposXC@posY[?posZ7I.@rotXT\?rotYV@rotZ:?scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g@!?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031970573/0298E11290F27901ACE896D78893617BEC2BC5DD/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI43yGUID416c47Name Custom_TileTransformposX@BposY`?posZ ,@rotX 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI44yGUID6dd034Name Custom_TileTransformposX0CposY?posZ|+@rotX x@rotY@p@rotZ)!?scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g@!?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031970573/0298E11290F27901ACE896D78893617BEC2BC5DD/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI45yGUIDe0280cName Custom_TileTransformposX`2DposY?posZ@*@rotX@z@rotYp@rotZ Od?scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g@!?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031970573/0298E11290F27901ACE896D78893617BEC2BC5DD/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI46yGUID0ec724Name Custom_TileTransformposX$Q@@posY?posZ|?rotX`?rotY f@rotZv@scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031969909/1AB92FC4B988B9A7A37E8F2C65807FDE6861F780/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI47yGUID358291Name Custom_TileTransformposXT-@@posY?posZ c@rotX@?rotYՀf@rotZ v@scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031969909/1AB92FC4B988B9A7A37E8F2C65807FDE6861F780/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI48yGUID546cf7Name Custom_TileTransformposX@j@posY?posZ`԰>rotX>/D?rotY f@rotZ1b]scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031969909/1AB92FC4B988B9A7A37E8F2C65807FDE6861F780/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI49yGUID59880bName Custom_TileTransformposX`@posY`?posZ ?rotXa%?rotY f@rotZv@scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031969909/1AB92FC4B988B9A7A37E8F2C65807FDE6861F780/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI50yGUIDc3c942Name Custom_TileTransformposX@posYދ?posZ Uu@rotXږ?rotYf@rotZv@scaleXm@scaleY?scaleZm@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2533920974031969909/1AB92FC4B988B9A7A37E8F2C65807FDE6861F780/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScripts function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, totals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateVSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].totals, {index=inputIndex, x=x}) end end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateVSums(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].totals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateVSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateVSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].totals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":"-0.13","y":-0.4},"role":"Normal Field","size":{"x":"2200","y":"350"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]},{"align":2,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"350","locked":false,"name":"","pos":{"x":-1.33,"y":0.85},"role":"Normal Field","size":{"x":"1000","y":"400"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":[""]}],"flip":"False","height":"0.2","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":0.7,"y":0.7},"sheetLocked":false}XmlUI51J GUID28c807Name Custom_TileTransformposX`a#@posY&?posZG@rotX 8brotY~f@rotZ`f@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074453/7BD4077DD9E23F76DE364C857ACDA62F5DCF625B/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUIStates 1MGUID4410eeName Custom_TileTransformposX -@posY29?posZ`G@rotXm?rotY@f@rotZpv@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458075049/487709CFE814F0B557813886CAE935F103B8CC24/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI2MGUID2f322fName Custom_TileTransformposX -@posY29?posZ`G@rotXm?rotY@f@rotZpv@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074702/39B04209548F4FBD7E39920407750F45882C25D7/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI3MGUID283be3Name Custom_TileTransformposX -@posY29?posZ`G@rotXm?rotY@f@rotZpv@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074186/13630B50A86599B60C5737A41B4FC1628F03A51D/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI52J GUID283be3Name Custom_TileTransformposX@tposY'?posZG@rotXIrrotYf@rotZf@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074186/13630B50A86599B60C5737A41B4FC1628F03A51D/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUIStates 4MGUIDe06c5bName Custom_TileTransformposX posY$?posZ6 G@rotX@X?rotYf@rotZf@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074453/7BD4077DD9E23F76DE364C857ACDA62F5DCF625B/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI1MGUID71a298Name Custom_TileTransformposX posY$?posZ6 G@rotXX?rotYf@rotZf@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458075049/487709CFE814F0B557813886CAE935F103B8CC24/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI2MGUID2f322fName Custom_TileTransformposX@tposY'?posZG@rotXIrrotYf@rotZf@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074702/39B04209548F4FBD7E39920407750F45882C25D7/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI53J GUID0e9624Name Custom_TileTransformposXY!posY?posZhFrotXT@?rotY@f@rotZ ?scaleX@scaleY?scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074186/13630B50A86599B60C5737A41B4FC1628F03A51D/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUIStates 1MGUID4410eeName Custom_TileTransformposXY!posY?posZhFrotX@Z@?rotY@f@rotZ ?scaleX@scaleY?scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458075049/487709CFE814F0B557813886CAE935F103B8CC24/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI4MGUIDe06c5bName Custom_TileTransformposXY!posY?posZhFrotXX@?rotY@f@rotZ ?scaleX@scaleY?scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074453/7BD4077DD9E23F76DE364C857ACDA62F5DCF625B/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI2MGUID2f322fName Custom_TileTransformposXY!posY?posZhFrotXX@?rotY@f@rotZ ?scaleX@scaleY?scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074702/39B04209548F4FBD7E39920407750F45882C25D7/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI54GUID1ad136NameDie_6TransformposX`F@posY`D'?posZ rotX$rotYqv@rotZp@scaleX?scaleY?scaleZ?Nickname triangleDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r`5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz55GUID7f8375NameDie_6TransformposXF@posYE'?posZ@!rotX@rotY Fov@rotZ>scaleX?scaleY?scaleZ?NicknameCircleDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r`5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz56GUID383e8aNameDie_6TransformposX@F@posY E'?posZrotXV@rotY2ov@rotZscaleX?scaleY?scaleZ?NicknameSquareDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r`5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsMaterialIndexLuaScriptLuaScriptStateXmlUIRotationValues0BValue1Rotation&xVyz1BValue2Rotation&xyz2BValue3Rotation&xyzV3BValue4Rotation&xyzV@4BValue5Rotation&xyzf5BValue6Rotation&xV@yz57GUIDb158a7NameCustom_AssetbundleTransformposX=rotY0p@rotZ`>scaleX ?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"4","y":"1"},"fieldColor":{"a":0.39215686917305,"b":1,"g":1,"r":1},"font":"1000","locked":false,"name":"","pos":{"x":-4,"y":0.2},"role":"Normal Field","size":{"x":"1500","y":"500"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["",""]},{"align":3,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"200","locked":false,"name":"","pos":{"x":0,"y":-0.9},"role":"Normal Field","size":{"x":"1000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["Final Guess"]}],"flip":"False","height":"0.4","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"4.61"},"sheetLocked":false}XmlUI58GUID0d2fc1NameCustom_AssetbundleTransformposX@8@posYY?posZȶCrotX>rotYf@rotZ4>scaleX ?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"4","y":"1"},"fieldColor":{"a":0.39215686917305,"b":1,"g":1,"r":1},"font":"1000","locked":false,"name":"","pos":{"x":-4,"y":0.2},"role":"Normal Field","size":{"x":"1500","y":"500"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["","",""]},{"align":3,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"200","locked":false,"name":"","pos":{"x":0,"y":-0.9},"role":"Normal Field","size":{"x":"1000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["Final Guess"]}],"flip":"False","height":"0.4","locks":{"checks":false,"decals":false,"fields":false},"scale":{"x":"1","y":"4.61"},"sheetLocked":false}XmlUI59GUID6a2de1NameCustom_AssetbundleTransformposX ?@posYL?posZ;?rotX$>rotYuV@rotZBg>scaleX ?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"4","y":"1"},"fieldColor":{"a":0.39215686917305,"b":1,"g":1,"r":1},"font":"1000","locked":false,"name":"","pos":{"x":-4,"y":0.2},"role":"Normal Field","size":{"x":"1500","y":"500"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["",""]},{"align":3,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"200","locked":false,"name":"","pos":{"x":0,"y":-0.9},"role":"Normal Field","size":{"x":"1000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["Final Guess"]}],"flip":"False","height":"0.4","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"4.61"},"sheetLocked":false}XmlUI60GUID481588NameCustom_AssetbundleTransformposX(5posYY?posZ@3CrotX`rotYf@rotZ෌>scaleX ?scaleY?scaleZ?NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURL2http://chry.me/tts/scrabble/notepad_sheet.unity3dAssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript function onload(saved_data) sheetLocked = self.script_state.sheetLocked or false local inverseScale = {x=math.floor(100/self.getScale().x)/100, y=math.floor(100/self.getScale().z)/100} scale = self.script_state.scale or inverseScale flip = self.script_state.flip or "False" fields = self.script_state.fields or {} checks = self.script_state.checks or {} decals = self.script_state.decals or {} height = self.script_state.height or 0.5 locks = self.script_state.locks or {fields=false, checks=false, decals=false} lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} lookupSelectionButtonIndices = {} lastFieldLockedMessage = 0 buttonIndex = 0 inputIndex = 0 dirtyUpdateWait = nil if saved_data ~= "" then local loadedData = JSON.decode(saved_data) sheetLocked = loadedData.sheetLocked or false flip = loadedData.flip or "False" height = loadedData.height or 0.5 fields = loadedData.fields or {} checks = loadedData.checks or {} decals = loadedData.decals or {} scale = loadedData.scale or inverseScale locks = loadedData.locks or {fields=false, checks=false, decals=false} end if (not getCommited()) then self.addContextMenuItem("Edit Layout", showEditPanel) self.addContextMenuItem("Commit Layout", showCommitPanel) nudgeDistance = self.script_state.nudgeDistance or 0.1 page = 1 editingSheet = false selectedId = 0 selectedArrayId = 1 selectedType = "" selectedMax = 5 creating = false else makeContextMenuItems() end createAll() end function makeContextMenuItems() self.clearContextMenu() if (not sheetLocked) then local getLocked = function(element) if (locks[element]) then return "◆ Lock" else return "◇ Lock" end end local allLocked = "Lock" if (locks.fields and locks.checks and locks.decals) then allLocked = "Unlock" end if (#fields > 0 and #checks > 0 and #decals > 0) then self.addContextMenuItem(allLocked.." everything", toggleAllLocks) end for k, v in pairs(fields) do if (v.locked != "True") then self.addContextMenuItem(getLocked("fields").." texts", toggleLockFields) break end end for k, v in pairs(checks) do if (v.locked != "True") then self.addContextMenuItem(getLocked("checks").." checkboxes", toggleLockChecks) break end end for k, v in pairs(decals) do if (v.locked != "True") then self.addContextMenuItem(getLocked("decals").." images", toggleLockDecals) break end end end end function toggleAllLocks(ply, pos, obj) if (locks.fields and locks.checks and locks.decals) then locks.fields = false locks.checks = false locks.decals = false broadcastToColor("Unlocked everything", ply) else locks.fields = true locks.checks = true locks.decals = true broadcastToColor("Locked everything", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockFields(ply, pos, obj) if (locks.fields) then locks.fields = false broadcastToColor("Unlocked texts", ply) else locks.fields = true broadcastToColor("Locked texts", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockChecks(ply, pos, obj) if (locks.checks) then locks.checks = false broadcastToColor("Unlocked checkboxes", ply) else locks.checks = true broadcastToColor("Locked checkboxes", ply) end updateSave() makeContextMenuItems() refresh() end function toggleLockDecals(ply, pos, obj) if (locks.decals) then locks.decals = false broadcastToColor("Unlocked images", ply) else locks.decals = true broadcastToColor("Locked images", ply) end updateSave() makeContextMenuItems() refresh() end function updateSave() local data_to_save = {scale=scale, height=height, fields=fields, checks=checks, decals=decals, flip=flip, sheetLocked=sheetLocked, locks=locks} if (not getCommited()) then data_to_save.nudgeDistance=nudgeDistance else data_to_save.nudgeDistance=nil end saved_data = JSON.encode(data_to_save) self.script_state = saved_data end function null() end function refresh() if (not creating) then self.clearInputs() self.clearButtons() inputIndex = 0 buttonIndex = 0 if (editingSheet) then createSelectionHighlight() end createAll() end end function createAll() lookupInputIndexToInfo = {} lookupButtonIndexToInfo = {} lookupFieldIndices = {} lookupCheckIndices = {} lookupDecalIndices = {} startLuaCoroutine(self, "createAllCoroutine") end function createAllCoroutine() if (not getCommited()) then UI.setAttribute(getPanelId("Loading"), "active", "True") end coroutine.yield(0) creating = true for fieldID, field in pairs(fields) do lookupFieldIndices[fieldID] = { inputs={}, colTotals={}, rowTotals={}, counterButtons={}, selectionButtons={} } local posx = field.pos.x local posy = field.pos.y local func = "edit" if (field.vsum == "True" or field.hsum == "True") then func = 'MarumEditableRecalculateSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID edit(obj, ply, input_value, selected) obj.call("recalculateSums",fID) end else local iIndex = inputIndex if (field.locked == "True" or sheetLocked or locks.fields) then func = 'MarumEditableRevert_'..fieldID _G[func] = function(obj, ply, input_value, selected) local fID = fieldID local iID = iIndex local sel = selected obj.call("revertField",{fID, ply, iID, sel}) end end end local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} local posMulx = 1 local flipped = 1 if (flip == "True") then rotation.y = 180 flipped = -1 end local upright = self.getTransformUp().y > 0 local shouldFlip = (field.locked == "True" or sheetLocked or locks.fields) and upright local unlockedRotation = {x=rotation.x, y=rotation.y, z=rotation.z} if (editingSheet or shouldFlip) then rotation.z = 180 posMulx = -posMulx fieldScale.x = -fieldScale.x end local vsum = 0 local hsums = {} local fontSize = field.font fontSize = math.min(field.size.y - 24, fontSize) local color = getFieldTextColor(fieldID) for x=1, field.array.x do vsum = 0 for y=1, field.array.y do local arrayID = x+(y-1)*field.array.x local pos = getFieldPosition(fieldID, x, y) self.createInput({ value = field.value[arrayID], tooltip=getFieldTooltip(fieldID, arrayID), input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation=rotation, font_size = fontSize, scale=fieldScale, font_color=color, color = field.fieldColor, tab = 2 }) if (field.counter == "True") then local counterButtonWidth = fontSize*0.75 _G['MarumEditableCounterIncrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("increaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="counter", id=fieldID} table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=1}) self.createButton({ click_function = 'MarumEditableCounterIncrease_'..fieldID..'_'..arrayID, tooltip="↑ Increase ↑", function_owner = self, label="[b]+[/b]", position={x=pos.x+(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, rotation=unlockedRotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) _G['MarumEditableCounterDecrease_'..fieldID..'_'..arrayID] = function(obj, ply, alt) local fID = fieldID local aID = arrayID obj.call("decreaseCounter",{fID, aID, ply}) end buttonIndex = buttonIndex+1 table.insert(lookupFieldIndices[fieldID].counterButtons, {index=buttonIndex, x=x, y=y, side=-1}) self.createButton({ click_function = 'MarumEditableCounterDecrease_'..fieldID..'_'..arrayID, tooltip="↓ Decrease ↓", function_owner = self, label="[b]-[/b]", position={x=pos.x-(field.size.x + counterButtonWidth) / 1000 * fieldScale.x * posMulx * flipped, y=pos.y, z=pos.z}, unlockedRotation=rotation, scale=fieldScale, width=counterButtonWidth, height=counterButtonWidth, font_size=fontSize/2, font_color= color, color = field.fieldColor, }) end inputIndex = inputIndex+1 lookupInputIndexToInfo[inputIndex] = {type="field", id=fieldID, arrayID=arrayID} table.insert(lookupFieldIndices[fieldID].inputs, {index=inputIndex, arrayID=arrayID, x=x, y=y}) if (field.vsum == "True") then if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end if (field.hsum == "True") then if (tonumber(field.value[arrayID])) then if (hsums[x] == nil) then hsums[x] = tonumber(field.value[arrayID]) else hsums[x] = hsums[x] + tonumber(field.value[arrayID]) end end end if (x == 1 and field.hsum == "True") then local pos = getFieldPosition(fieldID, field.array.x + 1, y) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]-[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].rowTotals, {index=inputIndex, y=y}) end if (inputIndex%10==0) then coroutine.yield(0) end end if (field.vsum == "True") then local pos = getFieldPosition(fieldID, x, field.array.y+1) local func = 'MarumEditableRevertSum_'..fieldID _G[func] = function(obj, ply, input_value, selected) obj.call("revertFieldSum",{fieldID, ply, iIndex, selected}) end self.createInput({ value = "[u]"..vsum.."[/u]", tooltip="[Sum]", input_function = func, function_owner = self, alignment = field.align, position = pos, width = field.size.x, height = field.size.y, rotation={x=unlockedRotation.x, y=unlockedRotation.y, z=unlockedRotation.z+180}, font_size = fontSize, scale={x=-scale.x,y=1,z=scale.y}, font_color= color, color = field.fieldColor, tab = 0 }) inputIndex = inputIndex+1 table.insert(lookupFieldIndices[fieldID].colTotals, {index=inputIndex, x=x}) end end if (field.hsum == "True") then Wait.frames( function() recalculateSums(fieldID) end, 10 ) end end createDecals() for decalID, decal in pairs(decals) do lookupDecalIndices[decalID] = { inputs={}, selectionButtons={} } local rotationAdd = 180 local posMulx = 1 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) if (decal.locked != "True" and not sheetLocked and not locks.decals and not editingSheet) then local func = 'MarumEditableSetURL_'..decalID _G[func] = function(obj, ply, alt) obj.call("showImageURLPanel",{decalID, ply}) end local tooltip = getDecalTooltip(decalID) self.createButton({ value = decal.url, tooltip=tooltip, click_function = func, function_owner = self, position = pos, width = 490, height = 490, rotation={x=0,y=0,z=0}, font_size = 10, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=decal.scale.y*scale.y}, font_color= {r=0, g=0, b=0, a=0}, color = {r=0, g=0, b=0, a=0}, }) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="decal", id=decalID} table.insert(lookupDecalIndices[decalID].inputs, {index=buttonIndex}) end if (inputIndex%10==0) then coroutine.yield(0) end end for checkID, check in pairs(checks) do lookupCheckIndices[checkID] = { buttons={}, selectionButtons={} } local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} local upright = self.getTransformUp().y > 0 local shouldFlip = (check.locked == "True" or sheetLocked or locks.checks) and upright if (editingSheet or shouldFlip) then checkScale.x = -checkScale.x rotationZ = 180 end for x=1, check.array.x do for y=1, check.array.y do local arrayID = x+(y-1)*check.array.x local func = "MarumEditableClickCheckbox_"..checkID.."_"..arrayID if (check.locked == "True" or sheetLocked or locks.checks) then func = "null" end local rotationY = 0 local posMul = 1 if (flip == "True") then rotationY = 180 posMul = -posMul end local pos = getCheckPosition(checkID, x, y) local bindex = buttonIndex _G['MarumEditableClickCheckbox_'..checkID..'_'..arrayID] = function(obj, ply, alt) local fID = checkID local aID = arrayID local bi = bindex obj.call("clickcheck",{fID, aID, alt, bi, ply}) end local tooltip = getCheckTooltip(checkID) local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) buttonIndex = buttonIndex+1 lookupButtonIndexToInfo[buttonIndex] = {type="check", id=checkID, arrayID=arrayID} table.insert(lookupCheckIndices[checkID].buttons, {index=buttonIndex, x=x, y=y, arrayID=arrayID}) self.createButton({ label = label, tooltip=tooltip, click_function = func, function_owner = self, alignment = 3, position = pos, width = 250, height = 250, rotation={x=0,y=rotationY,z=rotationZ}, font_size = check.font, scale={x=checkScale.x, y=checkScale.y, z=checkScale.z}, font_color=alphaCorrectedColor, color = check.checkColor, tab = 0 }) if (buttonIndex%10==0) then coroutine.yield(0) end end end end if (not getCommited() and editingSheet) then createSelectionButtons() end creating = false return 1 end function revertField(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=fields[fieldID].value[v.arrayID]}) end end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is locked", ply, {r=1,g=0.5,b=0}) lastFieldLockedMessage = fieldID end end end function recalculateSums(fieldID) local field = fields[fieldID] if (field.vsum) then for k, v in pairs(lookupFieldIndices[fieldID].colTotals) do local vsum = 0 for y=1, field.array.y do local arrayID = v.x+(y-1)*field.array.x if (tonumber(field.value[arrayID])) then vsum = vsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..vsum.."[/u]"}) end end if (field.hsum) then for k, v in pairs(lookupFieldIndices[fieldID].rowTotals) do local hsum = 0 for x=1, field.array.x do local arrayID = x+(v.y-1)*field.array.x if (tonumber(field.value[arrayID])) then hsum = hsum + tonumber(field.value[arrayID]) end end self.editInput({index=v.index-1, value="[u]"..hsum.."[/u]"}) end end end function revertFieldSum(args) local fieldID = args[1] local ply = args[2] local index = args[3] local selected = args[4] if (not selected) then Wait.frames( function() recalculateSums(fieldID) end, 1 ) else if (lastFieldLockedMessage != fieldID) then broadcastToColor("This text is reserved for the total sum", ply, {r=1,g=1,b=0}) lastFieldLockedMessage = fieldID end end end function createDecals() local decalParameters = {} for decalID, decal in pairs(decals) do local rotationAdd = 180 if (flip == "True") then rotationAdd = 0 end local pos = getDecalPosition(decalID) table.insert(decalParameters, { url=decal.url, name="Image #"..decalID, position={x=-pos.x, y=pos.y, z=pos.z}, rotation={x=90, y=rotationAdd+decal.rotation, z=0}, scale={x=decal.scale.x*scale.x, y=decal.scale.y*scale.y, z=1} }) end self.setDecals(decalParameters) end function getFieldPosition(fieldID, x, y) local field = fields[fieldID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(field.pos.x+(x-1)*field.distance.x) * scale.x * mul,y=height+0.002,z=(field.pos.y+(y-1)*field.distance.y) * scale.y * mul} end function getFieldTooltip(fieldID, arrayID) local field = fields[fieldID] local tooltip = "" if (not field.locked) then if (field.tooltip == nil or field.tooltip:find("name")) then tooltip = field.name or "" elseif (field.tooltip:find("content")) then tooltip = field.value[arrayID] end end return tooltip end function getCheckPosition(checkID, x, y) local check = checks[checkID] local mul = 1 if (flip == "True") then mul = -1 end return {x=(check.pos.x+(x-1)*check.distance.x) * mul*scale.x,y=height+0.002,z=(check.pos.y+(y-1)*check.distance.y) * mul*scale.y} end function getDecalPosition(decalID) local decal = decals[decalID] local mul = 1 if (flip == "True") then mul = -1 end return {x=decal.pos.x*mul*scale.x, y=height+0.005, z=decal.pos.y*mul*scale.y} end function getDecalTooltip(decalID) local decal = decals[decalID] local tooltip = "" if (decal.tooltip == nil) then tooltip = decal.name or "" elseif (decal.tooltip:find("name")) then tooltip = decal.name or "" elseif (decal.tooltip:find("hint")) then tooltip = "Click to change image" end return tooltip end function increaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])+1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function decreaseCounter(args) local fieldID = tonumber(args[1]) local arrayID = tonumber(args[2]) local ply = args[3] local field = fields[fieldID] if (field.value[arrayID] == nil or field.value[arrayID] == "") then field.value[arrayID] = 0 end if (tonumber(field.value[arrayID])) then field.value[arrayID] = tonumber(field.value[arrayID])-1 updateFieldNameContentAndTooltip(fieldID) if (field.vsum == "True") then recalculateSums(fieldID) end updateSave() else broadcastToColor("Field does not contain a valid number", ply) end end function scheduleUpdate() if (dirtyUpdateWait != nil) then Wait.stop(dirtyUpdateWait) end dirtyUpdateWait = Wait.time( function() for k, v in pairs(self.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then self.editInput({index=k-1, tooltip=v.value}) end end end end end end, 1 ) end function edit(obj, ply, value, selected) for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="field") then local field = fields[lookupInputIndexToInfo[k].id] if (v.value != field.value[lookupInputIndexToInfo[k].arrayID]) then field.value[lookupInputIndexToInfo[k].arrayID] = v.value if (field.tooltip != nil) then if (field.tooltip:find("content")) then scheduleUpdate() end end if (field.role != nil) then if (field.role:find("name")) then self.setName(value) elseif (field.role:find("description")) then self.setDescription(value) end end updateSave() break end end end end end function editUrl(obj, ply, value, selected) local shouldRefresh = false local shouldUpdateSave = false for k, v in pairs(obj.getInputs()) do if (lookupInputIndexToInfo[k] != nil) then if (lookupInputIndexToInfo[k].type=="decal") then if (v.value != decals[lookupInputIndexToInfo[k].id].url) then decals[lookupInputIndexToInfo[k].id].url = v.value shouldRefresh = true shouldUpdateSave = true updateSave() createDecals() break end end end end end function clickcheck(args) local checkID = tonumber(args[1]) local arrayID = tonumber(args[2]) local alt_click = args[3] local buttonIndex = args[4] local ply = args[5] local check = checks[checkID] local value = tonumber(check.value[arrayID]) if (value == nil) then value = 1 end if (alt_click) then if (value > 0) then check.value[arrayID] = 0 else check.value[arrayID] = 1 end else if (value == 1) then check.value[arrayID] = 2 else if (value == 2) then check.value[arrayID] = 1 elseif (check.fillFromDisabled == "True") then check.value[arrayID] = 2 else broadcastToColor("This checkbox is disabled. You can enable/disable checkboxes with Right click.", ply) end end end local label, color, alphaCorrectedColor = getCheckLabelAndColor(checkID, arrayID) self.editButton({index=buttonIndex, label=label, font_color=alphaCorrectedColor}) updateSave() end function getFieldTextColor(fieldID) local field = fields[fieldID] local textAlpha = field.textColor.a if (tonumber(field.fieldColor.a) > 0) then textAlpha = tonumber(field.textColor.a)/tonumber(field.fieldColor.a) else textAlpha = tonumber(field.textColor.a)*100 end return {r=field.textColor.r,g=field.textColor.g,b=field.textColor.b,a=textAlpha} end function getCheckTooltip(checkID) local check=checks[checkID] local tooltip = "" if (not check.locked) then if (check.tooltip == nil or check.tooltip:find("name")) then tooltip = check.name elseif (check.tooltip:find("hint")) then tooltip = "Left click to toggle, Right click to enable/disable" end end return tooltip end function getCheckLabelAndColor(checkID, arrayID) local check = checks[checkID] local label = check.characters.empty if (check.value[arrayID] == 0) then label = check.characters.disabled elseif (check.value[arrayID] == 2) then label = check.characters.filled end local color = nil if (check.separateColors == "True") then if (check.value[arrayID] == 0) then color = check.textColorDisabled or check.textColor elseif (check.value[arrayID] == 1) then color = check.textColorOff or check.textColor elseif (check.value[arrayID] == 2) then color = check.textColorOn or check.textColor else color = check.textColorOff or check.textColor end else color = check.textColorOn or check.textColor end local alpha = 1 local checkAlpha = math.max(1/255, check.checkColor.a) if (tonumber(check.checkColor.a) > 0) then alpha = tonumber(color.a)/tonumber(checkAlpha) else alpha = tonumber(color.a)*100 end return label, color, {r=color.r, g=color.g, b=color.b, a=color.a*alpha} end function split (inputstr, sep) if sep == nil then sep = "%s" end local t={} for str in string.gmatch(inputstr, "([^"..sep.."]+)") do table.insert(t, str) end return t end function onRotate(spin, flip, player_color, old_spin, old_flip) updateLockedFieldOrientation(flip < 90) end function onDrop(ply) updateLockedFieldOrientation(self.getTransformUp().y >= 0) end function updateLockedFieldOrientation(upright) for k, v in pairs(fields) do if (v.locked == "True" or sheetLocked or locks.fields or v.vsum == "True") then local field = v local fieldScale = {x=scale.x,y=1,z=scale.y} local rotation = {x=0,y=0,z=0} if (flip == "True") then rotation.y = 180 end if (upright) then rotation.z = 180 fieldScale.x = -scale.x end if (v.locked == "True" or sheetLocked or locks.fields) then for arrayID, inp in pairs(lookupFieldIndices[k].inputs) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end for arrayID, inp in pairs(lookupFieldIndices[k].colTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end for arrayID, inp in pairs(lookupFieldIndices[k].rowTotals) do self.editInput({index=inp.index-1, rotation=rotation, scale=fieldScale}) end end end for k, v in pairs(checks) do if (v.locked == "True" or sheetLocked or locks.fields) then local check=v local rotationZ = 0 local checkScale = {x=scale.x * check.size.x,y=1,z=scale.y * check.size.y} if (upright) then checkScale.x = -checkScale.x rotationZ = 180 end local rotationY = 0 if (flip == "True") then rotationY = 180 end for arrayID, inp in pairs(lookupCheckIndices[k].buttons) do self.editButton({index=inp.index-1, rotation={x=0,y=rotationY,z=rotationZ}, scale=checkScale}) end end end end function showImageURLPanel(args) local decalID = args[1] local ply = args[2] Player[ply].showInputDialog("Set image URL", function (text, player_color) decals[decalID].url = text createDecals() updateSave() end ) end function updateFieldNameContentAndTooltip(fieldID) local field = fields[fieldID] for k, v in pairs(lookupFieldIndices[fieldID].inputs) do self.editInput({index=v.index-1, value=field.value[v.arrayID], tooltip=getFieldTooltip(fieldID, v.arrayID)}) end local name = "T"..fieldID local tooltip = "Select "..(field.name or name) for k, v in pairs(lookupFieldIndices[fieldID].selectionButtons) do self.editButton({index=v.index-1, tooltip = tooltip}) end end function getCommited() return true end --LuaScriptState{"checks":[],"decals":[],"fields":[{"align":3,"array":{"x":"3","y":1},"distance":{"x":"4","y":"1"},"fieldColor":{"a":0.39215686917305,"b":1,"g":1,"r":1},"font":"1000","locked":false,"name":"","pos":{"x":-4,"y":0.2},"role":"Normal Field","size":{"x":"1500","y":"500"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["",""]},{"align":3,"array":{"x":1,"y":1},"distance":{"x":1,"y":1},"fieldColor":{"a":0,"b":1,"g":1,"r":1},"font":"200","locked":false,"name":"","pos":{"x":0,"y":-0.9},"role":"Normal Field","size":{"x":"1000","y":"300"},"textColor":{"a":1,"b":0,"g":0,"r":0},"tooltip":"name","value":["Final Guess"]}],"flip":"False","height":"0.4","locks":{"checks":false,"decals":false,"fields":false},"nudgeDistance":0.1,"scale":{"x":"1","y":"4.61"},"sheetLocked":false}XmlUI618GUID3eda65Name Custom_ModelTransformposX &posYn?posZ WK@rotXzrotY~f@rotZf@scaleX?scaleY?scaleZ?NicknameIFTTT by UglehDescriptionv0.0.1GMNotesAltLookAngle&xyzColorDiffuse&r ,5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomMeshOMeshURLihttps://steamusercontent-a.akamaihd.net/ugc/766103939698379333/6E044FA7595AA0ECCD53F991B7CD56974DC8505A/DiffuseURLihttps://steamusercontent-a.akamaihd.net/ugc/832513197079912705/7F68CA1C3C1528326399904A62CA6D05428EBC84/NormalURLColliderURLConvexMaterialIndexTypeIndexCastShadowsLuaScriptM/--Tabletop Simulator Lua --Trivia Game, grab data from https://ugleh.com/tts/openaijeopardyjson.php saved_data = { White = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Red = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Orange = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Yellow = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Green = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Teal = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Blue = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Purple = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Pink = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Brown = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Black = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Grey = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" } } COLORS = {"White", "Red", "Orange", "Yellow", "Green", "Teal", "Blue", "Purple", "Pink", "Brown", "Black", "Grey"} function onload(data) --Load saved data if data ~= "" then saved_data = JSON.decode(data) end --Setup buttons --For each player color for i, color in pairs(COLORS) do --Create a button self.createButton({ click_function = "click", function_owner = self, label = "", position = {6.5,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 400, height = 400, font_size = 250, color = color, font_color = {1,1,1} }) self.createButton({ click_function = "if_this_" .. color, function_owner = self, -- Get label from saved_data["color"]["if_this"], if blank use "IF THIS" label = (saved_data[color]["if_this"] ~= "" and saved_data[color]["if_this"]) or "IF THIS", position = {3.5,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 2000, height = 400, font_size = 200, color = {0,0,0}, font_color = color }) _G["if_this_" .. color] = function(object, player_color) if_this_global(color, player_color) end self.createButton({ click_function = "if_this_value_" .. color, function_owner = self, label = (saved_data[color]["if_this_value"] ~= "" and saved_data[color]["if_this_value"]) or "{stored value}", position = {0.35,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 1000, height = 400, font_size = 150, color = {0,0,0}, font_color = color, }) _G["if_this_value_" .. color] = function(object, player_color) if_this_value_global(color, player_color) end self.createButton({ click_function = "then_that_" .. color, function_owner = self, label = (saved_data[color]["then_that"] ~= "" and saved_data[color]["then_that"]) or "THEN THAT", position = {-2.8,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 2000, height = 400, font_size = 200, color = {0,0,0}, font_color = color }) _G["then_that_" .. color] = function(object, player_color) then_that_global(color, player_color) end self.createButton({ click_function = "then_that_value_" .. color, function_owner = self, label = (saved_data[color]["then_that_value"] ~= "" and saved_data[color]["then_that_value"]) or "{stored value}", position = {-5.9,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 1000, height = 400, font_size = 150, color = {0,0,0}, font_color = color }) _G["then_that_value_" .. color] = function(object, player_color) then_that_value_global(color, player_color) end end self.createButton({ click_function = "hideMenu", function_owner = self, label = "Hide Menu", position = {-7.5,0.17,-3.33}, rotation = {0,90,0}, width = 1400, height = 400, font_size = 250, color = {0,0,0}, font_color = {1,1,1} }) self.createButton({ click_function = "showMenu", function_owner = self, label = "SHOW", position = {0,-0.13,0}, rotation = {180,0,0}, width = 750, height = 400, font_size = 250, color = {0,0,0}, font_color = {1,1,1}, scale = {10, 10, 10} }) -- For each player color for i,j in ipairs(Player.getAvailableColors()) do -- If the player color is not in saved_data, skip if (saved_data[j]["if_this"] == "IF Game Loads") then if (saved_data[j]["then_that"] == "THEN Change Team") then playerChangeTeam(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Change Color") then playerChangeColor(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Promote") then playerPromote(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Blindfold") then playerBlindfold(j, saved_data[j]) end end end end function click() end savedPosition = {} savedRotation = {} function hideMenu() savedPosition = self.getPosition() savedRotation = self.getRotation() self.setScale({0.1, 0.1, 0.1}) self.flip() end function showMenu() self.setScale({1, 1, 1}) self.flip() self.setPosition(savedPosition) end function if_this_global(color_selected, player_color) Player[player_color].showOptionsDialog("Select the players IF THIS", {"IF Sits In Color", "IF Players Turn Start", "IF Players Turn End", "IF Game Loads"}, "IF Sits In Color", function(option_text, option_index, playerColor) editButton(color_selected, option_text, 1) end) end function if_this_value_global(color_selected, player_color) Player[player_color].showInputDialog("Enter the value for IF THIS", "", function(input, playerColor) editButton(color_selected, input, 2) end) end function then_that_global(color_selected, player_color) Player[player_color].showOptionsDialog("Select the players THEN THAT", {"THEN Change Team", "THEN Change Color", "THEN Promote", "THEN Set Camera Mode", "THEN Blindfold"}, "THEN Change Team", function(option_text, option_index, playerColor) editButton(color_selected, option_text, 3) end) end function then_that_value_global(color_selected, player_color) Player[player_color].showInputDialog("Enter the value for THEN THAT", "", function(input, playerColor) editButton(color_selected, input, 4) end) end offsetToKey = {"if_this", "if_this_value", "then_that", "then_that_value"} function editButton(color_selected, text, offset) local colorIndex = 0 for i, color in pairs(COLORS) do if color == color_selected then colorIndex = i end end local math = (colorIndex * 5) - 5 + offset self.editButton({ index = math, label = text, }) saved_data[color_selected][offsetToKey[offset]] = text end function saveData() local data = JSON.encode(saved_data) self.script_state = data end function onSave() saveData() return JSON.encode(saved_data) end function onPlayerChangeColor(player_color) local playerTable = saved_data[player_color] print(playerTable["then_that"]) if (playerTable["if_this"] == "IF Sits In Color") then if (playerTable["then_that"] == "THEN Change Team") then print(playerTable["then_that"]) playerChangeTeam(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Change Color") then playerChangeColor(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Promote") then playerPromote(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Blindfold") then playerBlindfold(player_color, playerTable) end end end function onPlayerTurnStart(player_color_start, player_color_previous) if (saved_data[player_color_start] == nil) then return end local playerTable = saved_data[player_color_start] if (playerTable["if_this"] == "IF Players Turn Start") then if (playerTable["then_that"] == "THEN Change Team") then playerChangeTeam(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Change Color") then playerChangeColor(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Promote") then playerPromote(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Blindfold") then playerBlindfold(player_color_start, playerTable) end end end function onPlayerTurnEnd(player_color_end, player_color_next) if (saved_data[player_color_end] == nil) then return end local playerTable = saved_data[player_color_end] log(playerTable) if (playerTable["if_this"] == "IF Players Turn End") then if (playerTable["then_that"] == "THEN Change Team") then playerChangeTeam(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Change Color") then playerChangeColor(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Promote") then playerPromote(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Blindfold") then playerBlindfold(player_color_end, playerTable) end end end function playerChangeTeam(player, playerTable) Player[player].team = playerTable["then_that_value"] end function playerChangeColor(player, playerTable) Player[player].changeColor(playerTable["then_that_value"]) end function playerPromote(player, playerTable) Player[player].promote() end function playerSetCameraMode(player, playerTable) Player[player].setCameraMode(playerTable["then_that_value"]) end function playerBlindfold(player, playerTable) if (playerTable["then_that_value"] == "false") then Player[player].blindfolded = false else Player[player].blindfolded = true end endLuaScriptState#{"Black":{"if_this":"","if_this_value":"","then_that":"","then_that_value":""},"Blue":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"Brown":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"},"Green":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"Grey":{"if_this":"","if_this_value":"","then_that":"","then_that_value":""},"Orange":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"},"Pink":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Diamonds"},"Purple":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Diamonds"},"Red":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"},"Teal":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"White":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"Yellow":{"if_this":"IF Sits In Color","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"}}XmlUI627GUIDab9fe4Name Custom_ModelTransformposX`/?posYn?posZ WK@rotX@N־rotY@~f@rotZf@scaleX?scaleY?scaleZ?NicknameIFTTT by UglehDescriptionv0.0.1GMNotesAltLookAngle&xyzColorDiffuse&r ,5?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomMeshOMeshURLihttps://steamusercontent-a.akamaihd.net/ugc/766103939698379333/6E044FA7595AA0ECCD53F991B7CD56974DC8505A/DiffuseURLihttps://steamusercontent-a.akamaihd.net/ugc/832513197079912705/7F68CA1C3C1528326399904A62CA6D05428EBC84/NormalURLColliderURLConvexMaterialIndexTypeIndexCastShadowsLuaScriptM/--Tabletop Simulator Lua --Trivia Game, grab data from https://ugleh.com/tts/openaijeopardyjson.php saved_data = { White = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Red = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Orange = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Yellow = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Green = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Teal = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Blue = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Purple = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Pink = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Brown = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Black = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" }, Grey = { if_this = "", if_this_value = "", then_that = "", then_that_value = "" } } COLORS = {"White", "Red", "Orange", "Yellow", "Green", "Teal", "Blue", "Purple", "Pink", "Brown", "Black", "Grey"} function onload(data) --Load saved data if data ~= "" then saved_data = JSON.decode(data) end --Setup buttons --For each player color for i, color in pairs(COLORS) do --Create a button self.createButton({ click_function = "click", function_owner = self, label = "", position = {6.5,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 400, height = 400, font_size = 250, color = color, font_color = {1,1,1} }) self.createButton({ click_function = "if_this_" .. color, function_owner = self, -- Get label from saved_data["color"]["if_this"], if blank use "IF THIS" label = (saved_data[color]["if_this"] ~= "" and saved_data[color]["if_this"]) or "IF THIS", position = {3.5,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 2000, height = 400, font_size = 200, color = {0,0,0}, font_color = color }) _G["if_this_" .. color] = function(object, player_color) if_this_global(color, player_color) end self.createButton({ click_function = "if_this_value_" .. color, function_owner = self, label = (saved_data[color]["if_this_value"] ~= "" and saved_data[color]["if_this_value"]) or "{stored value}", position = {0.35,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 1000, height = 400, font_size = 150, color = {0,0,0}, font_color = color, }) _G["if_this_value_" .. color] = function(object, player_color) if_this_value_global(color, player_color) end self.createButton({ click_function = "then_that_" .. color, function_owner = self, label = (saved_data[color]["then_that"] ~= "" and saved_data[color]["then_that"]) or "THEN THAT", position = {-2.8,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 2000, height = 400, font_size = 200, color = {0,0,0}, font_color = color }) _G["then_that_" .. color] = function(object, player_color) then_that_global(color, player_color) end self.createButton({ click_function = "then_that_value_" .. color, function_owner = self, label = (saved_data[color]["then_that_value"] ~= "" and saved_data[color]["then_that_value"]) or "{stored value}", position = {-5.9,0.17,5.2 - (i * 0.8)}, rotation = {0,180,0}, width = 1000, height = 400, font_size = 150, color = {0,0,0}, font_color = color }) _G["then_that_value_" .. color] = function(object, player_color) then_that_value_global(color, player_color) end end self.createButton({ click_function = "hideMenu", function_owner = self, label = "Hide Menu", position = {-7.5,0.17,-3.33}, rotation = {0,90,0}, width = 1400, height = 400, font_size = 250, color = {0,0,0}, font_color = {1,1,1} }) self.createButton({ click_function = "showMenu", function_owner = self, label = "SHOW", position = {0,-0.13,0}, rotation = {180,0,0}, width = 750, height = 400, font_size = 250, color = {0,0,0}, font_color = {1,1,1}, scale = {10, 10, 10} }) -- For each player color for i,j in ipairs(Player.getAvailableColors()) do -- If the player color is not in saved_data, skip if (saved_data[j]["if_this"] == "IF Game Loads") then if (saved_data[j]["then_that"] == "THEN Change Team") then playerChangeTeam(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Change Color") then playerChangeColor(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Promote") then playerPromote(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(j, saved_data[j]) elseif (saved_data[j]["then_that"] == "THEN Blindfold") then playerBlindfold(j, saved_data[j]) end end end end function click() end savedPosition = {} savedRotation = {} function hideMenu() savedPosition = self.getPosition() savedRotation = self.getRotation() self.setScale({0.1, 0.1, 0.1}) self.flip() end function showMenu() self.setScale({1, 1, 1}) self.flip() self.setPosition(savedPosition) end function if_this_global(color_selected, player_color) Player[player_color].showOptionsDialog("Select the players IF THIS", {"IF Sits In Color", "IF Players Turn Start", "IF Players Turn End", "IF Game Loads"}, "IF Sits In Color", function(option_text, option_index, playerColor) editButton(color_selected, option_text, 1) end) end function if_this_value_global(color_selected, player_color) Player[player_color].showInputDialog("Enter the value for IF THIS", "", function(input, playerColor) editButton(color_selected, input, 2) end) end function then_that_global(color_selected, player_color) Player[player_color].showOptionsDialog("Select the players THEN THAT", {"THEN Change Team", "THEN Change Color", "THEN Promote", "THEN Set Camera Mode", "THEN Blindfold"}, "THEN Change Team", function(option_text, option_index, playerColor) editButton(color_selected, option_text, 3) end) end function then_that_value_global(color_selected, player_color) Player[player_color].showInputDialog("Enter the value for THEN THAT", "", function(input, playerColor) editButton(color_selected, input, 4) end) end offsetToKey = {"if_this", "if_this_value", "then_that", "then_that_value"} function editButton(color_selected, text, offset) local colorIndex = 0 for i, color in pairs(COLORS) do if color == color_selected then colorIndex = i end end local math = (colorIndex * 5) - 5 + offset self.editButton({ index = math, label = text, }) saved_data[color_selected][offsetToKey[offset]] = text end function saveData() local data = JSON.encode(saved_data) self.script_state = data end function onSave() saveData() return JSON.encode(saved_data) end function onPlayerChangeColor(player_color) local playerTable = saved_data[player_color] print(playerTable["then_that"]) if (playerTable["if_this"] == "IF Sits In Color") then if (playerTable["then_that"] == "THEN Change Team") then print(playerTable["then_that"]) playerChangeTeam(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Change Color") then playerChangeColor(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Promote") then playerPromote(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(player_color, playerTable) elseif (playerTable["then_that"] == "THEN Blindfold") then playerBlindfold(player_color, playerTable) end end end function onPlayerTurnStart(player_color_start, player_color_previous) if (saved_data[player_color_start] == nil) then return end local playerTable = saved_data[player_color_start] if (playerTable["if_this"] == "IF Players Turn Start") then if (playerTable["then_that"] == "THEN Change Team") then playerChangeTeam(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Change Color") then playerChangeColor(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Promote") then playerPromote(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(player_color_start, playerTable) elseif (playerTable["then_that"] == "THEN Blindfold") then playerBlindfold(player_color_start, playerTable) end end end function onPlayerTurnEnd(player_color_end, player_color_next) if (saved_data[player_color_end] == nil) then return end local playerTable = saved_data[player_color_end] log(playerTable) if (playerTable["if_this"] == "IF Players Turn End") then if (playerTable["then_that"] == "THEN Change Team") then playerChangeTeam(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Change Color") then playerChangeColor(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Promote") then playerPromote(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Set Camera Mode") then playerSetCameraMode(player_color_end, playerTable) elseif (playerTable["then_that"] == "THEN Blindfold") then playerBlindfold(player_color_end, playerTable) end end end function playerChangeTeam(player, playerTable) Player[player].team = playerTable["then_that_value"] end function playerChangeColor(player, playerTable) Player[player].changeColor(playerTable["then_that_value"]) end function playerPromote(player, playerTable) Player[player].promote() end function playerSetCameraMode(player, playerTable) Player[player].setCameraMode(playerTable["then_that_value"]) end function playerBlindfold(player, playerTable) if (playerTable["then_that_value"] == "false") then Player[player].blindfolded = false else Player[player].blindfolded = true end endLuaScriptState{"Black":{"if_this":"","if_this_value":"","then_that":"","then_that_value":""},"Blue":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"Brown":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"},"Green":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"Grey":{"if_this":"","if_this_value":"","then_that":"","then_that_value":""},"Orange":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"},"Pink":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Diamonds"},"Purple":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Diamonds"},"Red":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"},"Teal":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"White":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Clubs"},"Yellow":{"if_this":"IF Game Loads","if_this_value":"","then_that":"THEN Change Team","then_that_value":"Jokers"}}XmlUI63J GUID73b5caName Custom_TileTransformposX 'posY'?posZG@rotXѤv?rotYRf@rotZ@_k'?scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074702/39B04209548F4FBD7E39920407750F45882C25D7/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUIStates 3MGUID283be3Name Custom_TileTransformposX -@posY29?posZ`G@rotXm?rotY@f@rotZpv@scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074186/13630B50A86599B60C5737A41B4FC1628F03A51D/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI4MGUID192c82Name Custom_TileTransformposX 'posY'?posZG@rotXѤv?rotYRf@rotZ@_k'?scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458074453/7BD4077DD9E23F76DE364C857ACDA62F5DCF625B/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI1MGUID4410eeName Custom_TileTransformposX 'posY'?posZG@rotXѤv?rotYRf@rotZ@_k'?scaleX`8!@scaleY?scaleZ`8!@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomImageImageURLjhttps://steamusercontent-a.akamaihd.net/ugc/2496766278458075049/487709CFE814F0B557813886CAE935F103B8CC24/ImageSecondaryURLImageScalar?WidthScaleCustomTile8TypeThickness?StackableStretchLuaScriptLuaScriptStateXmlUI64dGUID8430cfNameCustom_AssetbundleTransformposX?posY3posZX'rotXrotYrotZscaleX@scaleY@scaleZ@NicknameDescriptionGMNotesAltLookAngle&xyzColorDiffuse&r?g?b?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsCustomAssetbundleAssetbundleURLjhttps://steamusercontent-a.akamaihd.net/ugc/2009220912625490963/F36139927D241A9A4196B5FA118CC0EDC5BAA24D/AssetbundleSecondaryURLMaterialIndexTypeIndexLoopingEffectIndexLuaScript2function onLoad() self.interactable = false endLuaScriptStateXmlUI65ZGUID2e39f2NameFogOfWarTriggerTransformposXBposY@- @posZY#@rotXrotYrotZscaleX Z2@scaleY`ff@scaleZ F@NicknameDescriptionClubs Team ZoneGMNotesAltLookAngle&xyzColorDiffuse1r?g?b?a?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBlackFogHidePointersFogReverseHidingFogSeethroughLuaScriptfunction updateVisibility() local hide = {} local team_name = 'Clubs' for _, player in ipairs(Player.getPlayers()) do if player.team ~= team_name then table.insert(hide, player.color) end end for _, occupyingObject in ipairs(self.getObjects()) do occupyingObject.setInvisibleTo(hide) end end function onLoad() updateVisibility() end -- Called when a player joins the table function onPlayerConnect(player) updateVisibility() end -- Called when a player changes their color function onPlayerChangeColor(player, previousColor) updateVisibility() end -- If you have an event for team changes, use it; otherwise you might handle team changes via a custom function function onPlayerChangeTeam(player, previousTeam) updateVisibility() end function onObjectLeaveZone(zone, object) updateVisibility() object.setInvisibleTo({}) end function onObjectEnterZone(zone, object) updateVisibility() end LuaScriptStateXmlUI66\GUID4a64bfNameFogOfWarTriggerTransformposX@9C@posY @posZ@0%@rotXrotYrotZscaleXZ2@scaleY`ff@scaleZF@NicknameDescriptionJokers Team ZoneGMNotesAltLookAngle&xyzColorDiffuse1r?g?b?a?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBlackFogHidePointersFogReverseHidingFogSeethroughLuaScriptfunction updateVisibility() local hide = {} local team_name = 'Jokers' for _, player in ipairs(Player.getPlayers()) do if player.team ~= team_name then table.insert(hide, player.color) end end for _, occupyingObject in ipairs(self.getObjects()) do occupyingObject.setInvisibleTo(hide) end end function onLoad() updateVisibility() end -- Called when a player joins the table function onPlayerConnect(player) updateVisibility() end -- Called when a player changes their color function onPlayerChangeColor(player, previousColor) updateVisibility() end -- If you have an event for team changes, use it; otherwise you might handle team changes via a custom function function onPlayerChangeTeam(player, previousTeam) updateVisibility() end function onObjectLeaveZone(zone, object) updateVisibility() object.setInvisibleTo({}) end function onObjectEnterZone(zone, object) updateVisibility() end LuaScriptStateXmlUI67`GUID1a4dc2NameFogOfWarTriggerTransformposX`4?posY @posZ@>rotXrotYrotZscaleX >mS@scaleY`ff@scaleZ6@NicknameDescriptionDiamonds Team ZoneGMNotesAltLookAngle&xyzColorDiffuse1r?g?b?a?LayoutGroupSortIndexValueLockedGridSnapIgnoreFoWMeasureMovementDragSelectableAutoraiseStickyTooltipGridProjectionHideWhenFaceDownHandsFogColorBlackFogHidePointersFogReverseHidingFogSeethroughLuaScriptfunction updateVisibility() local hide = {} local team_name = 'Diamonds' for _, player in ipairs(Player.getPlayers()) do if player.team ~= team_name then table.insert(hide, player.color) end end for _, occupyingObject in ipairs(self.getObjects()) do occupyingObject.setInvisibleTo(hide) end end function onLoad() updateVisibility() end -- Called when a player joins the table function onPlayerConnect(player) updateVisibility() end -- Called when a player changes their color function onPlayerChangeColor(player, previousColor) updateVisibility() end -- If you have an event for team changes, use it; otherwise you might handle team changes via a custom function function onPlayerChangeTeam(player, previousTeam) updateVisibility() end function onObjectLeaveZone(zone, object) updateVisibility() object.setInvisibleTo({}) end function onObjectEnterZone(zone, object) updateVisibility() end LuaScriptStateXmlUI