Scripts

Layout

Set your editor to use 4-character-wide tabs. Use tabs to align expressions to each other where needed. Use space to align coordinates with and without – sign below each other.

hotspot2 -overhang,  0,     unID, width, 1 + 128 : unID = unID + 1
hotspot2 -overhang, -width, unID, width, 2       : unID = unID + 1
hotspot2 -overhang,  1,     unID, width, 3       : unID = unID + 1

To improve code readability, it is essential to express the hierarchy of nested statements. Code editors usually support folding code blocks based on indentation.

Recommended indentation of code blocks:

if condition1 then
    ...
else
    ...
endif

if condition2 then
    if condition3 then
        ...
    else
        ...
    endif

    if condition4 then
        ...
    endif
else
    ...
endif

for i = initialValue to endValue
    ...
next i

do
    ...
    for i = initialValue to endValue
        ...
    next i
    ...
    bCondition = ...
    ...
while bCondition

group "hole"
    ...
endgroup

cutform ...
    ...
cutend

Do not use indentation after transformations.

Sometimes a cut… command has to be conditional, do not indent the cut model in this case.

Use empty lines to emphasize closely related blocks of code. Do not over-use empty lines, too much scrolling degrades readability.
Write one statement per line. Exceptions are incrementing array indices where a loop is not suitable (HOTSPOT, UI_LISTITEM, array definitions).

Structure

Short and/or simple scripts can be of linear structure which makes them clearer. Subroutines are only needed when a calculation or model generation is done more than once.

It is an important principle to avoid coding the same – or any similar – thing twice. Redundancy will make the later changes much more difficult.

Avoid coding deep choice branches by preparing the data for a calculation or generation command in smaller, more general steps, introducing layers of abstraction with subroutines.

Split long code – longer than 1-2 screens or 100 lines – into subroutines that work at the same level of abstraction and handle only related data.

Calculate complex values only once to minimize redundancy and store them as variables or in the transformation stack (ADD, ROT, etc.).

Calculate values that are used locally before the block of use. Calculate values, initialize constants that are used throughout the script as early as possible at the start of the script.

Use drawing/modeling/hotspot commands at the local origin, unless transforming geometric parameters would not be feasible.
Macro calls with long parameter lists can be placed into subroutines for more compact code.

Use “” for non-localized strings (e.g. macro calls, parameter names). For localization _(”) can be used in .libpack format (e.g. strings shown on the UI).

Generalized solution that can be reused in other objects too:

add2 left, 0
_width  = right - left

if bOnHomeStory then
    line_type linetypeContour
    fill gs_fill_type
    _fillPen    = gs_fill_pen
    _fillBGPen  = gs_back_pen
    _height = depth
    _mask   = 1 + 2 + 4 + 8 ! bottom, left, right
    gosub "DrawFilledRectangleWithMaskedContour"
endif

if (bOnUpperStory or bOnAboveUpper) and bDrawContBB then
    line_type linetypeBelow
    fill filltypeBelow
    _fillPen    = penFillBelow
    _fillBGPen  = penFillBackBelow

    _height = depth / 2
    _mask   = 1 + 2 + 8 ! bottom, left, right
    gosub "DrawFilledRectangleWithMaskedContour"

    add2 0, _height / 2
    _fillBGPen  = 0
    _mask       = 2 + 4 + 8 ! top, left, right
    gosub "DrawFilledRectangleWithMaskedContour"
    del 1
endif

del 1

! ==============================================================================
end ! end ! end ! end ! end ! end ! end ! end ! end ! end ! end ! end ! end !
! ==============================================================================


! ==============================================================================
"DrawFilledRectangleWithMaskedContour":
! ------------------------------------------------------------------------------
! draws a rectangle with fill at (0, 0)
! input:
!   _mask       which side contours to draw 1 - bottom    
!                                           2 - left
!                                           4 - top
!                                           8 - right
!   _width
!   _height
!   _fillPen
!   _fillBGPen
! ==============================================================================

   poly2_b 5, 3, _fillPen, _fillBGPen,
        0,      _height,    bittest(_mask, 1),  ! left
        0,      0,          bittest(_mask, 0),  ! bottom
        _width, 0,          bittest(_mask, 3),  ! right
        _width, _height,    bittest(_mask, 2),  ! top,
        0,      _height,    -1

return

! instead of:
if bOnHomeStory then
   line_type linetypeContour
   fill gs_fill_type
   poly2_b 5, 3, gs_fill_pen, gs_back_pen,
       left,    0,     1,
       left,    depth, 1,
       right,   depth, 1,
       right,   0,     1,
       left,    0,    -1
endif

if (bOnUpperStory or bOnAboveUpper) and bDrawContBB then
   line_type linetypeBelow
   fill filltypeBelow
   poly2_b 5, 3, penFillBelow, penFillBackBelow,
       left,    0,         1,
       left,    depth / 2, 0,
       right,   depth / 2, 1,
       right,   0,         1,
       left,    0,        -1
   poly2_b 5, 3, penFillBelow, 0,
       left,   depth / 2,  1,
       left,   depth,      1,
       right,  depth,      1,
       right,  depth / 2,  0,
       left,   depth / 2,  -1
endif