Skip to content
26 changes: 26 additions & 0 deletions Gui/Layouts/MemoryPanelGui.layout
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<MyGUI type="Layout" version="3.2.0">
<Widget type="Widget" skin="BackgroundInteractableWide" name="BackPanel" position_real="0 0 0.2604166666666667 0.25462962962962965">
<Widget type="TextBox" align="HCenter Top" skin="TextBox" name="Title" position_real="0.208 0.05454545454545454 0.584 0.18181818181818182">
<Property key="Caption" value="#ffff00M E M O R Y#ffffff" />
<Property key="FontName" value="SM_Button" />
<Property key="TextAlign" value="Center" />
<Property key="TextShadow" value="true" />
</Widget>
<Widget type="Widget" align="HCenter Bottom" skin="NonBlurryBackgroundDarkRoundedLowerLeft" position_real="0 0.2909090909090909 1 0.7090909090909091">
<Widget type="Widget" align="Left Top" skin="SearchBarBackground" position_real="0.05 0.07692307692307693 0.9 0.6153846153846154">
<Widget type="EditBox" align="HStretch VStretch" skin="PhotoDescription" name="ValueInput" position_real="0.008888888888888889 0.075 0.9822222222222222 0.85">
<Property key="FontName" value="SM_Button" />
<Property key="TextShadow" value="false" />
<Property key="TextAlign" value="Left Top" />
<Property key="MultiLine" value="true" />
<Property key="VisibleVScroll" value="true" />
</Widget>
</Widget>
<Widget type="Button" align="Left Top" skin="PrimaryButton" name="SaveWrittenVal" position_real="0.04 0.7692307692307693 0.92 0.1794871794871795">
<Property key="FontName" value="SM_Button" />
<Property key="TextAlign" value="Center" />
<Property key="Caption" value="Write" />
</Widget>
</Widget>
</Widget>
</MyGUI>
159 changes: 158 additions & 1 deletion Scripts/interactable/NumberLogic/MemoryPanel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,50 @@ MemoryPanel.colorNormal = sm.color.new( 0x7F567Dff )
MemoryPanel.colorHighlight = sm.color.new( 0x9f7fa5ff )
MemoryPanel.poseWeightCount = 1

local character_width_table = {
["0"] = 13,
["1"] = 11,
["2"] = 12,
["3"] = 12,
["4"] = 12,
["5"] = 12,
["6"] = 12,
["7"] = 11,
["8"] = 12,
["9"] = 12,
["-"] = 9,
["."] = 6,
[","] = 6,
[":"] = 6
}

local line_width = 400

function wrapInput(text)
local wrapped = {}
local total = 0

for i = 1, #text do
local ch = text:sub(i,i)
local w = character_width_table[ch] or 6

-- if adding this char would exceed the line width, start a new line (but avoid leading newline)
if total > 0 and (total + w) > line_width then
wrapped[#wrapped + 1] = "\n"
total = 0
end

wrapped[#wrapped + 1] = ch
total = total + w
end

return table.concat(wrapped)
end

function stripInput(text)
-- sanitize allowed characters: digits, colon, comma, period, minus
return text:gsub("[^%d:,%.-]", "")
end

function MemoryPanel.server_onRefresh( self )
sm.isDev = true
Expand Down Expand Up @@ -58,11 +102,15 @@ function MemoryPanel.server_onCreate( self )
memorypanels[self.interactable.id] = self
end

function MemoryPanel.server_setData(self, saveData)
function MemoryPanel.server_setData(self, saveData, caller)
self.data = saveData
self.storage:save(saveData)
self.network:sendToClient(caller, "client_playSound")
end

function MemoryPanel.client_playSound(self)
sm.audio.play("GUI Item released", self.shape:getWorldPosition())
end

function MemoryPanel.server_onFixedUpdate( self, dt )
local parents = self.interactable:getParents()
Expand Down Expand Up @@ -129,6 +177,16 @@ function MemoryPanel.client_onCreate(self)
self.time = 0
end

function MemoryPanel.client_onDestroy(self)
self:client_onGuiCloseCallback()
end

function MemoryPanel.client_canInteract(self)
local tinker_key = mp_gui_getKeyBinding("Tinker", true)
sm.gui.setInteractionText("Press", tinker_key, "to open gui")
return true
end

function MemoryPanel.client_onFixedUpdate(self, dt)
local parents = self.interactable:getParents()
local address = 0
Expand Down Expand Up @@ -200,4 +258,103 @@ function MemoryPanel.client_setUvValue(self, value)
if value > 255 then value = 255 end
if value == math.huge then value = 0 end
self.interactable:setUvFrameIndex(value)
end

function MemoryPanel.client_onTinker(self, character, lookAt)
if mp_deprecated_game_version or not lookAt or character:getLockingInteractable() then return end

local mem_gui = sm.gui.createGuiFromLayout("$CONTENT_DATA/Gui/Layouts/MemoryPanelGui.layout", false, { backgroundAlpha = 0.5 })

local parts = {}
for k, v in pairs(self.data) do
parts[#parts + 1] = k .. ':' .. v
end

self.mem_gui_input = wrapInput(table.concat(parts, ","))

mem_gui:setText("ValueInput", self.mem_gui_input)

mem_gui:setButtonCallback("SaveWrittenVal", "client_gui_saveWrittenValue")

mem_gui:setTextChangedCallback("ValueInput", "client_onTextChangedCallback")
mem_gui:setOnCloseCallback("client_onGuiCloseCallback")

mem_gui:open()

self.mem_gui = mem_gui
end

function MemoryPanel.client_onTextChangedCallback(self, widget, text)
self.mem_gui_input = wrapInput(stripInput(text))
if text~=self.mem_gui_input then
self.mem_gui:setText("ValueInput", self.mem_gui_input)
end
end

function MemoryPanel.client_onGuiCloseCallback(self)
local mem_gui = self.mem_gui
if mem_gui and sm.exists(mem_gui) then
if mem_gui:isActive() then
mem_gui:close()
end

mem_gui:destroy()
end

self.mem_gui_input = nil
self.mem_gui = nil
end

function MemoryPanel.client_gui_saveWrittenValue(self)
local input = stripInput(self.mem_gui_input)
if not input then return end

-- sanitize allowed characters (already present)
wrapped = wrapInput(input)

-- reflect sanitized input back to GUI
self.mem_gui:setText("ValueInput", wrapped)

-- parse entries of the form "addr:value" or just "value" (addr defaults to 0)
local data = {}

for entry in input:gmatch("([^,]+)") do
-- trim surrounding whitespace
entry = entry:match("^%s*(.-)%s*$") or ""
if entry ~= "" then
-- require exactly one ':' in the entry (allow empty lhs/rhs)
local addrStr, valStr = entry:match("^([^:]*):([^:]*)$")
if addrStr then
-- parse address to integer, make positive
local addr = tonumber(addrStr) or 0
if addr < 0 then addr = -addr end
addr = math.floor(addr)

-- parse value (prefer numeric if looks like a number)
local numVal = tonumber(valStr)
local savedVal
if numVal and numVal == numVal then
savedVal = tostring(numVal)
else
-- if empty, default to "0"
if valStr == "" then
savedVal = "0"
else
savedVal = valStr
end
end

-- assign into data table
data[addr] = savedVal
end
end
end

-- ensure there's at least a default entry (address 0) if nothing parsed
if next(data) == nil then
data[0] = "0"
end

-- send parsed table to server to save
self.network:sendToServer("server_setData", data)
end