From d5d8218142eef46baecdddf7d3d57e1ad28fa0c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 21:52:51 +0000 Subject: [PATCH 1/3] Fix compile error: rename colorPalette -> partSwatches `colorPalette` collided with a name the LiveCode engine already knows, so `colorPalette()` parsed as a built-in call with a missing argument ("Expression: missing factor near )"). User-defined zero-arg calls like `shapePalette()` compile fine, confirming the name was the issue. Renamed the two new colour helpers to non-reserved names (partSwatches, swatchIndexOf). https://claude.ai/code/session_018VCGPT8pLd7wQhnLKdZDEx --- examples/box2dxt-contraption-builder.livecodescript | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/box2dxt-contraption-builder.livecodescript b/examples/box2dxt-contraption-builder.livecodescript index e13fc97..93d0cc5 100644 --- a/examples/box2dxt-contraption-builder.livecodescript +++ b/examples/box2dxt-contraption-builder.livecodescript @@ -3476,9 +3476,9 @@ on adjustPartProp pCtrl, pKey, pDir else resizePart pCtrl, 0.87 break case "color" - put colorPalette() into tPal + put partSwatches() into tPal put the number of lines of tPal into tCnt - put colorIndex(pCtrl, tPal) + pDir into tIdx + put swatchIndexOf(pCtrl, tPal) + pDir into tIdx if tIdx < 1 then put tCnt into tIdx if tIdx > tCnt then put 1 into tIdx set the backgroundColor of pCtrl to (line tIdx of tPal) @@ -3645,13 +3645,13 @@ on reapplyMaterial pCtrl end reapplyMaterial -- A generous palette the Colour setting cycles through. -function colorPalette +function partSwatches return "92,150,222" & cr & "84,206,142" & cr & "232,182,92" & cr & "214,92,92" & cr \ & "206,124,206" & cr & "120,200,232" & cr & "236,120,150" & cr & "150,160,180" & cr \ & "120,90,210" & cr & "240,140,60" & cr & "90,200,170" & cr & "232,232,236" -end colorPalette +end partSwatches -function colorIndex pCtrl, pPal +function swatchIndexOf pCtrl, pPal local tLine, tI put 0 into tI repeat for each line tLine in pPal @@ -3659,7 +3659,7 @@ function colorIndex pCtrl, pPal if tLine is (the uColor of pCtrl) then return tI end repeat return 0 -end colorIndex +end swatchIndexOf function frictionLabel pV if pV is empty then put 0.4 into pV From 491130644f33d88e2d7df22587b59f6b4dc389fc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 22:12:16 +0000 Subject: [PATCH 2/3] Add Duplicate tool, Reset-to-pre-run, and clearer Build/Run modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three release-polish items for the Contraption Builder: - Duplicate tool (next to Delete in SHAPES): click any part to drop an identical copy — same size, colour, material and special settings — by reusing rebuildPart + partSpecial/applyPartSpecial. The copy is nudged off the original, auto-selected, and the tool switches to Drag so it can be moved immediately. The palette section gap was tightened so 9 shape buttons fit. - Reset: toggleMode snapshots the layout (serializeText) the instant Run starts; the new Reset top-bar button restores it via a shared rebuildFromText (extracted from loadContraption) and returns to Build mode. Save now shares serializeText too. - Clearer Build vs Run: the top accent bar turns amber and the subtitle reads "RUNNING" while live (green/neutral in Build), and picking any build tool or joint while running drops straight back into Build mode. https://claude.ai/code/session_018VCGPT8pLd7wQhnLKdZDEx --- CHANGELOG.md | 8 ++ ...box2dxt-contraption-builder.livecodescript | 111 +++++++++++++++--- 2 files changed, 105 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c20156a..370cd5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,14 @@ The native shim's ABI is tracked separately by `b2Version()` (currently `2`). float**, **spin-lock**, **fixed ↔ free movement**, plus per-object specials (fan direction/strength/zone size, magnet pull/push/strength/reach, bomb blast radius/power and fuse). Everything round-trips through the save file. + - **Duplicate tool.** Click any part to drop an identical copy — same size, + colour, material and special settings — then drag it into place (it auto-selects + and switches to the Drag tool). + - **Reset.** A one-click "Reset" returns every part to exactly where it was the + moment you last pressed Run, so you can replay a contraption again and again. + - **Clearer Build vs Run.** The top accent turns amber and the subtitle reads + "RUNNING" while the simulation is live (green/neutral while building), and + picking any build tool while running drops you straight back into Build mode. - **New objects, all built from existing Kit primitives:** **helium balloons** (rise via negative gravity scale; lift parts by a rope; pop in a blast), **bombs** (detonate on a hard hit, a pressure-plate signal, or an optional diff --git a/examples/box2dxt-contraption-builder.livecodescript b/examples/box2dxt-contraption-builder.livecodescript index 93d0cc5..61fb0b2 100644 --- a/examples/box2dxt-contraption-builder.livecodescript +++ b/examples/box2dxt-contraption-builder.livecodescript @@ -1243,9 +1243,10 @@ local gRingN, gRingX, gRingY, gRingF local gDynParts, gDynDirty -- selection / inspector / onboarding / stress-test bookkeeping local gSelPart, gHoverId, gOnbStep, gStressCount +local gPreRun -- snapshot of the build, taken when Run starts -constant kShapeTools = "drag,box,ball,capsule,poly,image,anchor,delete" -constant kShapeLabels = "Drag,Box,Ball,Capsule,Poly,Image,Anchor,Delete" +constant kShapeTools = "drag,box,ball,capsule,poly,image,anchor,delete,duplicate" +constant kShapeLabels = "Drag,Box,Ball,Capsule,Poly,Image,Anchor,Delete,Duplicate" constant kSpecialTools = "balloon,bomb,plate,fan,magnet" constant kSpecialLabels = "Balloon,Bomb,Pressure Plate,Fan,Magnet" constant kJointTools = "hinge,weld,rope,slider,wheel" @@ -1259,7 +1260,7 @@ constant kTopBarH = 54, kAccentH = 3 constant kPaletteW = 196, kInspectorW = 248, kStatusH = 26 constant kCanvasInset = 2, kGroundBarH = 22 constant kPalPadX = 12, kPalBtnH = 26, kPalBtnGap = 4 -constant kPalSectionGap = 12, kPalHeaderH = 18 +constant kPalSectionGap = 8, kPalHeaderH = 18 -- Materials, motors, feedback timing, and default part sizes (were magic numbers). constant kBounceOn = 0.7, kFlashMs = 150, kSelectLine = 3 @@ -1405,9 +1406,9 @@ on makeTopBar makeBar "ui_accent", 0, kTopBarH, tWide, kTopBarH + kAccentH, "72,190,130" makeLabel "ui_title", 18, 7, 280, 33, "Box2Dxt", 22, "236,238,245", true makeLabel "ui_sub", 20, 32, 560, 50, "Contraption Builder — build a machine, then press Run to bring it to life", 11, "150,154,166", false - put "mode,clear,save,load,recipes,info" into tIds - put "Run,Clear,Save,Load,Recipes,?" into tLbls - put "84,64,64,64,84,30" into tWidths + put "mode,reset,clear,save,load,recipes,info" into tIds + put "Run,Reset,Clear,Save,Load,Recipes,?" into tLbls + put "84,60,64,64,64,84,30" into tWidths put 6 into tGap put 0 into tW repeat for each item tId in tWidths @@ -1641,6 +1642,15 @@ on highlightActions end if set the textColor of button "ui_act_mode" to "255,255,255" end if + -- A clear, full-width cue for which mode you're in: green accent + neutral + -- subtitle while building; amber accent + a "running" subtitle while simulating. + if gMode is "run" then + if there is a graphic "ui_accent" then set the backgroundColor of graphic "ui_accent" to "232,150,60" + if there is a field "ui_sub" then set the text of field "ui_sub" to "RUNNING — drag parts to play; press Build or Reset to edit" + else + if there is a graphic "ui_accent" then set the backgroundColor of graphic "ui_accent" to "72,190,130" + if there is a field "ui_sub" then set the text of field "ui_sub" to "Contraption Builder — build a machine, then press Run to bring it to life" + end if setToggle "ui_act_motor", gMotorOn setToggle "ui_act_bouncy", gBouncy end highlightActions @@ -1687,6 +1697,10 @@ on mouseDown end if exit mouseDown end if + if gTool is "duplicate" then + if tHit is not empty then duplicatePart tHit + exit mouseDown + end if if tHit is not empty then put tHit into gDragCtrl put "build" into gDragMode @@ -1748,6 +1762,8 @@ on handleUiClick hideRecipesMenu exit handleUiClick end if + -- Picking a build tool while the simulation runs drops you back into Build. + if ("ui_tool_" is in tName or "ui_joint_" is in tName) and gMode is "run" then toggleMode if "ui_tool_" is in tName then put the uToolId of the target into gTool put empty into gJointTool @@ -1778,6 +1794,9 @@ on handleAction pId case "mode" toggleMode break + case "reset" + resetMachine + break case "clear" clearAll break @@ -1855,6 +1874,7 @@ end handleAction on toggleMode if gMode is "build" then + put serializeText() into gPreRun -- remember the layout so Reset can restore it put "run" into gMode put empty into gJointTool resetPend @@ -2462,6 +2482,35 @@ on deletePart pCtrl put "Deleted a part and any joints on it." into gStatus end deletePart +-- Drop an identical copy of a part — same size, colour, material and special +-- settings — nudged off the original, then select it and switch to Drag so it +-- can be moved straight away. (Joints aren't copied; they connect two parts.) +on duplicatePart pCtrl + if pCtrl is empty then exit duplicatePart + if pCtrl is not among the lines of gParts then exit duplicatePart + local tKind, tLoc, tNx, tNy, tNew + put the uKind of pCtrl into tKind + put the loc of pCtrl into tLoc + put (item 1 of tLoc) + 26 into tNx + put (item 2 of tLoc) + 26 into tNy + if tNx > gArenaR then put (item 1 of tLoc) - 26 into tNx + if tNy > gArenaB then put (item 2 of tLoc) - 26 into tNy + put rebuildPart(tKind, tNx, tNy, (the width of pCtrl), (the height of pCtrl), \ + (the uColor of pCtrl), (the uFile of pCtrl)) into tNew + if tNew is empty then exit duplicatePart + if the uBounce of pCtrl is not empty then + set the uBounce of tNew to (the uBounce of pCtrl) + if (the uBounce of pCtrl) > 0 then b2kSetBounce tNew, (the uBounce of pCtrl) + end if + applyPartSpecial tNew, partSpecial(pCtrl) + renderBuild + put "drag" into gTool + selectPart tNew + highlightAll + put "Duplicated a " & friendlyKind(tKind) & ". Drag the copy where you want it." into gStatus + updateHud +end duplicatePart + on removeJointAt pIndex try b2kRemoveJoint gJHandle[pIndex] @@ -2576,7 +2625,8 @@ end buildSampleSwing -- ===================================================================== -- Save / load (whole contraption as tab-delimited text) -- ===================================================================== -on saveContraption +-- Serialise the whole scene to CB2 text (used by Save and by Reset's snapshot). +function serializeText local tOut, tP, tI put "CB2" & cr into tOut repeat for each line tP in gParts @@ -2590,6 +2640,12 @@ on saveContraption if gJKind[tI] is not empty then put jointLine(tI) & cr after tOut end repeat end if + return tOut +end serializeText + +on saveContraption + local tOut + put serializeText() into tOut ask file "Save contraption as:" with "contraption.cbx" if it is empty or the result is "cancel" then put "Save cancelled." into gStatus @@ -2684,9 +2740,7 @@ function jointLine pIndex end jointLine on loadContraption - local tFile, tData, tLine, tRec, tMap, tNew - local tId, tKind, tX, tY, tW, tH, tBounce, tColor, tFileRef, tSpecial - local tIdA, tIdB, tPx, tPy, tMotor, tA, tB + local tFile, tData answer file "Open a contraption:" with type "Contraption|cbx,txt" put it into tFile if tFile is empty or the result is "cancel" then @@ -2706,10 +2760,21 @@ on loadContraption exit loadContraption end if if gMode is "run" then toggleMode + rebuildFromText tData + put "Loaded contraption from " & tFile & ". Press Run to simulate." into gStatus + updateHud +end loadContraption + +-- Rebuild the whole scene from CB2/CB1 text, clearing the stage first. Shared by +-- Load (from a file) and Reset (from the pre-run snapshot). +on rebuildFromText pData + local tLine, tRec, tMap, tNew + local tId, tKind, tX, tY, tW, tH, tBounce, tColor, tFileRef, tSpecial + local tIdA, tIdB, tPx, tPy, tMotor, tA, tB clearAll put empty into tMap lock screen - repeat for each line tLine in tData + repeat for each line tLine in pData set the itemDelimiter to tab put item 1 of tLine into tRec if tRec is "part" then @@ -2754,9 +2819,22 @@ on loadContraption set the itemDelimiter to comma renderBuild unlock screen - put "Loaded contraption from " & tFile & ". Press Run to simulate." into gStatus +end rebuildFromText + +-- Restore the layout captured when Run last started, and return to Build mode. +on resetMachine + if gPreRun is empty then + put "Nothing to reset yet — press Run first; then Reset brings this build back." into gStatus + updateHud + exit resetMachine + end if + lock screen + if gMode is "run" then toggleMode + rebuildFromText gPreRun + unlock screen + put "Reset to your pre-run build." into gStatus updateHud -end loadContraption +end resetMachine function rebuildPart pKind, pX, pY, pW, pH, pColor, pFile if pW is empty or pW < 1 then put kBoxMin into pW @@ -3708,6 +3786,7 @@ function niceName pId local tNames put "drag=Move / Drag" & cr & "box=Box" & cr & "ball=Ball" & cr & "capsule=Capsule" & cr \ & "poly=Polygon" & cr & "image=Picture" & cr & "anchor=Anchor" & cr & "delete=Delete" & cr \ + & "duplicate=Duplicate" & cr \ & "balloon=Helium Balloon" & cr & "bomb=Bomb" & cr & "plate=Pressure Plate" & cr \ & "fan=Fan / Wind" & cr & "magnet=Magnet" & cr & "hinge=Hinge Joint" & cr \ & "weld=Weld Joint" & cr & "rope=Rope Joint" & cr & "slider=Slider Joint" & cr \ @@ -3729,7 +3808,7 @@ end niceName function toolGlyph pId local tGlyphs put "drag=✥" & cr & "box=■" & cr & "ball=●" & cr & "capsule=▬" & cr & "poly=◆" & cr \ - & "image=▦" & cr & "anchor=▼" & cr & "delete=✕" & cr & "balloon=◯" & cr & "bomb=◉" & cr \ + & "image=▦" & cr & "anchor=▼" & cr & "delete=✕" & cr & "duplicate=▣" & cr & "balloon=◯" & cr & "bomb=◉" & cr \ & "plate=▭" & cr & "fan=▷" & cr & "magnet=◐" & cr & "hinge=○" & cr & "weld=▰" & cr \ & "rope=∿" & cr & "slider=↔" & cr & "wheel=◎" into tGlyphs local tLine @@ -3763,6 +3842,8 @@ function toolHelp pId return "Place a fixed point that never moves." & cr & "An anchor is pinned to the world. Attach joints to it to hold things up — perfect as a motor mount or a pivot." case "delete" return "Remove a part you click on." & cr & "Delete tool. Click any part to remove it, along with any joints attached to it." + case "duplicate" + return "Copy a part you click on." & cr & "Duplicate tool. Click any part to drop an identical copy — same size, colour and settings — then drag it where you want." case "balloon" return "A helium balloon that floats upward." & cr & "A balloon rises on its own. Tie it to a part with a Rope joint to lift that part. A nearby bomb blast pops it." case "bomb" @@ -3791,6 +3872,8 @@ function actionHelp pId switch pId case "mode" return "Switch between Build (arrange) and Run (simulate)." + case "reset" + return "Put every part back where it was when you last pressed Run." case "clear" return "Remove everything and start with an empty stage." case "save" From 0119249a01cd18e43a28b84d2ae84d19b50fecce Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 22:20:53 +0000 Subject: [PATCH 3/3] Fix dangling-else compile error in highlightActions The mode-cue block put single-line `if there is a field ... then set ...` guards immediately before the block's `else`, so the parser bound that `else` to the inner single-line if (dangling else), leaving the outer `if gMode is "run"` block without its `end if` ("missing end if"). Compute the accent colour and subtitle text into locals inside the if/else (no inner ifs), then apply them with the `there is a ...` guards after the block. Scanned the whole script for the same pattern; this was the only one. https://claude.ai/code/session_018VCGPT8pLd7wQhnLKdZDEx --- examples/box2dxt-contraption-builder.livecodescript | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/box2dxt-contraption-builder.livecodescript b/examples/box2dxt-contraption-builder.livecodescript index 61fb0b2..2cc70dc 100644 --- a/examples/box2dxt-contraption-builder.livecodescript +++ b/examples/box2dxt-contraption-builder.livecodescript @@ -1632,6 +1632,7 @@ on highlightGroup pPrefix, pItems, pActiveId, pEnabled, pActiveColor end highlightGroup on highlightActions + local tAccent, tSub if there is a button "ui_act_mode" then if gMode is "run" then set the label of button "ui_act_mode" to "Build" @@ -1645,12 +1646,14 @@ on highlightActions -- A clear, full-width cue for which mode you're in: green accent + neutral -- subtitle while building; amber accent + a "running" subtitle while simulating. if gMode is "run" then - if there is a graphic "ui_accent" then set the backgroundColor of graphic "ui_accent" to "232,150,60" - if there is a field "ui_sub" then set the text of field "ui_sub" to "RUNNING — drag parts to play; press Build or Reset to edit" + put "232,150,60" into tAccent + put "RUNNING — drag parts to play; press Build or Reset to edit" into tSub else - if there is a graphic "ui_accent" then set the backgroundColor of graphic "ui_accent" to "72,190,130" - if there is a field "ui_sub" then set the text of field "ui_sub" to "Contraption Builder — build a machine, then press Run to bring it to life" + put "72,190,130" into tAccent + put "Contraption Builder — build a machine, then press Run to bring it to life" into tSub end if + if there is a graphic "ui_accent" then set the backgroundColor of graphic "ui_accent" to tAccent + if there is a field "ui_sub" then set the text of field "ui_sub" to tSub setToggle "ui_act_motor", gMotorOn setToggle "ui_act_bouncy", gBouncy end highlightActions