Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
126 changes: 106 additions & 20 deletions examples/box2dxt-contraption-builder.livecodescript
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1631,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"
Expand All @@ -1641,6 +1643,17 @@ 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
put "232,150,60" into tAccent
put "RUNNING — drag parts to play; press Build or Reset to edit" into tSub
else
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
Expand Down Expand Up @@ -1687,6 +1700,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
Expand Down Expand Up @@ -1748,6 +1765,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
Expand Down Expand Up @@ -1778,6 +1797,9 @@ on handleAction pId
case "mode"
toggleMode
break
case "reset"
resetMachine
break
case "clear"
clearAll
break
Expand Down Expand Up @@ -1855,6 +1877,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
Expand Down Expand Up @@ -2462,6 +2485,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]
Expand Down Expand Up @@ -2576,7 +2628,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
Expand All @@ -2590,6 +2643,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
Expand Down Expand Up @@ -2684,9 +2743,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
Expand All @@ -2706,10 +2763,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
Expand Down Expand Up @@ -2754,9 +2822,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
Expand Down Expand Up @@ -3476,9 +3557,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)
Expand Down Expand Up @@ -3645,21 +3726,21 @@ 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
add 1 to tI
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
Expand Down Expand Up @@ -3708,6 +3789,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 \
Expand All @@ -3729,7 +3811,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
Expand Down Expand Up @@ -3763,6 +3845,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"
Expand Down Expand Up @@ -3791,6 +3875,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"
Expand Down
Loading