Microsoft Small Basic

Program Listing: GDD325-22
' Halloween
' Version 2.2
' Copyright © 2014-2020 Nonki Takahashi. The MIT License.
' 2016-10-26 12:42:57 Shapes generated by Shapes 2.3b.
' Last update 2020-10-26
' Program ID GDD325-22

year = 2020
Init()
If sbd Or silverlight Then
buttonClicked = "False"
While Not[buttonClicked]
Program.Delay(200)
EndWhile
TrickOrTreat()
EndIf

Sub DrawTree
pw = 20 ' pen width
dl = 10 ' delta length
branch[1]["x1"] = 100
branch[1]["y1"] = gh * 0.6
branch[1]["angle"] = -90
GraphicsWindow.PenColor = "Black"
For j = 1 To 20
If silverlight Then ' for known issue 21649
Program.Delay(msWait)
EndIf
GraphicsWindow.PenWidth = pw
n = Array.GetItemCount(branch)
m = 0
For i = 1 To n
If Math.GetRandomNumber(100) < 15 Then
_a1 = Math.GetRadians(branch[i]["angle"] - 20)
branch[i]["x2"] = branch[i]["x1"] + dl * Math.Cos(_a1)
branch[i]["y2"] = branch[i]["y1"] + dl * Math.Sin(_a1)
branch[i]["angle"] = branch[i]["angle"] - 20 + Math.GetRandomNumber(11) - 6
GraphicsWindow.DrawLine(branch[i]["x1"], branch[i]["y1"], branch[i]["x2"], branch[i]["y2"])
branch[i]["x1"] = branch[i]["x2"]
branch[i]["y1"] = branch[i]["y2"]
_a2 = Math.GetRadians(branch[i]["angle"] + 20)
m = m + 1
branch[n + m]["x2"] = branch[i]["x1"] + dl * Math.Cos(_a2)
branch[n + m]["y2"] = branch[i]["y1"] + dl * Math.Sin(_a2)
branch[n + m]["angle"] = branch[i]["angle"] + 20 + Math.GetRandomNumber(11) - 6
GraphicsWindow.DrawLine(branch[i]["x1"], branch[i]["y1"], branch[n + m]["x2"], branch[n + m]["y2"])
branch[n + m]["x1"] = branch[n + m]["x2"]
branch[n + m]["y1"] = branch[n + m]["y2"]
Else
_a = Math.GetRadians(branch[i]["angle"])
branch[i]["x2"] = branch[i]["x1"] + dl * Math.Cos(_a)
branch[i]["y2"] = branch[i]["y1"] + dl * Math.Sin(_a)
branch[i]["angle"] = branch[i]["angle"] + Math.GetRandomNumber(11) - 6
GraphicsWindow.DrawLine(branch[i]["x1"], branch[i]["y1"], branch[i]["x2"], branch[i]["y2"])
branch[i]["x1"] = branch[i]["x2"]
branch[i]["y1"] = branch[i]["y2"]
EndIf
EndFor
pw = pw - 1
EndFor
EndSub

Sub Init
Not["True"] = "False"
Not["False"] = "True"
SB_Workaround()
gw = GraphicsWindow.Width
gh = GraphicsWindow.Height
GraphicsWindow.BackgroundColor = "#333333"
GraphicsWindow.FontName = "Arial"
GraphicsWindow.BrushColor = "Black"
trick = Controls.AddButton("TRICK", 10, 10)
GraphicsWindow.BrushColor = "LightGray"
orText = Shapes.AddText("OR")
If sbd Or silverlight Then
Shapes.Move(orText, 94, 16)
Else
Shapes.Move(orText, 112, 16)
EndIf
GraphicsWindow.BrushColor = "Black"
treat = Controls.AddButton("TREAT", 150, 10)
' initialize shapes
Shapes_Init_Jack()
' add shapes
scale = 1
angle = 0
name = "Jack"
SBO_SplitShape()
Sprite_Add()
i = nSprite
spr = sprite[i]
x = (gw - spr["width"]) / 2
y = (gh - spr["height"]) / 2
Sprite_Move()
clicked = "False"
Controls.ButtonClicked = OnButtonClicked
EndSub

Sub LightOn
For i = 8 To 12
shape[i]["bc"] = "#FFFF66"
EndFor
EndSub

Sub OnButtonClicked
If sbd Or silverlight Then
buttonClicked = "True"
Else
TrickOrTreat()
EndIf
EndSub

Sub OnTick
Timer.Pause()
For i = 1 To 9
bat[i]["cycle"] = bat[i]["cycle"] + 1
If 8 < bat[i]["cycle"] Then
bat[i]["cycle"] = 1
EndIf
bat[i]["x"] = bat[i]["x"] + Math.GetRandomNumber(7) - 4
If bat[i]["x"] < 0 Then
bat[i]["x"] = 0
ElseIf (gw - ow) < bat[i]["x"] Then
bat[i]["x"] = gw - ow
EndIf
bat[i]["y"] = bat[i]["y"] + Math.GetRandomNumber(17) - 9
If bat[i]["y"] < 0 Then
bat[i]["y"] = 0
ElseIf (gh - 110 - oh) < bat[i]["y"] Then
bat[i]["y"] = gh - 110 - oh
EndIf
bat[i]["angle"] = bat[i]["angle"] + Math.GetRandomNumber(7) - 4
If bat[i]["angle"] < -10 Then
bat[i]["angle"] = -10
ElseIf 10 < bat[i]["angle"] Then
bat[i]["angle"] = 10
EndIf
If bat[i]["cycle"] < 4 Then
Shapes.HideShape(bat[i]["close"])
Shapes.Move(bat[i]["open"], bat[i]["x"], bat[i]["y"])
Shapes.Rotate(bat[i]["open"], bat[i]["angle"])
Shapes.ShowShape(bat[i]["open"])
Else
Shapes.HideShape(bat[i]["open"])
Shapes.Move(bat[i]["close"], bat[i]["x"], bat[i]["y"])
Shapes.Rotate(bat[i]["close"], bat[i]["angle"])
Shapes.ShowShape(bat[i]["close"])
EndIf
EndFor
Timer.Resume()
EndSub

Sub TrickOrTreat
i = nSprite
Sprite_Remove()
Controls.Remove(trick)
Shapes.Remove(orText)
Controls.Remove(treat)
GraphicsWindow.FontSize = 40
If Controls.LastClickedButton = trick Then
GraphicsWindow.BackgroundColor = "Indigo"
' draw moon
GraphicsWindow.BrushColor = "White"
size = 100
x = gw - size - 10
y = 10
GraphicsWindow.FillEllipse(x, y, size, size)
' draw ground
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillRectangle(0, gh * 0.6, gw, gh * 0.4)
' draw tree
DrawTree()
' draw Jack-o'-lanterns
LightOn()
scale = (gw / 6) * 0.6 / spr["width"]
xl = spr["width"] * scale * 0.2
dx = gw / 6
y = gh * 0.6 - spr["height"] * scale / 2
For i = 1 To 6
name = "Jack" + i
Sprite_Add()
x = xl + (i - 1) * dx
Sprite_Move()
EndFor
' draw caption
GraphicsWindow.BrushColor = "Orange"
halloween = Shapes.AddText("Happy Halloween " + year)
x = 10
y = 10
Shapes.Move(halloween, x, y)
' draw bats
oImg = "http://www.nonkit.com/smallbasic.files/OpenBat.png"
If silverlight Then
Program.Delay(msWait)
EndIf
ow = 63
oh = 30
cImg = "http://www.nonkit.com/smallbasic.files/CloseBat.png"
cw = 26
ch = 26
For i = 1 To 9
bat[i]["open"] = Shapes.AddImage(oImg)
bat[i]["close"] = Shapes.AddImage(cImg)
bat[i]["cycle"] = Math.GetRandomNumber(4)
bat[i]["x"] = Math.GetRandomNumber(gw - ow)
bat[i]["y"] = Math.GetRandomNumber(gh * 0.6 - oh)
bat[i]["angle"] = Math.GetRandomNumber(21) - 11
EndFor
Program.Delay(500)
Timer.Interval = 1000 / 20
Timer.Tick = OnTick
Else
GraphicsWindow.BackgroundColor = "Orange"
url = "http://www.nonkit.com/smallbasic.files/Candy1.png"
candy1 = Shapes.AddImage(url)
If silverlight Then
Program.Delay(msWait)
EndIf
cw = 197
ch = 376
x = (gw - cw) / 2 - 200
y = (gh - ch) / 2
Shapes.Move(candy1, x, y)
Shapes.Rotate(candy1, -10)
url = "http://www.nonkit.com/smallbasic.files/Candy2.png"
candy2 = Shapes.AddImage(url)
x = (gw - cw) / 2
Shapes.Move(candy2, x, y)
x = (gw - cw) / 2 + 200
url = "http://www.nonkit.com/smallbasic.files/Candy3.png"
candy3 = Shapes.AddImage(url)
If silverlight Then
Program.Delay(msWait)
EndIf
Shapes.Move(candy3, x, y)
Shapes.Rotate(candy3, 10)
GraphicsWindow.BrushColor = "Black"
halloween = Shapes.AddText("Happy Halloween " + year)
x = 10
y = 10
Shapes.Move(halloween, x, y)
EndIf
If "False" Then
OnTick = 1
EndIf
EndSub

Sub Math_CartesianToPolar
' Math | convert cartesian coodinate to polar coordinate
' param x, y - cartesian coordinate
' return r, a - polar coordinate
r = Math.SquareRoot(x * x + y * y)
If x = 0 And y > 0 Then
a = 90 ' [degree]
ElseIf x = 0 And y < 0 Then
a = -90
Else
a = Math.ArcTan(y / x) * 180 / Math.Pi
EndIf
If x < 0 Then
a = a + 180
ElseIf x > 0 And y < 0 Then
a = a + 360
EndIf
EndSub

Sub SB_LineWorkaround
' Small Basic | line rotate workaround for SBD
' param x, y - coordinate of the position of the line
' param x1, y1 - coordinate of the first point
' param x2, y2 - coordinate of the second point
' param pw - pen width
' param alpha - to rotate [degree]
' return x, y - workaround value for the coordinate
Stack.PushValue("local", x)
Stack.PushValue("local", y)
x = x1 - x2
y = y1 - y2
Math_CartesianToPolar()
y = Stack.PopValue("local")
x = Stack.PopValue("local")
_a = Math.GetRadians(a)
_alpha = Math.GetRadians(a - alpha)
Δx = pw / 4 * (Math.Sin(_alpha) - Math.Sin(_a))
Δy = pw / 4 * (Math.Cos(_alpha) - Math.Cos(_a))
x = x - Δx
y = y - Δy
EndSub

Sub SB_RotateWorkaround
' Small Basic | rotate workaround for Silverlight
' param shp - current shape
' param x, y - original coordinate
' param _alpha - angle [radian]
' returns x, y - workaround coordinate
If shp["func"] = "tri" Then
x1 = -Math.Floor(shp["x3"] / 2)
y1 = -Math.Floor(shp["y3"] / 2)
ElseIf shp["func"] = "line" Then
x1 = -Math.Floor(Math.Abs(shp["x1"] - shp["x2"]) / 2)
y1 = -Math.Floor(Math.Abs(shp["y1"] - shp["y2"]) / 2)
EndIf
ox = x - x1
oy = y - y1
x = x1 * Math.Cos(_alpha) - y1 * Math.Sin(_alpha) + ox
y = x1 * Math.Sin(_alpha) + y1 * Math.Cos(_alpha) + oy
EndSub

Sub SB_Workaround
' Small Basic | workaround for Silverlight / SBD
' return silverlight - "True" if in remote
' return sbd - "True" if Small Basic Desktop
_gw = GraphicsWindow.Width
_gh = GraphicsWindow.Height
silverlight = "False"
sbd = "False"
If (_gw = 640) And (_gh = 480) Then
silverlight = "True"
msWait = 300
ElseIf (_gw = 624) And (_gh = 441) Then
sbd = "True"
EndIf
EndSub

Sub SBO_SplitParam
' Small Basic Online | split "index=value;" format as an array param
' param param[] - to split
' return param[] - split
_param = ""
p = 1
len = Text.GetLength(param)
While p <= len
eq = Text.GetIndexOf(Text.GetSubTextToEnd(param, p), "=")
sc = Text.GetIndexOf(Text.GetSubTextToEnd(param, p), ";")
name = Text.GetSubText(param, p, eq - 1)
value = Text.GetSubText(param, p + eq, sc - eq - 1)
_param[name] = value
p = p + sc
EndWhile
param = _param
EndSub

Sub SBO_SplitShape
' Small Basic Online | split "index=value;" format as a jagged array shape
' param shape[] - to split
' return shape[] - split
arry = shape
n = Array.GetItemCount(arry)
For i = 1 To n
param = arry[i]
SBO_SplitParam()
arry[i] = param
EndFor
shape = arry
EndSub

Sub Shapes_CalcRotateZoomPos
' Shapes | calculate position for rotated and zoomed shape
' param["x"], param["y"] - position of a shape
' param["width"], param["height"] - size of a shape
' param ["cx"], param["cy"] - center of rotation
' param ["angle"] - rotate angle
' param ["scale"] - zoom scale
' return x, y - rotated position of a shape
_cx = param["x"] + param["width"] / 2
_cy = param["y"] + param["height"] / 2
x = _cx - param["cx"]
y = _cy - param["cy"]
Math_CartesianToPolar()
a = a + param["angle"]
x = r * Math.Cos(a * Math.Pi / 180) * param["scale"]
y = r * Math.Sin(a * Math.Pi / 180) * param["scale"]
_cx = x + param["cx"]
_cy = y + param["cy"]
x = _cx - param["width"] / 2
y = _cy - param["height"] / 2
EndSub

Sub Shapes_CalcWidthAndHeight
' Shapes | calculate total width and height of shapes
' param shape[] - shape array
' return shWidth, shHeight - total size of shapes
For i = 1 To Array.GetItemCount(shape)
shp = shape[i]
If shp["func"] = "tri" Or shp["func"] = "line" Then
xmin = shp["x1"]
xmax = shp["x1"]
ymin = shp["y1"]
ymax = shp["y1"]
If shp["x2"] < xmin Then
xmin = shp["x2"]
EndIf
If xmax < shp["x2"] Then
xmax = shp["x2"]
EndIf
If shp["y2"] < ymin Then
ymin = shp["y2"]
EndIf
If ymax < shp["y2"] Then
ymax = shp["y2"]
EndIf
If shp["func"] = "tri" Then
If shp["x3"] < xmin Then
xmin = shp["x3"]
EndIf
If xmax < shp["x3"] Then
xmax = shp["x3"]
EndIf
If shp["y3"] < ymin Then
ymin = shp["y3"]
EndIf
If ymax < shp["y3"] Then
ymax = shp["y3"]
EndIf
EndIf
shp["width"] = xmax - xmin
shp["height"] = ymax - ymin
EndIf
If i = 1 Then
shWidth = shp["x"] + shp["width"]
shHeight = shp["y"] + shp["height"]
Else
If shWidth < shp["x"] + shp["width"] Then
shWidth = shp["x"] + shp["width"]
EndIf
If shHeight < shp["y"] + shp["height"] Then
shHeight = shp["y"] + shp["height"]
EndIf
EndIf
shape[i] = shp
EndFor
EndSub

Sub Shapes_Init_Jack
' Shapes | Initialize shapes data
' return shX, shY - current position of shapes
' return shape - array of shapes
shX = 146 ' x offset
shY = 90 ' y offset
shape = ""
shape[1] = "func=ell;x=14;y=270;width=287;height=31;bc=#000000;pw=0;"
shape[2] = "func=ell;x=0;y=47;width=79;height=224;bc=#D76E18;pw=0;"
shape[3] = "func=ell;x=43;y=32;width=99;height=255;bc=#D76E18;pw=0;"
shape[4] = "func=ell;x=231;y=49;width=84;height=215;bc=#D76E18;pw=0;"
shape[5] = "func=ell;x=181;y=45;width=99;height=239;bc=#D76E18;pw=0;"
shape[6] = "func=ell;x=99;y=33;width=127;height=255;bc=#D76E18;pw=0;"
shape[7] = "func=rect;x=159;y=20;width=16;height=40;angle=7;bc=#2D5C32;pw=0;"
shape[8] = "func=tri;x=65;y=107;x1=35;y1=0;x2=0;y2=60;x3=70;y3=60;bc=#000000;pw=0;"
shape[9] = "func=tri;x=179;y=105;x1=35;y1=0;x2=0;y2=60;x3=70;y3=60;bc=#000000;pw=0;"
shape[10] = "func=tri;x=35;y=177;x1=22;y1=0;x2=0;y2=65;x3=44;y3=65;angle=298;bc=#000000;pw=0;"
shape[11] = "func=tri;x=229;y=182;x1=22;y1=0;x2=0;y2=57;x3=44;y3=57;angle=59;bc=#000000;pw=0;"
shape[12] = "func=rect;x=74;y=202;width=166;height=46;bc=#000000;pw=0;"
shape[13] = "func=rect;x=92;y=197;width=34;height=30;bc=#D76E18;pw=0;"
shape[14] = "func=rect;x=182;y=195;width=26;height=31;bc=#D76E18;pw=0;"
shape[15] = "func=rect;x=62;y=223;width=18;height=36;angle=10;bc=#D76E18;pw=0;"
shape[16] = "func=rect;x=138;y=227;width=32;height=34;bc=#D76E18;pw=0;"
shape[17] = "func=rect;x=216;y=225;width=26;height=27;angle=350;bc=#D76E18;pw=0;"
shape[18] = "func=rect;x=169;y=0;width=17;height=30;angle=48;bc=#2D5C32;pw=0;"
shape[19] = "func=ell;x=144;y=51;width=43;height=21;bc=#2D5C32;pw=0;"
EndSub

Sub Sprite_Add
' Sprite | add shapes to a sprite
' param name - sprite name
' param shX, shY - origin of shape array
' param scale - to resize
' param shape[] - shape array
' param nSprite - number of sprite
' return nSprite - updated number of sprite
' return sprite[] - sprite array
Stack.PushValue("local", i)
Stack.PushValue("local", x)
Stack.PushValue("local", y)
nSprite = nSprite + 1
spr = ""
spr["name"] = name
spr["x"] = shX
spr["y"] = shY
spr["angle"] = 0
spr["dir"] = 1
Shapes_CalcWidthAndHeight()
spr["width"] = shWidth
spr["height"] = shHeight
spr["cx"] = shWidth / 2
spr["cy"] = shHeight / 2
If scale = "" Then
scale = 1
EndIf
s = scale
spr["scale"] = s
For i = 1 To Array.GetItemCount(shape)
shp = shape[i]
GraphicsWindow.PenWidth = shp["pw"] * s
If shp["pw"] > 0 Then
GraphicsWindow.PenColor = shp["pc"]
EndIf
If Text.IsSubText("rect|ell|tri|text|btn", shp["func"]) Then
GraphicsWindow.BrushColor = shp["bc"]
EndIf
If Text.IsSubText("text|btn", shp["func"]) Then
If silverlight Then
fs = Math.Floor(shp["fs"] * 0.9)
Else
fs = shp["fs"]
EndIf
GraphicsWindow.FontSize = fs * s
GraphicsWindow.FontName = shp["fn"]
If shp["fb"] = "False" Then
GraphicsWindow.FontBold = "False"
Else
GraphicsWindow.FontBold = "True"
EndIf
EndIf
If shp["func"] = "rect" Then
shp["obj"] = Shapes.AddRectangle(shp["width"] * s, shp["height"] * s)
ElseIf shp["func"] = "ell" Then
shp["obj"] = Shapes.AddEllipse(shp["width"] * s, shp["height"] * s)
ElseIf shp["func"] = "tri" Then
shp["obj"] = Shapes.AddTriangle(shp["x1"] * s, shp["y1"] * s, shp["x2"] * s, shp["y2"] * s, shp["x3"] * s, shp["y3"] * s)
ElseIf shp["func"] = "line" Then
shp["obj"] = Shapes.AddLine(shp["x1"] * s, shp["y1"] * s, shp["x2"] * s, shp["y2"] * s)
ElseIf shp["func"] = "text" Then
shp["obj"] = Shapes.AddText(shp["text"])
EndIf
x = shp["x"]
y = shp["y"]
shp["rx"] = x
shp["ry"] = y
If sbd And (shp["func"] = "line") Then
shp["wx"] = x
shp["wy"] = y
ElseIf silverlight And Text.IsSubText("tri|line", shp["func"]) Then
_alpha = Math.GetRadians(shp["angle"])
SB_RotateWorkaround()
shp["wx"] = x
shp["wy"] = y
EndIf
If shp["func"] = "btn" Then
shp["obj"] = Controls.AddButton(shp["caption"], shX + x * s, shY + y * s)
Else
Shapes.Move(shp["obj"], shX + x * s, shY + y * s)
EndIf
If Text.IsSubText("rect|ell|tri|text", shp["func"]) And (shp["angle"] <> 0) And (shp["angle"] <> "") Then
Shapes.Rotate(shp["obj"], shp["angle"])
EndIf
shape[i] = shp
EndFor
spr["shape"] = shape
sprite[nSprite] = spr
y = Stack.PopValue("local")
x = Stack.PopValue("local")
i = Stack.PopValue("local")
EndSub

Sub Sprite_Move
' Sprite | move a sprite
' param sprite[i] - sprite to move
' param x, y - position to move
' return sprite[i] - updated sprite
Stack.PushValue("local", j)
spr = sprite[i]
s = spr["scale"]
spr["x"] = x
spr["y"] = y
shape = spr["shape"]
n = Array.GetItemCount(shape)
For j = 1 To n
shp = shape[j]
If sbd And (shp["func"] = "line") Then
_x = shp["wx"]
_y = shp["wy"]
ElseIf silverlight And Text.IsSubText("tri|line", shp["func"]) Then
_x = shp["wx"]
_y = shp["wy"]
Else
_x = shp["rx"]
_y = shp["ry"]
EndIf
Shapes.Move(shp["obj"], spr["x"] + _x * s, spr["y"] + _y * s)
EndFor
sprite[i] = spr
j = Stack.PopValue("local")
EndSub

Sub Sprite_Remove
' Sprite | remove a sprite
' param sprite[i] - sprite to remove
' return nSprite - updated number of sprites
If (0 < i) And (i <= nSprite) Then
spr = sprite[i]
shape = spr["shape"]
For _i = 1 To Array.GetItemCount(shape)
Shapes.Remove(shape[_i]["obj"])
EndFor
For _i = i + 1 To nSprite
sprite[_i - 1] = sprite[_i]
EndFor
nSprite = nSprite - 1
EndIf
EndSub

Sub Sprite_Rotate
' Sprite | rotate a sprite
' param sprite[i] - sprite to rotate
' param cx, cy - rotation center (if given)
' param angle - to rotate
Stack.PushValue("local", x)
Stack.PushValue("local", y)
Stack.PushValue("local", n)
spr = sprite[i]
shape = spr["shape"]
moved = "False"
If cx <> "" Then
moved = "True"
Else
cx = "" ' to avoid syntax error
EndIf
If cy <> "" Then
moved = "True"
Else
cy = "" ' to avoid syntax error
EndIf
s = spr["scale"]
If moved Then
param["cx"] = (cx - spr["x"]) / s
param["cy"] = (cy - spr["y"]) / s
Else
param["cx"] = spr["width"] / 2
param["cy"] = spr["height"] / 2
EndIf
param["scale"] = 1
spr["angle"] = angle
param["angle"] = spr["angle"]
n = Array.GetItemCount(shape)
Stack.PushValue("local", i)
For i = 1 To n
shp = shape[i]
param["x"] = shp["x"]
param["y"] = shp["y"]
param["width"] = shp["width"]
param["height"] = shp["height"]
Shapes_CalcRotateZoomPos()
shp["rx"] = x
shp["ry"] = y
alpha = shp["angle"] + spr["angle"]
If sbd And (shp["func"] = "line") And (alpha <> 0) Then
x1 = shp["x1"]
y1 = shp["y1"]
x2 = shp["x2"]
y2 = shp["y2"]
pw = shp["pw"]
SB_LineWorkaround()
shp["wx"] = x
shp["wy"] = y
ElseIf silverlight And Text.IsSubText("tri|line", shp["func"]) Then
_alpha = Math.GetRadians(alpha)
SB_RotateWorkaround()
shp["wx"] = x
shp["wy"] = y
EndIf
Shapes.Move(shp["obj"], spr["x"] + x * s, spr["y"] + y * s)
Shapes.Rotate(shp["obj"], shp["angle"] + spr["angle"])
shape[i] = shp
EndFor
i = Stack.PopValue("local")
spr["shape"] = shape
sprite[i] = spr
n = Stack.PopValue("local")
y = Stack.PopValue("local")
x = Stack.PopValue("local")
EndSub